diff --git a/.github/workflows/minify.yml b/.github/workflows/minify.yml index 0f8d01e..e7d1eed 100644 --- a/.github/workflows/minify.yml +++ b/.github/workflows/minify.yml @@ -8,15 +8,14 @@ jobs: runs-on: ubuntu-latest steps: # Checks-out your repository under $GITHUB_WORKSPACE, so auto-minify job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Auto Minify - uses: nizarmah/auto-minify@v2.1 + uses: nizarmah/auto-minify@v3 # Auto commits minified files to the repository # Ignore it if you don't want to commit the files to the repository - name: Auto committing minified files - uses: stefanzweifel/git-auto-commit-action@v4 + uses: stefanzweifel/git-auto-commit-action@v6 with: - commit_message: "Auto Minified JS and CSS files" - branch: ${{ github.ref }} \ No newline at end of file + commit_message: "Auto Minify JS and CSS files" diff --git a/code-input.css b/code-input.css index 1795174..1023209 100644 --- a/code-input.css +++ b/code-input.css @@ -20,7 +20,14 @@ code-input { top: 0; left: 0; - color: black; + /* CSS variables rather than inline styles used for values synced from JavaScript + to keep low precedence and thus overridability + The variable names may change and are for internal use. */ + /* --code-input_highlight-text-color: Set by JS to be base text color of pre code element */ + /* --code-input_no-override-color: Set by JS for very short time to get whether color has been overriden */ + color: var(--code-input_no-override-color, black); + /* --code-input_default-caret-color: Set by JS to be same as color property - currentColor won't work because it's lazily evaluated so gives transparent for the textarea */ + caret-color: var(--code-input_default-caret-color, inherit); background-color: white; /* Normal inline styles */ @@ -30,13 +37,13 @@ code-input { --padding-right: var(--padding, 16px); --padding-top: var(--padding, 16px); --padding-bottom: var(--padding, 16px); + height: 250px; font-size: inherit; font-family: monospace; text-align: start; line-height: 1.5; /* Inherited to child elements */ tab-size: 2; - caret-color: darkgrey; white-space: pre; padding: 0!important; /* Use --padding to set the code-input element's padding */ display: grid; @@ -44,7 +51,7 @@ code-input { grid-template-rows: 100%; } -code-input * { +code-input :not(.code-input_dialog-container *) { box-sizing: content-box; /* Make height, width work consistently no matter the box-sizing of ancestors; dialogs can be styled as wanted so are excluded. */ } @@ -56,18 +63,23 @@ code-input textarea, code-input:not(.code-input_pre-element-styled) pre code, co padding-top: var(--padding-top, 16px)!important; padding-bottom: var(--padding-bottom, 16px)!important; border: 0; + min-width: calc(100% - var(--padding-left, 16px) - var(--padding-right, 16px)); min-height: calc(100% - var(--padding-top, 16px) - var(--padding-bottom, 16px)); - overflow: hidden; resize: none; + overflow: hidden; grid-row: 1; grid-column: 1; display: block; } - code-input:not(.code-input_pre-element-styled) pre code, code-input.code-input_pre-element-styled pre { height: max-content; width: max-content; + + /* Allow colour change to reflect properly; + transition-behavior: allow-discrete could be used but this is better supported and + works with the color property. */ + transition: color 0.001s; } code-input:not(.code-input_pre-element-styled) pre, code-input.code-input_pre-element-styled pre code { @@ -75,6 +87,9 @@ code-input:not(.code-input_pre-element-styled) pre, code-input.code-input_pre-el margin: 0!important; padding: 0!important; border: 0!important; + + min-width: 100%; + min-height: 100%; } code-input textarea, code-input pre, code-input pre * { @@ -85,6 +100,9 @@ code-input textarea, code-input pre, code-input pre * { tab-size: inherit!important; text-align: inherit!important; } +code-input textarea, code-input pre, code-input pre code { + overflow: visible!important; +} /* Make changing the text direction propogate */ code-input textarea[dir=auto] + pre { @@ -118,12 +136,13 @@ code-input pre { /* Make textarea almost completely transparent, except for caret and placeholder */ code-input textarea:not([data-code-input-fallback]) { - color: transparent; background: transparent; - caret-color: inherit!important; /* Or choose your favourite color */ + color: transparent; + caret-color: inherit; } -code-input textarea::placeholder { - color: lightgrey; +code-input textarea:not([data-code-input-fallback]):placeholder-shown { + /* Show placeholder */ + color: var(--code-input_highlight-text-color, inherit); } /* Can be scrolled */ @@ -163,6 +182,11 @@ code-input .code-input_dialog-container { /* Dialog boxes' text is based on text-direction */ text-align: inherit; + + /* Allow colour change to reflect properly; + * transition-behavior: allow-discrete could be used but this is * better supported and works with the color property. */ + color: inherit; + transition: color 0.001s; } [dir=rtl] code-input .code-input_dialog-container, code-input[dir=rtl] .code-input_dialog-container { @@ -252,11 +276,13 @@ code-input:not(.code-input_loaded) pre, code-input:not(.code-input_loaded) texta code-input:has(textarea[data-code-input-fallback]) { padding: 0!important; /* Padding now in the textarea */ box-sizing: content-box; + + caret-color: revert; /* JS not setting the colour since no highlighting */ } code-input textarea[data-code-input-fallback] { overflow: auto; background-color: inherit; - color: inherit; + color: var(--code-input_highlight-text-color, inherit); /* Don't overlap with message */ min-height: calc(100% - var(--padding-top, 16px) - 2em - var(--padding-bottom, 16px)); diff --git a/code-input.js b/code-input.js index b61123c..0d9d646 100644 --- a/code-input.js +++ b/code-input.js @@ -152,6 +152,8 @@ var codeInput = { } }, + stylesheetI: 0, // Increments to give different classes to each code-input element so they can have custom styles synchronised internally without affecting the inline style + /** * Please see `codeInput.templates.prism` or `codeInput.templates.hljs`. * Templates are used in `` elements and once registered with @@ -445,6 +447,16 @@ var codeInput = { */ dialogContainerElement = null; + /** + * Like style attribute, but with a specificity of 1 + * element, 1 class. Present so styles can be set on only + * this element while giving other code freedom of use of + * the style attribute. + * + * For internal use only. + */ + internalStyle = null; + /** * Form-Associated Custom Element Callbacks * https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example @@ -530,22 +542,75 @@ var codeInput = { this.pluginEvt("afterHighlight"); } + getStyledHighlightingElement() { + if(this.templateObject.preElementStyled) { + return this.preElement; + } else { + return this.codeElement; + } + } + /** * Set the size of the textarea element to the size of the pre/code element. */ syncSize() { // Synchronise the size of the pre/code and textarea elements - if(this.templateObject.preElementStyled) { - this.style.backgroundColor = getComputedStyle(this.preElement).backgroundColor; - this.textareaElement.style.height = getComputedStyle(this.preElement).height; - this.textareaElement.style.width = getComputedStyle(this.preElement).width; - } else { - this.style.backgroundColor = getComputedStyle(this.codeElement).backgroundColor; - this.textareaElement.style.height = getComputedStyle(this.codeElement).height; - this.textareaElement.style.width = getComputedStyle(this.codeElement).width; + this.textareaElement.style.height = getComputedStyle(this.getStyledHighlightingElement()).height; + this.textareaElement.style.width = getComputedStyle(this.getStyledHighlightingElement()).width; + } + + /** + * If the color attribute has been defined on the + * code-input element by external code, return true. + * 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. + */ + isColorOverridenSyncIfNot() { + const oldTransition = this.style.transition; + this.style.transition = "unset"; + window.requestAnimationFrame(() => { + this.internalStyle.setProperty("--code-input_no-override-color", "rgb(0, 0, 0)"); + if(getComputedStyle(this).color == "rgb(0, 0, 0)") { + // May not be overriden + this.internalStyle.setProperty("--code-input_no-override-color", "rgb(255, 255, 255)"); + if(getComputedStyle(this).color == "rgb(255, 255, 255)") { + // Definitely not overriden + this.internalStyle.removeProperty("--code-input_no-override-color"); + 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.internalStyle.removeProperty("--code-input_no-override-color"); + this.style.transition = oldTransition; + }); + + return true; + } + + /** + * Update the aspects the color affects + * (placeholder and caret colour) to the correct + * colour: either that defined on the code-input + * element, or if none is defined externally the + * base colour of the highlighted text. + */ + syncColorCompletely() { + // color of code-input element + if(this.isColorOverridenSyncIfNot()) { + // color overriden + this.internalStyle.removeProperty("--code-input_highlight-text-color"); + this.internalStyle.setProperty("--code-input_default-caret-color", getComputedStyle(this).color); } } + /** * Show some instructions to the user only if they are using keyboard navigation - for example, a prompt on how to navigate with the keyboard if Tab is repurposed. * @param {string} instructions The instructions to display only if keyboard navigation is being used. If it's blank, no instructions will be shown. @@ -741,7 +806,6 @@ var codeInput = { this.codeElement = code; pre.append(code); this.append(pre); - if (this.templateObject.isCode) { if (lang != undefined && lang != "") { code.classList.add("language-" + lang.toLowerCase()); @@ -780,7 +844,44 @@ var codeInput = { // The only element that could be resized is this code-input element. this.syncSize(); }); - resizeObserver.observe(this.textareaElement); + 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 + " {}"; + this.appendChild(stylesheet); + this.internalStyle = stylesheet.sheet.cssRules[0].style; + codeInput.stylesheetI++; + + // Synchronise colors + const preColorChangeCallback = (evt) => { + if(evt.propertyName == "color") { + this.isColorOverridenSyncIfNot(); + } + }; + this.preElement.addEventListener("transitionend", preColorChangeCallback); + this.preElement.addEventListener("-webkit-transitionend", preColorChangeCallback); + const thisColorChangeCallback = (evt) => { + if(evt.propertyName == "color") { + this.syncColorCompletely(); + } + if(evt.target == this.dialogContainerElement) { + evt.stopPropagation(); + // Prevent bubbling because code-input + // transitionend is separate + } + }; + // Not on this element so CSS transition property does not override publicly-visible one + this.dialogContainerElement.addEventListener("transitionend", thisColorChangeCallback); + this.dialogContainerElement.addEventListener("-webkit-transitionend", thisColorChangeCallback); + + // For when this code-input element has an externally-defined, different-duration transition + this.addEventListener("transitionend", thisColorChangeCallback); + this.addEventListener("-webkit-transitionend", thisColorChangeCallback); + + this.syncColorCompletely(); this.classList.add("code-input_loaded"); } @@ -938,7 +1039,9 @@ var codeInput = { code.classList.add("language-" + newValue); } - if (mainTextarea.placeholder == oldValue) mainTextarea.placeholder = newValue; + if (mainTextarea.placeholder == oldValue || oldValue == null && mainTextarea.placeholder == "") { + mainTextarea.placeholder = newValue; + } this.scheduleHighlight(); diff --git a/code-input.min.css b/code-input.min.css index 35b8590..4a5060a 100644 --- a/code-input.min.css +++ b/code-input.min.css @@ -1 +1 @@ -code-input{display:block;overflow-y:auto;overflow-x:auto;position:relative;top:0;left:0;color:#000;background-color:#fff;margin:8px;--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;font-family:monospace;text-align:start;line-height:1.5;tab-size:2;caret-color:#a9a9a9;white-space:pre;padding:0!important;display:grid;grid-template-columns:100%;grid-template-rows:100%}code-input *{box-sizing:content-box}code-input textarea,code-input.code-input_pre-element-styled pre,code-input:not(.code-input_pre-element-styled) pre code{margin:0!important;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;border:0;min-width:calc(100% - var(--padding-left,16px) - var(--padding-right,16px));min-height:calc(100% - var(--padding-top,16px) - var(--padding-bottom,16px));overflow:hidden;resize:none;grid-row:1;grid-column:1;display:block}code-input.code-input_pre-element-styled pre,code-input:not(.code-input_pre-element-styled) pre code{height:max-content;width:max-content}code-input.code-input_pre-element-styled pre code,code-input:not(.code-input_pre-element-styled) pre{margin:0!important;padding:0!important;border:0!important}code-input pre,code-input pre *,code-input textarea{font-size:inherit!important;font-family:inherit!important;line-height:inherit!important;tab-size:inherit!important;text-align:inherit!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 pre,code-input textarea{grid-column:1;grid-row: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:transparent;background:0 0;caret-color:inherit!important}code-input textarea::placeholder{color:#d3d3d3}code-input pre,code-input textarea{white-space:inherit;word-spacing:normal;word-break:normal;word-wrap:normal}code-input textarea{resize:none;outline:0!important}code-input:has(textarea:focus):not(.code-input_mouse-focused){outline:2px solid currentColor}code-input .code-input_dialog-container{z-index:2;position:sticky;grid-row:1;grid-column:1;top:0;left:0;margin:0;padding:0;height:0;width:100%;text-align:inherit}[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{top:0;left:0;display:block;position:absolute;top:0;left:0;background-color:#000;color:#fff;padding:2px;padding-left:var(--padding-left,16px);padding-right:var(--padding-right,16px);margin:0;text-wrap:balance;overflow-x:hidden;overflow-y:auto;width:100%;box-sizing:border-box;height:3em}code-input .code-input_dialog-container .code-input_keyboard-navigation-instructions:empty,code-input.code-input_mouse-focused .code-input_dialog-container .code-input_keyboard-navigation-instructions,code-input:not(:has(textarea:not([data-code-input-fallback]):focus)) .code-input_dialog-container .code-input_keyboard-navigation-instructions{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).code-input_pre-element-styled pre,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{padding-top:calc(var(--padding-top,16px) + 3em)!important;min-height:calc(100% - var(--padding-top,16px) - 3em - var(--padding-bottom,16px))}code-input:not(.code-input_loaded){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;overflow:hidden;display:block;box-sizing:border-box}code-input:not(.code-input_loaded)::after{content:"No highlighting. JavaScript support is disabled or insufficient, or codeInput.registerTemplate has not been called.";display:block;position:absolute;bottom:0;left:var(--padding-left,16px);width:calc(100% - var(--padding-left,1.6px) - var(--padding-right,1.6px));overflow-x:auto;border-top:1px solid currentColor;outline-top:0;background-color:inherit;color:inherit;margin:0;padding:0;height:2em}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]){padding:0!important;box-sizing:content-box}code-input textarea[data-code-input-fallback]{overflow:auto;background-color:inherit;color:inherit;min-height:calc(100% - var(--padding-top,16px) - 2em - var(--padding-bottom,16px))} \ 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 textarea,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 diff --git a/code-input.min.js b/code-input.min.js index defcc40..9c07f6f 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()}},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;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")}syncSize(){this.templateObject.preElementStyled?(this.style.backgroundColor=getComputedStyle(this.preElement).backgroundColor,this.textareaElement.style.height=getComputedStyle(this.preElement).height,this.textareaElement.style.width=getComputedStyle(this.preElement).width):(this.style.backgroundColor=getComputedStyle(this.codeElement).backgroundColor,this.textareaElement.style.height=getComputedStyle(this.codeElement).height,this.textareaElement.style.width=getComputedStyle(this.codeElement).width)}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&&(console.log("sel",b,c,d,"scr",f,e,"foc",g),k.setSelectionRange(b,c,d),k.scrollTo(f,e)),g&&k.focus(),this.animateFrame();const p=new ResizeObserver(()=>{this.syncSize()});p.observe(this.textareaElement),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}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 diff --git a/docs/_index.md b/docs/_index.md index bac2dbf..de83094 100644 --- a/docs/_index.md +++ b/docs/_index.md @@ -11,7 +11,11 @@ Aiming to be [more flexible, lightweight, [HTML forms](interface/forms), the [` - - + + - + - - + + - + - - + + - + - - + + - + - + - - + + - - + +

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.

@@ -134,12 +135,12 @@ Right now, you can only add one plugin of each type (e.g. one SelectTokenCallbac - - + + - - + + - - + + - - + + - + - - + + - + - - + + - + @@ -567,12 +568,12 @@ 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.

+

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

@@ -645,14 +646,14 @@ See https://github.com/WebCoder49/code-input/issues?q=is%3Aissue%20state%3Aopen% - - + + - + @@ -42,7 +42,7 @@

Test for Prism.js

Test Results (Click to Open)
- diff --git a/tests/i18n-common.js b/tests/i18n-common.js new file mode 100644 index 0000000..bb288dd --- /dev/null +++ b/tests/i18n-common.js @@ -0,0 +1,64 @@ +// Attribution: Translated by Oliver Geer with some help from English Wiktionary +let findAndReplaceTranslations = { + start: "Buscar términos en su código.", + none: "No hay sucesos", + oneFound: "1 suceso encontrado.", + matchIndex: (index, count) => `${index} de ${count} sucesos.`, + error: (message) => `Error: ${message}`, + infiniteLoopError: "Causa un ciclo infinito", + closeDialog: "Cerrar el Diálogo y Regresar al Editor", + findPlaceholder: "Buscar", + findCaseSensitive: "Prestar atención a las minúsculas/mayúsculas", + findRegExp: "Utilizar expresión regular de JavaScript", + replaceTitle: "Reemplazar", + replacePlaceholder: "Reemplazar con", + findNext: "Buscar Suceso Próximo", + findPrevious: "Buscar Suceso Previo", + replaceActionShort: "Reemplazar", + replaceAction: "Reemplazar este Suceso", + replaceAllActionShort: "Reemplazar Todos", + replaceAllAction: "Reemplazar Todos los Sucesos" +}; +let goToLineTranslations = { + closeDialog: "Cerrar el Diálogo y Regresar al Editor", + input: "Línea:Columno o Línea luego Retorno", + guidanceFormat: "Formato incorrecto. Ingresa un número de línea (por ej. 1) o un número de línea luego dos puntos luego un número de columno (por ej. 1:3).", + guidanceLineRange: (current, max) => `Número de línea (actualmente ${current}) debería ser entre 1 y ${max}.`, + guidanceColumnRange: (line, current, max) => `En la línea número ${line}, número de columno (actualmente ${current}) debería ser entre 1 y ${max}.`, + guidanceValidLine: (line) => `Tecla Retorno para ir a línea número ${line}.`, + guidanceValidColumn: (line, column) => `Tecla Retorno para ir a línea número ${line}, columno número ${column}.`, + }; + let indentTranslations = { + tabForIndentation: "Tabulador y Mayús-Tabulador actualmente para la indentación. Tecla Escape para activar la navegación por el teclado.", + tabForNavigation: "Tabulador y Mayús-Tabulador actualmente para la navegación por el teclado. Tecla para activar la indentación.", +}; + +rtl = `# ערך מומלץ ערך מומלץ + +**לוטרת** הים היא יונק ימי קטן יחסית, חבר במשפחת הסמורים, שחי לחופיו הצפוניים והמזרחיים של האוקיינוס השקט. + +ההגנה מפני קור אצל לוטרת הים מבוססת על שכבה עבה של פרווה, שהיא מהצפופות בעולם החי, +וזאת בשונה ממרבית היונקים הימיים הנסמכים על שכבת שומן. CC-BY-SA he.wikipedia.org אף שהיא מסוגלת להלך על היבשה, מבלה לוטרת הים את מרבית זמנה באוקיינוס הפתוח. + +# مقالة اليوم المختارة + +**الواقعية في الأدب الإسبا**ني هي حركة أدبية شكلت جزءًا من الواقعية، وهو تيار ثقافي ظهر في أوروبا في منتصف القرن التاسع عشر عقب اضمحلال اتجاهات الرومانسية. + +ظهر سابقًا في فرنسا سنة 1850 حيث تطورت أصوله التي كانت موجودة بالفعل في الرومانسية، وخصوصًا في الأدب الذي يتناول العادات والتقاليد. CC-BY-SA ar.wikipedia.org التي كانت تعج بكل ما هو خيالي وجمالي خلاب، وعمدوا إلى الملاحظة الموضوعية للأشخاص والمجتمع والأحداث المعاصرة في محاولة منهم إلى تقديم صورة واضحة للمجتمع آنذاك.`; + ltr = `# From today's featured article + +CC-BY-SA en.wikipedia.org: History is the systematic study of the past with its main focus on the human past. + +Historians analyse and interpret primary and secondary sources to construct narratives about what happened and explain why it happened. RTL: مقالة اليوم المختارة They engage in source criticism to assess the authenticity, content, and reliability of these sources. + +# निर्वाचित लेख + +CC-BY-SA hi.wikipedia.org: **ग्लेशियर नेशनल पार्क** अमेरिकी राष्ट्रीय उद्यान है, जो कि कनाडा-संयुक्त राज्य अमेरिका की सीमा पर स्थित है। उद्यान संयुक्त राज्य के उत्तर-पश्चिमी मोंटाना राज्य + +में स्थित है और कनाडा की ओर अल्बर्टा और ब्रिटिश कोलम्बिया प्रांतों से सटा हुआ है। उद्यान दस लाख एकड़ RTL: ערך מומלץ ערך מומלץ (4,000 किमी2) से अधिक क्षेत्र में फैला हुआ है और इसमें दो पर्वत श्रृंखला (रॉकी पर्वत की उप-श्रेणियाँ), 130 से अधिक नामित झीलें...`; +const elems = document.querySelectorAll("body > code-input, body > textarea"); +for(let i = 0; i < elems.length; i++) { + let dir = elems[i].getAttribute("dir"); + if(dir == "rtl") elems[i].value = rtl; + else elems[i].value = ltr; +} diff --git a/tests/i18n-common.min.js b/tests/i18n-common.min.js new file mode 100644 index 0000000..9b269f1 --- /dev/null +++ b/tests/i18n-common.min.js @@ -0,0 +1,22 @@ +let findAndReplaceTranslations={start:"Buscar t\xE9rminos en su c\xF3digo.",none:"No hay sucesos",oneFound:"1 suceso encontrado.",matchIndex:(a,b)=>`${a} de ${b} sucesos.`,error:a=>`Error: ${a}`,infiniteLoopError:"Causa un ciclo infinito",closeDialog:"Cerrar el Di\xE1logo y Regresar al Editor",findPlaceholder:"Buscar",findCaseSensitive:"Prestar atenci\xF3n a las min\xFAsculas/may\xFAsculas",findRegExp:"Utilizar expresi\xF3n regular de JavaScript",replaceTitle:"Reemplazar",replacePlaceholder:"Reemplazar con",findNext:"Buscar Suceso Pr\xF3ximo",findPrevious:"Buscar Suceso Previo",replaceActionShort:"Reemplazar",replaceAction:"Reemplazar este Suceso",replaceAllActionShort:"Reemplazar Todos",replaceAllAction:"Reemplazar Todos los Sucesos"},goToLineTranslations={closeDialog:"Cerrar el Di\xE1logo y Regresar al Editor",input:"L\xEDnea:Columno o L\xEDnea luego Retorno",guidanceFormat:"Formato incorrecto. Ingresa un n\xFAmero de l\xEDnea (por ej. 1) o un n\xFAmero de l\xEDnea luego dos puntos luego un n\xFAmero de columno (por ej. 1:3).",guidanceLineRange:(a,b)=>`Número de línea (actualmente ${a}) debería ser entre 1 y ${b}.`,guidanceColumnRange:(a,b,c)=>`En la línea número ${a}, número de columno (actualmente ${b}) debería ser entre 1 y ${c}.`,guidanceValidLine:a=>`Tecla Retorno para ir a línea número ${a}.`,guidanceValidColumn:(a,b)=>`Tecla Retorno para ir a línea número ${a}, columno número ${b}.`},indentTranslations={tabForIndentation:"Tabulador y May\xFAs-Tabulador actualmente para la indentaci\xF3n. Tecla Escape para activar la navegaci\xF3n por el teclado.",tabForNavigation:"Tabulador y May\xFAs-Tabulador actualmente para la navegaci\xF3n por el teclado. Tecla para activar la indentaci\xF3n."};rtl=`# ערך מומלץ ערך מומלץ + +**לוטרת** הים היא יונק ימי קטן יחסית, חבר במשפחת הסמורים, שחי לחופיו הצפוניים והמזרחיים של האוקיינוס השקט. + +ההגנה מפני קור אצל לוטרת הים מבוססת על שכבה עבה של פרווה, שהיא מהצפופות בעולם החי, +וזאת בשונה ממרבית היונקים הימיים הנסמכים על שכבת שומן. CC-BY-SA he.wikipedia.org אף שהיא מסוגלת להלך על היבשה, מבלה לוטרת הים את מרבית זמנה באוקיינוס הפתוח. + +# مقالة اليوم المختارة + +**الواقعية في الأدب الإسبا**ني هي حركة أدبية شكلت جزءًا من الواقعية، وهو تيار ثقافي ظهر في أوروبا في منتصف القرن التاسع عشر عقب اضمحلال اتجاهات الرومانسية. + +ظهر سابقًا في فرنسا سنة 1850 حيث تطورت أصوله التي كانت موجودة بالفعل في الرومانسية، وخصوصًا في الأدب الذي يتناول العادات والتقاليد. CC-BY-SA ar.wikipedia.org التي كانت تعج بكل ما هو خيالي وجمالي خلاب، وعمدوا إلى الملاحظة الموضوعية للأشخاص والمجتمع والأحداث المعاصرة في محاولة منهم إلى تقديم صورة واضحة للمجتمع آنذاك.`,ltr=`# From today's featured article + +CC-BY-SA en.wikipedia.org: History is the systematic study of the past with its main focus on the human past. + +Historians analyse and interpret primary and secondary sources to construct narratives about what happened and explain why it happened. RTL: مقالة اليوم المختارة They engage in source criticism to assess the authenticity, content, and reliability of these sources. + +# निर्वाचित लेख + +CC-BY-SA hi.wikipedia.org: **ग्लेशियर नेशनल पार्क** अमेरिकी राष्ट्रीय उद्यान है, जो कि कनाडा-संयुक्त राज्य अमेरिका की सीमा पर स्थित है। उद्यान संयुक्त राज्य के उत्तर-पश्चिमी मोंटाना राज्य + +में स्थित है और कनाडा की ओर अल्बर्टा और ब्रिटिश कोलम्बिया प्रांतों से सटा हुआ है। उद्यान दस लाख एकड़ RTL: ערך מומלץ ערך מומלץ (4,000 किमी2) से अधिक क्षेत्र में फैला हुआ है और इसमें दो पर्वत श्रृंखला (रॉकी पर्वत की उप-श्रेणियाँ), 130 से अधिक नामित झीलें...`;const elems=document.querySelectorAll("body > code-input, body > textarea");for(let a,b=0;b + + + code-input should be global: highlight.js + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/i18n-prism.html b/tests/i18n-prism.html new file mode 100644 index 0000000..3697c0c --- /dev/null +++ b/tests/i18n-prism.html @@ -0,0 +1,72 @@ + + + + code-input should be global: Prism.js + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/i18n.html b/tests/i18n.html index 46734d7..e73e3b2 100644 --- a/tests/i18n.html +++ b/tests/i18n.html @@ -4,170 +4,23 @@ code-input should be global - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +

Prism.js and highlight.js embedded separately: not in same page because their styles can interfere with each other whether or not code-input.js is present.

+ + diff --git a/tests/prism.html b/tests/prism.html index 50e4570..bfc6906 100644 --- a/tests/prism.html +++ b/tests/prism.html @@ -5,7 +5,7 @@ code-input Tester - + diff --git a/tests/tester.js b/tests/tester.js index 00d849d..ed5b293 100644 --- a/tests/tester.js +++ b/tests/tester.js @@ -383,6 +383,34 @@ console.log("I've got another line!", 2 < 3, "should be true."); // A third line with <html> tags `); // Extra newline so line numbers visible if enabled. + // Delete all code + textarea.selectionStart = 0; + textarea.selectionEnd = textarea.value.length; + backspace(textarea); + codeInputElement.setAttribute("language", "JavaScript"); // for placeholder + + await waitAsync(100); // Wait for rendered value to update + testAssertion("Core", "Light theme Caret/Placeholder Color Correct", confirm("Are the caret and placeholder near-black? (OK=Yes)"), "user-judged"); + + if(isHLJS) { + document.getElementById("theme-stylesheet").href = "https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/dark.min.css"; + } else { + document.getElementById("theme-stylesheet").href = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css"; + } + await waitAsync(200); // Wait for colours to update + 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); // Wait for colours to update + 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); // Wait for colours to update + 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"); + /*--- Tests for plugins ---*/ // AutoCloseBrackets testAddingText("AutoCloseBrackets", textarea, function(textarea) { diff --git a/tests/tester.min.js b/tests/tester.min.js index 938c696..88683fa 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 -`),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),addText(a,"// hello /\\S/g\nhe('llo', /\\s/g);\nhello\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\na very very very very very very very very very very very very long line with many many many many many many many many many many many words\nline\nline\nline\nline\nline\nline\nline"),a.selectionStart=a.selectionEnd=0,await waitAsync(50),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"f",ctrlKey:!0}));let p=codeInputElement.querySelectorAll(".code-input_find-and-replace_dialog input"),q=p[0],r=p[1],s=p[2],t=p[3],u=codeInputElement.querySelectorAll(".code-input_find-and-replace_dialog button"),v=u[0],w=u[1],x=u[2],y=u[3],z=codeInputElement.querySelector(".code-input_find-and-replace_dialog details summary");q.value="/\\s/g",s.click(),await waitAsync(150),testAssertion("FindAndReplace","Finds Case-Sensitive Matches Correctly",confirm("Is there a match on only the lowercase '/\\s/g'?"),"user-judged"),q.value="he[^l]*llo",r.click(),s.click(),await waitAsync(150),testAssertion("FindAndReplace","Finds RegExp Matches Correctly",confirm("Are there matches on all 'he...llo's?"),"user-judged"),z.click(),w.click(),t.value="do('hello",x.click(),await waitAsync(50),assertEqual("FindAndReplace","Replaces Once Correctly",a.value,"// hello /\\S/g\ndo('hello', /\\s/g);\nhello\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\na very very very very very very very very very very very very long line with many many many many many many many many many many many words\nline\nline\nline\nline\nline\nline\nline"),v.click(),codeInputElement.querySelector(".code-input_find-and-replace_dialog").dispatchEvent(new KeyboardEvent("keydown",{key:"Escape"})),codeInputElement.querySelector(".code-input_find-and-replace_dialog").dispatchEvent(new KeyboardEvent("keyup",{key:"Escape"})),assertEqual("FindAndReplace","Selection Start on Focused Match when Dialog Exited",a.selectionStart,3),assertEqual("FindAndReplace","Selection End on Focused Match when Dialog Exited",a.selectionEnd,8),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"h",ctrlKey:!0})),q.value="",q.focus(),allowInputEvents(q),addText(q,"hello"),await waitAsync(200),t.value="hi",y.click(),assertEqual("FindAndReplace","Replaces All Correctly",a.value,"// hi /\\S/g\ndo('hi', /\\s/g);\nhi\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\na very very very very very very very very very very very very long line with many many many many many many many many many many many words\nline\nline\nline\nline\nline\nline\nline"),q.value="line",r.click(),await waitAsync(200),codeInputElement.style="height: 100px; width: 1000px;",w.click(),w.click(),w.click(),w.click(),w.click(),w.click(),w.click(),w.click(),await waitAsync(200),testAssertion("FindAndReplace","Scrolls to Match Correctly",confirm("Is the match on the very long line highlighted orange and near the centre of the element?"),"user-judged"),codeInputElement.style="",codeInputElement.querySelector(".code-input_find-and-replace_dialog").dispatchEvent(new KeyboardEvent("keydown",{key:"Escape"})),codeInputElement.querySelector(".code-input_find-and-replace_dialog").dispatchEvent(new KeyboardEvent("keyup",{key:"Escape"})),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"// 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"),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"g",ctrlKey:!0}));let A=codeInputElement.querySelector(".code-input_go-to-line_dialog input");A.value="1",A.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Enter"})),assertEqual("GoToLine","Line Only",a.selectionStart,0),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"g",ctrlKey:!0})),A.value="3:18",A.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Enter"})),assertEqual("GoToLine","Line and Column",a.selectionStart,45),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"g",ctrlKey:!0})),A.value="10",A.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Enter"})),assertEqual("GoToLine","Rejects Out-of-range Line",A.classList.contains("code-input_go-to-line_error"),!0),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"g",ctrlKey:!0})),A.value="2:12",A.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Enter"})),assertEqual("GoToLine","Rejects Out-of-range Column",A.classList.contains("code-input_go-to-line_error"),!0),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"g",ctrlKey:!0})),A.value="sausages",A.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Enter"})),assertEqual("GoToLine","Rejects Invalid Input",A.classList.contains("code-input_go-to-line_error"),!0),assertEqual("GoToLine","Stays open when Rejects Input",A.parentElement.classList.contains("code-input_go-to-line_hidden-dialog"),!1),A.dispatchEvent(new KeyboardEvent("keydown",{key:"Escape"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Escape"})),assertEqual("GoToLine","Exits when Esc pressed",A.parentElement.classList.contains("code-input_go-to-line_hidden-dialog"),!0),a.selectionStart=a.selectionEnd=a.value.length,addText(a,"\nfor(let i = 0; i < 100; i++) {\n for(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n }\n}\n{\n // This is indented\n}"),a.selectionStart=0,a.selectionEnd=a.value.length,a.dispatchEvent(new KeyboardEvent("keydown",{key:"Tab",shiftKey:!1})),a.dispatchEvent(new KeyboardEvent("keyup",{key:"Tab",shiftKey:!1})),assertEqual("Indent","Indents Lines",a.value," // 7 times table\n let i = 1;\n while(i <= 12) { console.log(`7 x ${i} = ${7*i}`) }\n // That's my code.\n // This is another comment\n // Another\n // Line\n for(let i = 0; i < 100; i++) {\n for(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n }\n }\n {\n // This is indented\n }"),a.dispatchEvent(new KeyboardEvent("keydown",{key:"Tab",shiftKey:!0})),a.dispatchEvent(new KeyboardEvent("keyup",{key:"Tab",shiftKey:!0})),assertEqual("Indent","Unindents Lines",a.value,"// 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\nfor(let i = 0; i < 100; i++) {\n for(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n }\n}\n{\n // This is indented\n}"),a.dispatchEvent(new KeyboardEvent("keydown",{key:"Tab",shiftKey:!0})),a.dispatchEvent(new KeyboardEvent("keyup",{key:"Tab",shiftKey:!0})),assertEqual("Indent","Unindents Lines where some are already fully unindented",a.value,"// 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\nfor(let i = 0; i < 100; i++) {\nfor(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n}\n}\n{\n// This is indented\n}"),a.selectionStart=255,a.selectionEnd=274,a.dispatchEvent(new KeyboardEvent("keydown",{key:"Tab",shiftKey:!1})),a.dispatchEvent(new KeyboardEvent("keyup",{key:"Tab",shiftKey:!1})),assertEqual("Indent","Indents Lines by Selection",a.value,"// 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\nfor(let i = 0; i < 100; i++) {\nfor(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n}\n}\n{\n // This is indented\n}"),a.selectionStart=265,a.selectionEnd=265,a.dispatchEvent(new KeyboardEvent("keydown",{key:"Tab",shiftKey:!0})),a.dispatchEvent(new KeyboardEvent("keyup",{key:"Tab",shiftKey:!0})),assertEqual("Indent","Unindents Lines by Selection",a.value,"// 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\nfor(let i = 0; i < 100; i++) {\nfor(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n}\n}\n{\n// This is indented\n}"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),testAddingText("Indent-AutoCloseBrackets",a,function(a){addText(a,`function printTriples(max) {\nfor(let i = 0; i < max-2; i++) {\nfor(let j = 0; j < max-1; j++) {\nfor(let k = 0; k < max; k++) {\nconsole.log(i,j,k);\n}\n//Hmmm...\n}//Test auto-unindent\n{`,!0),move(a,1),backspace(a)},"function printTriples(max) {\n for(let i = 0; i < max-2; i++) {\n for(let j = 0; j < max-1; j++) {\n for(let k = 0; k < max; k++) {\n console.log(i,j,k);\n }\n //Hmmm...\n }//Test auto-unindent\n {\n }\n }\n }\n}",221,221),b?(addText(a,"\nlet x = 1;\nlet y = 2;\nconsole.log(`${x} + ${y} = ${x+y}`);"),move(a,-4),a.selectionStart-=35,await waitAsync(50),assertEqual("SelectTokenCallbacks","Number of Selected Tokens",codeInputElement.querySelectorAll(".in-selection").length,13),assertEqual("SelectTokenCallbacks","Number of Selected .hljs-string Tokens",codeInputElement.querySelectorAll(".hljs-string.in-selection").length,0),assertEqual("SelectTokenCallbacks","Number of Selected .hljs-subst Tokens",codeInputElement.querySelectorAll(".hljs-subst.in-selection").length,2)):(addText(a,"\n[(),((),'Hi')]"),await waitAsync(50),move(a,-2),await waitAsync(50),assertEqual("SelectTokenCallbacks","Number of Selected Braces 1",codeInputElement.getElementsByClassName("brace-hover").length,2),move(a,1),await waitAsync(50),assertEqual("SelectTokenCallbacks","Number of Selected Braces 2",codeInputElement.getElementsByClassName("brace-hover").length,4)),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"\"Some special characters: \x96,\x01\x03,\x02...\""),a.selectionStart=a.value.length-4,a.selectionEnd=a.value.length,await waitAsync(50),testAssertion("SpecialChars","Displays Correctly",confirm("Do the special characters read (0096),(0001)(0003),(0002) and align with the ellipsis? (OK=Yes)"),"user-judged"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),fetch(new Request("https://cdn.jsdelivr.net/gh/webcoder49/code-input@2.1/code-input.js")).then(a=>a.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"),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),addText(a,"// hello /\\S/g\nhe('llo', /\\s/g);\nhello\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\na very very very very very very very very very very very very long line with many many many many many many many many many many many words\nline\nline\nline\nline\nline\nline\nline"),a.selectionStart=a.selectionEnd=0,await waitAsync(50),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"f",ctrlKey:!0}));let p=codeInputElement.querySelectorAll(".code-input_find-and-replace_dialog input"),q=p[0],r=p[1],s=p[2],t=p[3],u=codeInputElement.querySelectorAll(".code-input_find-and-replace_dialog button"),v=u[0],w=u[1],x=u[2],y=u[3],z=codeInputElement.querySelector(".code-input_find-and-replace_dialog details summary");q.value="/\\s/g",s.click(),await waitAsync(150),testAssertion("FindAndReplace","Finds Case-Sensitive Matches Correctly",confirm("Is there a match on only the lowercase '/\\s/g'?"),"user-judged"),q.value="he[^l]*llo",r.click(),s.click(),await waitAsync(150),testAssertion("FindAndReplace","Finds RegExp Matches Correctly",confirm("Are there matches on all 'he...llo's?"),"user-judged"),z.click(),w.click(),t.value="do('hello",x.click(),await waitAsync(50),assertEqual("FindAndReplace","Replaces Once Correctly",a.value,"// hello /\\S/g\ndo('hello', /\\s/g);\nhello\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\na very very very very very very very very very very very very long line with many many many many many many many many many many many words\nline\nline\nline\nline\nline\nline\nline"),v.click(),codeInputElement.querySelector(".code-input_find-and-replace_dialog").dispatchEvent(new KeyboardEvent("keydown",{key:"Escape"})),codeInputElement.querySelector(".code-input_find-and-replace_dialog").dispatchEvent(new KeyboardEvent("keyup",{key:"Escape"})),assertEqual("FindAndReplace","Selection Start on Focused Match when Dialog Exited",a.selectionStart,3),assertEqual("FindAndReplace","Selection End on Focused Match when Dialog Exited",a.selectionEnd,8),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"h",ctrlKey:!0})),q.value="",q.focus(),allowInputEvents(q),addText(q,"hello"),await waitAsync(200),t.value="hi",y.click(),assertEqual("FindAndReplace","Replaces All Correctly",a.value,"// hi /\\S/g\ndo('hi', /\\s/g);\nhi\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\nline\na very very very very very very very very very very very very long line with many many many many many many many many many many many words\nline\nline\nline\nline\nline\nline\nline"),q.value="line",r.click(),await waitAsync(200),codeInputElement.style="height: 100px; width: 1000px;",w.click(),w.click(),w.click(),w.click(),w.click(),w.click(),w.click(),w.click(),await waitAsync(200),testAssertion("FindAndReplace","Scrolls to Match Correctly",confirm("Is the match on the very long line highlighted orange and near the centre of the element?"),"user-judged"),codeInputElement.style="",codeInputElement.querySelector(".code-input_find-and-replace_dialog").dispatchEvent(new KeyboardEvent("keydown",{key:"Escape"})),codeInputElement.querySelector(".code-input_find-and-replace_dialog").dispatchEvent(new KeyboardEvent("keyup",{key:"Escape"})),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"// 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"),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"g",ctrlKey:!0}));let A=codeInputElement.querySelector(".code-input_go-to-line_dialog input");A.value="1",A.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Enter"})),assertEqual("GoToLine","Line Only",a.selectionStart,0),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"g",ctrlKey:!0})),A.value="3:18",A.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Enter"})),assertEqual("GoToLine","Line and Column",a.selectionStart,45),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"g",ctrlKey:!0})),A.value="10",A.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Enter"})),assertEqual("GoToLine","Rejects Out-of-range Line",A.classList.contains("code-input_go-to-line_error"),!0),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"g",ctrlKey:!0})),A.value="2:12",A.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Enter"})),assertEqual("GoToLine","Rejects Out-of-range Column",A.classList.contains("code-input_go-to-line_error"),!0),a.dispatchEvent(new KeyboardEvent("keydown",{cancelable:!0,key:"g",ctrlKey:!0})),A.value="sausages",A.dispatchEvent(new KeyboardEvent("keydown",{key:"Enter"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Enter"})),assertEqual("GoToLine","Rejects Invalid Input",A.classList.contains("code-input_go-to-line_error"),!0),assertEqual("GoToLine","Stays open when Rejects Input",A.parentElement.classList.contains("code-input_go-to-line_hidden-dialog"),!1),A.dispatchEvent(new KeyboardEvent("keydown",{key:"Escape"})),A.dispatchEvent(new KeyboardEvent("keyup",{key:"Escape"})),assertEqual("GoToLine","Exits when Esc pressed",A.parentElement.classList.contains("code-input_go-to-line_hidden-dialog"),!0),a.selectionStart=a.selectionEnd=a.value.length,addText(a,"\nfor(let i = 0; i < 100; i++) {\n for(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n }\n}\n{\n // This is indented\n}"),a.selectionStart=0,a.selectionEnd=a.value.length,a.dispatchEvent(new KeyboardEvent("keydown",{key:"Tab",shiftKey:!1})),a.dispatchEvent(new KeyboardEvent("keyup",{key:"Tab",shiftKey:!1})),assertEqual("Indent","Indents Lines",a.value," // 7 times table\n let i = 1;\n while(i <= 12) { console.log(`7 x ${i} = ${7*i}`) }\n // That's my code.\n // This is another comment\n // Another\n // Line\n for(let i = 0; i < 100; i++) {\n for(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n }\n }\n {\n // This is indented\n }"),a.dispatchEvent(new KeyboardEvent("keydown",{key:"Tab",shiftKey:!0})),a.dispatchEvent(new KeyboardEvent("keyup",{key:"Tab",shiftKey:!0})),assertEqual("Indent","Unindents Lines",a.value,"// 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\nfor(let i = 0; i < 100; i++) {\n for(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n }\n}\n{\n // This is indented\n}"),a.dispatchEvent(new KeyboardEvent("keydown",{key:"Tab",shiftKey:!0})),a.dispatchEvent(new KeyboardEvent("keyup",{key:"Tab",shiftKey:!0})),assertEqual("Indent","Unindents Lines where some are already fully unindented",a.value,"// 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\nfor(let i = 0; i < 100; i++) {\nfor(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n}\n}\n{\n// This is indented\n}"),a.selectionStart=255,a.selectionEnd=274,a.dispatchEvent(new KeyboardEvent("keydown",{key:"Tab",shiftKey:!1})),a.dispatchEvent(new KeyboardEvent("keyup",{key:"Tab",shiftKey:!1})),assertEqual("Indent","Indents Lines by Selection",a.value,"// 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\nfor(let i = 0; i < 100; i++) {\nfor(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n}\n}\n{\n // This is indented\n}"),a.selectionStart=265,a.selectionEnd=265,a.dispatchEvent(new KeyboardEvent("keydown",{key:"Tab",shiftKey:!0})),a.dispatchEvent(new KeyboardEvent("keyup",{key:"Tab",shiftKey:!0})),assertEqual("Indent","Unindents Lines by Selection",a.value,"// 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\nfor(let i = 0; i < 100; i++) {\nfor(let j = i; j < 100; j++) {\n // Here's some code\n console.log(i,j);\n}\n}\n{\n// This is indented\n}"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),testAddingText("Indent-AutoCloseBrackets",a,function(a){addText(a,`function printTriples(max) {\nfor(let i = 0; i < max-2; i++) {\nfor(let j = 0; j < max-1; j++) {\nfor(let k = 0; k < max; k++) {\nconsole.log(i,j,k);\n}\n//Hmmm...\n}//Test auto-unindent\n{`,!0),move(a,1),backspace(a)},"function printTriples(max) {\n for(let i = 0; i < max-2; i++) {\n for(let j = 0; j < max-1; j++) {\n for(let k = 0; k < max; k++) {\n console.log(i,j,k);\n }\n //Hmmm...\n }//Test auto-unindent\n {\n }\n }\n }\n}",221,221),b?(addText(a,"\nlet x = 1;\nlet y = 2;\nconsole.log(`${x} + ${y} = ${x+y}`);"),move(a,-4),a.selectionStart-=35,await waitAsync(50),assertEqual("SelectTokenCallbacks","Number of Selected Tokens",codeInputElement.querySelectorAll(".in-selection").length,13),assertEqual("SelectTokenCallbacks","Number of Selected .hljs-string Tokens",codeInputElement.querySelectorAll(".hljs-string.in-selection").length,0),assertEqual("SelectTokenCallbacks","Number of Selected .hljs-subst Tokens",codeInputElement.querySelectorAll(".hljs-subst.in-selection").length,2)):(addText(a,"\n[(),((),'Hi')]"),await waitAsync(50),move(a,-2),await waitAsync(50),assertEqual("SelectTokenCallbacks","Number of Selected Braces 1",codeInputElement.getElementsByClassName("brace-hover").length,2),move(a,1),await waitAsync(50),assertEqual("SelectTokenCallbacks","Number of Selected Braces 2",codeInputElement.getElementsByClassName("brace-hover").length,4)),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),addText(a,"\"Some special characters: \x96,\x01\x03,\x02...\""),a.selectionStart=a.value.length-4,a.selectionEnd=a.value.length,await waitAsync(50),testAssertion("SpecialChars","Displays Correctly",confirm("Do the special characters read (0096),(0001)(0003),(0002) and align with the ellipsis? (OK=Yes)"),"user-judged"),a.selectionStart=0,a.selectionEnd=a.value.length,backspace(a),fetch(new Request("https://cdn.jsdelivr.net/gh/webcoder49/code-input@2.1/code-input.js")).then(a=>a.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