@webcoder49/code-input 2.0.3 → 2.2.1

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.
@@ -7,7 +7,7 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
7
7
  specialCharRegExp;
8
8
 
9
9
  cachedColors; // ascii number > [background color, text color]
10
- cachedWidths; // font > {character > character width}
10
+ cachedWidths; // character > character width
11
11
  canvasContext;
12
12
 
13
13
  /**
@@ -31,37 +31,31 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
31
31
  this.canvasContext = canvas.getContext("2d");
32
32
  }
33
33
 
34
- /* Runs before elements are added into a `code-input`; Params: codeInput element) */
35
- beforeElementsAdded(codeInput) {
36
- codeInput.classList.add("code-input_special-char_container");
37
- }
38
-
39
- /* Runs after elements are added into a `code-input` (useful for adding events to the textarea); Params: codeInput element) */
34
+ /* Initially render special characters as the highlighting algorithm may automatically highlight and remove them */
40
35
  afterElementsAdded(codeInput) {
41
- // For some reason, special chars aren't synced the first time - TODO is there a cleaner way to do this?
42
- setTimeout(() => { codeInput.update(codeInput.value); }, 100);
36
+ setTimeout(() => { codeInput.value = codeInput.value; }, 100);
43
37
  }
44
38
 
45
- /* Runs after code is highlighted; Params: codeInput element) */
39
+ /* After highlighting, render special characters as their stylised hexadecimal equivalents */
46
40
  afterHighlight(codeInput) {
47
- let result_element = codeInput.querySelector("pre code");
41
+ let resultElement = codeInput.codeElement;
48
42
 
49
43
  // Reset data each highlight so can change if font size, etc. changes
50
44
  codeInput.pluginData.specialChars = {};
51
- codeInput.pluginData.specialChars.textarea = codeInput.getElementsByTagName("textarea")[0];
52
- codeInput.pluginData.specialChars.contrastColor = window.getComputedStyle(result_element).color;
45
+ codeInput.pluginData.specialChars.contrastColor = window.getComputedStyle(resultElement).color;
53
46
 
54
- this.recursivelyReplaceText(codeInput, result_element);
47
+ this.recursivelyReplaceText(codeInput, resultElement);
55
48
 
56
- this.lastFont = window.getComputedStyle(codeInput.pluginData.specialChars.textarea).font;
49
+ this.lastFont = window.getComputedStyle(codeInput.textareaElement).font;
57
50
  }
58
51
 
52
+ /* Search for special characters in an element and replace them with their stylised hexadecimal equivalents */
59
53
  recursivelyReplaceText(codeInput, element) {
60
54
  for(let i = 0; i < element.childNodes.length; i++) {
61
55
 
62
56
  let nextNode = element.childNodes[i];
63
- if(nextNode.nodeName == "#text" && nextNode.nodeValue != "") {
64
- // Replace in here
57
+ if(nextNode.nodeType == 3) {
58
+ // Text node - Replace in here
65
59
  let oldValue = nextNode.nodeValue;
66
60
 
67
61
  this.specialCharRegExp.lastIndex = 0;
@@ -72,11 +66,11 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
72
66
  nextNode = nextNode.splitText(charIndex+1).previousSibling;
73
67
 
74
68
  if(charIndex > 0) {
75
- nextNode = nextNode.splitText(charIndex); // Keep those before in difft. span
69
+ nextNode = nextNode.splitText(charIndex); // Keep characters before the special character in a different span
76
70
  }
77
71
 
78
72
  if(nextNode.textContent != "") {
79
- let replacementElement = this.specialCharReplacer(codeInput, nextNode.textContent);
73
+ let replacementElement = this.getStylisedSpecialChar(codeInput, nextNode.textContent);
80
74
  nextNode.parentNode.insertBefore(replacementElement, nextNode);
81
75
  nextNode.textContent = "";
82
76
  }
@@ -90,29 +84,30 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
90
84
  }
91
85
  }
92
86
 
93
- specialCharReplacer(codeInput, match_char) {
94
- let hex_code = match_char.codePointAt(0);
87
+ /* Get the stylised hexadecimal representation HTML element for a given special character */
88
+ getStylisedSpecialChar(codeInput, matchChar) {
89
+ let hexCode = matchChar.codePointAt(0);
95
90
 
96
91
  let colors;
97
- if(this.colorInSpecialChars) colors = this.getCharacterColor(hex_code);
92
+ if(this.colorInSpecialChars) colors = this.getCharacterColors(hexCode);
98
93
 
99
- hex_code = hex_code.toString(16);
100
- hex_code = ("0000" + hex_code).substring(hex_code.length); // So 2 chars with leading 0
101
- hex_code = hex_code.toUpperCase();
94
+ hexCode = hexCode.toString(16);
95
+ hexCode = ("0000" + hexCode).substring(hexCode.length); // So 2 chars with leading 0
96
+ hexCode = hexCode.toUpperCase();
102
97
 
103
- let char_width = this.getCharacterWidth(codeInput, match_char);
98
+ let charWidth = this.getCharacterWidthEm(codeInput, matchChar);
104
99
 
105
100
  // Create element with hex code
106
101
  let result = document.createElement("span");
107
102
  result.classList.add("code-input_special-char");
108
- result.style.setProperty("--hex-0", "var(--code-input_special-chars_" + hex_code[0] + ")");
109
- result.style.setProperty("--hex-1", "var(--code-input_special-chars_" + hex_code[1] + ")");
110
- result.style.setProperty("--hex-2", "var(--code-input_special-chars_" + hex_code[2] + ")");
111
- result.style.setProperty("--hex-3", "var(--code-input_special-chars_" + hex_code[3] + ")");
103
+ result.style.setProperty("--hex-0", "var(--code-input_special-chars_" + hexCode[0] + ")");
104
+ result.style.setProperty("--hex-1", "var(--code-input_special-chars_" + hexCode[1] + ")");
105
+ result.style.setProperty("--hex-2", "var(--code-input_special-chars_" + hexCode[2] + ")");
106
+ result.style.setProperty("--hex-3", "var(--code-input_special-chars_" + hexCode[3] + ")");
112
107
 
113
108
  // Handle zero-width chars
114
- if(char_width == 0) result.classList.add("code-input_special-char_zero-width");
115
- else result.style.width = char_width + "px";
109
+ if(charWidth == 0) result.classList.add("code-input_special-char_zero-width");
110
+ else result.style.width = charWidth + "em";
116
111
 
117
112
  if(this.colorInSpecialChars) {
118
113
  result.style.backgroundColor = "#" + colors[0];
@@ -123,96 +118,73 @@ codeInput.plugins.SpecialChars = class extends codeInput.Plugin {
123
118
  return result;
124
119
  }
125
120
 
126
- getCharacterColor(ascii_code) {
127
- // Choose colors based on character code - lazy load and return [background color, text color]
128
- let background_color;
129
- let text_color;
130
- if(!(ascii_code in this.cachedColors)) {
121
+ /* Get the colors a stylised representation of a given character must be shown in; lazy load and return [background color, text color] */
122
+ getCharacterColors(asciiCode) {
123
+ let backgroundColor;
124
+ let textColor;
125
+ if(!(asciiCode in this.cachedColors)) {
131
126
  // Get background color - arbitrary bit manipulation to get a good range of colours
132
- background_color = ascii_code^(ascii_code << 3)^(ascii_code << 7)^(ascii_code << 14)^(ascii_code << 16); // Arbitrary
133
- background_color = background_color^0x1fc627; // Arbitrary
134
- background_color = background_color.toString(16);
135
- background_color = ("000000" + background_color).substring(background_color.length); // So 6 chars with leading 0
127
+ backgroundColor = asciiCode^(asciiCode << 3)^(asciiCode << 7)^(asciiCode << 14)^(asciiCode << 16); // Arbitrary
128
+ backgroundColor = backgroundColor^0x1fc627; // Arbitrary
129
+ backgroundColor = backgroundColor.toString(16);
130
+ backgroundColor = ("000000" + backgroundColor).substring(backgroundColor.length); // So 6 chars with leading 0
136
131
 
137
132
  // Get most suitable text color - white or black depending on background brightness
138
- let color_brightness = 0;
139
- let luminance_coefficients = [0.299, 0.587, 0.114];
133
+ let colorBrightness = 0;
134
+ const luminanceCoefficients = [0.299, 0.587, 0.114];
140
135
  for(let i = 0; i < 6; i += 2) {
141
- color_brightness += parseInt(background_color.substring(i, i+2), 16) * luminance_coefficients[i/2];
136
+ colorBrightness += parseInt(backgroundColor.substring(i, i+2), 16) * luminanceCoefficients[i/2];
142
137
  }
143
138
  // Calculate darkness
144
- text_color = color_brightness < 128 ? "white" : "black";
145
-
146
- // console.log(background_color, color_brightness, text_color);
139
+ textColor = colorBrightness < 128 ? "white" : "black";
147
140
 
148
- this.cachedColors[ascii_code] = [background_color, text_color];
149
- return [background_color, text_color];
141
+ this.cachedColors[asciiCode] = [backgroundColor, textColor];
142
+ return [backgroundColor, textColor];
150
143
  } else {
151
- return this.cachedColors[ascii_code];
144
+ return this.cachedColors[asciiCode];
152
145
  }
153
146
  }
154
147
 
155
- getCharacterWidth(codeInput, char) {
148
+ /* Get the width of a character in em (relative to font size), for use in creation of the stylised hexadecimal representation with the same width */
149
+ getCharacterWidthEm(codeInput, char) {
156
150
  // Force zero-width characters
157
151
  if(new RegExp("\u00AD|\u02de|[\u0300-\u036F]|[\u0483-\u0489]|\u200b").test(char) ) { return 0 }
158
152
  // Non-renderable ASCII characters should all be rendered at same size
159
153
  if(char != "\u0096" && new RegExp("[\u{0000}-\u{001F}]|[\u{007F}-\u{009F}]", "g").test(char)) {
160
- let fallbackWidth = this.getCharacterWidth("\u0096");
154
+ let fallbackWidth = this.getCharacterWidthEm(codeInput, "\u0096");
161
155
  return fallbackWidth;
162
156
  }
163
157
 
164
- let font = window.getComputedStyle(codeInput.pluginData.specialChars.textarea).font;
158
+ let font = getComputedStyle(codeInput.textareaElement).fontFamily + " " + getComputedStyle(codeInput.textareaElement).fontStretch + " " + getComputedStyle(codeInput.textareaElement).fontStyle + " " + getComputedStyle(codeInput.textareaElement).fontVariant + " " + getComputedStyle(codeInput.textareaElement).fontWeight + " " + getComputedStyle(codeInput.textareaElement).lineHeight; // Font without size
165
159
 
166
- // Lazy-load - TODO: Get a cleaner way of doing this
160
+ // Lazy-load width of each character
167
161
  if(this.cachedWidths[font] == undefined) {
168
- this.cachedWidths[font] = {}; // Create new cached widths for this font
162
+ this.cachedWidths[font] = {};
169
163
  }
170
164
  if(this.cachedWidths[font][char] != undefined) { // Use cached width
171
165
  return this.cachedWidths[font][char];
172
166
  }
173
167
 
174
- // Ensure font the same
175
- // console.log(font);
176
- this.canvasContext.font = font;
168
+ // Ensure font the same - 20px font size is where this algorithm works
169
+ this.canvasContext.font = getComputedStyle(codeInput.textareaElement).font.replace(getComputedStyle(codeInput.textareaElement).fontSize, "20px");
177
170
 
178
171
  // Try to get width from canvas
179
- let width = this.canvasContext.measureText(char).width;
180
- if(width > Number(font.split("px")[0])) {
172
+ let width = this.canvasContext.measureText(char).width/20; // From px to em (=proportion of font-size)
173
+ if(width > 1) {
181
174
  width /= 2; // Fix double-width-in-canvas Firefox bug
182
175
  } else if(width == 0 && char != "\u0096") {
183
- let fallbackWidth = this.getCharacterWidth("\u0096");
176
+ let fallbackWidth = this.getCharacterWidthEm(codeInput, "\u0096");
184
177
  return fallbackWidth; // In Firefox some control chars don't render, but all control chars are the same width
185
178
  }
186
179
 
180
+ // Firefox will never make smaller than size at 20px
181
+ if(navigator.userAgent.includes("Mozilla") && !navigator.userAgent.includes("Chrome") && !navigator.userAgent.includes("Safari")) {
182
+ let fontSize = Number(getComputedStyle(codeInput.textareaElement).fontSize.substring(0, getComputedStyle(codeInput.textareaElement).fontSize.length-2)); // Remove 20, make px
183
+ if(fontSize < 20) width *= 20 / fontSize;
184
+ }
185
+
187
186
  this.cachedWidths[font][char] = width;
188
187
 
189
- // console.log(this.cachedWidths);
190
188
  return width;
191
189
  }
192
-
193
- // getCharacterWidth(char) { // Doesn't work for now - from StackOverflow suggestion https://stackoverflow.com/a/76146120/21785620
194
- // let textarea = codeInput.pluginData.specialChars.textarea;
195
-
196
- // // Create a temporary element to measure the width of the character
197
- // const span = document.createElement('span');
198
- // span.textContent = char;
199
-
200
- // // Copy the textarea's font to the temporary element
201
- // span.style.fontSize = window.getComputedStyle(textarea).fontSize;
202
- // span.style.fontFamily = window.getComputedStyle(textarea).fontFamily;
203
- // span.style.fontWeight = window.getComputedStyle(textarea).fontWeight;
204
- // span.style.visibility = 'hidden';
205
- // span.style.position = 'absolute';
206
-
207
- // // Add the temporary element to the document so we can measure its width
208
- // document.body.appendChild(span);
209
-
210
- // // Get the width of the character in pixels
211
- // const width = span.offsetWidth;
212
-
213
- // // Remove the temporary element from the document
214
- // document.body.removeChild(span);
215
-
216
- // return width;
217
- // }
218
190
  }
@@ -1,4 +1,4 @@
1
- :root,body{--code-input_special-chars_0:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABtJREFUGFdjZGBgYPj///9/RhCAMcA0bg6yHgAPmh/6BoxTcQAAAABJRU5ErkJgggAA');--code-input_special-chars_1:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABZJREFUGFdjZGBgYPj///9/RhAggwMAitIUBr9U6sYAAAAASUVORK5CYII=');--code-input_special-chars_2:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB9JREFUGFdj/P///38GKGCEMUCCjCgyYBFGRrAKFBkAuLYT9kYcIu0AAAAASUVORK5CYII=');--code-input_special-chars_3:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABhJREFUGFdj/P///38GKGCEMUCCjMTJAACYiBPyG8sfAgAAAABJRU5ErkJggg==');--code-input_special-chars_4:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdj/P///39GRkZGMI3BYYACRhgDrAKZAwAYxhvyz0DRIQAAAABJRU5ErkJggg==');--code-input_special-chars_5:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACJJREFUGFdj/P///38GKGAEcRgZGRlBfDAHLgNjgFUgywAAuR4T9hxJl2YAAAAASUVORK5CYII=');--code-input_special-chars_6:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACBJREFUGFdj/P///38GKGAEcRgZGRlBfDAHQwasAlkGABcdF/Y4yco2AAAAAElFTkSuQmCC');--code-input_special-chars_7:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABZJREFUGFdj/P///38GKGCEMUCCRHIAWMgT8kue3bQAAAAASUVORK5CYII=');--code-input_special-chars_8:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABlJREFUGFdj/P///38GKGAEcRgZGSE0cTIAvHcb8v+mIfAAAAAASUVORK5CYII=');--code-input_special-chars_9:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB9JREFUGFdj/P///38GKGAEcRgZGSE0igxMCVgGmQMAPqcX8hWL1K0AAAAASUVORK5CYII=');--code-input_special-chars_A:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACBJREFUGFdjZGBgYPj///9/RhCAMcA0iADJggCmDEw5ALdxH/aGuYHqAAAAAElFTkSuQmCC');--code-input_special-chars_B:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABlJREFUGFdj/P///38GBgYGRhAAceA0cTIAvc0b/vRDnVoAAAAASUVORK5CYII=');--code-input_special-chars_C:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdjZGBgYPj///9/EM0IYjAyMjIS4CDrAQC57hP+uLwvFQAAAABJRU5ErkJggg==');--code-input_special-chars_D:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABtJREFUGFdj/P///38GBgYGRhAAceA0fg5MDwAveh/6ToN9VwAAAABJRU5ErkJggg==');--code-input_special-chars_E:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABxJREFUGFdj/P///38GKGAEcRgZGRlBfDCHsAwA2UwT+mVIH1MAAAAASUVORK5CYII=');--code-input_special-chars_F:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdj/P///38GKGAEcRgZGRlBfDAHtwxMGQDZZhP+BnB1kwAAAABJRU5ErkJggg==')}.code-input_special-char_container{font-size:20px}.code-input_special-char{display:inline-block;position:relative;top:0;left:0;height:1em;overflow:hidden;text-decoration:none;text-shadow:none;vertical-align:middle;outline:.1px solid currentColor;--hex-0:var(
1
+ :root,body{--code-input_special-chars_0:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABtJREFUGFdjZGBgYPj///9/RhCAMcA0bg6yHgAPmh/6BoxTcQAAAABJRU5ErkJgggAA');--code-input_special-chars_1:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABZJREFUGFdjZGBgYPj///9/RhAggwMAitIUBr9U6sYAAAAASUVORK5CYII=');--code-input_special-chars_2:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB9JREFUGFdj/P///38GKGCEMUCCjCgyYBFGRrAKFBkAuLYT9kYcIu0AAAAASUVORK5CYII=');--code-input_special-chars_3:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABhJREFUGFdj/P///38GKGCEMUCCjMTJAACYiBPyG8sfAgAAAABJRU5ErkJggg==');--code-input_special-chars_4:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdj/P///39GRkZGMI3BYYACRhgDrAKZAwAYxhvyz0DRIQAAAABJRU5ErkJggg==');--code-input_special-chars_5:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACJJREFUGFdj/P///38GKGAEcRgZGRlBfDAHLgNjgFUgywAAuR4T9hxJl2YAAAAASUVORK5CYII=');--code-input_special-chars_6:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACBJREFUGFdj/P///38GKGAEcRgZGRlBfDAHQwasAlkGABcdF/Y4yco2AAAAAElFTkSuQmCC');--code-input_special-chars_7:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABZJREFUGFdj/P///38GKGCEMUCCRHIAWMgT8kue3bQAAAAASUVORK5CYII=');--code-input_special-chars_8:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABlJREFUGFdj/P///38GKGAEcRgZGSE0cTIAvHcb8v+mIfAAAAAASUVORK5CYII=');--code-input_special-chars_9:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB9JREFUGFdj/P///38GKGAEcRgZGSE0igxMCVgGmQMAPqcX8hWL1K0AAAAASUVORK5CYII=');--code-input_special-chars_A:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACBJREFUGFdjZGBgYPj///9/RhCAMcA0iADJggCmDEw5ALdxH/aGuYHqAAAAAElFTkSuQmCC');--code-input_special-chars_B:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABlJREFUGFdj/P///38GBgYGRhAAceA0cTIAvc0b/vRDnVoAAAAASUVORK5CYII=');--code-input_special-chars_C:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdjZGBgYPj///9/EM0IYjAyMjIS4CDrAQC57hP+uLwvFQAAAABJRU5ErkJggg==');--code-input_special-chars_D:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABtJREFUGFdj/P///38GBgYGRhAAceA0fg5MDwAveh/6ToN9VwAAAABJRU5ErkJggg==');--code-input_special-chars_E:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABxJREFUGFdj/P///38GKGAEcRgZGRlBfDCHsAwA2UwT+mVIH1MAAAAASUVORK5CYII=');--code-input_special-chars_F:url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdj/P///38GKGAEcRgZGRlBfDAHtwxMGQDZZhP+BnB1kwAAAABJRU5ErkJggg==')}.code-input_special-char{display:inline-block;position:relative;top:0;left:0;height:1em;overflow:hidden;text-decoration:none;text-shadow:none;vertical-align:middle;outline:.1px solid currentColor;--hex-0:var(
2
2
  --code-input_special-chars_0);--hex-1:var(
3
3
  --code-input_special-chars_0);--hex-2:var(
4
4
  --code-input_special-chars_0);--hex-3:var(
@@ -1 +1 @@
1
- codeInput.plugins.SpecialChars=class extends codeInput.Plugin{specialCharRegExp;cachedColors;cachedWidths;canvasContext;constructor(a=!1,b=!1,c=/(?!\n)(?!\t)[\u{0000}-\u{001F}]|[\u{007F}-\u{009F}]|[\u{0200}-\u{FFFF}]/ug){super([]),this.specialCharRegExp=c,this.colorInSpecialChars=a,this.inheritTextColor=b,this.cachedColors={},this.cachedWidths={};let d=document.createElement("canvas");this.canvasContext=d.getContext("2d")}beforeElementsAdded(a){a.classList.add("code-input_special-char_container")}afterElementsAdded(a){setTimeout(()=>{a.update(a.value)},100)}afterHighlight(a){let b=a.querySelector("pre code");a.pluginData.specialChars={},a.pluginData.specialChars.textarea=a.getElementsByTagName("textarea")[0],a.pluginData.specialChars.contrastColor=window.getComputedStyle(b).color,this.recursivelyReplaceText(a,b),this.lastFont=window.getComputedStyle(a.pluginData.specialChars.textarea).font}recursivelyReplaceText(a,b){for(let c,d=0;d<b.childNodes.length;d++)if(c=b.childNodes[d],"#text"==c.nodeName&&""!=c.nodeValue){let b=c.nodeValue;this.specialCharRegExp.lastIndex=0;let d=this.specialCharRegExp.exec(b);if(null!=d){let b=d.index;if(c=c.splitText(b+1).previousSibling,0<b&&(c=c.splitText(b)),""!=c.textContent){let b=this.specialCharReplacer(a,c.textContent);c.parentNode.insertBefore(b,c),c.textContent=""}}}else 1==c.nodeType&&"code-input_special-char"!=c.className&&""!=c.nodeValue&&this.recursivelyReplaceText(a,c)}specialCharReplacer(a,b){let c,d=b.codePointAt(0);this.colorInSpecialChars&&(c=this.getCharacterColor(d)),d=d.toString(16),d=("0000"+d).substring(d.length),d=d.toUpperCase();let e=this.getCharacterWidth(a,b),f=document.createElement("span");return f.classList.add("code-input_special-char"),f.style.setProperty("--hex-0","var(--code-input_special-chars_"+d[0]+")"),f.style.setProperty("--hex-1","var(--code-input_special-chars_"+d[1]+")"),f.style.setProperty("--hex-2","var(--code-input_special-chars_"+d[2]+")"),f.style.setProperty("--hex-3","var(--code-input_special-chars_"+d[3]+")"),0==e?f.classList.add("code-input_special-char_zero-width"):f.style.width=e+"px",this.colorInSpecialChars?(f.style.backgroundColor="#"+c[0],f.style.setProperty("--code-input_special-char_color",c[1])):!this.inheritTextColor&&f.style.setProperty("--code-input_special-char_color",a.pluginData.specialChars.contrastColor),f}getCharacterColor(a){let b,c;if(!(a in this.cachedColors)){b=a^a<<3^a<<7^a<<14^a<<16,b^=2082343,b=b.toString(16),b=("000000"+b).substring(b.length);let d=0,e=[.299,.587,.114];for(let a=0;6>a;a+=2)d+=parseInt(b.substring(a,a+2),16)*e[a/2];return c=128>d?"white":"black",this.cachedColors[a]=[b,c],[b,c]}return this.cachedColors[a]}getCharacterWidth(a,b){if(/­|˞|[̀-ͯ]|[҃-҉]|​/.test(b))return 0;if("\x96"!=b&&/[\0-]|[-Ÿ]/g.test(b)){let a=this.getCharacterWidth("\x96");return a}let c=window.getComputedStyle(a.pluginData.specialChars.textarea).font;if(null==this.cachedWidths[c]&&(this.cachedWidths[c]={}),null!=this.cachedWidths[c][b])return this.cachedWidths[c][b];this.canvasContext.font=c;let d=this.canvasContext.measureText(b).width;if(d>+c.split("px")[0])d/=2;else if(0==d&&"\x96"!=b){let a=this.getCharacterWidth("\x96");return a}return this.cachedWidths[c][b]=d,d}};
1
+ codeInput.plugins.SpecialChars=class extends codeInput.Plugin{specialCharRegExp;cachedColors;cachedWidths;canvasContext;constructor(a=!1,b=!1,c=/(?!\n)(?!\t)[\u{0000}-\u{001F}]|[\u{007F}-\u{009F}]|[\u{0200}-\u{FFFF}]/ug){super([]),this.specialCharRegExp=c,this.colorInSpecialChars=a,this.inheritTextColor=b,this.cachedColors={},this.cachedWidths={};let d=document.createElement("canvas");this.canvasContext=d.getContext("2d")}afterElementsAdded(a){setTimeout(()=>{a.value=a.value},100)}afterHighlight(a){let b=a.codeElement;a.pluginData.specialChars={},a.pluginData.specialChars.contrastColor=window.getComputedStyle(b).color,this.recursivelyReplaceText(a,b),this.lastFont=window.getComputedStyle(a.textareaElement).font}recursivelyReplaceText(a,b){for(let c,d=0;d<b.childNodes.length;d++)if(c=b.childNodes[d],3==c.nodeType){let b=c.nodeValue;this.specialCharRegExp.lastIndex=0;let d=this.specialCharRegExp.exec(b);if(null!=d){let b=d.index;if(c=c.splitText(b+1).previousSibling,0<b&&(c=c.splitText(b)),""!=c.textContent){let b=this.getStylisedSpecialChar(a,c.textContent);c.parentNode.insertBefore(b,c),c.textContent=""}}}else 1==c.nodeType&&"code-input_special-char"!=c.className&&""!=c.nodeValue&&this.recursivelyReplaceText(a,c)}getStylisedSpecialChar(a,b){let c,d=b.codePointAt(0);this.colorInSpecialChars&&(c=this.getCharacterColors(d)),d=d.toString(16),d=("0000"+d).substring(d.length),d=d.toUpperCase();let e=this.getCharacterWidthEm(a,b),f=document.createElement("span");return f.classList.add("code-input_special-char"),f.style.setProperty("--hex-0","var(--code-input_special-chars_"+d[0]+")"),f.style.setProperty("--hex-1","var(--code-input_special-chars_"+d[1]+")"),f.style.setProperty("--hex-2","var(--code-input_special-chars_"+d[2]+")"),f.style.setProperty("--hex-3","var(--code-input_special-chars_"+d[3]+")"),0==e?f.classList.add("code-input_special-char_zero-width"):f.style.width=e+"em",this.colorInSpecialChars?(f.style.backgroundColor="#"+c[0],f.style.setProperty("--code-input_special-char_color",c[1])):!this.inheritTextColor&&f.style.setProperty("--code-input_special-char_color",a.pluginData.specialChars.contrastColor),f}getCharacterColors(a){let b,c;if(!(a in this.cachedColors)){b=a^a<<3^a<<7^a<<14^a<<16,b^=2082343,b=b.toString(16),b=("000000"+b).substring(b.length);let d=0;const e=[.299,.587,.114];for(let a=0;6>a;a+=2)d+=parseInt(b.substring(a,a+2),16)*e[a/2];return c=128>d?"white":"black",this.cachedColors[a]=[b,c],[b,c]}return this.cachedColors[a]}getCharacterWidthEm(a,b){if(/­|˞|[̀-ͯ]|[҃-҉]|​/.test(b))return 0;if("\x96"!=b&&/[\0-]|[-Ÿ]/g.test(b)){let b=this.getCharacterWidthEm(a,"\x96");return b}let c=getComputedStyle(a.textareaElement).fontFamily+" "+getComputedStyle(a.textareaElement).fontStretch+" "+getComputedStyle(a.textareaElement).fontStyle+" "+getComputedStyle(a.textareaElement).fontVariant+" "+getComputedStyle(a.textareaElement).fontWeight+" "+getComputedStyle(a.textareaElement).lineHeight;if(null==this.cachedWidths[c]&&(this.cachedWidths[c]={}),null!=this.cachedWidths[c][b])return this.cachedWidths[c][b];this.canvasContext.font=getComputedStyle(a.textareaElement).font.replace(getComputedStyle(a.textareaElement).fontSize,"20px");let d=this.canvasContext.measureText(b).width/20;if(1<d)d/=2;else if(0==d&&"\x96"!=b){let b=this.getCharacterWidthEm(a,"\x96");return b}if(navigator.userAgent.includes("Mozilla")&&!navigator.userAgent.includes("Chrome")&&!navigator.userAgent.includes("Safari")){let b=+getComputedStyle(a.textareaElement).fontSize.substring(0,getComputedStyle(a.textareaElement).fontSize.length-2);20>b&&(d*=20/b)}return this.cachedWidths[c][b]=d,d}};
package/plugins/test.js CHANGED
@@ -10,9 +10,8 @@
10
10
  */
11
11
  codeInput.plugins.Test = class extends codeInput.Plugin {
12
12
  constructor() {
13
- super(["testattr", "test-*"]);
13
+ super(["testattr"]);
14
14
  // Array of observed attributes as parameter
15
- // Wildcard "*" matches any text
16
15
  }
17
16
  /* Runs before code is highlighted; Params: codeInput element) */
18
17
  beforeHighlight(codeInput) {
@@ -1 +1 @@
1
- codeInput.plugins.Test=class extends codeInput.Plugin{constructor(){super(["testattr","test-*"])}beforeHighlight(a){console.log(a,"before highlight")}afterHighlight(a){console.log(a,"after highlight")}beforeElementsAdded(a){console.log(a,"before elements added")}afterElementsAdded(a){console.log(a,"after elements added")}attributeChanged(a,b,c,d){console.log(a,b,":",c,">",d)}};
1
+ codeInput.plugins.Test=class extends codeInput.Plugin{constructor(){super(["testattr"])}beforeHighlight(a){console.log(a,"before highlight")}afterHighlight(a){console.log(a,"after highlight")}beforeElementsAdded(a){console.log(a,"before elements added")}afterElementsAdded(a){console.log(a,"after elements added")}attributeChanged(a,b,c,d){console.log(a,b,":",c,">",d)}};
@@ -0,0 +1,54 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>code-input Tester</title>
7
+
8
+ <!--Import Highlight.JS-->
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css">
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/xml.min.js"></script>
12
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/css.min.js"></script>
13
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/javascript.min.js"></script>
14
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"></script>
15
+
16
+
17
+ <!--Import code-input-->
18
+ <link rel="stylesheet" href="../code-input.css">
19
+ <script src="../code-input.js"></script>
20
+
21
+ <!--Import code-input plugins-->
22
+ <script src="../plugins/auto-close-brackets.js"></script>
23
+ <script src="../plugins/autocomplete.js"></script>
24
+ <link rel="stylesheet" href="../plugins/autocomplete.css">
25
+ <script src="../plugins/autodetect.js"></script>
26
+ <script src="../plugins/find-and-replace.js"></script>
27
+ <link rel="stylesheet" href="../plugins/find-and-replace.css">
28
+ <script src="../plugins/go-to-line.js"></script>
29
+ <link rel="stylesheet" href="../plugins/go-to-line.css">
30
+ <script src="../plugins/indent.js"></script>
31
+ <script src="../plugins/special-chars.js"></script>
32
+ <link rel="stylesheet" href="../plugins/special-chars.css">
33
+
34
+ <script src="tester.js"></script>
35
+ </head>
36
+ <body>
37
+ <h1>code-input Tester (highlight.js)</h1>
38
+ <h2>If the page doesn't load, please reload it, and answer the questions in alert boxes.</h2>
39
+ <h4><a href="prism.html">Test for Prism.js</a></h4>
40
+ <p>This page carries out automated tests for the code-input library to check that both the core components and the plugins work in some ways. It doesn't fully cover every scenario so you should test any code you change by hand, but it's good for quickly checking a wide range of functionality works.</p>
41
+
42
+ <details id="collapse-results"><summary>Test Results (Click to Open)</summary><pre id="test-results"></pre></details>
43
+ <form method="GET" action="https://google.com/search" target="_blank">
44
+ <code-input language="JavaScript" name="q">console.log("Hello, World!");
45
+ // A second line
46
+ // A third line with &lt;html> tags</code-input>
47
+ <input type="submit" value="Search Google For Code"/>
48
+ </form>
49
+
50
+ <script>
51
+ beginTest(true);
52
+ </script>
53
+ </body>
54
+ </html>
@@ -0,0 +1,55 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>code-input Tester</title>
7
+
8
+ <!--Import Prism-->
9
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism.min.css">
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
12
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.js"></script>
13
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/line-numbers/prism-line-numbers.min.css">
14
+
15
+ <!--Import code-input-->
16
+ <link rel="stylesheet" href="../code-input.css">
17
+ <script src="../code-input.js"></script>
18
+
19
+ <!--Import code-input plugins-->
20
+ <script src="../plugins/auto-close-brackets.js"></script>
21
+ <script src="../plugins/autocomplete.js"></script>
22
+ <link rel="stylesheet" href="../plugins/autocomplete.css">
23
+ <script src="../plugins/find-and-replace.js"></script>
24
+ <link rel="stylesheet" href="../plugins/find-and-replace.css">
25
+ <script src="../plugins/go-to-line.js"></script>
26
+ <link rel="stylesheet" href="../plugins/go-to-line.css">
27
+ <script src="../plugins/indent.js"></script>
28
+ <link rel="stylesheet" href="../plugins/prism-line-numbers.css">
29
+ <script src="../plugins/special-chars.js"></script>
30
+ <link rel="stylesheet" href="../plugins/special-chars.css">
31
+
32
+ <script src="tester.js"></script>
33
+ </head>
34
+ <body>
35
+ <h1>code-input Tester (Prism.js)</h1>
36
+ <h2>If the page doesn't load, please reload it, and answer the questions in alert boxes.</h2>
37
+ <h4><a href="hljs.html">Test for highlight.js</a></h4>
38
+ <p>This page carries out automated tests for the code-input library to check that both the core components and the plugins work in some ways. It doesn't fully cover every scenario so you should test any code you change by hand, but it's good for quickly checking a wide range of functionality works.</p>
39
+
40
+ <!-- <pre class="line-numbers language-javascript"><code>// Hello
41
+ // World
42
+ // Yay!</code></pre> -->
43
+
44
+ <details id="collapse-results"><summary>Test Results (Click to Open)</summary><pre id="test-results"></pre></details>
45
+ <form method="GET" class="line-numbers" action="https://google.com/search" target="_blank">
46
+ <code-input language="JavaScript" name="q">console.log("Hello, World!");
47
+ // A second line
48
+ // A third line with &lt;html> tags</code-input>
49
+ <input type="submit" value="Search Google For Code"/>
50
+ </form>
51
+ <script>
52
+ beginTest(false);
53
+ </script>
54
+ </body>
55
+ </html>