@webcoder49/code-input 2.6.0 → 2.6.2
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/docs/_index.md +1 -1
- package/esm/code-input.d.mts +154 -0
- package/esm/code-input.mjs +995 -0
- package/esm/plugins/auto-close-brackets.d.mts +15 -0
- package/esm/plugins/auto-close-brackets.mjs +84 -0
- package/esm/plugins/autocomplete.d.mts +14 -0
- package/esm/plugins/autocomplete.mjs +93 -0
- package/esm/plugins/autodetect.d.mts +11 -0
- package/esm/plugins/autodetect.mjs +35 -0
- package/esm/plugins/find-and-replace.d.mts +43 -0
- package/esm/plugins/find-and-replace.mjs +777 -0
- package/esm/plugins/go-to-line.d.mts +29 -0
- package/esm/plugins/go-to-line.mjs +217 -0
- package/esm/plugins/indent.d.mts +22 -0
- package/esm/plugins/indent.mjs +359 -0
- package/esm/plugins/select-token-callbacks.d.mts +51 -0
- package/esm/plugins/select-token-callbacks.mjs +296 -0
- package/esm/plugins/special-chars.d.mts +25 -0
- package/esm/plugins/special-chars.mjs +207 -0
- package/esm/plugins/test.d.mts +16 -0
- package/esm/plugins/test.mjs +56 -0
- package/esm/templates/hljs.d.mts +16 -0
- package/esm/templates/hljs.mjs +28 -0
- package/esm/templates/prism.d.mts +16 -0
- package/esm/templates/prism.mjs +25 -0
- package/package.json +1 -1
- package/esm/.code-input.mjs.kate-swp +0 -0
package/esm/code-input.mjs
CHANGED
|
@@ -1,2 +1,997 @@
|
|
|
1
1
|
// NOTICE: This code is @generated from code outside the esm directory. Please do not edit it to contribute!
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* **code-input** is a library which lets you create custom HTML `<code-input>`
|
|
5
|
+
* elements that act like `<textarea>` elements but support syntax-highlighted
|
|
6
|
+
* code, implemented using any typical syntax highlighting library.
|
|
7
|
+
*
|
|
8
|
+
* License of whole library for bundlers:
|
|
9
|
+
*
|
|
10
|
+
* Copyright 2021-2025 Oliver Geer and contributors
|
|
11
|
+
* @license MIT
|
|
12
|
+
*
|
|
13
|
+
* **<https://code-input-js.org>**
|
|
14
|
+
*/
|
|
15
|
+
"use strict";
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
var codeInput = {
|
|
19
|
+
/**
|
|
20
|
+
* A list of attributes that will trigger the
|
|
21
|
+
* `codeInput.CodeInput.attributeChangedCallback`
|
|
22
|
+
* when modified in a code-input element. This
|
|
23
|
+
* does not include events, which are handled in
|
|
24
|
+
* `codeInput.CodeInput.addEventListener` and
|
|
25
|
+
* `codeInput.CodeInput.removeEventListener`.
|
|
26
|
+
*/
|
|
27
|
+
observedAttributes: [
|
|
28
|
+
"value",
|
|
29
|
+
"placeholder",
|
|
30
|
+
"language",
|
|
31
|
+
"lang",
|
|
32
|
+
"template"
|
|
33
|
+
],
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* A list of attributes that will be moved to
|
|
37
|
+
* the textarea after they are applied on the
|
|
38
|
+
* code-input element.
|
|
39
|
+
*/
|
|
40
|
+
textareaSyncAttributes: [
|
|
41
|
+
"value",
|
|
42
|
+
// Form validation - https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#using_built-in_form_validation
|
|
43
|
+
"min", "max",
|
|
44
|
+
"type",
|
|
45
|
+
"pattern",
|
|
46
|
+
|
|
47
|
+
// Source: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea
|
|
48
|
+
"autocomplete",
|
|
49
|
+
"autocorrect",
|
|
50
|
+
"autofocus",
|
|
51
|
+
"cols",
|
|
52
|
+
"dirname",
|
|
53
|
+
"disabled",
|
|
54
|
+
"form",
|
|
55
|
+
"maxlength",
|
|
56
|
+
"minlength",
|
|
57
|
+
"name",
|
|
58
|
+
"placeholder",
|
|
59
|
+
"readonly",
|
|
60
|
+
"required",
|
|
61
|
+
"rows",
|
|
62
|
+
"spellcheck",
|
|
63
|
+
"wrap"
|
|
64
|
+
],
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A list of events whose listeners will be moved to
|
|
68
|
+
* the textarea after they are added to the
|
|
69
|
+
* code-input element.
|
|
70
|
+
*/
|
|
71
|
+
textareaSyncEvents: [
|
|
72
|
+
"change",
|
|
73
|
+
"selectionchange",
|
|
74
|
+
"invalid",
|
|
75
|
+
"input",
|
|
76
|
+
"focus",
|
|
77
|
+
"blur",
|
|
78
|
+
"focusin",
|
|
79
|
+
"focusout"
|
|
80
|
+
],
|
|
81
|
+
|
|
82
|
+
/* ------------------------------------
|
|
83
|
+
* ------------Templates---------------
|
|
84
|
+
* ------------------------------------ */
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The templates currently available for any code-input elements
|
|
88
|
+
* to use. Registered using `codeInput.registerTemplate`.
|
|
89
|
+
* Key - Template Name
|
|
90
|
+
* Value - A Template Object
|
|
91
|
+
* @type {Object}
|
|
92
|
+
*/
|
|
93
|
+
usedTemplates: {
|
|
94
|
+
},
|
|
95
|
+
/**
|
|
96
|
+
* The name of the default template that a code-input element that
|
|
97
|
+
* does not specify the template attribute uses.
|
|
98
|
+
* @type {string}
|
|
99
|
+
*/
|
|
100
|
+
defaultTemplate: undefined,
|
|
101
|
+
/**
|
|
102
|
+
* A queue of elements waiting for a template to be registered,
|
|
103
|
+
* allowing elements to be created in HTML with a template before
|
|
104
|
+
* the template is registered in JS, for ease of use.
|
|
105
|
+
* Key - Template Name
|
|
106
|
+
* Value - An array of code-input elements
|
|
107
|
+
* @type {Object}
|
|
108
|
+
*/
|
|
109
|
+
templateNotYetRegisteredQueue: {},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Register a template so code-input elements with a template attribute that equals the templateName will use the template.
|
|
113
|
+
* See `codeInput.templates` for constructors to create templates.
|
|
114
|
+
* @param {string} templateName - the name to register the template under
|
|
115
|
+
* @param {Object} template - a Template object instance - see `codeInput.templates`
|
|
116
|
+
*/
|
|
117
|
+
registerTemplate: function (templateName, template) {
|
|
118
|
+
if(!(typeof templateName == "string" || templateName instanceof String)) throw TypeError(`code-input: Name of template "${templateName}" must be a string.`);
|
|
119
|
+
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.`);
|
|
120
|
+
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.`);
|
|
121
|
+
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.`);
|
|
122
|
+
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.`);
|
|
123
|
+
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.`);
|
|
124
|
+
|
|
125
|
+
template.plugins.forEach((plugin, i) => {
|
|
126
|
+
if(!(plugin instanceof codeInput.Plugin)) {
|
|
127
|
+
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.`);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
codeInput.usedTemplates[templateName] = template;
|
|
133
|
+
// Add waiting code-input elements wanting this template from queue
|
|
134
|
+
if (templateName in codeInput.templateNotYetRegisteredQueue) {
|
|
135
|
+
for (let i in codeInput.templateNotYetRegisteredQueue[templateName]) {
|
|
136
|
+
const elem = codeInput.templateNotYetRegisteredQueue[templateName][i];
|
|
137
|
+
elem.templateObject = template;
|
|
138
|
+
elem.connectedCallback();
|
|
139
|
+
// Bind sets elem as first parameter of function
|
|
140
|
+
// So innerHTML can be read
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (codeInput.defaultTemplate == undefined) {
|
|
145
|
+
codeInput.defaultTemplate = templateName;
|
|
146
|
+
// Add elements with default template from queue
|
|
147
|
+
if (undefined in codeInput.templateNotYetRegisteredQueue) {
|
|
148
|
+
for (let i in codeInput.templateNotYetRegisteredQueue[undefined]) {
|
|
149
|
+
const elem = codeInput.templateNotYetRegisteredQueue[undefined][i];
|
|
150
|
+
elem.templateObject = template;
|
|
151
|
+
elem.connectedCallback();
|
|
152
|
+
// Bind sets elem as first parameter of function
|
|
153
|
+
// So innerHTML can be read
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Please see `codeInput.templates.prism` or `codeInput.templates.hljs`.
|
|
161
|
+
* Templates are used in `<code-input>` elements and once registered with
|
|
162
|
+
* `codeInput.registerTemplate` will be in charge of the highlighting
|
|
163
|
+
* algorithm and settings for all code-inputs with a `template` attribute
|
|
164
|
+
* matching the registered name.
|
|
165
|
+
*/
|
|
166
|
+
Template: class {
|
|
167
|
+
/**
|
|
168
|
+
* Constructor to create a custom template instance. Pass this into `codeInput.registerTemplate` to use it.
|
|
169
|
+
* I would strongly recommend using the built-in simpler template `codeInput.templates.prism` or `codeInput.templates.hljs`.
|
|
170
|
+
* @param {(codeElement: HTMLCodeElement, codeInput?: codeInput.CodeInput) => void} highlight - a callback to highlight the code, that takes an HTML `<code>` element inside a `<pre>` element as a parameter
|
|
171
|
+
* @param {boolean} preElementStyled - is the `<pre>` element CSS-styled (if so set to true), or the `<code>` element (false)?
|
|
172
|
+
* @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]'.
|
|
173
|
+
* @param {boolean} includeCodeInputInHighlightFunc - Setting this to true passes the `<code-input>` element as a second argument to the highlight function.
|
|
174
|
+
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.Plugin`
|
|
175
|
+
* @returns {codeInput.Template} template object
|
|
176
|
+
*/
|
|
177
|
+
constructor(highlight = function (codeElement) { }, preElementStyled = true, isCode = true, includeCodeInputInHighlightFunc = false, plugins = []) {
|
|
178
|
+
this.highlight = highlight;
|
|
179
|
+
this.preElementStyled = preElementStyled;
|
|
180
|
+
this.isCode = isCode;
|
|
181
|
+
this.includeCodeInputInHighlightFunc = includeCodeInputInHighlightFunc;
|
|
182
|
+
this.plugins = plugins;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* A callback to highlight the code, that takes an HTML `<code>` element
|
|
187
|
+
* inside a `<pre>` element as a parameter, and an optional second
|
|
188
|
+
* `<code-input>` element parameter if `this.includeCodeInputInHighlightFunc` is
|
|
189
|
+
* `true`.
|
|
190
|
+
*/
|
|
191
|
+
highlight = function(codeElement) {};
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Is the <pre> element CSS-styled as well as the `<code>` element?
|
|
195
|
+
* If `true`, `<pre>` element's scrolling is synchronised; if false,
|
|
196
|
+
* <code> element's scrolling is synchronised.
|
|
197
|
+
*/
|
|
198
|
+
preElementStyled = true;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Is this for writing code?
|
|
202
|
+
* If true, the code-input's lang HTML attribute can be used,
|
|
203
|
+
* and the `<code>` element will be given the class name
|
|
204
|
+
* 'language-[lang attribute's value]'.
|
|
205
|
+
*/
|
|
206
|
+
isCode = true;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Setting this to true passes the `<code-input>` element as a
|
|
210
|
+
* second argument to the highlight function.
|
|
211
|
+
*/
|
|
212
|
+
includeCodeInputInHighlightFunc = false;
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* An array of plugin objects to add extra features -
|
|
216
|
+
* see `codeInput.Plugin`.
|
|
217
|
+
*/
|
|
218
|
+
plugins = [];
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
/* ------------------------------------
|
|
223
|
+
* ------------Plugins-----------------
|
|
224
|
+
* ------------------------------------ */
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Before using any plugin in this namespace, please ensure you import its JavaScript
|
|
228
|
+
* files (in the plugins folder), or continue to get a more detailed error in the developer
|
|
229
|
+
* console.
|
|
230
|
+
*
|
|
231
|
+
* Where plugins are stored, after they are imported. The plugin
|
|
232
|
+
* file assigns them a space in this object.
|
|
233
|
+
* For adding completely new syntax-highlighting algorithms, please see `codeInput.templates`.
|
|
234
|
+
*
|
|
235
|
+
* Key - plugin name
|
|
236
|
+
*
|
|
237
|
+
* Value - plugin object
|
|
238
|
+
* @type {Object}
|
|
239
|
+
*/
|
|
240
|
+
plugins: new Proxy({}, {
|
|
241
|
+
get(plugins, name) {
|
|
242
|
+
if(plugins[name] == undefined) {
|
|
243
|
+
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.`);
|
|
244
|
+
}
|
|
245
|
+
return plugins[name];
|
|
246
|
+
}
|
|
247
|
+
}),
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Plugins are imported from the plugins folder. They will then
|
|
251
|
+
* provide custom extra functionality to code-input elements.
|
|
252
|
+
*/
|
|
253
|
+
Plugin: class {
|
|
254
|
+
/**
|
|
255
|
+
* Create a Plugin
|
|
256
|
+
*
|
|
257
|
+
* @param {Array<string>} observedAttributes - The HTML attributes to watch for this plugin, and report any
|
|
258
|
+
* modifications to the `codeInput.Plugin.attributeChanged` method.
|
|
259
|
+
*/
|
|
260
|
+
constructor(observedAttributes) {
|
|
261
|
+
|
|
262
|
+
observedAttributes.forEach((attribute) => {
|
|
263
|
+
codeInput.observedAttributes.push(attribute);
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Replace the values in destination with those from source where the keys match, in-place.
|
|
269
|
+
* @param {Object} destination Where to place the translated strings, already filled with the keys pointing to English strings.
|
|
270
|
+
* @param {Object} source The same keys, or some of them, mapped to translated strings. Keys not present here will retain the values they are maapped to in destination.
|
|
271
|
+
*/
|
|
272
|
+
addTranslations(destination, source) {
|
|
273
|
+
for(const key in source) {
|
|
274
|
+
destination[key] = source[key];
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Runs before code is highlighted.
|
|
280
|
+
* @param {codeInput.CodeInput} codeInput - The codeInput element
|
|
281
|
+
*/
|
|
282
|
+
beforeHighlight(codeInput) { }
|
|
283
|
+
/**
|
|
284
|
+
* Runs after code is highlighted.
|
|
285
|
+
* @param {codeInput.CodeInput} codeInput - The codeInput element
|
|
286
|
+
*/
|
|
287
|
+
afterHighlight(codeInput) { }
|
|
288
|
+
/**
|
|
289
|
+
* Runs before elements are added into a code-input element.
|
|
290
|
+
* @param {codeInput.CodeInput} codeInput - The codeInput element
|
|
291
|
+
*/
|
|
292
|
+
beforeElementsAdded(codeInput) { }
|
|
293
|
+
/**
|
|
294
|
+
* Runs after elements are added into a code-input element (useful for adding events to the textarea).
|
|
295
|
+
* @param {codeInput.CodeInput} codeInput - The codeInput element
|
|
296
|
+
*/
|
|
297
|
+
afterElementsAdded(codeInput) { }
|
|
298
|
+
/**
|
|
299
|
+
* Runs when an attribute of a code-input element is changed (you must add the attribute name to `codeInput.Plugin.observedAttributes` first).
|
|
300
|
+
* @param {codeInput.CodeInput} codeInput - The codeInput element
|
|
301
|
+
* @param {string} name - The name of the attribute
|
|
302
|
+
* @param {string} oldValue - The value of the attribute before it was changed
|
|
303
|
+
* @param {string} newValue - The value of the attribute after it is changed
|
|
304
|
+
*/
|
|
305
|
+
attributeChanged(codeInput, name, oldValue, newValue) { }
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
/* ------------------------------------
|
|
309
|
+
* -------------Main-------------------
|
|
310
|
+
* ------------------------------------ */
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* A `<code-input>` element, an instance of an `HTMLElement`, and the result
|
|
314
|
+
* of `document.createElement("code-input")`. Attributes are only set when
|
|
315
|
+
* the element's template has been registered, and before this are null.
|
|
316
|
+
*/
|
|
317
|
+
CodeInput: class extends HTMLElement {
|
|
318
|
+
constructor() {
|
|
319
|
+
super(); // Element
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* When the code-input's template is registered, this contains its codeInput.Template object.
|
|
324
|
+
* Should be treated as read-only by external code.
|
|
325
|
+
*/
|
|
326
|
+
templateObject = null;
|
|
327
|
+
/**
|
|
328
|
+
* Exposed child textarea element for user to input code in
|
|
329
|
+
*/
|
|
330
|
+
textareaElement = null;
|
|
331
|
+
/**
|
|
332
|
+
* Exposed child pre element where syntax-highlighted code is outputted.
|
|
333
|
+
* Contains this.codeElement as its only child.
|
|
334
|
+
*/
|
|
335
|
+
preElement = null
|
|
336
|
+
/**
|
|
337
|
+
* Exposed child pre element's child code element where syntax-highlighted code is outputted.
|
|
338
|
+
* Has this.preElement as its parent.
|
|
339
|
+
*/
|
|
340
|
+
codeElement = null;
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Exposed non-scrolling element designed to contain dialog boxes etc. from plugins,
|
|
344
|
+
* that shouldn't scroll with the code-input element.
|
|
345
|
+
*/
|
|
346
|
+
dialogContainerElement = null;
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Form-Associated Custom Element Callbacks
|
|
350
|
+
* https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements-face-example
|
|
351
|
+
*/
|
|
352
|
+
static formAssociated = true;
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* When events are transferred to the textarea element, callbacks
|
|
356
|
+
* are bound to set the this variable to the code-input element
|
|
357
|
+
* rather than the textarea. This allows the callback to be converted
|
|
358
|
+
* to a bound one:
|
|
359
|
+
* Key - Callback not bound
|
|
360
|
+
* Value - Callback that is bound, with this equalling the code-input element in the callback
|
|
361
|
+
*/
|
|
362
|
+
boundEventCallbacks = {};
|
|
363
|
+
|
|
364
|
+
/** Trigger this event in all plugins with a optional list of arguments
|
|
365
|
+
* @param {string} eventName - the name of the event to trigger
|
|
366
|
+
* @param {Array} args - the arguments to pass into the event callback in the template after the code-input element. Normally left empty
|
|
367
|
+
*/
|
|
368
|
+
pluginEvt(eventName, args) {
|
|
369
|
+
for (let i in this.templateObject.plugins) {
|
|
370
|
+
let plugin = this.templateObject.plugins[i];
|
|
371
|
+
if (eventName in plugin) {
|
|
372
|
+
if (args === undefined) {
|
|
373
|
+
plugin[eventName](this);
|
|
374
|
+
} else {
|
|
375
|
+
plugin[eventName](this, ...args);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* ------------------------------------
|
|
382
|
+
* ----------Main Functionality--------
|
|
383
|
+
* ------------------------------------
|
|
384
|
+
* The main function of a code-input element is to take
|
|
385
|
+
* code written in its textarea element, copy this code into
|
|
386
|
+
* the result (pre code) element, then use the template object
|
|
387
|
+
* to syntax-highlight it. */
|
|
388
|
+
|
|
389
|
+
needsHighlight = false; // Just inputted
|
|
390
|
+
originalAriaDescription;
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Highlight the code as soon as possible
|
|
394
|
+
*/
|
|
395
|
+
scheduleHighlight() {
|
|
396
|
+
this.needsHighlight = true;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Call an animation frame
|
|
401
|
+
*/
|
|
402
|
+
animateFrame() {
|
|
403
|
+
// Synchronise the contents of the pre/code and textarea elements
|
|
404
|
+
if(this.needsHighlight) {
|
|
405
|
+
this.update();
|
|
406
|
+
this.needsHighlight = false;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
window.requestAnimationFrame(this.animateFrame.bind(this));
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Update the text value to the result element, after the textarea contents have changed.
|
|
414
|
+
*/
|
|
415
|
+
update() {
|
|
416
|
+
let resultElement = this.codeElement;
|
|
417
|
+
let value = this.value;
|
|
418
|
+
value += "\n"; // Placeholder for next line
|
|
419
|
+
|
|
420
|
+
// Update code
|
|
421
|
+
resultElement.innerHTML = this.escapeHtml(value);
|
|
422
|
+
this.pluginEvt("beforeHighlight");
|
|
423
|
+
|
|
424
|
+
// Syntax Highlight
|
|
425
|
+
if (this.templateObject.includeCodeInputInHighlightFunc) this.templateObject.highlight(resultElement, this);
|
|
426
|
+
else this.templateObject.highlight(resultElement);
|
|
427
|
+
|
|
428
|
+
this.syncSize();
|
|
429
|
+
|
|
430
|
+
this.pluginEvt("afterHighlight");
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Set the size of the textarea element to the size of the pre/code element.
|
|
435
|
+
*/
|
|
436
|
+
syncSize() {
|
|
437
|
+
// Synchronise the size of the pre/code and textarea elements
|
|
438
|
+
if(this.templateObject.preElementStyled) {
|
|
439
|
+
this.style.backgroundColor = getComputedStyle(this.preElement).backgroundColor;
|
|
440
|
+
this.textareaElement.style.height = getComputedStyle(this.preElement).height;
|
|
441
|
+
this.textareaElement.style.width = getComputedStyle(this.preElement).width;
|
|
442
|
+
} else {
|
|
443
|
+
this.style.backgroundColor = getComputedStyle(this.codeElement).backgroundColor;
|
|
444
|
+
this.textareaElement.style.height = getComputedStyle(this.codeElement).height;
|
|
445
|
+
this.textareaElement.style.width = getComputedStyle(this.codeElement).width;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Show some instructions to the user only if they are using keyboard navigation - for example, a prompt on how to navigate with the keyboard if Tab is repurposed.
|
|
451
|
+
* @param {string} instructions The instructions to display only if keyboard navigation is being used. If it's blank, no instructions will be shown.
|
|
452
|
+
* @param {boolean} includeAriaDescriptionFirst Whether to include the aria-description of the code-input element before the keyboard navigation instructions for a screenreader. Keep this as true when the textarea is first focused.
|
|
453
|
+
*/
|
|
454
|
+
setKeyboardNavInstructions(instructions, includeAriaDescriptionFirst) {
|
|
455
|
+
this.dialogContainerElement.querySelector(".code-input_keyboard-navigation-instructions").innerText = instructions;
|
|
456
|
+
if(includeAriaDescriptionFirst) {
|
|
457
|
+
this.textareaElement.setAttribute("aria-description", this.originalAriaDescription + ". " + instructions);
|
|
458
|
+
} else {
|
|
459
|
+
this.textareaElement.setAttribute("aria-description", instructions);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* HTML-escape an arbitrary string.
|
|
465
|
+
* @param {string} text - The original, unescaped text
|
|
466
|
+
* @returns {string} - The new, HTML-escaped text
|
|
467
|
+
*/
|
|
468
|
+
escapeHtml(text) {
|
|
469
|
+
return text.replace(new RegExp("&", "g"), "&").replace(new RegExp("<", "g"), "<"); /* Global RegExp */
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* HTML-unescape an arbitrary string.
|
|
474
|
+
* @param {string} text - The original, HTML-escaped text
|
|
475
|
+
* @returns {string} - The new, unescaped text
|
|
476
|
+
*/
|
|
477
|
+
unescapeHtml(text) {
|
|
478
|
+
return text.replace(new RegExp("&", "g"), "&").replace(new RegExp("<", "g"), "<").replace(new RegExp(">", "g"), ">"); /* Global RegExp */
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Get the template object this code-input element is using.
|
|
483
|
+
* @returns {Object} - Template object
|
|
484
|
+
*/
|
|
485
|
+
getTemplate() {
|
|
486
|
+
let templateName;
|
|
487
|
+
if (this.getAttribute("template") == undefined) {
|
|
488
|
+
// Default
|
|
489
|
+
templateName = codeInput.defaultTemplate;
|
|
490
|
+
} else {
|
|
491
|
+
templateName = this.getAttribute("template");
|
|
492
|
+
}
|
|
493
|
+
if (templateName in codeInput.usedTemplates) {
|
|
494
|
+
return codeInput.usedTemplates[templateName];
|
|
495
|
+
} else {
|
|
496
|
+
// Doesn't exist - add to queue
|
|
497
|
+
if (!(templateName in codeInput.templateNotYetRegisteredQueue)) {
|
|
498
|
+
codeInput.templateNotYetRegisteredQueue[templateName] = [];
|
|
499
|
+
}
|
|
500
|
+
codeInput.templateNotYetRegisteredQueue[templateName].push(this);
|
|
501
|
+
return undefined;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Set up and initialise the textarea.
|
|
507
|
+
* This will be called once the template has been added.
|
|
508
|
+
*/
|
|
509
|
+
setup() {
|
|
510
|
+
if(this.textareaElement != null) return; // Already set up
|
|
511
|
+
|
|
512
|
+
this.mutationObserver = new MutationObserver(this.mutationObserverCallback.bind(this));
|
|
513
|
+
this.mutationObserver.observe(this, {
|
|
514
|
+
attributes: true,
|
|
515
|
+
attributeOldValue: true
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
this.classList.add("code-input_registered"); // Remove register message
|
|
519
|
+
if (this.templateObject.preElementStyled) this.classList.add("code-input_pre-element-styled");
|
|
520
|
+
|
|
521
|
+
this.pluginEvt("beforeElementsAdded");
|
|
522
|
+
|
|
523
|
+
const fallbackTextarea = this.querySelector("textarea[data-code-input-fallback]");
|
|
524
|
+
let value;
|
|
525
|
+
if(fallbackTextarea) {
|
|
526
|
+
// Fallback textarea exists
|
|
527
|
+
// Sync attributes; existing code-input attributes take priority
|
|
528
|
+
let textareaAttributeNames = fallbackTextarea.getAttributeNames();
|
|
529
|
+
for(let i = 0; i < textareaAttributeNames.length; i++) {
|
|
530
|
+
const attr = textareaAttributeNames[i];
|
|
531
|
+
if(!this.hasAttribute(attr)) {
|
|
532
|
+
this.setAttribute(attr, fallbackTextarea.getAttribute(attr));
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
// Sync value
|
|
536
|
+
value = fallbackTextarea.value;
|
|
537
|
+
} else {
|
|
538
|
+
value = this.unescapeHtml(this.innerHTML);
|
|
539
|
+
}
|
|
540
|
+
value = value || this.getAttribute("value") || "";
|
|
541
|
+
|
|
542
|
+
// First-time attribute sync
|
|
543
|
+
const lang = this.getAttribute("language") || this.getAttribute("lang");
|
|
544
|
+
const placeholder = this.getAttribute("placeholder") || lang || "";
|
|
545
|
+
|
|
546
|
+
|
|
547
|
+
this.initialValue = value; // For form reset
|
|
548
|
+
|
|
549
|
+
// Create textarea
|
|
550
|
+
const textarea = document.createElement("textarea");
|
|
551
|
+
textarea.placeholder = placeholder;
|
|
552
|
+
if(value != "") {
|
|
553
|
+
textarea.value = value;
|
|
554
|
+
}
|
|
555
|
+
textarea.innerHTML = this.innerHTML;
|
|
556
|
+
textarea.setAttribute("spellcheck", "false");
|
|
557
|
+
|
|
558
|
+
// Disable focusing on the code-input element - only allow the textarea to be focusable
|
|
559
|
+
textarea.setAttribute("tabindex", this.getAttribute("tabindex") || 0);
|
|
560
|
+
this.setAttribute("tabindex", -1);
|
|
561
|
+
// Save aria-description so keyboard navigation guidance can be added.
|
|
562
|
+
this.originalAriaDescription = this.getAttribute("aria-description") || "Code input field";
|
|
563
|
+
|
|
564
|
+
// Accessibility - detect when mouse focus to remove focus outline + keyboard navigation guidance that could irritate users.
|
|
565
|
+
this.addEventListener("mousedown", () => {
|
|
566
|
+
this.classList.add("code-input_mouse-focused");
|
|
567
|
+
});
|
|
568
|
+
textarea.addEventListener("blur", () => {
|
|
569
|
+
this.classList.remove("code-input_mouse-focused");
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
this.innerHTML = ""; // Clear Content
|
|
573
|
+
|
|
574
|
+
// Synchronise attributes to textarea
|
|
575
|
+
for(let i = 0; i < this.attributes.length; i++) {
|
|
576
|
+
let attribute = this.attributes[i].name;
|
|
577
|
+
if (codeInput.textareaSyncAttributes.includes(attribute) || attribute.substring(0, 5) == "aria-") {
|
|
578
|
+
textarea.setAttribute(attribute, this.getAttribute(attribute));
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
textarea.addEventListener('input', (evt) => { this.value = this.textareaElement.value; });
|
|
583
|
+
|
|
584
|
+
// Save element internally
|
|
585
|
+
this.textareaElement = textarea;
|
|
586
|
+
this.append(textarea);
|
|
587
|
+
|
|
588
|
+
// Create result element
|
|
589
|
+
let code = document.createElement("code");
|
|
590
|
+
let pre = document.createElement("pre");
|
|
591
|
+
pre.setAttribute("aria-hidden", "true"); // Hide for screen readers
|
|
592
|
+
pre.setAttribute("tabindex", "-1"); // Hide for keyboard navigation
|
|
593
|
+
pre.setAttribute("inert", true); // Hide for keyboard navigation
|
|
594
|
+
|
|
595
|
+
// Save elements internally
|
|
596
|
+
this.preElement = pre;
|
|
597
|
+
this.codeElement = code;
|
|
598
|
+
pre.append(code);
|
|
599
|
+
this.append(pre);
|
|
600
|
+
|
|
601
|
+
if (this.templateObject.isCode) {
|
|
602
|
+
if (lang != undefined && lang != "") {
|
|
603
|
+
code.classList.add("language-" + lang.toLowerCase());
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
// dialogContainerElement used to store non-scrolling dialog boxes, etc.
|
|
608
|
+
let dialogContainerElement = document.createElement("div");
|
|
609
|
+
dialogContainerElement.classList.add("code-input_dialog-container");
|
|
610
|
+
this.append(dialogContainerElement);
|
|
611
|
+
this.dialogContainerElement = dialogContainerElement;
|
|
612
|
+
|
|
613
|
+
let keyboardNavigationInstructions = document.createElement("div");
|
|
614
|
+
keyboardNavigationInstructions.classList.add("code-input_keyboard-navigation-instructions");
|
|
615
|
+
dialogContainerElement.append(keyboardNavigationInstructions);
|
|
616
|
+
|
|
617
|
+
this.pluginEvt("afterElementsAdded");
|
|
618
|
+
|
|
619
|
+
this.dispatchEvent(new CustomEvent("code-input_load"));
|
|
620
|
+
|
|
621
|
+
this.value = value;
|
|
622
|
+
this.animateFrame();
|
|
623
|
+
|
|
624
|
+
const resizeObserver = new ResizeObserver((elements) => {
|
|
625
|
+
// The only element that could be resized is this code-input element.
|
|
626
|
+
this.syncSize();
|
|
627
|
+
});
|
|
628
|
+
resizeObserver.observe(this);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* @deprecated This shouldn't have been accessed as part of the library's public interface (to enable more flexibility in backwards-compatible versions), but is still here just in case it was.
|
|
633
|
+
*/
|
|
634
|
+
escape_html(text) {
|
|
635
|
+
return this.escapeHtml(text);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* @deprecated This shouldn't have been accessed as part of the library's public interface (to enable more flexibility in backwards-compatible versions), but is still here just in case it was.
|
|
640
|
+
*/
|
|
641
|
+
get_template() {
|
|
642
|
+
return this.getTemplate();
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* @deprecated Present for backwards compatibility; use CodeInput.templateObject.
|
|
647
|
+
*/
|
|
648
|
+
get template() {
|
|
649
|
+
return this.templateObject;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* @deprecated The Vue framework may try to set the template
|
|
654
|
+
* property to the value of the template attribute, a string.
|
|
655
|
+
* This should not happen. Intentional use of this should
|
|
656
|
+
* also not happen since templates are changed by changing
|
|
657
|
+
* the template attribute to the name of one registered.
|
|
658
|
+
*/
|
|
659
|
+
set template(value) { }
|
|
660
|
+
|
|
661
|
+
/* ------------------------------------
|
|
662
|
+
* -----------Callbacks----------------
|
|
663
|
+
* ------------------------------------
|
|
664
|
+
* Implement the `HTMLElement` callbacks
|
|
665
|
+
* to trigger the main functionality properly. */
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* When the code-input element has been added to the document,
|
|
669
|
+
* find its template and set up the element.
|
|
670
|
+
*/
|
|
671
|
+
connectedCallback() {
|
|
672
|
+
// Stored in templateObject because some frameworks will override
|
|
673
|
+
// template property with the string value of the attribute
|
|
674
|
+
this.templateObject = this.getTemplate();
|
|
675
|
+
if (this.templateObject != undefined) {
|
|
676
|
+
this.classList.add("code-input_registered");
|
|
677
|
+
this.setup();
|
|
678
|
+
this.classList.add("code-input_loaded");
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
mutationObserverCallback(mutationList, observer) {
|
|
683
|
+
for (const mutation of mutationList) {
|
|
684
|
+
if (mutation.type !== 'attributes')
|
|
685
|
+
continue;
|
|
686
|
+
|
|
687
|
+
/* Check regular attributes */
|
|
688
|
+
for(let i = 0; i < codeInput.observedAttributes.length; i++) {
|
|
689
|
+
if (mutation.attributeName == codeInput.observedAttributes[i]) {
|
|
690
|
+
return this.attributeChangedCallback(mutation.attributeName, mutation.oldValue, super.getAttribute(mutation.attributeName));
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
if (mutation.attributeName.substring(0, 5) == "aria-") {
|
|
694
|
+
return this.attributeChangedCallback(mutation.attributeName, mutation.oldValue, super.getAttribute(mutation.attributeName));
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
disconnectedCallback() {
|
|
700
|
+
this.mutationObserver.disconnect();
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Triggered when an observed HTML attribute
|
|
705
|
+
* has been modified (called from `mutationObserverCallback`).
|
|
706
|
+
* @param {string} name - The name of the attribute
|
|
707
|
+
* @param {string} oldValue - The value of the attribute before it was changed
|
|
708
|
+
* @param {string} newValue - The value of the attribute after it is changed
|
|
709
|
+
*/
|
|
710
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
711
|
+
if (this.isConnected) {
|
|
712
|
+
this.pluginEvt("attributeChanged", [name, oldValue, newValue]);
|
|
713
|
+
switch (name) {
|
|
714
|
+
|
|
715
|
+
case "value":
|
|
716
|
+
this.value = newValue;
|
|
717
|
+
break;
|
|
718
|
+
case "template":
|
|
719
|
+
this.templateObject = codeInput.usedTemplates[newValue || codeInput.defaultTemplate];
|
|
720
|
+
if (this.templateObject.preElementStyled) this.classList.add("code-input_pre-element-styled");
|
|
721
|
+
else this.classList.remove("code-input_pre-element-styled");
|
|
722
|
+
// Syntax Highlight
|
|
723
|
+
this.scheduleHighlight();
|
|
724
|
+
|
|
725
|
+
break;
|
|
726
|
+
|
|
727
|
+
case "lang":
|
|
728
|
+
case "language":
|
|
729
|
+
let code = this.codeElement;
|
|
730
|
+
let mainTextarea = this.textareaElement;
|
|
731
|
+
|
|
732
|
+
// Check not already updated
|
|
733
|
+
if (newValue != null) {
|
|
734
|
+
newValue = newValue.toLowerCase();
|
|
735
|
+
|
|
736
|
+
if (code.classList.contains(`language-${newValue}`)) break; // Already updated
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if(oldValue !== null) {
|
|
740
|
+
// Case insensitive
|
|
741
|
+
oldValue = oldValue.toLowerCase();
|
|
742
|
+
|
|
743
|
+
// Remove old language class and add new
|
|
744
|
+
code.classList.remove("language-" + oldValue); // From codeElement
|
|
745
|
+
code.parentElement.classList.remove("language-" + oldValue); // From preElement
|
|
746
|
+
}
|
|
747
|
+
// Add new language class
|
|
748
|
+
code.classList.remove("language-none"); // Prism
|
|
749
|
+
code.parentElement.classList.remove("language-none"); // Prism
|
|
750
|
+
|
|
751
|
+
if (newValue != undefined && newValue != "") {
|
|
752
|
+
code.classList.add("language-" + newValue);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
if (mainTextarea.placeholder == oldValue) mainTextarea.placeholder = newValue;
|
|
756
|
+
|
|
757
|
+
this.scheduleHighlight();
|
|
758
|
+
|
|
759
|
+
break;
|
|
760
|
+
default:
|
|
761
|
+
if (codeInput.textareaSyncAttributes.includes(name) || name.substring(0, 5) == "aria-") {
|
|
762
|
+
if(newValue == null || newValue == undefined) {
|
|
763
|
+
this.textareaElement.removeAttribute(name);
|
|
764
|
+
} else {
|
|
765
|
+
this.textareaElement.setAttribute(name, newValue);
|
|
766
|
+
}
|
|
767
|
+
} else {
|
|
768
|
+
codeInput.textareaSyncAttributes.regexp.forEach((attribute) => {
|
|
769
|
+
if (name.match(attribute)) {
|
|
770
|
+
if(newValue == null) {
|
|
771
|
+
this.textareaElement.removeAttribute(name);
|
|
772
|
+
} else {
|
|
773
|
+
this.textareaElement.setAttribute(name, newValue);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
break;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/* ------------------------------------
|
|
785
|
+
* -----------Overrides----------------
|
|
786
|
+
* ------------------------------------
|
|
787
|
+
* Override/Implement ordinary HTML textarea functionality so that the <code-input>
|
|
788
|
+
* element acts just like a <textarea>. */
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* @override
|
|
792
|
+
*/
|
|
793
|
+
addEventListener(type, listener, options = undefined) {
|
|
794
|
+
// Save a copy of the callback where `this` refers to the code-input element.
|
|
795
|
+
let boundCallback = function (evt) {
|
|
796
|
+
if (typeof listener === 'function') {
|
|
797
|
+
listener(evt);
|
|
798
|
+
} else if (listener && listener.handleEvent) {
|
|
799
|
+
listener.handleEvent(evt);
|
|
800
|
+
}
|
|
801
|
+
}.bind(this);
|
|
802
|
+
this.boundEventCallbacks[listener] = boundCallback;
|
|
803
|
+
|
|
804
|
+
if (codeInput.textareaSyncEvents.includes(type)) {
|
|
805
|
+
// Synchronise with textarea
|
|
806
|
+
this.boundEventCallbacks[listener] = boundCallback;
|
|
807
|
+
|
|
808
|
+
if (options === undefined) {
|
|
809
|
+
if(this.textareaElement == null) {
|
|
810
|
+
this.addEventListener("code-input_load", () => { this.textareaElement.addEventListener(type, boundCallback); });
|
|
811
|
+
} else {
|
|
812
|
+
this.textareaElement.addEventListener(type, boundCallback);
|
|
813
|
+
}
|
|
814
|
+
} else {
|
|
815
|
+
if(this.textareaElement == null) {
|
|
816
|
+
this.addEventListener("code-input_load", () => { this.textareaElement.addEventListener(type, boundCallback, options); });
|
|
817
|
+
} else {
|
|
818
|
+
this.textareaElement.addEventListener(type, boundCallback, options);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
} else {
|
|
822
|
+
// Synchronise with code-input element
|
|
823
|
+
if (options === undefined) {
|
|
824
|
+
super.addEventListener(type, boundCallback);
|
|
825
|
+
} else {
|
|
826
|
+
super.addEventListener(type, boundCallback, options);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* @override
|
|
833
|
+
*/
|
|
834
|
+
removeEventListener(type, listener, options = undefined) {
|
|
835
|
+
// Save a copy of the callback where `this` refers to the code-input element
|
|
836
|
+
let boundCallback = this.boundEventCallbacks[listener];
|
|
837
|
+
|
|
838
|
+
if (codeInput.textareaSyncEvents.includes(type)) {
|
|
839
|
+
// Synchronise with textarea
|
|
840
|
+
if (options === undefined) {
|
|
841
|
+
if(this.textareaElement == null) {
|
|
842
|
+
this.addEventListener("code-input_load", () => { this.textareaElement.removeEventListener(type, boundCallback); });
|
|
843
|
+
} else {
|
|
844
|
+
this.textareaElement.removeEventListener(type, boundCallback);
|
|
845
|
+
}
|
|
846
|
+
} else {
|
|
847
|
+
if(this.textareaElement == null) {
|
|
848
|
+
this.addEventListener("code-input_load", () => { this.textareaElement.removeEventListener(type, boundCallback, options); });
|
|
849
|
+
} else {
|
|
850
|
+
this.textareaElement.removeEventListener(type, boundCallback, options);
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
} else {
|
|
854
|
+
// Synchronise with code-input element
|
|
855
|
+
if (options === undefined) {
|
|
856
|
+
super.removeEventListener(type, boundCallback);
|
|
857
|
+
} else {
|
|
858
|
+
super.removeEventListener(type, boundCallback, options);
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* Get the text contents of the code-input element.
|
|
865
|
+
*/
|
|
866
|
+
get value() {
|
|
867
|
+
if(this.textareaElement) {
|
|
868
|
+
// Get from editable textarea element
|
|
869
|
+
return this.textareaElement.value;
|
|
870
|
+
} else {
|
|
871
|
+
// Unregistered
|
|
872
|
+
const fallbackTextarea = this.querySelector("textarea[data-code-input-fallback]");
|
|
873
|
+
if(fallbackTextarea) {
|
|
874
|
+
return fallbackTextarea.value;
|
|
875
|
+
} else {
|
|
876
|
+
return this.innerHTML;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Set the text contents of the code-input element.
|
|
882
|
+
* @param {string} val - New text contents
|
|
883
|
+
*/
|
|
884
|
+
set value(val) {
|
|
885
|
+
if (val === null || val === undefined) {
|
|
886
|
+
val = "";
|
|
887
|
+
}
|
|
888
|
+
if(this.textareaElement) {
|
|
889
|
+
// Save in editable textarea element
|
|
890
|
+
this.textareaElement.value = val;
|
|
891
|
+
// Trigger highlight
|
|
892
|
+
this.scheduleHighlight();
|
|
893
|
+
} else {
|
|
894
|
+
// Unregistered
|
|
895
|
+
const fallbackTextarea = this.querySelector("textarea[data-code-input-fallback]");
|
|
896
|
+
if(fallbackTextarea) {
|
|
897
|
+
fallbackTextarea.value = val;
|
|
898
|
+
} else {
|
|
899
|
+
this.innerHTML = val;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
// Synchronise blur and focus
|
|
905
|
+
focus(options={}) {
|
|
906
|
+
return this.textareaElement.focus(options);
|
|
907
|
+
}
|
|
908
|
+
blur(options={}) {
|
|
909
|
+
return this.textareaElement.blur(options);
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/**
|
|
913
|
+
* Get the placeholder of the code-input element that appears
|
|
914
|
+
* when no code has been entered.
|
|
915
|
+
*/
|
|
916
|
+
get placeholder() {
|
|
917
|
+
return this.getAttribute("placeholder");
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Set the placeholder of the code-input element that appears
|
|
921
|
+
* when no code has been entered.
|
|
922
|
+
* @param {string} val - New placeholder
|
|
923
|
+
*/
|
|
924
|
+
set placeholder(val) {
|
|
925
|
+
return this.setAttribute("placeholder", val);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Returns a ValidityState object that represents the validity states of an element.
|
|
930
|
+
*
|
|
931
|
+
* See `HTMLTextAreaElement.validity`
|
|
932
|
+
*/
|
|
933
|
+
get validity() {
|
|
934
|
+
return this.textareaElement.validity;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Returns the error message that would be displayed if the user submits the form, or an empty string if no error message.
|
|
939
|
+
* It also triggers the standard error message, such as "this is a required field". The result is that the user sees validation
|
|
940
|
+
* messages without actually submitting.
|
|
941
|
+
*
|
|
942
|
+
* See `HTMLTextAreaElement.validationMessage`
|
|
943
|
+
*/
|
|
944
|
+
get validationMessage() {
|
|
945
|
+
return this.textareaElement.validationMessage;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Sets a custom error message that is displayed when a form is submitted.
|
|
950
|
+
*
|
|
951
|
+
* See `HTMLTextAreaElement.setCustomValidity`
|
|
952
|
+
* @param error Sets a custom error message that is displayed when a form is submitted.
|
|
953
|
+
*/
|
|
954
|
+
setCustomValidity(error) {
|
|
955
|
+
return this.textareaElement.setCustomValidity(error);
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
/**
|
|
959
|
+
* Returns whether a form will validate when it is submitted,
|
|
960
|
+
* without having to submit it.
|
|
961
|
+
*
|
|
962
|
+
* See `HTMLTextAreaElement.checkValidity`
|
|
963
|
+
*/
|
|
964
|
+
checkValidity() {
|
|
965
|
+
return this.textareaElement.checkValidity();
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/**
|
|
969
|
+
* See `HTMLTextAreaElement.reportValidity`
|
|
970
|
+
*/
|
|
971
|
+
reportValidity() {
|
|
972
|
+
return this.textareaElement.reportValidity();
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/**
|
|
976
|
+
* Allows plugins to store data in the scope of a single element.
|
|
977
|
+
* Key - name of the plugin, in camelCase
|
|
978
|
+
* Value - object of data to be stored; different plugins may use this differently.
|
|
979
|
+
*/
|
|
980
|
+
pluginData = {};
|
|
981
|
+
|
|
982
|
+
/**
|
|
983
|
+
* Update value on form reset
|
|
984
|
+
*/
|
|
985
|
+
formResetCallback() {
|
|
986
|
+
this.value = this.initialValue;
|
|
987
|
+
};
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
|
|
992
|
+
customElements.define("code-input", codeInput.CodeInput);
|
|
993
|
+
export const Plugin = codeInput.Plugin;
|
|
994
|
+
export const Template = codeInput.Template;
|
|
995
|
+
export const CodeInput = codeInput.CodeInput;
|
|
996
|
+
export const registerTemplate = codeInput.registerTemplate;
|
|
997
|
+
export default { Plugin, Template, CodeInput, registerTemplate };
|