@webcoder49/code-input 2.6.4 → 2.6.6

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/code-input.css CHANGED
@@ -26,6 +26,10 @@ code-input {
26
26
  /* Normal inline styles */
27
27
  margin: 8px;
28
28
  --padding: 16px;
29
+ --padding-left: var(--padding, 16px);
30
+ --padding-right: var(--padding, 16px);
31
+ --padding-top: var(--padding, 16px);
32
+ --padding-bottom: var(--padding, 16px);
29
33
  height: 250px;
30
34
  font-size: inherit;
31
35
  font-family: monospace;
@@ -40,19 +44,20 @@ code-input {
40
44
  grid-template-rows: 100%;
41
45
  }
42
46
 
43
-
44
- code-input:not(.code-input_loaded) {
45
- padding: var(--padding, 16px)!important;
47
+ code-input * {
48
+ 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. */
46
49
  }
47
50
 
48
51
  code-input textarea, code-input:not(.code-input_pre-element-styled) pre code, code-input.code-input_pre-element-styled pre {
49
52
  /* Both elements need the same text and space styling so they are directly on top of each other */
50
53
  margin: 0px!important;
51
- padding: var(--padding, 16px)!important;
54
+ padding-left: var(--padding-left, 16px)!important;
55
+ padding-right: var(--padding-right, 16px)!important;
56
+ padding-top: var(--padding-top, 16px)!important;
57
+ padding-bottom: var(--padding-bottom, 16px)!important;
52
58
  border: 0;
53
- min-width: calc(100% - var(--padding, 16px) * 2);
54
- min-height: calc(100% - var(--padding, 16px) * 2);
55
- 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. */
59
+ min-width: calc(100% - var(--padding-left, 16px) - var(--padding-right, 16px));
60
+ min-height: calc(100% - var(--padding-top, 16px) - var(--padding-bottom, 16px));
56
61
  overflow: hidden;
57
62
  resize: none;
58
63
  grid-row: 1;
@@ -138,42 +143,6 @@ code-input:has(textarea:focus):not(.code-input_mouse-focused) {
138
143
  outline: 2px solid currentColor;
139
144
  }
140
145
 
141
- /* Before registering give a hint about how to register. */
142
- code-input:not(.code-input_registered) {
143
- overflow: hidden;
144
- display: block;
145
- box-sizing: border-box; /* Include padding in width/height */
146
- }
147
-
148
- code-input:not(.code-input_registered)::after {
149
- /* Display message to register */
150
- content: "No highlighting. JavaScript support is disabled or insufficient, or codeInput.registerTemplate has not been called.";
151
- display: block;
152
- position: absolute;
153
- bottom: 0;
154
- left: var(--padding, 16px);
155
- width: calc(100% - 2 * var(--padding, 16px));
156
- overflow-x: auto;
157
-
158
- border-top: 1px solid currentColor;
159
- outline-top: 0;
160
- background-color: inherit;
161
- color: inherit;
162
-
163
- margin: 0;
164
- padding: 0;
165
- height: 2em;
166
- }
167
-
168
- code-input:not(.code-input_registered) textarea {
169
- /* Don't overlap with message */
170
- min-height: calc(100% - var(--padding, 16px) * 2 - 2em);
171
- }
172
-
173
- code-input:not(.code-input_loaded) pre, code-input:not(.code-input_loaded) textarea:not([data-code-input-fallback]) {
174
- opacity: 0;
175
- }
176
-
177
146
  /* Contains dialog boxes that might appear as the result of a plugin.
178
147
  Sticks to the top of the code-input element */
179
148
 
@@ -195,9 +164,6 @@ code-input .code-input_dialog-container {
195
164
  /* Dialog boxes' text is based on text-direction */
196
165
  text-align: inherit;
197
166
  }
198
- code-input.code-input_pre-element-styled .code-input_dialog-container {
199
- width: calc(100% + 2 * var(--padding, 16px));
200
- }
201
167
 
202
168
  [dir=rtl] code-input .code-input_dialog-container, code-input[dir=rtl] .code-input_dialog-container {
203
169
  left: unset;
@@ -217,14 +183,16 @@ code-input .code-input_dialog-container .code-input_keyboard-navigation-instruct
217
183
  background-color: black;
218
184
  color: white;
219
185
  padding: 2px;
186
+ padding-left: var(--padding-left, 16px);
187
+ padding-right: var(--padding-right, 16px);
220
188
  margin: 0;
221
189
  text-wrap: balance;
222
- overflow: hidden;
223
- text-overflow: ellipsis;
190
+ overflow-x: hidden;
191
+ overflow-y: auto;
224
192
 
225
193
  width: 100%;
226
194
  box-sizing: border-box;
227
- max-height: 3em;
195
+ height: 3em;
228
196
  }
229
197
 
230
198
  code-input:not(:has(textarea:not([data-code-input-fallback]):focus)) .code-input_dialog-container .code-input_keyboard-navigation-instructions,
@@ -238,17 +206,58 @@ code-input .code-input_dialog-container .code-input_keyboard-navigation-instruct
238
206
  code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused) textarea,
239
207
  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,
240
208
  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 {
241
- padding-top: calc(var(--padding, 16px) + 3em)!important;
209
+ padding-top: calc(var(--padding-top, 16px) + 3em)!important;
210
+ min-height: calc(100% - var(--padding-top, 16px) - 3em - var(--padding-bottom, 16px));
211
+ }
212
+
213
+ /* Pre-loaded appearance */
214
+
215
+ code-input:not(.code-input_loaded) {
216
+ padding-left: var(--padding-left, 16px)!important;
217
+ padding-right: var(--padding-right, 16px)!important;
218
+ padding: var(--padding-top, 16px)!important;
219
+ padding: var(--padding-bottom, 16px)!important;
220
+ overflow: hidden;
221
+ display: block;
222
+ box-sizing: border-box; /* Include padding in width/height */
223
+ }
224
+
225
+ code-input:not(.code-input_loaded)::after {
226
+ /* Display message to register */
227
+ content: "No highlighting. JavaScript support is disabled or insufficient, or codeInput.registerTemplate has not been called.";
228
+ display: block;
229
+ position: absolute;
230
+ bottom: 0;
231
+ left: var(--padding-left, 16px);
232
+ width: calc(100% - var(--padding-left, 1.6px) - var(--padding-right, 1.6px));
233
+ overflow-x: auto;
234
+
235
+ border-top: 1px solid currentColor;
236
+ outline-top: 0;
237
+ background-color: inherit;
238
+ color: inherit;
239
+
240
+ margin: 0;
241
+ padding: 0;
242
+ height: 2em;
242
243
  }
243
- 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 {
244
- min-height: calc(100% - var(--padding, 16px) * 2 - 3em);
244
+
245
+
246
+ code-input:not(.code-input_loaded) pre, code-input:not(.code-input_loaded) textarea:not([data-code-input-fallback]) {
247
+ opacity: 0;
245
248
  }
246
249
 
247
250
  /* No JavaScript fallback - styles to override all previous */
248
251
 
252
+ code-input:has(textarea[data-code-input-fallback]) {
253
+ padding: 0!important; /* Padding now in the textarea */
254
+ box-sizing: content-box;
255
+ }
249
256
  code-input textarea[data-code-input-fallback] {
250
257
  overflow: auto;
251
258
  background-color: inherit;
252
259
  color: inherit;
253
- height: max-content;
260
+
261
+ /* Don't overlap with message */
262
+ min-height: calc(100% - var(--padding-top, 16px) - 2em - var(--padding-bottom, 16px));
254
263
  }
package/code-input.js CHANGED
@@ -620,9 +620,26 @@ var codeInput = {
620
620
  this.classList.add("code-input_registered"); // Remove register message
621
621
  if (this.templateObject.preElementStyled) this.classList.add("code-input_pre-element-styled");
622
622
 
623
- this.pluginEvt("beforeElementsAdded");
624
-
625
623
  const fallbackTextarea = this.querySelector("textarea[data-code-input-fallback]");
624
+ let fallbackFocused = false;
625
+ let fallbackSelectionStart = undefined;
626
+ let fallbackSelectionEnd = undefined;
627
+ let fallbackSelectionDirection = undefined;
628
+ let fallbackScrollLeft = undefined;
629
+ let fallbackScrollTop = undefined;
630
+ if(fallbackTextarea) {
631
+ // Move some attributes to new textarea so typing during
632
+ // slow load not interrupted
633
+ if(fallbackTextarea === document.activeElement) { // Thanks to https://stackoverflow.com/a/36430896
634
+ fallbackFocused = true;
635
+ }
636
+ fallbackSelectionStart = fallbackTextarea.selectionStart;
637
+ fallbackSelectionEnd = fallbackTextarea.selectionEnd;
638
+ fallbackSelectionDirection = fallbackTextarea.selectionDirection;
639
+ fallbackScrollLeft = fallbackTextarea.scrollLeft;
640
+ fallbackScrollTop = fallbackTextarea.scrollTop;
641
+ }
642
+
626
643
  let value;
627
644
  if(fallbackTextarea) {
628
645
  // Fallback textarea exists
@@ -638,11 +655,15 @@ var codeInput = {
638
655
  }
639
656
  // Sync value
640
657
  value = fallbackTextarea.value;
658
+ // Backwards compatibility with plugins
659
+ this.innerHTML = this.escapeHtml(value);
641
660
  } else {
642
661
  value = this.unescapeHtml(this.innerHTML);
643
662
  }
644
663
  value = value || this.getAttribute("value") || "";
645
664
 
665
+ this.pluginEvt("beforeElementsAdded");
666
+
646
667
  // First-time attribute sync
647
668
  const lang = this.getAttribute("language") || this.getAttribute("lang");
648
669
  const placeholder = this.getAttribute("placeholder") || lang || "";
@@ -671,9 +692,23 @@ var codeInput = {
671
692
  // Accessibility - detect when mouse focus to remove focus outline + keyboard navigation guidance that could irritate users.
672
693
  this.addEventListener("mousedown", () => {
673
694
  this.classList.add("code-input_mouse-focused");
695
+ // Wait for CSS to update padding
696
+ window.setTimeout(() => {
697
+ this.syncSize();
698
+ }, 0);
674
699
  });
675
700
  textarea.addEventListener("blur", () => {
676
701
  this.classList.remove("code-input_mouse-focused");
702
+ // Wait for CSS to update padding
703
+ window.setTimeout(() => {
704
+ this.syncSize();
705
+ }, 0);
706
+ });
707
+ textarea.addEventListener("focus", () => {
708
+ // Wait for CSS to update padding
709
+ window.setTimeout(() => {
710
+ this.syncSize();
711
+ }, 0);
677
712
  });
678
713
 
679
714
  this.innerHTML = ""; // Clear Content
@@ -726,13 +761,25 @@ var codeInput = {
726
761
  this.dispatchEvent(new CustomEvent("code-input_load"));
727
762
 
728
763
  this.value = value;
764
+
765
+ // Update with fallback textarea's state so can keep editing
766
+ // if loaded slowly
767
+ if(fallbackSelectionStart !== undefined) {
768
+ console.log("sel", fallbackSelectionStart, fallbackSelectionEnd, fallbackSelectionDirection, "scr", fallbackScrollTop, fallbackScrollLeft, "foc", fallbackFocused);
769
+ textarea.setSelectionRange(fallbackSelectionStart, fallbackSelectionEnd, fallbackSelectionDirection);
770
+ textarea.scrollTo(fallbackScrollTop, fallbackScrollLeft);
771
+ }
772
+ if(fallbackFocused) {
773
+ textarea.focus();
774
+ }
775
+
729
776
  this.animateFrame();
730
777
 
731
778
  const resizeObserver = new ResizeObserver((elements) => {
732
779
  // The only element that could be resized is this code-input element.
733
780
  this.syncSize();
734
781
  });
735
- resizeObserver.observe(this);
782
+ resizeObserver.observe(this.textareaElement);
736
783
 
737
784
  this.classList.add("code-input_loaded");
738
785
  }
@@ -784,8 +831,12 @@ var codeInput = {
784
831
  if (this.templateObject != undefined) {
785
832
  // Template registered before loading
786
833
  this.classList.add("code-input_registered");
787
- // Children not yet present - wait until they are
788
- window.addEventListener("DOMContentLoaded", this.setup.bind(this))
834
+ if (document.readyState === 'loading') {
835
+ // Children not yet present - wait until they are
836
+ window.addEventListener("DOMContentLoaded", this.setup.bind(this))
837
+ } else {
838
+ this.setup();
839
+ }
789
840
  }
790
841
  }
791
842
 
@@ -812,7 +863,9 @@ var codeInput = {
812
863
  }
813
864
 
814
865
  disconnectedCallback() {
815
- this.mutationObserver.disconnect();
866
+ if (this.mutationObserver) {
867
+ this.mutationObserver.disconnect();
868
+ }
816
869
  }
817
870
 
818
871
  /**
@@ -1128,14 +1181,40 @@ var codeInput = {
1128
1181
  get wrap() { return this.getAttribute("wrap") || ""; }
1129
1182
  set wrap(val) { this.setAttribute("wrap", val); }
1130
1183
 
1184
+ /**
1185
+ * Get the JavaScript method from the internal textarea
1186
+ * element, throwing an error when no textarea is present.
1187
+ * The method is bound to the textarea as `this`.
1188
+ *
1189
+ * For internal use - treat the code-input element as a
1190
+ * textarea for the standard functions (e.g. document.
1191
+ * querySelector("code-input").focus()).
1192
+ */
1193
+ getTextareaMethod(name) {
1194
+ if(this.textareaElement) {
1195
+ return this.textareaElement[name].bind(this.textareaElement);
1196
+ } else {
1197
+ // Unregistered
1198
+ const fallbackTextarea = this.querySelector("textarea[data-code-input-fallback]");
1199
+ if(fallbackTextarea) {
1200
+ return fallbackTextarea[name].bind(fallbackTextarea);
1201
+ } else {
1202
+ throw new Error("Cannot call "+name+" on an unregistered code-input element without a data-code-input-fallback textarea.");
1203
+ }
1204
+ }
1205
+ }
1131
1206
 
1132
- blur(options={}) { return this.textareaElement.blur(options); }
1133
- checkValidity() { return this.textareaElement.checkValidity(); }
1134
- focus(options={}) { return this.textareaElement.focus(options); }
1135
- reportValidity() { return this.textareaElement.reportValidity(); }
1136
- setCustomValidity(error) { this.textareaElement.setCustomValidity(error); }
1137
- setRangeText(replacement, selectionStart=this.selectionStart, selectionEnd=this.selectionEnd, selectMode="preserve") { this.getTextareaProperty("setRangeText")(replacement, selectionStart, selectionEnd, selectMode); }
1138
- setSelectionRange(selectionStart, selectionEnd, selectionDirection="none") { this.getTextareaProperty("setSelectionRange")(selectionStart, selectionEnd, selectionDirection); }
1207
+ blur(options={}) { this.getTextareaMethod("blur")(options); }
1208
+ checkValidity() { return this.getTextareaMethod("checkValidity")(); }
1209
+ focus(options={}) { this.getTextareaMethod("focus")(options); }
1210
+ reportValidity() { return this.getTextareaMethod("reportValidity")(); }
1211
+ setCustomValidity(error) { this.getTextareaMethod("setCustomValidity")(error); }
1212
+ setRangeText(replacement, selectionStart=this.selectionStart, selectionEnd=this.selectionEnd, selectMode="preserve") {
1213
+ this.getTextareaMethod("setRangeText")(replacement, selectionStart, selectionEnd, selectMode);
1214
+ // Reflect that value updated
1215
+ if(this.textareaElement) this.scheduleHighlight();
1216
+ }
1217
+ setSelectionRange(selectionStart, selectionEnd, selectionDirection="none") { this.getTextareaMethod("setSelectionRange")(selectionStart, selectionEnd, selectionDirection); }
1139
1218
 
1140
1219
  /**
1141
1220
  * Allows plugins to store data in the scope of a single element.
@@ -1 +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;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: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,16px) * 2);min-height:calc(100% - var(--padding,16px) * 2);box-sizing:content-box;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:not(.code-input_registered){overflow:hidden;display:block;box-sizing:border-box}code-input:not(.code-input_registered)::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,16px);width:calc(100% - 2 * var(--padding,16px));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_registered) textarea{min-height:calc(100% - var(--padding,16px) * 2 - 2em)}code-input:not(.code-input_loaded) pre,code-input:not(.code-input_loaded) textarea:not([data-code-input-fallback]){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;height:0;width:100%;text-align:inherit}code-input.code-input_pre-element-styled .code-input_dialog-container{width:calc(100% + 2 * var(--padding,16px))}[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;margin:0;text-wrap:balance;overflow:hidden;text-overflow:ellipsis;width:100%;box-sizing:border-box;max-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,16px) + 3em)!important}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{min-height:calc(100% - var(--padding,16px) * 2 - 3em)}code-input textarea[data-code-input-fallback]{overflow:auto;background-color:inherit;color:inherit;height:max-content}
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))}
package/code-input.min.js CHANGED
@@ -9,4 +9,4 @@
9
9
  * @license MIT
10
10
  *
11
11
  * **<https://code-input-js.org>**
12
- */"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}<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){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,"&amp;").replace(/</g,"&lt;")}unescapeHtml(a){return a.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/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.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"),this.pluginEvt("beforeElementsAdded");const a=this.querySelector("textarea[data-code-input-fallback]");let b;if(a){let c=a.getAttributeNames();for(let b=0;b<c.length;b++){const d=c[b];"data-code-input-fallback"!=d&&(this.hasAttribute(d)||this.setAttribute(d,a.getAttribute(d)))}b=a.value}else b=this.unescapeHtml(this.innerHTML);b=b||this.getAttribute("value")||"";const c=this.getAttribute("language")||this.getAttribute("lang"),d=this.getAttribute("placeholder")||c||"";this.initialValue=b;const e=document.createElement("textarea");e.placeholder=d,""!=b&&(e.value=b),e.innerHTML=this.innerHTML,this.hasAttribute("spellcheck")||e.setAttribute("spellcheck","false"),e.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")}),e.addEventListener("blur",()=>{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))&&e.setAttribute(a,this.getAttribute(a));e.addEventListener("input",()=>{this.value=this.textareaElement.value}),this.textareaElement=e,this.append(e);let f=document.createElement("code"),g=document.createElement("pre");g.setAttribute("aria-hidden","true"),g.setAttribute("tabindex","-1"),g.setAttribute("inert",!0),this.preElement=g,this.codeElement=f,g.append(f),this.append(g),this.templateObject.isCode&&c!=null&&""!=c&&f.classList.add("language-"+c.toLowerCase());let h=document.createElement("div");h.classList.add("code-input_dialog-container"),this.append(h),this.dialogContainerElement=h;let i=document.createElement("div");i.classList.add("code-input_keyboard-navigation-instructions"),h.append(i),this.pluginEvt("afterElementsAdded"),this.dispatchEvent(new CustomEvent("code-input_load")),this.value=b,this.animateFrame();const j=new ResizeObserver(()=>{this.syncSize()});j.observe(this),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(){this.templateObject=this.getTemplate(),this.templateObject!=null&&(this.classList.add("code-input_registered"),window.addEventListener("DOMContentLoaded",this.setup.bind(this)))}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));for(let a=0;a<codeInput.textareaSyncAttributes.length;a++)if(b.attributeName==codeInput.textareaSyncAttributes[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.templateObject=codeInput.usedTemplates[c||codeInput.defaultTemplate],this.templateObject.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);this.boundEventCallbacks[b]=d,codeInput.textareaSyncEvents.includes(a)?(this.boundEventCallbacks[b]=d,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)}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)}blur(a={}){return this.textareaElement.blur(a)}checkValidity(){return this.textareaElement.checkValidity()}focus(a={}){return this.textareaElement.focus(a)}reportValidity(){return this.textareaElement.reportValidity()}setCustomValidity(a){this.textareaElement.setCustomValidity(a)}setRangeText(a,b=this.selectionStart,c=this.selectionEnd,d="preserve"){this.getTextareaProperty("setRangeText")(a,b,c,d)}setSelectionRange(a,b,c="none"){this.getTextareaProperty("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);
12
+ */"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}<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){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,"&amp;").replace(/</g,"&lt;")}unescapeHtml(a){return a.replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/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.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<b.length;c++){const d=b[c];"data-code-input-fallback"!=d&&(this.hasAttribute(d)||this.setAttribute(d,a.getAttribute(d)))}h=a.value,this.innerHTML=this.escapeHtml(h)}else h=this.unescapeHtml(this.innerHTML);h=h||this.getAttribute("value")||"",this.pluginEvt("beforeElementsAdded");const i=this.getAttribute("language")||this.getAttribute("lang"),j=this.getAttribute("placeholder")||i||"";this.initialValue=h;const k=document.createElement("textarea");k.placeholder=j,""!=h&&(k.value=h),k.innerHTML=this.innerHTML,this.hasAttribute("spellcheck")||k.setAttribute("spellcheck","false"),k.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"),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.attributes.length;b++)a=this.attributes[b].name,(codeInput.textareaSyncAttributes.includes(a)||"aria-"==a.substring(0,5))&&k.setAttribute(a,this.getAttribute(a));k.addEventListener("input",()=>{this.value=this.textareaElement.value}),this.textareaElement=k,this.append(k);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(){this.templateObject=this.getTemplate(),this.templateObject!=null&&(this.classList.add("code-input_registered"),"loading"===document.readyState?window.addEventListener("DOMContentLoaded",this.setup.bind(this)):this.setup())}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));for(let a=0;a<codeInput.textareaSyncAttributes.length;a++)if(b.attributeName==codeInput.textareaSyncAttributes[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&&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.templateObject=codeInput.usedTemplates[c||codeInput.defaultTemplate],this.templateObject.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);this.boundEventCallbacks[b]=d,codeInput.textareaSyncEvents.includes(a)?(this.boundEventCallbacks[b]=d,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)}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);
package/docs/_index.md CHANGED
@@ -5,12 +5,13 @@ title = 'Flexible Syntax Highlighted Editable Textareas'
5
5
  # An <mark>editable</mark> `<textarea>` that supports *any* <mark>syntax highlighting</mark> algorithm, for code or something else. [Also, added plugins.](plugins)
6
6
 
7
7
  Aiming to be [more <mark>flexible</mark>, <mark>lightweight</mark>,
8
- <mark>modular</mark>, <mark>progressively enhanced</mark> and
9
- <mark>standards-based</mark than the alternatives](#features), we support
8
+ <mark>modular</mark>,
9
+ <mark>accessible</mark>, <mark>progressively enhanced</mark> and
10
+ <mark>standards-based</mark> than the alternatives](#features), we support
10
11
  [HTML forms](interface/forms), the [`<textarea>` JavaScript interface](interface/js), more languages and
11
12
  more use cases.
12
13
 
13
- *code-input.js is free, libre, open source software under the MIT (AKA Expat) license.* **Download it [from the Git repository](https://github.com/WebCoder49/code-input/tree/v2.6.4), [in a ZIP archive](/release/code-input-js-v2.6.4.zip), [in a TAR.GZ archive](/release/code-input-js-v2.6.4.tar.gz), or from `@webcoder49/code-input` on the NPM registry ([Yarn](https://yarnpkg.com/package?name=@webcoder49/code-input), [NPM](https://npmjs.com/package/@webcoder49/code-input), etc.).**
14
+ *code-input.js is free, libre, open source software under the MIT (AKA Expat) license.* **Download it [from the Git repository](https://github.com/WebCoder49/code-input/tree/v2.6.6), [in a ZIP archive](/release/code-input-js-v2.6.6.zip), [in a TAR.GZ archive](/release/code-input-js-v2.6.6.tar.gz), or from `@webcoder49/code-input` on the NPM registry ([Yarn](https://yarnpkg.com/package?name=@webcoder49/code-input), [NPM](https://npmjs.com/package/@webcoder49/code-input), etc.).**
14
15
 
15
16
  ## Get Started with a Demo
16
17
 
@@ -312,6 +313,6 @@ something like [CodeMirror](https://codemirror.net/),
312
313
 
313
314
  ## Contribute Bug Reports / Code / Docs {#contributing}
314
315
 
315
- 🎉 code-input.js is collaboratively developed by many people, which is what keeps it going strong. By version 2.6.0, many have reported bugs and suggestions, and [9 people (see them on GitHub)](https://github.com/WebCoder49/code-input/graphs/contributors) have contributed code or documentation directly. If you have found a bug, would like to help with the code or documentation, or have additional suggestions, for plugins or core functionality, [please look at GitHub](https://github.com/WebCoder49/code-input/tree/main/CONTRIBUTING.md) or [get in touch via email so I can add it for you](mailto:code-input-js@webcoder49.dev) / [this address for security issues](mailto:security@webcoder49.dev) ([address owner, encryption and privacy info](https://oliver.geer.im/#email))!
316
+ 🎉 code-input.js is collaboratively developed by many people, which is what keeps it going strong. Many have reported bugs and suggestions, and [10 people (see them on GitHub)](https://github.com/WebCoder49/code-input/graphs/contributors) have contributed code or documentation directly. If you have found a bug, would like to help with the code or documentation, or have additional suggestions, for plugins or core functionality, [please look at GitHub](https://github.com/WebCoder49/code-input/tree/main/CONTRIBUTING.md) or [get in touch via email so I can add it for you](mailto:code-input-js@webcoder49.dev) / [this address for security issues](mailto:security@webcoder49.dev) ([address owner, encryption and privacy info](https://oliver.geer.im/#email))!
316
317
 
317
318
  *I'm looking into mirroring code-input.js onto Codeberg as well as GitHub for more flexibility and freedom - if you have ideas for this please get in touch!*
@@ -7,6 +7,6 @@ title = 'Styling `code-input` elements with CSS'
7
7
  > Contributors: 2025 Oliver Geer
8
8
 
9
9
  `code-input` elements can be styled like `textarea` elements in most cases; however, there are some exceptions:
10
- * The CSS variable `--padding` should be used rather than the property `padding` (e.g. `<code-input style="--padding: 10px;">...`)
10
+ * The CSS variable `--padding` should be used rather than the property `padding` (e.g. `<code-input style="--padding: 10px;">...`), or `--padding-left`, `--padding-right`, `--padding-top` and `--padding-bottom` instead of the CSS properties of the same names. For technical reasons, the value must have a unit (i.e. `0px`, not `0`).
11
11
  * Background colours set on `code-input` elements will not work with highlighters that set background colours themselves - use `(code-input's selector) pre[class*="language-"]` for Prism.js or `.hljs` for highlight.js to target the highlighted element with higher specificity than the highlighter's theme. You may also set the `background-color` of the code-input element for its appearance when its template is unregistered / there is no JavaScript.
12
12
  * For now, elements on top of `code-input` elements should have a CSS `z-index` at least 3 greater than the `code-input` element.
@@ -60,7 +60,7 @@ Note the `.mjs` extensions on code-input.js import paths. They are needed, and a
60
60
  The import paths above assume a package manager and `package.json` export paths are being used.
61
61
 
62
62
  In some setups, this will not work. You have two options (replace `node_modules/@webcoder49/code-input` with the relative path to the library, if it is different):
63
- * use relative paths instead: replace `"@webcoder49/code-input/path/to/file.mjs"` with `"node_modules/@webcoder49/code-input/esm/path/to/file.mjs"` (note the `esm` directory). Also replace `"@webcoder49/code-input/path/to/file.mjs"` with `"node_modules/@highlightjs/cdn-assets/es/path/to/file.mjs"` (note the `es` directory) *If you're not using an import map yet, I recommend this option because import maps are not supported on as many browsers.*
63
+ * use relative paths instead: assuming you are importing from your project's root directory and have installed the libraries with a typical JS package manager, replace `"@webcoder49/code-input/path/to/file.mjs"` with `"./node_modules/@webcoder49/code-input/esm/path/to/file.mjs"` (note the `esm` directory). Also replace `"@webcoder49/code-input/path/to/file.mjs"` with `"./node_modules/@highlightjs/cdn-assets/es/path/to/file.mjs"` (note the `es` directory) *If you're not using an import map yet, I recommend this option because import maps are not supported on as many browsers.*
64
64
  * If you're using an [import map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules#importing_modules_using_import_maps), add the following to it:
65
65
  ```json
66
66
  {
@@ -518,9 +518,26 @@ var codeInput = {
518
518
  this.classList.add("code-input_registered"); // Remove register message
519
519
  if (this.templateObject.preElementStyled) this.classList.add("code-input_pre-element-styled");
520
520
 
521
- this.pluginEvt("beforeElementsAdded");
522
-
523
521
  const fallbackTextarea = this.querySelector("textarea[data-code-input-fallback]");
522
+ let fallbackFocused = false;
523
+ let fallbackSelectionStart = undefined;
524
+ let fallbackSelectionEnd = undefined;
525
+ let fallbackSelectionDirection = undefined;
526
+ let fallbackScrollLeft = undefined;
527
+ let fallbackScrollTop = undefined;
528
+ if(fallbackTextarea) {
529
+ // Move some attributes to new textarea so typing during
530
+ // slow load not interrupted
531
+ if(fallbackTextarea === document.activeElement) { // Thanks to https://stackoverflow.com/a/36430896
532
+ fallbackFocused = true;
533
+ }
534
+ fallbackSelectionStart = fallbackTextarea.selectionStart;
535
+ fallbackSelectionEnd = fallbackTextarea.selectionEnd;
536
+ fallbackSelectionDirection = fallbackTextarea.selectionDirection;
537
+ fallbackScrollLeft = fallbackTextarea.scrollLeft;
538
+ fallbackScrollTop = fallbackTextarea.scrollTop;
539
+ }
540
+
524
541
  let value;
525
542
  if(fallbackTextarea) {
526
543
  // Fallback textarea exists
@@ -536,11 +553,15 @@ var codeInput = {
536
553
  }
537
554
  // Sync value
538
555
  value = fallbackTextarea.value;
556
+ // Backwards compatibility with plugins
557
+ this.innerHTML = this.escapeHtml(value);
539
558
  } else {
540
559
  value = this.unescapeHtml(this.innerHTML);
541
560
  }
542
561
  value = value || this.getAttribute("value") || "";
543
562
 
563
+ this.pluginEvt("beforeElementsAdded");
564
+
544
565
  // First-time attribute sync
545
566
  const lang = this.getAttribute("language") || this.getAttribute("lang");
546
567
  const placeholder = this.getAttribute("placeholder") || lang || "";
@@ -569,9 +590,23 @@ var codeInput = {
569
590
  // Accessibility - detect when mouse focus to remove focus outline + keyboard navigation guidance that could irritate users.
570
591
  this.addEventListener("mousedown", () => {
571
592
  this.classList.add("code-input_mouse-focused");
593
+ // Wait for CSS to update padding
594
+ window.setTimeout(() => {
595
+ this.syncSize();
596
+ }, 0);
572
597
  });
573
598
  textarea.addEventListener("blur", () => {
574
599
  this.classList.remove("code-input_mouse-focused");
600
+ // Wait for CSS to update padding
601
+ window.setTimeout(() => {
602
+ this.syncSize();
603
+ }, 0);
604
+ });
605
+ textarea.addEventListener("focus", () => {
606
+ // Wait for CSS to update padding
607
+ window.setTimeout(() => {
608
+ this.syncSize();
609
+ }, 0);
575
610
  });
576
611
 
577
612
  this.innerHTML = ""; // Clear Content
@@ -624,13 +659,25 @@ var codeInput = {
624
659
  this.dispatchEvent(new CustomEvent("code-input_load"));
625
660
 
626
661
  this.value = value;
662
+
663
+ // Update with fallback textarea's state so can keep editing
664
+ // if loaded slowly
665
+ if(fallbackSelectionStart !== undefined) {
666
+ console.log("sel", fallbackSelectionStart, fallbackSelectionEnd, fallbackSelectionDirection, "scr", fallbackScrollTop, fallbackScrollLeft, "foc", fallbackFocused);
667
+ textarea.setSelectionRange(fallbackSelectionStart, fallbackSelectionEnd, fallbackSelectionDirection);
668
+ textarea.scrollTo(fallbackScrollTop, fallbackScrollLeft);
669
+ }
670
+ if(fallbackFocused) {
671
+ textarea.focus();
672
+ }
673
+
627
674
  this.animateFrame();
628
675
 
629
676
  const resizeObserver = new ResizeObserver((elements) => {
630
677
  // The only element that could be resized is this code-input element.
631
678
  this.syncSize();
632
679
  });
633
- resizeObserver.observe(this);
680
+ resizeObserver.observe(this.textareaElement);
634
681
 
635
682
  this.classList.add("code-input_loaded");
636
683
  }
@@ -682,8 +729,12 @@ var codeInput = {
682
729
  if (this.templateObject != undefined) {
683
730
  // Template registered before loading
684
731
  this.classList.add("code-input_registered");
685
- // Children not yet present - wait until they are
686
- window.addEventListener("DOMContentLoaded", this.setup.bind(this))
732
+ if (document.readyState === 'loading') {
733
+ // Children not yet present - wait until they are
734
+ window.addEventListener("DOMContentLoaded", this.setup.bind(this))
735
+ } else {
736
+ this.setup();
737
+ }
687
738
  }
688
739
  }
689
740
 
@@ -710,7 +761,9 @@ var codeInput = {
710
761
  }
711
762
 
712
763
  disconnectedCallback() {
713
- this.mutationObserver.disconnect();
764
+ if (this.mutationObserver) {
765
+ this.mutationObserver.disconnect();
766
+ }
714
767
  }
715
768
 
716
769
  /**
@@ -1026,14 +1079,40 @@ var codeInput = {
1026
1079
  get wrap() { return this.getAttribute("wrap") || ""; }
1027
1080
  set wrap(val) { this.setAttribute("wrap", val); }
1028
1081
 
1082
+ /**
1083
+ * Get the JavaScript method from the internal textarea
1084
+ * element, throwing an error when no textarea is present.
1085
+ * The method is bound to the textarea as `this`.
1086
+ *
1087
+ * For internal use - treat the code-input element as a
1088
+ * textarea for the standard functions (e.g. document.
1089
+ * querySelector("code-input").focus()).
1090
+ */
1091
+ getTextareaMethod(name) {
1092
+ if(this.textareaElement) {
1093
+ return this.textareaElement[name].bind(this.textareaElement);
1094
+ } else {
1095
+ // Unregistered
1096
+ const fallbackTextarea = this.querySelector("textarea[data-code-input-fallback]");
1097
+ if(fallbackTextarea) {
1098
+ return fallbackTextarea[name].bind(fallbackTextarea);
1099
+ } else {
1100
+ throw new Error("Cannot call "+name+" on an unregistered code-input element without a data-code-input-fallback textarea.");
1101
+ }
1102
+ }
1103
+ }
1029
1104
 
1030
- blur(options={}) { return this.textareaElement.blur(options); }
1031
- checkValidity() { return this.textareaElement.checkValidity(); }
1032
- focus(options={}) { return this.textareaElement.focus(options); }
1033
- reportValidity() { return this.textareaElement.reportValidity(); }
1034
- setCustomValidity(error) { this.textareaElement.setCustomValidity(error); }
1035
- setRangeText(replacement, selectionStart=this.selectionStart, selectionEnd=this.selectionEnd, selectMode="preserve") { this.getTextareaProperty("setRangeText")(replacement, selectionStart, selectionEnd, selectMode); }
1036
- setSelectionRange(selectionStart, selectionEnd, selectionDirection="none") { this.getTextareaProperty("setSelectionRange")(selectionStart, selectionEnd, selectionDirection); }
1105
+ blur(options={}) { this.getTextareaMethod("blur")(options); }
1106
+ checkValidity() { return this.getTextareaMethod("checkValidity")(); }
1107
+ focus(options={}) { this.getTextareaMethod("focus")(options); }
1108
+ reportValidity() { return this.getTextareaMethod("reportValidity")(); }
1109
+ setCustomValidity(error) { this.getTextareaMethod("setCustomValidity")(error); }
1110
+ setRangeText(replacement, selectionStart=this.selectionStart, selectionEnd=this.selectionEnd, selectMode="preserve") {
1111
+ this.getTextareaMethod("setRangeText")(replacement, selectionStart, selectionEnd, selectMode);
1112
+ // Reflect that value updated
1113
+ if(this.textareaElement) this.scheduleHighlight();
1114
+ }
1115
+ setSelectionRange(selectionStart, selectionEnd, selectionDirection="none") { this.getTextareaMethod("setSelectionRange")(selectionStart, selectionEnd, selectionDirection); }
1037
1116
 
1038
1117
  /**
1039
1118
  * Allows plugins to store data in the scope of a single element.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webcoder49/code-input",
3
- "version": "2.6.4",
3
+ "version": "2.6.6",
4
4
  "description": "An editable &lt;textarea&gt; that supports *any* syntax highlighting algorithm, for code or something else. Also, added plugins.",
5
5
  "browser": "code-input.js",
6
6
  "exports": {
@@ -7,13 +7,18 @@
7
7
  /* Update padding to match line-numbers plugin */
8
8
  code-input.line-numbers textarea, code-input.line-numbers.code-input_pre-element-styled pre, code-input.line-numbers:not(.code-input_pre-element-styled) pre code,
9
9
  .line-numbers code-input textarea, .line-numbers code-input.code-input_pre-element-styled pre, .line-numbers code-input:not(.code-input_pre-element-styled) pre code {
10
- padding-left: max(3.8em, var(--padding, 16px))!important;
10
+ padding-left: max(3.8em, var(--padding-left, 16px))!important;
11
11
  }
12
12
 
13
13
  /* Ensure pre code/textarea just wide enough to give 100% width with line numbers */
14
14
  code-input.line-numbers, .line-numbers code-input {
15
- grid-template-columns: calc(100% - max(0em, calc(3.8em - var(--padding, 16px))));
15
+ grid-template-columns: calc(100% - max(0em, calc(3.8em - var(--padding-left, 16px))));
16
16
  }
17
+ /* Despite the above, keep dialog container full width */
18
+ code-input.line-numbers .code-input_dialog-container, .line-numbers code-input .code-input_dialog-container {
19
+ width: calc(100% + max(0em, calc(3.8em - var(--padding-left, 16px))));
20
+ }
21
+
17
22
 
18
23
  /* Override Prism styles so there's no display:inline, relatively-positioned code element which breaks offsetTop, used in FindAndReplace, in Firefox. */
19
24
  code-input pre[class*=language-].line-numbers>code {
@@ -21,10 +26,10 @@ code-input pre[class*=language-].line-numbers>code {
21
26
  }
22
27
  /* Line numbers now positioned relative to the pre element not the code element. */
23
28
  code-input .line-numbers .line-numbers-rows {
24
- left: 0;
25
- top: var(--padding);
29
+ left: max(0em, calc(var(--padding-left, 16px) - 3.8em));
30
+ top: var(--padding-top);
26
31
  }
27
32
  /* Things with padding when instructions are present */
28
33
  code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused) .line-numbers .line-numbers-rows {
29
- top: calc(var(--padding) + 3em);
34
+ top: calc(var(--padding-top) + 3em);
30
35
  }
@@ -1 +1 @@
1
- .line-numbers code-input textarea,.line-numbers code-input.code-input_pre-element-styled pre,.line-numbers code-input:not(.code-input_pre-element-styled) pre code,code-input.line-numbers textarea,code-input.line-numbers.code-input_pre-element-styled pre,code-input.line-numbers:not(.code-input_pre-element-styled) pre code{padding-left:max(3.8em,var(--padding,16px))!important}.line-numbers code-input,code-input.line-numbers{grid-template-columns:calc(100% - max(0em,calc(3.8em - var(--padding,16px))))}code-input pre[class*=language-].line-numbers>code{position:static}code-input .line-numbers .line-numbers-rows{left:0;top:var(--padding)}code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused) .line-numbers .line-numbers-rows{top:calc(var(--padding) + 3em)}
1
+ .line-numbers code-input textarea,.line-numbers code-input.code-input_pre-element-styled pre,.line-numbers code-input:not(.code-input_pre-element-styled) pre code,code-input.line-numbers textarea,code-input.line-numbers.code-input_pre-element-styled pre,code-input.line-numbers:not(.code-input_pre-element-styled) pre code{padding-left:max(3.8em,var(--padding-left,16px))!important}.line-numbers code-input,code-input.line-numbers{grid-template-columns:calc(100% - max(0em,calc(3.8em - var(--padding-left,16px))))}.line-numbers code-input .code-input_dialog-container,code-input.line-numbers .code-input_dialog-container{width:calc(100% + max(0em,calc(3.8em - var(--padding-left,16px))))}code-input pre[class*=language-].line-numbers>code{position:static}code-input .line-numbers .line-numbers-rows{left:max(0,calc(var(--padding-left,16px) - 3.8em));top:var(--padding-top)}code-input:not(:has(.code-input_keyboard-navigation-instructions:empty)):has(textarea:not([data-code-input-fallback]):focus):not(.code-input_mouse-focused) .line-numbers .line-numbers-rows{top:calc(var(--padding-top) + 3em)}