From 92da817a90ba6c3d5a321ba136c3b9cd861a4883 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Fri, 7 Nov 2025 11:52:20 +0000 Subject: [PATCH 01/54] Add plugin support for Cmd on Macs rather than Ctrl (optional) Tested with i18n-hljs.html, but undocumented --- code-input.d.ts | 9 ++++-- plugins/find-and-replace.js | 20 +++++++++++-- plugins/go-to-line.js | 19 +++++++++++-- tests/i18n-hljs.html | 4 +-- tests/i18n-prism.html | 4 +-- tests/tester.js | 57 ++++++++++++++++++++++++++----------- 6 files changed, 84 insertions(+), 29 deletions(-) diff --git a/code-input.d.ts b/code-input.d.ts index e97206a..8465673 100644 --- a/code-input.d.ts +++ b/code-input.d.ts @@ -138,6 +138,7 @@ export namespace plugins { * @param {boolean} useCtrlF Should Ctrl+F be overriden for find-and-replace find functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, false)`. * @param {boolean} useCtrlH Should Ctrl+H be overriden for find-and-replace replace functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, true)`. * @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the find-and-replace.js source code for the English text. + * @param {boolean} alwaysCtrl: if true always use Ctrl+F/H as the keyboard shortcut; if false use Cmd+F/H on Apple devices and Ctrl+F/H elsewhere. False highly recommended; defaults to true for backwards compatiblity. */ constructor(useCtrlF?: boolean, useCtrlH?: boolean, instructionTranslations?: { @@ -159,7 +160,8 @@ export namespace plugins { replaceAction?: string; replaceAllActionShort?: string; replaceAllAction?: string - } + }, + alwaysCtrl?: boolean ); /** * Show a find-and-replace dialog. @@ -190,7 +192,9 @@ export namespace plugins { guidanceColumnRange?: (line: Number, current: Number, max: Number) => string; guidanceValidLine?: (line: Number) => string; guidanceValidColumn?: (line: Number, column: Number) => string; - }); + }, + alwaysCtrl?: boolean + ); /** * Show a search-like dialog prompting line number. * @param {codeInput.CodeInput} codeInput the `` element. @@ -213,6 +217,7 @@ export namespace plugins { * @param {Object} bracketPairs Opening brackets mapped to closing brackets, default and example {"(": ")", "[": "]", "{": "}"}. All brackets must only be one character, and this can be left as null to remove bracket-based indentation behaviour. * @param {boolean} escTabToChangeFocus Whether pressing the Escape key before (Shift+)Tab should make this keypress focus on a different element (Tab's default behaviour). You should always either enable this or use this plugin's disableTabIndentation and enableTabIndentation methods linked to other keyboard shortcuts, for accessibility. * @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the English text. + * @param {boolean} alwaysCtrl: if true always use Ctrl+G as the keyboard shortcut; if false use Cmd+G on Apple devices and Ctrl+G elsewhere. False highly recommended; defaults to true for backwards compatiblity. */ constructor(defaultSpaces?: boolean, numSpaces?: Number, bracketPairs?: Object, escTabToChangeFocus?: boolean, instructionTranslations?: { tabForIndentation?: string; diff --git a/plugins/find-and-replace.js b/plugins/find-and-replace.js index 3d4417c..2b8da87 100644 --- a/plugins/find-and-replace.js +++ b/plugins/find-and-replace.js @@ -36,11 +36,13 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin { * @param {boolean} useCtrlF Should Ctrl+F be overriden for find-and-replace find functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, false)`. * @param {boolean} useCtrlH Should Ctrl+H be overriden for find-and-replace replace functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, true)`. * @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the find-and-replace.js source code for the English text and available keys. + * @param {boolean} alwaysCtrl: if true always use Ctrl+F/H as the keyboard shortcut; if false use Cmd+F/H on Apple devices and Ctrl+F/H elsewhere. False highly recommended; defaults to true for backwards compatiblity. */ - constructor(useCtrlF = true, useCtrlH = true, instructionTranslations = {}) { + constructor(useCtrlF = true, useCtrlH = true, instructionTranslations = {}, alwaysCtrl = true) { super([]); // No observed attributes this.useCtrlF = useCtrlF; this.useCtrlH = useCtrlH; + this.alwaysCtrl = alwaysCtrl; this.addTranslations(this.instructions, instructionTranslations); } @@ -420,9 +422,21 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin { this.updateFindMatches(dialog); } + hasModifier(event) { + if(this.alwaysCtrl) return event.ctrlKey; + // Thanks to https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform + if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") { + // Command + return event.metaKey; + } else { + // Control + return event.ctrlKey; + } + } + /* Event handler for keydown event that makes Ctrl+F open find dialog */ checkCtrlF(codeInput, event) { - if (event.ctrlKey && event.key == 'f') { + if (this.hasModifier(event) && event.key == 'f') { event.preventDefault(); this.showPrompt(codeInput, false); } @@ -430,7 +444,7 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin { /* Event handler for keydown event that makes Ctrl+H open find+replace dialog */ checkCtrlH(codeInput, event) { - if (event.ctrlKey && event.key == 'h') { + if (this.hasModifier(event) && event.key == 'h') { event.preventDefault(); this.showPrompt(codeInput, true); } diff --git a/plugins/go-to-line.js b/plugins/go-to-line.js index 54182da..ec24a05 100644 --- a/plugins/go-to-line.js +++ b/plugins/go-to-line.js @@ -19,10 +19,11 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin { /** * Create a go-to-line command plugin to pass into a template - * @param {boolean} useCtrlG Should Ctrl+G be overriden for go-to-line functionality? Either way, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element)`. + * @param {boolean} useCtrlG Should Ctrl/Cmd+G be overriden for go-to-line functionality? Either way, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element)`. * @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the available keys and English text. + * @param {boolean} alwaysCtrl: if true always use Ctrl+G as the keyboard shortcut; if false use Cmd+G on Apple devices and Ctrl+G elsewhere. False highly recommended; defaults to true for backwards compatiblity. */ - constructor(useCtrlG = true, instructionTranslations = {}) { + constructor(useCtrlG = true, instructionTranslations = {}, alwaysCtrl = true) { super([]); // No observed attributes this.useCtrlG = useCtrlG; this.addTranslations(this.instructions, instructionTranslations); @@ -202,9 +203,21 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin { } } + hasModifier(event) { + if(this.alwaysCtrl) return event.ctrlKey; + // Thanks to https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform + if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") { + // Command + return event.metaKey; + } else { + // Control + return event.ctrlKey; + } + } + /* Event handler for keydown event that makes Ctrl+G open go to line dialog */ checkCtrlG(codeInput, event) { - if (event.ctrlKey && event.key == 'g') { + if (this.hasModifier(event) && event.key == 'g') { event.preventDefault(); this.showPrompt(codeInput); } diff --git a/tests/i18n-hljs.html b/tests/i18n-hljs.html index a3a83f9..70c64cc 100644 --- a/tests/i18n-hljs.html +++ b/tests/i18n-hljs.html @@ -62,8 +62,8 @@ } }), new codeInput.plugins.Autodetect(), - new codeInput.plugins.FindAndReplace(true, true, findAndReplaceTranslations), - new codeInput.plugins.GoToLine(true, goToLineTranslations), + new codeInput.plugins.FindAndReplace(true, true, findAndReplaceTranslations, false), + new codeInput.plugins.GoToLine(true, goToLineTranslations, false), new codeInput.plugins.Indent(true, 2, {"(": ")", "[": "]", "{": "}"}, true, indentTranslations), new codeInput.plugins.SelectTokenCallbacks(codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks.createClassSynchronisation("in-selection"), false, true, true, true, true, false), //new codeInput.plugins.SpecialChars(true), diff --git a/tests/i18n-prism.html b/tests/i18n-prism.html index 3697c0c..7778d02 100644 --- a/tests/i18n-prism.html +++ b/tests/i18n-prism.html @@ -61,8 +61,8 @@ popupElem.style.display = "none"; } }), - new codeInput.plugins.FindAndReplace(true, true, findAndReplaceTranslations), - new codeInput.plugins.GoToLine(true, goToLineTranslations), + new codeInput.plugins.FindAndReplace(true, true, findAndReplaceTranslations, false), + new codeInput.plugins.GoToLine(true, goToLineTranslations, false), new codeInput.plugins.Indent(true, 2, {"(": ")", "[": "]", "{": "}"}, true, indentTranslations), new codeInput.plugins.SelectTokenCallbacks(new codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks(selectBrace, deselectAllBraces), true), //new codeInput.plugins.SpecialChars(true), diff --git a/tests/tester.js b/tests/tester.js index ed5b293..b15899c 100644 --- a/tests/tester.js +++ b/tests/tester.js @@ -120,8 +120,8 @@ function beginTest(isHLJS) { } }), new codeInput.plugins.Autodetect(), - new codeInput.plugins.FindAndReplace(), - new codeInput.plugins.GoToLine(), + new codeInput.plugins.FindAndReplace(true, true, {}, false), + new codeInput.plugins.GoToLine(true, {}, false), new codeInput.plugins.Indent(true, 2), new codeInput.plugins.SelectTokenCallbacks(codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks.createClassSynchronisation("in-selection"), false, true, true, true, true, false), new codeInput.plugins.SpecialChars(true), @@ -138,8 +138,8 @@ function beginTest(isHLJS) { popupElem.style.display = "none"; } }), - new codeInput.plugins.FindAndReplace(), - new codeInput.plugins.GoToLine(), + new codeInput.plugins.FindAndReplace(true, true, {}, false), + new codeInput.plugins.GoToLine(true, {}, false), new codeInput.plugins.Indent(true, 2), new codeInput.plugins.SelectTokenCallbacks(new codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks(selectBrace, deselectAllBraces), true), new codeInput.plugins.SpecialChars(true), @@ -489,7 +489,12 @@ console.log("I've got another line!", 2 < 3, "should be true."); await waitAsync(50); // Wait for highlighting so text updates // Open dialog and get interactive elements - textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "f", "ctrlKey": true })); + // Thanks to https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform + if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "f", "metaKey": true })); + } else { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "f", "ctrlKey": true })); + } let inputBoxes = codeInputElement.querySelectorAll(".code-input_find-and-replace_dialog input"); let findInput = inputBoxes[0]; let regExpCheckbox = inputBoxes[1]; @@ -534,8 +539,11 @@ console.log("I've got another line!", 2 < 3, "should be true."); assertEqual("FindAndReplace", "Selection End on Focused Match when Dialog Exited", textarea.selectionEnd, 8); // Open replace dialog; conduct a find and replace - textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "h", "ctrlKey": true })); - findInput.value = ""; + if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "h", "metaKey": true })); + } else { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "h", "ctrlKey": true })); + } findInput.value = ""; findInput.focus(); allowInputEvents(findInput); addText(findInput, "hello"); @@ -575,33 +583,48 @@ console.log("I've got another line!", 2 < 3, "should be true."); backspace(textarea); addText(textarea, "// 7 times table\nlet i = 1;\nwhile(i <= 12) { console.log(`7 x ${i} = ${7*i}`) }\n// That's my code.\n// This is another comment\n// Another\n// Line"); - textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true })); - let lineInput = codeInputElement.querySelector(".code-input_go-to-line_dialog input"); + if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "metaKey": true })); + } else { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true })); + } let lineInput = codeInputElement.querySelector(".code-input_go-to-line_dialog input"); lineInput.value = "1"; lineInput.dispatchEvent(new KeyboardEvent("keydown", { "key": "Enter" })); lineInput.dispatchEvent(new KeyboardEvent("keyup", { "key": "Enter" })); assertEqual("GoToLine", "Line Only", textarea.selectionStart, 0); - textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true })); - lineInput.value = "3:18"; + if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "metaKey": true })); + } else { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true })); + } lineInput.value = "3:18"; lineInput.dispatchEvent(new KeyboardEvent("keydown", { "key": "Enter" })); lineInput.dispatchEvent(new KeyboardEvent("keyup", { "key": "Enter" })); assertEqual("GoToLine", "Line and Column", textarea.selectionStart, 45); - textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true })); - lineInput.value = "10"; + if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "metaKey": true })); + } else { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true })); + } lineInput.value = "10"; lineInput.dispatchEvent(new KeyboardEvent("keydown", { "key": "Enter" })); lineInput.dispatchEvent(new KeyboardEvent("keyup", { "key": "Enter" })); assertEqual("GoToLine", "Rejects Out-of-range Line", lineInput.classList.contains("code-input_go-to-line_error"), true); - textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true })); - lineInput.value = "2:12"; + if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "metaKey": true })); + } else { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true })); + } lineInput.value = "2:12"; lineInput.dispatchEvent(new KeyboardEvent("keydown", { "key": "Enter" })); lineInput.dispatchEvent(new KeyboardEvent("keyup", { "key": "Enter" })); assertEqual("GoToLine", "Rejects Out-of-range Column", lineInput.classList.contains("code-input_go-to-line_error"), true); - textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true })); - lineInput.value = "sausages"; + if(navigator.platform.startsWith("Mac") || navigator.platform === "iPhone") { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "metaKey": true })); + } else { + textarea.dispatchEvent(new KeyboardEvent("keydown", { "cancelable": true, "key": "g", "ctrlKey": true })); + } lineInput.value = "sausages"; lineInput.dispatchEvent(new KeyboardEvent("keydown", { "key": "Enter" })); lineInput.dispatchEvent(new KeyboardEvent("keyup", { "key": "Enter" })); assertEqual("GoToLine", "Rejects Invalid Input", lineInput.classList.contains("code-input_go-to-line_error"), true); From 3cc7431b4622a79d1f40b4b0a232fe4f8d8bc524 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Fri, 7 Nov 2025 15:50:16 +0000 Subject: [PATCH 02/54] Correct FindAndReplace/GoToLine dialog caret color (Fixes #203) --- plugins/find-and-replace.css | 1 + plugins/go-to-line.css | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/find-and-replace.css b/plugins/find-and-replace.css index 07c0171..c5c2901 100644 --- a/plugins/find-and-replace.css +++ b/plugins/find-and-replace.css @@ -57,6 +57,7 @@ width: 240px; height: 32px; top: -3px; font-size: large; color: #000000aa; + caret-color: currentColor; /* Don't inherit from the code-input element. */ border: 0; } diff --git a/plugins/go-to-line.css b/plugins/go-to-line.css index 91cedd6..ec35c42 100644 --- a/plugins/go-to-line.css +++ b/plugins/go-to-line.css @@ -40,6 +40,7 @@ width: 240px; height: 32px; top: -3px; font-size: large; color: #000000aa; + caret-color: currentColor; /* Don't inherit from the code-input element. */ border: 0; } From ef7834223e7756ff3db989f88cc4204e7b35da9f Mon Sep 17 00:00:00 2001 From: WebCoder49 <69071853+WebCoder49@users.noreply.github.com> Date: Fri, 7 Nov 2025 15:51:05 +0000 Subject: [PATCH 03/54] Auto Minify JS and CSS files --- plugins/find-and-replace.min.css | 2 +- plugins/go-to-line.min.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/find-and-replace.min.css b/plugins/find-and-replace.min.css index 32aa8d9..6bb7187 100644 --- a/plugins/find-and-replace.min.css +++ b/plugins/find-and-replace.min.css @@ -1 +1 @@ -.code-input_find-and-replace_find-match{color:inherit;text-shadow:none!important;background-color:#ff0!important}.code-input_find-and-replace_find-match-focused,.code-input_find-and-replace_find-match-focused *{color:#000!important;background-color:#f80!important}.code-input_find-and-replace_start-newline:before{content:"⤶"}@keyframes code-input_find-and-replace_roll-in{0%{opacity:0;transform:translateY(-34px)}to{opacity:1;transform:translateY(0)}}@keyframes code-input_find-and-replace_roll-out{0%{opacity:1;top:0}to{opacity:0;top:-34px}}.code-input_find-and-replace_dialog{background-color:#fff;border:1px solid #0004;border-radius:6px;padding:8px 6px 6px;position:absolute;top:0;right:14px;box-shadow:0 .2em 1em .2em #00000029}.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog){opacity:1;pointer-events:all;animation:.2s code-input_find-and-replace_roll-in}.code-input_find-and-replace_dialog.code-input_find-and-replace_hidden-dialog{opacity:0;pointer-events:none;animation:.2s code-input_find-and-replace_roll-out}.code-input_find-and-replace_dialog input::placeholder{font-size:80%}.code-input_find-and-replace_dialog input{color:#000a;border:0;width:240px;height:32px;font-size:large;position:relative;top:-3px}.code-input_find-and-replace_dialog input.code-input_find-and-replace_error{color:#f00a}.code-input_find-and-replace_dialog button,.code-input_find-and-replace_dialog input[type=checkbox]{cursor:pointer;appearance:none;text-align:center;color:#000;vertical-align:top;background-color:#ddd;border:0;width:min-content;margin:5px;padding:5px;font-size:22px;line-height:24px;display:inline-block}.code-input_find-and-replace_dialog input[type=checkbox].code-input_find-and-replace_case-sensitive-checkbox:before{content:"Aa"}.code-input_find-and-replace_dialog input[type=checkbox].code-input_find-and-replace_reg-exp-checkbox:before{content:".*"}.code-input_find-and-replace_dialog button:hover,.code-input_find-and-replace_dialog input[type=checkbox]:hover{background-color:#bbb}.code-input_find-and-replace_dialog input[type=checkbox]:checked{color:#fff;background-color:#222}.code-input_find-and-replace_match-description{color:#444;display:block}.code-input_find-and-replace_dialog details summary,.code-input_find-and-replace_dialog button{cursor:pointer}.code-input_find-and-replace_dialog button.code-input_find-and-replace_button-hidden{opacity:0;pointer-events:none}.code-input_find-and-replace_dialog span{float:right;text-align:center;color:#000;opacity:.6;border-radius:50%;width:24px;margin:5px;padding:5px;font-family:system-ui;font-size:22px;font-weight:500;line-height:24px;display:block}.code-input_find-and-replace_dialog span:before{content:"×"}.code-input_find-and-replace_dialog span:hover{opacity:.8;background-color:#00000018} \ No newline at end of file +.code-input_find-and-replace_find-match{color:inherit;text-shadow:none!important;background-color:#ff0!important}.code-input_find-and-replace_find-match-focused,.code-input_find-and-replace_find-match-focused *{color:#000!important;background-color:#f80!important}.code-input_find-and-replace_start-newline:before{content:"⤶"}@keyframes code-input_find-and-replace_roll-in{0%{opacity:0;transform:translateY(-34px)}to{opacity:1;transform:translateY(0)}}@keyframes code-input_find-and-replace_roll-out{0%{opacity:1;top:0}to{opacity:0;top:-34px}}.code-input_find-and-replace_dialog{background-color:#fff;border:1px solid #0004;border-radius:6px;padding:8px 6px 6px;position:absolute;top:0;right:14px;box-shadow:0 .2em 1em .2em #00000029}.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog){opacity:1;pointer-events:all;animation:.2s code-input_find-and-replace_roll-in}.code-input_find-and-replace_dialog.code-input_find-and-replace_hidden-dialog{opacity:0;pointer-events:none;animation:.2s code-input_find-and-replace_roll-out}.code-input_find-and-replace_dialog input::placeholder{font-size:80%}.code-input_find-and-replace_dialog input{color:#000a;caret-color:currentColor;border:0;width:240px;height:32px;font-size:large;position:relative;top:-3px}.code-input_find-and-replace_dialog input.code-input_find-and-replace_error{color:#f00a}.code-input_find-and-replace_dialog button,.code-input_find-and-replace_dialog input[type=checkbox]{cursor:pointer;appearance:none;text-align:center;color:#000;vertical-align:top;background-color:#ddd;border:0;width:min-content;margin:5px;padding:5px;font-size:22px;line-height:24px;display:inline-block}.code-input_find-and-replace_dialog input[type=checkbox].code-input_find-and-replace_case-sensitive-checkbox:before{content:"Aa"}.code-input_find-and-replace_dialog input[type=checkbox].code-input_find-and-replace_reg-exp-checkbox:before{content:".*"}.code-input_find-and-replace_dialog button:hover,.code-input_find-and-replace_dialog input[type=checkbox]:hover{background-color:#bbb}.code-input_find-and-replace_dialog input[type=checkbox]:checked{color:#fff;background-color:#222}.code-input_find-and-replace_match-description{color:#444;display:block}.code-input_find-and-replace_dialog details summary,.code-input_find-and-replace_dialog button{cursor:pointer}.code-input_find-and-replace_dialog button.code-input_find-and-replace_button-hidden{opacity:0;pointer-events:none}.code-input_find-and-replace_dialog span{float:right;text-align:center;color:#000;opacity:.6;border-radius:50%;width:24px;margin:5px;padding:5px;font-family:system-ui;font-size:22px;font-weight:500;line-height:24px;display:block}.code-input_find-and-replace_dialog span:before{content:"×"}.code-input_find-and-replace_dialog span:hover{opacity:.8;background-color:#00000018} \ No newline at end of file diff --git a/plugins/go-to-line.min.css b/plugins/go-to-line.min.css index 2aa8e76..8f0f283 100644 --- a/plugins/go-to-line.min.css +++ b/plugins/go-to-line.min.css @@ -1 +1 @@ -@keyframes code-input_go-to-line_roll-in{0%{opacity:0;transform:translateY(-34px)}to{opacity:1;transform:translateY(0)}}@keyframes code-input_go-to-line_roll-out{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-34px)}}.code-input_go-to-line_dialog{background-color:#fff;border:1px solid #0004;border-radius:6px;padding:8px 6px 6px;position:absolute;top:0;right:14px;box-shadow:0 .2em 1em .2em #00000029}.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog){opacity:1;pointer-events:all;animation:.2s code-input_go-to-line_roll-in}.code-input_go-to-line_dialog.code-input_go-to-line_hidden-dialog{opacity:0;pointer-events:none;animation:.2s code-input_go-to-line_roll-out}.code-input_go-to-line_dialog input::placeholder{font-size:80%}.code-input_go-to-line_dialog input{color:#000a;border:0;width:240px;height:32px;font-size:large;position:relative;top:-3px}.code-input_go-to-line_dialog input.code-input_go-to-line_error{color:#f00a}.code-input_go-to-line_dialog span{text-align:center;color:#000;opacity:.6;vertical-align:top;border-radius:50%;width:24px;font-family:system-ui;font-size:22px;font-weight:500;line-height:24px;display:inline-block}.code-input_go-to-line_dialog span:before{content:"×"}.code-input_go-to-line_dialog span:hover{opacity:.8;background-color:#00000018}.code-input_go-to-line_dialog p{width:264px;white-space:wrap;margin:0;font-family:monospace;overflow:hidden} \ No newline at end of file +@keyframes code-input_go-to-line_roll-in{0%{opacity:0;transform:translateY(-34px)}to{opacity:1;transform:translateY(0)}}@keyframes code-input_go-to-line_roll-out{0%{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-34px)}}.code-input_go-to-line_dialog{background-color:#fff;border:1px solid #0004;border-radius:6px;padding:8px 6px 6px;position:absolute;top:0;right:14px;box-shadow:0 .2em 1em .2em #00000029}.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog){opacity:1;pointer-events:all;animation:.2s code-input_go-to-line_roll-in}.code-input_go-to-line_dialog.code-input_go-to-line_hidden-dialog{opacity:0;pointer-events:none;animation:.2s code-input_go-to-line_roll-out}.code-input_go-to-line_dialog input::placeholder{font-size:80%}.code-input_go-to-line_dialog input{color:#000a;caret-color:currentColor;border:0;width:240px;height:32px;font-size:large;position:relative;top:-3px}.code-input_go-to-line_dialog input.code-input_go-to-line_error{color:#f00a}.code-input_go-to-line_dialog span{text-align:center;color:#000;opacity:.6;vertical-align:top;border-radius:50%;width:24px;font-family:system-ui;font-size:22px;font-weight:500;line-height:24px;display:inline-block}.code-input_go-to-line_dialog span:before{content:"×"}.code-input_go-to-line_dialog span:hover{opacity:.8;background-color:#00000018}.code-input_go-to-line_dialog p{width:264px;white-space:wrap;margin:0;font-family:monospace;overflow:hidden} \ No newline at end of file From e6df781b0b54abffd06a73ffc6b0b42d60595587 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sat, 15 Nov 2025 01:42:51 +0000 Subject: [PATCH 04/54] Use new website demo links --- plugins/README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/README.md b/plugins/README.md index 67debcb..ae4d9b6 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -13,49 +13,49 @@ is activated for. Files: [auto-close-brackets.js](./auto-close-brackets.js) -[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/qBgGGKR) +[🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-auto-close-brackets) ### Autocomplete Display a popup under the caret using the text in the code-input element. This works well with autocomplete suggestions. Files: [autocomplete.js](./autocomplete.js) / [autocomplete.css](./autocomplete.css) -[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/xxapjXB) +[🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-autocomplete) ### Autodetect Autodetect the language live and change the `lang` attribute using the syntax highlighter's autodetect capabilities. Works with highlight.js. Files: [autodetect.js](./autodetect.js) -[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/eYLyMae) +[🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-autodetect) ### Find and Replace Add Find-and-Replace (Ctrl+F for find, Ctrl+H for replace by default, or when JavaScript triggers it) functionality to the code editor. Files: [find-and-replace.js](./find-and-replace.js) / [find-and-replace.css](./find-and-replace.css) -[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/oNVVBBz) +[🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-find-and-replace) ### Go To Line Add a feature to go to a specific line when a line number is given (or column as well, in the format line no:column no) that appears when (optionally) Ctrl+G is pressed or when JavaScript triggers it. Files: [go-to-line.js](./go-to-line.js) / [go-to-line.css](./go-to-line.css) -[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/YzBMOXP) +[🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-go-to-line) ### Indent Add indentation using the `Tab` key, and auto-indents after a newline, as well as making it possible to indent/unindent multiple lines using Tab/Shift+Tab. **Supports tab characters and custom numbers of spaces as indentation, as well as (optionally) brackets typed affecting indentation.** Files: [indent.js](./indent.js) -[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/WNgdzar) +[🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-indent) ### Prism Line Numbers Allow code-input elements to be used with the Prism.js line-numbers plugin, as long as the code-input element or a parent element of it has the CSS class `line-numbers`. [Prism.js Plugin Docs](https://prismjs.com/plugins/line-numbers/) Files: [prism-line-numbers.css](./prism-line-numbers.css) (NO JS FILE) -[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/XWPVrWv) +[🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-prism-line-numbers) ### Special Chars Render special characters and control characters as a symbol @@ -65,14 +65,14 @@ with their hex code. Files: [special-chars.js](./special-chars.js) / [special-chars.css](./special-chars.css) -[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/jOeYJbm) +[🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-special-chars) ### Select Token Callbacks Make tokens in the `
` element that are included within the selected text of the `` gain a CSS class while selected, or trigger JavaScript callbacks.
 
 Files: select-token-callbacks.js
 
-[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/WNVZXxM)
+[🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-select-token-callbacks)
 
 ## Using Plugins
 Plugins allow you to add extra features to a template, like [automatic indentation](./indent.js) or [support for highlight.js's language autodetection](./autodetect.js). To use them, just:

From c0631958b31212f3e374302fe3a82ac0db713f92 Mon Sep 17 00:00:00 2001
From: Oliver Geer 
Date: Sat, 15 Nov 2025 01:45:40 +0000
Subject: [PATCH 05/54] Add note about README animation

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/README.md b/README.md
index c974739..e1b7052 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,8 @@ Demos and usage instructions are available at  and [t
 
 ---
 
+A lightweight setup like the animation below is still the default for `code-input.js`, but a wide range of plugins are available to modularly and progressively enhance the experience - see [the website](https://code-input-js.org) for interactive demos!
+
 ![Using code-input with many different themes](https://user-images.githubusercontent.com/69071853/133924472-05edde5c-23e7-4350-a41b-5a74d2dc1a9a.gif)
 *This demonstration uses themes from [Prism.js](https://prismjs.com/) and [highlight.js](https://highlightjs.org/), two syntax-highlighting programs which work well with and have compatibility built-in with code-input.*
 

From b82231895964b73bfe7546e4301d3d929836a4e5 Mon Sep 17 00:00:00 2001
From: Oliver Geer 
Date: Fri, 7 Nov 2025 13:28:04 +0000
Subject: [PATCH 06/54] Document recommended Ctrl/Cmd interoperability for
 FindAndReplace and GoToLine

---
 docs/plugins/_index.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/docs/plugins/_index.md b/docs/plugins/_index.md
index 23deee2..e3b538e 100644
--- a/docs/plugins/_index.md
+++ b/docs/plugins/_index.md
@@ -146,6 +146,8 @@ Right now, you can only add one plugin of each type (e.g. one SelectTokenCallbac
             let findAndReplacePlugin = new codeInput.plugins.FindAndReplace(
                 true, // Should Ctrl+F be overriden for find-and-replace find functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, false)`.
                 true, // Should Ctrl+H be overriden for find-and-replace replace functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, true)`.
+                {}, // Keep this as an empty object for the English UI, or add translations as in https://code-input-js.org/i18n/
+                false // Should Ctrl key be forced for keyboard shortcuts; setting this to false makes the keyboard shortcut follow the operating system (i.e. Cmd on Apple).
             );
             // Programatically opening the dialogs, to integrate with your user interface
             function find() {
@@ -196,6 +198,8 @@ Hickory dickory dock.
         
-        

When focused in the editor: Try Ctrl+F, or click to find. Try Ctrl+H, or click to replace.

+

When focused in the editor: Try Ctrl/Cmd+F, or click to find. Try Ctrl+H, or click to replace.

# Hickory dickory dock Hickory dickory dock. The mouse ran up the clock. From 15eab9985bd82910d73b7a462ad795bf3494be3b Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sat, 15 Nov 2025 17:26:19 +0000 Subject: [PATCH 11/54] Release v2.7.2 --- docs/_index.md | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/_index.md b/docs/_index.md index 7fc6e17..9162b6a 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -13,7 +13,7 @@ more use cases. ## Download -*code-input.js is free, libre, open source software under the MIT (AKA Expat) license.* **Download it [from the Git repository](https://github.com/WebCoder49/code-input/tree/v2.7.1), [in a ZIP archive](/release/code-input-js-v2.7.1.zip), [in a TAR.GZ archive](/release/code-input-js-v2.7.1.tar.gz), or from `@webcoder49/code-input` on the NPM registry ([Yarn](https://yarnpkg.com/package?name=@webcoder49/code-input), [NPM](https://npmjs.com/package/@webcoder49/code-input), etc.).** +*code-input.js is free, libre, open source software under the MIT (AKA Expat) license.* **Download it [from the Git repository](https://github.com/WebCoder49/code-input/tree/v2.7.2), [in a ZIP archive](/release/code-input-js-v2.7.2.zip), [in a TAR.GZ archive](/release/code-input-js-v2.7.2.tar.gz), or from `@webcoder49/code-input` on the NPM registry ([Yarn](https://yarnpkg.com/package?name=@webcoder49/code-input), [NPM](https://npmjs.com/package/@webcoder49/code-input), etc.).** [Want to contribute to the code? You're very welcome to! See here.](#contributing) diff --git a/package.json b/package.json index fcabbe6..6e89cc9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@webcoder49/code-input", - "version": "2.7.1", + "version": "2.7.2", "description": "An editable <textarea> that supports *any* syntax highlighting algorithm, for code or something else. Also, added plugins.", "browser": "code-input.js", "exports": { @@ -97,7 +97,7 @@ "author": { "name": "Oliver Geer and contributors", "email": "hi@webcoder49.dev", - "url": "https://oliver.geer.im/" + "url": "https://ogeer.org/" }, "license": "MIT", "bugs": { From fdb2c26e855ef479e72246a94643c1afb1f0112e Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sat, 15 Nov 2025 18:39:27 +0000 Subject: [PATCH 12/54] Fix link to website in docs directory --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index 07d0fb7..2d534d2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@ # `docs` -The content of this directory is the Hugo content source of [code-input-js.org](code-input-js.org). Most code in curly braces {} is relied on by the website setup. +The content of this directory is the Hugo content source of [code-input-js.org](https://code-input-js.org). Most code in curly braces {} is relied on by the website setup. The homepage can be found at [_index.md](_index.md). From 6737d3c77f08179f7dda45938b4099be68b511a2 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sat, 29 Nov 2025 14:19:30 +0000 Subject: [PATCH 13/54] Remove redundant block styling of code-input element since display: grid present --- code-input.css | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/code-input.css b/code-input.css index 1023209..a6de98b 100644 --- a/code-input.css +++ b/code-input.css @@ -13,7 +13,9 @@ code-input { /* Allow other elements to be inside */ - display: block; + display: grid; + grid-template-columns: 100%; + grid-template-rows: 100%; overflow-y: auto; overflow-x: auto; position: relative; @@ -46,9 +48,6 @@ code-input { tab-size: 2; white-space: pre; padding: 0!important; /* Use --padding to set the code-input element's padding */ - display: grid; - grid-template-columns: 100%; - grid-template-rows: 100%; } code-input :not(.code-input_dialog-container *) { From fea11e397d4aa453950b099fd406b501df125f01 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sun, 30 Nov 2025 02:18:31 +0000 Subject: [PATCH 14/54] Work in Progress (nearly complete; needs docs and testing): Add autogrow plugin --- code-input.js | 9 +++-- docs/interface/css/_index.md | 8 +++++ docs/interface/css/resize-both-screenshot.png | Bin 0 -> 5931 bytes plugins/autogrow.css | 32 ++++++++++++++++++ 4 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 docs/interface/css/resize-both-screenshot.png create mode 100644 plugins/autogrow.css diff --git a/code-input.js b/code-input.js index 0d9d646..13dbe02 100644 --- a/code-input.js +++ b/code-input.js @@ -555,8 +555,13 @@ var codeInput = { */ syncSize() { // Synchronise the size of the pre/code and textarea elements - this.textareaElement.style.height = getComputedStyle(this.getStyledHighlightingElement()).height; - this.textareaElement.style.width = getComputedStyle(this.getStyledHighlightingElement()).width; + const height = getComputedStyle(this.getStyledHighlightingElement()).height; + this.textareaElement.style.height = height; + this.textareaElement.style.setProperty("--code-input_synced-height", height); + + const width = getComputedStyle(this.getStyledHighlightingElement()).width; + this.textareaElement.style.width = width; + this.textareaElement.style.setProperty("--code-input_synced-width", width); } /** diff --git a/docs/interface/css/_index.md b/docs/interface/css/_index.md index fc02a7c..e37108d 100644 --- a/docs/interface/css/_index.md +++ b/docs/interface/css/_index.md @@ -14,3 +14,11 @@ title = 'Styling `code-input` elements with CSS' * For now, elements on top of `code-input` elements should have a CSS `z-index` at least 3 greater than the `code-input` element. Please do **not** use `className` in JavaScript referring to code-input elements, because the code-input library needs to add its own classes to code-input elements for easier progressive enhancement. You can, however, use `classList` and `style` as much as you want - it will make your code cleaner anyway. + +## Methods of resizing + +`code-input` elements default to having a fixed height and filling the width of their container while the code inside scrolls, and you can set the ordinary CSS properties (for example `height` and `width`) to change this size. You can also make the size more flexible using CSS: + +* The [CSS `resize` property (see link)](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/resize) can be used on `code-input` elements to give them the manual resizing handle `textarea`s often come with, when the web browser supports it. For example, `SgecLXw+)i$1S_ix ztG88Fzx%)Ux$lSf+xzAHFu$2O^URzx=b3qa=bTub=js$>Ok@B6fI{P`%1Z!%Kng#8 zPD+f&22Zj(`0gJ3sj(*jK>6c8jUY{sk{K_g_EI(Sa(Qd-Xl>F9Bmoa5=u!@Y>qM`^`)n#v5l&`U(poaxS&J$Ct%ao^lRg&JX-#8rwKh@)^^$K zV;byQb?J2Z%6$7iVOqHUdv-7pAwf$FX&C&MH_tDwluT*NRi46#4MGb)3nR1--Jkg| zGbn98l38QVvxx7tX_QF0G!QS7m=qKdT>Sb!!&A8UZGZhYZXVuk(FzD z=qPD0nDH_G5am3K;MPD8o3~2B@IntdeMCtrH~I7b&**JTpJO{4$vM9`)?eqUw7fUq z%N1E5PM>g1uCDciyZND!6z%fc&5e~b5cJ5JXe$CTx_CFyLP=&fx%^wX>}WFM@t==2yAR|Jvey+nkl zUQG&Hti!10)GYRDak>aDZmXZ<)!d9LDXYBxSA}h>>I6&9z=O?9?A+yA-(fNBhjdh8 zo?qmU7Zs+$>HO?<$M5R911)aTsm;7+`BscqyIUlr#7wK|=4QXSU34^_xhq@1Kf}(T zEhR}-3t}#n+v^Jp%DjAxP};=8@eJ6D7?V4UaeY5?w2nMt+^_!38ud(FFwN?;rlh3l zmsj-OPl+?~KzjR`5d#>Ug1%lOeVl?nZe*$Tpkxi^r!8uQyRZal3{HoV#JKS-ea7eq z%rmsW!=Vq`)`_khq+%rgS3T%ogWh{n;!;6RqcUtL|yT9rEqhO1c)E;Qs#f{5dU|x6Lc~>Do&k%3b zlOqV>v`WaYDB-4G$71bt_4Gd$37A2x$zZ2Jw1QHDb&Pqt++!O9W8ypv7;ClOZ~igi z!;zeJJMQXA?@@Nn`c;V^Kf`eA6=-x*&TO4EThw`RrVb>l0*lcV|9D7bEgiI?l@YE- zuUb8&F#!M?_slj8r$4-G!Az^(8>FzjR3~XU=_d*nl6-f2vP+^EhIxG}R~(J$C#z`f zOztnWyV=ZSkdaeBXA``us0gxN+r1hfcQ(2dif770W-yEvTWVR9VR%>jPE4C^$4}x% zhI{LBlw?iK2U;p*LS9MRSX<|hF8RyXbMW0Xo032{NM=-aoLRZemBx z5q6`Jk+{P%*3TkiLwR=xa8ujaxa=A*=lrT$ekq9(X-M0tTpW2neD^EA(ZlH*v(}N` z?dp}&Y=)*61$>|EBH^{4gBjtIz0N5i+8T&fCWN_HlPmUjbk*EVU(|-ligv9zNrLaYP(icG%l`MxzAr4_&S;h5lSL@Aea++|O(BM53-^2Fq(Iw+}8T8VGYdb{!VM*tcgZOt!8*4&_P{ZOC*AyP)k3PwLMxuiI6sYLa#R zTZX#qc)CKa%^!?(&>|sH{_A&GzxNorwdXzk@o$r12I~3S&!bNCi=`{xGI$%XI#)a` zIOfH{wxomzCQbv@G`vh#mKl2DRjU1XhcUP7cJu8&cQ`@}p!^bU>dUrNv6%vS#ckE& z$~QlJD%5dt)szH^A)_jsQ0c18;*88?jxyB^$4#^S zpOPWTI?g*Mt5GI*a(&Vn-KbjK*r|<5V?mVO{FnICOib^*581IvE_s!{=bga6J6yn! z0}#s~fw9ENZ)uT+Z9P-|_6X%OW4(%}Oa!?3$)burJ&{$k#w)j_c*C#oE=r1IE{!dR zF>*?dR+qTGlrjHL5iM zSx@zXugXiLKzTjiPim|RKzFW(;WAmvZ;6wvC`_hCV+sF`%I%SJ`!N{~Lvc+MTu5dI zhLGna{so|ru`|8kqJ}N;yMtpd!z$MugV;j47D2>FTV446^`W(Z@Si>^;v6V*$l2+G zUONxfi3z5a+=<*EkM{7p1A@5g>G`Xj+p68u3)kz<6-+ZjIdOl7`|b#_GB-L>RWK=j z+bU3q8H?N=Gw~_fpmr*cW6O?A0Bv3R+Ns6>mkI22s#*zbP*FvAo7A5Z*LQ}#mROc) zNS_~H(|M>MaZkc&b?vQp9)t4^CGuU?cCdA9V6w6zA1G?`_J2!ainwBkc*`@!+F3CVX+>h0f(pnCiq& zJn6Q@d}LXrMb5h!+*In1PYoq;idJe4*2kgklcvwF33+KIHe>MU*dPp-B% z#7EzJkujJCUk@FG>VE0oAMFi1v2lU*9Cz3`m{MN>YDv%4W}e5 zMgKeaPQlfXzeUL9U8O<@ADJ=8F=(|D@4pFvRM3jpTbC=y=>ncHa0|-5O&x^Wi;F!i zTQc}};nTsZ?EL<9R}=p!s>!utn^|T)rO*o#(zNUG{Fp%r;^xIlwdn0xMo!45tqw`3 zR>R_Sfe+Dlj)MrvNHHgoU_}&r_wm9zbDXzM(V}s$lHlb%wMm&jUJvP`bUu*FdD5fq zjODC3lILj0i}iBf0yeo_X>x$-2F0H#*pY|%mV`1kq^)DnfZqq!PuUh9aAYl`9ULh% z?2IDKagCIqA&2M_25F=eW@bn;=}FRao8Cl@DpV$`P8Ov)RE#In%3VzPxSn6DVfB`T z^T6O1D#q#|pMw86H0DiZPTn?X30{Gb!;{O-oZ5CgWv~cTF72}h0+J8VP4?-w?j=|B z;@%$E2{upHI|S~jGFt)JkzH0|{kBiH z!OGlwO&*R_UZVd%d?t0Erlho6oxte2vLaCXx3aUmWtYHcOYXmUfn!Hz?jQl{6xdzI zQ8<^IbZR3wM&9nHuunhHs7gje8x`npdc=d_11_~y=iniDwP$q4;AOtmd!S>F>))mr zd>jRmdt=l)^)HbsLl5FZUs0R$HdfxDjW=S@w$|qGL`!I7K$MdOtPr8d!3eAi9$?jB z2M=qX^J5a#bm`~Z7}m-JK(I&IIbXFz<F_6}{!n*tsA|uQ-mJw<#Zxy5KXZ zp4NTF(AQ_xPYc~0RKYl@nvZe149gyT`ilyc5;D(DKUrM^qg;lLiB7Cu1=zZ*t=6=C86SgLA z)_lFb{M*HN&Tf$^q zL?K_&y68ArjgoMuV3W(^$@q@w(sSMVdYR|bv4gej(1&O7HSd;*IBKIl-WMSV^UxK= z1&kJ)q;llgM9*&Y`SOeuP>Cti4)!<Nm2U-#oRS1TR!y58ywK=FLx*$Mwzxr#vh!{kh2_6RM#- z3i4Q4@kV*Igr6Lo!x564AN74hzF-M^OB+9f05$5Xre}*`MFKl8uW*KGmw$1;`hXu6 zK9x$QyJF`AnOj2kw0Ia2cfDRj$BR)RJ)_;vUSiI=CDK9e)x0G4GSr#T4UYO{h~)1O zAJ;aa)Vwb0a0;Hk4VRE8@?<#c%7ndx*m!Bp2FKkF`>|3)@>$Cs^tf(;%>^=E&1gv+ z%mnf$C;nt*OZA^CA3NH_Gg?Lq3io<2igd*Qm5Z+pcc0jpJg9L&{l4}M`GzL?j)8+_ zhV!OtJe)ZrKNga?S@=C1?P#SzKQNQFZTFm;FD%AKSA93U~Cj2?d=s=S2=fm_qoV#_c>xT*x zh0q0GLcsu*Y4qn%H9h@jWc?89XCmG=JU)90{iz5g3zM@LqXh2!{11|xx%{ixmVkcx zkkc!fBaixTVCGQ0cj!*I+$8UVH{_BGBN>*LRH6YzTUSZwFgwLP@5SC@8{f+?U^Mp2 zY?+8pQ7o@=;bdNbDo3RyPij!uo&>9ltcDFvqO>grSop2Oo^jDM`Ve)aEN5dh?-^a2 z!Vy{&+tvjd`8eRV!LKYl$Z=uu?}ey)nQw;GVYleewyX4U5<%2Xudz;h`7OxnXYLte zDXbC=WEjlr2y6*B-NE(DGaVyUBHv*wX1n7Z=`s}pE1>KnP~pzL6HhyceZakrDvJSp zsU$()%hBa;fDXI52}bl>O(X=M+1k@dB_vVMsN0?~7|JsnDyL z@d>6!oWsf*y7G@!z6MB&e?QtZ#wGJDi`qr%oKsP z1uPwvcs5leWXZ5);nSQH)rDGMZSLyIoFj;^^2M@39qZtCa^)||`r3pt&Cd=-uI1x+ zza!^hlN1bDWG68S1t&vlsXEfF({V*8lueRB9oY;nJNB|W7rPHvLYG)l}%ePVzQ+|F`BJvFw^USfTods&62Az943Yy3rMSlJbSq?Gl`(r5Hi*}DyeF&&3jV#SXPHicL_eEIE~w@(95 zV*bm+!(Xp{GaPtHbv`zZE3#~sRq3tD?bJ4Nqe@?!dq%A|B_=k{&tio(vg0}p(x223 zr%=_UQLcNprL)uC>w7WLx0c7jLN=9p9jVU!c&CMHzl@)s%*B5>QDKmTAMMSxjN$rP+_s{Cz^abZ! z9CP*csoL0Ze{Yi7fPo@i&y3s@j}wgQ=Khi{o2>`4vz4HZuXAFCmXbg$44lfM3ub2z zC~k-is*QS1slH=J_9cA;W)^_Io)c>zN*1p6)(pqRKCTV!TVdD1sTmnq)YjV*M?Jr> znI(M274Rt1ayUy;S3C{2-yU(cfF8>H{fyQB9|t9i)L2=gnlT)~WiElw%CAk|PSOHB zM(f&U+il4igv>10;?{x=dJGlIf?>1ymYTS>iRm%KP%K z^D@2u8mZv1loBC(I6nY0Z~ZD+hu6;Jp+vf1RG%JQs%B7a)-zp&mhwBGrjms#pqpH+ z_dA1wFyE$d!SXG!0vZ$cgXF@UDpD-VH~N))V6g2>^?VQpmZ)oD@_XR&6gxk_wz`pw zls*6RB2ez8j Date: Sun, 30 Nov 2025 19:54:28 +0000 Subject: [PATCH 15/54] Create first try at CSS-driven autogrow, which works on Firefox but does not shrink again on Chromium --- code-input.js | 2 + docs/plugins/_index.md | 35 +++++++++++++++++ plugins/README.md | 7 ++++ plugins/autogrow.css | 4 +- plugins/prism-line-numbers.css | 6 +++ tests/hljs.html | 1 + tests/prism.html | 1 + tests/tester.js | 70 ++++++++++++++++++++++++++++++++++ 8 files changed, 124 insertions(+), 2 deletions(-) diff --git a/code-input.js b/code-input.js index 13dbe02..600ea6a 100644 --- a/code-input.js +++ b/code-input.js @@ -556,10 +556,12 @@ var codeInput = { syncSize() { // Synchronise the size of the pre/code and textarea elements const height = getComputedStyle(this.getStyledHighlightingElement()).height; + this.textareaElement.style.height = 0; this.textareaElement.style.height = height; this.textareaElement.style.setProperty("--code-input_synced-height", height); const width = getComputedStyle(this.getStyledHighlightingElement()).width; + this.textareaElement.style.width = 0; this.textareaElement.style.width = width; this.textareaElement.style.setProperty("--code-input_synced-width", width); } diff --git a/docs/plugins/_index.md b/docs/plugins/_index.md index 8728745..e7efaf9 100644 --- a/docs/plugins/_index.md +++ b/docs/plugins/_index.md @@ -124,6 +124,41 @@ Right now, you can only add one plugin of each type (e.g. one SelectTokenCallbac ``` +#### `Autogrow`: Let code-input elements resize to fit their content (optionally between limits) {#playground-preset-prism-line-numbers} + +``` + + + + + + + + + + + + + + + + +

Enter newlines to grow vertically:

+ +

Type to grow horizontally:

+ +

Grows vertically between 100px and 200px:

+ +

Grows horizontally between 100px and 200px:

+ + + +``` + #### `FindAndReplace`: Add an openable dialog to find and replace matches to a search term, including highlighting the matches {#playground-preset-find-and-replace} ``` diff --git a/plugins/README.md b/plugins/README.md index ae4d9b6..7a783b2 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -29,6 +29,13 @@ Files: [autodetect.js](./autodetect.js) [🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-autodetect) +### Autogrow +Make code-input elements resize automatically and fit their content live using CSS classes, optionally between a minimum and maximum size specified using CSS variables. + +Files: [autogrow.css](./autogrow.css) (NO JS FILE) + +[🚀 *Demo*](https://v2.code-input-js.org/plugins/#playground-preset-autogrow) + ### Find and Replace Add Find-and-Replace (Ctrl+F for find, Ctrl+H for replace by default, or when JavaScript triggers it) functionality to the code editor. diff --git a/plugins/autogrow.css b/plugins/autogrow.css index ba46258..f312ae3 100644 --- a/plugins/autogrow.css +++ b/plugins/autogrow.css @@ -15,7 +15,7 @@ code-input.code-input_autogrow_height { max-height: var(--code-input_autogrow_max-height); } code-input.code-input_autogrow_height textarea { - height: var(--code-input_autogrow_min-height)!important; /* So minimum height possible while containing highlighted code */ + height: calc(var(--code-input_autogrow_min-height) - var(--padding-top, 16px) - var(--padding-bottom, 16px))!important; /* So minimum height possible while containing highlighted code */ min-height: max(var(--code-input_synced-height), calc(100% - var(--padding-top, 16px) - var(--padding-bottom, 16px))); } @@ -27,6 +27,6 @@ code-input.code-input_autogrow_width { } code-input.code-input_autogrow_width textarea { - width: var(--code-input_autogrow_min-width)!important; /* So minimum width possible while containing highlighted code */ + width: calc(var(--code-input_autogrow_min-width) - var(--padding-left, 16px) - var(--padding-right, 16px))!important; /* So minimum width possible while containing highlighted code */ min-width: max(var(--code-input_synced-width), calc(100% - var(--padding-left, 16px) - var(--padding-right, 16px))); } diff --git a/plugins/prism-line-numbers.css b/plugins/prism-line-numbers.css index 30a3f00..3304075 100644 --- a/plugins/prism-line-numbers.css +++ b/plugins/prism-line-numbers.css @@ -33,3 +33,9 @@ code-input .line-numbers .line-numbers-rows { code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused) .line-numbers .line-numbers-rows { top: calc(var(--padding-top) + 3em); } + +/* Compatibility with autogrow plugin */ +code-input.line-numbers.code-input_autogrow_width textarea, .line-numbers code-input.code-input_autogrow_width textarea { + width: calc(var(--code-input_autogrow_min-width) - max(3.8em, var(--padding-left, 16px)) - var(--padding-right, 16px))!important; /* So minimum width possible while containing highlighted code */ + min-width: max(var(--code-input_synced-width), calc(100% - max(3.8em, var(--padding-left, 16px)) - var(--padding-right, 16px))); +} diff --git a/tests/hljs.html b/tests/hljs.html index 25bf95a..179e207 100644 --- a/tests/hljs.html +++ b/tests/hljs.html @@ -22,6 +22,7 @@ + diff --git a/tests/prism.html b/tests/prism.html index bfc6906..787216c 100644 --- a/tests/prism.html +++ b/tests/prism.html @@ -21,6 +21,7 @@ + diff --git a/tests/tester.js b/tests/tester.js index fae0ad0..c1c60c2 100644 --- a/tests/tester.js +++ b/tests/tester.js @@ -478,6 +478,76 @@ console.log("I've got another line!", 2 < 3, "should be true."); assertEqual("Autodetect", "Detects CSS", codeInputElement.getAttribute("language"), "css"); } + // Autogrow + // Replace all code + textarea.selectionStart = 0; + textarea.selectionEnd = textarea.value.length; + backspace(textarea); + textarea.parentElement.classList.add("code-input_autogrow_height"); + await waitAsync(100); // Wait for height to update + + const emptyHeight = textarea.parentElement.clientHeight; + addText(textarea, "// a\n// b\n// c\n// d\n// e\n// f\n// g"); + await waitAsync(100); // Wait for height to update + + const fullHeight = textarea.parentElement.clientHeight; + testAssertion("Autogrow", "Content Increases Height", fullHeight > emptyHeight, `${fullHeight} should be > ${emptyHeight}`); + textarea.parentElement.style.setProperty("font-size", "50%"); + await waitAsync(200); // Wait for height to update + + testAssertion("Autogrow", "font-size Decrease Decreases Height", textarea.parentElement.clientHeight < fullHeight, `${textarea.parentElement.clientHeight} should be < ${fullHeight}`); + textarea.parentElement.style.setProperty("font-size", "100%"); + textarea.parentElement.style.removeProperty("font-size"); + textarea.parentElement.style.setProperty("--code-input_autogrow_min-height", (fullHeight + 10) + "px"); + textarea.blur(); // Prevent focus instruction bar from affecting height + await waitAsync(200); // Wait for height to update + + assertEqual("Autogrow", "--code-input_autogrow_min-height Sets Height", textarea.parentElement.clientHeight, fullHeight + 10); + textarea.focus(); // Enable inserting text again + textarea.parentElement.style.removeProperty("--code-input_autogrow_min-height"); + textarea.parentElement.style.setProperty("--code-input_autogrow_max-height", (fullHeight - 10) + "px"); + await waitAsync(100); // Wait for height to update + + assertEqual("Autogrow", "--code-input_autogrow_max-height Sets Height", textarea.parentElement.clientHeight, fullHeight - 10); + textarea.parentElement.style.removeProperty("--code-input_autogrow_max-height"); + textarea.parentElement.style.removeProperty("--code-input_autogrow_max-height"); + await waitAsync(100); // Wait for height to update + + textarea.selectionStart = 0; + textarea.selectionEnd = textarea.value.length; + backspace(textarea); + textarea.parentElement.classList.add("code-input_autogrow_width"); + await waitAsync(100); // Wait for width to update + + const emptyWidth = textarea.parentElement.clientWidth; + addText(textarea, "// A very very very very extremely vastly very very very very long line of code is written here in this very comment; yes, this very comment!"); + await waitAsync(100); // Wait for width to update + + const fullWidth = textarea.parentElement.clientWidth; + testAssertion("Autogrow", "Content Increases Width", fullWidth > emptyWidth, `${fullWidth} should be > ${emptyWidth}`); + textarea.parentElement.style.setProperty("font-size", "50%"); + await waitAsync(200); // Wait for width to update + + testAssertion("Autogrow", "font-size Decrease Decreases Width", textarea.parentElement.clientWidth < fullWidth, `${textarea.parentElement.clientWidth} should be < ${fullWidth}`); + textarea.parentElement.style.setProperty("font-size", "100%"); + textarea.parentElement.style.removeProperty("font-size"); + textarea.parentElement.style.setProperty("--code-input_autogrow_min-width", (fullWidth + 10) + "px"); + await waitAsync(200); // Wait for width to update + + assertEqual("Autogrow", "--code-input_autogrow_min-width Sets Width", textarea.parentElement.clientWidth, fullWidth + 10); + textarea.parentElement.style.removeProperty("--code-input_autogrow_min-width"); + textarea.parentElement.style.setProperty("--code-input_autogrow_max-width", (fullWidth - 10) + "px"); + await waitAsync(100); // Wait for width to update + + assertEqual("Autogrow", "--code-input_autogrow_max-width Sets Width", textarea.parentElement.clientWidth, fullWidth - 10); + textarea.parentElement.style.removeProperty("--code-input_autogrow_max-width"); + textarea.parentElement.style.removeProperty("--code-input_autogrow_max-width"); + + textarea.parentElement.classList.remove("code-input_autogrow_height"); + textarea.parentElement.classList.remove("code-input_autogrow_width"); + + // TODO add reaons to above testAssertions; check why min-width failing; manual tests to check properly fits; prompt tests; fix no-content hljs.html autogrow-both-ways sizing; test with unregistered. + // FindAndReplace // Replace all code textarea.selectionStart = 0; From d8733279107debb209ed7918d86335f334ed0201 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sun, 7 Dec 2025 17:22:11 +0000 Subject: [PATCH 16/54] Fix and clean core color-syncing code (Fixes #208, #207) --- code-input.js | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/code-input.js b/code-input.js index 0d9d646..491183a 100644 --- a/code-input.js +++ b/code-input.js @@ -561,13 +561,15 @@ var codeInput = { /** * If the color attribute has been defined on the - * code-input element by external code, return true. + * code-input element by external code, run the callback + * provided. * Otherwise, make the aspects the color affects * (placeholder and caret colour) be the base colour - * of the highlighted text, for best contrast, and - * return false. + * of the highlighted text, for best contrast. */ - isColorOverridenSyncIfNot() { + syncIfColorNotOverridden(callbackIfOverridden=function() {}) { + if(this.checkingColorOverridden) return; + this.checkingColorOverridden = true; const oldTransition = this.style.transition; this.style.transition = "unset"; window.requestAnimationFrame(() => { @@ -578,20 +580,28 @@ var codeInput = { if(getComputedStyle(this).color == "rgb(255, 255, 255)") { // Definitely not overriden this.internalStyle.removeProperty("--code-input_no-override-color"); + console.log(this, "Autoadapt; " + oldTransition); this.style.transition = oldTransition; const highlightedTextColor = getComputedStyle(this.getStyledHighlightingElement()).color; this.internalStyle.setProperty("--code-input_highlight-text-color", highlightedTextColor); this.internalStyle.setProperty("--code-input_default-caret-color", highlightedTextColor); - return false; + this.checkingColorOverridden = false; + } else { + this.style.transition = oldTransition; + this.checkingColorOverridden = false; + callbackIfOverridden(); } + } else { + this.style.transition = oldTransition; + this.checkingColorOverridden = false; + callbackIfOverridden(); } this.internalStyle.removeProperty("--code-input_no-override-color"); + console.log(this, "No autoadapt; " + oldTransition); this.style.transition = oldTransition; }); - - return true; } /** @@ -603,11 +613,11 @@ var codeInput = { */ syncColorCompletely() { // color of code-input element - if(this.isColorOverridenSyncIfNot()) { + this.syncIfColorNotOverridden(() => { // color overriden this.internalStyle.removeProperty("--code-input_highlight-text-color"); this.internalStyle.setProperty("--code-input_default-caret-color", getComputedStyle(this).color); - } + }); } @@ -847,7 +857,6 @@ var codeInput = { resizeObserver.observe(this); - // Add internal style as non-externally-overridable alternative to style attribute for e.g. syncing color this.classList.add("code-input_styles_" + codeInput.stylesheetI); const stylesheet = document.createElement("style"); stylesheet.innerHTML = "code-input.code-input_styles_" + codeInput.stylesheetI + " {}"; @@ -858,7 +867,7 @@ var codeInput = { // Synchronise colors const preColorChangeCallback = (evt) => { if(evt.propertyName == "color") { - this.isColorOverridenSyncIfNot(); + this.syncIfColorNotOverridden(); } }; this.preElement.addEventListener("transitionend", preColorChangeCallback); From b760a169fbbb1755c4119dd58461735714c8f60d Mon Sep 17 00:00:00 2001 From: WebCoder49 <69071853+WebCoder49@users.noreply.github.com> Date: Sun, 7 Dec 2025 17:57:17 +0000 Subject: [PATCH 17/54] Auto Minify JS and CSS files --- code-input.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code-input.min.js b/code-input.min.js index 9c07f6f..2888c16 100644 --- a/code-input.min.js +++ b/code-input.min.js @@ -9,4 +9,4 @@ * @license MIT * * **** - */"use strict";var codeInput={observedAttributes:["value","placeholder","language","lang","template"],textareaSyncAttributes:["value","min","max","type","pattern","autocomplete","autocorrect","autofocus","cols","dirname","disabled","form","maxlength","minlength","name","placeholder","readonly","required","rows","spellcheck","wrap"],textareaSyncEvents:["change","selectionchange","invalid","input","focus","blur","focusin","focusout"],usedTemplates:{},defaultTemplate:void 0,templateNotYetRegisteredQueue:{},registerTemplate:function(a,b){if(!("string"==typeof a||a instanceof String))throw TypeError(`code-input: Name of template "${a}" must be a string.`);if(!("function"==typeof b.highlight||b.highlight instanceof Function))throw TypeError(`code-input: Template for "${a}" invalid, because the highlight function provided is not a function; it is "${b.highlight}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.includeCodeInputInHighlightFunc||b.includeCodeInputInHighlightFunc instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the includeCodeInputInHighlightFunc value provided is not a true or false; it is "${b.includeCodeInputInHighlightFunc}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.preElementStyled||b.preElementStyled instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the preElementStyled value provided is not a true or false; it is "${b.preElementStyled}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.isCode||b.isCode instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the isCode value provided is not a true or false; it is "${b.isCode}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!Array.isArray(b.plugins))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin array provided is not an array; it is "${b.plugins}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(b.plugins.forEach((c,d)=>{if(!(c instanceof codeInput.Plugin))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin provided at index ${d} is not valid; it is "${b.plugins[d]}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`)}),codeInput.usedTemplates[a]=b,a in codeInput.templateNotYetRegisteredQueue)for(let c in codeInput.templateNotYetRegisteredQueue[a]){const d=codeInput.templateNotYetRegisteredQueue[a][c];d.templateObject=b,d.setup()}if(null==codeInput.defaultTemplate&&(codeInput.defaultTemplate=a,void 0 in codeInput.templateNotYetRegisteredQueue))for(let a in codeInput.templateNotYetRegisteredQueue[void 0]){const c=codeInput.templateNotYetRegisteredQueue[void 0][a];c.templateObject=b,c.setup()}},stylesheetI:0,Template:class{constructor(a=function(){},b=!0,c=!0,d=!1,e=[]){this.highlight=a,this.preElementStyled=b,this.isCode=c,this.includeCodeInputInHighlightFunc=d,this.plugins=e}highlight=function(){};preElementStyled=!0;isCode=!0;includeCodeInputInHighlightFunc=!1;plugins=[]},templates:{prism(a,b=[]){return new codeInput.templates.Prism(a,b)},hljs(a,b=[]){return new codeInput.templates.Hljs(a,b)},characterLimit(a){return{highlight:function(a,b,c=[]){let d=+b.getAttribute("data-character-limit"),e=b.escapeHtml(b.value.slice(0,d)),f=b.escapeHtml(b.value.slice(d));a.innerHTML=`${e}${f}`,0${b.getAttribute("data-overflow-msg")||"(Character limit reached)"}`)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,plugins:a}},rainbowText(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return{highlight:function(a,b){let c=[],d=b.value.split(b.template.delimiter);for(let e=0;e${b.escapeHtml(d[e])}`);a.innerHTML=c.join(b.template.delimiter)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,rainbowColors:a,delimiter:b,plugins:c}},character_limit(){return this.characterLimit([])},rainbow_text(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return this.rainbowText(a,b,c)},custom(a=function(){},b=!0,c=!0,d=!1,e=[]){return{highlight:a,includeCodeInputInHighlightFunc:d,preElementStyled:b,isCode:c,plugins:e}}},plugins:new Proxy({},{get(a,b){if(a[b]==null)throw ReferenceError(`code-input: Plugin '${b}' is not defined. Please ensure you import the necessary files from the plugins folder in the WebCoder49/code-input repository, in the of your HTML, before the plugin is instatiated.`);return a[b]}}),Plugin:class{constructor(a){a.forEach(a=>{codeInput.observedAttributes.push(a)})}addTranslations(a,b){for(const c in b)a[c]=b[c]}beforeHighlight(){}afterHighlight(){}beforeElementsAdded(){}afterElementsAdded(){}attributeChanged(){}},CodeInput:class extends HTMLElement{constructor(){super()}templateObject=null;textareaElement=null;preElement=null;codeElement=null;dialogContainerElement=null;internalStyle=null;static formAssociated=!0;boundEventCallbacks={};pluginEvt(a,b){for(let c in this.templateObject.plugins){let d=this.templateObject.plugins[c];a in d&&(b===void 0?d[a](this):d[a](this,...b))}}needsHighlight=!1;originalAriaDescription;scheduleHighlight(){this.needsHighlight=!0}animateFrame(){this.needsHighlight&&(this.update(),this.needsHighlight=!1),window.requestAnimationFrame(this.animateFrame.bind(this))}update(){let a=this.codeElement,b=this.value;b+="\n",a.innerHTML=this.escapeHtml(b),this.pluginEvt("beforeHighlight"),this.templateObject.includeCodeInputInHighlightFunc?this.templateObject.highlight(a,this):this.templateObject.highlight(a),this.syncSize(),this.pluginEvt("afterHighlight")}getStyledHighlightingElement(){return this.templateObject.preElementStyled?this.preElement:this.codeElement}syncSize(){this.textareaElement.style.height=getComputedStyle(this.getStyledHighlightingElement()).height,this.textareaElement.style.width=getComputedStyle(this.getStyledHighlightingElement()).width}isColorOverridenSyncIfNot(){const a=this.style.transition;return this.style.transition="unset",window.requestAnimationFrame(()=>{if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(0, 0, 0)"),"rgb(0, 0, 0)"==getComputedStyle(this).color&&(this.internalStyle.setProperty("--code-input_no-override-color","rgb(255, 255, 255)"),"rgb(255, 255, 255)"==getComputedStyle(this).color)){this.internalStyle.removeProperty("--code-input_no-override-color"),this.style.transition=a;const b=getComputedStyle(this.getStyledHighlightingElement()).color;return this.internalStyle.setProperty("--code-input_highlight-text-color",b),this.internalStyle.setProperty("--code-input_default-caret-color",b),!1}this.internalStyle.removeProperty("--code-input_no-override-color"),this.style.transition=a}),!0}syncColorCompletely(){this.isColorOverridenSyncIfNot()&&(this.internalStyle.removeProperty("--code-input_highlight-text-color"),this.internalStyle.setProperty("--code-input_default-caret-color",getComputedStyle(this).color))}setKeyboardNavInstructions(a,b){this.dialogContainerElement.querySelector(".code-input_keyboard-navigation-instructions").innerText=a,b?this.textareaElement.setAttribute("aria-description",this.originalAriaDescription+". "+a):this.textareaElement.setAttribute("aria-description",a)}escapeHtml(a){return a.replace(/&/g,"&").replace(/")}getTemplate(){let a;return a=null==this.getAttribute("template")?codeInput.defaultTemplate:this.getAttribute("template"),a in codeInput.usedTemplates?codeInput.usedTemplates[a]:(a in codeInput.templateNotYetRegisteredQueue||(codeInput.templateNotYetRegisteredQueue[a]=[]),void codeInput.templateNotYetRegisteredQueue[a].push(this))}setup(){if(null!=this.textareaElement)return;this.classList.add("code-input_registered"),this.mutationObserver=new MutationObserver(this.mutationObserverCallback.bind(this)),this.mutationObserver.observe(this,{attributes:!0,attributeOldValue:!0}),this.classList.add("code-input_registered"),this.templateObject.preElementStyled&&this.classList.add("code-input_pre-element-styled");const a=this.querySelector("textarea[data-code-input-fallback]");let b,c,d,e,f,g=!1;a&&(a===document.activeElement&&(g=!0),b=a.selectionStart,c=a.selectionEnd,d=a.selectionDirection,e=a.scrollLeft,f=a.scrollTop);let h;if(a){let b=a.getAttributeNames();for(let c=0;c{this.classList.add("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("blur",()=>{this.classList.remove("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("focus",()=>{window.setTimeout(()=>{this.syncSize()},0)}),this.innerHTML="";for(let a,b=0;b{this.value=this.textareaElement.value}),this.textareaElement=k,this.append(k),this.setupTextareaSyncEvents(this.textareaElement);let l=document.createElement("code"),m=document.createElement("pre");m.setAttribute("aria-hidden","true"),m.setAttribute("tabindex","-1"),m.setAttribute("inert",!0),this.preElement=m,this.codeElement=l,m.append(l),this.append(m),this.templateObject.isCode&&i!=null&&""!=i&&l.classList.add("language-"+i.toLowerCase());let n=document.createElement("div");n.classList.add("code-input_dialog-container"),this.append(n),this.dialogContainerElement=n;let o=document.createElement("div");o.classList.add("code-input_keyboard-navigation-instructions"),n.append(o),this.pluginEvt("afterElementsAdded"),this.dispatchEvent(new CustomEvent("code-input_load")),this.value=h,b!==void 0&&(k.setSelectionRange(b,c,d),k.scrollTo(f,e)),g&&k.focus(),this.animateFrame();const p=new ResizeObserver(()=>{this.syncSize()});p.observe(this),this.classList.add("code-input_styles_"+codeInput.stylesheetI);const q=document.createElement("style");q.innerHTML="code-input.code-input_styles_"+codeInput.stylesheetI+" {}",this.appendChild(q),this.internalStyle=q.sheet.cssRules[0].style,codeInput.stylesheetI++;const r=a=>{"color"==a.propertyName&&this.isColorOverridenSyncIfNot()};this.preElement.addEventListener("transitionend",r),this.preElement.addEventListener("-webkit-transitionend",r);const s=a=>{"color"==a.propertyName&&this.syncColorCompletely(),a.target==this.dialogContainerElement&&a.stopPropagation()};this.dialogContainerElement.addEventListener("transitionend",s),this.dialogContainerElement.addEventListener("-webkit-transitionend",s),this.addEventListener("transitionend",s),this.addEventListener("-webkit-transitionend",s),this.syncColorCompletely(),this.classList.add("code-input_loaded")}escape_html(a){return this.escapeHtml(a)}get_template(){return this.getTemplate()}get template(){return this.templateObject}set template(a){}connectedCallback(){if(this.templateObject=this.getTemplate(),null!=this.templateObject&&(this.classList.add("code-input_registered"),"loading"===document.readyState?window.addEventListener("DOMContentLoaded",this.setup.bind(this)):this.setup()),"loading"===document.readyState)window.addEventListener("DOMContentLoaded",()=>{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)});else{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)}}mutationObserverCallback(a){for(const b of a)if("attributes"===b.type){for(let a=0;a{a.match(b)&&(null==c?this.textareaElement.removeAttribute(a):this.textareaElement.setAttribute(a,c))})}}setupTextareaSyncEvents(a){for(let b=0;b{a.bubbles||this.dispatchEvent(new a.constructor(a.type,a))})}}addEventListener(a,b,c=void 0){let d=function(a){"function"==typeof b?b(a):b&&b.handleEvent&&b.handleEvent(a)}.bind(this);if(this.boundEventCallbacks[b]=d,!codeInput.textareaSyncEvents.includes(a))void 0===c?super.addEventListener(a,d):super.addEventListener(a,d,c);else if(this.boundEventCallbacks[b]=d,void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d)})}else this.textareaElement.addEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d,c)})}else this.textareaElement.addEventListener(a,d,c)}removeEventListener(a,b,c=void 0){let d=this.boundEventCallbacks[b];if(!codeInput.textareaSyncEvents.includes(a))void 0===c?super.removeEventListener(a,d):super.removeEventListener(a,d,c);else if(void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d)})}else this.textareaElement.removeEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d,c)})}else this.textareaElement.removeEventListener(a,d,c)}getTextareaProperty(a,b=void 0){if(this.textareaElement)return this.textareaElement[a];else{const c=this.querySelector("textarea[data-code-input-fallback]");if(c)return c[a];if(void 0===b)throw new Error("Cannot get "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.");return b}}setTextareaProperty(a,b,c=!0){if(this.textareaElement)this.textareaElement[a]=b;else{const d=this.querySelector("textarea[data-code-input-fallback]");if(d)d[a]=b;else{if(!c)return(!1);throw new Error("Cannot set "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.")}}return(!0)}get autocomplete(){return this.getAttribute("autocomplete")}set autocomplete(a){return this.setAttribute("autocomplete",a)}get cols(){return this.getTextareaProperty("cols",+this.getAttribute("cols"))}set cols(a){this.setAttribute("cols",a)}get defaultValue(){return this.initialValue}set defaultValue(a){this.initialValue=a}get textContent(){return this.initialValue}set textContent(a){this.initialValue=a}get dirName(){return this.getAttribute("dirName")||""}set dirName(a){this.setAttribute("dirname",a)}get disabled(){return this.hasAttribute("disabled")}set disabled(a){a?this.setAttribute("disabled",!0):this.removeAttribute("disabled")}get form(){return this.getTextareaProperty("form")}get labels(){return this.getTextareaProperty("labels")}get maxLength(){const a=+this.getAttribute("maxlength");return isNaN(a)?-1:a}set maxLength(a){-1==a?this.removeAttribute("maxlength"):this.setAttribute("maxlength",a)}get minLength(){const a=+this.getAttribute("minlength");return isNaN(a)?-1:a}set minLength(a){-1==a?this.removeAttribute("minlength"):this.setAttribute("minlength",a)}get name(){return this.getAttribute("name")||""}set name(a){this.setAttribute("name",a)}get placeholder(){return this.getAttribute("placeholder")||""}set placeholder(a){this.setAttribute("placeholder",a)}get readOnly(){return this.hasAttribute("readonly")}set readOnly(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get required(){return this.hasAttribute("readonly")}set required(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get rows(){return this.getTextareaProperty("rows",+this.getAttribute("rows"))}set rows(a){this.setAttribute("rows",a)}get selectionDirection(){return this.getTextareaProperty("selectionDirection")}set selectionDirection(a){this.setTextareaProperty("selectionDirection",a)}get selectionEnd(){return this.getTextareaProperty("selectionEnd")}set selectionEnd(a){this.setTextareaProperty("selectionEnd",a)}get selectionStart(){return this.getTextareaProperty("selectionStart")}set selectionStart(a){this.setTextareaProperty("selectionStart",a)}get textLength(){return this.value.length}get type(){return"textarea"}get validationMessage(){return this.getTextareaProperty("validationMessage")}get validity(){return this.getTextareaProperty("validationMessage")}get value(){return this.getTextareaProperty("value",this.getAttribute("value")||this.innerHTML)}set value(a){a=a||"",this.setTextareaProperty("value",a,!1)?this.textareaElement&&this.scheduleHighlight():this.innerHTML=a}get willValidate(){return this.getTextareaProperty("willValidate",this.disabled||this.readOnly)}get wrap(){return this.getAttribute("wrap")||""}set wrap(a){this.setAttribute("wrap",a)}getTextareaMethod(a){if(this.textareaElement)return this.textareaElement[a].bind(this.textareaElement);else{const b=this.querySelector("textarea[data-code-input-fallback]");if(b)return b[a].bind(b);throw new Error("Cannot call "+a+" on an unregistered code-input element without a data-code-input-fallback textarea.")}}blur(a={}){this.getTextareaMethod("blur")(a)}checkValidity(){return this.getTextareaMethod("checkValidity")()}focus(a={}){this.getTextareaMethod("focus")(a)}reportValidity(){return this.getTextareaMethod("reportValidity")()}setCustomValidity(a){this.getTextareaMethod("setCustomValidity")(a)}setRangeText(a,b=this.selectionStart,c=this.selectionEnd,d="preserve"){this.getTextareaMethod("setRangeText")(a,b,c,d),this.textareaElement&&this.scheduleHighlight()}setSelectionRange(a,b,c="none"){this.getTextareaMethod("setSelectionRange")(a,b,c)}pluginData={};formResetCallback(){this.value=this.initialValue}}};{class a extends codeInput.Template{constructor(a,b=[],c=!0){super(a.highlightElement,c,!0,!1,b)}}codeInput.templates.Prism=a;class b extends codeInput.Template{constructor(a,b=[],c=!1){super(function(b){b.removeAttribute("data-highlighted"),a.highlightElement(b)},c,!0,!1,b)}}codeInput.templates.Hljs=b}customElements.define("code-input",codeInput.CodeInput); \ No newline at end of file + */"use strict";var codeInput={observedAttributes:["value","placeholder","language","lang","template"],textareaSyncAttributes:["value","min","max","type","pattern","autocomplete","autocorrect","autofocus","cols","dirname","disabled","form","maxlength","minlength","name","placeholder","readonly","required","rows","spellcheck","wrap"],textareaSyncEvents:["change","selectionchange","invalid","input","focus","blur","focusin","focusout"],usedTemplates:{},defaultTemplate:void 0,templateNotYetRegisteredQueue:{},registerTemplate:function(a,b){if(!("string"==typeof a||a instanceof String))throw TypeError(`code-input: Name of template "${a}" must be a string.`);if(!("function"==typeof b.highlight||b.highlight instanceof Function))throw TypeError(`code-input: Template for "${a}" invalid, because the highlight function provided is not a function; it is "${b.highlight}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.includeCodeInputInHighlightFunc||b.includeCodeInputInHighlightFunc instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the includeCodeInputInHighlightFunc value provided is not a true or false; it is "${b.includeCodeInputInHighlightFunc}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.preElementStyled||b.preElementStyled instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the preElementStyled value provided is not a true or false; it is "${b.preElementStyled}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.isCode||b.isCode instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the isCode value provided is not a true or false; it is "${b.isCode}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!Array.isArray(b.plugins))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin array provided is not an array; it is "${b.plugins}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(b.plugins.forEach((c,d)=>{if(!(c instanceof codeInput.Plugin))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin provided at index ${d} is not valid; it is "${b.plugins[d]}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`)}),codeInput.usedTemplates[a]=b,a in codeInput.templateNotYetRegisteredQueue)for(let c in codeInput.templateNotYetRegisteredQueue[a]){const d=codeInput.templateNotYetRegisteredQueue[a][c];d.templateObject=b,d.setup()}if(null==codeInput.defaultTemplate&&(codeInput.defaultTemplate=a,void 0 in codeInput.templateNotYetRegisteredQueue))for(let a in codeInput.templateNotYetRegisteredQueue[void 0]){const c=codeInput.templateNotYetRegisteredQueue[void 0][a];c.templateObject=b,c.setup()}},stylesheetI:0,Template:class{constructor(a=function(){},b=!0,c=!0,d=!1,e=[]){this.highlight=a,this.preElementStyled=b,this.isCode=c,this.includeCodeInputInHighlightFunc=d,this.plugins=e}highlight=function(){};preElementStyled=!0;isCode=!0;includeCodeInputInHighlightFunc=!1;plugins=[]},templates:{prism(a,b=[]){return new codeInput.templates.Prism(a,b)},hljs(a,b=[]){return new codeInput.templates.Hljs(a,b)},characterLimit(a){return{highlight:function(a,b,c=[]){let d=+b.getAttribute("data-character-limit"),e=b.escapeHtml(b.value.slice(0,d)),f=b.escapeHtml(b.value.slice(d));a.innerHTML=`${e}${f}`,0${b.getAttribute("data-overflow-msg")||"(Character limit reached)"}`)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,plugins:a}},rainbowText(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return{highlight:function(a,b){let c=[],d=b.value.split(b.template.delimiter);for(let e=0;e${b.escapeHtml(d[e])}`);a.innerHTML=c.join(b.template.delimiter)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,rainbowColors:a,delimiter:b,plugins:c}},character_limit(){return this.characterLimit([])},rainbow_text(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return this.rainbowText(a,b,c)},custom(a=function(){},b=!0,c=!0,d=!1,e=[]){return{highlight:a,includeCodeInputInHighlightFunc:d,preElementStyled:b,isCode:c,plugins:e}}},plugins:new Proxy({},{get(a,b){if(a[b]==null)throw ReferenceError(`code-input: Plugin '${b}' is not defined. Please ensure you import the necessary files from the plugins folder in the WebCoder49/code-input repository, in the of your HTML, before the plugin is instatiated.`);return a[b]}}),Plugin:class{constructor(a){a.forEach(a=>{codeInput.observedAttributes.push(a)})}addTranslations(a,b){for(const c in b)a[c]=b[c]}beforeHighlight(){}afterHighlight(){}beforeElementsAdded(){}afterElementsAdded(){}attributeChanged(){}},CodeInput:class extends HTMLElement{constructor(){super()}templateObject=null;textareaElement=null;preElement=null;codeElement=null;dialogContainerElement=null;internalStyle=null;static formAssociated=!0;boundEventCallbacks={};pluginEvt(a,b){for(let c in this.templateObject.plugins){let d=this.templateObject.plugins[c];a in d&&(b===void 0?d[a](this):d[a](this,...b))}}needsHighlight=!1;originalAriaDescription;scheduleHighlight(){this.needsHighlight=!0}animateFrame(){this.needsHighlight&&(this.update(),this.needsHighlight=!1),window.requestAnimationFrame(this.animateFrame.bind(this))}update(){let a=this.codeElement,b=this.value;b+="\n",a.innerHTML=this.escapeHtml(b),this.pluginEvt("beforeHighlight"),this.templateObject.includeCodeInputInHighlightFunc?this.templateObject.highlight(a,this):this.templateObject.highlight(a),this.syncSize(),this.pluginEvt("afterHighlight")}getStyledHighlightingElement(){return this.templateObject.preElementStyled?this.preElement:this.codeElement}syncSize(){this.textareaElement.style.height=getComputedStyle(this.getStyledHighlightingElement()).height,this.textareaElement.style.width=getComputedStyle(this.getStyledHighlightingElement()).width}syncIfColorNotOverridden(a=function(){}){if(this.checkingColorOverridden)return;this.checkingColorOverridden=!0;const b=this.style.transition;this.style.transition="unset",window.requestAnimationFrame(()=>{if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(0, 0, 0)"),"rgb(0, 0, 0)"!=getComputedStyle(this).color)this.style.transition=b,this.checkingColorOverridden=!1,a();else if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(255, 255, 255)"),"rgb(255, 255, 255)"==getComputedStyle(this).color){this.internalStyle.removeProperty("--code-input_no-override-color"),console.log(this,"Autoadapt; "+b),this.style.transition=b;const a=getComputedStyle(this.getStyledHighlightingElement()).color;this.internalStyle.setProperty("--code-input_highlight-text-color",a),this.internalStyle.setProperty("--code-input_default-caret-color",a),this.checkingColorOverridden=!1}else this.style.transition=b,this.checkingColorOverridden=!1,a();this.internalStyle.removeProperty("--code-input_no-override-color"),console.log(this,"No autoadapt; "+b),this.style.transition=b})}syncColorCompletely(){this.syncIfColorNotOverridden(()=>{this.internalStyle.removeProperty("--code-input_highlight-text-color"),this.internalStyle.setProperty("--code-input_default-caret-color",getComputedStyle(this).color)})}setKeyboardNavInstructions(a,b){this.dialogContainerElement.querySelector(".code-input_keyboard-navigation-instructions").innerText=a,b?this.textareaElement.setAttribute("aria-description",this.originalAriaDescription+". "+a):this.textareaElement.setAttribute("aria-description",a)}escapeHtml(a){return a.replace(/&/g,"&").replace(/")}getTemplate(){let a;return a=null==this.getAttribute("template")?codeInput.defaultTemplate:this.getAttribute("template"),a in codeInput.usedTemplates?codeInput.usedTemplates[a]:(a in codeInput.templateNotYetRegisteredQueue||(codeInput.templateNotYetRegisteredQueue[a]=[]),void codeInput.templateNotYetRegisteredQueue[a].push(this))}setup(){if(null!=this.textareaElement)return;this.classList.add("code-input_registered"),this.mutationObserver=new MutationObserver(this.mutationObserverCallback.bind(this)),this.mutationObserver.observe(this,{attributes:!0,attributeOldValue:!0}),this.classList.add("code-input_registered"),this.templateObject.preElementStyled&&this.classList.add("code-input_pre-element-styled");const a=this.querySelector("textarea[data-code-input-fallback]");let b,c,d,e,f,g=!1;a&&(a===document.activeElement&&(g=!0),b=a.selectionStart,c=a.selectionEnd,d=a.selectionDirection,e=a.scrollLeft,f=a.scrollTop);let h;if(a){let b=a.getAttributeNames();for(let c=0;c{this.classList.add("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("blur",()=>{this.classList.remove("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("focus",()=>{window.setTimeout(()=>{this.syncSize()},0)}),this.innerHTML="";for(let a,b=0;b{this.value=this.textareaElement.value}),this.textareaElement=k,this.append(k),this.setupTextareaSyncEvents(this.textareaElement);let l=document.createElement("code"),m=document.createElement("pre");m.setAttribute("aria-hidden","true"),m.setAttribute("tabindex","-1"),m.setAttribute("inert",!0),this.preElement=m,this.codeElement=l,m.append(l),this.append(m),this.templateObject.isCode&&i!=null&&""!=i&&l.classList.add("language-"+i.toLowerCase());let n=document.createElement("div");n.classList.add("code-input_dialog-container"),this.append(n),this.dialogContainerElement=n;let o=document.createElement("div");o.classList.add("code-input_keyboard-navigation-instructions"),n.append(o),this.pluginEvt("afterElementsAdded"),this.dispatchEvent(new CustomEvent("code-input_load")),this.value=h,b!==void 0&&(k.setSelectionRange(b,c,d),k.scrollTo(f,e)),g&&k.focus(),this.animateFrame();const p=new ResizeObserver(()=>{this.syncSize()});p.observe(this),this.classList.add("code-input_styles_"+codeInput.stylesheetI);const q=document.createElement("style");q.innerHTML="code-input.code-input_styles_"+codeInput.stylesheetI+" {}",this.appendChild(q),this.internalStyle=q.sheet.cssRules[0].style,codeInput.stylesheetI++;const r=a=>{"color"==a.propertyName&&this.syncIfColorNotOverridden()};this.preElement.addEventListener("transitionend",r),this.preElement.addEventListener("-webkit-transitionend",r);const s=a=>{"color"==a.propertyName&&this.syncColorCompletely(),a.target==this.dialogContainerElement&&a.stopPropagation()};this.dialogContainerElement.addEventListener("transitionend",s),this.dialogContainerElement.addEventListener("-webkit-transitionend",s),this.addEventListener("transitionend",s),this.addEventListener("-webkit-transitionend",s),this.syncColorCompletely(),this.classList.add("code-input_loaded")}escape_html(a){return this.escapeHtml(a)}get_template(){return this.getTemplate()}get template(){return this.templateObject}set template(a){}connectedCallback(){if(this.templateObject=this.getTemplate(),null!=this.templateObject&&(this.classList.add("code-input_registered"),"loading"===document.readyState?window.addEventListener("DOMContentLoaded",this.setup.bind(this)):this.setup()),"loading"===document.readyState)window.addEventListener("DOMContentLoaded",()=>{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)});else{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)}}mutationObserverCallback(a){for(const b of a)if("attributes"===b.type){for(let a=0;a{a.match(b)&&(null==c?this.textareaElement.removeAttribute(a):this.textareaElement.setAttribute(a,c))})}}setupTextareaSyncEvents(a){for(let b=0;b{a.bubbles||this.dispatchEvent(new a.constructor(a.type,a))})}}addEventListener(a,b,c=void 0){let d=function(a){"function"==typeof b?b(a):b&&b.handleEvent&&b.handleEvent(a)}.bind(this);if(this.boundEventCallbacks[b]=d,!codeInput.textareaSyncEvents.includes(a))void 0===c?super.addEventListener(a,d):super.addEventListener(a,d,c);else if(this.boundEventCallbacks[b]=d,void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d)})}else this.textareaElement.addEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d,c)})}else this.textareaElement.addEventListener(a,d,c)}removeEventListener(a,b,c=void 0){let d=this.boundEventCallbacks[b];if(!codeInput.textareaSyncEvents.includes(a))void 0===c?super.removeEventListener(a,d):super.removeEventListener(a,d,c);else if(void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d)})}else this.textareaElement.removeEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d,c)})}else this.textareaElement.removeEventListener(a,d,c)}getTextareaProperty(a,b=void 0){if(this.textareaElement)return this.textareaElement[a];else{const c=this.querySelector("textarea[data-code-input-fallback]");if(c)return c[a];if(void 0===b)throw new Error("Cannot get "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.");return b}}setTextareaProperty(a,b,c=!0){if(this.textareaElement)this.textareaElement[a]=b;else{const d=this.querySelector("textarea[data-code-input-fallback]");if(d)d[a]=b;else{if(!c)return(!1);throw new Error("Cannot set "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.")}}return(!0)}get autocomplete(){return this.getAttribute("autocomplete")}set autocomplete(a){return this.setAttribute("autocomplete",a)}get cols(){return this.getTextareaProperty("cols",+this.getAttribute("cols"))}set cols(a){this.setAttribute("cols",a)}get defaultValue(){return this.initialValue}set defaultValue(a){this.initialValue=a}get textContent(){return this.initialValue}set textContent(a){this.initialValue=a}get dirName(){return this.getAttribute("dirName")||""}set dirName(a){this.setAttribute("dirname",a)}get disabled(){return this.hasAttribute("disabled")}set disabled(a){a?this.setAttribute("disabled",!0):this.removeAttribute("disabled")}get form(){return this.getTextareaProperty("form")}get labels(){return this.getTextareaProperty("labels")}get maxLength(){const a=+this.getAttribute("maxlength");return isNaN(a)?-1:a}set maxLength(a){-1==a?this.removeAttribute("maxlength"):this.setAttribute("maxlength",a)}get minLength(){const a=+this.getAttribute("minlength");return isNaN(a)?-1:a}set minLength(a){-1==a?this.removeAttribute("minlength"):this.setAttribute("minlength",a)}get name(){return this.getAttribute("name")||""}set name(a){this.setAttribute("name",a)}get placeholder(){return this.getAttribute("placeholder")||""}set placeholder(a){this.setAttribute("placeholder",a)}get readOnly(){return this.hasAttribute("readonly")}set readOnly(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get required(){return this.hasAttribute("readonly")}set required(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get rows(){return this.getTextareaProperty("rows",+this.getAttribute("rows"))}set rows(a){this.setAttribute("rows",a)}get selectionDirection(){return this.getTextareaProperty("selectionDirection")}set selectionDirection(a){this.setTextareaProperty("selectionDirection",a)}get selectionEnd(){return this.getTextareaProperty("selectionEnd")}set selectionEnd(a){this.setTextareaProperty("selectionEnd",a)}get selectionStart(){return this.getTextareaProperty("selectionStart")}set selectionStart(a){this.setTextareaProperty("selectionStart",a)}get textLength(){return this.value.length}get type(){return"textarea"}get validationMessage(){return this.getTextareaProperty("validationMessage")}get validity(){return this.getTextareaProperty("validationMessage")}get value(){return this.getTextareaProperty("value",this.getAttribute("value")||this.innerHTML)}set value(a){a=a||"",this.setTextareaProperty("value",a,!1)?this.textareaElement&&this.scheduleHighlight():this.innerHTML=a}get willValidate(){return this.getTextareaProperty("willValidate",this.disabled||this.readOnly)}get wrap(){return this.getAttribute("wrap")||""}set wrap(a){this.setAttribute("wrap",a)}getTextareaMethod(a){if(this.textareaElement)return this.textareaElement[a].bind(this.textareaElement);else{const b=this.querySelector("textarea[data-code-input-fallback]");if(b)return b[a].bind(b);throw new Error("Cannot call "+a+" on an unregistered code-input element without a data-code-input-fallback textarea.")}}blur(a={}){this.getTextareaMethod("blur")(a)}checkValidity(){return this.getTextareaMethod("checkValidity")()}focus(a={}){this.getTextareaMethod("focus")(a)}reportValidity(){return this.getTextareaMethod("reportValidity")()}setCustomValidity(a){this.getTextareaMethod("setCustomValidity")(a)}setRangeText(a,b=this.selectionStart,c=this.selectionEnd,d="preserve"){this.getTextareaMethod("setRangeText")(a,b,c,d),this.textareaElement&&this.scheduleHighlight()}setSelectionRange(a,b,c="none"){this.getTextareaMethod("setSelectionRange")(a,b,c)}pluginData={};formResetCallback(){this.value=this.initialValue}}};{class a extends codeInput.Template{constructor(a,b=[],c=!0){super(a.highlightElement,c,!0,!1,b)}}codeInput.templates.Prism=a;class b extends codeInput.Template{constructor(a,b=[],c=!1){super(function(b){b.removeAttribute("data-highlighted"),a.highlightElement(b)},c,!0,!1,b)}}codeInput.templates.Hljs=b}customElements.define("code-input",codeInput.CodeInput); \ No newline at end of file From ef0d11755b06e6e6b32e547ec7e712b2129f8407 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Sun, 7 Dec 2025 18:56:30 +0000 Subject: [PATCH 18/54] Fix typos and wording, refer to Codeberg as well as GitHub, don't be partial to CodePen, all in docs and comments --- CONTRIBUTING.md | 8 ++++---- code-input.d.ts | 6 +++--- docs/_index.md | 9 ++------- docs/interface/js/_index.md | 2 +- .../hljs/esm/._index.md.kate-swp | Bin 0 -> 118 bytes docs/plugins/_index.md | 6 +++--- plugins/README.md | 2 +- plugins/find-and-replace.js | 4 ++-- plugins/go-to-line.js | 2 +- 9 files changed, 17 insertions(+), 22 deletions(-) create mode 100644 docs/modules-and-frameworks/hljs/esm/._index.md.kate-swp diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0970fdd..1d0bb25 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,23 +14,23 @@ This said, if you're not sure whether your change will be accepted, please ask i --- -To keep this community productive and enjoyable, please [don't break our code of conduct](https://github.com/WebCoder49/code-input/blob/main/CODE_OF_CONDUCT.md). +To keep this community productive and enjoyable, please [don't break our code of conduct](CODE_OF_CONDUCT.md). --- # Ways you could contribute: If you don't want to use GitHub, for all of these you can alternatively [get in touch via email so the maintainer can add your contribution for you](mailto:code-input-js@webcoder49.dev). ## 1. I've found a bug but don't know how / don't have time to fix it. -If you think you've found a bug, please [submit an issue](https://github.com/WebCoder49/code-input/issues) with screenshots, how you found the bug, and copies of the console's logs if an error is in them. Please also mention the template and plugins you used (your `codeInput.registerTemplate(...)` snippet). We'd be more than happy to help you fix it. A demo using [CodePen](https://codepen.io/) would be incredibly useful. +If you think you've found a bug, please submit an issue on [GitHub](https://github.com/WebCoder49/code-input/issues) or [Codeberg](https://codeberg.org/code-input-js/code-input-js/issues), with screenshots, how you found the bug, and copies of the console's logs if an error is in them. Please also mention the template and plugins you used (your `codeInput.registerTemplate(...)` snippet). We'd be more than happy to help you fix it. A piece of code demonstrating your bug would be incredibly useful. ## 2. I have implemented a feature / have thought of a potential feature for the library and think it could be useful for others. The best way to implement a feature is as a plugin like those in the `plugins` folder of `code-input`. If you do this, you could contribute it as in point 3 below. -Otherwise, if you do not have the time needed / do not want to implement it, any potential plugins would be [welcome as an Issue](https://github.com/WebCoder49/code-input/issues) which specifies the uses and desired characteristics. +Otherwise, if you do not have the time needed / do not want to implement it, any potential plugins would be (on [GitHub](https://github.com/WebCoder49/code-input/issues) or [Codeberg](https://codeberg.org/code-input-js/code-input-js/issues)) which specifies the uses and desired characteristics. ## 3. I want to contribute code that I need / have seen in the Issues. Firstly, thank you for doing this! This is probably the most time consuming way of contributing but is also the most rewarding for both you and me as a maintainer. -Please first open an issue if one doesn't already exist, and assign yourself to it. Then, [make a fork of the repo and submit a pull request](https://docs.github.com/en/get-started/quickstart/contributing-to-projects). +Please first open an issue if one doesn't already exist, and assign yourself to it. Then, make a fork of the repo and submit a pull request (how-tos by [Codeberg](https://docs.codeberg.org/collaborating/pull-requests-and-git-flow/) and [GitHub](https://docs.github.com/en/get-started/quickstart/contributing-to-projects)). In the pull request, include the code updates for your feature / bug, and if you're adding a new feature make sure you comment your code so it's understandable to future contributors, and if you can, add unit tests for it in tests/tester.js. If you have any questions, just let me (@WebCoder49) know! diff --git a/code-input.d.ts b/code-input.d.ts index e50e14a..3c398ea 100644 --- a/code-input.d.ts +++ b/code-input.d.ts @@ -135,8 +135,8 @@ export namespace plugins { class FindAndReplace extends Plugin { /** * Create a find-and-replace command plugin to pass into a template. To ensure keyboard shortcuts remain intuitive, set the alwaysCtrl parameter to false. - * @param {boolean} useCtrlF Should Ctrl/Cmd+F be overriden for find-and-replace find functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, false)`. - * @param {boolean} useCtrlH Should Ctrl+H be overriden for find-and-replace replace functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, true)`. + * @param {boolean} useCtrlF Should Ctrl/Cmd+F be overridden for find-and-replace find functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, false)`. + * @param {boolean} useCtrlH Should Ctrl+H be overridden for find-and-replace replace functionality? Either way, you can also trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, true)`. * @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the find-and-replace.js source code for the English text. * @param {boolean} alwaysCtrl Setting this to false makes the keyboard shortcuts follow the operating system while avoiding clashes (right now: Cmd+F/Ctrl+H on Apple, Ctrl+F/Ctrl+H otherwise.) and is recommended; true forces Ctrl+F/Ctrl+H and is default for backwards compatibility. */ @@ -180,7 +180,7 @@ export namespace plugins { class GoToLine extends Plugin { /** * Create a go-to-line command plugin to pass into a template. - * @param {boolean} useCtrlG Should Ctrl/Cmd+G be overriden for go-to-line functionality? Either way, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element)`. + * @param {boolean} useCtrlG Should Ctrl/Cmd+G be overridden for go-to-line functionality? Either way, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element)`. * @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the English text. */ constructor(useCtrlG?: boolean, diff --git a/docs/_index.md b/docs/_index.md index 9162b6a..55491a0 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -4,16 +4,11 @@ title = 'Flexible Syntax Highlighted Editable Textareas' # An editable `
- - + + - + - - + + - + - - + + - + - - + + - + - + - - + + - - + + - - + + - + - - + + - - + + - - + + - - + + - + - - + + - + - - + + - + @@ -608,12 +608,12 @@ See https://github.com/WebCoder49/code-input/issues?q=is%3Aissue%20state%3Aopen% - - + + - - + + - - + + - + - - + + - +

This uses both the auto-close brackets and indent plugins. Try typing some brackets / double quotes!

- + ``` @@ -119,7 +119,7 @@ Right now, you can only add one plugin of each type (e.g. one SelectTokenCallbac ]));

Start typing some HTML tags to see the autocomplete in action. You can click an autocomplete suggestion, or press the Tab key to select the first.

- + ``` @@ -148,13 +148,13 @@ Right now, you can only add one plugin of each type (e.g. one SelectTokenCallbac ]));

Enter newlines to grow vertically:

- +

Type to grow horizontally:

- +

Grows vertically between 100px and 200px:

- +

Grows horizontally between 100px and 200px:

- + ``` @@ -197,12 +197,12 @@ Right now, you can only add one plugin of each type (e.g. one SelectTokenCallbac ]));

When focused in the editor: Try Ctrl/Cmd+F, or click to find. Try Ctrl+H, or click to replace.

- # Hickory dickory dock +

When setting the size of code-input elements with this plugin, make sure they are large enough to fit the dialog, at least 500px wide and 170px tall with the default styling. (If you are using the Autogrow plugin, this will be done for you as long as your --code-input_autogrow_max-width and --code-input_autogrow_max-height are large enough.) For technical reasons the dialog cannot overflow the editing area.

@@ -246,12 +246,12 @@ Hickory dickory dock.
]));

Try Ctrl+G when focused in the editor, or click

- # Hickory dickory dock +

When setting the size of code-input elements with this plugin, make sure they are large enough to fit the dialog, at least 300px wide and 150px tall with the default styling. (If you are using the Autogrow plugin, this will be done for you as long as your --code-input_autogrow_max-width and --code-input_autogrow_max-height are large enough.) For technical reasons the dialog cannot overflow the editing area.

@@ -290,13 +290,13 @@ Hickory dickory dock.
]));

Try Tab or Shift+Tab when selecting or editing text.

- { + ``` @@ -575,14 +575,14 @@ Hickory dickory dock. ]));

Try selecting some code with a bracket. This is just one demo use of this very flexible plugin, which lets you use more features from your highlighter (here, Prism.js' match-braces plugin)!

- function hello() { + ``` @@ -625,7 +625,7 @@ See https://github.com/WebCoder49/code-input/issues?q=is%3Aissue%20state%3Aopen% ) ])); - +

Start typing code of any language. Detected language: . Inaccurate language detection should be reported to highlight.js, not code-input-js.

- +

@@ -701,12 +701,12 @@ See https://github.com/WebCoder49/code-input/issues?q=is%3Aissue%20state%3Aopen% ]));

The lines are numbered!

- # Hickory dickory dock + ``` From 60618907b3dad8a54d654c6eb236c0ecbdb19672 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Fri, 12 Dec 2025 16:31:56 +0000 Subject: [PATCH 33/54] Document keyboard shortcut browser interception (#201) --- docs/plugins/_index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/plugins/_index.md b/docs/plugins/_index.md index 9f61680..48094cc 100644 --- a/docs/plugins/_index.md +++ b/docs/plugins/_index.md @@ -203,7 +203,9 @@ The mouse ran up the clock. The clock struck one, The mouse ran down, Hickory dickory dock. +

Please Note

When setting the size of code-input elements with this plugin, make sure they are large enough to fit the dialog, at least 500px wide and 170px tall with the default styling. (If you are using the Autogrow plugin, this will be done for you as long as your --code-input_autogrow_max-width and --code-input_autogrow_max-height are large enough.) For technical reasons the dialog cannot overflow the editing area.

+

The keyboard shortcuts may be intercepted by the browser and unable to be used by code-input.js, for example in GNOME Web or Konqueror. Thus if you want this plugin to be available to all users, provide separate buttons to trigger the dialogs as shown above.

``` @@ -252,7 +254,9 @@ The mouse ran up the clock. The clock struck one, The mouse ran down, Hickory dickory dock.
+

Please Note

When setting the size of code-input elements with this plugin, make sure they are large enough to fit the dialog, at least 300px wide and 150px tall with the default styling. (If you are using the Autogrow plugin, this will be done for you as long as your --code-input_autogrow_max-width and --code-input_autogrow_max-height are large enough.) For technical reasons the dialog cannot overflow the editing area.

+

The keyboard shortcuts may be intercepted by the browser and unable to be used by code-input.js, for example in GNOME Web or Konqueror. Thus if you want this plugin to be available to all users, provide a separate button to trigger the dialog as shown above.

``` From 9ca3a13906b30e833a5720fc8baffea2a15405fc Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Wed, 21 Jan 2026 22:39:43 +0000 Subject: [PATCH 34/54] Make fallback textarea size correctly with autogrow plugin (Fixes #213) --- plugins/autogrow.css | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plugins/autogrow.css b/plugins/autogrow.css index da04b11..d775f5f 100644 --- a/plugins/autogrow.css +++ b/plugins/autogrow.css @@ -61,3 +61,17 @@ code-input.code-input_autogrow_width:has(.code-input_find-and-replace_dialog:not code-input.code-input_autogrow_height:has(.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog)) { --code-input_autogrow_internal-min-height: 170px; } + +/* Autogrow doesn't work with fallback textarea (since its functioning depends on +synchronisation to pre code element). Stick to the minimum size. */ + +/* At least one line high, leave space for no-JS instructions */ +code-input.code-input_autogrow_height:has(textarea[data-code-input-fallback]) { + --code-input_autogrow_internal-min-height: calc(4em + var(--padding-top, 16px) + var(--padding-bottom, 16px)); /* For browsers that don't support 1lh */ + --code-input_autogrow_internal-min-height: calc(1lh + 2em + var(--padding-top, 16px) + var(--padding-bottom, 16px)); /* So minimum height possible while containing highlighted code */ + height: var(--code-input_autogrow_true-min-height); +} +code-input textarea[data-code-input-fallback] { + height: calc(var(--code-input_autogrow_true-min-height) - var(--padding-top, 16px) - var(--padding-bottom, 16px) - 2em)!important; /* So minimum height possible while containing highlighted code */ + min-height: calc(100% - var(--padding-top, 16px) - var(--padding-bottom, 16px) - 2em); +} From b240aebca6484f6196a09a6efca8932b6f4f09e2 Mon Sep 17 00:00:00 2001 From: WebCoder49 <69071853+WebCoder49@users.noreply.github.com> Date: Wed, 21 Jan 2026 22:46:10 +0000 Subject: [PATCH 35/54] Auto Minify JS and CSS files --- plugins/autogrow.min.css | 2 +- plugins/prism-line-numbers.min.css | 2 +- plugins/special-chars.min.css | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/autogrow.min.css b/plugins/autogrow.min.css index b50386b..275cd60 100644 --- a/plugins/autogrow.min.css +++ b/plugins/autogrow.min.css @@ -1 +1 @@ -code-input.code-input_autogrow_height{--code-input_autogrow_min-height:0px;--code-input_autogrow_max-height:calc(infinity*1px);--code-input_autogrow_internal-min-height:0px;--code-input_autogrow_true-min-height:max(var(--code-input_autogrow_min-height),var(--code-input_autogrow_internal-min-height));height:max-content;max-height:var(--code-input_autogrow_max-height)}code-input.code-input_autogrow_height textarea{min-height:max(var(--code-input_synced-height),calc(100% - var(--padding-top,16px) - var(--padding-bottom,16px)));height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - var(--padding-bottom,16px))!important}code-input.code-input_autogrow_height>pre,code-input.code-input_autogrow_height>pre code{min-height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - var(--padding-bottom,16px))}code-input.code-input_autogrow_width{--code-input_autogrow_min-width:100px;--code-input_autogrow_max-width:100%;--code-input_autogrow_internal-min-width:0px;--code-input_autogrow_true-min-width:max(var(--code-input_autogrow_min-width),var(--code-input_autogrow_internal-min-width));width:max-content;max-width:var(--code-input_autogrow_max-width)}code-input.code-input_autogrow_width textarea{min-width:max(var(--code-input_synced-width),calc(100% - var(--padding-left,16px) - var(--padding-right,16px)));width:calc(var(--code-input_autogrow_true-min-width) - var(--padding-left,16px) - var(--padding-right,16px))!important}code-input.code-input_autogrow_width pre code,code-input.code-input_autogrow_width pre{min-width:calc(var(--code-input_autogrow_true-min-width) - var(--padding-left,16px) - var(--padding-right,16px))}code-input.code-input_autogrow_width:has(.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog)){--code-input_autogrow_internal-min-width:300px}code-input.code-input_autogrow_height:has(.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog)){--code-input_autogrow_internal-min-height:150px}code-input.code-input_autogrow_width:has(.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog)){--code-input_autogrow_internal-min-width:500px}code-input.code-input_autogrow_height:has(.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog)){--code-input_autogrow_internal-min-height:170px} \ No newline at end of file +code-input.code-input_autogrow_height{--code-input_autogrow_min-height:0px;--code-input_autogrow_max-height:calc(infinity * 1px);--code-input_autogrow_internal-min-height:0px;--code-input_autogrow_true-min-height:max(var(--code-input_autogrow_min-height), var(--code-input_autogrow_internal-min-height));height:max-content;max-height:var(--code-input_autogrow_max-height)}code-input.code-input_autogrow_height textarea{min-height:max(var(--code-input_synced-height), calc(100% - var(--padding-top,16px) - var(--padding-bottom,16px)));height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - var(--padding-bottom,16px))!important}code-input.code-input_autogrow_height>pre,code-input.code-input_autogrow_height>pre code{min-height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - var(--padding-bottom,16px))}code-input.code-input_autogrow_width{--code-input_autogrow_min-width:100px;--code-input_autogrow_max-width:100%;--code-input_autogrow_internal-min-width:0px;--code-input_autogrow_true-min-width:max(var(--code-input_autogrow_min-width), var(--code-input_autogrow_internal-min-width));width:max-content;max-width:var(--code-input_autogrow_max-width)}code-input.code-input_autogrow_width textarea{min-width:max(var(--code-input_synced-width), calc(100% - var(--padding-left,16px) - var(--padding-right,16px)));width:calc(var(--code-input_autogrow_true-min-width) - var(--padding-left,16px) - var(--padding-right,16px))!important}code-input.code-input_autogrow_width pre code,code-input.code-input_autogrow_width pre{min-width:calc(var(--code-input_autogrow_true-min-width) - var(--padding-left,16px) - var(--padding-right,16px))}code-input.code-input_autogrow_width:has(.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog)){--code-input_autogrow_internal-min-width:300px}code-input.code-input_autogrow_height:has(.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog)){--code-input_autogrow_internal-min-height:150px}code-input.code-input_autogrow_width:has(.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog)){--code-input_autogrow_internal-min-width:500px}code-input.code-input_autogrow_height:has(.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog)){--code-input_autogrow_internal-min-height:170px}code-input.code-input_autogrow_height:has(textarea[data-code-input-fallback]){--code-input_autogrow_internal-min-height:calc(1lh + 2em + var(--padding-top,16px) + var(--padding-bottom,16px));height:var(--code-input_autogrow_true-min-height)}code-input textarea[data-code-input-fallback]{min-height:calc(100% - var(--padding-top,16px) - var(--padding-bottom,16px) - 2em);height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - var(--padding-bottom,16px) - 2em)!important} \ No newline at end of file diff --git a/plugins/prism-line-numbers.min.css b/plugins/prism-line-numbers.min.css index aa0b5fd..73ebccb 100644 --- a/plugins/prism-line-numbers.min.css +++ b/plugins/prism-line-numbers.min.css @@ -1 +1 @@ -code-input.line-numbers textarea,code-input.line-numbers.code-input_pre-element-styled pre,code-input.line-numbers:not(.code-input_pre-element-styled) pre code,.line-numbers code-input textarea,.line-numbers code-input.code-input_pre-element-styled pre,.line-numbers code-input:not(.code-input_pre-element-styled) pre code{padding-left:max(3.8em,var(--padding-left,16px))!important}code-input.line-numbers,.line-numbers code-input{grid-template-columns:calc(100% - max(0em,calc(3.8em - var(--padding-left,16px))))}code-input.line-numbers .code-input_dialog-container,.line-numbers code-input .code-input_dialog-container{width:calc(100% + max(0em,calc(3.8em - var(--padding-left,16px))))}code-input pre[class*=language-].line-numbers>code{position:static}code-input .line-numbers .line-numbers-rows{left:max(0em,calc(var(--padding-left,16px) - 3.8em));top:var(--padding-top)}code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused) .line-numbers .line-numbers-rows{top:calc(var(--padding-top) + 3em)}code-input.line-numbers.code-input_autogrow_width textarea,.line-numbers code-input.code-input_autogrow_width textarea{min-width:max(var(--code-input_synced-width),calc(100% - max(3.8em,var(--padding-left,16px)) - var(--padding-right,16px)));width:calc(var(--code-input_autogrow_true-min-width) - max(3.8em,var(--padding-left,16px)) - var(--padding-right,16px))!important}code-input.line-numbers.code-input_autogrow_width pre code,code-input.line-numbers.code-input_autogrow_width pre,.line-numbers code-input.code-input_autogrow_width pre code,.line-numbers code-input.code-input_autogrow_width pre{min-width:calc(var(--code-input_autogrow_true-min-width) - max(3.8em,var(--padding-left,16px)) - var(--padding-right,16px))} \ No newline at end of file +code-input.line-numbers textarea,code-input.line-numbers.code-input_pre-element-styled pre,code-input.line-numbers:not(.code-input_pre-element-styled) pre code,.line-numbers code-input textarea,.line-numbers code-input.code-input_pre-element-styled pre,.line-numbers code-input:not(.code-input_pre-element-styled) pre code{padding-left:max(3.8em, var(--padding-left,16px))!important}code-input.line-numbers,.line-numbers code-input{grid-template-columns:calc(100% - max(0em, calc(3.8em - var(--padding-left,16px))))}code-input.line-numbers .code-input_dialog-container,.line-numbers code-input .code-input_dialog-container{width:calc(100% + max(0em, calc(3.8em - var(--padding-left,16px))))}code-input pre[class*=language-].line-numbers>code{position:static}code-input .line-numbers .line-numbers-rows{left:max(0em, calc(var(--padding-left,16px) - 3.8em));top:var(--padding-top)}code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused) .line-numbers .line-numbers-rows{top:calc(var(--padding-top) + 3em)}code-input.line-numbers.code-input_autogrow_width textarea,.line-numbers code-input.code-input_autogrow_width textarea{min-width:max(var(--code-input_synced-width), calc(100% - max(3.8em, var(--padding-left,16px)) - var(--padding-right,16px)));width:calc(var(--code-input_autogrow_true-min-width) - max(3.8em, var(--padding-left,16px)) - var(--padding-right,16px))!important}code-input.line-numbers.code-input_autogrow_width pre code,code-input.line-numbers.code-input_autogrow_width pre,.line-numbers code-input.code-input_autogrow_width pre code,.line-numbers code-input.code-input_autogrow_width pre{min-width:calc(var(--code-input_autogrow_true-min-width) - max(3.8em, var(--padding-left,16px)) - var(--padding-right,16px))} \ No newline at end of file diff --git a/plugins/special-chars.min.css b/plugins/special-chars.min.css index 48bd958..1a19046 100644 --- a/plugins/special-chars.min.css +++ b/plugins/special-chars.min.css @@ -1 +1 @@ -:root,body{--code-input_special-chars_0:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABtJREFUGFdjZGBgYPj///9/RhCAMcA0bg6yHgAPmh/6BoxTcQAAAABJRU5ErkJgggAA);--code-input_special-chars_1:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABZJREFUGFdjZGBgYPj///9/RhAggwMAitIUBr9U6sYAAAAASUVORK5CYII=);--code-input_special-chars_2:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB9JREFUGFdj/P///38GKGCEMUCCjCgyYBFGRrAKFBkAuLYT9kYcIu0AAAAASUVORK5CYII=);--code-input_special-chars_3:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABhJREFUGFdj/P///38GKGCEMUCCjMTJAACYiBPyG8sfAgAAAABJRU5ErkJggg==);--code-input_special-chars_4:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdj/P///39GRkZGMI3BYYACRhgDrAKZAwAYxhvyz0DRIQAAAABJRU5ErkJggg==);--code-input_special-chars_5:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACJJREFUGFdj/P///38GKGAEcRgZGRlBfDAHLgNjgFUgywAAuR4T9hxJl2YAAAAASUVORK5CYII=);--code-input_special-chars_6:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACBJREFUGFdj/P///38GKGAEcRgZGRlBfDAHQwasAlkGABcdF/Y4yco2AAAAAElFTkSuQmCC);--code-input_special-chars_7:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABZJREFUGFdj/P///38GKGCEMUCCRHIAWMgT8kue3bQAAAAASUVORK5CYII=);--code-input_special-chars_8:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABlJREFUGFdj/P///38GKGAEcRgZGSE0cTIAvHcb8v+mIfAAAAAASUVORK5CYII=);--code-input_special-chars_9:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB9JREFUGFdj/P///38GKGAEcRgZGSE0igxMCVgGmQMAPqcX8hWL1K0AAAAASUVORK5CYII=);--code-input_special-chars_A:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACBJREFUGFdjZGBgYPj///9/RhCAMcA0iADJggCmDEw5ALdxH/aGuYHqAAAAAElFTkSuQmCC);--code-input_special-chars_B:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABlJREFUGFdj/P///38GBgYGRhAAceA0cTIAvc0b/vRDnVoAAAAASUVORK5CYII=);--code-input_special-chars_C:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdjZGBgYPj///9/EM0IYjAyMjIS4CDrAQC57hP+uLwvFQAAAABJRU5ErkJggg==);--code-input_special-chars_D:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABtJREFUGFdj/P///38GBgYGRhAAceA0fg5MDwAveh/6ToN9VwAAAABJRU5ErkJggg==);--code-input_special-chars_E:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABxJREFUGFdj/P///38GKGAEcRgZGRlBfDCHsAwA2UwT+mVIH1MAAAAASUVORK5CYII=);--code-input_special-chars_F:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdj/P///38GKGAEcRgZGRlBfDAHtwxMGQDZZhP+BnB1kwAAAABJRU5ErkJggg==)}.code-input_special-char{height:1em;text-shadow:none;vertical-align:middle;color:#0000;--hex-0:var(--code-input_special-chars_0);--hex-1:var(--code-input_special-chars_0);--hex-2:var(--code-input_special-chars_0);--hex-3:var(--code-input_special-chars_0);outline:.1px solid;font-size:0;text-decoration:none;display:inline-block;position:relative;top:0;left:0;overflow:hidden}.code-input_special-char:before{content:" ";background-color:var(--code-input_special-char_color,currentColor);image-rendering:pixelated;width:calc(100%-2px);height:100%;mask-image:var(--hex-0),var(--hex-1),var(--hex-2),var(--hex-3);mask-position:10% 10%,90% 10%,10% 90%,90% 90%;mask-size:min(40%,.25em),min(40%,.25em),min(40%,.25em),min(40%,.25em);mask-repeat:no-repeat,no-repeat,no-repeat,no-repeat;-webkit-mask-image:var(--hex-0),var(--hex-1),var(--hex-2),var(--hex-3);margin-left:50%;display:inline-block;transform:translate(-50%);-webkit-mask-position:10% 10%,min(90%,.5em) 10%,10% 90%,min(90%,.5em) 90%;-webkit-mask-size:min(40%,.25em),min(40%,.25em),min(40%,.25em),min(40%,.25em);-webkit-mask-repeat:no-repeat,no-repeat,no-repeat,no-repeat}.code-input_special-char_zero-width{z-index:1;opacity:.75;width:1em;margin-left:-.5em;margin-right:-.5em;position:relative}.code-input_special-char_one-byte:before{content:attr(data-hex2);height:1.5em;top:-1em}.code-input_special-char_one-byte:after{content:attr(data-hex3);height:1.5em;bottom:-1em} \ No newline at end of file +:root,body{--code-input_special-chars_0:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABtJREFUGFdjZGBgYPj///9/RhCAMcA0bg6yHgAPmh/6BoxTcQAAAABJRU5ErkJgggAA);--code-input_special-chars_1:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABZJREFUGFdjZGBgYPj///9/RhAggwMAitIUBr9U6sYAAAAASUVORK5CYII=);--code-input_special-chars_2:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB9JREFUGFdj/P///38GKGCEMUCCjCgyYBFGRrAKFBkAuLYT9kYcIu0AAAAASUVORK5CYII=);--code-input_special-chars_3:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABhJREFUGFdj/P///38GKGCEMUCCjMTJAACYiBPyG8sfAgAAAABJRU5ErkJggg==);--code-input_special-chars_4:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdj/P///39GRkZGMI3BYYACRhgDrAKZAwAYxhvyz0DRIQAAAABJRU5ErkJggg==);--code-input_special-chars_5:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACJJREFUGFdj/P///38GKGAEcRgZGRlBfDAHLgNjgFUgywAAuR4T9hxJl2YAAAAASUVORK5CYII=);--code-input_special-chars_6:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACBJREFUGFdj/P///38GKGAEcRgZGRlBfDAHQwasAlkGABcdF/Y4yco2AAAAAElFTkSuQmCC);--code-input_special-chars_7:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABZJREFUGFdj/P///38GKGCEMUCCRHIAWMgT8kue3bQAAAAASUVORK5CYII=);--code-input_special-chars_8:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABlJREFUGFdj/P///38GKGAEcRgZGSE0cTIAvHcb8v+mIfAAAAAASUVORK5CYII=);--code-input_special-chars_9:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB9JREFUGFdj/P///38GKGAEcRgZGSE0igxMCVgGmQMAPqcX8hWL1K0AAAAASUVORK5CYII=);--code-input_special-chars_A:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACBJREFUGFdjZGBgYPj///9/RhCAMcA0iADJggCmDEw5ALdxH/aGuYHqAAAAAElFTkSuQmCC);--code-input_special-chars_B:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABlJREFUGFdj/P///38GBgYGRhAAceA0cTIAvc0b/vRDnVoAAAAASUVORK5CYII=);--code-input_special-chars_C:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdjZGBgYPj///9/EM0IYjAyMjIS4CDrAQC57hP+uLwvFQAAAABJRU5ErkJggg==);--code-input_special-chars_D:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABtJREFUGFdj/P///38GBgYGRhAAceA0fg5MDwAveh/6ToN9VwAAAABJRU5ErkJggg==);--code-input_special-chars_E:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABxJREFUGFdj/P///38GKGAEcRgZGRlBfDCHsAwA2UwT+mVIH1MAAAAASUVORK5CYII=);--code-input_special-chars_F:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdj/P///38GKGAEcRgZGRlBfDAHtwxMGQDZZhP+BnB1kwAAAABJRU5ErkJggg==)}.code-input_special-char{height:1em;text-shadow:none;vertical-align:middle;color:#0000;--hex-0:var(--code-input_special-chars_0);--hex-1:var(--code-input_special-chars_0);--hex-2:var(--code-input_special-chars_0);--hex-3:var(--code-input_special-chars_0);outline:.1px solid;font-size:0;text-decoration:none;display:inline-block;position:relative;top:0;left:0;overflow:hidden}.code-input_special-char:before{content:" ";background-color:var(--code-input_special-char_color,currentColor);image-rendering:pixelated;width:calc(100%-2px);height:100%;mask-image:var(--hex-0), var(--hex-1), var(--hex-2), var(--hex-3);mask-position:10% 10%,90% 10%,10% 90%,90% 90%;mask-size:min(40%,.25em),min(40%,.25em),min(40%,.25em),min(40%,.25em);mask-repeat:no-repeat,no-repeat,no-repeat,no-repeat;-webkit-mask-image:var(--hex-0), var(--hex-1), var(--hex-2), var(--hex-3);margin-left:50%;display:inline-block;transform:translate(-50%);-webkit-mask-position:10% 10%,min(90%,.5em) 10%,10% 90%,min(90%,.5em) 90%;-webkit-mask-size:min(40%,.25em),min(40%,.25em),min(40%,.25em),min(40%,.25em);-webkit-mask-repeat:no-repeat,no-repeat,no-repeat,no-repeat}.code-input_special-char_zero-width{z-index:1;opacity:.75;width:1em;margin-left:-.5em;margin-right:-.5em;position:relative}.code-input_special-char_one-byte:before{content:attr(data-hex2);height:1.5em;top:-1em}.code-input_special-char_one-byte:after{content:attr(data-hex3);height:1.5em;bottom:-1em} \ No newline at end of file From 877e5968f406e55bd96cb64dbac5c630af278adb Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Wed, 21 Jan 2026 23:04:06 +0000 Subject: [PATCH 36/54] Release v2.8.1 --- docs/_index.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/_index.md b/docs/_index.md index e9d243d..28b628b 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -8,7 +8,7 @@ title = 'Flexible Syntax Highlighted Editable Textareas' ## Download -*`code-input.js` is free, libre, open source software under the MIT (AKA Expat) license.* You have choices! **Download it from the Git repository on [GitHub](https://github.com/WebCoder49/code-input/tree/v2.8.0) or [Codeberg](https://codeberg.org/code-input-js/code-input-js/), [in a ZIP archive](/release/code-input-js-v2.8.0.zip), [in a TAR.GZ archive](/release/code-input-js-v2.8.0.tar.gz), or from `@webcoder49/code-input` on the NPM registry ([Yarn](https://yarnpkg.com/package?name=@webcoder49/code-input), [NPM](https://npmjs.com/package/@webcoder49/code-input), etc.).** +*`code-input.js` is free, libre, open source software under the MIT (AKA Expat) license.* You have choices! **Download it from the Git repository on [GitHub](https://github.com/WebCoder49/code-input/tree/v2.8.1) or [Codeberg](https://codeberg.org/code-input-js/code-input-js/), [in a ZIP archive](/release/code-input-js-v2.8.1.zip), [in a TAR.GZ archive](/release/code-input-js-v2.8.1.tar.gz), or from `@webcoder49/code-input` on the NPM registry ([Yarn](https://yarnpkg.com/package?name=@webcoder49/code-input), [NPM](https://npmjs.com/package/@webcoder49/code-input), etc.).** [Want to contribute to the code? You're very welcome to! See here.](#contributing) diff --git a/package.json b/package.json index a10968b..466e5bf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@webcoder49/code-input", - "version": "2.8.0", + "version": "2.8.1", "description": "An editable <textarea> that supports *any* syntax highlighting algorithm, for code or something else. Also, added plugins.", "browser": "code-input.js", "exports": { From 996a0e905453ba4e0f537999f7594c739a37555e Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Tue, 17 Mar 2026 14:04:29 +0000 Subject: [PATCH 37/54] Remove error style from FindAndReplace and GoToLine dialogs when they contain no input text (Fixes #220) --- plugins/find-and-replace.js | 3 +++ plugins/go-to-line.js | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/find-and-replace.js b/plugins/find-and-replace.js index 0b53e02..e312ed9 100644 --- a/plugins/find-and-replace.js +++ b/plugins/find-and-replace.js @@ -123,6 +123,9 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin { // No matches - error dialog.findInput.classList.add('code-input_find-and-replace_error'); } + } else { + // Input box is empty - no error + dialog.findInput.classList.remove('code-input_find-and-replace_error'); } this.updateMatchDescription(dialog); } diff --git a/plugins/go-to-line.js b/plugins/go-to-line.js index 68b79cc..f0420f5 100644 --- a/plugins/go-to-line.js +++ b/plugins/go-to-line.js @@ -81,8 +81,9 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin { } } } else { - // No value + // No value, so no message or error dialog.guidance.textContent = ""; + dialog.input.classList.remove('code-input_go-to-line_error'); } if (event.key == 'Enter') { From b4dca53778715cf1078f8d0ab4f486d036cca183 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Tue, 17 Mar 2026 14:14:04 +0000 Subject: [PATCH 38/54] Improve contrast for FindAndReplace and GoToLine dialog error styles (Fixes #218) --- plugins/find-and-replace.css | 2 +- plugins/go-to-line.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/find-and-replace.css b/plugins/find-and-replace.css index 8b276d9..8675556 100644 --- a/plugins/find-and-replace.css +++ b/plugins/find-and-replace.css @@ -60,7 +60,7 @@ } .code-input_find-and-replace_dialog input.code-input_find-and-replace_error { - color: #ff0000aa; + color: #b60000; } .code-input_find-and-replace_dialog button, .code-input_find-and-replace_dialog input[type="checkbox"] { diff --git a/plugins/go-to-line.css b/plugins/go-to-line.css index fd090a2..edc3a07 100644 --- a/plugins/go-to-line.css +++ b/plugins/go-to-line.css @@ -43,7 +43,7 @@ } .code-input_go-to-line_dialog input.code-input_go-to-line_error { - color: #ff0000aa; + color: #b60000; } /* Cancel icon */ From 6a1b02ea5d09c116e6ed9491ba53518f4ebcf5fe Mon Sep 17 00:00:00 2001 From: WebCoder49 <69071853+WebCoder49@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:33:05 +0000 Subject: [PATCH 39/54] Auto Minify JS and CSS files --- plugins/find-and-replace.min.css | 2 +- plugins/find-and-replace.min.js | 2 +- plugins/go-to-line.min.css | 2 +- plugins/go-to-line.min.js | 2 +- tests/prism-match-braces-compatibility.min.js | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/find-and-replace.min.css b/plugins/find-and-replace.min.css index f51cad4..2f1c43e 100644 --- a/plugins/find-and-replace.min.css +++ b/plugins/find-and-replace.min.css @@ -1 +1 @@ -.code-input_find-and-replace_find-match{color:inherit;text-shadow:none!important;background-color:#ff0!important}.code-input_find-and-replace_find-match-focused,.code-input_find-and-replace_find-match-focused *{color:#000!important;background-color:#f80!important}.code-input_find-and-replace_start-newline:before{content:"⤶"}@keyframes code-input_find-and-replace_roll-in{0%{opacity:0;transform:translateY(-34px)}to{opacity:1;transform:translateY(0)}}.code-input_find-and-replace_dialog{background-color:#fff;border:1px solid #0004;border-radius:6px;padding:8px 6px 6px;position:absolute;top:0;right:14px;box-shadow:0 .2em 1em .2em #00000029}.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog){animation:.2s code-input_find-and-replace_roll-in;display:block}.code-input_find-and-replace_dialog.code-input_find-and-replace_hidden-dialog{display:none}.code-input_find-and-replace_dialog input::placeholder{font-size:80%}.code-input_find-and-replace_dialog input{color:#000a;caret-color:currentColor;border:0;width:240px;height:32px;font-size:large;position:relative;top:-3px}.code-input_find-and-replace_dialog input.code-input_find-and-replace_error{color:#f00a}.code-input_find-and-replace_dialog button,.code-input_find-and-replace_dialog input[type=checkbox]{cursor:pointer;appearance:none;text-align:center;color:#000;vertical-align:top;background-color:#ddd;border:0;width:min-content;margin:5px;padding:5px;font-size:22px;line-height:24px;display:inline-block}.code-input_find-and-replace_dialog input[type=checkbox].code-input_find-and-replace_case-sensitive-checkbox:before{content:"Aa"}.code-input_find-and-replace_dialog input[type=checkbox].code-input_find-and-replace_reg-exp-checkbox:before{content:".*"}.code-input_find-and-replace_dialog button:hover,.code-input_find-and-replace_dialog input[type=checkbox]:hover{background-color:#bbb}.code-input_find-and-replace_dialog input[type=checkbox]:checked{color:#fff;background-color:#222}.code-input_find-and-replace_match-description{color:#444;display:block}.code-input_find-and-replace_dialog details summary,.code-input_find-and-replace_dialog button{cursor:pointer}.code-input_find-and-replace_dialog button.code-input_find-and-replace_button-hidden{opacity:0;pointer-events:none}.code-input_find-and-replace_dialog span{float:right;text-align:center;color:#000;opacity:.6;border-radius:50%;width:24px;margin:5px;padding:5px;font-family:system-ui;font-size:22px;font-weight:500;line-height:24px;display:block}.code-input_find-and-replace_dialog span:before{content:"×"}.code-input_find-and-replace_dialog span:hover{opacity:.8;background-color:#00000018} \ No newline at end of file +.code-input_find-and-replace_find-match{color:inherit;text-shadow:none!important;background-color:#ff0!important}.code-input_find-and-replace_find-match-focused,.code-input_find-and-replace_find-match-focused *{color:#000!important;background-color:#f80!important}.code-input_find-and-replace_start-newline:before{content:"⤶"}@keyframes code-input_find-and-replace_roll-in{0%{opacity:0;transform:translateY(-34px)}to{opacity:1;transform:translateY(0)}}.code-input_find-and-replace_dialog{background-color:#fff;border:1px solid #0004;border-radius:6px;padding:8px 6px 6px;position:absolute;top:0;right:14px;box-shadow:0 .2em 1em .2em #00000029}.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog){animation:.2s code-input_find-and-replace_roll-in;display:block}.code-input_find-and-replace_dialog.code-input_find-and-replace_hidden-dialog{display:none}.code-input_find-and-replace_dialog input::placeholder{font-size:80%}.code-input_find-and-replace_dialog input{color:#000a;caret-color:currentColor;border:0;width:240px;height:32px;font-size:large;position:relative;top:-3px}.code-input_find-and-replace_dialog input.code-input_find-and-replace_error{color:#b60000}.code-input_find-and-replace_dialog button,.code-input_find-and-replace_dialog input[type=checkbox]{cursor:pointer;appearance:none;text-align:center;color:#000;vertical-align:top;background-color:#ddd;border:0;width:min-content;margin:5px;padding:5px;font-size:22px;line-height:24px;display:inline-block}.code-input_find-and-replace_dialog input[type=checkbox].code-input_find-and-replace_case-sensitive-checkbox:before{content:"Aa"}.code-input_find-and-replace_dialog input[type=checkbox].code-input_find-and-replace_reg-exp-checkbox:before{content:".*"}.code-input_find-and-replace_dialog button:hover,.code-input_find-and-replace_dialog input[type=checkbox]:hover{background-color:#bbb}.code-input_find-and-replace_dialog input[type=checkbox]:checked{color:#fff;background-color:#222}.code-input_find-and-replace_match-description{color:#444;display:block}.code-input_find-and-replace_dialog details summary,.code-input_find-and-replace_dialog button{cursor:pointer}.code-input_find-and-replace_dialog button.code-input_find-and-replace_button-hidden{opacity:0;pointer-events:none}.code-input_find-and-replace_dialog span{float:right;text-align:center;color:#000;opacity:.6;border-radius:50%;width:24px;margin:5px;padding:5px;font-family:system-ui;font-size:22px;font-weight:500;line-height:24px;display:block}.code-input_find-and-replace_dialog span:before{content:"×"}.code-input_find-and-replace_dialog span:hover{opacity:.8;background-color:#00000018} \ No newline at end of file diff --git a/plugins/find-and-replace.min.js b/plugins/find-and-replace.min.js index 53ad5d7..c51b137 100644 --- a/plugins/find-and-replace.min.js +++ b/plugins/find-and-replace.min.js @@ -1 +1 @@ -"use strict";codeInput.plugins.FindAndReplace=class extends codeInput.Plugin{useCtrlF=!1;useCtrlH=!1;findMatchesOnValueChange=!0;instructions={start:"Search for matches in your code.",none:"No matches",oneFound:"1 match found.",matchIndex:(a,b)=>`${a} of ${b} matches.`,error:a=>`Error: ${a}`,infiniteLoopError:"Causes an infinite loop",closeDialog:"Close Dialog and Return to Editor",findPlaceholder:"Find",findCaseSensitive:"Match Case Sensitive",findRegExp:"Use JavaScript Regular Expression",replaceTitle:"Replace",replacePlaceholder:"Replace with",findNext:"Find Next Occurrence",findPrevious:"Find Previous Occurrence",replaceActionShort:"Replace",replaceAction:"Replace This Occurrence",replaceAllActionShort:"Replace All",replaceAllAction:"Replace All Occurrences"};constructor(a=!0,b=!0,c={},d=!0){super([]),this.useCtrlF=a,this.useCtrlH=b,this.alwaysCtrl=d,this.addTranslations(this.instructions,c)}afterElementsAdded(a){const b=a.textareaElement;this.useCtrlF&&b.addEventListener("keydown",b=>{this.checkCtrlF(a,b)}),this.useCtrlH&&b.addEventListener("keydown",b=>{this.checkCtrlH(a,b)})}afterHighlight(a){a.pluginData.findAndReplace==null||a.pluginData.findAndReplace.dialog==null||a.pluginData.findAndReplace.dialog.classList.contains("code-input_find-and-replace_hidden-dialog")||(a.pluginData.findAndReplace.dialog.findMatchState.rehighlightMatches(),this.updateMatchDescription(a.pluginData.findAndReplace.dialog),0==a.pluginData.findAndReplace.dialog.findMatchState.numMatches&&a.pluginData.findAndReplace.dialog.findInput.classList.add("code-input_find-and-replace_error"))}text2RegExp(a,b,c){return new RegExp(c?a:a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),b?"g":"gi")}updateMatchDescription(a){a.matchDescription.textContent=0==a.findInput.value.length?this.instructions.start:0>=a.findMatchState.numMatches?this.instructions.none:1==a.findMatchState.numMatches?this.instructions.oneFound:this.instructions.matchIndex(a.findMatchState.focusedMatchID+1,a.findMatchState.numMatches)}updateFindMatches(a){let b=a.findInput.value;setTimeout(()=>{if(b==a.findInput.value){if(a.findMatchState.clearMatches(),0{j.setAttribute("open",!0)}),p.className="code-input_find-and-replace_button-hidden",p.innerText=this.instructions.replaceAllActionShort,p.title=this.instructions.replaceAllAction,p.addEventListener("focus",()=>{j.setAttribute("open",!0)}),m.addEventListener("click",a=>{a.preventDefault(),c.findMatchState.nextMatch(),this.updateMatchDescription(c)}),n.addEventListener("click",()=>{event.preventDefault(),c.findMatchState.previousMatch(),this.updateMatchDescription(c)}),o.addEventListener("click",a=>{a.preventDefault(),c.findMatchState.replaceOnce(i.value),c.focus()}),p.addEventListener("click",a=>{a.preventDefault(),c.findMatchState.replaceAll(i.value),p.focus()}),j.addEventListener("toggle",()=>{o.classList.toggle("code-input_find-and-replace_button-hidden"),p.classList.toggle("code-input_find-and-replace_button-hidden")}),c.findMatchState=new codeInput.plugins.FindAndReplace.FindMatchState(a),c.codeInput=a,c.textarea=d,c.findInput=e,c.findCaseSensitiveCheckbox=f,c.findRegExpCheckbox=g,c.matchDescription=h,c.replaceInput=i,c.replaceDropdown=j,this.checkCtrlH&&e.addEventListener("keydown",a=>{a.ctrlKey&&"h"==a.key&&(a.preventDefault(),j.setAttribute("open",!0))}),e.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),i.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),i.addEventListener("input",()=>{c.classList.contains("code-input_find-and-replace_hidden-dialog")?this.showPrompt(c.codeInput,!0):!c.replaceDropdown.hasAttribute("open")&&c.replaceDropdown.setAttribute("open",!0)}),c.addEventListener("keyup",b=>{"Escape"==b.key&&this.cancelPrompt(c,a,b)}),e.addEventListener("keyup",b=>{this.checkFindPrompt(c,a,b)}),e.addEventListener("input",()=>{this.findMatchesOnValueChange&&this.updateFindMatches(c),c.classList.contains("code-input_find-and-replace_hidden-dialog")&&this.showPrompt(c.codeInput,!1)}),f.addEventListener("click",()=>{this.updateFindMatches(c)}),g.addEventListener("click",()=>{this.updateFindMatches(c)}),i.addEventListener("keyup",b=>{this.checkReplacePrompt(c,a,b),i.focus()}),q.addEventListener("click",b=>{this.cancelPrompt(c,a,b)}),q.addEventListener("keypress",b=>{("Space"==b.key||"Enter"==b.key)&&this.cancelPrompt(c,a,b)}),a.dialogContainerElement.appendChild(c),a.pluginData.findAndReplace={dialog:c},e.focus(),b&&j.setAttribute("open",!0),c.selectionStart=a.textareaElement.selectionStart,c.selectionEnd=a.textareaElement.selectionEnd,c.selectionStart=this.matchStartIndexes.length&&(a=0)}this.focusedMatchStartIndex=this.matchStartIndexes[a],this.focusedMatchID=a;let b=this.codeInput.codeElement.querySelectorAll(".code-input_find-and-replace_find-match-focused");for(let c=0;c=c){if(g.length>=d){if(h){let b=document.createElement("span");b.classList.add("code-input_find-and-replace_find-match"),b.setAttribute("data-code-input_find-and-replace_match-id",a),b.classList.add("code-input_find-and-replace_temporary-span"),b.textContent=g.substring(0,d),"\n"==b.textContent[0]&&b.classList.add("code-input_find-and-replace_start-newline");let c=g.substring(d);return f.textContent=c,f.insertAdjacentElement("beforebegin",b),void e++}return void this.highlightMatchNewlineOnlyAtStart(a,f,0,d)}f.classList.add("code-input_find-and-replace_find-match"),f.setAttribute("data-code-input_find-and-replace_match-id",a),"\n"==f.textContent[0]&&f.classList.add("code-input_find-and-replace_start-newline")}else if(g.length>c){if(!h)this.highlightMatchNewlineOnlyAtStart(a,f,c,d);else if(g.length>d){let b=document.createElement("span");b.classList.add("code-input_find-and-replace_temporary-span"),b.textContent=g.substring(0,c);let h=g.substring(c,d);f.textContent=h,f.classList.add("code-input_find-and-replace_find-match"),f.setAttribute("data-code-input_find-and-replace_match-id",a),"\n"==f.textContent[0]&&f.classList.add("code-input_find-and-replace_start-newline");let i=document.createElement("span");i.classList.add("code-input_find-and-replace_temporary-span"),i.textContent=g.substring(d),f.insertAdjacentElement("beforebegin",b),f.insertAdjacentElement("afterend",i),e++}else{let b=g.substring(0,c);f.textContent=b;let d=document.createElement("span");d.classList.add("code-input_find-and-replace_find-match"),d.setAttribute("data-code-input_find-and-replace_match-id",a),d.classList.add("code-input_find-and-replace_temporary-span"),d.textContent=g.substring(c),"\n"==d.textContent[0]&&d.classList.add("code-input_find-and-replace_start-newline"),f.insertAdjacentElement("afterend",d),e++}if(g.length>d)return}c-=g.length,d-=g.length}}}; \ No newline at end of file +"use strict";codeInput.plugins.FindAndReplace=class extends codeInput.Plugin{useCtrlF=!1;useCtrlH=!1;findMatchesOnValueChange=!0;instructions={start:"Search for matches in your code.",none:"No matches",oneFound:"1 match found.",matchIndex:(a,b)=>`${a} of ${b} matches.`,error:a=>`Error: ${a}`,infiniteLoopError:"Causes an infinite loop",closeDialog:"Close Dialog and Return to Editor",findPlaceholder:"Find",findCaseSensitive:"Match Case Sensitive",findRegExp:"Use JavaScript Regular Expression",replaceTitle:"Replace",replacePlaceholder:"Replace with",findNext:"Find Next Occurrence",findPrevious:"Find Previous Occurrence",replaceActionShort:"Replace",replaceAction:"Replace This Occurrence",replaceAllActionShort:"Replace All",replaceAllAction:"Replace All Occurrences"};constructor(a=!0,b=!0,c={},d=!0){super([]),this.useCtrlF=a,this.useCtrlH=b,this.alwaysCtrl=d,this.addTranslations(this.instructions,c)}afterElementsAdded(a){const b=a.textareaElement;this.useCtrlF&&b.addEventListener("keydown",b=>{this.checkCtrlF(a,b)}),this.useCtrlH&&b.addEventListener("keydown",b=>{this.checkCtrlH(a,b)})}afterHighlight(a){a.pluginData.findAndReplace==null||a.pluginData.findAndReplace.dialog==null||a.pluginData.findAndReplace.dialog.classList.contains("code-input_find-and-replace_hidden-dialog")||(a.pluginData.findAndReplace.dialog.findMatchState.rehighlightMatches(),this.updateMatchDescription(a.pluginData.findAndReplace.dialog),0==a.pluginData.findAndReplace.dialog.findMatchState.numMatches&&a.pluginData.findAndReplace.dialog.findInput.classList.add("code-input_find-and-replace_error"))}text2RegExp(a,b,c){return new RegExp(c?a:a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),b?"g":"gi")}updateMatchDescription(a){a.matchDescription.textContent=0==a.findInput.value.length?this.instructions.start:0>=a.findMatchState.numMatches?this.instructions.none:1==a.findMatchState.numMatches?this.instructions.oneFound:this.instructions.matchIndex(a.findMatchState.focusedMatchID+1,a.findMatchState.numMatches)}updateFindMatches(a){let b=a.findInput.value;setTimeout(()=>{if(b==a.findInput.value){if(a.findMatchState.clearMatches(),0{j.setAttribute("open",!0)}),p.className="code-input_find-and-replace_button-hidden",p.innerText=this.instructions.replaceAllActionShort,p.title=this.instructions.replaceAllAction,p.addEventListener("focus",()=>{j.setAttribute("open",!0)}),m.addEventListener("click",a=>{a.preventDefault(),c.findMatchState.nextMatch(),this.updateMatchDescription(c)}),n.addEventListener("click",()=>{event.preventDefault(),c.findMatchState.previousMatch(),this.updateMatchDescription(c)}),o.addEventListener("click",a=>{a.preventDefault(),c.findMatchState.replaceOnce(i.value),c.focus()}),p.addEventListener("click",a=>{a.preventDefault(),c.findMatchState.replaceAll(i.value),p.focus()}),j.addEventListener("toggle",()=>{o.classList.toggle("code-input_find-and-replace_button-hidden"),p.classList.toggle("code-input_find-and-replace_button-hidden")}),c.findMatchState=new codeInput.plugins.FindAndReplace.FindMatchState(a),c.codeInput=a,c.textarea=d,c.findInput=e,c.findCaseSensitiveCheckbox=f,c.findRegExpCheckbox=g,c.matchDescription=h,c.replaceInput=i,c.replaceDropdown=j,this.checkCtrlH&&e.addEventListener("keydown",a=>{a.ctrlKey&&"h"==a.key&&(a.preventDefault(),j.setAttribute("open",!0))}),e.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),i.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),i.addEventListener("input",()=>{c.classList.contains("code-input_find-and-replace_hidden-dialog")?this.showPrompt(c.codeInput,!0):!c.replaceDropdown.hasAttribute("open")&&c.replaceDropdown.setAttribute("open",!0)}),c.addEventListener("keyup",b=>{"Escape"==b.key&&this.cancelPrompt(c,a,b)}),e.addEventListener("keyup",b=>{this.checkFindPrompt(c,a,b)}),e.addEventListener("input",()=>{this.findMatchesOnValueChange&&this.updateFindMatches(c),c.classList.contains("code-input_find-and-replace_hidden-dialog")&&this.showPrompt(c.codeInput,!1)}),f.addEventListener("click",()=>{this.updateFindMatches(c)}),g.addEventListener("click",()=>{this.updateFindMatches(c)}),i.addEventListener("keyup",b=>{this.checkReplacePrompt(c,a,b),i.focus()}),q.addEventListener("click",b=>{this.cancelPrompt(c,a,b)}),q.addEventListener("keypress",b=>{("Space"==b.key||"Enter"==b.key)&&this.cancelPrompt(c,a,b)}),a.dialogContainerElement.appendChild(c),a.pluginData.findAndReplace={dialog:c},e.focus(),b&&j.setAttribute("open",!0),c.selectionStart=a.textareaElement.selectionStart,c.selectionEnd=a.textareaElement.selectionEnd,c.selectionStart=this.matchStartIndexes.length&&(a=0)}this.focusedMatchStartIndex=this.matchStartIndexes[a],this.focusedMatchID=a;let b=this.codeInput.codeElement.querySelectorAll(".code-input_find-and-replace_find-match-focused");for(let c=0;c=c){if(g.length>=d){if(h){let b=document.createElement("span");b.classList.add("code-input_find-and-replace_find-match"),b.setAttribute("data-code-input_find-and-replace_match-id",a),b.classList.add("code-input_find-and-replace_temporary-span"),b.textContent=g.substring(0,d),"\n"==b.textContent[0]&&b.classList.add("code-input_find-and-replace_start-newline");let c=g.substring(d);return f.textContent=c,f.insertAdjacentElement("beforebegin",b),void e++}return void this.highlightMatchNewlineOnlyAtStart(a,f,0,d)}f.classList.add("code-input_find-and-replace_find-match"),f.setAttribute("data-code-input_find-and-replace_match-id",a),"\n"==f.textContent[0]&&f.classList.add("code-input_find-and-replace_start-newline")}else if(g.length>c){if(!h)this.highlightMatchNewlineOnlyAtStart(a,f,c,d);else if(g.length>d){let b=document.createElement("span");b.classList.add("code-input_find-and-replace_temporary-span"),b.textContent=g.substring(0,c);let h=g.substring(c,d);f.textContent=h,f.classList.add("code-input_find-and-replace_find-match"),f.setAttribute("data-code-input_find-and-replace_match-id",a),"\n"==f.textContent[0]&&f.classList.add("code-input_find-and-replace_start-newline");let i=document.createElement("span");i.classList.add("code-input_find-and-replace_temporary-span"),i.textContent=g.substring(d),f.insertAdjacentElement("beforebegin",b),f.insertAdjacentElement("afterend",i),e++}else{let b=g.substring(0,c);f.textContent=b;let d=document.createElement("span");d.classList.add("code-input_find-and-replace_find-match"),d.setAttribute("data-code-input_find-and-replace_match-id",a),d.classList.add("code-input_find-and-replace_temporary-span"),d.textContent=g.substring(c),"\n"==d.textContent[0]&&d.classList.add("code-input_find-and-replace_start-newline"),f.insertAdjacentElement("afterend",d),e++}if(g.length>d)return}c-=g.length,d-=g.length}}}; \ No newline at end of file diff --git a/plugins/go-to-line.min.css b/plugins/go-to-line.min.css index d0b8917..e3449cb 100644 --- a/plugins/go-to-line.min.css +++ b/plugins/go-to-line.min.css @@ -1 +1 @@ -@keyframes code-input_go-to-line_roll-in{0%{opacity:0;transform:translateY(-34px)}to{opacity:1;transform:translateY(0)}}.code-input_go-to-line_dialog{background-color:#fff;border:1px solid #0004;border-radius:6px;padding:8px 6px 6px;position:absolute;top:0;right:14px;box-shadow:0 .2em 1em .2em #00000029}.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog){animation:.2s code-input_go-to-line_roll-in;display:block}.code-input_go-to-line_dialog.code-input_go-to-line_hidden-dialog{display:none}.code-input_go-to-line_dialog input::placeholder{font-size:80%}.code-input_go-to-line_dialog input{color:#000a;caret-color:currentColor;border:0;width:240px;height:32px;font-size:large;position:relative;top:-3px}.code-input_go-to-line_dialog input.code-input_go-to-line_error{color:#f00a}.code-input_go-to-line_dialog span{text-align:center;color:#000;opacity:.6;vertical-align:top;border-radius:50%;width:24px;font-family:system-ui;font-size:22px;font-weight:500;line-height:24px;display:inline-block}.code-input_go-to-line_dialog span:before{content:"×"}.code-input_go-to-line_dialog span:hover{opacity:.8;background-color:#00000018}.code-input_go-to-line_dialog p{width:264px;white-space:wrap;margin:0;font-family:monospace;overflow:hidden} \ No newline at end of file +@keyframes code-input_go-to-line_roll-in{0%{opacity:0;transform:translateY(-34px)}to{opacity:1;transform:translateY(0)}}.code-input_go-to-line_dialog{background-color:#fff;border:1px solid #0004;border-radius:6px;padding:8px 6px 6px;position:absolute;top:0;right:14px;box-shadow:0 .2em 1em .2em #00000029}.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog){animation:.2s code-input_go-to-line_roll-in;display:block}.code-input_go-to-line_dialog.code-input_go-to-line_hidden-dialog{display:none}.code-input_go-to-line_dialog input::placeholder{font-size:80%}.code-input_go-to-line_dialog input{color:#000a;caret-color:currentColor;border:0;width:240px;height:32px;font-size:large;position:relative;top:-3px}.code-input_go-to-line_dialog input.code-input_go-to-line_error{color:#b60000}.code-input_go-to-line_dialog span{text-align:center;color:#000;opacity:.6;vertical-align:top;border-radius:50%;width:24px;font-family:system-ui;font-size:22px;font-weight:500;line-height:24px;display:inline-block}.code-input_go-to-line_dialog span:before{content:"×"}.code-input_go-to-line_dialog span:hover{opacity:.8;background-color:#00000018}.code-input_go-to-line_dialog p{width:264px;white-space:wrap;margin:0;font-family:monospace;overflow:hidden} \ No newline at end of file diff --git a/plugins/go-to-line.min.js b/plugins/go-to-line.min.js index 46a84ff..96f8b27 100644 --- a/plugins/go-to-line.min.js +++ b/plugins/go-to-line.min.js @@ -1 +1 @@ -"use strict";codeInput.plugins.GoToLine=class extends codeInput.Plugin{useCtrlG=!1;instructions={closeDialog:"Close Dialog and Return to Editor",input:"Line:Column / Line no. then Enter",guidanceFormat:"Wrong format. Enter a line number (e.g. 1) or a line number then colon then column number (e.g. 1:3).",guidanceLineRange:(a,b)=>`Line number (currently ${a}) should be between 1 and ${b} inclusive.`,guidanceColumnRange:(a,b,c)=>`On line ${a}, column number (currently ${b}) should be between 1 and ${c} inclusive.`,guidanceValidLine:a=>`Press Enter to go to line ${a}.`,guidanceValidColumn:(a,b)=>`Press Enter to go to line ${a}, column ${b}.`};constructor(a=!0,b={}){super([]),this.useCtrlG=a,this.addTranslations(this.instructions,b)}afterElementsAdded(a){const b=a.textareaElement;this.useCtrlG&&b.addEventListener("keydown",b=>{this.checkCtrlG(a,b)})}checkPrompt(a,b){if("Escape"==b.key)return this.cancelPrompt(a,b);const c=a.textarea.value.split("\n"),d=c.length,e=+a.input.value.split(":")[0];let f=0,g=1;const h=a.input.value.split(":");if(2e||e>d)return a.guidance.textContent=this.instructions.guidanceLineRange(e,d),a.input.classList.add("code-input_go-to-line_error");if(2<=h.length&&(f=+h[1],g=c[e-1].length+1),0>f||f>g)return a.guidance.textContent=this.instructions.guidanceColumnRange(e,f,g),a.input.classList.add("code-input_go-to-line_error");a.guidance.textContent=0===f?this.instructions.guidanceValidLine(e):this.instructions.guidanceValidColumn(e,f),a.input.classList.remove("code-input_go-to-line_error")}else a.guidance.textContent="";"Enter"==b.key&&(this.goTo(a.textarea,e,f),this.cancelPrompt(a,b))}cancelPrompt(a,b){b.preventDefault(),a.codeInput.handleEventsFromTextarea=!1,a.textarea.focus(),a.codeInput.handleEventsFromTextarea=!0,a.setAttribute("inert",!0),a.setAttribute("tabindex",-1),a.setAttribute("aria-hidden",!0),a.classList.add("code-input_go-to-line_hidden-dialog"),a.input.value=""}showPrompt(a){if(a.pluginData.goToLine==null||a.pluginData.goToLine.dialog==null){const b=a.textareaElement,c=document.createElement("div"),d=document.createElement("input"),e=document.createElement("span");e.setAttribute("role","button"),e.setAttribute("aria-label",this.instructions.closeDialog),e.setAttribute("tabindex",0),e.setAttribute("title",this.instructions.closeDialog);const f=document.createElement("p");f.setAttribute("aria-live","assertive"),f.textContent="",c.appendChild(d),c.appendChild(e),c.appendChild(f),c.className="code-input_go-to-line_dialog",d.spellcheck=!1,d.placeholder=this.instructions.input,c.codeInput=a,c.textarea=b,c.input=d,c.guidance=f,d.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),d.addEventListener("keyup",a=>this.checkPrompt(c,a)),e.addEventListener("click",a=>{this.cancelPrompt(c,a)}),e.addEventListener("keypress",a=>{("Space"==a.key||"Enter"==a.key)&&this.cancelPrompt(c,a)}),a.dialogContainerElement.appendChild(c),a.pluginData.goToLine={dialog:c},d.focus()}else a.pluginData.goToLine.dialog.classList.remove("code-input_go-to-line_hidden-dialog"),a.pluginData.goToLine.dialog.removeAttribute("inert"),a.pluginData.goToLine.dialog.setAttribute("tabindex",0),a.pluginData.goToLine.dialog.removeAttribute("aria-hidden"),a.pluginData.goToLine.dialog.input.focus()}goTo(a,b,c=0){let d,e,f,g,h=-1,i=a.value.split("\n");if(0`Line number (currently ${a}) should be between 1 and ${b} inclusive.`,guidanceColumnRange:(a,b,c)=>`On line ${a}, column number (currently ${b}) should be between 1 and ${c} inclusive.`,guidanceValidLine:a=>`Press Enter to go to line ${a}.`,guidanceValidColumn:(a,b)=>`Press Enter to go to line ${a}, column ${b}.`};constructor(a=!0,b={}){super([]),this.useCtrlG=a,this.addTranslations(this.instructions,b)}afterElementsAdded(a){const b=a.textareaElement;this.useCtrlG&&b.addEventListener("keydown",b=>{this.checkCtrlG(a,b)})}checkPrompt(a,b){if("Escape"==b.key)return this.cancelPrompt(a,b);const c=a.textarea.value.split("\n"),d=c.length,e=+a.input.value.split(":")[0];let f=0,g=1;const h=a.input.value.split(":");if(2e||e>d)return a.guidance.textContent=this.instructions.guidanceLineRange(e,d),a.input.classList.add("code-input_go-to-line_error");if(2<=h.length&&(f=+h[1],g=c[e-1].length+1),0>f||f>g)return a.guidance.textContent=this.instructions.guidanceColumnRange(e,f,g),a.input.classList.add("code-input_go-to-line_error");a.guidance.textContent=0===f?this.instructions.guidanceValidLine(e):this.instructions.guidanceValidColumn(e,f),a.input.classList.remove("code-input_go-to-line_error")}else a.guidance.textContent="",a.input.classList.remove("code-input_go-to-line_error");"Enter"==b.key&&(this.goTo(a.textarea,e,f),this.cancelPrompt(a,b))}cancelPrompt(a,b){b.preventDefault(),a.codeInput.handleEventsFromTextarea=!1,a.textarea.focus(),a.codeInput.handleEventsFromTextarea=!0,a.setAttribute("inert",!0),a.setAttribute("tabindex",-1),a.setAttribute("aria-hidden",!0),a.classList.add("code-input_go-to-line_hidden-dialog"),a.input.value=""}showPrompt(a){if(a.pluginData.goToLine==null||a.pluginData.goToLine.dialog==null){const b=a.textareaElement,c=document.createElement("div"),d=document.createElement("input"),e=document.createElement("span");e.setAttribute("role","button"),e.setAttribute("aria-label",this.instructions.closeDialog),e.setAttribute("tabindex",0),e.setAttribute("title",this.instructions.closeDialog);const f=document.createElement("p");f.setAttribute("aria-live","assertive"),f.textContent="",c.appendChild(d),c.appendChild(e),c.appendChild(f),c.className="code-input_go-to-line_dialog",d.spellcheck=!1,d.placeholder=this.instructions.input,c.codeInput=a,c.textarea=b,c.input=d,c.guidance=f,d.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),d.addEventListener("keyup",a=>this.checkPrompt(c,a)),e.addEventListener("click",a=>{this.cancelPrompt(c,a)}),e.addEventListener("keypress",a=>{("Space"==a.key||"Enter"==a.key)&&this.cancelPrompt(c,a)}),a.dialogContainerElement.appendChild(c),a.pluginData.goToLine={dialog:c},d.focus()}else a.pluginData.goToLine.dialog.classList.remove("code-input_go-to-line_hidden-dialog"),a.pluginData.goToLine.dialog.removeAttribute("inert"),a.pluginData.goToLine.dialog.setAttribute("tabindex",0),a.pluginData.goToLine.dialog.removeAttribute("aria-hidden"),a.pluginData.goToLine.dialog.input.focus()}goTo(a,b,c=0){let d,e,f,g,h=-1,i=a.value.split("\n");if(0{m.test(a.id)&&c.apply(a)},deselectAllBraces=b=>{for(let c=b.getElementsByClassName(a("brace-hover"));0{m.test(a.id)&&c.apply(a)},deselectAllBraces=b=>{for(let c=b.getElementsByClassName(a("brace-hover"));0 Date: Tue, 17 Mar 2026 14:34:25 +0000 Subject: [PATCH 40/54] Remove console.logs accidentally left from testing --- code-input.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/code-input.js b/code-input.js index 95c4697..a728df2 100644 --- a/code-input.js +++ b/code-input.js @@ -587,7 +587,6 @@ var codeInput = { if(getComputedStyle(this).color == "rgb(255, 255, 255)") { // Definitely not overriden this.internalStyle.removeProperty("--code-input_no-override-color"); - console.log(this, "Autoadapt; " + oldTransition); this.style.transition = oldTransition; const highlightedTextColor = getComputedStyle(this.getStyledHighlightingElement()).color; @@ -606,7 +605,6 @@ var codeInput = { callbackIfOverridden(); } this.internalStyle.removeProperty("--code-input_no-override-color"); - console.log(this, "No autoadapt; " + oldTransition); this.style.transition = oldTransition; }); } From ffe146b8404640d5114b9097555bae9e31ef836f Mon Sep 17 00:00:00 2001 From: WebCoder49 <69071853+WebCoder49@users.noreply.github.com> Date: Tue, 17 Mar 2026 14:35:54 +0000 Subject: [PATCH 41/54] Auto Minify JS and CSS files --- code-input.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code-input.min.js b/code-input.min.js index 7c6e068..bbb1003 100644 --- a/code-input.min.js +++ b/code-input.min.js @@ -9,4 +9,4 @@ * @license MIT * * **** - */"use strict";var codeInput={observedAttributes:["value","placeholder","language","lang","template"],textareaSyncAttributes:["value","min","max","type","pattern","autocomplete","autocorrect","autofocus","cols","dirname","disabled","form","maxlength","minlength","name","placeholder","readonly","required","rows","spellcheck","wrap"],textareaSyncEvents:["change","selectionchange","invalid","input","focus","blur","focusin","focusout"],usedTemplates:{},defaultTemplate:void 0,templateNotYetRegisteredQueue:{},registerTemplate:function(a,b){if(!("string"==typeof a||a instanceof String))throw TypeError(`code-input: Name of template "${a}" must be a string.`);if(!("function"==typeof b.highlight||b.highlight instanceof Function))throw TypeError(`code-input: Template for "${a}" invalid, because the highlight function provided is not a function; it is "${b.highlight}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.includeCodeInputInHighlightFunc||b.includeCodeInputInHighlightFunc instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the includeCodeInputInHighlightFunc value provided is not a true or false; it is "${b.includeCodeInputInHighlightFunc}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.preElementStyled||b.preElementStyled instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the preElementStyled value provided is not a true or false; it is "${b.preElementStyled}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.isCode||b.isCode instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the isCode value provided is not a true or false; it is "${b.isCode}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!Array.isArray(b.plugins))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin array provided is not an array; it is "${b.plugins}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(b.plugins.forEach((c,d)=>{if(!(c instanceof codeInput.Plugin))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin provided at index ${d} is not valid; it is "${b.plugins[d]}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`)}),codeInput.usedTemplates[a]=b,a in codeInput.templateNotYetRegisteredQueue)for(let c in codeInput.templateNotYetRegisteredQueue[a]){const d=codeInput.templateNotYetRegisteredQueue[a][c];d.templateObject=b,d.setup()}if(null==codeInput.defaultTemplate&&(codeInput.defaultTemplate=a,void 0 in codeInput.templateNotYetRegisteredQueue))for(let a in codeInput.templateNotYetRegisteredQueue[void 0]){const c=codeInput.templateNotYetRegisteredQueue[void 0][a];c.templateObject=b,c.setup()}},stylesheetI:0,Template:class{constructor(a=function(){},b=!0,c=!0,d=!1,e=[]){this.highlight=a,this.preElementStyled=b,this.isCode=c,this.includeCodeInputInHighlightFunc=d,this.plugins=e}highlight=function(){};preElementStyled=!0;isCode=!0;includeCodeInputInHighlightFunc=!1;plugins=[]},templates:{prism(a,b=[]){return new codeInput.templates.Prism(a,b)},hljs(a,b=[]){return new codeInput.templates.Hljs(a,b)},characterLimit(a){return{highlight:function(a,b,c=[]){let d=+b.getAttribute("data-character-limit"),e=b.escapeHtml(b.value.slice(0,d)),f=b.escapeHtml(b.value.slice(d));a.innerHTML=`${e}${f}`,0${b.getAttribute("data-overflow-msg")||"(Character limit reached)"}`)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,plugins:a}},rainbowText(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return{highlight:function(a,b){let c=[],d=b.value.split(b.template.delimiter);for(let e=0;e${b.escapeHtml(d[e])}`);a.innerHTML=c.join(b.template.delimiter)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,rainbowColors:a,delimiter:b,plugins:c}},character_limit(){return this.characterLimit([])},rainbow_text(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return this.rainbowText(a,b,c)},custom(a=function(){},b=!0,c=!0,d=!1,e=[]){return{highlight:a,includeCodeInputInHighlightFunc:d,preElementStyled:b,isCode:c,plugins:e}}},plugins:new Proxy({},{get(a,b){if(a[b]==null)throw ReferenceError(`code-input: Plugin '${b}' is not defined. Please ensure you import the necessary files from the plugins folder in the WebCoder49/code-input repository, in the of your HTML, before the plugin is instatiated.`);return a[b]}}),Plugin:class{constructor(a){a.forEach(a=>{codeInput.observedAttributes.push(a)})}addTranslations(a,b){for(const c in b)a[c]=b[c]}beforeHighlight(){}afterHighlight(){}beforeElementsAdded(){}afterElementsAdded(){}attributeChanged(){}},CodeInput:class extends HTMLElement{constructor(){super()}templateObject=null;textareaElement=null;preElement=null;codeElement=null;dialogContainerElement=null;internalStyle=null;static formAssociated=!0;boundEventCallbacks={};pluginEvt(a,b){for(let c in this.templateObject.plugins){let d=this.templateObject.plugins[c];a in d&&(b===void 0?d[a](this):d[a](this,...b))}}needsHighlight=!1;originalAriaDescription;scheduleHighlight(){this.needsHighlight=!0}animateFrame(){this.needsHighlight&&(this.update(),this.needsHighlight=!1),window.requestAnimationFrame(this.animateFrame.bind(this))}update(){let a=this.codeElement,b=this.value;b+="\n",a.innerHTML=this.escapeHtml(b),this.pluginEvt("beforeHighlight"),this.templateObject.includeCodeInputInHighlightFunc?this.templateObject.highlight(a,this):this.templateObject.highlight(a),this.syncSize(),this.pluginEvt("afterHighlight")}getStyledHighlightingElement(){return this.templateObject.preElementStyled?this.preElement:this.codeElement}syncSize(){const a=getComputedStyle(this.getStyledHighlightingElement()).height;this.textareaElement.style.height=a,this.internalStyle.setProperty("--code-input_synced-height",a);const b=getComputedStyle(this.getStyledHighlightingElement()).width;this.textareaElement.style.width=b,this.internalStyle.setProperty("--code-input_synced-width",b)}syncIfColorNotOverridden(a=function(){}){if(this.checkingColorOverridden)return;this.checkingColorOverridden=!0;const b=this.style.transition;this.style.transition="unset",window.requestAnimationFrame(()=>{if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(0, 0, 0)"),"rgb(0, 0, 0)"!=getComputedStyle(this).color)this.style.transition=b,this.checkingColorOverridden=!1,a();else if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(255, 255, 255)"),"rgb(255, 255, 255)"==getComputedStyle(this).color){this.internalStyle.removeProperty("--code-input_no-override-color"),console.log(this,"Autoadapt; "+b),this.style.transition=b;const a=getComputedStyle(this.getStyledHighlightingElement()).color;this.internalStyle.setProperty("--code-input_highlight-text-color",a),this.internalStyle.setProperty("--code-input_default-caret-color",a),this.checkingColorOverridden=!1}else this.style.transition=b,this.checkingColorOverridden=!1,a();this.internalStyle.removeProperty("--code-input_no-override-color"),console.log(this,"No autoadapt; "+b),this.style.transition=b})}syncColorCompletely(){this.syncIfColorNotOverridden(()=>{this.internalStyle.removeProperty("--code-input_highlight-text-color"),this.internalStyle.setProperty("--code-input_default-caret-color",getComputedStyle(this).color)})}setKeyboardNavInstructions(a,b){this.dialogContainerElement.querySelector(".code-input_keyboard-navigation-instructions").innerText=a,b?this.textareaElement.setAttribute("aria-description",this.originalAriaDescription+". "+a):this.textareaElement.setAttribute("aria-description",a)}escapeHtml(a){return a.replace(/&/g,"&").replace(/")}getTemplate(){let a;return a=null==this.getAttribute("template")?codeInput.defaultTemplate:this.getAttribute("template"),a in codeInput.usedTemplates?codeInput.usedTemplates[a]:(a in codeInput.templateNotYetRegisteredQueue||(codeInput.templateNotYetRegisteredQueue[a]=[]),void codeInput.templateNotYetRegisteredQueue[a].push(this))}setup(){if(null!=this.textareaElement)return;this.classList.add("code-input_registered"),this.mutationObserver=new MutationObserver(this.mutationObserverCallback.bind(this)),this.mutationObserver.observe(this,{attributes:!0,attributeOldValue:!0}),this.classList.add("code-input_registered"),this.templateObject.preElementStyled&&this.classList.add("code-input_pre-element-styled");const a=this.querySelector("textarea[data-code-input-fallback]");let b,c,d,e,f,g=!1;a&&(a===document.activeElement&&(g=!0),b=a.selectionStart,c=a.selectionEnd,d=a.selectionDirection,e=a.scrollLeft,f=a.scrollTop);let h;if(a){let b=a.getAttributeNames();for(let c=0;c{this.classList.add("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("blur",()=>{this.classList.remove("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("focus",()=>{window.setTimeout(()=>{this.syncSize()},0)}),this.innerHTML="",this.classList.add("code-input_styles_"+codeInput.stylesheetI);const l=document.createElement("style");l.innerHTML="code-input.code-input_styles_"+codeInput.stylesheetI+" {}",this.appendChild(l),this.internalStyle=l.sheet.cssRules[0].style,codeInput.stylesheetI++;for(let a,b=0;b{this.value=this.textareaElement.value}),this.textareaElement=k,this.append(k),this.setupTextareaSyncEvents(this.textareaElement);let m=document.createElement("code"),n=document.createElement("pre");n.setAttribute("aria-hidden","true"),n.setAttribute("tabindex","-1"),n.setAttribute("inert",!0),this.preElement=n,this.codeElement=m,n.append(m),this.append(n),this.templateObject.isCode&&i!=null&&""!=i&&m.classList.add("language-"+i.toLowerCase());let o=document.createElement("div");o.classList.add("code-input_dialog-container"),this.append(o),this.dialogContainerElement=o;let p=document.createElement("div");p.classList.add("code-input_keyboard-navigation-instructions"),o.append(p),this.pluginEvt("afterElementsAdded"),this.dispatchEvent(new CustomEvent("code-input_load")),this.value=h,b!==void 0&&(k.setSelectionRange(b,c,d),k.scrollTo(f,e)),g&&k.focus(),this.animateFrame();const q=new ResizeObserver(()=>{this.syncSize()});q.observe(this),q.observe(this.preElement),q.observe(this.codeElement);const r=a=>{"color"==a.propertyName&&this.syncIfColorNotOverridden()};this.preElement.addEventListener("transitionend",r),this.preElement.addEventListener("-webkit-transitionend",r);const s=a=>{"color"==a.propertyName&&this.syncColorCompletely(),a.target==this.dialogContainerElement&&a.stopPropagation()};this.dialogContainerElement.addEventListener("transitionend",s),this.dialogContainerElement.addEventListener("-webkit-transitionend",s),this.addEventListener("transitionend",s),this.addEventListener("-webkit-transitionend",s),this.syncColorCompletely(),this.classList.add("code-input_loaded")}escape_html(a){return this.escapeHtml(a)}get_template(){return this.getTemplate()}get template(){return this.templateObject}set template(a){}connectedCallback(){if(this.templateObject=this.getTemplate(),null!=this.templateObject&&(this.classList.add("code-input_registered"),"loading"===document.readyState?window.addEventListener("DOMContentLoaded",this.setup.bind(this)):this.setup()),"loading"===document.readyState)window.addEventListener("DOMContentLoaded",()=>{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)});else{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)}}mutationObserverCallback(a){for(const b of a)if("attributes"===b.type){for(let a=0;a{a.match(b)&&(null==c?this.textareaElement.removeAttribute(a):this.textareaElement.setAttribute(a,c))})}}setupTextareaSyncEvents(a){for(let b=0;b{a.bubbles||this.dispatchEvent(new a.constructor(a.type,a))})}}addEventListener(a,b,c=void 0){let d=function(a){"function"==typeof b?b(a):b&&b.handleEvent&&b.handleEvent(a)}.bind(this);if(this.boundEventCallbacks[b]=d,!codeInput.textareaSyncEvents.includes(a))void 0===c?super.addEventListener(a,d):super.addEventListener(a,d,c);else if(this.boundEventCallbacks[b]=d,void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d)})}else this.textareaElement.addEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d,c)})}else this.textareaElement.addEventListener(a,d,c)}removeEventListener(a,b,c=void 0){let d=this.boundEventCallbacks[b];if(!codeInput.textareaSyncEvents.includes(a))void 0===c?super.removeEventListener(a,d):super.removeEventListener(a,d,c);else if(void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d)})}else this.textareaElement.removeEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d,c)})}else this.textareaElement.removeEventListener(a,d,c)}getTextareaProperty(a,b=void 0){if(this.textareaElement)return this.textareaElement[a];else{const c=this.querySelector("textarea[data-code-input-fallback]");if(c)return c[a];if(void 0===b)throw new Error("Cannot get "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.");return b}}setTextareaProperty(a,b,c=!0){if(this.textareaElement)this.textareaElement[a]=b;else{const d=this.querySelector("textarea[data-code-input-fallback]");if(d)d[a]=b;else{if(!c)return(!1);throw new Error("Cannot set "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.")}}return(!0)}get autocomplete(){return this.getAttribute("autocomplete")}set autocomplete(a){return this.setAttribute("autocomplete",a)}get cols(){return this.getTextareaProperty("cols",+this.getAttribute("cols"))}set cols(a){this.setAttribute("cols",a)}get defaultValue(){return this.initialValue}set defaultValue(a){this.initialValue=a}get textContent(){return this.initialValue}set textContent(a){this.initialValue=a}get dirName(){return this.getAttribute("dirName")||""}set dirName(a){this.setAttribute("dirname",a)}get disabled(){return this.hasAttribute("disabled")}set disabled(a){a?this.setAttribute("disabled",!0):this.removeAttribute("disabled")}get form(){return this.getTextareaProperty("form")}get labels(){return this.getTextareaProperty("labels")}get maxLength(){const a=+this.getAttribute("maxlength");return isNaN(a)?-1:a}set maxLength(a){-1==a?this.removeAttribute("maxlength"):this.setAttribute("maxlength",a)}get minLength(){const a=+this.getAttribute("minlength");return isNaN(a)?-1:a}set minLength(a){-1==a?this.removeAttribute("minlength"):this.setAttribute("minlength",a)}get name(){return this.getAttribute("name")||""}set name(a){this.setAttribute("name",a)}get placeholder(){return this.getAttribute("placeholder")||""}set placeholder(a){this.setAttribute("placeholder",a)}get readOnly(){return this.hasAttribute("readonly")}set readOnly(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get required(){return this.hasAttribute("readonly")}set required(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get rows(){return this.getTextareaProperty("rows",+this.getAttribute("rows"))}set rows(a){this.setAttribute("rows",a)}get selectionDirection(){return this.getTextareaProperty("selectionDirection")}set selectionDirection(a){this.setTextareaProperty("selectionDirection",a)}get selectionEnd(){return this.getTextareaProperty("selectionEnd")}set selectionEnd(a){this.setTextareaProperty("selectionEnd",a)}get selectionStart(){return this.getTextareaProperty("selectionStart")}set selectionStart(a){this.setTextareaProperty("selectionStart",a)}get textLength(){return this.value.length}get type(){return"textarea"}get validationMessage(){return this.getTextareaProperty("validationMessage")}get validity(){return this.getTextareaProperty("validationMessage")}get value(){return this.getTextareaProperty("value",this.getAttribute("value")||this.innerHTML)}set value(a){a=a||"",this.setTextareaProperty("value",a,!1)?this.textareaElement&&this.scheduleHighlight():this.innerHTML=a}get willValidate(){return this.getTextareaProperty("willValidate",this.disabled||this.readOnly)}get wrap(){return this.getAttribute("wrap")||""}set wrap(a){this.setAttribute("wrap",a)}getTextareaMethod(a){if(this.textareaElement)return this.textareaElement[a].bind(this.textareaElement);else{const b=this.querySelector("textarea[data-code-input-fallback]");if(b)return b[a].bind(b);throw new Error("Cannot call "+a+" on an unregistered code-input element without a data-code-input-fallback textarea.")}}blur(a={}){this.getTextareaMethod("blur")(a)}checkValidity(){return this.getTextareaMethod("checkValidity")()}focus(a={}){this.getTextareaMethod("focus")(a)}reportValidity(){return this.getTextareaMethod("reportValidity")()}setCustomValidity(a){this.getTextareaMethod("setCustomValidity")(a)}setRangeText(a,b=this.selectionStart,c=this.selectionEnd,d="preserve"){this.getTextareaMethod("setRangeText")(a,b,c,d),this.textareaElement&&this.scheduleHighlight()}setSelectionRange(a,b,c="none"){this.getTextareaMethod("setSelectionRange")(a,b,c)}pluginData={};formResetCallback(){this.value=this.initialValue}}};{class a extends codeInput.Template{constructor(a,b=[],c=!0){super(a.highlightElement,c,!0,!1,b)}}codeInput.templates.Prism=a;class b extends codeInput.Template{constructor(a,b=[],c=!1){super(function(b){b.removeAttribute("data-highlighted"),a.highlightElement(b)},c,!0,!1,b)}}codeInput.templates.Hljs=b}customElements.define("code-input",codeInput.CodeInput); \ No newline at end of file + */"use strict";var codeInput={observedAttributes:["value","placeholder","language","lang","template"],textareaSyncAttributes:["value","min","max","type","pattern","autocomplete","autocorrect","autofocus","cols","dirname","disabled","form","maxlength","minlength","name","placeholder","readonly","required","rows","spellcheck","wrap"],textareaSyncEvents:["change","selectionchange","invalid","input","focus","blur","focusin","focusout"],usedTemplates:{},defaultTemplate:void 0,templateNotYetRegisteredQueue:{},registerTemplate:function(a,b){if(!("string"==typeof a||a instanceof String))throw TypeError(`code-input: Name of template "${a}" must be a string.`);if(!("function"==typeof b.highlight||b.highlight instanceof Function))throw TypeError(`code-input: Template for "${a}" invalid, because the highlight function provided is not a function; it is "${b.highlight}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.includeCodeInputInHighlightFunc||b.includeCodeInputInHighlightFunc instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the includeCodeInputInHighlightFunc value provided is not a true or false; it is "${b.includeCodeInputInHighlightFunc}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.preElementStyled||b.preElementStyled instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the preElementStyled value provided is not a true or false; it is "${b.preElementStyled}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.isCode||b.isCode instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the isCode value provided is not a true or false; it is "${b.isCode}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!Array.isArray(b.plugins))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin array provided is not an array; it is "${b.plugins}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(b.plugins.forEach((c,d)=>{if(!(c instanceof codeInput.Plugin))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin provided at index ${d} is not valid; it is "${b.plugins[d]}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`)}),codeInput.usedTemplates[a]=b,a in codeInput.templateNotYetRegisteredQueue)for(let c in codeInput.templateNotYetRegisteredQueue[a]){const d=codeInput.templateNotYetRegisteredQueue[a][c];d.templateObject=b,d.setup()}if(null==codeInput.defaultTemplate&&(codeInput.defaultTemplate=a,void 0 in codeInput.templateNotYetRegisteredQueue))for(let a in codeInput.templateNotYetRegisteredQueue[void 0]){const c=codeInput.templateNotYetRegisteredQueue[void 0][a];c.templateObject=b,c.setup()}},stylesheetI:0,Template:class{constructor(a=function(){},b=!0,c=!0,d=!1,e=[]){this.highlight=a,this.preElementStyled=b,this.isCode=c,this.includeCodeInputInHighlightFunc=d,this.plugins=e}highlight=function(){};preElementStyled=!0;isCode=!0;includeCodeInputInHighlightFunc=!1;plugins=[]},templates:{prism(a,b=[]){return new codeInput.templates.Prism(a,b)},hljs(a,b=[]){return new codeInput.templates.Hljs(a,b)},characterLimit(a){return{highlight:function(a,b,c=[]){let d=+b.getAttribute("data-character-limit"),e=b.escapeHtml(b.value.slice(0,d)),f=b.escapeHtml(b.value.slice(d));a.innerHTML=`${e}${f}`,0${b.getAttribute("data-overflow-msg")||"(Character limit reached)"}`)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,plugins:a}},rainbowText(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return{highlight:function(a,b){let c=[],d=b.value.split(b.template.delimiter);for(let e=0;e${b.escapeHtml(d[e])}`);a.innerHTML=c.join(b.template.delimiter)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,rainbowColors:a,delimiter:b,plugins:c}},character_limit(){return this.characterLimit([])},rainbow_text(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return this.rainbowText(a,b,c)},custom(a=function(){},b=!0,c=!0,d=!1,e=[]){return{highlight:a,includeCodeInputInHighlightFunc:d,preElementStyled:b,isCode:c,plugins:e}}},plugins:new Proxy({},{get(a,b){if(a[b]==null)throw ReferenceError(`code-input: Plugin '${b}' is not defined. Please ensure you import the necessary files from the plugins folder in the WebCoder49/code-input repository, in the of your HTML, before the plugin is instatiated.`);return a[b]}}),Plugin:class{constructor(a){a.forEach(a=>{codeInput.observedAttributes.push(a)})}addTranslations(a,b){for(const c in b)a[c]=b[c]}beforeHighlight(){}afterHighlight(){}beforeElementsAdded(){}afterElementsAdded(){}attributeChanged(){}},CodeInput:class extends HTMLElement{constructor(){super()}templateObject=null;textareaElement=null;preElement=null;codeElement=null;dialogContainerElement=null;internalStyle=null;static formAssociated=!0;boundEventCallbacks={};pluginEvt(a,b){for(let c in this.templateObject.plugins){let d=this.templateObject.plugins[c];a in d&&(b===void 0?d[a](this):d[a](this,...b))}}needsHighlight=!1;originalAriaDescription;scheduleHighlight(){this.needsHighlight=!0}animateFrame(){this.needsHighlight&&(this.update(),this.needsHighlight=!1),window.requestAnimationFrame(this.animateFrame.bind(this))}update(){let a=this.codeElement,b=this.value;b+="\n",a.innerHTML=this.escapeHtml(b),this.pluginEvt("beforeHighlight"),this.templateObject.includeCodeInputInHighlightFunc?this.templateObject.highlight(a,this):this.templateObject.highlight(a),this.syncSize(),this.pluginEvt("afterHighlight")}getStyledHighlightingElement(){return this.templateObject.preElementStyled?this.preElement:this.codeElement}syncSize(){const a=getComputedStyle(this.getStyledHighlightingElement()).height;this.textareaElement.style.height=a,this.internalStyle.setProperty("--code-input_synced-height",a);const b=getComputedStyle(this.getStyledHighlightingElement()).width;this.textareaElement.style.width=b,this.internalStyle.setProperty("--code-input_synced-width",b)}syncIfColorNotOverridden(a=function(){}){if(this.checkingColorOverridden)return;this.checkingColorOverridden=!0;const b=this.style.transition;this.style.transition="unset",window.requestAnimationFrame(()=>{if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(0, 0, 0)"),"rgb(0, 0, 0)"!=getComputedStyle(this).color)this.style.transition=b,this.checkingColorOverridden=!1,a();else if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(255, 255, 255)"),"rgb(255, 255, 255)"==getComputedStyle(this).color){this.internalStyle.removeProperty("--code-input_no-override-color"),this.style.transition=b;const a=getComputedStyle(this.getStyledHighlightingElement()).color;this.internalStyle.setProperty("--code-input_highlight-text-color",a),this.internalStyle.setProperty("--code-input_default-caret-color",a),this.checkingColorOverridden=!1}else this.style.transition=b,this.checkingColorOverridden=!1,a();this.internalStyle.removeProperty("--code-input_no-override-color"),this.style.transition=b})}syncColorCompletely(){this.syncIfColorNotOverridden(()=>{this.internalStyle.removeProperty("--code-input_highlight-text-color"),this.internalStyle.setProperty("--code-input_default-caret-color",getComputedStyle(this).color)})}setKeyboardNavInstructions(a,b){this.dialogContainerElement.querySelector(".code-input_keyboard-navigation-instructions").innerText=a,b?this.textareaElement.setAttribute("aria-description",this.originalAriaDescription+". "+a):this.textareaElement.setAttribute("aria-description",a)}escapeHtml(a){return a.replace(/&/g,"&").replace(/")}getTemplate(){let a;return a=null==this.getAttribute("template")?codeInput.defaultTemplate:this.getAttribute("template"),a in codeInput.usedTemplates?codeInput.usedTemplates[a]:(a in codeInput.templateNotYetRegisteredQueue||(codeInput.templateNotYetRegisteredQueue[a]=[]),void codeInput.templateNotYetRegisteredQueue[a].push(this))}setup(){if(null!=this.textareaElement)return;this.classList.add("code-input_registered"),this.mutationObserver=new MutationObserver(this.mutationObserverCallback.bind(this)),this.mutationObserver.observe(this,{attributes:!0,attributeOldValue:!0}),this.classList.add("code-input_registered"),this.templateObject.preElementStyled&&this.classList.add("code-input_pre-element-styled");const a=this.querySelector("textarea[data-code-input-fallback]");let b,c,d,e,f,g=!1;a&&(a===document.activeElement&&(g=!0),b=a.selectionStart,c=a.selectionEnd,d=a.selectionDirection,e=a.scrollLeft,f=a.scrollTop);let h;if(a){let b=a.getAttributeNames();for(let c=0;c{this.classList.add("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("blur",()=>{this.classList.remove("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("focus",()=>{window.setTimeout(()=>{this.syncSize()},0)}),this.innerHTML="",this.classList.add("code-input_styles_"+codeInput.stylesheetI);const l=document.createElement("style");l.innerHTML="code-input.code-input_styles_"+codeInput.stylesheetI+" {}",this.appendChild(l),this.internalStyle=l.sheet.cssRules[0].style,codeInput.stylesheetI++;for(let a,b=0;b{this.value=this.textareaElement.value}),this.textareaElement=k,this.append(k),this.setupTextareaSyncEvents(this.textareaElement);let m=document.createElement("code"),n=document.createElement("pre");n.setAttribute("aria-hidden","true"),n.setAttribute("tabindex","-1"),n.setAttribute("inert",!0),this.preElement=n,this.codeElement=m,n.append(m),this.append(n),this.templateObject.isCode&&i!=null&&""!=i&&m.classList.add("language-"+i.toLowerCase());let o=document.createElement("div");o.classList.add("code-input_dialog-container"),this.append(o),this.dialogContainerElement=o;let p=document.createElement("div");p.classList.add("code-input_keyboard-navigation-instructions"),o.append(p),this.pluginEvt("afterElementsAdded"),this.dispatchEvent(new CustomEvent("code-input_load")),this.value=h,b!==void 0&&(k.setSelectionRange(b,c,d),k.scrollTo(f,e)),g&&k.focus(),this.animateFrame();const q=new ResizeObserver(()=>{this.syncSize()});q.observe(this),q.observe(this.preElement),q.observe(this.codeElement);const r=a=>{"color"==a.propertyName&&this.syncIfColorNotOverridden()};this.preElement.addEventListener("transitionend",r),this.preElement.addEventListener("-webkit-transitionend",r);const s=a=>{"color"==a.propertyName&&this.syncColorCompletely(),a.target==this.dialogContainerElement&&a.stopPropagation()};this.dialogContainerElement.addEventListener("transitionend",s),this.dialogContainerElement.addEventListener("-webkit-transitionend",s),this.addEventListener("transitionend",s),this.addEventListener("-webkit-transitionend",s),this.syncColorCompletely(),this.classList.add("code-input_loaded")}escape_html(a){return this.escapeHtml(a)}get_template(){return this.getTemplate()}get template(){return this.templateObject}set template(a){}connectedCallback(){if(this.templateObject=this.getTemplate(),null!=this.templateObject&&(this.classList.add("code-input_registered"),"loading"===document.readyState?window.addEventListener("DOMContentLoaded",this.setup.bind(this)):this.setup()),"loading"===document.readyState)window.addEventListener("DOMContentLoaded",()=>{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)});else{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)}}mutationObserverCallback(a){for(const b of a)if("attributes"===b.type){for(let a=0;a{a.match(b)&&(null==c?this.textareaElement.removeAttribute(a):this.textareaElement.setAttribute(a,c))})}}setupTextareaSyncEvents(a){for(let b=0;b{a.bubbles||this.dispatchEvent(new a.constructor(a.type,a))})}}addEventListener(a,b,c=void 0){let d=function(a){"function"==typeof b?b(a):b&&b.handleEvent&&b.handleEvent(a)}.bind(this);if(this.boundEventCallbacks[b]=d,!codeInput.textareaSyncEvents.includes(a))void 0===c?super.addEventListener(a,d):super.addEventListener(a,d,c);else if(this.boundEventCallbacks[b]=d,void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d)})}else this.textareaElement.addEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d,c)})}else this.textareaElement.addEventListener(a,d,c)}removeEventListener(a,b,c=void 0){let d=this.boundEventCallbacks[b];if(!codeInput.textareaSyncEvents.includes(a))void 0===c?super.removeEventListener(a,d):super.removeEventListener(a,d,c);else if(void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d)})}else this.textareaElement.removeEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d,c)})}else this.textareaElement.removeEventListener(a,d,c)}getTextareaProperty(a,b=void 0){if(this.textareaElement)return this.textareaElement[a];else{const c=this.querySelector("textarea[data-code-input-fallback]");if(c)return c[a];if(void 0===b)throw new Error("Cannot get "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.");return b}}setTextareaProperty(a,b,c=!0){if(this.textareaElement)this.textareaElement[a]=b;else{const d=this.querySelector("textarea[data-code-input-fallback]");if(d)d[a]=b;else{if(!c)return(!1);throw new Error("Cannot set "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.")}}return(!0)}get autocomplete(){return this.getAttribute("autocomplete")}set autocomplete(a){return this.setAttribute("autocomplete",a)}get cols(){return this.getTextareaProperty("cols",+this.getAttribute("cols"))}set cols(a){this.setAttribute("cols",a)}get defaultValue(){return this.initialValue}set defaultValue(a){this.initialValue=a}get textContent(){return this.initialValue}set textContent(a){this.initialValue=a}get dirName(){return this.getAttribute("dirName")||""}set dirName(a){this.setAttribute("dirname",a)}get disabled(){return this.hasAttribute("disabled")}set disabled(a){a?this.setAttribute("disabled",!0):this.removeAttribute("disabled")}get form(){return this.getTextareaProperty("form")}get labels(){return this.getTextareaProperty("labels")}get maxLength(){const a=+this.getAttribute("maxlength");return isNaN(a)?-1:a}set maxLength(a){-1==a?this.removeAttribute("maxlength"):this.setAttribute("maxlength",a)}get minLength(){const a=+this.getAttribute("minlength");return isNaN(a)?-1:a}set minLength(a){-1==a?this.removeAttribute("minlength"):this.setAttribute("minlength",a)}get name(){return this.getAttribute("name")||""}set name(a){this.setAttribute("name",a)}get placeholder(){return this.getAttribute("placeholder")||""}set placeholder(a){this.setAttribute("placeholder",a)}get readOnly(){return this.hasAttribute("readonly")}set readOnly(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get required(){return this.hasAttribute("readonly")}set required(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get rows(){return this.getTextareaProperty("rows",+this.getAttribute("rows"))}set rows(a){this.setAttribute("rows",a)}get selectionDirection(){return this.getTextareaProperty("selectionDirection")}set selectionDirection(a){this.setTextareaProperty("selectionDirection",a)}get selectionEnd(){return this.getTextareaProperty("selectionEnd")}set selectionEnd(a){this.setTextareaProperty("selectionEnd",a)}get selectionStart(){return this.getTextareaProperty("selectionStart")}set selectionStart(a){this.setTextareaProperty("selectionStart",a)}get textLength(){return this.value.length}get type(){return"textarea"}get validationMessage(){return this.getTextareaProperty("validationMessage")}get validity(){return this.getTextareaProperty("validationMessage")}get value(){return this.getTextareaProperty("value",this.getAttribute("value")||this.innerHTML)}set value(a){a=a||"",this.setTextareaProperty("value",a,!1)?this.textareaElement&&this.scheduleHighlight():this.innerHTML=a}get willValidate(){return this.getTextareaProperty("willValidate",this.disabled||this.readOnly)}get wrap(){return this.getAttribute("wrap")||""}set wrap(a){this.setAttribute("wrap",a)}getTextareaMethod(a){if(this.textareaElement)return this.textareaElement[a].bind(this.textareaElement);else{const b=this.querySelector("textarea[data-code-input-fallback]");if(b)return b[a].bind(b);throw new Error("Cannot call "+a+" on an unregistered code-input element without a data-code-input-fallback textarea.")}}blur(a={}){this.getTextareaMethod("blur")(a)}checkValidity(){return this.getTextareaMethod("checkValidity")()}focus(a={}){this.getTextareaMethod("focus")(a)}reportValidity(){return this.getTextareaMethod("reportValidity")()}setCustomValidity(a){this.getTextareaMethod("setCustomValidity")(a)}setRangeText(a,b=this.selectionStart,c=this.selectionEnd,d="preserve"){this.getTextareaMethod("setRangeText")(a,b,c,d),this.textareaElement&&this.scheduleHighlight()}setSelectionRange(a,b,c="none"){this.getTextareaMethod("setSelectionRange")(a,b,c)}pluginData={};formResetCallback(){this.value=this.initialValue}}};{class a extends codeInput.Template{constructor(a,b=[],c=!0){super(a.highlightElement,c,!0,!1,b)}}codeInput.templates.Prism=a;class b extends codeInput.Template{constructor(a,b=[],c=!1){super(function(b){b.removeAttribute("data-highlighted"),a.highlightElement(b)},c,!0,!1,b)}}codeInput.templates.Hljs=b}customElements.define("code-input",codeInput.CodeInput); \ No newline at end of file From 5b5ce49b15b5012ba225f2d8f824cf230d4c1121 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Tue, 17 Mar 2026 14:56:36 +0000 Subject: [PATCH 42/54] Remove anticipatory Codeberg message because code-input.js is now on Codeberg too --- docs/_index.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/_index.md b/docs/_index.md index 28b628b..a818791 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -317,5 +317,3 @@ something like [CodeMirror](https://codemirror.net/), 🎉 code-input.js is collaboratively developed by many people, which is what keeps it going strong. Many have reported bugs and suggestions, and 10 people (see them on [GitHub](https://github.com/WebCoder49/code-input/graphs/contributors) or [Codeberg](https://codeberg.org/code-input-js/code-input-js/activity/contributors)) have contributed code or documentation directly. If you have found a bug, would like to help with the code or documentation, or have additional suggestions, for plugins or core functionality, please look at [GitHub](https://github.com/WebCoder49/code-input/tree/main/CONTRIBUTING.md), at [Codeberg](https://codeberg.org/code-input-js/code-input-js/src/branch/main/CONTRIBUTING.md), or [get in touch via email so I can add it for you](mailto:code-input-js@webcoder49.dev). **If you find a sensitive security vulnerability in the code-input.js library, please email the maintainer Oliver Geer at [security@webcoder49.dev](mailto:security@webcoder49.dev), optionally using [this encryption key](https://ogeer.org#pgp). GitHub security advisories (different to the more general "issues") are also accepted.** - -*I'm looking into mirroring code-input.js onto Codeberg as well as GitHub for more flexibility and freedom - if you have ideas for this please get in touch!* From d24192e848f21d28172429b984b78b983e209af6 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Tue, 17 Mar 2026 15:01:52 +0000 Subject: [PATCH 43/54] Document usage with SPA libraries like Turbo (Fixes #219) --- docs/interface/js/_index.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/interface/js/_index.md b/docs/interface/js/_index.md index c6c5120..f3f976e 100644 --- a/docs/interface/js/_index.md +++ b/docs/interface/js/_index.md @@ -22,3 +22,13 @@ codeInputElement.addEventListener("code-input_load", () => { ``` For backwards compatibility, you should also implement the subset of the functionality that doesn't require `code-input.js` on the `
diff --git a/tests/tester.js b/tests/tester.js index 679703b..05cec13 100644 --- a/tests/tester.js +++ b/tests/tester.js @@ -105,8 +105,74 @@ function waitAsync(milliseconds) { /* --- Running the tests --- */ /* Start the test, for Prism.js if isHLJS is false, or for highlight.js if isHLJS is true. */ -function beginTest(isHLJS) { +var loadEventFired = false; // Global variable so can check the load event is fired in startLoad function +async function beginTest(isHLJS) { let codeInputElem = document.querySelector("code-input"); + codeInputElem.addEventListener("code-input_load", () => { + loadEventFired = true; + testAssertion("Load", "code-input_load Event Fired Late Enough", codeInputElem.querySelector("textarea:not([data-code-input-fallback])") != null, "code-input_load event fired before non-fallback textarea element appeared"); + }); + const fallbackTextarea = codeInputElem.querySelector("textarea[data-code-input-fallback]"); + + + startLoad(codeInputElem, isHLJS); + codeInputElem.style.height = "calc(1lh + 2em)"; // 2em for the "No highlighting." message + codeInputElem.style.setProperty("--padding", "0px"); + await waitAsync(50); // Wait for display to update + // Select the "A" in the fallback textarea's last line + fallbackTextarea.selectionStart = 50; + fallbackTextarea.selectionEnd = 51; + fallbackTextarea.focus(); + fallbackTextarea.scrollTo(0, codeInputElem.clientHeight * 3); // At least 3 lines + + await waitAsync(50); // Wait for scroll to occur + testAssertion("FallbackTextarea", "Scrolls Correctly", confirm("Is the phrase 'A third', with 'A' highlighted, visible? "), "user-judged"); + codeInputElem.style.removeProperty("--padding"); + codeInputElem.style.removeProperty("height"); + + // Select the "log" in the fallback textarea's initial line + fallbackTextarea.selectionStart = 8; + fallbackTextarea.selectionEnd = 11; + fallbackTextarea.focus(); + + codeInputElem.style.setProperty("--padding", "100px"); + await waitAsync(50); // Wait for display to update + testAssertion("FallbackTextarea", "Displayed Correctly With More Padding", confirm("Is the highlighted 'log' properly aligned after the 'console.', which is then properly aligned inside the visible (non-highlighted) textarea? Also, there should be a lot of padding."), "user-judged"); + codeInputElem.style.setProperty("--padding", "0px"); + await waitAsync(50); // Wait for display to update + testAssertion("FallbackTextarea", "Displayed Correctly With No Padding", confirm("Does the element have zero padding, but otherwise the display remains correct?"), "user-judged"); + codeInputElem.style.removeProperty("--padding"); + await waitAsync(50); // Wait for display to update + testAssertion("FallbackTextarea", "Displayed Correctly By Default", confirm("Is the highlighted 'log' properly aligned after the 'console.', which is then properly aligned inside the visible (non-highlighted) textarea?"), "user-judged"); + + + codeInputElem.classList.add("code-input_autogrow_height"); + await waitAsync(50); // Wait for display to update + testAssertion("FallbackTextarea-Autogrow", "Displayed Correctly With Default Autogrow Height", confirm("Is the element not very tall (but tall enough to properly show some code), but otherwise the display remains correct?"), "user-judged"); + + codeInputElem.style.setProperty("--code-input_autogrow_min-height", "200px"); + await waitAsync(50); // Wait for display to update + assertEqual("FallbackTextarea-Autogrow", "--code-input_autogrow_min-height Sets Height", codeInputElem.clientHeight, 200); + codeInputElem.style.removeProperty("--code-input_autogrow_min-height"); + codeInputElem.classList.remove("code-input_autogrow_height"); + + codeInputElem.classList.add("code-input_autogrow_width"); + await waitAsync(50); // Wait for display to update + testAssertion("FallbackTextarea-Autogrow", "Displayed Correctly With Default Autogrow Width", confirm("Is the element a sensible but narrow width, and otherwise the display remains correct?"), "user-judged"); + + codeInputElem.style.setProperty("--code-input_autogrow_min-width", "200px"); + await waitAsync(50); // Wait for display to update + assertEqual("FallbackTextarea-Autogrow", "--code-input_autogrow_min-height Sets Height", codeInputElem.clientWidth, 200); + codeInputElem.style.removeProperty("--code-input_autogrow_min-width"); + codeInputElem.classList.remove("code-input_autogrow_width"); + + + if(!isHLJS) { + codeInputElem.classList.add("line-numbers"); + await waitAsync(50); // Wait for display to update + testAssertion("FallbackTextarea-PrismLineNumbers", "Displayed Correctly With line-numbers Class", confirm("Is there more padding to the left now, but otherwise the display remains correct?"), "user-judged"); + } + if(isHLJS) { codeInput.registerTemplate("code-editor", new codeInput.templates.Hljs(hljs, [ new codeInput.plugins.AutoCloseBrackets(), @@ -145,7 +211,6 @@ function beginTest(isHLJS) { new codeInput.plugins.SpecialChars(true), ])); } - startLoad(codeInputElem, isHLJS); } /* Start loading the tests, using the codeInput load time as one of the tests. */ @@ -153,11 +218,14 @@ function startLoad(codeInputElem, isHLJS) { let textarea; let timeToLoad = 0; let interval = window.setInterval(() => { - textarea = codeInputElem.querySelector("textarea"); - if(textarea != null) window.clearInterval(interval); - timeToLoad += 10; - testData("TimeTaken", "Textarea Appears", timeToLoad+"ms (nearest 10)"); - startTests(textarea, isHLJS); + textarea = codeInputElem.querySelector("textarea:not([data-code-input-fallback])"); + if(textarea != null) { + testAssertion("Load", "code-input_load Event Fired Early Enough", loadEventFired, "non-fallback textarea element appeared before code-input_load event"); + window.clearInterval(interval); + timeToLoad += 10; + testData("Load", "Time Taken for Textarea to Appear", timeToLoad+"ms (nearest 10)"); + beginTestsAfterLoad(textarea, isHLJS); + } }, 10); } @@ -178,8 +246,16 @@ function allowInputEvents(inputElement, codeInputElement=undefined) { } /* Start the tests using the textarea inside the code-input element and whether highlight.js is being used (as the Autodetect plugin only works with highlight.js, for example) */ -async function startTests(textarea, isHLJS) { +async function beginTestsAfterLoad(textarea, isHLJS) { textarea.focus(); + if(!isHLJS) { + await waitAsync(200); // Wait for display to update + testAssertion("FallbackTextarea-PrismLineNumbers", "Alignment between Fallback and Loaded Texareas", confirm("Now with the highlighting, is all the code in the same horizontal position (don't mind the vertical offset by keyboard navigation instructions) as with the last question?"), "user-judged"); + + } else { + await waitAsync(200); // Wait for display to update + testAssertion("FallbackTextarea+PrismLineNumbers", "Alignment between Fallback and Loaded Texareas", confirm("Now with the highlighting, is all the code in the same horizontal position (don't mind the vertical offset by keyboard navigation instructions) as with the last question?"), "user-judged"); + } codeInputElement = textarea.parentElement; allowInputEvents(textarea, codeInputElement); @@ -187,7 +263,10 @@ async function startTests(textarea, isHLJS) { /*--- Tests for core functionality ---*/ // Textarea's initial value should be correct. - assertEqual("Core", "Initial Textarea Value", textarea.value, `console.log("Hello, World!"); + assertEqual("FallbackTextarea", "Textarea selectionStart Once Loaded same as Fallback Textarea's", textarea.selectionStart, 8); + assertEqual("FallbackTextarea", "Textarea selectionEnd Once Loaded same as Fallback Textarea's", textarea.selectionEnd, 11); + + assertEqual("FallbackTextarea", "Textarea Value Once Loaded same as Fallback Textarea's", textarea.value, `console.log("Hello, World!"); // A second line // A third line with tags`); // Code element's displayed value, ignoring appearance with HTML tags, should be the initial value but HTML-escaped From 26c1ffd91e1ba92739663bdcb64b0383a3550a8e Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Tue, 30 Jun 2026 23:41:45 +0100 Subject: [PATCH 47/54] Fix typo in docs --- docs/modules-and-frameworks/custom/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/modules-and-frameworks/custom/_index.md b/docs/modules-and-frameworks/custom/_index.md index 91275cd..c97b312 100644 --- a/docs/modules-and-frameworks/custom/_index.md +++ b/docs/modules-and-frameworks/custom/_index.md @@ -1,5 +1,5 @@ +++ -title = 'Using code-input.js with custom highlighting algorihms in projects which use modules or frameworks' +title = 'Using code-input.js with custom highlighting algorithms in projects which use modules or frameworks' +++ # Using code-input.js with custom highlighting algorithms in projects which use modules or frameworks From 28e1f9933e5df6569632d523b7ba2252e67daff8 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Tue, 30 Jun 2026 23:49:45 +0100 Subject: [PATCH 48/54] Update copyright year --- LICENSE | 2 +- code-input.css | 2 +- code-input.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index a0a9e97..19fa604 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021-2025 Oliver Geer and contributors +Copyright (c) 2021-2026 Oliver Geer and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/code-input.css b/code-input.css index 07140e6..b794a63 100644 --- a/code-input.css +++ b/code-input.css @@ -5,7 +5,7 @@ * * License of whole library for bundlers: * - * Copyright 2021-2025 Oliver Geer and contributors + * Copyright 2021-2026 Oliver Geer and contributors * @license MIT * * **** diff --git a/code-input.js b/code-input.js index a728df2..5eb6ff0 100644 --- a/code-input.js +++ b/code-input.js @@ -5,7 +5,7 @@ * * License of whole library for bundlers: * - * Copyright 2021-2025 Oliver Geer and contributors + * Copyright 2021-2026 Oliver Geer and contributors * @license MIT * * **** From d27af35c3d9b96ee56ee4d4c62d6799d35f7151d Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Wed, 1 Jul 2026 15:34:27 +0100 Subject: [PATCH 49/54] Add test Autocomplete: Popup Clickable (Closes #174) --- tests/tester.js | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/tests/tester.js b/tests/tester.js index 05cec13..96bfdd4 100644 --- a/tests/tester.js +++ b/tests/tester.js @@ -106,6 +106,7 @@ function waitAsync(milliseconds) { /* Start the test, for Prism.js if isHLJS is false, or for highlight.js if isHLJS is true. */ var loadEventFired = false; // Global variable so can check the load event is fired in startLoad function +var popupClicked = false; // Global variable for Autocomplete plugin async function beginTest(isHLJS) { let codeInputElem = document.querySelector("code-input"); codeInputElem.addEventListener("code-input_load", () => { @@ -180,7 +181,7 @@ async function beginTest(isHLJS) { if(selectionStart == selectionEnd && textarea.value.substring(selectionEnd-5, selectionEnd) == "popup") { // Show popup popupElem.style.display = "block"; - popupElem.innerHTML = "Here's your popup!"; + popupElem.innerHTML = ''; } else { popupElem.style.display = "none"; } @@ -199,7 +200,7 @@ async function beginTest(isHLJS) { if(selectionStart == selectionEnd && textarea.value.substring(selectionEnd-5, selectionEnd) == "popup") { // Show popup popupElem.style.display = "block"; - popupElem.innerHTML = "Here's your popup!"; + popupElem.innerHTML = ''; } else { popupElem.style.display = "none"; } @@ -518,12 +519,31 @@ console.log("I've got another line!", 2 < 3, "should be true."); await waitAsync(50); // Wait for popup to be rendered testAssertion("Autocomplete", "Popup Shows on arrow key", confirm("Does the autocomplete popup display correctly? (OK=Yes)"), "user-judged"); - backspace(textarea); - await waitAsync(50); // Wait for popup disappearance to be rendered + backspace(textarea); + await waitAsync(50); // Wait for popup disappearance to be rendered testAssertion("Autocomplete", "Popup Disappears on backspace", confirm("Has the popup disappeared? (OK=Yes)"), "user-judged"); - move(textarea, 1); + + addText(textarea, "p"); + await waitAsync(50); + + const beforeClickSelectionStart = textarea.selectionStart; + const beforeClickSelectionEnd = textarea.selectionEnd; + alert("Dismiss this alert, then click the popup in the next 3 seconds."); + // To speed up test, wait until 3s have passed or a click has fired, whichever is earlier. + let timePassed = 0; + while(timePassed < 3000 && !popupClicked) { + await waitAsync(10); + timePassed += 10; + } + testAssertion("Autocomplete", "Popup Clickable", popupClicked, "The onclick event of the popup element didn't fire"); + // In case clicking the popup moved the caret inside the textarea: + textarea.selectionStart = beforeClickSelectionStart; + textarea.selectionEnd = beforeClickSelectionEnd; + textarea.focus(); + + backspace(textarea); backspace(textarea); backspace(textarea); backspace(textarea); From 9c94cea615df5ddc8b27b2a495aeb1540e088d9c Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Wed, 1 Jul 2026 15:54:28 +0100 Subject: [PATCH 50/54] Document a case of bold text misalignment (#130) --- docs/interface/css/_index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/interface/css/_index.md b/docs/interface/css/_index.md index 7fde3a5..f349842 100644 --- a/docs/interface/css/_index.md +++ b/docs/interface/css/_index.md @@ -9,6 +9,7 @@ title = 'Styling `code-input` elements with CSS' `code-input` elements can be styled like `textarea` elements in most cases; however, there are some exceptions: * The CSS variable `--padding` should be used rather than the property `padding` (e.g. `...`), or `--padding-left`, `--padding-right`, `--padding-top` and `--padding-bottom` instead of the CSS properties of the same names. For technical reasons, the value must have a unit (i.e. `0px`, not `0`). To avoid overcomplicating the code, this padding is always *included in* any width/heights of `code-input` elements, so if you want to style `textarea`s and `code-input` elements with best consistency set `box-sizing: border-box` on them. * Background colours set on `code-input` elements will not work with highlighters that set background colours themselves - use `(code-input's selector) pre[class*="language-"]` for Prism.js or `.hljs` for highlight.js to target the highlighted element with higher specificity than the highlighter's theme. You may also set the `background-color` of the code-input element for its appearance when its template is unregistered / there is no JavaScript. +* To minimise the chance of [this bug](https://github.com/WebCoder49/code-input/issues/130): if you import a monospace font family from an online source (for example, Google Fonts or Bunny Fonts) to style `code-input` elements, and use a syntax highlighting theme that includes **bold text**, import both the standard (400) and bold (700) weights of the font. * The caret and placeholder colour by default follow and give good contrast with the highlighted theme. Setting a CSS rule for `color` and/or `caret-color` properties on the code-input element will override this behaviour. * For technical reasons, `code-input:focus` won't match anything. Use `code-input:has(textarea:focus)` instead. * For now, elements on top of `code-input` elements should have a CSS `z-index` at least 3 greater than the `code-input` element. From 352744d73df1ba456ddf25214a23f76fbb14f21e Mon Sep 17 00:00:00 2001 From: WebCoder49 <69071853+WebCoder49@users.noreply.github.com> Date: Wed, 1 Jul 2026 14:56:41 +0000 Subject: [PATCH 51/54] Auto Minify JS and CSS files --- code-input.min.css | 2 +- code-input.min.js | 2 +- plugins/autogrow.min.css | 2 +- tests/tester.min.js | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code-input.min.css b/code-input.min.css index fcd0e9e..759634c 100644 --- a/code-input.min.css +++ b/code-input.min.css @@ -1 +1 @@ -code-input{color:var(--code-input_no-override-color,black);caret-color:var(--code-input_default-caret-color,inherit);--padding:16px;--padding-left:var(--padding,16px);--padding-right:var(--padding,16px);--padding-top:var(--padding,16px);--padding-bottom:var(--padding,16px);height:250px;font-size:inherit;text-align:start;tab-size:2;white-space:pre;background-color:#fff;grid-template-rows:100%;grid-template-columns:100%;margin:8px;font-family:monospace;line-height:1.5;display:grid;position:relative;top:0;left:0;overflow:auto;padding:0!important}code-input :not(.code-input_dialog-container *){box-sizing:content-box}code-input textarea,code-input:not(.code-input_pre-element-styled) pre code,code-input.code-input_pre-element-styled pre{min-width:calc(100% - var(--padding-left,16px) - var(--padding-right,16px));min-height:calc(100% - var(--padding-top,16px) - var(--padding-bottom,16px));resize:none;border:0;grid-area:1/1;display:block;overflow:hidden;padding-left:var(--padding-left,16px)!important;padding-right:var(--padding-right,16px)!important;padding-top:var(--padding-top,16px)!important;padding-bottom:var(--padding-bottom,16px)!important;margin:0!important}code-input:not(.code-input_pre-element-styled) pre code,code-input.code-input_pre-element-styled pre{width:max-content;height:max-content;transition:color 1ms}code-input:not(.code-input_pre-element-styled) pre,code-input.code-input_pre-element-styled pre code{min-width:100%;min-height:100%;border:0!important;margin:0!important;padding:0!important}code-input textarea,code-input pre,code-input pre *{font-size:inherit!important;font-family:inherit!important;line-height:inherit!important;tab-size:inherit!important;text-align:inherit!important}code-input pre,code-input pre code{overflow:visible!important}code-input textarea[dir=auto]+pre{unicode-bidi:plaintext}code-input textarea[dir=ltr]+pre{direction:ltr}code-input textarea[dir=rtl]+pre{direction:rtl}code-input textarea,code-input pre{grid-area:1/1}code-input textarea:not([data-code-input-fallback]){z-index:1}code-input pre{z-index:0}code-input textarea:not([data-code-input-fallback]){color:#0000;caret-color:inherit;background:0 0}code-input textarea:not([data-code-input-fallback]):placeholder-shown{color:var(--code-input_highlight-text-color,inherit)}code-input textarea,code-input pre{white-space:inherit;word-spacing:normal;word-break:normal;word-wrap:normal}code-input textarea{resize:none;outline:none!important}code-input:has(textarea:focus):not(.code-input_mouse-focused){outline:2px solid}code-input .code-input_dialog-container{z-index:2;width:100%;height:0;text-align:inherit;color:inherit;grid-area:1/1;margin:0;padding:0;transition:color 1ms;position:sticky;top:0;left:0}[dir=rtl] code-input .code-input_dialog-container,code-input[dir=rtl] .code-input_dialog-container{left:unset;right:0}code-input .code-input_dialog-container .code-input_keyboard-navigation-instructions{color:#fff;padding:2px;padding-left:var(--padding-left,16px);padding-right:var(--padding-right,16px);text-wrap:balance;box-sizing:border-box;background-color:#000;width:100%;height:3em;margin:0;display:block;position:absolute;top:0;left:0;overflow:hidden auto}code-input:not(:has(textarea:not([data-code-input-fallback]):focus)) .code-input_dialog-container .code-input_keyboard-navigation-instructions,code-input.code-input_mouse-focused .code-input_dialog-container .code-input_keyboard-navigation-instructions,code-input .code-input_dialog-container .code-input_keyboard-navigation-instructions:empty{display:none}code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused) textarea,code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code,code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre{min-height:calc(100% - var(--padding-top,16px) - 3em - var(--padding-bottom,16px));padding-top:calc(var(--padding-top,16px) + 3em)!important}code-input:not(.code-input_loaded){box-sizing:border-box;display:block;overflow:hidden;padding-left:var(--padding-left,16px)!important;padding-right:var(--padding-right,16px)!important;padding:var(--padding-top,16px)!important;padding:var(--padding-bottom,16px)!important}code-input:not(.code-input_loaded):after{content:"No highlighting. JavaScript support is disabled or insufficient, or codeInput.registerTemplate has not been called.";bottom:0;left:var(--padding-left,16px);width:calc(100% - var(--padding-left,1.6px) - var(--padding-right,1.6px));outline-top:0;background-color:inherit;color:inherit;border-top:1px solid;height:2em;margin:0;padding:0;display:block;position:absolute;overflow-x:auto}code-input:not(.code-input_loaded) pre,code-input:not(.code-input_loaded) textarea:not([data-code-input-fallback]){opacity:0}code-input:has(textarea[data-code-input-fallback]){box-sizing:content-box;caret-color:revert;padding:0!important}code-input textarea[data-code-input-fallback]{background-color:inherit;color:var(--code-input_highlight-text-color,inherit);min-height:calc(100% - var(--padding-top,16px) - 2em - var(--padding-bottom,16px));overflow:auto} \ No newline at end of file +code-input{color:var(--code-input_no-override-color,black);caret-color:var(--code-input_default-caret-color,inherit);--padding:16px;--padding-left:var(--padding,16px);--padding-right:var(--padding,16px);--padding-top:var(--padding,16px);--padding-bottom:var(--padding,16px);height:250px;font-size:inherit;text-align:start;tab-size:2;white-space:pre;background-color:#fff;grid-template-rows:100%;grid-template-columns:100%;margin:8px;font-family:monospace;line-height:1.5;display:grid;position:relative;top:0;left:0;overflow:auto;padding:0!important}code-input :not(.code-input_dialog-container *){box-sizing:content-box}code-input textarea,code-input:not(.code-input_pre-element-styled) pre code,code-input.code-input_pre-element-styled pre{min-width:calc(100% - var(--padding-left,16px) - var(--padding-right,16px));min-height:calc(100% - var(--padding-top,16px) - var(--padding-bottom,16px));resize:none;border:0;grid-area:1/1;display:block;overflow:hidden;padding-left:var(--padding-left,16px)!important;padding-right:var(--padding-right,16px)!important;padding-top:var(--padding-top,16px)!important;padding-bottom:var(--padding-bottom,16px)!important;margin:0!important}code-input:not(.code-input_pre-element-styled) pre code,code-input.code-input_pre-element-styled pre{width:max-content;height:max-content;transition:color 1ms}code-input:not(.code-input_pre-element-styled) pre,code-input.code-input_pre-element-styled pre code{min-width:100%;min-height:100%;border:0!important;margin:0!important;padding:0!important}code-input textarea,code-input pre,code-input pre *{font-size:inherit!important;font-family:inherit!important;line-height:inherit!important;tab-size:inherit!important;text-align:inherit!important}code-input pre,code-input pre code{overflow:visible!important}code-input textarea[dir=auto]+pre{unicode-bidi:plaintext}code-input textarea[dir=ltr]+pre{direction:ltr}code-input textarea[dir=rtl]+pre{direction:rtl}code-input textarea,code-input pre{grid-area:1/1}code-input textarea:not([data-code-input-fallback]){z-index:1}code-input pre{z-index:0}code-input textarea:not([data-code-input-fallback]){color:#0000;caret-color:inherit;background:0 0}code-input textarea:not([data-code-input-fallback]):placeholder-shown{color:var(--code-input_highlight-text-color,inherit)}code-input textarea,code-input pre{white-space:inherit;word-spacing:normal;word-break:normal;word-wrap:normal}code-input textarea{resize:none;outline:none!important}code-input:has(textarea:focus):not(.code-input_mouse-focused){outline:2px solid}code-input .code-input_dialog-container{z-index:2;width:100%;height:0;text-align:inherit;color:inherit;grid-area:1/1;margin:0;padding:0;transition:color 1ms;position:sticky;top:0;left:0}[dir=rtl] code-input .code-input_dialog-container,code-input[dir=rtl] .code-input_dialog-container{left:unset;right:0}code-input .code-input_dialog-container .code-input_keyboard-navigation-instructions{color:#fff;padding:2px;padding-left:var(--padding-left,16px);padding-right:var(--padding-right,16px);text-wrap:balance;box-sizing:border-box;background-color:#000;width:100%;height:3em;margin:0;display:block;position:absolute;top:0;left:0;overflow:hidden auto}code-input:not(:has(textarea:not([data-code-input-fallback]):focus)) .code-input_dialog-container .code-input_keyboard-navigation-instructions,code-input.code-input_mouse-focused .code-input_dialog-container .code-input_keyboard-navigation-instructions,code-input .code-input_dialog-container .code-input_keyboard-navigation-instructions:empty{display:none}code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused) textarea,code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code,code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre{min-height:calc(100% - var(--padding-top,16px) - 3em - var(--padding-bottom,16px));padding-top:calc(var(--padding-top,16px) + 3em)!important}code-input:not(.code-input_loaded){box-sizing:border-box;display:block;overflow:hidden;padding-left:var(--padding-left,16px)!important;padding-right:var(--padding-right,16px)!important;padding:var(--padding-top,16px)!important;padding:var(--padding-bottom,16px)!important}code-input:not(.code-input_loaded):after{content:"No highlighting. JavaScript support is disabled or insufficient, or codeInput.registerTemplate has not been called.";bottom:0;left:var(--padding-left,16px);width:calc(100% - var(--padding-left,1.6px) - var(--padding-right,1.6px));outline-top:0;background-color:inherit;color:inherit;border-top:1px solid;height:2em;margin:0;padding:0;display:block;position:absolute;overflow-x:auto}code-input:not(.code-input_loaded) pre,code-input:not(.code-input_loaded) textarea:not([data-code-input-fallback]){opacity:0}code-input:has(textarea[data-code-input-fallback]){box-sizing:content-box;caret-color:revert;padding:0!important}code-input textarea[data-code-input-fallback]{background-color:inherit;color:var(--code-input_highlight-text-color,inherit);min-height:calc(100% - var(--padding-top,16px) - max(2em, var(--padding-bottom,16px)));height:calc(100% - var(--padding-top,16px) - max(2em, var(--padding-bottom,16px)));overflow:auto;padding-bottom:max(2em, var(--padding-bottom,16px))!important} \ No newline at end of file diff --git a/code-input.min.js b/code-input.min.js index bbb1003..bbe4ff2 100644 --- a/code-input.min.js +++ b/code-input.min.js @@ -5,7 +5,7 @@ * * License of whole library for bundlers: * - * Copyright 2021-2025 Oliver Geer and contributors + * Copyright 2021-2026 Oliver Geer and contributors * @license MIT * * **** diff --git a/plugins/autogrow.min.css b/plugins/autogrow.min.css index 275cd60..2f5d154 100644 --- a/plugins/autogrow.min.css +++ b/plugins/autogrow.min.css @@ -1 +1 @@ -code-input.code-input_autogrow_height{--code-input_autogrow_min-height:0px;--code-input_autogrow_max-height:calc(infinity * 1px);--code-input_autogrow_internal-min-height:0px;--code-input_autogrow_true-min-height:max(var(--code-input_autogrow_min-height), var(--code-input_autogrow_internal-min-height));height:max-content;max-height:var(--code-input_autogrow_max-height)}code-input.code-input_autogrow_height textarea{min-height:max(var(--code-input_synced-height), calc(100% - var(--padding-top,16px) - var(--padding-bottom,16px)));height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - var(--padding-bottom,16px))!important}code-input.code-input_autogrow_height>pre,code-input.code-input_autogrow_height>pre code{min-height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - var(--padding-bottom,16px))}code-input.code-input_autogrow_width{--code-input_autogrow_min-width:100px;--code-input_autogrow_max-width:100%;--code-input_autogrow_internal-min-width:0px;--code-input_autogrow_true-min-width:max(var(--code-input_autogrow_min-width), var(--code-input_autogrow_internal-min-width));width:max-content;max-width:var(--code-input_autogrow_max-width)}code-input.code-input_autogrow_width textarea{min-width:max(var(--code-input_synced-width), calc(100% - var(--padding-left,16px) - var(--padding-right,16px)));width:calc(var(--code-input_autogrow_true-min-width) - var(--padding-left,16px) - var(--padding-right,16px))!important}code-input.code-input_autogrow_width pre code,code-input.code-input_autogrow_width pre{min-width:calc(var(--code-input_autogrow_true-min-width) - var(--padding-left,16px) - var(--padding-right,16px))}code-input.code-input_autogrow_width:has(.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog)){--code-input_autogrow_internal-min-width:300px}code-input.code-input_autogrow_height:has(.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog)){--code-input_autogrow_internal-min-height:150px}code-input.code-input_autogrow_width:has(.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog)){--code-input_autogrow_internal-min-width:500px}code-input.code-input_autogrow_height:has(.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog)){--code-input_autogrow_internal-min-height:170px}code-input.code-input_autogrow_height:has(textarea[data-code-input-fallback]){--code-input_autogrow_internal-min-height:calc(1lh + 2em + var(--padding-top,16px) + var(--padding-bottom,16px));height:var(--code-input_autogrow_true-min-height)}code-input textarea[data-code-input-fallback]{min-height:calc(100% - var(--padding-top,16px) - var(--padding-bottom,16px) - 2em);height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - var(--padding-bottom,16px) - 2em)!important} \ No newline at end of file +code-input.code-input_autogrow_height{--code-input_autogrow_min-height:0px;--code-input_autogrow_max-height:calc(infinity * 1px);--code-input_autogrow_internal-min-height:0px;--code-input_autogrow_true-min-height:max(var(--code-input_autogrow_min-height), var(--code-input_autogrow_internal-min-height));height:max-content;max-height:var(--code-input_autogrow_max-height)}code-input.code-input_autogrow_height textarea{min-height:max(var(--code-input_synced-height), calc(100% - var(--padding-top,16px) - var(--padding-bottom,16px)));height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - var(--padding-bottom,16px))!important}code-input.code-input_autogrow_height>pre,code-input.code-input_autogrow_height>pre code{min-height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - var(--padding-bottom,16px))}code-input.code-input_autogrow_width{--code-input_autogrow_min-width:100px;--code-input_autogrow_max-width:100%;--code-input_autogrow_internal-min-width:0px;--code-input_autogrow_true-min-width:max(var(--code-input_autogrow_min-width), var(--code-input_autogrow_internal-min-width));width:max-content;max-width:var(--code-input_autogrow_max-width)}code-input.code-input_autogrow_width textarea{min-width:max(var(--code-input_synced-width), calc(100% - var(--padding-left,16px) - var(--padding-right,16px)));width:calc(var(--code-input_autogrow_true-min-width) - var(--padding-left,16px) - var(--padding-right,16px))!important}code-input.code-input_autogrow_width pre code,code-input.code-input_autogrow_width pre{min-width:calc(var(--code-input_autogrow_true-min-width) - var(--padding-left,16px) - var(--padding-right,16px))}code-input.code-input_autogrow_width:has(.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog)){--code-input_autogrow_internal-min-width:300px}code-input.code-input_autogrow_height:has(.code-input_go-to-line_dialog:not(.code-input_go-to-line_hidden-dialog)){--code-input_autogrow_internal-min-height:150px}code-input.code-input_autogrow_width:has(.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog)){--code-input_autogrow_internal-min-width:500px}code-input.code-input_autogrow_height:has(.code-input_find-and-replace_dialog:not(.code-input_find-and-replace_hidden-dialog)){--code-input_autogrow_internal-min-height:170px}code-input.code-input_autogrow_height:has(textarea[data-code-input-fallback]){--code-input_autogrow_internal-min-height:calc(1lh + 2em + var(--padding-top,16px) + var(--padding-bottom,16px));height:var(--code-input_autogrow_true-min-height)}code-input.code-input_autogrow_height textarea[data-code-input-fallback]{min-height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - max(2em, var(--padding-bottom,16px)));height:calc(var(--code-input_autogrow_true-min-height) - var(--padding-top,16px) - max(2em, var(--padding-bottom,16px)))!important} \ No newline at end of file diff --git a/tests/tester.min.js b/tests/tester.min.js index f41a9f6..537ad31 100644 --- a/tests/tester.min.js +++ b/tests/tester.min.js @@ -1,4 +1,4 @@ -var testsFailed=!1;function testData(a,b,c){let d=document.getElementById("test-results"),e=d.querySelector("#test-"+a);e==null&&(e=document.createElement("span"),e.innerHTML=`Group ${a}:\n`,e.id="test-"+a,d.append(e)),e.innerHTML+=`\t${b}: ${c}\n`}function testAssertion(a,b,c,d){let e=document.getElementById("test-results"),f=e.querySelector("#test-"+a);f==null&&(f=document.createElement("span"),f.innerHTML=`Group ${a}:\n`,f.id="test-"+a,e.append(f)),f.innerHTML+=`\t${b}: ${c?"passed":"failed ("+d+")"}\n`,c||(testsFailed=!0)}function assertEqual(a,b,c,d){let e=c==d;testAssertion(a,b,e,"see console output"),e||console.error(a,b,c,"should be",d)}function testAddingText(a,b,c,d,e,f){let g=b.selectionStart,h=b.value.substring(0,b.selectionStart),i=b.value.substring(b.selectionEnd);c(b);let j=h+d+i;assertEqual(a,"Text Output",b.value,j),assertEqual(a,"Code-Input Value JS Property Output",b.parentElement.value,j),assertEqual(a,"Selection Start",b.selectionStart,g+e),assertEqual(a,"Selection End",b.selectionEnd,g+f)}function addText(a,b,c=!1){for(let d=0;d{setTimeout(()=>{b()},a)})}function beginTest(a){let b=document.querySelector("code-input");a?codeInput.registerTemplate("code-editor",new codeInput.templates.Hljs(hljs,[new codeInput.plugins.AutoCloseBrackets,new codeInput.plugins.Autocomplete(function(a,b,c,d){d==c&&"popup"==b.value.substring(c-5,c)?(a.style.display="block",a.innerHTML="Here's your popup!"):a.style.display="none"}),new codeInput.plugins.Autodetect,new codeInput.plugins.FindAndReplace(!0,!0,{},!1),new codeInput.plugins.GoToLine(!0,{}),new codeInput.plugins.Indent(!0,2),new codeInput.plugins.SelectTokenCallbacks(codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks.createClassSynchronisation("in-selection"),!1,!0,!0,!0,!0,!1),new codeInput.plugins.SpecialChars(!0)])):codeInput.registerTemplate("code-editor",new codeInput.templates.Prism(Prism,[new codeInput.plugins.AutoCloseBrackets,new codeInput.plugins.Autocomplete(function(a,b,c,d){d==c&&"popup"==b.value.substring(c-5,c)?(a.style.display="block",a.innerHTML="Here's your popup!"):a.style.display="none"}),new codeInput.plugins.FindAndReplace(!0,!0,{},!1),new codeInput.plugins.GoToLine(!0,{}),new codeInput.plugins.Indent(!0,2),new codeInput.plugins.SelectTokenCallbacks(new codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks(selectBrace,deselectAllBraces),!0),new codeInput.plugins.SpecialChars(!0)])),startLoad(b,a)}function startLoad(a,b){let c,d=0,e=window.setInterval(()=>{c=a.querySelector("textarea"),null!=c&&window.clearInterval(e),d+=10,testData("TimeTaken","Textarea Appears",d+"ms (nearest 10)"),startTests(c,b)},10)}function allowInputEvents(a,b=void 0){a.addEventListener("input",function(a){a.isTrusted||(a.preventDefault(),b!==void 0&&(b.pluginData.autoCloseBrackets.automatedKeypresses=!0),document.execCommand("insertText",!1,a.data),b!==void 0&&(b.pluginData.autoCloseBrackets.automatedKeypresses=!1))},!1)}async function startTests(a,b){a.focus(),codeInputElement=a.parentElement,allowInputEvents(a,codeInputElement),assertEqual("Core","Initial Textarea Value",a.value,`console.log("Hello, World!"); +var testsFailed=!1;function testData(a,b,c){let d=document.getElementById("test-results"),e=d.querySelector("#test-"+a);e==null&&(e=document.createElement("span"),e.innerHTML=`Group ${a}:\n`,e.id="test-"+a,d.append(e)),e.innerHTML+=`\t${b}: ${c}\n`}function testAssertion(a,b,c,d){let e=document.getElementById("test-results"),f=e.querySelector("#test-"+a);f==null&&(f=document.createElement("span"),f.innerHTML=`Group ${a}:\n`,f.id="test-"+a,e.append(f)),f.innerHTML+=`\t${b}: ${c?"passed":"failed ("+d+")"}\n`,c||(testsFailed=!0)}function assertEqual(a,b,c,d){let e=c==d;testAssertion(a,b,e,"see console output"),e||console.error(a,b,c,"should be",d)}function testAddingText(a,b,c,d,e,f){let g=b.selectionStart,h=b.value.substring(0,b.selectionStart),i=b.value.substring(b.selectionEnd);c(b);let j=h+d+i;assertEqual(a,"Text Output",b.value,j),assertEqual(a,"Code-Input Value JS Property Output",b.parentElement.value,j),assertEqual(a,"Selection Start",b.selectionStart,g+e),assertEqual(a,"Selection End",b.selectionEnd,g+f)}function addText(a,b,c=!1){for(let d=0;d{setTimeout(()=>{b()},a)})}var loadEventFired=!1,popupClicked=!1;async function beginTest(a){let b=document.querySelector("code-input");b.addEventListener("code-input_load",()=>{loadEventFired=!0,testAssertion("Load","code-input_load Event Fired Late Enough",null!=b.querySelector("textarea:not([data-code-input-fallback])"),"code-input_load event fired before non-fallback textarea element appeared")});const c=b.querySelector("textarea[data-code-input-fallback]");startLoad(b,a),b.style.height="calc(1lh + 2em)",b.style.setProperty("--padding","0px"),await waitAsync(50),c.selectionStart=50,c.selectionEnd=51,c.focus(),c.scrollTo(0,3*b.clientHeight),await waitAsync(50),testAssertion("FallbackTextarea","Scrolls Correctly",confirm("Is the phrase 'A third', with 'A' highlighted, visible? "),"user-judged"),b.style.removeProperty("--padding"),b.style.removeProperty("height"),c.selectionStart=8,c.selectionEnd=11,c.focus(),b.style.setProperty("--padding","100px"),await waitAsync(50),testAssertion("FallbackTextarea","Displayed Correctly With More Padding",confirm("Is the highlighted 'log' properly aligned after the 'console.', which is then properly aligned inside the visible (non-highlighted) textarea? Also, there should be a lot of padding."),"user-judged"),b.style.setProperty("--padding","0px"),await waitAsync(50),testAssertion("FallbackTextarea","Displayed Correctly With No Padding",confirm("Does the element have zero padding, but otherwise the display remains correct?"),"user-judged"),b.style.removeProperty("--padding"),await waitAsync(50),testAssertion("FallbackTextarea","Displayed Correctly By Default",confirm("Is the highlighted 'log' properly aligned after the 'console.', which is then properly aligned inside the visible (non-highlighted) textarea?"),"user-judged"),b.classList.add("code-input_autogrow_height"),await waitAsync(50),testAssertion("FallbackTextarea-Autogrow","Displayed Correctly With Default Autogrow Height",confirm("Is the element not very tall (but tall enough to properly show some code), but otherwise the display remains correct?"),"user-judged"),b.style.setProperty("--code-input_autogrow_min-height","200px"),await waitAsync(50),assertEqual("FallbackTextarea-Autogrow","--code-input_autogrow_min-height Sets Height",b.clientHeight,200),b.style.removeProperty("--code-input_autogrow_min-height"),b.classList.remove("code-input_autogrow_height"),b.classList.add("code-input_autogrow_width"),await waitAsync(50),testAssertion("FallbackTextarea-Autogrow","Displayed Correctly With Default Autogrow Width",confirm("Is the element a sensible but narrow width, and otherwise the display remains correct?"),"user-judged"),b.style.setProperty("--code-input_autogrow_min-width","200px"),await waitAsync(50),assertEqual("FallbackTextarea-Autogrow","--code-input_autogrow_min-height Sets Height",b.clientWidth,200),b.style.removeProperty("--code-input_autogrow_min-width"),b.classList.remove("code-input_autogrow_width"),a||(b.classList.add("line-numbers"),await waitAsync(50),testAssertion("FallbackTextarea-PrismLineNumbers","Displayed Correctly With line-numbers Class",confirm("Is there more padding to the left now, but otherwise the display remains correct?"),"user-judged")),a?codeInput.registerTemplate("code-editor",new codeInput.templates.Hljs(hljs,[new codeInput.plugins.AutoCloseBrackets,new codeInput.plugins.Autocomplete(function(a,b,c,d){d==c&&"popup"==b.value.substring(c-5,c)?(a.style.display="block",a.innerHTML=""):a.style.display="none"}),new codeInput.plugins.Autodetect,new codeInput.plugins.FindAndReplace(!0,!0,{},!1),new codeInput.plugins.GoToLine(!0,{}),new codeInput.plugins.Indent(!0,2),new codeInput.plugins.SelectTokenCallbacks(codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks.createClassSynchronisation("in-selection"),!1,!0,!0,!0,!0,!1),new codeInput.plugins.SpecialChars(!0)])):codeInput.registerTemplate("code-editor",new codeInput.templates.Prism(Prism,[new codeInput.plugins.AutoCloseBrackets,new codeInput.plugins.Autocomplete(function(a,b,c,d){d==c&&"popup"==b.value.substring(c-5,c)?(a.style.display="block",a.innerHTML=""):a.style.display="none"}),new codeInput.plugins.FindAndReplace(!0,!0,{},!1),new codeInput.plugins.GoToLine(!0,{}),new codeInput.plugins.Indent(!0,2),new codeInput.plugins.SelectTokenCallbacks(new codeInput.plugins.SelectTokenCallbacks.TokenSelectorCallbacks(selectBrace,deselectAllBraces),!0),new codeInput.plugins.SpecialChars(!0)]))}function startLoad(a,b){let c,d=0,e=window.setInterval(()=>{c=a.querySelector("textarea:not([data-code-input-fallback])"),null!=c&&(testAssertion("Load","code-input_load Event Fired Early Enough",loadEventFired,"non-fallback textarea element appeared before code-input_load event"),window.clearInterval(e),d+=10,testData("Load","Time Taken for Textarea to Appear",d+"ms (nearest 10)"),beginTestsAfterLoad(c,b))},10)}function allowInputEvents(a,b=void 0){a.addEventListener("input",function(a){a.isTrusted||(a.preventDefault(),b!==void 0&&(b.pluginData.autoCloseBrackets.automatedKeypresses=!0),document.execCommand("insertText",!1,a.data),b!==void 0&&(b.pluginData.autoCloseBrackets.automatedKeypresses=!1))},!1)}async function beginTestsAfterLoad(a,b){a.focus(),b?(await waitAsync(200),testAssertion("FallbackTextarea+PrismLineNumbers","Alignment between Fallback and Loaded Texareas",confirm("Now with the highlighting, is all the code in the same horizontal position (don't mind the vertical offset by keyboard navigation instructions) as with the last question?"),"user-judged")):(await waitAsync(200),testAssertion("FallbackTextarea-PrismLineNumbers","Alignment between Fallback and Loaded Texareas",confirm("Now with the highlighting, is all the code in the same horizontal position (don't mind the vertical offset by keyboard navigation instructions) as with the last question?"),"user-judged")),codeInputElement=a.parentElement,allowInputEvents(a,codeInputElement),assertEqual("FallbackTextarea","Textarea selectionStart Once Loaded same as Fallback Textarea's",a.selectionStart,8),assertEqual("FallbackTextarea","Textarea selectionEnd Once Loaded same as Fallback Textarea's",a.selectionEnd,11),assertEqual("FallbackTextarea","Textarea Value Once Loaded same as Fallback Textarea's",a.value,`console.log("Hello, World!"); // A second line // A third line with tags`);let c=codeInputElement.codeElement.innerHTML.replace(/<[^>]+>/g,"");assertEqual("Core","Initial Rendered Value",c,`console.log("Hello, World!"); // A second line @@ -18,4 +18,4 @@ console.log("I've got another line!", 2 < 3, "should be true."); // A third line with tags`),c=codeInputElement.codeElement.innerHTML.replace(/<[^>]+>/g,""),assertEqual("Core","Form Reset resets Rendered Value",c,`console.log("Hello, World!"); // A second line // A third line with <html> tags -`),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),codeInputElement.setAttribute("language","JavaScript"),await waitAsync(100),testAssertion("Core","Light theme Caret/Placeholder Color Correct",confirm("Are the caret and placeholder near-black? (OK=Yes)"),"user-judged"),document.getElementById("theme-stylesheet").href=b?"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/dark.min.css":"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css",await waitAsync(200),testAssertion("Core","Dark theme Caret/Placeholder Color Correct",confirm("Are the caret and placeholder near-white? (OK=Yes)"),"user-judged"),codeInputElement.style.color="red",await waitAsync(200),testAssertion("Core","Overriden color Caret/Placeholder Color Correct",confirm("Are the caret and placeholder (for Firefox) or just caret (for Chromium/WebKit, for consistency with textareas) red? (OK=Yes)"),"user-judged"),codeInputElement.style.removeProperty("color"),codeInputElement.style.caretColor="red",await waitAsync(200),testAssertion("Core","Overriden caret-color Caret/Placeholder Color Correct",confirm("Is the caret red and placeholder near-white? (OK=Yes)"),"user-judged"),codeInputElement.style.removeProperty("caret-color"),testAddingText("AutoCloseBrackets",a,function(a){addText(a,`\nconsole.log("A test message`),move(a,2),addText(a,`;\nconsole.log("Another test message");\n{[{[]}(([[`),backspace(a),backspace(a),backspace(a),addText(a,`)`)},"\nconsole.log(\"A test message\");\nconsole.log(\"Another test message\");\n{[{[]}()]}",77,77),addText(a,"popup"),await waitAsync(50),testAssertion("Autocomplete","Popup Shows on input",confirm("Does the autocomplete popup display correctly? (OK=Yes)"),"user-judged"),move(a,-1),await waitAsync(50),testAssertion("Autocomplete","Popup Disappears on arrow key",confirm("Has the popup disappeared? (OK=Yes)"),"user-judged"),move(a,1),await waitAsync(50),testAssertion("Autocomplete","Popup Shows on arrow key",confirm("Does the autocomplete popup display correctly? (OK=Yes)"),"user-judged"),backspace(a),await waitAsync(50),testAssertion("Autocomplete","Popup Disappears on backspace",confirm("Has the popup disappeared? (OK=Yes)"),"user-judged"),move(a,1),backspace(a),backspace(a),backspace(a),backspace(a),b&&(a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"console.log(\"Hello, World!\");\nfunction sayHello(name) {\n console.log(\"Hello, \" + name + \"!\");\n}\nsayHello(\"code-input\");"),await waitAsync(50),assertEqual("Autodetect","Detects JavaScript",codeInputElement.getAttribute("language"),"javascript"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"#!/usr/bin/python\nprint(\"Hello, World!\")\nfor i in range(5):\n print(i)"),await waitAsync(50),assertEqual("Autodetect","Detects Python",codeInputElement.getAttribute("language"),"python"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"body, html {\n height: 100%;\n background-color: blue;\n color: red;\n}"),await waitAsync(50),assertEqual("Autodetect","Detects CSS",codeInputElement.getAttribute("language"),"css")),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),a.parentElement.classList.add("code-input_autogrow_height"),await waitAsync(100);const p=a.parentElement.clientHeight;addText(a,"// a\n// b\n// c\n// d\n// e\n// f\n// g"),await waitAsync(100);const q=a.parentElement.clientHeight;testAssertion("Autogrow","Content Increases Height",q>p,`${q} should be > ${p}`),a.parentElement.style.setProperty("font-size","50%"),await waitAsync(200),testAssertion("Autogrow","font-size Decrease Decreases Height",a.parentElement.clientHeightr,`${s} should be > ${r}`),a.parentElement.style.setProperty("font-size","50%"),await waitAsync(200),testAssertion("Autogrow","font-size Decrease Decreases Width",a.parentElement.clientWidtha.text()).then(b=>{a.value="// code-input v2.1: A large code file (not the latest version!)\n// Editing this here should give little latency.\n\n"+b,a.selectionStart=112,a.selectionEnd=112,addText(a,"\n",!0),document.getElementById("collapse-results").setAttribute("open",!0)}),testsFailed?(document.querySelector("h2").style.backgroundColor="red",document.querySelector("h2").textContent="Some Tests have Failed."):(document.querySelector("h2").style.backgroundColor="lightgreen",document.querySelector("h2").textContent="All Tests have Passed.")} \ No newline at end of file +`),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),codeInputElement.setAttribute("language","JavaScript"),await waitAsync(100),testAssertion("Core","Light theme Caret/Placeholder Color Correct",confirm("Are the caret and placeholder near-black? (OK=Yes)"),"user-judged"),document.getElementById("theme-stylesheet").href=b?"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/dark.min.css":"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css",await waitAsync(200),testAssertion("Core","Dark theme Caret/Placeholder Color Correct",confirm("Are the caret and placeholder near-white? (OK=Yes)"),"user-judged"),codeInputElement.style.color="red",await waitAsync(200),testAssertion("Core","Overriden color Caret/Placeholder Color Correct",confirm("Are the caret and placeholder (for Firefox) or just caret (for Chromium/WebKit, for consistency with textareas) red? (OK=Yes)"),"user-judged"),codeInputElement.style.removeProperty("color"),codeInputElement.style.caretColor="red",await waitAsync(200),testAssertion("Core","Overriden caret-color Caret/Placeholder Color Correct",confirm("Is the caret red and placeholder near-white? (OK=Yes)"),"user-judged"),codeInputElement.style.removeProperty("caret-color"),testAddingText("AutoCloseBrackets",a,function(a){addText(a,`\nconsole.log("A test message`),move(a,2),addText(a,`;\nconsole.log("Another test message");\n{[{[]}(([[`),backspace(a),backspace(a),backspace(a),addText(a,`)`)},"\nconsole.log(\"A test message\");\nconsole.log(\"Another test message\");\n{[{[]}()]}",77,77),addText(a,"popup"),await waitAsync(50),testAssertion("Autocomplete","Popup Shows on input",confirm("Does the autocomplete popup display correctly? (OK=Yes)"),"user-judged"),move(a,-1),await waitAsync(50),testAssertion("Autocomplete","Popup Disappears on arrow key",confirm("Has the popup disappeared? (OK=Yes)"),"user-judged"),move(a,1),await waitAsync(50),testAssertion("Autocomplete","Popup Shows on arrow key",confirm("Does the autocomplete popup display correctly? (OK=Yes)"),"user-judged"),backspace(a),await waitAsync(50),testAssertion("Autocomplete","Popup Disappears on backspace",confirm("Has the popup disappeared? (OK=Yes)"),"user-judged"),addText(a,"p"),await waitAsync(50);const p=a.selectionStart,q=a.selectionEnd;alert("Dismiss this alert, then click the popup in the next 3 seconds.");for(let c=0;3e3>c&&!popupClicked;)await waitAsync(10),c+=10;testAssertion("Autocomplete","Popup Clickable",popupClicked,"The onclick event of the popup element didn't fire"),a.selectionStart=p,a.selectionEnd=q,a.focus(),backspace(a),backspace(a),backspace(a),backspace(a),backspace(a),b&&(a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"console.log(\"Hello, World!\");\nfunction sayHello(name) {\n console.log(\"Hello, \" + name + \"!\");\n}\nsayHello(\"code-input\");"),await waitAsync(50),assertEqual("Autodetect","Detects JavaScript",codeInputElement.getAttribute("language"),"javascript"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"#!/usr/bin/python\nprint(\"Hello, World!\")\nfor i in range(5):\n print(i)"),await waitAsync(50),assertEqual("Autodetect","Detects Python",codeInputElement.getAttribute("language"),"python"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"body, html {\n height: 100%;\n background-color: blue;\n color: red;\n}"),await waitAsync(50),assertEqual("Autodetect","Detects CSS",codeInputElement.getAttribute("language"),"css")),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),a.parentElement.classList.add("code-input_autogrow_height"),await waitAsync(100);const r=a.parentElement.clientHeight;addText(a,"// a\n// b\n// c\n// d\n// e\n// f\n// g"),await waitAsync(100);const s=a.parentElement.clientHeight;testAssertion("Autogrow","Content Increases Height",s>r,`${s} should be > ${r}`),a.parentElement.style.setProperty("font-size","50%"),await waitAsync(200),testAssertion("Autogrow","font-size Decrease Decreases Height",a.parentElement.clientHeightt,`${u} should be > ${t}`),a.parentElement.style.setProperty("font-size","50%"),await waitAsync(200),testAssertion("Autogrow","font-size Decrease Decreases Width",a.parentElement.clientWidtha.text()).then(b=>{a.value="// code-input v2.1: A large code file (not the latest version!)\n// Editing this here should give little latency.\n\n"+b,a.selectionStart=112,a.selectionEnd=112,addText(a,"\n",!0),document.getElementById("collapse-results").setAttribute("open",!0)}),testsFailed?(document.querySelector("h2").style.backgroundColor="red",document.querySelector("h2").textContent="Some Tests have Failed."):(document.querySelector("h2").style.backgroundColor="lightgreen",document.querySelector("h2").textContent="All Tests have Passed.")} \ No newline at end of file From 86f00b9eceec709fac5fc7353d5fbb1dfbeddab5 Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Fri, 3 Jul 2026 00:56:38 +0100 Subject: [PATCH 52/54] Fix bugs and add test for language attribute (Fixes #228) --- code-input.js | 14 ++++++++++---- tests/tester.js | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/code-input.js b/code-input.js index 5eb6ff0..b02c381 100644 --- a/code-input.js +++ b/code-input.js @@ -986,16 +986,16 @@ var codeInput = { /* Check regular attributes */ for(let i = 0; i < codeInput.observedAttributes.length; i++) { if (mutation.attributeName == codeInput.observedAttributes[i]) { - return this.attributeChangedCallback(mutation.attributeName, mutation.oldValue, super.getAttribute(mutation.attributeName)); + this.attributeChangedCallback(mutation.attributeName, mutation.oldValue, super.getAttribute(mutation.attributeName)); } } for(let i = 0; i < codeInput.textareaSyncAttributes.length; i++) { if (mutation.attributeName == codeInput.textareaSyncAttributes[i]) { - return this.attributeChangedCallback(mutation.attributeName, mutation.oldValue, super.getAttribute(mutation.attributeName)); + this.attributeChangedCallback(mutation.attributeName, mutation.oldValue, super.getAttribute(mutation.attributeName)); } } if (mutation.attributeName.substring(0, 5) == "aria-") { - return this.attributeChangedCallback(mutation.attributeName, mutation.oldValue, super.getAttribute(mutation.attributeName)); + this.attributeChangedCallback(mutation.attributeName, mutation.oldValue, super.getAttribute(mutation.attributeName)); } } } @@ -1059,7 +1059,13 @@ var codeInput = { } if (mainTextarea.placeholder == oldValue || oldValue == null && mainTextarea.placeholder == "") { - mainTextarea.placeholder = newValue; + if(newValue === null) { + mainTextarea.removeAttribute("placeholder"); + // If always setAttribute, would set it to the string + // "null" here, which isn't wanted.' + } else { + mainTextarea.setAttribute("placeholder", newValue); + } } this.scheduleHighlight(); diff --git a/tests/tester.js b/tests/tester.js index 96bfdd4..f619b12 100644 --- a/tests/tester.js +++ b/tests/tester.js @@ -491,6 +491,25 @@ console.log("I've got another line!", 2 < 3, "should be true."); codeInputElement.style.removeProperty("caret-color"); + if(!isHLJS) { + // These tests require autodetect plugin to be absent + + codeInputElement.setAttribute("language", "css"); + await waitAsync(50); // Wait for language to propogate to class + testAssertion("Core", "Language Class Propogates", codeInputElement.querySelector("pre").classList.contains("language-css"), `Class name of pre element was "${codeInputElement.querySelector("pre").className}" but code-input element had language="css"`); + + window.requestAnimationFrame(function() { + codeInputElement.setAttribute("placeholder", "Gimme some HTML!"); + codeInputElement.setAttribute("language", "html"); + }); + await waitAsync(500); // Wait for animation frame; language to propogate to class + testAssertion("Core", "Language Class Propogates When Set Immediately After Placeholder Set", codeInputElement.querySelector("pre").classList.contains("language-html"), `Class name of pre element was "${codeInputElement.querySelector("pre").className}" but code-input element had language="css"`); + + codeInputElement.removeAttribute("placeholder"); + codeInputElement.setAttribute("language", "JavaScript"); + await waitAsync(50); // Wait for language to propogate to class + } + /*--- Tests for plugins ---*/ // AutoCloseBrackets testAddingText("AutoCloseBrackets", textarea, function(textarea) { From 652ef886243ab004db2785ee670da618c99ebf98 Mon Sep 17 00:00:00 2001 From: WebCoder49 <69071853+WebCoder49@users.noreply.github.com> Date: Thu, 2 Jul 2026 23:58:52 +0000 Subject: [PATCH 53/54] Auto Minify JS and CSS files --- code-input.min.js | 2 +- tests/tester.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code-input.min.js b/code-input.min.js index bbe4ff2..b7bd29e 100644 --- a/code-input.min.js +++ b/code-input.min.js @@ -9,4 +9,4 @@ * @license MIT * * **** - */"use strict";var codeInput={observedAttributes:["value","placeholder","language","lang","template"],textareaSyncAttributes:["value","min","max","type","pattern","autocomplete","autocorrect","autofocus","cols","dirname","disabled","form","maxlength","minlength","name","placeholder","readonly","required","rows","spellcheck","wrap"],textareaSyncEvents:["change","selectionchange","invalid","input","focus","blur","focusin","focusout"],usedTemplates:{},defaultTemplate:void 0,templateNotYetRegisteredQueue:{},registerTemplate:function(a,b){if(!("string"==typeof a||a instanceof String))throw TypeError(`code-input: Name of template "${a}" must be a string.`);if(!("function"==typeof b.highlight||b.highlight instanceof Function))throw TypeError(`code-input: Template for "${a}" invalid, because the highlight function provided is not a function; it is "${b.highlight}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.includeCodeInputInHighlightFunc||b.includeCodeInputInHighlightFunc instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the includeCodeInputInHighlightFunc value provided is not a true or false; it is "${b.includeCodeInputInHighlightFunc}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.preElementStyled||b.preElementStyled instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the preElementStyled value provided is not a true or false; it is "${b.preElementStyled}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.isCode||b.isCode instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the isCode value provided is not a true or false; it is "${b.isCode}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!Array.isArray(b.plugins))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin array provided is not an array; it is "${b.plugins}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(b.plugins.forEach((c,d)=>{if(!(c instanceof codeInput.Plugin))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin provided at index ${d} is not valid; it is "${b.plugins[d]}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`)}),codeInput.usedTemplates[a]=b,a in codeInput.templateNotYetRegisteredQueue)for(let c in codeInput.templateNotYetRegisteredQueue[a]){const d=codeInput.templateNotYetRegisteredQueue[a][c];d.templateObject=b,d.setup()}if(null==codeInput.defaultTemplate&&(codeInput.defaultTemplate=a,void 0 in codeInput.templateNotYetRegisteredQueue))for(let a in codeInput.templateNotYetRegisteredQueue[void 0]){const c=codeInput.templateNotYetRegisteredQueue[void 0][a];c.templateObject=b,c.setup()}},stylesheetI:0,Template:class{constructor(a=function(){},b=!0,c=!0,d=!1,e=[]){this.highlight=a,this.preElementStyled=b,this.isCode=c,this.includeCodeInputInHighlightFunc=d,this.plugins=e}highlight=function(){};preElementStyled=!0;isCode=!0;includeCodeInputInHighlightFunc=!1;plugins=[]},templates:{prism(a,b=[]){return new codeInput.templates.Prism(a,b)},hljs(a,b=[]){return new codeInput.templates.Hljs(a,b)},characterLimit(a){return{highlight:function(a,b,c=[]){let d=+b.getAttribute("data-character-limit"),e=b.escapeHtml(b.value.slice(0,d)),f=b.escapeHtml(b.value.slice(d));a.innerHTML=`${e}${f}`,0${b.getAttribute("data-overflow-msg")||"(Character limit reached)"}`)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,plugins:a}},rainbowText(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return{highlight:function(a,b){let c=[],d=b.value.split(b.template.delimiter);for(let e=0;e${b.escapeHtml(d[e])}`);a.innerHTML=c.join(b.template.delimiter)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,rainbowColors:a,delimiter:b,plugins:c}},character_limit(){return this.characterLimit([])},rainbow_text(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return this.rainbowText(a,b,c)},custom(a=function(){},b=!0,c=!0,d=!1,e=[]){return{highlight:a,includeCodeInputInHighlightFunc:d,preElementStyled:b,isCode:c,plugins:e}}},plugins:new Proxy({},{get(a,b){if(a[b]==null)throw ReferenceError(`code-input: Plugin '${b}' is not defined. Please ensure you import the necessary files from the plugins folder in the WebCoder49/code-input repository, in the of your HTML, before the plugin is instatiated.`);return a[b]}}),Plugin:class{constructor(a){a.forEach(a=>{codeInput.observedAttributes.push(a)})}addTranslations(a,b){for(const c in b)a[c]=b[c]}beforeHighlight(){}afterHighlight(){}beforeElementsAdded(){}afterElementsAdded(){}attributeChanged(){}},CodeInput:class extends HTMLElement{constructor(){super()}templateObject=null;textareaElement=null;preElement=null;codeElement=null;dialogContainerElement=null;internalStyle=null;static formAssociated=!0;boundEventCallbacks={};pluginEvt(a,b){for(let c in this.templateObject.plugins){let d=this.templateObject.plugins[c];a in d&&(b===void 0?d[a](this):d[a](this,...b))}}needsHighlight=!1;originalAriaDescription;scheduleHighlight(){this.needsHighlight=!0}animateFrame(){this.needsHighlight&&(this.update(),this.needsHighlight=!1),window.requestAnimationFrame(this.animateFrame.bind(this))}update(){let a=this.codeElement,b=this.value;b+="\n",a.innerHTML=this.escapeHtml(b),this.pluginEvt("beforeHighlight"),this.templateObject.includeCodeInputInHighlightFunc?this.templateObject.highlight(a,this):this.templateObject.highlight(a),this.syncSize(),this.pluginEvt("afterHighlight")}getStyledHighlightingElement(){return this.templateObject.preElementStyled?this.preElement:this.codeElement}syncSize(){const a=getComputedStyle(this.getStyledHighlightingElement()).height;this.textareaElement.style.height=a,this.internalStyle.setProperty("--code-input_synced-height",a);const b=getComputedStyle(this.getStyledHighlightingElement()).width;this.textareaElement.style.width=b,this.internalStyle.setProperty("--code-input_synced-width",b)}syncIfColorNotOverridden(a=function(){}){if(this.checkingColorOverridden)return;this.checkingColorOverridden=!0;const b=this.style.transition;this.style.transition="unset",window.requestAnimationFrame(()=>{if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(0, 0, 0)"),"rgb(0, 0, 0)"!=getComputedStyle(this).color)this.style.transition=b,this.checkingColorOverridden=!1,a();else if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(255, 255, 255)"),"rgb(255, 255, 255)"==getComputedStyle(this).color){this.internalStyle.removeProperty("--code-input_no-override-color"),this.style.transition=b;const a=getComputedStyle(this.getStyledHighlightingElement()).color;this.internalStyle.setProperty("--code-input_highlight-text-color",a),this.internalStyle.setProperty("--code-input_default-caret-color",a),this.checkingColorOverridden=!1}else this.style.transition=b,this.checkingColorOverridden=!1,a();this.internalStyle.removeProperty("--code-input_no-override-color"),this.style.transition=b})}syncColorCompletely(){this.syncIfColorNotOverridden(()=>{this.internalStyle.removeProperty("--code-input_highlight-text-color"),this.internalStyle.setProperty("--code-input_default-caret-color",getComputedStyle(this).color)})}setKeyboardNavInstructions(a,b){this.dialogContainerElement.querySelector(".code-input_keyboard-navigation-instructions").innerText=a,b?this.textareaElement.setAttribute("aria-description",this.originalAriaDescription+". "+a):this.textareaElement.setAttribute("aria-description",a)}escapeHtml(a){return a.replace(/&/g,"&").replace(/")}getTemplate(){let a;return a=null==this.getAttribute("template")?codeInput.defaultTemplate:this.getAttribute("template"),a in codeInput.usedTemplates?codeInput.usedTemplates[a]:(a in codeInput.templateNotYetRegisteredQueue||(codeInput.templateNotYetRegisteredQueue[a]=[]),void codeInput.templateNotYetRegisteredQueue[a].push(this))}setup(){if(null!=this.textareaElement)return;this.classList.add("code-input_registered"),this.mutationObserver=new MutationObserver(this.mutationObserverCallback.bind(this)),this.mutationObserver.observe(this,{attributes:!0,attributeOldValue:!0}),this.classList.add("code-input_registered"),this.templateObject.preElementStyled&&this.classList.add("code-input_pre-element-styled");const a=this.querySelector("textarea[data-code-input-fallback]");let b,c,d,e,f,g=!1;a&&(a===document.activeElement&&(g=!0),b=a.selectionStart,c=a.selectionEnd,d=a.selectionDirection,e=a.scrollLeft,f=a.scrollTop);let h;if(a){let b=a.getAttributeNames();for(let c=0;c{this.classList.add("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("blur",()=>{this.classList.remove("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("focus",()=>{window.setTimeout(()=>{this.syncSize()},0)}),this.innerHTML="",this.classList.add("code-input_styles_"+codeInput.stylesheetI);const l=document.createElement("style");l.innerHTML="code-input.code-input_styles_"+codeInput.stylesheetI+" {}",this.appendChild(l),this.internalStyle=l.sheet.cssRules[0].style,codeInput.stylesheetI++;for(let a,b=0;b{this.value=this.textareaElement.value}),this.textareaElement=k,this.append(k),this.setupTextareaSyncEvents(this.textareaElement);let m=document.createElement("code"),n=document.createElement("pre");n.setAttribute("aria-hidden","true"),n.setAttribute("tabindex","-1"),n.setAttribute("inert",!0),this.preElement=n,this.codeElement=m,n.append(m),this.append(n),this.templateObject.isCode&&i!=null&&""!=i&&m.classList.add("language-"+i.toLowerCase());let o=document.createElement("div");o.classList.add("code-input_dialog-container"),this.append(o),this.dialogContainerElement=o;let p=document.createElement("div");p.classList.add("code-input_keyboard-navigation-instructions"),o.append(p),this.pluginEvt("afterElementsAdded"),this.dispatchEvent(new CustomEvent("code-input_load")),this.value=h,b!==void 0&&(k.setSelectionRange(b,c,d),k.scrollTo(f,e)),g&&k.focus(),this.animateFrame();const q=new ResizeObserver(()=>{this.syncSize()});q.observe(this),q.observe(this.preElement),q.observe(this.codeElement);const r=a=>{"color"==a.propertyName&&this.syncIfColorNotOverridden()};this.preElement.addEventListener("transitionend",r),this.preElement.addEventListener("-webkit-transitionend",r);const s=a=>{"color"==a.propertyName&&this.syncColorCompletely(),a.target==this.dialogContainerElement&&a.stopPropagation()};this.dialogContainerElement.addEventListener("transitionend",s),this.dialogContainerElement.addEventListener("-webkit-transitionend",s),this.addEventListener("transitionend",s),this.addEventListener("-webkit-transitionend",s),this.syncColorCompletely(),this.classList.add("code-input_loaded")}escape_html(a){return this.escapeHtml(a)}get_template(){return this.getTemplate()}get template(){return this.templateObject}set template(a){}connectedCallback(){if(this.templateObject=this.getTemplate(),null!=this.templateObject&&(this.classList.add("code-input_registered"),"loading"===document.readyState?window.addEventListener("DOMContentLoaded",this.setup.bind(this)):this.setup()),"loading"===document.readyState)window.addEventListener("DOMContentLoaded",()=>{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)});else{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)}}mutationObserverCallback(a){for(const b of a)if("attributes"===b.type){for(let a=0;a{a.match(b)&&(null==c?this.textareaElement.removeAttribute(a):this.textareaElement.setAttribute(a,c))})}}setupTextareaSyncEvents(a){for(let b=0;b{a.bubbles||this.dispatchEvent(new a.constructor(a.type,a))})}}addEventListener(a,b,c=void 0){let d=function(a){"function"==typeof b?b(a):b&&b.handleEvent&&b.handleEvent(a)}.bind(this);if(this.boundEventCallbacks[b]=d,!codeInput.textareaSyncEvents.includes(a))void 0===c?super.addEventListener(a,d):super.addEventListener(a,d,c);else if(this.boundEventCallbacks[b]=d,void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d)})}else this.textareaElement.addEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d,c)})}else this.textareaElement.addEventListener(a,d,c)}removeEventListener(a,b,c=void 0){let d=this.boundEventCallbacks[b];if(!codeInput.textareaSyncEvents.includes(a))void 0===c?super.removeEventListener(a,d):super.removeEventListener(a,d,c);else if(void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d)})}else this.textareaElement.removeEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d,c)})}else this.textareaElement.removeEventListener(a,d,c)}getTextareaProperty(a,b=void 0){if(this.textareaElement)return this.textareaElement[a];else{const c=this.querySelector("textarea[data-code-input-fallback]");if(c)return c[a];if(void 0===b)throw new Error("Cannot get "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.");return b}}setTextareaProperty(a,b,c=!0){if(this.textareaElement)this.textareaElement[a]=b;else{const d=this.querySelector("textarea[data-code-input-fallback]");if(d)d[a]=b;else{if(!c)return(!1);throw new Error("Cannot set "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.")}}return(!0)}get autocomplete(){return this.getAttribute("autocomplete")}set autocomplete(a){return this.setAttribute("autocomplete",a)}get cols(){return this.getTextareaProperty("cols",+this.getAttribute("cols"))}set cols(a){this.setAttribute("cols",a)}get defaultValue(){return this.initialValue}set defaultValue(a){this.initialValue=a}get textContent(){return this.initialValue}set textContent(a){this.initialValue=a}get dirName(){return this.getAttribute("dirName")||""}set dirName(a){this.setAttribute("dirname",a)}get disabled(){return this.hasAttribute("disabled")}set disabled(a){a?this.setAttribute("disabled",!0):this.removeAttribute("disabled")}get form(){return this.getTextareaProperty("form")}get labels(){return this.getTextareaProperty("labels")}get maxLength(){const a=+this.getAttribute("maxlength");return isNaN(a)?-1:a}set maxLength(a){-1==a?this.removeAttribute("maxlength"):this.setAttribute("maxlength",a)}get minLength(){const a=+this.getAttribute("minlength");return isNaN(a)?-1:a}set minLength(a){-1==a?this.removeAttribute("minlength"):this.setAttribute("minlength",a)}get name(){return this.getAttribute("name")||""}set name(a){this.setAttribute("name",a)}get placeholder(){return this.getAttribute("placeholder")||""}set placeholder(a){this.setAttribute("placeholder",a)}get readOnly(){return this.hasAttribute("readonly")}set readOnly(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get required(){return this.hasAttribute("readonly")}set required(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get rows(){return this.getTextareaProperty("rows",+this.getAttribute("rows"))}set rows(a){this.setAttribute("rows",a)}get selectionDirection(){return this.getTextareaProperty("selectionDirection")}set selectionDirection(a){this.setTextareaProperty("selectionDirection",a)}get selectionEnd(){return this.getTextareaProperty("selectionEnd")}set selectionEnd(a){this.setTextareaProperty("selectionEnd",a)}get selectionStart(){return this.getTextareaProperty("selectionStart")}set selectionStart(a){this.setTextareaProperty("selectionStart",a)}get textLength(){return this.value.length}get type(){return"textarea"}get validationMessage(){return this.getTextareaProperty("validationMessage")}get validity(){return this.getTextareaProperty("validationMessage")}get value(){return this.getTextareaProperty("value",this.getAttribute("value")||this.innerHTML)}set value(a){a=a||"",this.setTextareaProperty("value",a,!1)?this.textareaElement&&this.scheduleHighlight():this.innerHTML=a}get willValidate(){return this.getTextareaProperty("willValidate",this.disabled||this.readOnly)}get wrap(){return this.getAttribute("wrap")||""}set wrap(a){this.setAttribute("wrap",a)}getTextareaMethod(a){if(this.textareaElement)return this.textareaElement[a].bind(this.textareaElement);else{const b=this.querySelector("textarea[data-code-input-fallback]");if(b)return b[a].bind(b);throw new Error("Cannot call "+a+" on an unregistered code-input element without a data-code-input-fallback textarea.")}}blur(a={}){this.getTextareaMethod("blur")(a)}checkValidity(){return this.getTextareaMethod("checkValidity")()}focus(a={}){this.getTextareaMethod("focus")(a)}reportValidity(){return this.getTextareaMethod("reportValidity")()}setCustomValidity(a){this.getTextareaMethod("setCustomValidity")(a)}setRangeText(a,b=this.selectionStart,c=this.selectionEnd,d="preserve"){this.getTextareaMethod("setRangeText")(a,b,c,d),this.textareaElement&&this.scheduleHighlight()}setSelectionRange(a,b,c="none"){this.getTextareaMethod("setSelectionRange")(a,b,c)}pluginData={};formResetCallback(){this.value=this.initialValue}}};{class a extends codeInput.Template{constructor(a,b=[],c=!0){super(a.highlightElement,c,!0,!1,b)}}codeInput.templates.Prism=a;class b extends codeInput.Template{constructor(a,b=[],c=!1){super(function(b){b.removeAttribute("data-highlighted"),a.highlightElement(b)},c,!0,!1,b)}}codeInput.templates.Hljs=b}customElements.define("code-input",codeInput.CodeInput); \ No newline at end of file + */"use strict";var codeInput={observedAttributes:["value","placeholder","language","lang","template"],textareaSyncAttributes:["value","min","max","type","pattern","autocomplete","autocorrect","autofocus","cols","dirname","disabled","form","maxlength","minlength","name","placeholder","readonly","required","rows","spellcheck","wrap"],textareaSyncEvents:["change","selectionchange","invalid","input","focus","blur","focusin","focusout"],usedTemplates:{},defaultTemplate:void 0,templateNotYetRegisteredQueue:{},registerTemplate:function(a,b){if(!("string"==typeof a||a instanceof String))throw TypeError(`code-input: Name of template "${a}" must be a string.`);if(!("function"==typeof b.highlight||b.highlight instanceof Function))throw TypeError(`code-input: Template for "${a}" invalid, because the highlight function provided is not a function; it is "${b.highlight}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.includeCodeInputInHighlightFunc||b.includeCodeInputInHighlightFunc instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the includeCodeInputInHighlightFunc value provided is not a true or false; it is "${b.includeCodeInputInHighlightFunc}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.preElementStyled||b.preElementStyled instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the preElementStyled value provided is not a true or false; it is "${b.preElementStyled}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!("boolean"==typeof b.isCode||b.isCode instanceof Boolean))throw TypeError(`code-input: Template for "${a}" invalid, because the isCode value provided is not a true or false; it is "${b.isCode}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(!Array.isArray(b.plugins))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin array provided is not an array; it is "${b.plugins}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);if(b.plugins.forEach((c,d)=>{if(!(c instanceof codeInput.Plugin))throw TypeError(`code-input: Template for "${a}" invalid, because the plugin provided at index ${d} is not valid; it is "${b.plugins[d]}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`)}),codeInput.usedTemplates[a]=b,a in codeInput.templateNotYetRegisteredQueue)for(let c in codeInput.templateNotYetRegisteredQueue[a]){const d=codeInput.templateNotYetRegisteredQueue[a][c];d.templateObject=b,d.setup()}if(null==codeInput.defaultTemplate&&(codeInput.defaultTemplate=a,void 0 in codeInput.templateNotYetRegisteredQueue))for(let a in codeInput.templateNotYetRegisteredQueue[void 0]){const c=codeInput.templateNotYetRegisteredQueue[void 0][a];c.templateObject=b,c.setup()}},stylesheetI:0,Template:class{constructor(a=function(){},b=!0,c=!0,d=!1,e=[]){this.highlight=a,this.preElementStyled=b,this.isCode=c,this.includeCodeInputInHighlightFunc=d,this.plugins=e}highlight=function(){};preElementStyled=!0;isCode=!0;includeCodeInputInHighlightFunc=!1;plugins=[]},templates:{prism(a,b=[]){return new codeInput.templates.Prism(a,b)},hljs(a,b=[]){return new codeInput.templates.Hljs(a,b)},characterLimit(a){return{highlight:function(a,b,c=[]){let d=+b.getAttribute("data-character-limit"),e=b.escapeHtml(b.value.slice(0,d)),f=b.escapeHtml(b.value.slice(d));a.innerHTML=`${e}${f}`,0${b.getAttribute("data-overflow-msg")||"(Character limit reached)"}`)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,plugins:a}},rainbowText(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return{highlight:function(a,b){let c=[],d=b.value.split(b.template.delimiter);for(let e=0;e${b.escapeHtml(d[e])}`);a.innerHTML=c.join(b.template.delimiter)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,rainbowColors:a,delimiter:b,plugins:c}},character_limit(){return this.characterLimit([])},rainbow_text(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return this.rainbowText(a,b,c)},custom(a=function(){},b=!0,c=!0,d=!1,e=[]){return{highlight:a,includeCodeInputInHighlightFunc:d,preElementStyled:b,isCode:c,plugins:e}}},plugins:new Proxy({},{get(a,b){if(a[b]==null)throw ReferenceError(`code-input: Plugin '${b}' is not defined. Please ensure you import the necessary files from the plugins folder in the WebCoder49/code-input repository, in the of your HTML, before the plugin is instatiated.`);return a[b]}}),Plugin:class{constructor(a){a.forEach(a=>{codeInput.observedAttributes.push(a)})}addTranslations(a,b){for(const c in b)a[c]=b[c]}beforeHighlight(){}afterHighlight(){}beforeElementsAdded(){}afterElementsAdded(){}attributeChanged(){}},CodeInput:class extends HTMLElement{constructor(){super()}templateObject=null;textareaElement=null;preElement=null;codeElement=null;dialogContainerElement=null;internalStyle=null;static formAssociated=!0;boundEventCallbacks={};pluginEvt(a,b){for(let c in this.templateObject.plugins){let d=this.templateObject.plugins[c];a in d&&(b===void 0?d[a](this):d[a](this,...b))}}needsHighlight=!1;originalAriaDescription;scheduleHighlight(){this.needsHighlight=!0}animateFrame(){this.needsHighlight&&(this.update(),this.needsHighlight=!1),window.requestAnimationFrame(this.animateFrame.bind(this))}update(){let a=this.codeElement,b=this.value;b+="\n",a.innerHTML=this.escapeHtml(b),this.pluginEvt("beforeHighlight"),this.templateObject.includeCodeInputInHighlightFunc?this.templateObject.highlight(a,this):this.templateObject.highlight(a),this.syncSize(),this.pluginEvt("afterHighlight")}getStyledHighlightingElement(){return this.templateObject.preElementStyled?this.preElement:this.codeElement}syncSize(){const a=getComputedStyle(this.getStyledHighlightingElement()).height;this.textareaElement.style.height=a,this.internalStyle.setProperty("--code-input_synced-height",a);const b=getComputedStyle(this.getStyledHighlightingElement()).width;this.textareaElement.style.width=b,this.internalStyle.setProperty("--code-input_synced-width",b)}syncIfColorNotOverridden(a=function(){}){if(this.checkingColorOverridden)return;this.checkingColorOverridden=!0;const b=this.style.transition;this.style.transition="unset",window.requestAnimationFrame(()=>{if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(0, 0, 0)"),"rgb(0, 0, 0)"!=getComputedStyle(this).color)this.style.transition=b,this.checkingColorOverridden=!1,a();else if(this.internalStyle.setProperty("--code-input_no-override-color","rgb(255, 255, 255)"),"rgb(255, 255, 255)"==getComputedStyle(this).color){this.internalStyle.removeProperty("--code-input_no-override-color"),this.style.transition=b;const a=getComputedStyle(this.getStyledHighlightingElement()).color;this.internalStyle.setProperty("--code-input_highlight-text-color",a),this.internalStyle.setProperty("--code-input_default-caret-color",a),this.checkingColorOverridden=!1}else this.style.transition=b,this.checkingColorOverridden=!1,a();this.internalStyle.removeProperty("--code-input_no-override-color"),this.style.transition=b})}syncColorCompletely(){this.syncIfColorNotOverridden(()=>{this.internalStyle.removeProperty("--code-input_highlight-text-color"),this.internalStyle.setProperty("--code-input_default-caret-color",getComputedStyle(this).color)})}setKeyboardNavInstructions(a,b){this.dialogContainerElement.querySelector(".code-input_keyboard-navigation-instructions").innerText=a,b?this.textareaElement.setAttribute("aria-description",this.originalAriaDescription+". "+a):this.textareaElement.setAttribute("aria-description",a)}escapeHtml(a){return a.replace(/&/g,"&").replace(/")}getTemplate(){let a;return a=null==this.getAttribute("template")?codeInput.defaultTemplate:this.getAttribute("template"),a in codeInput.usedTemplates?codeInput.usedTemplates[a]:(a in codeInput.templateNotYetRegisteredQueue||(codeInput.templateNotYetRegisteredQueue[a]=[]),void codeInput.templateNotYetRegisteredQueue[a].push(this))}setup(){if(null!=this.textareaElement)return;this.classList.add("code-input_registered"),this.mutationObserver=new MutationObserver(this.mutationObserverCallback.bind(this)),this.mutationObserver.observe(this,{attributes:!0,attributeOldValue:!0}),this.classList.add("code-input_registered"),this.templateObject.preElementStyled&&this.classList.add("code-input_pre-element-styled");const a=this.querySelector("textarea[data-code-input-fallback]");let b,c,d,e,f,g=!1;a&&(a===document.activeElement&&(g=!0),b=a.selectionStart,c=a.selectionEnd,d=a.selectionDirection,e=a.scrollLeft,f=a.scrollTop);let h;if(a){let b=a.getAttributeNames();for(let c=0;c{this.classList.add("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("blur",()=>{this.classList.remove("code-input_mouse-focused"),window.setTimeout(()=>{this.syncSize()},0)}),k.addEventListener("focus",()=>{window.setTimeout(()=>{this.syncSize()},0)}),this.innerHTML="",this.classList.add("code-input_styles_"+codeInput.stylesheetI);const l=document.createElement("style");l.innerHTML="code-input.code-input_styles_"+codeInput.stylesheetI+" {}",this.appendChild(l),this.internalStyle=l.sheet.cssRules[0].style,codeInput.stylesheetI++;for(let a,b=0;b{this.value=this.textareaElement.value}),this.textareaElement=k,this.append(k),this.setupTextareaSyncEvents(this.textareaElement);let m=document.createElement("code"),n=document.createElement("pre");n.setAttribute("aria-hidden","true"),n.setAttribute("tabindex","-1"),n.setAttribute("inert",!0),this.preElement=n,this.codeElement=m,n.append(m),this.append(n),this.templateObject.isCode&&i!=null&&""!=i&&m.classList.add("language-"+i.toLowerCase());let o=document.createElement("div");o.classList.add("code-input_dialog-container"),this.append(o),this.dialogContainerElement=o;let p=document.createElement("div");p.classList.add("code-input_keyboard-navigation-instructions"),o.append(p),this.pluginEvt("afterElementsAdded"),this.dispatchEvent(new CustomEvent("code-input_load")),this.value=h,b!==void 0&&(k.setSelectionRange(b,c,d),k.scrollTo(f,e)),g&&k.focus(),this.animateFrame();const q=new ResizeObserver(()=>{this.syncSize()});q.observe(this),q.observe(this.preElement),q.observe(this.codeElement);const r=a=>{"color"==a.propertyName&&this.syncIfColorNotOverridden()};this.preElement.addEventListener("transitionend",r),this.preElement.addEventListener("-webkit-transitionend",r);const s=a=>{"color"==a.propertyName&&this.syncColorCompletely(),a.target==this.dialogContainerElement&&a.stopPropagation()};this.dialogContainerElement.addEventListener("transitionend",s),this.dialogContainerElement.addEventListener("-webkit-transitionend",s),this.addEventListener("transitionend",s),this.addEventListener("-webkit-transitionend",s),this.syncColorCompletely(),this.classList.add("code-input_loaded")}escape_html(a){return this.escapeHtml(a)}get_template(){return this.getTemplate()}get template(){return this.templateObject}set template(a){}connectedCallback(){if(this.templateObject=this.getTemplate(),null!=this.templateObject&&(this.classList.add("code-input_registered"),"loading"===document.readyState?window.addEventListener("DOMContentLoaded",this.setup.bind(this)):this.setup()),"loading"===document.readyState)window.addEventListener("DOMContentLoaded",()=>{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)});else{const a=this.querySelector("textarea[data-code-input-fallback]");a&&this.setupTextareaSyncEvents(a)}}mutationObserverCallback(a){for(const b of a)if("attributes"===b.type){for(let a=0;a{a.match(b)&&(null==c?this.textareaElement.removeAttribute(a):this.textareaElement.setAttribute(a,c))})}}setupTextareaSyncEvents(a){for(let b=0;b{a.bubbles||this.dispatchEvent(new a.constructor(a.type,a))})}}addEventListener(a,b,c=void 0){let d=function(a){"function"==typeof b?b(a):b&&b.handleEvent&&b.handleEvent(a)}.bind(this);if(this.boundEventCallbacks[b]=d,!codeInput.textareaSyncEvents.includes(a))void 0===c?super.addEventListener(a,d):super.addEventListener(a,d,c);else if(this.boundEventCallbacks[b]=d,void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d)})}else this.textareaElement.addEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.addEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d,c)})}else this.textareaElement.addEventListener(a,d,c)}removeEventListener(a,b,c=void 0){let d=this.boundEventCallbacks[b];if(!codeInput.textareaSyncEvents.includes(a))void 0===c?super.removeEventListener(a,d):super.removeEventListener(a,d,c);else if(void 0===c){if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d)})}else this.textareaElement.removeEventListener(a,d);}else if(null==this.textareaElement){const b=this.querySelector("textarea[data-code-input-fallback]");b&&b.removeEventListener(a,d,c),this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d,c)})}else this.textareaElement.removeEventListener(a,d,c)}getTextareaProperty(a,b=void 0){if(this.textareaElement)return this.textareaElement[a];else{const c=this.querySelector("textarea[data-code-input-fallback]");if(c)return c[a];if(void 0===b)throw new Error("Cannot get "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.");return b}}setTextareaProperty(a,b,c=!0){if(this.textareaElement)this.textareaElement[a]=b;else{const d=this.querySelector("textarea[data-code-input-fallback]");if(d)d[a]=b;else{if(!c)return(!1);throw new Error("Cannot set "+a+" of an unregistered code-input element without a data-code-input-fallback textarea.")}}return(!0)}get autocomplete(){return this.getAttribute("autocomplete")}set autocomplete(a){return this.setAttribute("autocomplete",a)}get cols(){return this.getTextareaProperty("cols",+this.getAttribute("cols"))}set cols(a){this.setAttribute("cols",a)}get defaultValue(){return this.initialValue}set defaultValue(a){this.initialValue=a}get textContent(){return this.initialValue}set textContent(a){this.initialValue=a}get dirName(){return this.getAttribute("dirName")||""}set dirName(a){this.setAttribute("dirname",a)}get disabled(){return this.hasAttribute("disabled")}set disabled(a){a?this.setAttribute("disabled",!0):this.removeAttribute("disabled")}get form(){return this.getTextareaProperty("form")}get labels(){return this.getTextareaProperty("labels")}get maxLength(){const a=+this.getAttribute("maxlength");return isNaN(a)?-1:a}set maxLength(a){-1==a?this.removeAttribute("maxlength"):this.setAttribute("maxlength",a)}get minLength(){const a=+this.getAttribute("minlength");return isNaN(a)?-1:a}set minLength(a){-1==a?this.removeAttribute("minlength"):this.setAttribute("minlength",a)}get name(){return this.getAttribute("name")||""}set name(a){this.setAttribute("name",a)}get placeholder(){return this.getAttribute("placeholder")||""}set placeholder(a){this.setAttribute("placeholder",a)}get readOnly(){return this.hasAttribute("readonly")}set readOnly(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get required(){return this.hasAttribute("readonly")}set required(a){a?this.setAttribute("readonly",!0):this.removeAttribute("readonly")}get rows(){return this.getTextareaProperty("rows",+this.getAttribute("rows"))}set rows(a){this.setAttribute("rows",a)}get selectionDirection(){return this.getTextareaProperty("selectionDirection")}set selectionDirection(a){this.setTextareaProperty("selectionDirection",a)}get selectionEnd(){return this.getTextareaProperty("selectionEnd")}set selectionEnd(a){this.setTextareaProperty("selectionEnd",a)}get selectionStart(){return this.getTextareaProperty("selectionStart")}set selectionStart(a){this.setTextareaProperty("selectionStart",a)}get textLength(){return this.value.length}get type(){return"textarea"}get validationMessage(){return this.getTextareaProperty("validationMessage")}get validity(){return this.getTextareaProperty("validationMessage")}get value(){return this.getTextareaProperty("value",this.getAttribute("value")||this.innerHTML)}set value(a){a=a||"",this.setTextareaProperty("value",a,!1)?this.textareaElement&&this.scheduleHighlight():this.innerHTML=a}get willValidate(){return this.getTextareaProperty("willValidate",this.disabled||this.readOnly)}get wrap(){return this.getAttribute("wrap")||""}set wrap(a){this.setAttribute("wrap",a)}getTextareaMethod(a){if(this.textareaElement)return this.textareaElement[a].bind(this.textareaElement);else{const b=this.querySelector("textarea[data-code-input-fallback]");if(b)return b[a].bind(b);throw new Error("Cannot call "+a+" on an unregistered code-input element without a data-code-input-fallback textarea.")}}blur(a={}){this.getTextareaMethod("blur")(a)}checkValidity(){return this.getTextareaMethod("checkValidity")()}focus(a={}){this.getTextareaMethod("focus")(a)}reportValidity(){return this.getTextareaMethod("reportValidity")()}setCustomValidity(a){this.getTextareaMethod("setCustomValidity")(a)}setRangeText(a,b=this.selectionStart,c=this.selectionEnd,d="preserve"){this.getTextareaMethod("setRangeText")(a,b,c,d),this.textareaElement&&this.scheduleHighlight()}setSelectionRange(a,b,c="none"){this.getTextareaMethod("setSelectionRange")(a,b,c)}pluginData={};formResetCallback(){this.value=this.initialValue}}};{class a extends codeInput.Template{constructor(a,b=[],c=!0){super(a.highlightElement,c,!0,!1,b)}}codeInput.templates.Prism=a;class b extends codeInput.Template{constructor(a,b=[],c=!1){super(function(b){b.removeAttribute("data-highlighted"),a.highlightElement(b)},c,!0,!1,b)}}codeInput.templates.Hljs=b}customElements.define("code-input",codeInput.CodeInput); \ No newline at end of file diff --git a/tests/tester.min.js b/tests/tester.min.js index 537ad31..083b35c 100644 --- a/tests/tester.min.js +++ b/tests/tester.min.js @@ -18,4 +18,4 @@ console.log("I've got another line!", 2 < 3, "should be true."); // A third line with tags`),c=codeInputElement.codeElement.innerHTML.replace(/<[^>]+>/g,""),assertEqual("Core","Form Reset resets Rendered Value",c,`console.log("Hello, World!"); // A second line // A third line with <html> tags -`),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),codeInputElement.setAttribute("language","JavaScript"),await waitAsync(100),testAssertion("Core","Light theme Caret/Placeholder Color Correct",confirm("Are the caret and placeholder near-black? (OK=Yes)"),"user-judged"),document.getElementById("theme-stylesheet").href=b?"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/dark.min.css":"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css",await waitAsync(200),testAssertion("Core","Dark theme Caret/Placeholder Color Correct",confirm("Are the caret and placeholder near-white? (OK=Yes)"),"user-judged"),codeInputElement.style.color="red",await waitAsync(200),testAssertion("Core","Overriden color Caret/Placeholder Color Correct",confirm("Are the caret and placeholder (for Firefox) or just caret (for Chromium/WebKit, for consistency with textareas) red? (OK=Yes)"),"user-judged"),codeInputElement.style.removeProperty("color"),codeInputElement.style.caretColor="red",await waitAsync(200),testAssertion("Core","Overriden caret-color Caret/Placeholder Color Correct",confirm("Is the caret red and placeholder near-white? (OK=Yes)"),"user-judged"),codeInputElement.style.removeProperty("caret-color"),testAddingText("AutoCloseBrackets",a,function(a){addText(a,`\nconsole.log("A test message`),move(a,2),addText(a,`;\nconsole.log("Another test message");\n{[{[]}(([[`),backspace(a),backspace(a),backspace(a),addText(a,`)`)},"\nconsole.log(\"A test message\");\nconsole.log(\"Another test message\");\n{[{[]}()]}",77,77),addText(a,"popup"),await waitAsync(50),testAssertion("Autocomplete","Popup Shows on input",confirm("Does the autocomplete popup display correctly? (OK=Yes)"),"user-judged"),move(a,-1),await waitAsync(50),testAssertion("Autocomplete","Popup Disappears on arrow key",confirm("Has the popup disappeared? (OK=Yes)"),"user-judged"),move(a,1),await waitAsync(50),testAssertion("Autocomplete","Popup Shows on arrow key",confirm("Does the autocomplete popup display correctly? (OK=Yes)"),"user-judged"),backspace(a),await waitAsync(50),testAssertion("Autocomplete","Popup Disappears on backspace",confirm("Has the popup disappeared? (OK=Yes)"),"user-judged"),addText(a,"p"),await waitAsync(50);const p=a.selectionStart,q=a.selectionEnd;alert("Dismiss this alert, then click the popup in the next 3 seconds.");for(let c=0;3e3>c&&!popupClicked;)await waitAsync(10),c+=10;testAssertion("Autocomplete","Popup Clickable",popupClicked,"The onclick event of the popup element didn't fire"),a.selectionStart=p,a.selectionEnd=q,a.focus(),backspace(a),backspace(a),backspace(a),backspace(a),backspace(a),b&&(a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"console.log(\"Hello, World!\");\nfunction sayHello(name) {\n console.log(\"Hello, \" + name + \"!\");\n}\nsayHello(\"code-input\");"),await waitAsync(50),assertEqual("Autodetect","Detects JavaScript",codeInputElement.getAttribute("language"),"javascript"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"#!/usr/bin/python\nprint(\"Hello, World!\")\nfor i in range(5):\n print(i)"),await waitAsync(50),assertEqual("Autodetect","Detects Python",codeInputElement.getAttribute("language"),"python"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"body, html {\n height: 100%;\n background-color: blue;\n color: red;\n}"),await waitAsync(50),assertEqual("Autodetect","Detects CSS",codeInputElement.getAttribute("language"),"css")),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),a.parentElement.classList.add("code-input_autogrow_height"),await waitAsync(100);const r=a.parentElement.clientHeight;addText(a,"// a\n// b\n// c\n// d\n// e\n// f\n// g"),await waitAsync(100);const s=a.parentElement.clientHeight;testAssertion("Autogrow","Content Increases Height",s>r,`${s} should be > ${r}`),a.parentElement.style.setProperty("font-size","50%"),await waitAsync(200),testAssertion("Autogrow","font-size Decrease Decreases Height",a.parentElement.clientHeightt,`${u} should be > ${t}`),a.parentElement.style.setProperty("font-size","50%"),await waitAsync(200),testAssertion("Autogrow","font-size Decrease Decreases Width",a.parentElement.clientWidtha.text()).then(b=>{a.value="// code-input v2.1: A large code file (not the latest version!)\n// Editing this here should give little latency.\n\n"+b,a.selectionStart=112,a.selectionEnd=112,addText(a,"\n",!0),document.getElementById("collapse-results").setAttribute("open",!0)}),testsFailed?(document.querySelector("h2").style.backgroundColor="red",document.querySelector("h2").textContent="Some Tests have Failed."):(document.querySelector("h2").style.backgroundColor="lightgreen",document.querySelector("h2").textContent="All Tests have Passed.")} \ No newline at end of file +`),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),codeInputElement.setAttribute("language","JavaScript"),await waitAsync(100),testAssertion("Core","Light theme Caret/Placeholder Color Correct",confirm("Are the caret and placeholder near-black? (OK=Yes)"),"user-judged"),document.getElementById("theme-stylesheet").href=b?"https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/dark.min.css":"https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css",await waitAsync(200),testAssertion("Core","Dark theme Caret/Placeholder Color Correct",confirm("Are the caret and placeholder near-white? (OK=Yes)"),"user-judged"),codeInputElement.style.color="red",await waitAsync(200),testAssertion("Core","Overriden color Caret/Placeholder Color Correct",confirm("Are the caret and placeholder (for Firefox) or just caret (for Chromium/WebKit, for consistency with textareas) red? (OK=Yes)"),"user-judged"),codeInputElement.style.removeProperty("color"),codeInputElement.style.caretColor="red",await waitAsync(200),testAssertion("Core","Overriden caret-color Caret/Placeholder Color Correct",confirm("Is the caret red and placeholder near-white? (OK=Yes)"),"user-judged"),codeInputElement.style.removeProperty("caret-color"),b||(codeInputElement.setAttribute("language","css"),await waitAsync(50),testAssertion("Core","Language Class Propogates",codeInputElement.querySelector("pre").classList.contains("language-css"),`Class name of pre element was "${codeInputElement.querySelector("pre").className}" but code-input element had language="css"`),window.requestAnimationFrame(function(){codeInputElement.setAttribute("placeholder","Gimme some HTML!"),codeInputElement.setAttribute("language","html")}),await waitAsync(500),testAssertion("Core","Language Class Propogates When Set Immediately After Placeholder Set",codeInputElement.querySelector("pre").classList.contains("language-html"),`Class name of pre element was "${codeInputElement.querySelector("pre").className}" but code-input element had language="css"`),codeInputElement.removeAttribute("placeholder"),codeInputElement.setAttribute("language","JavaScript"),await waitAsync(50)),testAddingText("AutoCloseBrackets",a,function(a){addText(a,`\nconsole.log("A test message`),move(a,2),addText(a,`;\nconsole.log("Another test message");\n{[{[]}(([[`),backspace(a),backspace(a),backspace(a),addText(a,`)`)},"\nconsole.log(\"A test message\");\nconsole.log(\"Another test message\");\n{[{[]}()]}",77,77),addText(a,"popup"),await waitAsync(50),testAssertion("Autocomplete","Popup Shows on input",confirm("Does the autocomplete popup display correctly? (OK=Yes)"),"user-judged"),move(a,-1),await waitAsync(50),testAssertion("Autocomplete","Popup Disappears on arrow key",confirm("Has the popup disappeared? (OK=Yes)"),"user-judged"),move(a,1),await waitAsync(50),testAssertion("Autocomplete","Popup Shows on arrow key",confirm("Does the autocomplete popup display correctly? (OK=Yes)"),"user-judged"),backspace(a),await waitAsync(50),testAssertion("Autocomplete","Popup Disappears on backspace",confirm("Has the popup disappeared? (OK=Yes)"),"user-judged"),addText(a,"p"),await waitAsync(50);const p=a.selectionStart,q=a.selectionEnd;alert("Dismiss this alert, then click the popup in the next 3 seconds.");for(let c=0;3e3>c&&!popupClicked;)await waitAsync(10),c+=10;testAssertion("Autocomplete","Popup Clickable",popupClicked,"The onclick event of the popup element didn't fire"),a.selectionStart=p,a.selectionEnd=q,a.focus(),backspace(a),backspace(a),backspace(a),backspace(a),backspace(a),b&&(a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"console.log(\"Hello, World!\");\nfunction sayHello(name) {\n console.log(\"Hello, \" + name + \"!\");\n}\nsayHello(\"code-input\");"),await waitAsync(50),assertEqual("Autodetect","Detects JavaScript",codeInputElement.getAttribute("language"),"javascript"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"#!/usr/bin/python\nprint(\"Hello, World!\")\nfor i in range(5):\n print(i)"),await waitAsync(50),assertEqual("Autodetect","Detects Python",codeInputElement.getAttribute("language"),"python"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"body, html {\n height: 100%;\n background-color: blue;\n color: red;\n}"),await waitAsync(50),assertEqual("Autodetect","Detects CSS",codeInputElement.getAttribute("language"),"css")),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),a.parentElement.classList.add("code-input_autogrow_height"),await waitAsync(100);const r=a.parentElement.clientHeight;addText(a,"// a\n// b\n// c\n// d\n// e\n// f\n// g"),await waitAsync(100);const s=a.parentElement.clientHeight;testAssertion("Autogrow","Content Increases Height",s>r,`${s} should be > ${r}`),a.parentElement.style.setProperty("font-size","50%"),await waitAsync(200),testAssertion("Autogrow","font-size Decrease Decreases Height",a.parentElement.clientHeightt,`${u} should be > ${t}`),a.parentElement.style.setProperty("font-size","50%"),await waitAsync(200),testAssertion("Autogrow","font-size Decrease Decreases Width",a.parentElement.clientWidtha.text()).then(b=>{a.value="// code-input v2.1: A large code file (not the latest version!)\n// Editing this here should give little latency.\n\n"+b,a.selectionStart=112,a.selectionEnd=112,addText(a,"\n",!0),document.getElementById("collapse-results").setAttribute("open",!0)}),testsFailed?(document.querySelector("h2").style.backgroundColor="red",document.querySelector("h2").textContent="Some Tests have Failed."):(document.querySelector("h2").style.backgroundColor="lightgreen",document.querySelector("h2").textContent="All Tests have Passed.")} \ No newline at end of file From 2d1be3df08ee20c1375272b2b16fbd4c11a199bb Mon Sep 17 00:00:00 2001 From: Oliver Geer Date: Fri, 3 Jul 2026 01:01:13 +0100 Subject: [PATCH 54/54] Release v2.8.3 --- docs/_index.md | 2 +- package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/_index.md b/docs/_index.md index 56f4f9f..cc13b73 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -8,7 +8,7 @@ title = 'Flexible Syntax Highlighted Editable Textareas' ## Download -*`code-input.js` is free, libre, open source software under the MIT (AKA Expat) license.* You have choices! **Download it from the Git repository on [GitHub](https://github.com/WebCoder49/code-input/tree/v2.8.2) or [Codeberg](https://codeberg.org/code-input-js/code-input-js/), [in a ZIP archive](/release/code-input-js-v2.8.2.zip), [in a TAR.GZ archive](/release/code-input-js-v2.8.2.tar.gz), or from `@webcoder49/code-input` on the NPM registry ([Yarn](https://yarnpkg.com/package?name=@webcoder49/code-input), [NPM](https://npmjs.com/package/@webcoder49/code-input), etc.).** +*`code-input.js` is free, libre, open source software under the MIT (AKA Expat) license.* You have choices! **Download it from the Git repository on [GitHub](https://github.com/WebCoder49/code-input/tree/v2.8.3) or [Codeberg](https://codeberg.org/code-input-js/code-input-js/), [in a ZIP archive](/release/code-input-js-v2.8.3.zip), [in a TAR.GZ archive](/release/code-input-js-v2.8.3.tar.gz), or from `@webcoder49/code-input` on the NPM registry ([Yarn](https://yarnpkg.com/package?name=@webcoder49/code-input), [NPM](https://npmjs.com/package/@webcoder49/code-input), etc.).** [Want to contribute to the code? You're very welcome to! See here.](#contributing) diff --git a/package.json b/package.json index e70b3cb..85abb1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@webcoder49/code-input", - "version": "2.8.2", + "version": "2.8.3", "description": "An editable <textarea> that supports *any* syntax highlighting algorithm, for code or something else. Also, added plugins.", "browser": "code-input.js", "exports": { @@ -90,7 +90,7 @@ "highlight", "textarea", "editable", - "web-components", + "web-component", "code-editor", "text-editor" ],