@webcoder49/code-input 1.5.1 → 2.0.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.
- package/README.md +24 -8
- package/code-input.css +19 -4
- package/code-input.d.ts +242 -0
- package/code-input.js +366 -81
- package/code-input.min.css +1 -1
- package/code-input.min.js +1 -1
- package/package.json +1 -1
- package/plugins/autocomplete.js +2 -3
- package/plugins/autocomplete.min.js +1 -1
- package/plugins/autodetect.js +2 -3
- package/plugins/autodetect.min.js +1 -1
- package/plugins/debounce-update.js +1 -1
- package/plugins/debounce-update.min.js +1 -1
- package/plugins/indent.js +1 -1
- package/plugins/indent.min.js +1 -1
- package/plugins/special-chars.js +4 -5
- package/plugins/special-chars.min.js +1 -1
- package/plugins/test.js +5 -6
- package/plugins/test.min.js +1 -1
package/code-input.js
CHANGED
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* **<https://github.com/WebCoder49/code-input>**
|
|
7
7
|
*/
|
|
8
|
+
|
|
9
|
+
|
|
8
10
|
var codeInput = {
|
|
9
11
|
/**
|
|
10
12
|
* A list of attributes that will trigger the
|
|
@@ -27,6 +29,7 @@ var codeInput = {
|
|
|
27
29
|
* code-input element.
|
|
28
30
|
*/
|
|
29
31
|
textareaSyncAttributes: [
|
|
32
|
+
"aria-*",
|
|
30
33
|
"value",
|
|
31
34
|
"name",
|
|
32
35
|
// Form validation - https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#using_built-in_form_validation
|
|
@@ -85,17 +88,19 @@ var codeInput = {
|
|
|
85
88
|
* @param {Object} template - a Template object instance - see `codeInput.templates`
|
|
86
89
|
*/
|
|
87
90
|
registerTemplate: function (templateName, template) {
|
|
88
|
-
if(!(typeof templateName == "string" || templateName instanceof String)) throw TypeError(`Template for "${templateName}" must be a string.`);
|
|
89
|
-
if(!(typeof template.highlight == "function" || template.highlight instanceof Function)) throw TypeError(`Template for "${templateName}" invalid, because the highlight function provided is not a function; it is "${template.highlight}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
90
|
-
if(!(typeof template.includeCodeInputInHighlightFunc == "boolean" || template.includeCodeInputInHighlightFunc instanceof Boolean)) throw TypeError(`Template for "${templateName}" invalid, because the includeCodeInputInHighlightFunc value provided is not a true or false; it is "${template.includeCodeInputInHighlightFunc}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
91
|
-
if(!(typeof template.preElementStyled == "boolean" || template.preElementStyled instanceof Boolean)) throw TypeError(`Template for "${templateName}" invalid, because the preElementStyled value provided is not a true or false; it is "${template.preElementStyled}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
92
|
-
if(!(typeof template.isCode == "boolean" || template.isCode instanceof Boolean)) throw TypeError(`Template for "${templateName}" invalid, because the isCode value provided is not a true or false; it is "${template.isCode}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
93
|
-
if(!Array.isArray(template.plugins)) throw TypeError(`Template for "${templateName}" invalid, because the plugin array provided is not an array; it is "${template.plugins}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
91
|
+
if(!(typeof templateName == "string" || templateName instanceof String)) throw TypeError(`code-input: Template for "${templateName}" must be a string.`);
|
|
92
|
+
if(!(typeof template.highlight == "function" || template.highlight instanceof Function)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the highlight function provided is not a function; it is "${template.highlight}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
93
|
+
if(!(typeof template.includeCodeInputInHighlightFunc == "boolean" || template.includeCodeInputInHighlightFunc instanceof Boolean)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the includeCodeInputInHighlightFunc value provided is not a true or false; it is "${template.includeCodeInputInHighlightFunc}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
94
|
+
if(!(typeof template.preElementStyled == "boolean" || template.preElementStyled instanceof Boolean)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the preElementStyled value provided is not a true or false; it is "${template.preElementStyled}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
95
|
+
if(!(typeof template.isCode == "boolean" || template.isCode instanceof Boolean)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the isCode value provided is not a true or false; it is "${template.isCode}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
96
|
+
if(!Array.isArray(template.plugins)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the plugin array provided is not an array; it is "${template.plugins}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
97
|
+
|
|
94
98
|
template.plugins.forEach((plugin, i) => {
|
|
95
99
|
if(!(plugin instanceof codeInput.Plugin)) {
|
|
96
|
-
throw TypeError(`Template for "${templateName}" invalid, because the plugin provided at index ${i} is not valid; it is "${template.plugins[i]}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
100
|
+
throw TypeError(`code-input: Template for "${templateName}" invalid, because the plugin provided at index ${i} is not valid; it is "${template.plugins[i]}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
|
|
97
101
|
}
|
|
98
102
|
});
|
|
103
|
+
|
|
99
104
|
|
|
100
105
|
codeInput.usedTemplates[templateName] = template;
|
|
101
106
|
// Add waiting code-input elements wanting this template from queue
|
|
@@ -103,10 +108,13 @@ var codeInput = {
|
|
|
103
108
|
for (let i in codeInput.templateNotYetRegisteredQueue[templateName]) {
|
|
104
109
|
elem = codeInput.templateNotYetRegisteredQueue[templateName][i];
|
|
105
110
|
elem.template = template;
|
|
106
|
-
elem.
|
|
111
|
+
codeInput.runOnceWindowLoaded((function(elem) { elem.connectedCallback(); }).bind(null, elem), elem);
|
|
112
|
+
// Bind sets elem in parameter
|
|
113
|
+
// So innerHTML can be read
|
|
107
114
|
}
|
|
108
115
|
console.log(`code-input: template: Added existing elements with template ${templateName}`);
|
|
109
116
|
}
|
|
117
|
+
|
|
110
118
|
if (codeInput.defaultTemplate == undefined) {
|
|
111
119
|
codeInput.defaultTemplate = templateName;
|
|
112
120
|
// Add elements with default template from queue
|
|
@@ -114,7 +122,9 @@ var codeInput = {
|
|
|
114
122
|
for (let i in codeInput.templateNotYetRegisteredQueue[undefined]) {
|
|
115
123
|
elem = codeInput.templateNotYetRegisteredQueue[undefined][i];
|
|
116
124
|
elem.template = template;
|
|
117
|
-
elem.
|
|
125
|
+
codeInput.runOnceWindowLoaded((function(elem) { elem.connectedCallback(); }).bind(null, elem), elem);
|
|
126
|
+
// Bind sets elem in parameter
|
|
127
|
+
// So innerHTML can be read
|
|
118
128
|
}
|
|
119
129
|
}
|
|
120
130
|
console.log(`code-input: template: Set template ${templateName} as default`);
|
|
@@ -123,34 +133,80 @@ var codeInput = {
|
|
|
123
133
|
},
|
|
124
134
|
|
|
125
135
|
/**
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* the highlighting.
|
|
132
|
-
* For adding small pieces of functionality, please see `codeInput.plugins`.
|
|
136
|
+
* Please see `codeInput.templates.prism` or `codeInput.templates.hljs`.
|
|
137
|
+
* Templates are used in `<code-input>` elements and once registered with
|
|
138
|
+
* `codeInput.registerTemplate` will be in charge of the highlighting
|
|
139
|
+
* algorithm and settings for all code-inputs with a `template` attribute
|
|
140
|
+
* matching the registered name.
|
|
133
141
|
*/
|
|
134
|
-
|
|
142
|
+
Template: class {
|
|
135
143
|
/**
|
|
136
144
|
* Constructor to create a custom template instance. Pass this into `codeInput.registerTemplate` to use it.
|
|
137
145
|
* I would strongly recommend using the built-in simpler template `codeInput.templates.prism` or `codeInput.templates.hljs`.
|
|
138
146
|
* @param {Function} highlight - a callback to highlight the code, that takes an HTML `<code>` element inside a `<pre>` element as a parameter
|
|
139
|
-
* @param {boolean} preElementStyled - is the
|
|
147
|
+
* @param {boolean} preElementStyled - is the `<pre>` element CSS-styled as well as the `<code>` element? If true, `<pre>` element's scrolling is synchronised; if false, `<code>` element's scrolling is synchronised.
|
|
140
148
|
* @param {boolean} isCode - is this for writing code? If true, the code-input's lang HTML attribute can be used, and the `<code>` element will be given the class name 'language-[lang attribute's value]'.
|
|
141
149
|
* @param {boolean} includeCodeInputInHighlightFunc - Setting this to true passes the `<code-input>` element as a second argument to the highlight function.
|
|
142
|
-
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.
|
|
150
|
+
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.Plugin`
|
|
143
151
|
* @returns template object
|
|
144
152
|
*/
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
153
|
+
constructor(highlight = function () { }, preElementStyled = true, isCode = true, includeCodeInputInHighlightFunc = false, plugins = []) {
|
|
154
|
+
this.highlight = highlight;
|
|
155
|
+
this.preElementStyled = preElementStyled;
|
|
156
|
+
this.isCode = isCode;
|
|
157
|
+
this.includeCodeInputInHighlightFunc = includeCodeInputInHighlightFunc;
|
|
158
|
+
this.plugins = plugins;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* A callback to highlight the code, that takes an HTML `<code>` element
|
|
163
|
+
* inside a `<pre>` element as a parameter, and an optional second
|
|
164
|
+
* `<code-input>` element parameter if `this.includeCodeInputInHighlightFunc` is
|
|
165
|
+
* `true`.
|
|
166
|
+
*/
|
|
167
|
+
highlight = function() {};
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Is the <pre> element CSS-styled as well as the `<code>` element?
|
|
171
|
+
* If `true`, `<pre>` element's scrolling is synchronised; if false,
|
|
172
|
+
* <code> element's scrolling is synchronised.
|
|
173
|
+
*/
|
|
174
|
+
preElementStyled = true;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Is this for writing code?
|
|
178
|
+
* If true, the code-input's lang HTML attribute can be used,
|
|
179
|
+
* and the `<code>` element will be given the class name
|
|
180
|
+
* 'language-[lang attribute's value]'.
|
|
181
|
+
*/
|
|
182
|
+
isCode = true;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Setting this to true passes the `<code-input>` element as a
|
|
186
|
+
* second argument to the highlight function.
|
|
187
|
+
*/
|
|
188
|
+
includeCodeInputInHighlightFunc = false;
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* An array of plugin objects to add extra features -
|
|
192
|
+
* see `codeInput.Plugin`.
|
|
193
|
+
*/
|
|
194
|
+
plugins = [];
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* For creating a custom template from scratch, please
|
|
199
|
+
* use `new codeInput.Template(...)`
|
|
200
|
+
*
|
|
201
|
+
* Shortcut functions for creating templates.
|
|
202
|
+
* Each code-input element has a template attribute that
|
|
203
|
+
* tells it which template to use.
|
|
204
|
+
* Each template contains functions and preferences that
|
|
205
|
+
* run the syntax-highlighting and let code-input control
|
|
206
|
+
* the highlighting.
|
|
207
|
+
* For adding small pieces of functionality, please see `codeInput.plugins`.
|
|
208
|
+
*/
|
|
209
|
+
templates: {
|
|
154
210
|
/**
|
|
155
211
|
* Constructor to create a template that uses Prism.js syntax highlighting (https://prismjs.com/)
|
|
156
212
|
* @param {Object} prism Import Prism.js, then after that import pass the `Prism` object as this parameter.
|
|
@@ -245,7 +301,20 @@ var codeInput = {
|
|
|
245
301
|
*/
|
|
246
302
|
rainbow_text(rainbowColors = ["red", "orangered", "orange", "goldenrod", "gold", "green", "darkgreen", "navy", "blue", "magenta"], delimiter = "", plugins = []) {
|
|
247
303
|
return this.rainbowText(rainbowColors, delimiter, plugins);
|
|
248
|
-
}
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* @deprecated Please use `new codeInput.Template()`
|
|
308
|
+
*/
|
|
309
|
+
custom(highlight = function () { }, preElementStyled = true, isCode = true, includeCodeInputInHighlightFunc = false, plugins = []) {
|
|
310
|
+
return {
|
|
311
|
+
highlight: highlight,
|
|
312
|
+
includeCodeInputInHighlightFunc: includeCodeInputInHighlightFunc,
|
|
313
|
+
preElementStyled: preElementStyled,
|
|
314
|
+
isCode: isCode,
|
|
315
|
+
plugins: plugins,
|
|
316
|
+
};
|
|
317
|
+
},
|
|
249
318
|
},
|
|
250
319
|
|
|
251
320
|
/* ------------------------------------
|
|
@@ -253,25 +322,52 @@ var codeInput = {
|
|
|
253
322
|
* ------------------------------------ */
|
|
254
323
|
|
|
255
324
|
/**
|
|
325
|
+
* Before using any plugin in this namespace, please ensure you import its JavaScript
|
|
326
|
+
* files (in the plugins folder), or continue to get a more detailed error in the developer
|
|
327
|
+
* console.
|
|
328
|
+
*
|
|
256
329
|
* Where plugins are stored, after they are imported. The plugin
|
|
257
330
|
* file assigns them a space in this object.
|
|
258
331
|
* For adding completely new syntax-highlighting algorithms, please see `codeInput.templates`.
|
|
332
|
+
*
|
|
259
333
|
* Key - plugin name
|
|
334
|
+
*
|
|
260
335
|
* Value - plugin object
|
|
261
336
|
* @type {Object}
|
|
262
337
|
*/
|
|
263
|
-
plugins: {
|
|
264
|
-
|
|
338
|
+
plugins: new Proxy({}, {
|
|
339
|
+
get(plugins, name) {
|
|
340
|
+
if(plugins[name] == undefined) {
|
|
341
|
+
throw ReferenceError(`code-input: Plugin '${name}' 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.`);
|
|
342
|
+
}
|
|
343
|
+
return plugins[name];
|
|
344
|
+
}
|
|
345
|
+
}),
|
|
265
346
|
|
|
266
347
|
/**
|
|
267
348
|
* Plugins are imported from the plugins folder. They will then
|
|
268
349
|
* provide custom extra functionality to code-input elements.
|
|
269
350
|
*/
|
|
270
351
|
Plugin: class {
|
|
271
|
-
|
|
352
|
+
/**
|
|
353
|
+
* Create a Plugin
|
|
354
|
+
*
|
|
355
|
+
* @param {Array<string>} observedAttributes - The HTML attributes to watch for this plugin, and report any
|
|
356
|
+
* modifications to the `codeInput.Plugin.attributeChanged` method.
|
|
357
|
+
*/
|
|
358
|
+
constructor(observedAttributes) {
|
|
272
359
|
console.log("code-input: plugin: Created plugin");
|
|
273
360
|
|
|
274
|
-
|
|
361
|
+
observedAttributes.forEach((attribute) => {
|
|
362
|
+
// Move plugin attribute to codeInput observed attributes
|
|
363
|
+
let regexFromWildcard = codeInput.wildcard2regex(attribute);
|
|
364
|
+
if(regexFromWildcard == null) {
|
|
365
|
+
// Not a wildcard
|
|
366
|
+
codeInput.observedAttributes.push(attribute);
|
|
367
|
+
} else {
|
|
368
|
+
codeInput.observedAttributes.regexp.push(regexFromWildcard);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
275
371
|
}
|
|
276
372
|
|
|
277
373
|
/**
|
|
@@ -302,11 +398,6 @@ var codeInput = {
|
|
|
302
398
|
* @param {string} newValue - The value of the attribute after it is changed
|
|
303
399
|
*/
|
|
304
400
|
attributeChanged(codeInput, name, oldValue, newValue) { }
|
|
305
|
-
/**
|
|
306
|
-
* The HTML attributes to watch for this plugin, and report any
|
|
307
|
-
* modifications to the `codeInput.Plugin.attributeChanged` method.
|
|
308
|
-
*/
|
|
309
|
-
observedAttributes = []
|
|
310
401
|
},
|
|
311
402
|
|
|
312
403
|
/* ------------------------------------
|
|
@@ -320,10 +411,35 @@ var codeInput = {
|
|
|
320
411
|
constructor() {
|
|
321
412
|
super(); // Element
|
|
322
413
|
}
|
|
414
|
+
/**
|
|
415
|
+
* Store value internally
|
|
416
|
+
*/
|
|
417
|
+
_value = '';
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Exposed child textarea element for user to input code in
|
|
421
|
+
*/
|
|
422
|
+
textareaElement = null;
|
|
423
|
+
/**
|
|
424
|
+
* Exposed child pre element where syntax-highlighted code is outputted.
|
|
425
|
+
* Contains this.codeElement as its only child.
|
|
426
|
+
*/
|
|
427
|
+
preElement = null;
|
|
428
|
+
/**
|
|
429
|
+
* Exposed child pre element's child code element where syntax-highlighted code is outputted.
|
|
430
|
+
* Has this.preElement as its parent.
|
|
431
|
+
*/
|
|
432
|
+
codeElement = null;
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Form-Associated Custom Element Callbacks
|
|
436
|
+
* https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example
|
|
437
|
+
*/
|
|
438
|
+
static formAssociated = true;
|
|
323
439
|
|
|
324
440
|
/**
|
|
325
441
|
* When events are transferred to the textarea element, callbacks
|
|
326
|
-
* are bound to set the this variable to the code-
|
|
442
|
+
* are bound to set the this variable to the code-input element
|
|
327
443
|
* rather than the textarea. This allows the callback to be converted
|
|
328
444
|
* to a bound one:
|
|
329
445
|
* Key - Callback not bound
|
|
@@ -358,6 +474,7 @@ var codeInput = {
|
|
|
358
474
|
|
|
359
475
|
/** Update the text value to the result element, after the textarea contents have changed.
|
|
360
476
|
* @param {string} value - The text value of the code-input element
|
|
477
|
+
* @param {boolean} originalUpdate - Whether this update originates from the textarea's content; if so, run it first so custom updates override it.
|
|
361
478
|
*/
|
|
362
479
|
update(value) {
|
|
363
480
|
// Prevent this from running multiple times on the same input when "value" attribute is changed,
|
|
@@ -365,13 +482,18 @@ var codeInput = {
|
|
|
365
482
|
// been run). Thank you to peterprvy for this.
|
|
366
483
|
if (this.ignoreValueUpdate) return;
|
|
367
484
|
|
|
485
|
+
if(this.textareaElement == null) {
|
|
486
|
+
this.addEventListener("code-input_load", () => { this.update(value) }); // Only run when fully loaded
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
368
490
|
this.ignoreValueUpdate = true;
|
|
369
491
|
this.value = value;
|
|
370
492
|
this.ignoreValueUpdate = false;
|
|
371
|
-
if (this.
|
|
493
|
+
if (this.textareaElement.value != value) this.textareaElement.value = value;
|
|
372
494
|
|
|
373
495
|
|
|
374
|
-
let resultElement = this.
|
|
496
|
+
let resultElement = this.codeElement;
|
|
375
497
|
|
|
376
498
|
// Handle final newlines
|
|
377
499
|
if (value[value.length - 1] == "\n") {
|
|
@@ -393,8 +515,8 @@ var codeInput = {
|
|
|
393
515
|
* Synchronise the scrolling of the textarea to the result element.
|
|
394
516
|
*/
|
|
395
517
|
syncScroll() {
|
|
396
|
-
let inputElement = this.
|
|
397
|
-
let resultElement = this.template.preElementStyled ? this.
|
|
518
|
+
let inputElement = this.textareaElement;
|
|
519
|
+
let resultElement = this.template.preElementStyled ? this.preElement : this.codeElement;
|
|
398
520
|
|
|
399
521
|
resultElement.scrollTop = inputElement.scrollTop;
|
|
400
522
|
resultElement.scrollLeft = inputElement.scrollLeft;
|
|
@@ -409,6 +531,15 @@ var codeInput = {
|
|
|
409
531
|
return text.replace(new RegExp("&", "g"), "&").replace(new RegExp("<", "g"), "<"); /* Global RegExp */
|
|
410
532
|
}
|
|
411
533
|
|
|
534
|
+
/**
|
|
535
|
+
* HTML-unescape an arbitrary string.
|
|
536
|
+
* @param {string} text - The original, HTML-escaped text
|
|
537
|
+
* @returns {string} - The new, unescaped text
|
|
538
|
+
*/
|
|
539
|
+
unescapeHtml(text) {
|
|
540
|
+
return text.replace(new RegExp("&", "g"), "&").replace(new RegExp("<", "g"), "<").replace(new RegExp(">", "g"), ">"); /* Global RegExp */
|
|
541
|
+
}
|
|
542
|
+
|
|
412
543
|
/**
|
|
413
544
|
* Get the template object this code-input element is using.
|
|
414
545
|
* @returns {Object} - Template object
|
|
@@ -438,6 +569,8 @@ var codeInput = {
|
|
|
438
569
|
* This will be called once the template has been added.
|
|
439
570
|
*/
|
|
440
571
|
setup() {
|
|
572
|
+
if(this.textareaElement != null) return; // Already set up
|
|
573
|
+
|
|
441
574
|
this.classList.add("code-input_registered"); // Remove register message
|
|
442
575
|
if (this.template.preElementStyled) this.classList.add("code-input_pre-element-styled");
|
|
443
576
|
|
|
@@ -446,32 +579,52 @@ var codeInput = {
|
|
|
446
579
|
// First-time attribute sync
|
|
447
580
|
let lang = this.getAttribute("lang");
|
|
448
581
|
let placeholder = this.getAttribute("placeholder") || this.getAttribute("lang") || "";
|
|
449
|
-
let value = this.
|
|
582
|
+
let value = this.unescapeHtml(this.innerHTML) || this.getAttribute("value") || "";
|
|
583
|
+
// Value attribute deprecated, but included for compatibility
|
|
450
584
|
|
|
451
|
-
this.
|
|
585
|
+
this.initialValue = value; // For form reset
|
|
452
586
|
|
|
453
587
|
// Create textarea
|
|
454
588
|
let textarea = document.createElement("textarea");
|
|
455
589
|
textarea.placeholder = placeholder;
|
|
456
|
-
|
|
590
|
+
if(value != "") {
|
|
591
|
+
textarea.value = value;
|
|
592
|
+
}
|
|
593
|
+
textarea.innerHTML = this.innerHTML;
|
|
457
594
|
textarea.setAttribute("spellcheck", "false");
|
|
458
595
|
|
|
596
|
+
this.innerHTML = ""; // Clear Content
|
|
597
|
+
|
|
459
598
|
// Synchronise attributes to textarea
|
|
460
599
|
codeInput.textareaSyncAttributes.forEach((attribute) => {
|
|
461
600
|
if (this.hasAttribute(attribute)) {
|
|
462
601
|
textarea.setAttribute(attribute, this.getAttribute(attribute));
|
|
463
602
|
}
|
|
464
603
|
});
|
|
604
|
+
codeInput.textareaSyncAttributes.regexp.forEach((reg) =>
|
|
605
|
+
{
|
|
606
|
+
for(const attr of this.attributes) {
|
|
607
|
+
if (attr.nodeName.match(reg)) {
|
|
608
|
+
textarea.setAttribute(attr.nodeName, attr.nodeValue);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
});
|
|
465
612
|
|
|
466
613
|
textarea.addEventListener('input', (evt) => { textarea.parentElement.update(textarea.value); textarea.parentElement.sync_scroll(); });
|
|
467
614
|
textarea.addEventListener('scroll', (evt) => textarea.parentElement.sync_scroll());
|
|
468
615
|
|
|
616
|
+
// Save element internally
|
|
617
|
+
this.textareaElement = textarea;
|
|
469
618
|
this.append(textarea);
|
|
470
619
|
|
|
471
620
|
// Create result element
|
|
472
621
|
let code = document.createElement("code");
|
|
473
622
|
let pre = document.createElement("pre");
|
|
474
623
|
pre.setAttribute("aria-hidden", "true"); // Hide for screen readers
|
|
624
|
+
|
|
625
|
+
// Save elements internally
|
|
626
|
+
this.preElement = pre;
|
|
627
|
+
this.codeElement = code;
|
|
475
628
|
pre.append(code);
|
|
476
629
|
this.append(pre);
|
|
477
630
|
|
|
@@ -484,6 +637,8 @@ var codeInput = {
|
|
|
484
637
|
this.pluginEvt("afterElementsAdded");
|
|
485
638
|
|
|
486
639
|
this.update(value);
|
|
640
|
+
|
|
641
|
+
this.dispatchEvent(new CustomEvent("code-input_load"));
|
|
487
642
|
}
|
|
488
643
|
|
|
489
644
|
/**
|
|
@@ -520,20 +675,49 @@ var codeInput = {
|
|
|
520
675
|
*/
|
|
521
676
|
connectedCallback() {
|
|
522
677
|
this.template = this.getTemplate();
|
|
523
|
-
if (this.template != undefined)
|
|
678
|
+
if (this.template != undefined) {
|
|
679
|
+
this.classList.add("code-input_registered");
|
|
680
|
+
codeInput.runOnceWindowLoaded(() => {
|
|
681
|
+
this.setup();
|
|
682
|
+
this.classList.add("code-input_loaded");
|
|
683
|
+
}, this);
|
|
684
|
+
}
|
|
685
|
+
this.mutationObserver = new MutationObserver(this.mutationObserverCallback.bind(this));
|
|
686
|
+
this.mutationObserver.observe(this, {
|
|
687
|
+
attributes: true,
|
|
688
|
+
attributeOldValue: true
|
|
689
|
+
});
|
|
524
690
|
}
|
|
525
691
|
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
692
|
+
mutationObserverCallback(mutationList, observer) {
|
|
693
|
+
for (const mutation of mutationList) {
|
|
694
|
+
if (mutation.type !== 'attributes')
|
|
695
|
+
continue;
|
|
696
|
+
|
|
697
|
+
/* Check regular attributes */
|
|
698
|
+
for(let i = 0; i < codeInput.observedAttributes.length; i++) {
|
|
699
|
+
if (mutation.attributeName == codeInput.observedAttributes[i]) {
|
|
700
|
+
return this.attributeChangedCallback(mutation.attributeName, mutation.oldValue, super.getAttribute(mutation.attributeName));
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/* Check wildcard attributes */
|
|
705
|
+
for(let i = 0; i < codeInput.observedAttributes.regexp.length; i++) {
|
|
706
|
+
const reg = codeInput.observedAttributes.regexp[i];
|
|
707
|
+
if (mutation.attributeName.match(reg)) {
|
|
708
|
+
return this.attributeChangedCallback(mutation.attributeName, mutation.oldValue, super.getAttribute(mutation.attributeName));
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
disconnectedCallback() {
|
|
715
|
+
this.mutationObserver.disconnect();
|
|
532
716
|
}
|
|
533
717
|
|
|
534
718
|
/**
|
|
535
|
-
* Triggered when an HTML attribute
|
|
536
|
-
* has been modified.
|
|
719
|
+
* Triggered when an observed HTML attribute
|
|
720
|
+
* has been modified (called from `mutationObserverCallback`).
|
|
537
721
|
* @param {string} name - The name of the attribute
|
|
538
722
|
* @param {string} oldValue - The value of the attribute before it was changed
|
|
539
723
|
* @param {string} newValue - The value of the attribute after it is changed
|
|
@@ -544,10 +728,10 @@ var codeInput = {
|
|
|
544
728
|
switch (name) {
|
|
545
729
|
|
|
546
730
|
case "value":
|
|
547
|
-
this.
|
|
731
|
+
this.value = newValue;
|
|
548
732
|
break;
|
|
549
733
|
case "placeholder":
|
|
550
|
-
this.
|
|
734
|
+
this.textareaElement.placeholder = newValue;
|
|
551
735
|
break;
|
|
552
736
|
case "template":
|
|
553
737
|
this.template = codeInput.usedTemplates[newValue || codeInput.defaultTemplate];
|
|
@@ -560,8 +744,8 @@ var codeInput = {
|
|
|
560
744
|
|
|
561
745
|
case "lang":
|
|
562
746
|
|
|
563
|
-
let code = this.
|
|
564
|
-
let mainTextarea = this.
|
|
747
|
+
let code = this.codeElement;
|
|
748
|
+
let mainTextarea = this.textareaElement;
|
|
565
749
|
|
|
566
750
|
// Check not already updated
|
|
567
751
|
if (newValue != null) {
|
|
@@ -576,14 +760,14 @@ var codeInput = {
|
|
|
576
760
|
|
|
577
761
|
// Remove old language class and add new
|
|
578
762
|
console.log("code-input: Language: REMOVE", "language-" + oldValue);
|
|
579
|
-
code.classList.remove("language-" + oldValue); // From
|
|
580
|
-
code.parentElement.classList.remove("language-" + oldValue); // From
|
|
763
|
+
code.classList.remove("language-" + oldValue); // From codeElement
|
|
764
|
+
code.parentElement.classList.remove("language-" + oldValue); // From preElement
|
|
581
765
|
code.classList.remove("language-none"); // Prism
|
|
582
766
|
code.parentElement.classList.remove("language-none"); // Prism
|
|
583
767
|
|
|
584
768
|
if (newValue != undefined && newValue != "") {
|
|
585
769
|
code.classList.add("language-" + newValue);
|
|
586
|
-
console.log("code-input: Language:ADD", "language-" + newValue);
|
|
770
|
+
console.log("code-input: Language: ADD", "language-" + newValue);
|
|
587
771
|
}
|
|
588
772
|
|
|
589
773
|
if (mainTextarea.placeholder == oldValue) mainTextarea.placeholder = newValue;
|
|
@@ -593,7 +777,21 @@ var codeInput = {
|
|
|
593
777
|
break;
|
|
594
778
|
default:
|
|
595
779
|
if (codeInput.textareaSyncAttributes.includes(name)) {
|
|
596
|
-
|
|
780
|
+
if(newValue == null) {
|
|
781
|
+
this.textareaElement.removeAttribute(name);
|
|
782
|
+
} else {
|
|
783
|
+
this.textareaElement.setAttribute(name, newValue);
|
|
784
|
+
}
|
|
785
|
+
} else {
|
|
786
|
+
codeInput.textareaSyncAttributes.regexp.forEach((attribute) => {
|
|
787
|
+
if (name.match(attribute)) {
|
|
788
|
+
if(newValue == null) {
|
|
789
|
+
this.textareaElement.removeAttribute(name);
|
|
790
|
+
} else {
|
|
791
|
+
this.textareaElement.setAttribute(name, newValue);
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
});
|
|
597
795
|
}
|
|
598
796
|
break;
|
|
599
797
|
}
|
|
@@ -616,9 +814,17 @@ var codeInput = {
|
|
|
616
814
|
|
|
617
815
|
if (codeInput.textareaSyncEvents.includes(type)) {
|
|
618
816
|
if (options === undefined) {
|
|
619
|
-
this.
|
|
817
|
+
if(this.textareaElement == null) {
|
|
818
|
+
this.addEventListener("code-input_load", () => { this.textareaElement.addEventListener(type, boundCallback); });
|
|
819
|
+
} else {
|
|
820
|
+
this.textareaElement.addEventListener(type, boundCallback);
|
|
821
|
+
}
|
|
620
822
|
} else {
|
|
621
|
-
this.
|
|
823
|
+
if(this.textareaElement == null) {
|
|
824
|
+
this.addEventListener("code-input_load", () => { this.textareaElement.addEventListener(type, boundCallback, options); });
|
|
825
|
+
} else {
|
|
826
|
+
this.textareaElement.addEventListener(type, boundCallback, options);
|
|
827
|
+
}
|
|
622
828
|
}
|
|
623
829
|
} else {
|
|
624
830
|
if (options === undefined) {
|
|
@@ -636,15 +842,15 @@ var codeInput = {
|
|
|
636
842
|
let boundCallback = this.boundEventCallbacks[listener];
|
|
637
843
|
if (type == "change") {
|
|
638
844
|
if (options === null) {
|
|
639
|
-
this.
|
|
845
|
+
this.textareaElement.removeEventListener("change", boundCallback);
|
|
640
846
|
} else {
|
|
641
|
-
this.
|
|
847
|
+
this.textareaElement.removeEventListener("change", boundCallback, options);
|
|
642
848
|
}
|
|
643
849
|
} else if (type == "selectionchange") {
|
|
644
850
|
if (options === null) {
|
|
645
|
-
this.
|
|
851
|
+
this.textareaElement.removeEventListener("selectionchange", boundCallback);
|
|
646
852
|
} else {
|
|
647
|
-
this.
|
|
853
|
+
this.textareaElement.removeEventListener("selectionchange", boundCallback, options);
|
|
648
854
|
}
|
|
649
855
|
} else {
|
|
650
856
|
super.removeEventListener(type, listener, options);
|
|
@@ -655,14 +861,19 @@ var codeInput = {
|
|
|
655
861
|
* Get the text contents of the code-input element.
|
|
656
862
|
*/
|
|
657
863
|
get value() {
|
|
658
|
-
return this.
|
|
864
|
+
return this._value;
|
|
659
865
|
}
|
|
660
866
|
/**
|
|
661
867
|
* Set the text contents of the code-input element.
|
|
662
868
|
* @param {string} val - New text contents
|
|
663
869
|
*/
|
|
664
870
|
set value(val) {
|
|
665
|
-
|
|
871
|
+
if (val === null || val === undefined) {
|
|
872
|
+
val = "";
|
|
873
|
+
}
|
|
874
|
+
this._value = val;
|
|
875
|
+
this.update(val);
|
|
876
|
+
return val;
|
|
666
877
|
}
|
|
667
878
|
|
|
668
879
|
/**
|
|
@@ -687,7 +898,7 @@ var codeInput = {
|
|
|
687
898
|
* See `HTMLTextAreaElement.validity`
|
|
688
899
|
*/
|
|
689
900
|
get validity() {
|
|
690
|
-
return this.
|
|
901
|
+
return this.textareaElement.validity;
|
|
691
902
|
}
|
|
692
903
|
|
|
693
904
|
/**
|
|
@@ -698,7 +909,7 @@ var codeInput = {
|
|
|
698
909
|
* See `HTMLTextAreaElement.validationMessage`
|
|
699
910
|
*/
|
|
700
911
|
get validationMessage() {
|
|
701
|
-
return this.
|
|
912
|
+
return this.textareaElement.validationMessage;
|
|
702
913
|
}
|
|
703
914
|
|
|
704
915
|
/**
|
|
@@ -708,7 +919,7 @@ var codeInput = {
|
|
|
708
919
|
* @param error Sets a custom error message that is displayed when a form is submitted.
|
|
709
920
|
*/
|
|
710
921
|
setCustomValidity(error) {
|
|
711
|
-
return this.
|
|
922
|
+
return this.textareaElement.setCustomValidity(error);
|
|
712
923
|
}
|
|
713
924
|
|
|
714
925
|
/**
|
|
@@ -718,14 +929,14 @@ var codeInput = {
|
|
|
718
929
|
* See `HTMLTextAreaElement.checkValidity`
|
|
719
930
|
*/
|
|
720
931
|
checkValidity() {
|
|
721
|
-
return this.
|
|
932
|
+
return this.textareaElement.checkValidity();
|
|
722
933
|
}
|
|
723
934
|
|
|
724
935
|
/**
|
|
725
936
|
* See `HTMLTextAreaElement.reportValidity`
|
|
726
937
|
*/
|
|
727
938
|
reportValidity() {
|
|
728
|
-
return this.
|
|
939
|
+
return this.textareaElement.reportValidity();
|
|
729
940
|
}
|
|
730
941
|
|
|
731
942
|
|
|
@@ -734,17 +945,19 @@ var codeInput = {
|
|
|
734
945
|
*/
|
|
735
946
|
setAttribute(qualifiedName, value) {
|
|
736
947
|
super.setAttribute(qualifiedName, value); // code-input
|
|
737
|
-
this.
|
|
948
|
+
if(this.textareaElement != null) {
|
|
949
|
+
this.textareaElement.setAttribute(qualifiedName, value); // textarea
|
|
950
|
+
}
|
|
738
951
|
}
|
|
739
952
|
|
|
740
953
|
/**
|
|
741
954
|
* @override
|
|
742
955
|
*/
|
|
743
956
|
getAttribute(qualifiedName) {
|
|
744
|
-
if (this.
|
|
957
|
+
if (this.textareaElement == null) {
|
|
745
958
|
return super.getAttribute(qualifiedName);
|
|
746
959
|
}
|
|
747
|
-
return this.
|
|
960
|
+
return this.textareaElement.getAttribute(qualifiedName); // textarea
|
|
748
961
|
}
|
|
749
962
|
|
|
750
963
|
/**
|
|
@@ -753,7 +966,79 @@ var codeInput = {
|
|
|
753
966
|
* Value - object of data to be stored; different plugins may use this differently.
|
|
754
967
|
*/
|
|
755
968
|
pluginData = {};
|
|
756
|
-
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* Update value on form reset
|
|
972
|
+
*/
|
|
973
|
+
formResetCallback() {
|
|
974
|
+
this.update(this.initialValue);
|
|
975
|
+
};
|
|
976
|
+
},
|
|
977
|
+
|
|
978
|
+
arrayWildcards2regex(list) {
|
|
979
|
+
for(let i = 0; i < list.length; i++) {
|
|
980
|
+
const name = list[i];
|
|
981
|
+
if (name.indexOf("*") < 0)
|
|
982
|
+
continue;
|
|
983
|
+
|
|
984
|
+
list.regexp.push(new RegExp("^" +
|
|
985
|
+
name.replace(/[/\-\\^$+?.()|[\]{}]/g, '\\$&')
|
|
986
|
+
.replace("*", ".*")
|
|
987
|
+
+ "$", "i"));
|
|
988
|
+
list.splice(i--, 1);
|
|
989
|
+
};
|
|
990
|
+
},
|
|
991
|
+
|
|
992
|
+
wildcard2regex(wildcard) {
|
|
993
|
+
if (wildcard.indexOf("*") < 0)
|
|
994
|
+
return null;
|
|
995
|
+
|
|
996
|
+
return new RegExp("^" +
|
|
997
|
+
wildcard.replace(/[/\-\\^$+?.()|[\]{}]/g, '\\$&')
|
|
998
|
+
.replace("*", ".*")
|
|
999
|
+
+ "$", "i");
|
|
1000
|
+
},
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* To ensure the DOM is ready, run this callback after the window
|
|
1004
|
+
* has loaded (or now if it has already loaded)
|
|
1005
|
+
*/
|
|
1006
|
+
runOnceWindowLoaded(callback, codeInputElem) {
|
|
1007
|
+
if(codeInput.windowLoaded) {
|
|
1008
|
+
callback(); // Fully loaded
|
|
1009
|
+
} else {
|
|
1010
|
+
window.addEventListener("load", callback);
|
|
1011
|
+
}
|
|
1012
|
+
},
|
|
1013
|
+
windowLoaded: false
|
|
1014
|
+
}
|
|
1015
|
+
window.addEventListener("load", function() {
|
|
1016
|
+
codeInput.windowLoaded = true;
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* convert wildcards into regex
|
|
1022
|
+
*/
|
|
1023
|
+
|
|
1024
|
+
{
|
|
1025
|
+
Object.defineProperty(codeInput.textareaSyncAttributes, 'regexp', {
|
|
1026
|
+
value: [],
|
|
1027
|
+
writable: false,
|
|
1028
|
+
enumerable: false,
|
|
1029
|
+
configurable: false
|
|
1030
|
+
});
|
|
1031
|
+
codeInput.observedAttributes = codeInput.observedAttributes.concat(codeInput.textareaSyncAttributes);
|
|
1032
|
+
|
|
1033
|
+
Object.defineProperty(codeInput.observedAttributes, 'regexp', {
|
|
1034
|
+
value: [],
|
|
1035
|
+
writable: false,
|
|
1036
|
+
enumerable: false,
|
|
1037
|
+
configurable: false
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
codeInput.arrayWildcards2regex(codeInput.textareaSyncAttributes);
|
|
1041
|
+
codeInput.arrayWildcards2regex(codeInput.observedAttributes);
|
|
757
1042
|
}
|
|
758
1043
|
|
|
759
|
-
customElements.define("code-input", codeInput.CodeInput);
|
|
1044
|
+
customElements.define("code-input", codeInput.CodeInput);
|