@webcoder49/code-input 2.2.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -4
- package/code-input.css +91 -19
- package/code-input.d.ts +94 -13
- package/code-input.js +114 -40
- package/code-input.min.css +1 -1
- package/code-input.min.js +1 -1
- package/package.json +1 -1
- package/plugins/README.md +7 -0
- package/plugins/autocomplete.js +4 -2
- package/plugins/autocomplete.min.js +1 -1
- package/plugins/find-and-replace.js +136 -42
- package/plugins/find-and-replace.min.js +1 -1
- package/plugins/go-to-line.js +20 -2
- package/plugins/go-to-line.min.js +1 -1
- package/plugins/indent.js +97 -6
- package/plugins/indent.min.js +1 -1
- package/plugins/prism-line-numbers.css +10 -9
- package/plugins/prism-line-numbers.min.css +1 -1
- package/plugins/select-token-callbacks.js +289 -0
- package/plugins/select-token-callbacks.min.js +1 -0
- package/plugins/special-chars.css +1 -1
- package/plugins/special-chars.js +10 -8
- package/plugins/special-chars.min.css +1 -1
- package/plugins/special-chars.min.js +1 -1
- package/tests/hljs.html +2 -1
- package/tests/i18n.html +197 -0
- package/tests/prism-match-braces-compatibility.js +215 -0
- package/tests/prism-match-braces-compatibility.min.js +1 -0
- package/tests/prism.html +7 -8
- package/tests/tester.js +77 -13
- package/tests/tester.min.js +7 -4
package/code-input.js
CHANGED
|
@@ -158,14 +158,14 @@ var codeInput = {
|
|
|
158
158
|
/**
|
|
159
159
|
* Constructor to create a custom template instance. Pass this into `codeInput.registerTemplate` to use it.
|
|
160
160
|
* I would strongly recommend using the built-in simpler template `codeInput.templates.prism` or `codeInput.templates.hljs`.
|
|
161
|
-
* @param {
|
|
161
|
+
* @param {(codeElement: HTMLCodeElement, codeInput?: codeInput.CodeInput) => void} highlight - a callback to highlight the code, that takes an HTML `<code>` element inside a `<pre>` element as a parameter
|
|
162
162
|
* @param {boolean} preElementStyled - is the `<pre>` element CSS-styled as well as the `<code>` element? If true, `<pre>` element's scrolling is synchronised; if false, `<code>` element's scrolling is synchronised.
|
|
163
163
|
* @param {boolean} isCode - is this for writing code? If true, the code-input's lang HTML attribute can be used, and the `<code>` element will be given the class name 'language-[lang attribute's value]'.
|
|
164
164
|
* @param {boolean} includeCodeInputInHighlightFunc - Setting this to true passes the `<code-input>` element as a second argument to the highlight function.
|
|
165
165
|
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.Plugin`
|
|
166
166
|
* @returns {codeInput.Template} template object
|
|
167
167
|
*/
|
|
168
|
-
constructor(highlight = function () { }, preElementStyled = true, isCode = true, includeCodeInputInHighlightFunc = false, plugins = []) {
|
|
168
|
+
constructor(highlight = function (codeElement) { }, preElementStyled = true, isCode = true, includeCodeInputInHighlightFunc = false, plugins = []) {
|
|
169
169
|
this.highlight = highlight;
|
|
170
170
|
this.preElementStyled = preElementStyled;
|
|
171
171
|
this.isCode = isCode;
|
|
@@ -377,6 +377,17 @@ var codeInput = {
|
|
|
377
377
|
});
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
+
/**
|
|
381
|
+
* Replace the keys in destination with any source
|
|
382
|
+
* @param {Object} destination Where to place the translated strings, already filled with the keys pointing to English strings.
|
|
383
|
+
* @param {Object} source The same keys, or some of them, mapped to translated strings.
|
|
384
|
+
*/
|
|
385
|
+
addTranslations(destination, source) {
|
|
386
|
+
for(const key in source) {
|
|
387
|
+
destination[key] = source[key];
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
380
391
|
/**
|
|
381
392
|
* Runs before code is highlighted.
|
|
382
393
|
* @param {codeInput.CodeInput} codeInput - The codeInput element
|
|
@@ -482,6 +493,8 @@ var codeInput = {
|
|
|
482
493
|
* to syntax-highlight it. */
|
|
483
494
|
|
|
484
495
|
needsHighlight = false; // Just inputted
|
|
496
|
+
handleEventsFromTextarea = true; // Turn to false when unusual internal events are called on the textarea
|
|
497
|
+
originalAriaDescription;
|
|
485
498
|
|
|
486
499
|
/**
|
|
487
500
|
* Highlight the code as soon as possible
|
|
@@ -494,17 +507,6 @@ var codeInput = {
|
|
|
494
507
|
* Call an animation frame
|
|
495
508
|
*/
|
|
496
509
|
animateFrame() {
|
|
497
|
-
// Synchronise the size of the pre/code and textarea elements
|
|
498
|
-
if(this.template.preElementStyled) {
|
|
499
|
-
this.style.backgroundColor = getComputedStyle(this.preElement).backgroundColor;
|
|
500
|
-
this.textareaElement.style.height = getComputedStyle(this.preElement).height;
|
|
501
|
-
this.textareaElement.style.width = getComputedStyle(this.preElement).width;
|
|
502
|
-
} else {
|
|
503
|
-
this.style.backgroundColor = getComputedStyle(this.codeElement).backgroundColor;
|
|
504
|
-
this.textareaElement.style.height = getComputedStyle(this.codeElement).height;
|
|
505
|
-
this.textareaElement.style.width = getComputedStyle(this.codeElement).width;
|
|
506
|
-
}
|
|
507
|
-
|
|
508
510
|
// Synchronise the contents of the pre/code and textarea elements
|
|
509
511
|
if(this.needsHighlight) {
|
|
510
512
|
this.update();
|
|
@@ -520,11 +522,7 @@ var codeInput = {
|
|
|
520
522
|
update() {
|
|
521
523
|
let resultElement = this.codeElement;
|
|
522
524
|
let value = this.value;
|
|
523
|
-
|
|
524
|
-
// Handle final newlines
|
|
525
|
-
if (value[value.length - 1] == "\n" || value.length == 0) {
|
|
526
|
-
value += " ";
|
|
527
|
-
}
|
|
525
|
+
value += "\n"; // Placeholder for next line
|
|
528
526
|
|
|
529
527
|
// Update code
|
|
530
528
|
resultElement.innerHTML = this.escapeHtml(value);
|
|
@@ -534,9 +532,49 @@ var codeInput = {
|
|
|
534
532
|
if (this.template.includeCodeInputInHighlightFunc) this.template.highlight(resultElement, this);
|
|
535
533
|
else this.template.highlight(resultElement);
|
|
536
534
|
|
|
535
|
+
this.syncSize();
|
|
536
|
+
|
|
537
|
+
// If editing here, scroll to the caret by focusing, though this shouldn't count as a focus event
|
|
538
|
+
if(this.textareaElement === document.activeElement) {
|
|
539
|
+
this.handleEventsFromTextarea = false;
|
|
540
|
+
this.textareaElement.blur();
|
|
541
|
+
this.textareaElement.focus();
|
|
542
|
+
this.handleEventsFromTextarea = true;
|
|
543
|
+
}
|
|
544
|
+
|
|
537
545
|
this.pluginEvt("afterHighlight");
|
|
538
546
|
}
|
|
539
547
|
|
|
548
|
+
/**
|
|
549
|
+
* Set the size of the textarea element to the size of the pre/code element.
|
|
550
|
+
*/
|
|
551
|
+
syncSize() {
|
|
552
|
+
// Synchronise the size of the pre/code and textarea elements
|
|
553
|
+
if(this.template.preElementStyled) {
|
|
554
|
+
this.style.backgroundColor = getComputedStyle(this.preElement).backgroundColor;
|
|
555
|
+
this.textareaElement.style.height = getComputedStyle(this.preElement).height;
|
|
556
|
+
this.textareaElement.style.width = getComputedStyle(this.preElement).width;
|
|
557
|
+
} else {
|
|
558
|
+
this.style.backgroundColor = getComputedStyle(this.codeElement).backgroundColor;
|
|
559
|
+
this.textareaElement.style.height = getComputedStyle(this.codeElement).height;
|
|
560
|
+
this.textareaElement.style.width = getComputedStyle(this.codeElement).width;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* 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.
|
|
566
|
+
* @param {string} instructions The instructions to display only if keyboard navigation is being used. If it's blank, no instructions will be shown.
|
|
567
|
+
* @param {boolean} includeAriaDescriptionFirst Whether to include the aria-description of the code-input element before the keyboard navigation instructions for a screenreader. Keep this as true when the textarea is first focused.
|
|
568
|
+
*/
|
|
569
|
+
setKeyboardNavInstructions(instructions, includeAriaDescriptionFirst) {
|
|
570
|
+
this.dialogContainerElement.querySelector(".code-input_keyboard-navigation-instructions").innerText = instructions;
|
|
571
|
+
if(includeAriaDescriptionFirst) {
|
|
572
|
+
this.textareaElement.setAttribute("aria-description", this.originalAriaDescription + ". " + instructions);
|
|
573
|
+
} else {
|
|
574
|
+
this.textareaElement.setAttribute("aria-description", instructions);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
540
578
|
/**
|
|
541
579
|
* HTML-escape an arbitrary string.
|
|
542
580
|
* @param {string} text - The original, unescaped text
|
|
@@ -593,7 +631,7 @@ var codeInput = {
|
|
|
593
631
|
|
|
594
632
|
// First-time attribute sync
|
|
595
633
|
let lang = this.getAttribute("language") || this.getAttribute("lang");
|
|
596
|
-
let placeholder = this.getAttribute("placeholder") || this.getAttribute("lang") || "";
|
|
634
|
+
let placeholder = this.getAttribute("placeholder") || this.getAttribute("language") || this.getAttribute("lang") || "";
|
|
597
635
|
let value = this.unescapeHtml(this.innerHTML) || this.getAttribute("value") || "";
|
|
598
636
|
// Value attribute deprecated, but included for compatibility
|
|
599
637
|
|
|
@@ -607,6 +645,22 @@ var codeInput = {
|
|
|
607
645
|
}
|
|
608
646
|
textarea.innerHTML = this.innerHTML;
|
|
609
647
|
textarea.setAttribute("spellcheck", "false");
|
|
648
|
+
|
|
649
|
+
// Disable focusing on the code-input element - only allow the textarea to be focusable
|
|
650
|
+
textarea.setAttribute("tabindex", this.getAttribute("tabindex") || 0);
|
|
651
|
+
this.setAttribute("tabindex", -1);
|
|
652
|
+
// Save aria-description so keyboard navigation guidance can be added.
|
|
653
|
+
this.originalAriaDescription = this.getAttribute("aria-description") || "Code input field";
|
|
654
|
+
|
|
655
|
+
// Accessibility - detect when mouse focus to remove focus outline + keyboard navigation guidance that could irritate users.
|
|
656
|
+
this.addEventListener("mousedown", () => {
|
|
657
|
+
this.classList.add("code-input_mouse-focused");
|
|
658
|
+
});
|
|
659
|
+
textarea.addEventListener("blur", () => {
|
|
660
|
+
if(this.handleEventsFromTextarea) {
|
|
661
|
+
this.classList.remove("code-input_mouse-focused");
|
|
662
|
+
}
|
|
663
|
+
});
|
|
610
664
|
|
|
611
665
|
this.innerHTML = ""; // Clear Content
|
|
612
666
|
|
|
@@ -628,6 +682,8 @@ var codeInput = {
|
|
|
628
682
|
let code = document.createElement("code");
|
|
629
683
|
let pre = document.createElement("pre");
|
|
630
684
|
pre.setAttribute("aria-hidden", "true"); // Hide for screen readers
|
|
685
|
+
pre.setAttribute("tabindex", "-1"); // Hide for keyboard navigation
|
|
686
|
+
pre.setAttribute("inert", true); // Hide for keyboard navigation
|
|
631
687
|
|
|
632
688
|
// Save elements internally
|
|
633
689
|
this.preElement = pre;
|
|
@@ -647,12 +703,22 @@ var codeInput = {
|
|
|
647
703
|
this.append(dialogContainerElement);
|
|
648
704
|
this.dialogContainerElement = dialogContainerElement;
|
|
649
705
|
|
|
706
|
+
let keyboardNavigationInstructions = document.createElement("div");
|
|
707
|
+
keyboardNavigationInstructions.classList.add("code-input_keyboard-navigation-instructions");
|
|
708
|
+
dialogContainerElement.append(keyboardNavigationInstructions);
|
|
709
|
+
|
|
650
710
|
this.pluginEvt("afterElementsAdded");
|
|
651
711
|
|
|
652
712
|
this.dispatchEvent(new CustomEvent("code-input_load"));
|
|
653
713
|
|
|
654
714
|
this.value = value;
|
|
655
715
|
this.animateFrame();
|
|
716
|
+
|
|
717
|
+
const resizeObserver = new ResizeObserver((elements) => {
|
|
718
|
+
// The only element that could be resized is this code-input element.
|
|
719
|
+
this.syncSize();
|
|
720
|
+
});
|
|
721
|
+
resizeObserver.observe(this);
|
|
656
722
|
}
|
|
657
723
|
|
|
658
724
|
/**
|
|
@@ -737,7 +803,7 @@ var codeInput = {
|
|
|
737
803
|
if (this.template.preElementStyled) this.classList.add("code-input_pre-element-styled");
|
|
738
804
|
else this.classList.remove("code-input_pre-element-styled");
|
|
739
805
|
// Syntax Highlight
|
|
740
|
-
this.
|
|
806
|
+
this.scheduleHighlight();
|
|
741
807
|
|
|
742
808
|
break;
|
|
743
809
|
|
|
@@ -753,13 +819,15 @@ var codeInput = {
|
|
|
753
819
|
if (code.classList.contains(`language-${newValue}`)) break; // Already updated
|
|
754
820
|
}
|
|
755
821
|
|
|
822
|
+
if(oldValue !== null) {
|
|
823
|
+
// Case insensitive
|
|
824
|
+
oldValue = oldValue.toLowerCase();
|
|
756
825
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
code.parentElement.classList.remove("language-" + oldValue); // From preElement
|
|
826
|
+
// Remove old language class and add new
|
|
827
|
+
code.classList.remove("language-" + oldValue); // From codeElement
|
|
828
|
+
code.parentElement.classList.remove("language-" + oldValue); // From preElement
|
|
829
|
+
}
|
|
830
|
+
// Add new language class
|
|
763
831
|
code.classList.remove("language-none"); // Prism
|
|
764
832
|
code.parentElement.classList.remove("language-none"); // Prism
|
|
765
833
|
|
|
@@ -769,7 +837,7 @@ var codeInput = {
|
|
|
769
837
|
|
|
770
838
|
if (mainTextarea.placeholder == oldValue) mainTextarea.placeholder = newValue;
|
|
771
839
|
|
|
772
|
-
this.
|
|
840
|
+
this.scheduleHighlight();
|
|
773
841
|
|
|
774
842
|
break;
|
|
775
843
|
default:
|
|
@@ -806,23 +874,33 @@ var codeInput = {
|
|
|
806
874
|
* @override
|
|
807
875
|
*/
|
|
808
876
|
addEventListener(type, listener, options = undefined) {
|
|
809
|
-
// Save a copy of the callback where `this` refers to the code-input element
|
|
810
|
-
let boundCallback =
|
|
877
|
+
// Save a copy of the callback where `this` refers to the code-input element.
|
|
878
|
+
let boundCallback = function (evt) {
|
|
879
|
+
if (typeof listener === 'function') {
|
|
880
|
+
listener(evt);
|
|
881
|
+
} else if (listener && listener.handleEvent) {
|
|
882
|
+
listener.handleEvent(evt);
|
|
883
|
+
}
|
|
884
|
+
}.bind(this);
|
|
811
885
|
this.boundEventCallbacks[listener] = boundCallback;
|
|
812
886
|
|
|
813
887
|
if (codeInput.textareaSyncEvents.includes(type)) {
|
|
814
|
-
// Synchronise with textarea
|
|
888
|
+
// Synchronise with textarea, only when handleEventsFromTextarea is true
|
|
889
|
+
// This callback is modified to only run when the handleEventsFromTextarea is set.
|
|
890
|
+
let conditionalBoundCallback = function(evt) { if(this.handleEventsFromTextarea) boundCallback(evt); }.bind(this);
|
|
891
|
+
this.boundEventCallbacks[listener] = conditionalBoundCallback;
|
|
892
|
+
|
|
815
893
|
if (options === undefined) {
|
|
816
894
|
if(this.textareaElement == null) {
|
|
817
895
|
this.addEventListener("code-input_load", () => { this.textareaElement.addEventListener(type, boundCallback); });
|
|
818
896
|
} else {
|
|
819
|
-
this.textareaElement.addEventListener(type,
|
|
897
|
+
this.textareaElement.addEventListener(type, conditionalBoundCallback);
|
|
820
898
|
}
|
|
821
899
|
} else {
|
|
822
900
|
if(this.textareaElement == null) {
|
|
823
901
|
this.addEventListener("code-input_load", () => { this.textareaElement.addEventListener(type, boundCallback, options); });
|
|
824
902
|
} else {
|
|
825
|
-
this.textareaElement.addEventListener(type,
|
|
903
|
+
this.textareaElement.addEventListener(type, conditionalBoundCallback, options);
|
|
826
904
|
}
|
|
827
905
|
}
|
|
828
906
|
} else {
|
|
@@ -885,7 +963,7 @@ var codeInput = {
|
|
|
885
963
|
// Save in editable textarea element
|
|
886
964
|
this.textareaElement.value = val;
|
|
887
965
|
// Trigger highlight
|
|
888
|
-
this.
|
|
966
|
+
this.scheduleHighlight();
|
|
889
967
|
return val;
|
|
890
968
|
}
|
|
891
969
|
|
|
@@ -972,16 +1050,12 @@ var codeInput = {
|
|
|
972
1050
|
* has loaded (or now if it has already loaded)
|
|
973
1051
|
*/
|
|
974
1052
|
runOnceWindowLoaded(callback, codeInputElem) {
|
|
975
|
-
if(
|
|
1053
|
+
if(document.readyState == "complete") {
|
|
976
1054
|
callback(); // Fully loaded
|
|
977
1055
|
} else {
|
|
978
1056
|
window.addEventListener("load", callback);
|
|
979
1057
|
}
|
|
980
|
-
}
|
|
981
|
-
windowLoaded: false
|
|
1058
|
+
}
|
|
982
1059
|
}
|
|
983
|
-
window.addEventListener("load", function() {
|
|
984
|
-
codeInput.windowLoaded = true;
|
|
985
|
-
});
|
|
986
1060
|
|
|
987
|
-
customElements.define("code-input", codeInput.CodeInput);
|
|
1061
|
+
customElements.define("code-input", codeInput.CodeInput);
|
package/code-input.min.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
code-input{display:block;overflow-y:auto;overflow-x:auto;position:relative;top:0;left:0;margin:8px;--padding:16px;height:250px;font-size:inherit;font-family:monospace;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:not(.code-input_loaded){
|
|
1
|
+
code-input{display:block;overflow-y:auto;overflow-x:auto;position:relative;top:0;left:0;margin:8px;--padding:16px;height:250px;font-size:inherit;font-family:monospace;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:not(.code-input_loaded){padding:var(--padding,16px)!important}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:var(--padding,16px)!important;border:0;min-width:calc(100% - var(--padding) * 2);min-height:calc(100% - var(--padding) * 2);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;width:100%;height:100%}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{z-index:1}code-input pre{z-index:0}code-input textarea{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 #000}code-input:not(.code-input_registered){overflow:hidden;display:block;box-sizing:border-box}code-input:not(.code-input_registered)::after{content:"Use codeInput.registerTemplate to set up.";display:block;position:absolute;bottom:var(--padding);left:var(--padding);width:calc(100% - 2 * var(--padding));border-top:1px solid grey;outline:var(--padding) solid #fff;background-color:#fff}code-input:not(.code-input_loaded) pre,code-input:not(.code-input_loaded) textarea{opacity:0}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;width:100%;height:0;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;background-color:#000;color:#fff;padding:2px;padding-left:10px;margin:0;text-wrap:balance;overflow:hidden;text-overflow:ellipsis;width:calc(100% - 12px);max-height:3em}code-input:has(pre[dir=rtl]) .code-input_dialog-container .code-input_keyboard-navigation-instructions{left:unset;right:0}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:focus)) .code-input_dialog-container .code-input_keyboard-navigation-instructions{display:none}code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused) textarea,code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused).code-input_pre-element-styled pre,code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:focus):not(.code-input_mouse-focused):not(.code-input_pre-element-styled) pre code{padding-top:calc(var(--padding) + 3em)!important}
|
package/code-input.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
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"],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])elem=codeInput.templateNotYetRegisteredQueue[a][c],elem.template=b,codeInput.runOnceWindowLoaded(function(a){a.connectedCallback()}.bind(null,elem),elem);console.log(`code-input: template: Added existing elements with template ${a}`)}if(null==codeInput.defaultTemplate){if(codeInput.defaultTemplate=a,void 0 in codeInput.templateNotYetRegisteredQueue)for(let a in codeInput.templateNotYetRegisteredQueue[void 0])elem=codeInput.templateNotYetRegisteredQueue[void 0][a],elem.template=b,codeInput.runOnceWindowLoaded(function(a){a.connectedCallback()}.bind(null,elem),elem);console.log(`code-input: template: Set template ${a} as default`)}console.log(`code-input: template: Created template ${a}`)},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.Template(a.highlightElement,!0,!0,!1,b)},hljs(a,b=[]){return new codeInput.Template(function(b){b.removeAttribute("data-highlighted"),a.highlightElement(b)},!1,!0,!1,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}<mark class="overflow">${f}</mark>`,0<f.length&&(a.innerHTML+=` <mark class="overflow-msg">${b.getAttribute("data-overflow-msg")||"(Character limit reached)"}</mark>`)},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<d.length;e++)c.push(`<span style="color: ${b.template.rainbowColors[e%b.template.rainbowColors.length]}">${b.escapeHtml(d[e])}</span>`);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 <head> of your HTML, before the plugin is instatiated.`);return a[b]}}),Plugin:class{constructor(a){console.log("code-input: plugin: Created plugin"),a.forEach(a=>{codeInput.observedAttributes.push(a)})}beforeHighlight(){}afterHighlight(){}beforeElementsAdded(){}afterElementsAdded(){}attributeChanged(){}},CodeInput:class extends HTMLElement{constructor(){super()}textareaElement=null;preElement=null;codeElement=null;dialogContainerElement=null;static formAssociated=!0;boundEventCallbacks={};pluginEvt(a,b){for(let c in this.template.plugins){let d=this.template.plugins[c];a in d&&(b===void 0?d[a](this):d[a](this,...b))}}needsHighlight=!1;scheduleHighlight(){this.needsHighlight=!0}animateFrame(){this.template.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),this.needsHighlight&&(this.update(),this.needsHighlight=!1),window.requestAnimationFrame(this.animateFrame.bind(this))}update(){let a=this.codeElement,b=this.value;("\n"==b[b.length-1]||0==b.length)&&(b+=" "),a.innerHTML=this.escapeHtml(b),this.pluginEvt("beforeHighlight"),this.template.includeCodeInputInHighlightFunc?this.template.highlight(a,this):this.template.highlight(a),this.pluginEvt("afterHighlight")}escapeHtml(a){return a.replace(/&/g,"&").replace(/</g,"<")}unescapeHtml(a){return a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}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.template.preElementStyled&&this.classList.add("code-input_pre-element-styled"),this.pluginEvt("beforeElementsAdded");let a=this.getAttribute("language")||this.getAttribute("lang"),b=this.getAttribute("placeholder")||this.getAttribute("lang")||"",c=this.unescapeHtml(this.innerHTML)||this.getAttribute("value")||"";this.initialValue=c;let d=document.createElement("textarea");d.placeholder=b,""!=c&&(d.value=c),d.innerHTML=this.innerHTML,d.setAttribute("spellcheck","false"),this.innerHTML="";for(let a,b=0;b<this.attributes.length;b++)a=this.attributes[b].name,(codeInput.textareaSyncAttributes.includes(a)||"aria-"==a.substring(0,5))&&d.setAttribute(a,this.getAttribute(a));d.addEventListener("input",()=>{this.value=this.textareaElement.value}),this.textareaElement=d,this.append(d);let e=document.createElement("code"),f=document.createElement("pre");f.setAttribute("aria-hidden","true"),this.preElement=f,this.codeElement=e,f.append(e),this.append(f),this.template.isCode&&a!=null&&""!=a&&e.classList.add("language-"+a.toLowerCase());let g=document.createElement("div");g.classList.add("code-input_dialog-container"),this.append(g),this.dialogContainerElement=g,this.pluginEvt("afterElementsAdded"),this.dispatchEvent(new CustomEvent("code-input_load")),this.value=c,this.animateFrame()}escape_html(a){return this.escapeHtml(a)}get_template(){return this.getTemplate()}connectedCallback(){this.template=this.getTemplate(),this.template!=null&&(this.classList.add("code-input_registered"),codeInput.runOnceWindowLoaded(()=>{this.setup(),this.classList.add("code-input_loaded")},this)),this.mutationObserver=new MutationObserver(this.mutationObserverCallback.bind(this)),this.mutationObserver.observe(this,{attributes:!0,attributeOldValue:!0})}mutationObserverCallback(a){for(const b of a)if("attributes"===b.type){for(let a=0;a<codeInput.observedAttributes.length;a++)if(b.attributeName==codeInput.observedAttributes[a])return this.attributeChangedCallback(b.attributeName,b.oldValue,super.getAttribute(b.attributeName));if("aria-"==b.attributeName.substring(0,5))return this.attributeChangedCallback(b.attributeName,b.oldValue,super.getAttribute(b.attributeName))}}disconnectedCallback(){this.mutationObserver.disconnect()}attributeChangedCallback(a,b,c){if(this.isConnected)switch(this.pluginEvt("attributeChanged",[a,b,c]),a){case"value":this.value=c;break;case"template":this.template=codeInput.usedTemplates[c||codeInput.defaultTemplate],this.template.preElementStyled?this.classList.add("code-input_pre-element-styled"):this.classList.remove("code-input_pre-element-styled"),this.needsHighlight=!0;break;case"lang":case"language":let d=this.codeElement,e=this.textareaElement;if(null!=c&&(c=c.toLowerCase(),d.classList.contains(`language-${c}`)))break;b=b.toLowerCase(),d.classList.remove("language-"+b),d.parentElement.classList.remove("language-"+b),d.classList.remove("language-none"),d.parentElement.classList.remove("language-none"),null!=c&&""!=c&&d.classList.add("language-"+c),e.placeholder==b&&(e.placeholder=c),this.needsHighlight=!0;break;default:codeInput.textareaSyncAttributes.includes(a)||"aria-"==a.substring(0,5)?null==c||null==c?this.textareaElement.removeAttribute(a):this.textareaElement.setAttribute(a,c):codeInput.textareaSyncAttributes.regexp.forEach(b=>{a.match(b)&&(null==c?this.textareaElement.removeAttribute(a):this.textareaElement.setAttribute(a,c))})}}addEventListener(a,b,c=void 0){let d=b.bind(this);this.boundEventCallbacks[b]=d,codeInput.textareaSyncEvents.includes(a)?c===void 0?null==this.textareaElement?this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d)}):this.textareaElement.addEventListener(a,d):null==this.textareaElement?this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d,c)}):this.textareaElement.addEventListener(a,d,c):c===void 0?super.addEventListener(a,d):super.addEventListener(a,d,c)}removeEventListener(a,b,c=void 0){let d=this.boundEventCallbacks[b];codeInput.textareaSyncEvents.includes(a)?c===void 0?null==this.textareaElement?this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d)}):this.textareaElement.removeEventListener(a,d):null==this.textareaElement?this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d,c)}):this.textareaElement.removeEventListener(a,d,c):c===void 0?super.removeEventListener(a,d):super.removeEventListener(a,d,c)}get value(){return this.textareaElement.value}set value(a){return(null===a||void 0===a)&&(a=""),this.textareaElement.value=a,this.needsHighlight=!0,a}get placeholder(){return this.getAttribute("placeholder")}set placeholder(a){return this.setAttribute("placeholder",a)}get validity(){return this.textareaElement.validity}get validationMessage(){return this.textareaElement.validationMessage}setCustomValidity(a){return this.textareaElement.setCustomValidity(a)}checkValidity(){return this.textareaElement.checkValidity()}reportValidity(){return this.textareaElement.reportValidity()}pluginData={};formResetCallback(){this.value=this.initialValue}},runOnceWindowLoaded(a){codeInput.windowLoaded?a():window.addEventListener("load",a)},windowLoaded:!1};window.addEventListener("load",function(){codeInput.windowLoaded=!0}),customElements.define("code-input",codeInput.CodeInput);
|
|
1
|
+
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"],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])elem=codeInput.templateNotYetRegisteredQueue[a][c],elem.template=b,codeInput.runOnceWindowLoaded(function(a){a.connectedCallback()}.bind(null,elem),elem);console.log(`code-input: template: Added existing elements with template ${a}`)}if(null==codeInput.defaultTemplate){if(codeInput.defaultTemplate=a,void 0 in codeInput.templateNotYetRegisteredQueue)for(let a in codeInput.templateNotYetRegisteredQueue[void 0])elem=codeInput.templateNotYetRegisteredQueue[void 0][a],elem.template=b,codeInput.runOnceWindowLoaded(function(a){a.connectedCallback()}.bind(null,elem),elem);console.log(`code-input: template: Set template ${a} as default`)}console.log(`code-input: template: Created template ${a}`)},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.Template(a.highlightElement,!0,!0,!1,b)},hljs(a,b=[]){return new codeInput.Template(function(b){b.removeAttribute("data-highlighted"),a.highlightElement(b)},!1,!0,!1,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}<mark class="overflow">${f}</mark>`,0<f.length&&(a.innerHTML+=` <mark class="overflow-msg">${b.getAttribute("data-overflow-msg")||"(Character limit reached)"}</mark>`)},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<d.length;e++)c.push(`<span style="color: ${b.template.rainbowColors[e%b.template.rainbowColors.length]}">${b.escapeHtml(d[e])}</span>`);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 <head> of your HTML, before the plugin is instatiated.`);return a[b]}}),Plugin:class{constructor(a){console.log("code-input: plugin: Created plugin"),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()}textareaElement=null;preElement=null;codeElement=null;dialogContainerElement=null;static formAssociated=!0;boundEventCallbacks={};pluginEvt(a,b){for(let c in this.template.plugins){let d=this.template.plugins[c];a in d&&(b===void 0?d[a](this):d[a](this,...b))}}needsHighlight=!1;handleEventsFromTextarea=!0;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.template.includeCodeInputInHighlightFunc?this.template.highlight(a,this):this.template.highlight(a),this.syncSize(),this.textareaElement===document.activeElement&&(this.handleEventsFromTextarea=!1,this.textareaElement.blur(),this.textareaElement.focus(),this.handleEventsFromTextarea=!0),this.pluginEvt("afterHighlight")}syncSize(){this.template.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(/</g,"<")}unescapeHtml(a){return a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}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.template.preElementStyled&&this.classList.add("code-input_pre-element-styled"),this.pluginEvt("beforeElementsAdded");let a=this.getAttribute("language")||this.getAttribute("lang"),b=this.getAttribute("placeholder")||this.getAttribute("language")||this.getAttribute("lang")||"",c=this.unescapeHtml(this.innerHTML)||this.getAttribute("value")||"";this.initialValue=c;let d=document.createElement("textarea");d.placeholder=b,""!=c&&(d.value=c),d.innerHTML=this.innerHTML,d.setAttribute("spellcheck","false"),d.setAttribute("tabindex",this.getAttribute("tabindex")||0),this.setAttribute("tabindex",-1),this.originalAriaDescription=this.getAttribute("aria-description")||"Code input field",this.addEventListener("mousedown",()=>{this.classList.add("code-input_mouse-focused")}),d.addEventListener("blur",()=>{this.handleEventsFromTextarea&&this.classList.remove("code-input_mouse-focused")}),this.innerHTML="";for(let a,b=0;b<this.attributes.length;b++)a=this.attributes[b].name,(codeInput.textareaSyncAttributes.includes(a)||"aria-"==a.substring(0,5))&&d.setAttribute(a,this.getAttribute(a));d.addEventListener("input",()=>{this.value=this.textareaElement.value}),this.textareaElement=d,this.append(d);let e=document.createElement("code"),f=document.createElement("pre");f.setAttribute("aria-hidden","true"),f.setAttribute("tabindex","-1"),f.setAttribute("inert",!0),this.preElement=f,this.codeElement=e,f.append(e),this.append(f),this.template.isCode&&a!=null&&""!=a&&e.classList.add("language-"+a.toLowerCase());let g=document.createElement("div");g.classList.add("code-input_dialog-container"),this.append(g),this.dialogContainerElement=g;let h=document.createElement("div");h.classList.add("code-input_keyboard-navigation-instructions"),g.append(h),this.pluginEvt("afterElementsAdded"),this.dispatchEvent(new CustomEvent("code-input_load")),this.value=c,this.animateFrame();const i=new ResizeObserver(()=>{this.syncSize()});i.observe(this)}escape_html(a){return this.escapeHtml(a)}get_template(){return this.getTemplate()}connectedCallback(){this.template=this.getTemplate(),this.template!=null&&(this.classList.add("code-input_registered"),codeInput.runOnceWindowLoaded(()=>{this.setup(),this.classList.add("code-input_loaded")},this)),this.mutationObserver=new MutationObserver(this.mutationObserverCallback.bind(this)),this.mutationObserver.observe(this,{attributes:!0,attributeOldValue:!0})}mutationObserverCallback(a){for(const b of a)if("attributes"===b.type){for(let a=0;a<codeInput.observedAttributes.length;a++)if(b.attributeName==codeInput.observedAttributes[a])return this.attributeChangedCallback(b.attributeName,b.oldValue,super.getAttribute(b.attributeName));if("aria-"==b.attributeName.substring(0,5))return this.attributeChangedCallback(b.attributeName,b.oldValue,super.getAttribute(b.attributeName))}}disconnectedCallback(){this.mutationObserver.disconnect()}attributeChangedCallback(a,b,c){if(this.isConnected)switch(this.pluginEvt("attributeChanged",[a,b,c]),a){case"value":this.value=c;break;case"template":this.template=codeInput.usedTemplates[c||codeInput.defaultTemplate],this.template.preElementStyled?this.classList.add("code-input_pre-element-styled"):this.classList.remove("code-input_pre-element-styled"),this.scheduleHighlight();break;case"lang":case"language":let d=this.codeElement,e=this.textareaElement;if(null!=c&&(c=c.toLowerCase(),d.classList.contains(`language-${c}`)))break;null!==b&&(b=b.toLowerCase(),d.classList.remove("language-"+b),d.parentElement.classList.remove("language-"+b)),d.classList.remove("language-none"),d.parentElement.classList.remove("language-none"),null!=c&&""!=c&&d.classList.add("language-"+c),e.placeholder==b&&(e.placeholder=c),this.scheduleHighlight();break;default:codeInput.textareaSyncAttributes.includes(a)||"aria-"==a.substring(0,5)?null==c||null==c?this.textareaElement.removeAttribute(a):this.textareaElement.setAttribute(a,c):codeInput.textareaSyncAttributes.regexp.forEach(b=>{a.match(b)&&(null==c?this.textareaElement.removeAttribute(a):this.textareaElement.setAttribute(a,c))})}}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)){let e=function(a){this.handleEventsFromTextarea&&d(a)}.bind(this);this.boundEventCallbacks[b]=e,void 0===c?null==this.textareaElement?this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d)}):this.textareaElement.addEventListener(a,e):null==this.textareaElement?this.addEventListener("code-input_load",()=>{this.textareaElement.addEventListener(a,d,c)}):this.textareaElement.addEventListener(a,e,c)}else void 0===c?super.addEventListener(a,d):super.addEventListener(a,d,c)}removeEventListener(a,b,c=void 0){let d=this.boundEventCallbacks[b];codeInput.textareaSyncEvents.includes(a)?c===void 0?null==this.textareaElement?this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d)}):this.textareaElement.removeEventListener(a,d):null==this.textareaElement?this.addEventListener("code-input_load",()=>{this.textareaElement.removeEventListener(a,d,c)}):this.textareaElement.removeEventListener(a,d,c):c===void 0?super.removeEventListener(a,d):super.removeEventListener(a,d,c)}get value(){return this.textareaElement.value}set value(a){return(null===a||void 0===a)&&(a=""),this.textareaElement.value=a,this.scheduleHighlight(),a}get placeholder(){return this.getAttribute("placeholder")}set placeholder(a){return this.setAttribute("placeholder",a)}get validity(){return this.textareaElement.validity}get validationMessage(){return this.textareaElement.validationMessage}setCustomValidity(a){return this.textareaElement.setCustomValidity(a)}checkValidity(){return this.textareaElement.checkValidity()}reportValidity(){return this.textareaElement.reportValidity()}pluginData={};formResetCallback(){this.value=this.initialValue}},runOnceWindowLoaded(a){"complete"==document.readyState?a():window.addEventListener("load",a)}};customElements.define("code-input",codeInput.CodeInput);
|
package/package.json
CHANGED
package/plugins/README.md
CHANGED
|
@@ -65,6 +65,13 @@ Files: [special-chars.js](./special-chars.js) / [special-chars.css](./special-ch
|
|
|
65
65
|
|
|
66
66
|
[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/jOeYJbm)
|
|
67
67
|
|
|
68
|
+
### Select Token Callbacks
|
|
69
|
+
Make tokens in the `<pre><code>` element that are included within the selected text of the `<code-input>` gain a CSS class while selected, or trigger JavaScript callbacks.
|
|
70
|
+
|
|
71
|
+
Files: select-token-callbacks.js
|
|
72
|
+
|
|
73
|
+
[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/WNVZXxM)
|
|
74
|
+
|
|
68
75
|
## Using Plugins
|
|
69
76
|
Plugins allow you to add extra features to a template, like [automatic indentation](./indent.js) or [support for highlight.js's language autodetection](./autodetect.js). To use them, just:
|
|
70
77
|
- Import the plugins' JS/CSS files (there may only be one of these; import all of the files that exist) after you have imported `code-input` and before registering the template.
|
package/plugins/autocomplete.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
codeInput.plugins.Autocomplete = class extends codeInput.Plugin {
|
|
6
6
|
/**
|
|
7
7
|
* Pass in a function to create a plugin that displays the popup that takes in (popup element, textarea, textarea.selectionEnd).
|
|
8
|
-
* @param {
|
|
8
|
+
* @param {(popupElement: HTMLElement, textarea: HTMLTextAreaElement, selectionEnd: number) => void} updatePopupCallback a function to display the popup that takes in (popup element, textarea, textarea.selectionEnd).
|
|
9
9
|
*/
|
|
10
10
|
constructor(updatePopupCallback) {
|
|
11
11
|
super([]); // No observed attributes
|
|
@@ -30,7 +30,9 @@ codeInput.plugins.Autocomplete = class extends codeInput.Plugin {
|
|
|
30
30
|
codeInput.appendChild(popupElem);
|
|
31
31
|
|
|
32
32
|
let testPosPre = document.createElement("pre");
|
|
33
|
-
|
|
33
|
+
popupElem.setAttribute("inert", true); // Invisible to keyboard navigation
|
|
34
|
+
popupElem.setAttribute("tabindex", -1); // Invisible to keyboard navigation
|
|
35
|
+
testPosPre.setAttribute("aria-hidden", true); // Hide for screen readers
|
|
34
36
|
if(codeInput.template.preElementStyled) {
|
|
35
37
|
testPosPre.classList.add("code-input_autocomplete_testpos");
|
|
36
38
|
codeInput.appendChild(testPosPre); // Styled like first pre, but first pre found to update
|
|
@@ -1 +1 @@
|
|
|
1
|
-
codeInput.plugins.Autocomplete=class extends codeInput.Plugin{constructor(a){super([]),this.updatePopupCallback=a}updatePopup(a,b){let c=a.textareaElement,d=this.getCaretCoordinates(a,c,c.selectionEnd,b),e=a.querySelector(".code-input_autocomplete_popup");e.style.top=d.top+"px",e.style.left=d.left+"px",b||this.updatePopupCallback(e,c,c.selectionEnd)}afterElementsAdded(a){let b=document.createElement("div");b.classList.add("code-input_autocomplete_popup"),a.appendChild(b);let c=document.createElement("pre");if(c.setAttribute("aria-hidden"
|
|
1
|
+
codeInput.plugins.Autocomplete=class extends codeInput.Plugin{constructor(a){super([]),this.updatePopupCallback=a}updatePopup(a,b){let c=a.textareaElement,d=this.getCaretCoordinates(a,c,c.selectionEnd,b),e=a.querySelector(".code-input_autocomplete_popup");e.style.top=d.top+"px",e.style.left=d.left+"px",b||this.updatePopupCallback(e,c,c.selectionEnd)}afterElementsAdded(a){let b=document.createElement("div");b.classList.add("code-input_autocomplete_popup"),a.appendChild(b);let c=document.createElement("pre");if(b.setAttribute("inert",!0),b.setAttribute("tabindex",-1),c.setAttribute("aria-hidden",!0),a.template.preElementStyled)c.classList.add("code-input_autocomplete_testpos"),a.appendChild(c);else{let b=document.createElement("code");b.classList.add("code-input_autocomplete_testpos"),c.appendChild(b),a.appendChild(c)}let d=a.textareaElement;d.addEventListener("input",()=>{this.updatePopup(a,!1)}),d.addEventListener("click",()=>{this.updatePopup(a,!1)})}getCaretCoordinates(a,b,c,d){let e;if(d){let d=a.querySelector(".code-input_autocomplete_testpos").querySelectorAll("span");if(2>d.length)return this.getCaretCoordinates(a,b,c,!1);e=d[1]}else{let d=a.querySelector(".code-input_autocomplete_testpos"),f=document.createElement("span");for(f.textContent=b.value.substring(0,c),e=document.createElement("span"),e.textContent=".";d.firstChild;)d.removeChild(d.firstChild);d.appendChild(f),d.appendChild(e)}return{top:e.offsetTop-b.scrollTop,left:e.offsetLeft-b.scrollLeft}}updatePopupCallback=function(){}};
|