@webcoder49/code-input 2.2.1 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -4
- package/code-input.css +91 -19
- package/code-input.d.ts +94 -13
- package/code-input.js +114 -40
- package/code-input.min.css +1 -1
- package/code-input.min.js +1 -1
- package/package.json +1 -1
- package/plugins/README.md +7 -0
- package/plugins/autocomplete.js +4 -2
- package/plugins/autocomplete.min.js +1 -1
- package/plugins/find-and-replace.js +136 -42
- package/plugins/find-and-replace.min.js +1 -1
- package/plugins/go-to-line.js +20 -2
- package/plugins/go-to-line.min.js +1 -1
- package/plugins/indent.js +97 -6
- package/plugins/indent.min.js +1 -1
- package/plugins/prism-line-numbers.css +10 -9
- package/plugins/prism-line-numbers.min.css +1 -1
- package/plugins/select-token-callbacks.js +289 -0
- package/plugins/select-token-callbacks.min.js +1 -0
- package/plugins/special-chars.css +1 -1
- package/plugins/special-chars.js +10 -8
- package/plugins/special-chars.min.css +1 -1
- package/plugins/special-chars.min.js +1 -1
- package/tests/hljs.html +2 -1
- package/tests/i18n.html +197 -0
- package/tests/prism-match-braces-compatibility.js +215 -0
- package/tests/prism-match-braces-compatibility.min.js +1 -0
- package/tests/prism.html +7 -8
- package/tests/tester.js +77 -13
- package/tests/tester.min.js +7 -4
|
@@ -6,15 +6,40 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
6
6
|
useCtrlF = false;
|
|
7
7
|
useCtrlH = false;
|
|
8
8
|
|
|
9
|
+
findMatchesOnValueChange = true; // Needed so the program can insert text to the find value and thus add it to Ctrl+Z without highlighting matches.
|
|
10
|
+
|
|
11
|
+
instructions = {
|
|
12
|
+
start: "Search for matches in your code.",
|
|
13
|
+
none: "No matches",
|
|
14
|
+
oneFound: "1 match found.",
|
|
15
|
+
matchIndex: (index, count) => `${index} of ${count} matches.`,
|
|
16
|
+
error: (message) => `Error: ${message}`,
|
|
17
|
+
infiniteLoopError: "Causes an infinite loop",
|
|
18
|
+
closeDialog: "Close Dialog and Return to Editor",
|
|
19
|
+
findPlaceholder: "Find",
|
|
20
|
+
findCaseSensitive: "Match Case Sensitive",
|
|
21
|
+
findRegExp: "Use JavaScript Regular Expression",
|
|
22
|
+
replaceTitle: "Replace",
|
|
23
|
+
replacePlaceholder: "Replace with",
|
|
24
|
+
findNext: "Find Next Occurrence",
|
|
25
|
+
findPrevious: "Find Previous Occurrence",
|
|
26
|
+
replaceActionShort: "Replace",
|
|
27
|
+
replaceAction: "Replace This Occurrence",
|
|
28
|
+
replaceAllActionShort: "Replace All",
|
|
29
|
+
replaceAllAction: "Replace All Occurrences"
|
|
30
|
+
};
|
|
31
|
+
|
|
9
32
|
/**
|
|
10
33
|
* Create a find-and-replace command plugin to pass into a template
|
|
11
34
|
* @param {boolean} useCtrlF Should Ctrl+F be overriden for find-and-replace find functionality? If not, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, false)`.
|
|
12
35
|
* @param {boolean} useCtrlH Should Ctrl+H be overriden for find-and-replace replace functionality? If not, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element, true)`.
|
|
36
|
+
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the find-and-replace.js source code for the English text and available keys.
|
|
13
37
|
*/
|
|
14
|
-
constructor(useCtrlF = true, useCtrlH = true) {
|
|
38
|
+
constructor(useCtrlF = true, useCtrlH = true, instructionTranslations = {}) {
|
|
15
39
|
super([]); // No observed attributes
|
|
16
40
|
this.useCtrlF = useCtrlF;
|
|
17
41
|
this.useCtrlH = useCtrlH;
|
|
42
|
+
this.addTranslations(this.instructions, instructionTranslations);
|
|
18
43
|
}
|
|
19
44
|
|
|
20
45
|
/* Add keystroke events */
|
|
@@ -54,13 +79,13 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
54
79
|
updateMatchDescription(dialog) {
|
|
55
80
|
// 1-indexed
|
|
56
81
|
if(dialog.findInput.value.length == 0) {
|
|
57
|
-
dialog.matchDescription.textContent =
|
|
82
|
+
dialog.matchDescription.textContent = this.instructions.start;
|
|
58
83
|
} else if(dialog.findMatchState.numMatches <= 0) {
|
|
59
|
-
dialog.matchDescription.textContent =
|
|
84
|
+
dialog.matchDescription.textContent = this.instructions.none;
|
|
60
85
|
} else if(dialog.findMatchState.numMatches == 1) {
|
|
61
|
-
dialog.matchDescription.textContent =
|
|
86
|
+
dialog.matchDescription.textContent = this.instructions.oneFound;
|
|
62
87
|
} else {
|
|
63
|
-
dialog.matchDescription.textContent =
|
|
88
|
+
dialog.matchDescription.textContent = this.instructions.matchIndex(dialog.findMatchState.focusedMatchID+1, dialog.findMatchState.numMatches);
|
|
64
89
|
}
|
|
65
90
|
}
|
|
66
91
|
|
|
@@ -81,7 +106,7 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
81
106
|
dialog.findInput.classList.add('code-input_find-and-replace_error');
|
|
82
107
|
// Only show last part of error message
|
|
83
108
|
let messageParts = err.message.split(": ");
|
|
84
|
-
dialog.matchDescription.textContent =
|
|
109
|
+
dialog.matchDescription.textContent = this.instructions.error(messageParts[messageParts.length-1]); // Show only last part of error.
|
|
85
110
|
return;
|
|
86
111
|
} else {
|
|
87
112
|
throw err;
|
|
@@ -100,7 +125,7 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
100
125
|
}, 100);
|
|
101
126
|
}
|
|
102
127
|
|
|
103
|
-
/* Deal with Enter
|
|
128
|
+
/* Deal with Enter being pressed in the find field */
|
|
104
129
|
checkFindPrompt(dialog, codeInput, event) {
|
|
105
130
|
if (event.key == 'Enter') {
|
|
106
131
|
// Find next match
|
|
@@ -109,11 +134,12 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
109
134
|
}
|
|
110
135
|
}
|
|
111
136
|
|
|
112
|
-
/* Deal with Enter
|
|
137
|
+
/* Deal with Enter being pressed in the replace field */
|
|
113
138
|
checkReplacePrompt(dialog, codeInput, event) {
|
|
114
139
|
if (event.key == 'Enter') {
|
|
115
140
|
// Replace focused match
|
|
116
141
|
dialog.findMatchState.replaceOnce(dialog.replaceInput.value);
|
|
142
|
+
dialog.replaceInput.focus();
|
|
117
143
|
this.updateMatchDescription(dialog);
|
|
118
144
|
}
|
|
119
145
|
}
|
|
@@ -121,9 +147,21 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
121
147
|
/* Called with a dialog box keyup event to close and clear the dialog box */
|
|
122
148
|
cancelPrompt(dialog, codeInput, event) {
|
|
123
149
|
event.preventDefault();
|
|
124
|
-
|
|
150
|
+
|
|
151
|
+
// Add current value of find/replace to Ctrl+Z stack.
|
|
152
|
+
this.findMatchesOnValueChange = false;
|
|
153
|
+
dialog.findInput.focus();
|
|
154
|
+
dialog.findInput.selectionStart = 0;
|
|
155
|
+
dialog.findInput.selectionEnd = dialog.findInput.value.length;
|
|
156
|
+
document.execCommand("insertText", false, dialog.findInput.value);
|
|
157
|
+
this.findMatchesOnValueChange = true;
|
|
158
|
+
|
|
125
159
|
// Reset original selection in code-input
|
|
126
160
|
dialog.textarea.focus();
|
|
161
|
+
dialog.setAttribute("inert", true); // Hide from keyboard navigation when closed.
|
|
162
|
+
dialog.setAttribute("tabindex", -1); // Hide from keyboard navigation when closed.
|
|
163
|
+
dialog.setAttribute("aria-hidden", true); // Hide from screen reader when closed.
|
|
164
|
+
|
|
127
165
|
if(dialog.findMatchState.numMatches > 0) {
|
|
128
166
|
// Select focused match
|
|
129
167
|
codeInput.textareaElement.selectionStart = dialog.findMatchState.matchStartIndexes[dialog.findMatchState.focusedMatchID];
|
|
@@ -146,14 +184,16 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
146
184
|
* @param {boolean} replacePartExpanded whether the replace part of the find-and-replace dialog should be expanded
|
|
147
185
|
*/
|
|
148
186
|
showPrompt(codeInputElement, replacePartExpanded) {
|
|
187
|
+
let dialog;
|
|
149
188
|
if(codeInputElement.pluginData.findAndReplace == undefined || codeInputElement.pluginData.findAndReplace.dialog == undefined) {
|
|
150
189
|
const textarea = codeInputElement.textareaElement;
|
|
151
190
|
|
|
152
|
-
|
|
191
|
+
dialog = document.createElement('div');
|
|
153
192
|
const findInput = document.createElement('input');
|
|
154
193
|
const findCaseSensitiveCheckbox = document.createElement('input');
|
|
155
194
|
const findRegExpCheckbox = document.createElement('input');
|
|
156
195
|
const matchDescription = document.createElement('code');
|
|
196
|
+
matchDescription.setAttribute("aria-live", "assertive"); // Screen reader must read the number of matches found.
|
|
157
197
|
|
|
158
198
|
const replaceInput = document.createElement('input');
|
|
159
199
|
const replaceDropdown = document.createElement('details');
|
|
@@ -165,6 +205,8 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
165
205
|
const replaceButton = document.createElement('button');
|
|
166
206
|
const replaceAllButton = document.createElement('button');
|
|
167
207
|
const cancel = document.createElement('span');
|
|
208
|
+
cancel.setAttribute("tabindex", 0); // Visible to keyboard navigation
|
|
209
|
+
cancel.setAttribute("title", this.instructions.closeDialog);
|
|
168
210
|
|
|
169
211
|
buttonContainer.appendChild(findNextButton);
|
|
170
212
|
buttonContainer.appendChild(findPreviousButton);
|
|
@@ -184,31 +226,39 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
184
226
|
|
|
185
227
|
dialog.className = 'code-input_find-and-replace_dialog';
|
|
186
228
|
findInput.spellcheck = false;
|
|
187
|
-
findInput.placeholder =
|
|
229
|
+
findInput.placeholder = this.instructions.findPlaceholder;
|
|
188
230
|
findCaseSensitiveCheckbox.setAttribute("type", "checkbox");
|
|
189
|
-
findCaseSensitiveCheckbox.title =
|
|
231
|
+
findCaseSensitiveCheckbox.title = this.instructions.findCaseSensitive;
|
|
190
232
|
findCaseSensitiveCheckbox.classList.add("code-input_find-and-replace_case-sensitive-checkbox");
|
|
191
233
|
findRegExpCheckbox.setAttribute("type", "checkbox");
|
|
192
|
-
findRegExpCheckbox.title =
|
|
234
|
+
findRegExpCheckbox.title = this.instructions.findRegExp;
|
|
193
235
|
findRegExpCheckbox.classList.add("code-input_find-and-replace_reg-exp-checkbox");
|
|
194
236
|
|
|
195
237
|
matchDescription.textContent = "Search for matches in your code.";
|
|
196
238
|
matchDescription.classList.add("code-input_find-and-replace_match-description");
|
|
197
239
|
|
|
198
240
|
|
|
199
|
-
replaceSummary.innerText =
|
|
241
|
+
replaceSummary.innerText = this.instructions.replaceTitle;
|
|
200
242
|
replaceInput.spellcheck = false;
|
|
201
|
-
replaceInput.placeholder =
|
|
243
|
+
replaceInput.placeholder = this.instructions.replacePlaceholder;
|
|
202
244
|
findNextButton.innerText = "↓";
|
|
203
|
-
findNextButton.title =
|
|
245
|
+
findNextButton.title = this.instructions.findNext;
|
|
204
246
|
findPreviousButton.innerText = "↑";
|
|
205
|
-
findPreviousButton.title =
|
|
247
|
+
findPreviousButton.title = this.instructions.findPrevious;
|
|
206
248
|
replaceButton.className = 'code-input_find-and-replace_button-hidden';
|
|
207
|
-
replaceButton.innerText =
|
|
208
|
-
replaceButton.title =
|
|
249
|
+
replaceButton.innerText = this.instructions.replaceActionShort;
|
|
250
|
+
replaceButton.title = this.instructions.replaceAction;
|
|
251
|
+
replaceButton.addEventListener("focus", () => {
|
|
252
|
+
// Show replace section
|
|
253
|
+
replaceDropdown.setAttribute("open", true);
|
|
254
|
+
});
|
|
209
255
|
replaceAllButton.className = 'code-input_find-and-replace_button-hidden';
|
|
210
|
-
replaceAllButton.innerText =
|
|
211
|
-
replaceAllButton.title =
|
|
256
|
+
replaceAllButton.innerText = this.instructions.replaceAllActionShort;
|
|
257
|
+
replaceAllButton.title = this.instructions.replaceAllAction;
|
|
258
|
+
replaceAllButton.addEventListener("focus", () => {
|
|
259
|
+
// Show replace section
|
|
260
|
+
replaceDropdown.setAttribute("open", true);
|
|
261
|
+
});
|
|
212
262
|
|
|
213
263
|
findNextButton.addEventListener("click", (event) => {
|
|
214
264
|
// Stop form submit
|
|
@@ -229,7 +279,7 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
229
279
|
event.preventDefault();
|
|
230
280
|
|
|
231
281
|
dialog.findMatchState.replaceOnce(replaceInput.value);
|
|
232
|
-
|
|
282
|
+
dialog.focus();
|
|
233
283
|
});
|
|
234
284
|
replaceAllButton.addEventListener("click", (event) => {
|
|
235
285
|
// Stop form submit
|
|
@@ -275,6 +325,16 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
275
325
|
/* Stop enter from submitting form */
|
|
276
326
|
if (event.key == 'Enter') event.preventDefault();
|
|
277
327
|
});
|
|
328
|
+
replaceInput.addEventListener('input', (event) => {
|
|
329
|
+
// Ctrl+Z can trigger this. If the dialog/replace dropdown aren't open, open them!
|
|
330
|
+
if(dialog.classList.contains("code-input_find-and-replace_hidden-dialog")) {
|
|
331
|
+
// Show prompt
|
|
332
|
+
this.showPrompt(dialog.codeInput, true);
|
|
333
|
+
} else if(!dialog.replaceDropdown.hasAttribute("open")) {
|
|
334
|
+
// Open dropdown
|
|
335
|
+
dialog.replaceDropdown.setAttribute("open", true);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
278
338
|
|
|
279
339
|
dialog.addEventListener('keyup', (event) => {
|
|
280
340
|
/* Close prompt on Enter pressed */
|
|
@@ -282,7 +342,13 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
282
342
|
});
|
|
283
343
|
|
|
284
344
|
findInput.addEventListener('keyup', (event) => { this.checkFindPrompt(dialog, codeInputElement, event); });
|
|
285
|
-
findInput.addEventListener('input', (event) => {
|
|
345
|
+
findInput.addEventListener('input', (event) => {
|
|
346
|
+
if(this.findMatchesOnValueChange) this.updateFindMatches(dialog);
|
|
347
|
+
// Ctrl+Z can trigger this. If the dialog isn't open, open it!
|
|
348
|
+
if(dialog.classList.contains("code-input_find-and-replace_hidden-dialog")) {
|
|
349
|
+
this.showPrompt(dialog.codeInput, false);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
286
352
|
findCaseSensitiveCheckbox.addEventListener('click', (event) => { this.updateFindMatches(dialog); });
|
|
287
353
|
findRegExpCheckbox.addEventListener('click', (event) => { this.updateFindMatches(dialog); });
|
|
288
354
|
|
|
@@ -291,6 +357,7 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
291
357
|
replaceInput.focus();
|
|
292
358
|
});
|
|
293
359
|
cancel.addEventListener('click', (event) => { this.cancelPrompt(dialog, codeInputElement, event); });
|
|
360
|
+
cancel.addEventListener('keypress', (event) => { if (event.key == "Space" || event.key == "Enter") this.cancelPrompt(dialog, codeInputElement, event); });
|
|
294
361
|
|
|
295
362
|
codeInputElement.dialogContainerElement.appendChild(dialog);
|
|
296
363
|
codeInputElement.pluginData.findAndReplace = {dialog: dialog};
|
|
@@ -303,24 +370,45 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
303
370
|
// Save selection position
|
|
304
371
|
dialog.selectionStart = codeInputElement.textareaElement.selectionStart;
|
|
305
372
|
dialog.selectionEnd = codeInputElement.textareaElement.selectionEnd;
|
|
373
|
+
|
|
374
|
+
if(dialog.selectionStart < dialog.selectionEnd) {
|
|
375
|
+
// Copy selected text to Find input
|
|
376
|
+
let textToFind = codeInputElement.textareaElement.value.substring(dialog.selectionStart, dialog.selectionEnd);
|
|
377
|
+
dialog.findInput.focus();
|
|
378
|
+
dialog.findInput.selectionStart = 0;
|
|
379
|
+
dialog.findInput.selectionEnd = dialog.findInput.value.length;
|
|
380
|
+
document.execCommand("insertText", false, textToFind);
|
|
381
|
+
}
|
|
306
382
|
} else {
|
|
383
|
+
dialog = codeInputElement.pluginData.findAndReplace.dialog;
|
|
307
384
|
// Re-open dialog
|
|
308
|
-
|
|
309
|
-
|
|
385
|
+
dialog.classList.remove("code-input_find-and-replace_hidden-dialog");
|
|
386
|
+
dialog.removeAttribute("inert"); // Show to keyboard navigation when open.
|
|
387
|
+
dialog.setAttribute("tabindex", 0); // Show to keyboard navigation when open.
|
|
388
|
+
dialog.removeAttribute("aria-hidden"); // Show to screen reader when open.
|
|
389
|
+
dialog.findInput.focus();
|
|
310
390
|
if(replacePartExpanded) {
|
|
311
|
-
|
|
391
|
+
dialog.replaceDropdown.setAttribute("open", true);
|
|
312
392
|
} else {
|
|
313
|
-
|
|
393
|
+
dialog.replaceDropdown.removeAttribute("open");
|
|
314
394
|
}
|
|
395
|
+
}
|
|
315
396
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
codeInputElement.
|
|
397
|
+
// Save selection position
|
|
398
|
+
dialog.selectionStart = codeInputElement.textareaElement.selectionStart;
|
|
399
|
+
dialog.selectionEnd = codeInputElement.textareaElement.selectionEnd;
|
|
400
|
+
|
|
401
|
+
if(dialog.selectionStart < dialog.selectionEnd) {
|
|
402
|
+
// Copy selected text to Find input
|
|
403
|
+
let textToFind = codeInputElement.textareaElement.value.substring(dialog.selectionStart, dialog.selectionEnd);
|
|
404
|
+
dialog.findInput.focus();
|
|
405
|
+
dialog.findInput.selectionStart = 0;
|
|
406
|
+
dialog.findInput.selectionEnd = dialog.findInput.value.length;
|
|
407
|
+
document.execCommand("insertText", false, textToFind);
|
|
323
408
|
}
|
|
409
|
+
|
|
410
|
+
// Highlight matches
|
|
411
|
+
this.updateFindMatches(dialog);
|
|
324
412
|
}
|
|
325
413
|
|
|
326
414
|
/* Event handler for keydown event that makes Ctrl+F open find dialog */
|
|
@@ -405,7 +493,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
405
493
|
while ((match = searchRegexp.exec(this.codeInput.value)) !== null) {
|
|
406
494
|
let matchText = match[0];
|
|
407
495
|
if(matchText.length == 0) {
|
|
408
|
-
throw SyntaxError(
|
|
496
|
+
throw SyntaxError(this.instructions.infiniteLoopError);
|
|
409
497
|
}
|
|
410
498
|
|
|
411
499
|
// Add next match block if needed
|
|
@@ -441,12 +529,14 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
441
529
|
this.focusedMatchStartIndex += replacementText.length;
|
|
442
530
|
|
|
443
531
|
// Select the match
|
|
532
|
+
this.codeInput.handleEventsFromTextarea = false;
|
|
444
533
|
this.codeInput.textareaElement.focus();
|
|
445
534
|
this.codeInput.textareaElement.selectionStart = this.matchStartIndexes[this.focusedMatchID];
|
|
446
535
|
this.codeInput.textareaElement.selectionEnd = this.matchEndIndexes[this.focusedMatchID];
|
|
447
536
|
|
|
448
537
|
// Replace it with the replacement text
|
|
449
538
|
document.execCommand("insertText", false, replacementText);
|
|
539
|
+
this.codeInput.handleEventsFromTextarea = true;
|
|
450
540
|
}
|
|
451
541
|
}
|
|
452
542
|
|
|
@@ -459,6 +549,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
459
549
|
// Replace each match
|
|
460
550
|
|
|
461
551
|
// Select the match, taking into account characters added before
|
|
552
|
+
this.codeInput.handleEventsFromTextarea = false;
|
|
462
553
|
this.codeInput.textareaElement.focus();
|
|
463
554
|
this.codeInput.textareaElement.selectionStart = this.matchStartIndexes[i] + numCharsAdded;
|
|
464
555
|
this.codeInput.textareaElement.selectionEnd = this.matchEndIndexes[i] + numCharsAdded;
|
|
@@ -467,6 +558,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
467
558
|
|
|
468
559
|
// Replace it with the replacement text
|
|
469
560
|
document.execCommand("insertText", false, replacementText);
|
|
561
|
+
this.codeInput.handleEventsFromTextarea = true;
|
|
470
562
|
}
|
|
471
563
|
}
|
|
472
564
|
|
|
@@ -527,7 +619,8 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
527
619
|
|
|
528
620
|
/* Highlight a match from the find functionality given its start and end indexes in the text.
|
|
529
621
|
Start from the currentElement as this function is recursive. Use the matchID in the class name
|
|
530
|
-
of the match so different matches can be identified.
|
|
622
|
+
of the match so different matches can be identified.
|
|
623
|
+
This code is similar to codeInput.plugins.SelectTokenCallbacks.SelectedTokenState.updateSelectedTokens*/
|
|
531
624
|
highlightMatch(matchID, currentElement, startIndex, endIndex) {
|
|
532
625
|
for(let i = 0; i < currentElement.childNodes.length; i++) {
|
|
533
626
|
let childElement = currentElement.childNodes[i];
|
|
@@ -535,6 +628,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
535
628
|
|
|
536
629
|
let noInnerElements = false;
|
|
537
630
|
if(childElement.nodeType == 3) {
|
|
631
|
+
// Text node
|
|
538
632
|
if(i + 1 < currentElement.childNodes.length && currentElement.childNodes[i+1].nodeType == 3) {
|
|
539
633
|
// Can merge with next text node
|
|
540
634
|
currentElement.childNodes[i+1].textContent = childElement.textContent + currentElement.childNodes[i+1].textContent; // Merge textContent with next node
|
|
@@ -547,7 +641,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
547
641
|
|
|
548
642
|
let replacementElement = document.createElement("span");
|
|
549
643
|
replacementElement.textContent = childText;
|
|
550
|
-
replacementElement.classList.add("code-input_find-and-replace_temporary-span"); // Can remove
|
|
644
|
+
replacementElement.classList.add("code-input_find-and-replace_temporary-span"); // Can remove span later
|
|
551
645
|
|
|
552
646
|
currentElement.replaceChild(replacementElement, childElement);
|
|
553
647
|
childElement = replacementElement;
|
|
@@ -562,7 +656,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
562
656
|
let startSpan = document.createElement("span");
|
|
563
657
|
startSpan.classList.add("code-input_find-and-replace_find-match"); // Highlighted
|
|
564
658
|
startSpan.setAttribute("data-code-input_find-and-replace_match-id", matchID);
|
|
565
|
-
startSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove
|
|
659
|
+
startSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove span later
|
|
566
660
|
startSpan.textContent = childText.substring(0, endIndex);
|
|
567
661
|
if(startSpan.textContent[0] == "\n") {
|
|
568
662
|
// Newline at start - make clear
|
|
@@ -597,7 +691,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
597
691
|
// Match starts and ends in childElement - highlight middle part
|
|
598
692
|
// Text node - highlight last part
|
|
599
693
|
let startSpan = document.createElement("span");
|
|
600
|
-
startSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove
|
|
694
|
+
startSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove span later
|
|
601
695
|
startSpan.textContent = childText.substring(0, startIndex);
|
|
602
696
|
|
|
603
697
|
let middleText = childText.substring(startIndex, endIndex);
|
|
@@ -610,7 +704,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
610
704
|
}
|
|
611
705
|
|
|
612
706
|
let endSpan = document.createElement("span");
|
|
613
|
-
endSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove
|
|
707
|
+
endSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove span later
|
|
614
708
|
endSpan.textContent = childText.substring(endIndex);
|
|
615
709
|
|
|
616
710
|
childElement.insertAdjacentElement('beforebegin', startSpan);
|
|
@@ -624,7 +718,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
624
718
|
let endSpan = document.createElement("span");
|
|
625
719
|
endSpan.classList.add("code-input_find-and-replace_find-match"); // Highlighted
|
|
626
720
|
endSpan.setAttribute("data-code-input_find-and-replace_match-id", matchID);
|
|
627
|
-
endSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove
|
|
721
|
+
endSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove span later
|
|
628
722
|
endSpan.textContent = childText.substring(startIndex);
|
|
629
723
|
if(endSpan.textContent[0] == "\n") {
|
|
630
724
|
// Newline at start - make clear
|
|
@@ -649,4 +743,4 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
649
743
|
endIndex -= childText.length;
|
|
650
744
|
}
|
|
651
745
|
}
|
|
652
|
-
}
|
|
746
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
codeInput.plugins.FindAndReplace=class extends codeInput.Plugin{useCtrlF=!1;useCtrlH=!1;constructor(a=!0,b=!0){super([]),this.useCtrlF=a,this.useCtrlH=b}afterElementsAdded(a){const b=a.textareaElement;this.useCtrlF&&b.addEventListener("keydown",b=>{this.checkCtrlF(a,b)}),this.useCtrlH&&b.addEventListener("keydown",b=>{this.checkCtrlH(a,b)})}afterHighlight(a){a.pluginData.findAndReplace==null||a.pluginData.findAndReplace.dialog==null||a.pluginData.findAndReplace.dialog.classList.contains("code-input_find-and-replace_hidden-dialog")||(a.pluginData.findAndReplace.dialog.findMatchState.rehighlightMatches(),this.updateMatchDescription(a.pluginData.findAndReplace.dialog),0==a.pluginData.findAndReplace.dialog.findMatchState.numMatches&&a.pluginData.findAndReplace.dialog.findInput.classList.add("code-input_find-and-replace_error"))}text2RegExp(a,b,c){return new RegExp(c?a:a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),b?"g":"gi")}updateMatchDescription(a){a.matchDescription.textContent=0==a.findInput.value.length?"Search for matches in your code.":0>=a.findMatchState.numMatches?"No matches.":1==a.findMatchState.numMatches?"1 match found.":`${a.findMatchState.focusedMatchID+1} of ${a.findMatchState.numMatches} matches.`}updateFindMatches(a){let b=a.findInput.value;setTimeout(()=>{if(b==a.findInput.value){if(a.findMatchState.clearMatches(),0<b.length){try{a.findMatchState.updateMatches(this.text2RegExp(a.findInput.value,a.findCaseSensitiveCheckbox.checked,a.findRegExpCheckbox.checked))}catch(b){if(b instanceof SyntaxError){a.findInput.classList.add("code-input_find-and-replace_error");let c=b.message.split(": ");return void(a.matchDescription.textContent="Error: "+c[c.length-1])}throw b}0<a.findMatchState.numMatches?a.findInput.classList.remove("code-input_find-and-replace_error"):a.findInput.classList.add("code-input_find-and-replace_error")}this.updateMatchDescription(a)}},100)}checkFindPrompt(a,b,c){"Enter"==c.key&&(a.findMatchState.nextMatch(),this.updateMatchDescription(a))}checkReplacePrompt(a,b,c){"Enter"==c.key&&(a.findMatchState.replaceOnce(a.replaceInput.value),this.updateMatchDescription(a))}cancelPrompt(a,b,c){c.preventDefault(),a.textarea.focus(),0<a.findMatchState.numMatches?(b.textareaElement.selectionStart=a.findMatchState.matchStartIndexes[a.findMatchState.focusedMatchID],b.textareaElement.selectionEnd=a.findMatchState.matchEndIndexes[a.findMatchState.focusedMatchID]):(b.textareaElement.selectionStart=a.selectionStart,b.textareaElement.selectionEnd=a.selectionEnd),a.findMatchState.clearMatches(),a.classList.add("code-input_find-and-replace_hidden-dialog")}showPrompt(a,b){if(a.pluginData.findAndReplace==null||a.pluginData.findAndReplace.dialog==null){const c=a.textareaElement,d=document.createElement("div"),e=document.createElement("input"),f=document.createElement("input"),g=document.createElement("input"),h=document.createElement("code"),i=document.createElement("input"),j=document.createElement("details"),k=document.createElement("summary"),l=document.createElement("div"),m=document.createElement("button"),n=document.createElement("button"),o=document.createElement("button"),p=document.createElement("button"),q=document.createElement("span");l.appendChild(m),l.appendChild(n),l.appendChild(o),l.appendChild(p),l.appendChild(q),d.appendChild(l),d.appendChild(e),d.appendChild(g),d.appendChild(f),d.appendChild(h),j.appendChild(k),j.appendChild(i),d.appendChild(j),d.className="code-input_find-and-replace_dialog",e.spellcheck=!1,e.placeholder="Find",f.setAttribute("type","checkbox"),f.title="Match Case Sensitive",f.classList.add("code-input_find-and-replace_case-sensitive-checkbox"),g.setAttribute("type","checkbox"),g.title="Use JavaScript Regular Expression",g.classList.add("code-input_find-and-replace_reg-exp-checkbox"),h.textContent="Search for matches in your code.",h.classList.add("code-input_find-and-replace_match-description"),k.innerText="Replace",i.spellcheck=!1,i.placeholder="Replace with",m.innerText="\u2193",m.title="Find Next Occurence",n.innerText="\u2191",n.title="Find Previous Occurence",o.className="code-input_find-and-replace_button-hidden",o.innerText="Replace",o.title="Replace This Occurence",p.className="code-input_find-and-replace_button-hidden",p.innerText="Replace All",p.title="Replace All Occurences",m.addEventListener("click",a=>{a.preventDefault(),d.findMatchState.nextMatch(),this.updateMatchDescription(d)}),n.addEventListener("click",()=>{event.preventDefault(),d.findMatchState.previousMatch(),this.updateMatchDescription(d)}),o.addEventListener("click",a=>{a.preventDefault(),d.findMatchState.replaceOnce(i.value),o.focus()}),p.addEventListener("click",a=>{a.preventDefault(),d.findMatchState.replaceAll(i.value),p.focus()}),j.addEventListener("toggle",()=>{o.classList.toggle("code-input_find-and-replace_button-hidden"),p.classList.toggle("code-input_find-and-replace_button-hidden")}),d.findMatchState=new codeInput.plugins.FindAndReplace.FindMatchState(a),d.codeInput=a,d.textarea=c,d.findInput=e,d.findCaseSensitiveCheckbox=f,d.findRegExpCheckbox=g,d.matchDescription=h,d.replaceInput=i,d.replaceDropdown=j,this.checkCtrlH&&e.addEventListener("keydown",a=>{a.ctrlKey&&"h"==a.key&&(a.preventDefault(),j.setAttribute("open",!0))}),e.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),i.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),d.addEventListener("keyup",b=>{"Escape"==b.key&&this.cancelPrompt(d,a,b)}),e.addEventListener("keyup",b=>{this.checkFindPrompt(d,a,b)}),e.addEventListener("input",()=>{this.updateFindMatches(d)}),f.addEventListener("click",()=>{this.updateFindMatches(d)}),g.addEventListener("click",()=>{this.updateFindMatches(d)}),i.addEventListener("keyup",b=>{this.checkReplacePrompt(d,a,b),i.focus()}),q.addEventListener("click",b=>{this.cancelPrompt(d,a,b)}),a.dialogContainerElement.appendChild(d),a.pluginData.findAndReplace={dialog:d},e.focus(),b&&j.setAttribute("open",!0),d.selectionStart=a.textareaElement.selectionStart,d.selectionEnd=a.textareaElement.selectionEnd}else a.pluginData.findAndReplace.dialog.classList.remove("code-input_find-and-replace_hidden-dialog"),a.pluginData.findAndReplace.dialog.findInput.focus(),b?a.pluginData.findAndReplace.dialog.replaceDropdown.setAttribute("open",!0):a.pluginData.findAndReplace.dialog.replaceDropdown.removeAttribute("open"),this.updateFindMatches(a.pluginData.findAndReplace.dialog),a.pluginData.findAndReplace.dialog.selectionStart=a.textareaElement.selectionStart,a.pluginData.findAndReplace.dialog.selectionEnd=a.textareaElement.selectionEnd}checkCtrlF(a,b){b.ctrlKey&&"f"==b.key&&(b.preventDefault(),this.showPrompt(a,!1))}checkCtrlH(a,b){b.ctrlKey&&"h"==b.key&&(b.preventDefault(),this.showPrompt(a,!0))}};const CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE=500;codeInput.plugins.FindAndReplace.FindMatchState=class{codeInput=null;lastValue=null;lastSearchRegexp=null;numMatches=0;focusedMatchID=0;matchStartIndexes=[];matchEndIndexes=[];focusedMatchStartIndex=0;matchBlocksHighlighted=[];constructor(a){this.focusedMatchStartIndex=a.textareaElement.selectionStart,this.codeInput=a}clearMatches(){this.numMatches=0,this.matchStartIndexes=[],this.matchEndIndexes=[];let a=this.codeInput.codeElement.querySelectorAll(".code-input_find-and-replace_temporary-span");for(let b=0;b<a.length;b++)a[b].parentElement.replaceChild(new Text(a[b].textContent),a[b]);let b=this.codeInput.codeElement.querySelectorAll(".code-input_find-and-replace_find-match");for(let a=0;a<b.length;a++)b[a].removeAttribute("data-code-input_find-and-replace_match-id"),b[a].classList.remove("code-input_find-and-replace_find-match"),b[a].classList.remove("code-input_find-and-replace_find-match-focused")}updateMatches(a){var b=Math.floor;this.lastSearchRegexp=a,this.lastValue=this.codeInput.value;let c,d=0;this.matchStartIndexes=[],this.matchEndIndexes=[],this.matchBlocksHighlighted=[];let e=b(this.focusedMatchID/CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE);for(let b=0;b<e;b++)this.matchBlocksHighlighted.push(!1);for(this.matchBlocksHighlighted.push(!0);null!==(c=a.exec(this.codeInput.value));){let a=c[0];if(0==a.length)throw SyntaxError("Causes an infinite loop");let e=b(d/CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE);this.matchBlocksHighlighted.length<e&&this.matchBlocksHighlighted.push(!1),this.matchBlocksHighlighted[e]&&this.highlightMatch(d,this.codeInput.codeElement,c.index,c.index+a.length),this.matchStartIndexes.push(c.index),this.matchEndIndexes.push(c.index+a.length),d++}this.numMatches=d,0<this.numMatches&&this.focusMatch()}rehighlightMatches(){this.updateMatches(this.lastSearchRegexp),this.focusMatch()}replaceOnce(a){0<this.numMatches&&a!=this.codeInput.value.substring(0,this.matchStartIndexes[this.focusedMatchID],this.matchEndIndexes[this.focusedMatchID])&&(this.focusedMatchStartIndex+=a.length,this.codeInput.textareaElement.focus(),this.codeInput.textareaElement.selectionStart=this.matchStartIndexes[this.focusedMatchID],this.codeInput.textareaElement.selectionEnd=this.matchEndIndexes[this.focusedMatchID],document.execCommand("insertText",!1,a))}replaceAll(a){const b=a.length;let c=0;for(let d=0;d<this.numMatches;d++)this.codeInput.textareaElement.focus(),this.codeInput.textareaElement.selectionStart=this.matchStartIndexes[d]+c,this.codeInput.textareaElement.selectionEnd=this.matchEndIndexes[d]+c,c+=b-(this.matchEndIndexes[d]-this.matchStartIndexes[d]),document.execCommand("insertText",!1,a)}nextMatch(){this.focusMatch((this.focusedMatchID+1)%this.numMatches)}previousMatch(){this.focusMatch((this.focusedMatchID+this.numMatches-1)%this.numMatches)}focusMatch(a=void 0){if(a===void 0){for(a=0;a<this.matchStartIndexes.length&&this.matchStartIndexes[a]<this.focusedMatchStartIndex;)a++;a>=this.matchStartIndexes.length&&(a=0)}this.focusedMatchStartIndex=this.matchStartIndexes[a],this.focusedMatchID=a;let b=this.codeInput.codeElement.querySelectorAll(".code-input_find-and-replace_find-match-focused");for(let c=0;c<b.length;c++)b[c].classList.remove("code-input_find-and-replace_find-match-focused");let c=Math.floor(a/CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE);if(!this.matchBlocksHighlighted[c]){this.matchBlocksHighlighted[c]=!0;for(let a=CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE*c;a<CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE*(c+1);a++)this.highlightMatch(a,this.codeInput.codeElement,this.matchStartIndexes[a],this.matchEndIndexes[a])}let d=this.codeInput.codeElement.querySelectorAll(`.code-input_find-and-replace_find-match[data-code-input_find-and-replace_match-id="${a}"]`);for(let b=0;b<d.length;b++)d[b].classList.add("code-input_find-and-replace_find-match-focused");0<d.length&&this.codeInput.scrollTo(d[0].offsetLeft-this.codeInput.offsetWidth/2,d[0].offsetTop-this.codeInput.offsetHeight/2)}highlightMatch(a,b,c,d){for(let e=0;e<b.childNodes.length;e++){let f=b.childNodes[e],g=f.textContent,h=!1;if(3==f.nodeType){if(e+1<b.childNodes.length&&3==b.childNodes[e+1].nodeType){b.childNodes[e+1].textContent=f.textContent+b.childNodes[e+1].textContent,b.removeChild(f),e--;continue}h=!0;let a=document.createElement("span");a.textContent=g,a.classList.add("code-input_find-and-replace_temporary-span"),b.replaceChild(a,f),f=a}if(0>=c){if(g.length>=d){if(h){let b=document.createElement("span");b.classList.add("code-input_find-and-replace_find-match"),b.setAttribute("data-code-input_find-and-replace_match-id",a),b.classList.add("code-input_find-and-replace_temporary-span"),b.textContent=g.substring(0,d),"\n"==b.textContent[0]&&b.classList.add("code-input_find-and-replace_start-newline");let c=g.substring(d);return f.textContent=c,f.insertAdjacentElement("beforebegin",b),void e++}return void this.highlightMatch(a,f,0,d)}f.classList.add("code-input_find-and-replace_find-match"),f.setAttribute("data-code-input_find-and-replace_match-id",a),"\n"==f.textContent[0]&&f.classList.add("code-input_find-and-replace_start-newline")}else if(g.length>c){if(!h)this.highlightMatch(a,f,c,d);else if(g.length>d){let b=document.createElement("span");b.classList.add("code-input_find-and-replace_temporary-span"),b.textContent=g.substring(0,c);let h=g.substring(c,d);f.textContent=h,f.classList.add("code-input_find-and-replace_find-match"),f.setAttribute("data-code-input_find-and-replace_match-id",a),"\n"==f.textContent[0]&&f.classList.add("code-input_find-and-replace_start-newline");let i=document.createElement("span");i.classList.add("code-input_find-and-replace_temporary-span"),i.textContent=g.substring(d),f.insertAdjacentElement("beforebegin",b),f.insertAdjacentElement("afterend",i),e++}else{let b=g.substring(0,c);f.textContent=b;let d=document.createElement("span");d.classList.add("code-input_find-and-replace_find-match"),d.setAttribute("data-code-input_find-and-replace_match-id",a),d.classList.add("code-input_find-and-replace_temporary-span"),d.textContent=g.substring(c),"\n"==d.textContent[0]&&d.classList.add("code-input_find-and-replace_start-newline"),f.insertAdjacentElement("afterend",d),e++}if(g.length>d)return}c-=g.length,d-=g.length}}};
|
|
1
|
+
codeInput.plugins.FindAndReplace=class extends codeInput.Plugin{useCtrlF=!1;useCtrlH=!1;findMatchesOnValueChange=!0;instructions={start:"Search for matches in your code.",none:"No matches",oneFound:"1 match found.",matchIndex:(a,b)=>`${a} of ${b} matches.`,error:a=>`Error: ${a}`,infiniteLoopError:"Causes an infinite loop",closeDialog:"Close Dialog and Return to Editor",findPlaceholder:"Find",findCaseSensitive:"Match Case Sensitive",findRegExp:"Use JavaScript Regular Expression",replaceTitle:"Replace",replacePlaceholder:"Replace with",findNext:"Find Next Occurrence",findPrevious:"Find Previous Occurrence",replaceActionShort:"Replace",replaceAction:"Replace This Occurrence",replaceAllActionShort:"Replace All",replaceAllAction:"Replace All Occurrences"};constructor(a=!0,b=!0,c={}){super([]),this.useCtrlF=a,this.useCtrlH=b,this.addTranslations(this.instructions,c)}afterElementsAdded(a){const b=a.textareaElement;this.useCtrlF&&b.addEventListener("keydown",b=>{this.checkCtrlF(a,b)}),this.useCtrlH&&b.addEventListener("keydown",b=>{this.checkCtrlH(a,b)})}afterHighlight(a){a.pluginData.findAndReplace==null||a.pluginData.findAndReplace.dialog==null||a.pluginData.findAndReplace.dialog.classList.contains("code-input_find-and-replace_hidden-dialog")||(a.pluginData.findAndReplace.dialog.findMatchState.rehighlightMatches(),this.updateMatchDescription(a.pluginData.findAndReplace.dialog),0==a.pluginData.findAndReplace.dialog.findMatchState.numMatches&&a.pluginData.findAndReplace.dialog.findInput.classList.add("code-input_find-and-replace_error"))}text2RegExp(a,b,c){return new RegExp(c?a:a.replace(/[.*+?^${}()|[\]\\]/g,"\\$&"),b?"g":"gi")}updateMatchDescription(a){a.matchDescription.textContent=0==a.findInput.value.length?this.instructions.start:0>=a.findMatchState.numMatches?this.instructions.none:1==a.findMatchState.numMatches?this.instructions.oneFound:this.instructions.matchIndex(a.findMatchState.focusedMatchID+1,a.findMatchState.numMatches)}updateFindMatches(a){let b=a.findInput.value;setTimeout(()=>{if(b==a.findInput.value){if(a.findMatchState.clearMatches(),0<b.length){try{a.findMatchState.updateMatches(this.text2RegExp(a.findInput.value,a.findCaseSensitiveCheckbox.checked,a.findRegExpCheckbox.checked))}catch(b){if(b instanceof SyntaxError){a.findInput.classList.add("code-input_find-and-replace_error");let c=b.message.split(": ");return void(a.matchDescription.textContent=this.instructions.error(c[c.length-1]))}throw b}0<a.findMatchState.numMatches?a.findInput.classList.remove("code-input_find-and-replace_error"):a.findInput.classList.add("code-input_find-and-replace_error")}this.updateMatchDescription(a)}},100)}checkFindPrompt(a,b,c){"Enter"==c.key&&(a.findMatchState.nextMatch(),this.updateMatchDescription(a))}checkReplacePrompt(a,b,c){"Enter"==c.key&&(a.findMatchState.replaceOnce(a.replaceInput.value),a.replaceInput.focus(),this.updateMatchDescription(a))}cancelPrompt(a,b,c){c.preventDefault(),this.findMatchesOnValueChange=!1,a.findInput.focus(),a.findInput.selectionStart=0,a.findInput.selectionEnd=a.findInput.value.length,document.execCommand("insertText",!1,a.findInput.value),this.findMatchesOnValueChange=!0,a.textarea.focus(),a.setAttribute("inert",!0),a.setAttribute("tabindex",-1),a.setAttribute("aria-hidden",!0),0<a.findMatchState.numMatches?(b.textareaElement.selectionStart=a.findMatchState.matchStartIndexes[a.findMatchState.focusedMatchID],b.textareaElement.selectionEnd=a.findMatchState.matchEndIndexes[a.findMatchState.focusedMatchID]):(b.textareaElement.selectionStart=a.selectionStart,b.textareaElement.selectionEnd=a.selectionEnd),a.findMatchState.clearMatches(),a.classList.add("code-input_find-and-replace_hidden-dialog")}showPrompt(a,b){let c;if(null==a.pluginData.findAndReplace||null==a.pluginData.findAndReplace.dialog){const d=a.textareaElement;c=document.createElement("div");const e=document.createElement("input"),f=document.createElement("input"),g=document.createElement("input"),h=document.createElement("code");h.setAttribute("aria-live","assertive");const i=document.createElement("input"),j=document.createElement("details"),k=document.createElement("summary"),l=document.createElement("div"),m=document.createElement("button"),n=document.createElement("button"),o=document.createElement("button"),p=document.createElement("button"),q=document.createElement("span");if(q.setAttribute("tabindex",0),q.setAttribute("title",this.instructions.closeDialog),l.appendChild(m),l.appendChild(n),l.appendChild(o),l.appendChild(p),l.appendChild(q),c.appendChild(l),c.appendChild(e),c.appendChild(g),c.appendChild(f),c.appendChild(h),j.appendChild(k),j.appendChild(i),c.appendChild(j),c.className="code-input_find-and-replace_dialog",e.spellcheck=!1,e.placeholder=this.instructions.findPlaceholder,f.setAttribute("type","checkbox"),f.title=this.instructions.findCaseSensitive,f.classList.add("code-input_find-and-replace_case-sensitive-checkbox"),g.setAttribute("type","checkbox"),g.title=this.instructions.findRegExp,g.classList.add("code-input_find-and-replace_reg-exp-checkbox"),h.textContent="Search for matches in your code.",h.classList.add("code-input_find-and-replace_match-description"),k.innerText=this.instructions.replaceTitle,i.spellcheck=!1,i.placeholder=this.instructions.replacePlaceholder,m.innerText="\u2193",m.title=this.instructions.findNext,n.innerText="\u2191",n.title=this.instructions.findPrevious,o.className="code-input_find-and-replace_button-hidden",o.innerText=this.instructions.replaceActionShort,o.title=this.instructions.replaceAction,o.addEventListener("focus",()=>{j.setAttribute("open",!0)}),p.className="code-input_find-and-replace_button-hidden",p.innerText=this.instructions.replaceAllActionShort,p.title=this.instructions.replaceAllAction,p.addEventListener("focus",()=>{j.setAttribute("open",!0)}),m.addEventListener("click",a=>{a.preventDefault(),c.findMatchState.nextMatch(),this.updateMatchDescription(c)}),n.addEventListener("click",()=>{event.preventDefault(),c.findMatchState.previousMatch(),this.updateMatchDescription(c)}),o.addEventListener("click",a=>{a.preventDefault(),c.findMatchState.replaceOnce(i.value),c.focus()}),p.addEventListener("click",a=>{a.preventDefault(),c.findMatchState.replaceAll(i.value),p.focus()}),j.addEventListener("toggle",()=>{o.classList.toggle("code-input_find-and-replace_button-hidden"),p.classList.toggle("code-input_find-and-replace_button-hidden")}),c.findMatchState=new codeInput.plugins.FindAndReplace.FindMatchState(a),c.codeInput=a,c.textarea=d,c.findInput=e,c.findCaseSensitiveCheckbox=f,c.findRegExpCheckbox=g,c.matchDescription=h,c.replaceInput=i,c.replaceDropdown=j,this.checkCtrlH&&e.addEventListener("keydown",a=>{a.ctrlKey&&"h"==a.key&&(a.preventDefault(),j.setAttribute("open",!0))}),e.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),i.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),i.addEventListener("input",()=>{c.classList.contains("code-input_find-and-replace_hidden-dialog")?this.showPrompt(c.codeInput,!0):!c.replaceDropdown.hasAttribute("open")&&c.replaceDropdown.setAttribute("open",!0)}),c.addEventListener("keyup",b=>{"Escape"==b.key&&this.cancelPrompt(c,a,b)}),e.addEventListener("keyup",b=>{this.checkFindPrompt(c,a,b)}),e.addEventListener("input",()=>{this.findMatchesOnValueChange&&this.updateFindMatches(c),c.classList.contains("code-input_find-and-replace_hidden-dialog")&&this.showPrompt(c.codeInput,!1)}),f.addEventListener("click",()=>{this.updateFindMatches(c)}),g.addEventListener("click",()=>{this.updateFindMatches(c)}),i.addEventListener("keyup",b=>{this.checkReplacePrompt(c,a,b),i.focus()}),q.addEventListener("click",b=>{this.cancelPrompt(c,a,b)}),q.addEventListener("keypress",b=>{("Space"==b.key||"Enter"==b.key)&&this.cancelPrompt(c,a,b)}),a.dialogContainerElement.appendChild(c),a.pluginData.findAndReplace={dialog:c},e.focus(),b&&j.setAttribute("open",!0),c.selectionStart=a.textareaElement.selectionStart,c.selectionEnd=a.textareaElement.selectionEnd,c.selectionStart<c.selectionEnd){let b=a.textareaElement.value.substring(c.selectionStart,c.selectionEnd);c.findInput.focus(),c.findInput.selectionStart=0,c.findInput.selectionEnd=c.findInput.value.length,document.execCommand("insertText",!1,b)}}else c=a.pluginData.findAndReplace.dialog,c.classList.remove("code-input_find-and-replace_hidden-dialog"),c.removeAttribute("inert"),c.setAttribute("tabindex",0),c.removeAttribute("aria-hidden"),c.findInput.focus(),b?c.replaceDropdown.setAttribute("open",!0):c.replaceDropdown.removeAttribute("open");if(c.selectionStart=a.textareaElement.selectionStart,c.selectionEnd=a.textareaElement.selectionEnd,c.selectionStart<c.selectionEnd){let b=a.textareaElement.value.substring(c.selectionStart,c.selectionEnd);c.findInput.focus(),c.findInput.selectionStart=0,c.findInput.selectionEnd=c.findInput.value.length,document.execCommand("insertText",!1,b)}this.updateFindMatches(c)}checkCtrlF(a,b){b.ctrlKey&&"f"==b.key&&(b.preventDefault(),this.showPrompt(a,!1))}checkCtrlH(a,b){b.ctrlKey&&"h"==b.key&&(b.preventDefault(),this.showPrompt(a,!0))}};const CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE=500;codeInput.plugins.FindAndReplace.FindMatchState=class{codeInput=null;lastValue=null;lastSearchRegexp=null;numMatches=0;focusedMatchID=0;matchStartIndexes=[];matchEndIndexes=[];focusedMatchStartIndex=0;matchBlocksHighlighted=[];constructor(a){this.focusedMatchStartIndex=a.textareaElement.selectionStart,this.codeInput=a}clearMatches(){this.numMatches=0,this.matchStartIndexes=[],this.matchEndIndexes=[];let a=this.codeInput.codeElement.querySelectorAll(".code-input_find-and-replace_temporary-span");for(let b=0;b<a.length;b++)a[b].parentElement.replaceChild(new Text(a[b].textContent),a[b]);let b=this.codeInput.codeElement.querySelectorAll(".code-input_find-and-replace_find-match");for(let a=0;a<b.length;a++)b[a].removeAttribute("data-code-input_find-and-replace_match-id"),b[a].classList.remove("code-input_find-and-replace_find-match"),b[a].classList.remove("code-input_find-and-replace_find-match-focused")}updateMatches(a){var b=Math.floor;this.lastSearchRegexp=a,this.lastValue=this.codeInput.value;let c,d=0;this.matchStartIndexes=[],this.matchEndIndexes=[],this.matchBlocksHighlighted=[];let e=b(this.focusedMatchID/CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE);for(let b=0;b<e;b++)this.matchBlocksHighlighted.push(!1);for(this.matchBlocksHighlighted.push(!0);null!==(c=a.exec(this.codeInput.value));){let a=c[0];if(0==a.length)throw SyntaxError(this.instructions.infiniteLoopError);let e=b(d/CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE);this.matchBlocksHighlighted.length<e&&this.matchBlocksHighlighted.push(!1),this.matchBlocksHighlighted[e]&&this.highlightMatch(d,this.codeInput.codeElement,c.index,c.index+a.length),this.matchStartIndexes.push(c.index),this.matchEndIndexes.push(c.index+a.length),d++}this.numMatches=d,0<this.numMatches&&this.focusMatch()}rehighlightMatches(){this.updateMatches(this.lastSearchRegexp),this.focusMatch()}replaceOnce(a){0<this.numMatches&&a!=this.codeInput.value.substring(0,this.matchStartIndexes[this.focusedMatchID],this.matchEndIndexes[this.focusedMatchID])&&(this.focusedMatchStartIndex+=a.length,this.codeInput.handleEventsFromTextarea=!1,this.codeInput.textareaElement.focus(),this.codeInput.textareaElement.selectionStart=this.matchStartIndexes[this.focusedMatchID],this.codeInput.textareaElement.selectionEnd=this.matchEndIndexes[this.focusedMatchID],document.execCommand("insertText",!1,a),this.codeInput.handleEventsFromTextarea=!0)}replaceAll(a){const b=a.length;let c=0;for(let d=0;d<this.numMatches;d++)this.codeInput.handleEventsFromTextarea=!1,this.codeInput.textareaElement.focus(),this.codeInput.textareaElement.selectionStart=this.matchStartIndexes[d]+c,this.codeInput.textareaElement.selectionEnd=this.matchEndIndexes[d]+c,c+=b-(this.matchEndIndexes[d]-this.matchStartIndexes[d]),document.execCommand("insertText",!1,a),this.codeInput.handleEventsFromTextarea=!0}nextMatch(){this.focusMatch((this.focusedMatchID+1)%this.numMatches)}previousMatch(){this.focusMatch((this.focusedMatchID+this.numMatches-1)%this.numMatches)}focusMatch(a=void 0){if(a===void 0){for(a=0;a<this.matchStartIndexes.length&&this.matchStartIndexes[a]<this.focusedMatchStartIndex;)a++;a>=this.matchStartIndexes.length&&(a=0)}this.focusedMatchStartIndex=this.matchStartIndexes[a],this.focusedMatchID=a;let b=this.codeInput.codeElement.querySelectorAll(".code-input_find-and-replace_find-match-focused");for(let c=0;c<b.length;c++)b[c].classList.remove("code-input_find-and-replace_find-match-focused");let c=Math.floor(a/CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE);if(!this.matchBlocksHighlighted[c]){this.matchBlocksHighlighted[c]=!0;for(let a=CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE*c;a<CODE_INPUT_FIND_AND_REPLACE_MATCH_BLOCK_SIZE*(c+1);a++)this.highlightMatch(a,this.codeInput.codeElement,this.matchStartIndexes[a],this.matchEndIndexes[a])}let d=this.codeInput.codeElement.querySelectorAll(`.code-input_find-and-replace_find-match[data-code-input_find-and-replace_match-id="${a}"]`);for(let b=0;b<d.length;b++)d[b].classList.add("code-input_find-and-replace_find-match-focused");0<d.length&&this.codeInput.scrollTo(d[0].offsetLeft-this.codeInput.offsetWidth/2,d[0].offsetTop-this.codeInput.offsetHeight/2)}highlightMatch(a,b,c,d){for(let e=0;e<b.childNodes.length;e++){let f=b.childNodes[e],g=f.textContent,h=!1;if(3==f.nodeType){if(e+1<b.childNodes.length&&3==b.childNodes[e+1].nodeType){b.childNodes[e+1].textContent=f.textContent+b.childNodes[e+1].textContent,b.removeChild(f),e--;continue}h=!0;let a=document.createElement("span");a.textContent=g,a.classList.add("code-input_find-and-replace_temporary-span"),b.replaceChild(a,f),f=a}if(0>=c){if(g.length>=d){if(h){let b=document.createElement("span");b.classList.add("code-input_find-and-replace_find-match"),b.setAttribute("data-code-input_find-and-replace_match-id",a),b.classList.add("code-input_find-and-replace_temporary-span"),b.textContent=g.substring(0,d),"\n"==b.textContent[0]&&b.classList.add("code-input_find-and-replace_start-newline");let c=g.substring(d);return f.textContent=c,f.insertAdjacentElement("beforebegin",b),void e++}return void this.highlightMatch(a,f,0,d)}f.classList.add("code-input_find-and-replace_find-match"),f.setAttribute("data-code-input_find-and-replace_match-id",a),"\n"==f.textContent[0]&&f.classList.add("code-input_find-and-replace_start-newline")}else if(g.length>c){if(!h)this.highlightMatch(a,f,c,d);else if(g.length>d){let b=document.createElement("span");b.classList.add("code-input_find-and-replace_temporary-span"),b.textContent=g.substring(0,c);let h=g.substring(c,d);f.textContent=h,f.classList.add("code-input_find-and-replace_find-match"),f.setAttribute("data-code-input_find-and-replace_match-id",a),"\n"==f.textContent[0]&&f.classList.add("code-input_find-and-replace_start-newline");let i=document.createElement("span");i.classList.add("code-input_find-and-replace_temporary-span"),i.textContent=g.substring(d),f.insertAdjacentElement("beforebegin",b),f.insertAdjacentElement("afterend",i),e++}else{let b=g.substring(0,c);f.textContent=b;let d=document.createElement("span");d.classList.add("code-input_find-and-replace_find-match"),d.setAttribute("data-code-input_find-and-replace_match-id",a),d.classList.add("code-input_find-and-replace_temporary-span"),d.textContent=g.substring(c),"\n"==d.textContent[0]&&d.classList.add("code-input_find-and-replace_start-newline"),f.insertAdjacentElement("afterend",d),e++}if(g.length>d)return}c-=g.length,d-=g.length}}};
|
package/plugins/go-to-line.js
CHANGED
|
@@ -5,13 +5,20 @@
|
|
|
5
5
|
codeInput.plugins.GoToLine = class extends codeInput.Plugin {
|
|
6
6
|
useCtrlG = false;
|
|
7
7
|
|
|
8
|
+
instructions = {
|
|
9
|
+
closeDialog: "Close Dialog and Return to Editor",
|
|
10
|
+
input: "Line:Column / Line no. then Enter",
|
|
11
|
+
};
|
|
12
|
+
|
|
8
13
|
/**
|
|
9
14
|
* Create a go-to-line command plugin to pass into a template
|
|
10
15
|
* @param {boolean} useCtrlG Should Ctrl+G be overriden for go-to-line functionality? If not, you can trigger it yourself using (instance of this plugin)`.showPrompt(code-input element)`.
|
|
16
|
+
* @param {Object} instructionTranslations: user interface string keys mapped to translated versions for localisation. Look at the go-to-line.js source code for the available keys and English text.
|
|
11
17
|
*/
|
|
12
|
-
constructor(useCtrlG = true) {
|
|
18
|
+
constructor(useCtrlG = true, instructionTranslations = {}) {
|
|
13
19
|
super([]); // No observed attributes
|
|
14
20
|
this.useCtrlG = useCtrlG;
|
|
21
|
+
this.addTranslations(this.instructions, instructionTranslations);
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
/* Add keystroke events */
|
|
@@ -61,7 +68,12 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin {
|
|
|
61
68
|
/* Called with a dialog box keyup event to close and clear the dialog box */
|
|
62
69
|
cancelPrompt(dialog, event) {
|
|
63
70
|
event.preventDefault();
|
|
71
|
+
dialog.codeInput.handleEventsFromTextarea = false;
|
|
64
72
|
dialog.textarea.focus();
|
|
73
|
+
dialog.codeInput.handleEventsFromTextarea = true;
|
|
74
|
+
dialog.setAttribute("inert", true); // Hide from keyboard navigation when closed.
|
|
75
|
+
dialog.setAttribute("tabindex", -1); // Hide from keyboard navigation when closed.
|
|
76
|
+
dialog.setAttribute("aria-hidden", true); // Hide from screen reader when closed.
|
|
65
77
|
|
|
66
78
|
// Remove dialog after animation
|
|
67
79
|
dialog.classList.add('code-input_go-to-line_hidden-dialog');
|
|
@@ -79,13 +91,15 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin {
|
|
|
79
91
|
const dialog = document.createElement('div');
|
|
80
92
|
const input = document.createElement('input');
|
|
81
93
|
const cancel = document.createElement('span');
|
|
94
|
+
cancel.setAttribute("tabindex", 0); // Visible to keyboard navigation
|
|
95
|
+
cancel.setAttribute("title", this.instructions.closeDialog);
|
|
82
96
|
|
|
83
97
|
dialog.appendChild(input);
|
|
84
98
|
dialog.appendChild(cancel);
|
|
85
99
|
|
|
86
100
|
dialog.className = 'code-input_go-to-line_dialog';
|
|
87
101
|
input.spellcheck = false;
|
|
88
|
-
input.placeholder =
|
|
102
|
+
input.placeholder = this.instructions.input;
|
|
89
103
|
dialog.codeInput = codeInput;
|
|
90
104
|
dialog.textarea = textarea;
|
|
91
105
|
dialog.input = input;
|
|
@@ -97,12 +111,16 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin {
|
|
|
97
111
|
|
|
98
112
|
input.addEventListener('keyup', (event) => { return this.checkPrompt(dialog, event); });
|
|
99
113
|
cancel.addEventListener('click', (event) => { this.cancelPrompt(dialog, event); });
|
|
114
|
+
cancel.addEventListener('keypress', (event) => { if (event.key == "Space" || event.key == "Enter") this.cancelPrompt(dialog, event); });
|
|
100
115
|
|
|
101
116
|
codeInput.dialogContainerElement.appendChild(dialog);
|
|
102
117
|
codeInput.pluginData.goToLine = {dialog: dialog};
|
|
103
118
|
input.focus();
|
|
104
119
|
} else {
|
|
105
120
|
codeInput.pluginData.goToLine.dialog.classList.remove("code-input_go-to-line_hidden-dialog");
|
|
121
|
+
codeInput.pluginData.goToLine.dialog.removeAttribute("inert"); // Show to keyboard navigation when open.
|
|
122
|
+
codeInput.pluginData.goToLine.dialog.setAttribute("tabindex", 0); // Show to keyboard navigation when open.
|
|
123
|
+
codeInput.pluginData.goToLine.dialog.removeAttribute("aria-hidden"); // Show to screen reader when open.
|
|
106
124
|
codeInput.pluginData.goToLine.dialog.input.focus();
|
|
107
125
|
}
|
|
108
126
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
codeInput.plugins.GoToLine=class extends codeInput.Plugin{useCtrlG=!1;constructor(a=!0){super([]),this.useCtrlG=a}afterElementsAdded(a){const b=a.textareaElement;this.useCtrlG&&b.addEventListener("keydown",b=>{this.checkCtrlG(a,b)})}checkPrompt(a,b){const c=a.textarea.value.split("\n"),d=c.length,e=+a.input.value.split(":")[0];let f=0,g=1;const h=a.input.value.split(":");if(2<h.length)return a.input.classList.add("code-input_go-to-line_error");if("Escape"==b.key)return this.cancelPrompt(a,b);if(a.input.value){if(!/^[0-9:]*$/.test(a.input.value)||1>e||e>d)return a.input.classList.add("code-input_go-to-line_error");if(2<=h.length&&(f=+h[1],g=c[e-1].length),0>f||f>g)return a.input.classList.add("code-input_go-to-line_error");a.input.classList.remove("code-input_go-to-line_error")}"Enter"==b.key&&(this.goTo(a.textarea,e,f),this.cancelPrompt(a,b))}cancelPrompt(a,b){b.preventDefault(),a.textarea.focus(),a.classList.add("code-input_go-to-line_hidden-dialog"),a.input.value=""}showPrompt(a){if(a.pluginData.goToLine==null||a.pluginData.goToLine.dialog==null){const b=a.textareaElement,c=document.createElement("div"),d=document.createElement("input"),e=document.createElement("span");c.appendChild(d),c.appendChild(e),c.className="code-input_go-to-line_dialog",d.spellcheck=!1,d.placeholder=
|
|
1
|
+
codeInput.plugins.GoToLine=class extends codeInput.Plugin{useCtrlG=!1;instructions={closeDialog:"Close Dialog and Return to Editor",input:"Line:Column / Line no. then Enter"};constructor(a=!0,b={}){super([]),this.useCtrlG=a,this.addTranslations(this.instructions,b)}afterElementsAdded(a){const b=a.textareaElement;this.useCtrlG&&b.addEventListener("keydown",b=>{this.checkCtrlG(a,b)})}checkPrompt(a,b){const c=a.textarea.value.split("\n"),d=c.length,e=+a.input.value.split(":")[0];let f=0,g=1;const h=a.input.value.split(":");if(2<h.length)return a.input.classList.add("code-input_go-to-line_error");if("Escape"==b.key)return this.cancelPrompt(a,b);if(a.input.value){if(!/^[0-9:]*$/.test(a.input.value)||1>e||e>d)return a.input.classList.add("code-input_go-to-line_error");if(2<=h.length&&(f=+h[1],g=c[e-1].length),0>f||f>g)return a.input.classList.add("code-input_go-to-line_error");a.input.classList.remove("code-input_go-to-line_error")}"Enter"==b.key&&(this.goTo(a.textarea,e,f),this.cancelPrompt(a,b))}cancelPrompt(a,b){b.preventDefault(),a.codeInput.handleEventsFromTextarea=!1,a.textarea.focus(),a.codeInput.handleEventsFromTextarea=!0,a.setAttribute("inert",!0),a.setAttribute("tabindex",-1),a.setAttribute("aria-hidden",!0),a.classList.add("code-input_go-to-line_hidden-dialog"),a.input.value=""}showPrompt(a){if(a.pluginData.goToLine==null||a.pluginData.goToLine.dialog==null){const b=a.textareaElement,c=document.createElement("div"),d=document.createElement("input"),e=document.createElement("span");e.setAttribute("tabindex",0),e.setAttribute("title",this.instructions.closeDialog),c.appendChild(d),c.appendChild(e),c.className="code-input_go-to-line_dialog",d.spellcheck=!1,d.placeholder=this.instructions.input,c.codeInput=a,c.textarea=b,c.input=d,d.addEventListener("keypress",a=>{"Enter"==a.key&&a.preventDefault()}),d.addEventListener("keyup",a=>this.checkPrompt(c,a)),e.addEventListener("click",a=>{this.cancelPrompt(c,a)}),e.addEventListener("keypress",a=>{("Space"==a.key||"Enter"==a.key)&&this.cancelPrompt(c,a)}),a.dialogContainerElement.appendChild(c),a.pluginData.goToLine={dialog:c},d.focus()}else a.pluginData.goToLine.dialog.classList.remove("code-input_go-to-line_hidden-dialog"),a.pluginData.goToLine.dialog.removeAttribute("inert"),a.pluginData.goToLine.dialog.setAttribute("tabindex",0),a.pluginData.goToLine.dialog.removeAttribute("aria-hidden"),a.pluginData.goToLine.dialog.input.focus()}goTo(a,b,c=0){let d,e,f,g,h=-1,i=a.value.split("\n");if(0<b&&b<=i.length){if(a.computedStyleMap?(d=a.computedStyleMap().get("font-size").value,e=d*a.computedStyleMap().get("line-height").value):(d=document.defaultView.getComputedStyle(a,null).getPropertyValue("font-size").split("px")[0],e=document.defaultView.getComputedStyle(a,null).getPropertyValue("line-height").split("px")[0]),f=(3<b?b-3:1)*e,g=(e-d)/2,1<b&&(h=i.slice(0,b-1).join("\n").length),0==c)do h++;while("\n"!=a.value[h]&&/\s/.test(a.value[h]));else h+=1+c-1;a.scrollTop=f-g,a.setSelectionRange(h,h),a.click()}}checkCtrlG(a,b){b.ctrlKey&&"g"==b.key&&(b.preventDefault(),this.showPrompt(a))}};
|