@webcoder49/code-input 2.2.1 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -4
- package/code-input.css +92 -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/auto-close-brackets.js +23 -7
- package/plugins/auto-close-brackets.min.js +1 -1
- package/plugins/autocomplete.js +4 -2
- package/plugins/autocomplete.min.js +1 -1
- package/plugins/find-and-replace.js +142 -42
- package/plugins/find-and-replace.min.js +1 -1
- package/plugins/go-to-line.js +24 -2
- package/plugins/go-to-line.min.js +1 -1
- package/plugins/indent.js +121 -7
- 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 +90 -19
- 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');
|
|
@@ -164,7 +204,13 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
164
204
|
const findPreviousButton = document.createElement('button');
|
|
165
205
|
const replaceButton = document.createElement('button');
|
|
166
206
|
const replaceAllButton = document.createElement('button');
|
|
207
|
+
|
|
208
|
+
// TODO: Make a button element (semantic HTML for accessibility) in next major version
|
|
167
209
|
const cancel = document.createElement('span');
|
|
210
|
+
cancel.setAttribute("role", "button");
|
|
211
|
+
cancel.setAttribute("aria-label", this.instructions.closeDialog);
|
|
212
|
+
cancel.setAttribute("tabindex", 0); // Visible to keyboard navigation
|
|
213
|
+
cancel.setAttribute("title", this.instructions.closeDialog);
|
|
168
214
|
|
|
169
215
|
buttonContainer.appendChild(findNextButton);
|
|
170
216
|
buttonContainer.appendChild(findPreviousButton);
|
|
@@ -184,31 +230,41 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
184
230
|
|
|
185
231
|
dialog.className = 'code-input_find-and-replace_dialog';
|
|
186
232
|
findInput.spellcheck = false;
|
|
187
|
-
findInput.placeholder =
|
|
233
|
+
findInput.placeholder = this.instructions.findPlaceholder;
|
|
188
234
|
findCaseSensitiveCheckbox.setAttribute("type", "checkbox");
|
|
189
|
-
findCaseSensitiveCheckbox.title =
|
|
235
|
+
findCaseSensitiveCheckbox.title = this.instructions.findCaseSensitive;
|
|
190
236
|
findCaseSensitiveCheckbox.classList.add("code-input_find-and-replace_case-sensitive-checkbox");
|
|
191
237
|
findRegExpCheckbox.setAttribute("type", "checkbox");
|
|
192
|
-
findRegExpCheckbox.title =
|
|
238
|
+
findRegExpCheckbox.title = this.instructions.findRegExp;
|
|
193
239
|
findRegExpCheckbox.classList.add("code-input_find-and-replace_reg-exp-checkbox");
|
|
194
240
|
|
|
195
241
|
matchDescription.textContent = "Search for matches in your code.";
|
|
196
242
|
matchDescription.classList.add("code-input_find-and-replace_match-description");
|
|
197
243
|
|
|
198
244
|
|
|
199
|
-
replaceSummary.innerText =
|
|
245
|
+
replaceSummary.innerText = this.instructions.replaceTitle;
|
|
200
246
|
replaceInput.spellcheck = false;
|
|
201
|
-
replaceInput.placeholder =
|
|
247
|
+
replaceInput.placeholder = this.instructions.replacePlaceholder;
|
|
202
248
|
findNextButton.innerText = "↓";
|
|
203
|
-
findNextButton.title =
|
|
249
|
+
findNextButton.title = this.instructions.findNext;
|
|
250
|
+
findNextButton.setAttribute("aria-label", this.instructions.findNext);
|
|
204
251
|
findPreviousButton.innerText = "↑";
|
|
205
|
-
findPreviousButton.title =
|
|
252
|
+
findPreviousButton.title = this.instructions.findPrevious;
|
|
253
|
+
findNextButton.setAttribute("aria-label", this.instructions.findPrevious);
|
|
206
254
|
replaceButton.className = 'code-input_find-and-replace_button-hidden';
|
|
207
|
-
replaceButton.innerText =
|
|
208
|
-
replaceButton.title =
|
|
255
|
+
replaceButton.innerText = this.instructions.replaceActionShort;
|
|
256
|
+
replaceButton.title = this.instructions.replaceAction;
|
|
257
|
+
replaceButton.addEventListener("focus", () => {
|
|
258
|
+
// Show replace section
|
|
259
|
+
replaceDropdown.setAttribute("open", true);
|
|
260
|
+
});
|
|
209
261
|
replaceAllButton.className = 'code-input_find-and-replace_button-hidden';
|
|
210
|
-
replaceAllButton.innerText =
|
|
211
|
-
replaceAllButton.title =
|
|
262
|
+
replaceAllButton.innerText = this.instructions.replaceAllActionShort;
|
|
263
|
+
replaceAllButton.title = this.instructions.replaceAllAction;
|
|
264
|
+
replaceAllButton.addEventListener("focus", () => {
|
|
265
|
+
// Show replace section
|
|
266
|
+
replaceDropdown.setAttribute("open", true);
|
|
267
|
+
});
|
|
212
268
|
|
|
213
269
|
findNextButton.addEventListener("click", (event) => {
|
|
214
270
|
// Stop form submit
|
|
@@ -229,7 +285,7 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
229
285
|
event.preventDefault();
|
|
230
286
|
|
|
231
287
|
dialog.findMatchState.replaceOnce(replaceInput.value);
|
|
232
|
-
|
|
288
|
+
dialog.focus();
|
|
233
289
|
});
|
|
234
290
|
replaceAllButton.addEventListener("click", (event) => {
|
|
235
291
|
// Stop form submit
|
|
@@ -275,6 +331,16 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
275
331
|
/* Stop enter from submitting form */
|
|
276
332
|
if (event.key == 'Enter') event.preventDefault();
|
|
277
333
|
});
|
|
334
|
+
replaceInput.addEventListener('input', (event) => {
|
|
335
|
+
// Ctrl+Z can trigger this. If the dialog/replace dropdown aren't open, open them!
|
|
336
|
+
if(dialog.classList.contains("code-input_find-and-replace_hidden-dialog")) {
|
|
337
|
+
// Show prompt
|
|
338
|
+
this.showPrompt(dialog.codeInput, true);
|
|
339
|
+
} else if(!dialog.replaceDropdown.hasAttribute("open")) {
|
|
340
|
+
// Open dropdown
|
|
341
|
+
dialog.replaceDropdown.setAttribute("open", true);
|
|
342
|
+
}
|
|
343
|
+
});
|
|
278
344
|
|
|
279
345
|
dialog.addEventListener('keyup', (event) => {
|
|
280
346
|
/* Close prompt on Enter pressed */
|
|
@@ -282,7 +348,13 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
282
348
|
});
|
|
283
349
|
|
|
284
350
|
findInput.addEventListener('keyup', (event) => { this.checkFindPrompt(dialog, codeInputElement, event); });
|
|
285
|
-
findInput.addEventListener('input', (event) => {
|
|
351
|
+
findInput.addEventListener('input', (event) => {
|
|
352
|
+
if(this.findMatchesOnValueChange) this.updateFindMatches(dialog);
|
|
353
|
+
// Ctrl+Z can trigger this. If the dialog isn't open, open it!
|
|
354
|
+
if(dialog.classList.contains("code-input_find-and-replace_hidden-dialog")) {
|
|
355
|
+
this.showPrompt(dialog.codeInput, false);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
286
358
|
findCaseSensitiveCheckbox.addEventListener('click', (event) => { this.updateFindMatches(dialog); });
|
|
287
359
|
findRegExpCheckbox.addEventListener('click', (event) => { this.updateFindMatches(dialog); });
|
|
288
360
|
|
|
@@ -291,6 +363,7 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
291
363
|
replaceInput.focus();
|
|
292
364
|
});
|
|
293
365
|
cancel.addEventListener('click', (event) => { this.cancelPrompt(dialog, codeInputElement, event); });
|
|
366
|
+
cancel.addEventListener('keypress', (event) => { if (event.key == "Space" || event.key == "Enter") this.cancelPrompt(dialog, codeInputElement, event); });
|
|
294
367
|
|
|
295
368
|
codeInputElement.dialogContainerElement.appendChild(dialog);
|
|
296
369
|
codeInputElement.pluginData.findAndReplace = {dialog: dialog};
|
|
@@ -303,24 +376,45 @@ codeInput.plugins.FindAndReplace = class extends codeInput.Plugin {
|
|
|
303
376
|
// Save selection position
|
|
304
377
|
dialog.selectionStart = codeInputElement.textareaElement.selectionStart;
|
|
305
378
|
dialog.selectionEnd = codeInputElement.textareaElement.selectionEnd;
|
|
379
|
+
|
|
380
|
+
if(dialog.selectionStart < dialog.selectionEnd) {
|
|
381
|
+
// Copy selected text to Find input
|
|
382
|
+
let textToFind = codeInputElement.textareaElement.value.substring(dialog.selectionStart, dialog.selectionEnd);
|
|
383
|
+
dialog.findInput.focus();
|
|
384
|
+
dialog.findInput.selectionStart = 0;
|
|
385
|
+
dialog.findInput.selectionEnd = dialog.findInput.value.length;
|
|
386
|
+
document.execCommand("insertText", false, textToFind);
|
|
387
|
+
}
|
|
306
388
|
} else {
|
|
389
|
+
dialog = codeInputElement.pluginData.findAndReplace.dialog;
|
|
307
390
|
// Re-open dialog
|
|
308
|
-
|
|
309
|
-
|
|
391
|
+
dialog.classList.remove("code-input_find-and-replace_hidden-dialog");
|
|
392
|
+
dialog.removeAttribute("inert"); // Show to keyboard navigation when open.
|
|
393
|
+
dialog.setAttribute("tabindex", 0); // Show to keyboard navigation when open.
|
|
394
|
+
dialog.removeAttribute("aria-hidden"); // Show to screen reader when open.
|
|
395
|
+
dialog.findInput.focus();
|
|
310
396
|
if(replacePartExpanded) {
|
|
311
|
-
|
|
397
|
+
dialog.replaceDropdown.setAttribute("open", true);
|
|
312
398
|
} else {
|
|
313
|
-
|
|
399
|
+
dialog.replaceDropdown.removeAttribute("open");
|
|
314
400
|
}
|
|
401
|
+
}
|
|
315
402
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
codeInputElement.
|
|
403
|
+
// Save selection position
|
|
404
|
+
dialog.selectionStart = codeInputElement.textareaElement.selectionStart;
|
|
405
|
+
dialog.selectionEnd = codeInputElement.textareaElement.selectionEnd;
|
|
406
|
+
|
|
407
|
+
if(dialog.selectionStart < dialog.selectionEnd) {
|
|
408
|
+
// Copy selected text to Find input
|
|
409
|
+
let textToFind = codeInputElement.textareaElement.value.substring(dialog.selectionStart, dialog.selectionEnd);
|
|
410
|
+
dialog.findInput.focus();
|
|
411
|
+
dialog.findInput.selectionStart = 0;
|
|
412
|
+
dialog.findInput.selectionEnd = dialog.findInput.value.length;
|
|
413
|
+
document.execCommand("insertText", false, textToFind);
|
|
323
414
|
}
|
|
415
|
+
|
|
416
|
+
// Highlight matches
|
|
417
|
+
this.updateFindMatches(dialog);
|
|
324
418
|
}
|
|
325
419
|
|
|
326
420
|
/* Event handler for keydown event that makes Ctrl+F open find dialog */
|
|
@@ -405,7 +499,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
405
499
|
while ((match = searchRegexp.exec(this.codeInput.value)) !== null) {
|
|
406
500
|
let matchText = match[0];
|
|
407
501
|
if(matchText.length == 0) {
|
|
408
|
-
throw SyntaxError(
|
|
502
|
+
throw SyntaxError(this.instructions.infiniteLoopError);
|
|
409
503
|
}
|
|
410
504
|
|
|
411
505
|
// Add next match block if needed
|
|
@@ -441,12 +535,14 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
441
535
|
this.focusedMatchStartIndex += replacementText.length;
|
|
442
536
|
|
|
443
537
|
// Select the match
|
|
538
|
+
this.codeInput.handleEventsFromTextarea = false;
|
|
444
539
|
this.codeInput.textareaElement.focus();
|
|
445
540
|
this.codeInput.textareaElement.selectionStart = this.matchStartIndexes[this.focusedMatchID];
|
|
446
541
|
this.codeInput.textareaElement.selectionEnd = this.matchEndIndexes[this.focusedMatchID];
|
|
447
542
|
|
|
448
543
|
// Replace it with the replacement text
|
|
449
544
|
document.execCommand("insertText", false, replacementText);
|
|
545
|
+
this.codeInput.handleEventsFromTextarea = true;
|
|
450
546
|
}
|
|
451
547
|
}
|
|
452
548
|
|
|
@@ -459,6 +555,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
459
555
|
// Replace each match
|
|
460
556
|
|
|
461
557
|
// Select the match, taking into account characters added before
|
|
558
|
+
this.codeInput.handleEventsFromTextarea = false;
|
|
462
559
|
this.codeInput.textareaElement.focus();
|
|
463
560
|
this.codeInput.textareaElement.selectionStart = this.matchStartIndexes[i] + numCharsAdded;
|
|
464
561
|
this.codeInput.textareaElement.selectionEnd = this.matchEndIndexes[i] + numCharsAdded;
|
|
@@ -467,6 +564,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
467
564
|
|
|
468
565
|
// Replace it with the replacement text
|
|
469
566
|
document.execCommand("insertText", false, replacementText);
|
|
567
|
+
this.codeInput.handleEventsFromTextarea = true;
|
|
470
568
|
}
|
|
471
569
|
}
|
|
472
570
|
|
|
@@ -527,7 +625,8 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
527
625
|
|
|
528
626
|
/* Highlight a match from the find functionality given its start and end indexes in the text.
|
|
529
627
|
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.
|
|
628
|
+
of the match so different matches can be identified.
|
|
629
|
+
This code is similar to codeInput.plugins.SelectTokenCallbacks.SelectedTokenState.updateSelectedTokens*/
|
|
531
630
|
highlightMatch(matchID, currentElement, startIndex, endIndex) {
|
|
532
631
|
for(let i = 0; i < currentElement.childNodes.length; i++) {
|
|
533
632
|
let childElement = currentElement.childNodes[i];
|
|
@@ -535,6 +634,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
535
634
|
|
|
536
635
|
let noInnerElements = false;
|
|
537
636
|
if(childElement.nodeType == 3) {
|
|
637
|
+
// Text node
|
|
538
638
|
if(i + 1 < currentElement.childNodes.length && currentElement.childNodes[i+1].nodeType == 3) {
|
|
539
639
|
// Can merge with next text node
|
|
540
640
|
currentElement.childNodes[i+1].textContent = childElement.textContent + currentElement.childNodes[i+1].textContent; // Merge textContent with next node
|
|
@@ -547,7 +647,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
547
647
|
|
|
548
648
|
let replacementElement = document.createElement("span");
|
|
549
649
|
replacementElement.textContent = childText;
|
|
550
|
-
replacementElement.classList.add("code-input_find-and-replace_temporary-span"); // Can remove
|
|
650
|
+
replacementElement.classList.add("code-input_find-and-replace_temporary-span"); // Can remove span later
|
|
551
651
|
|
|
552
652
|
currentElement.replaceChild(replacementElement, childElement);
|
|
553
653
|
childElement = replacementElement;
|
|
@@ -562,7 +662,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
562
662
|
let startSpan = document.createElement("span");
|
|
563
663
|
startSpan.classList.add("code-input_find-and-replace_find-match"); // Highlighted
|
|
564
664
|
startSpan.setAttribute("data-code-input_find-and-replace_match-id", matchID);
|
|
565
|
-
startSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove
|
|
665
|
+
startSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove span later
|
|
566
666
|
startSpan.textContent = childText.substring(0, endIndex);
|
|
567
667
|
if(startSpan.textContent[0] == "\n") {
|
|
568
668
|
// Newline at start - make clear
|
|
@@ -597,7 +697,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
597
697
|
// Match starts and ends in childElement - highlight middle part
|
|
598
698
|
// Text node - highlight last part
|
|
599
699
|
let startSpan = document.createElement("span");
|
|
600
|
-
startSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove
|
|
700
|
+
startSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove span later
|
|
601
701
|
startSpan.textContent = childText.substring(0, startIndex);
|
|
602
702
|
|
|
603
703
|
let middleText = childText.substring(startIndex, endIndex);
|
|
@@ -610,7 +710,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
610
710
|
}
|
|
611
711
|
|
|
612
712
|
let endSpan = document.createElement("span");
|
|
613
|
-
endSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove
|
|
713
|
+
endSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove span later
|
|
614
714
|
endSpan.textContent = childText.substring(endIndex);
|
|
615
715
|
|
|
616
716
|
childElement.insertAdjacentElement('beforebegin', startSpan);
|
|
@@ -624,7 +724,7 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
624
724
|
let endSpan = document.createElement("span");
|
|
625
725
|
endSpan.classList.add("code-input_find-and-replace_find-match"); // Highlighted
|
|
626
726
|
endSpan.setAttribute("data-code-input_find-and-replace_match-id", matchID);
|
|
627
|
-
endSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove
|
|
727
|
+
endSpan.classList.add("code-input_find-and-replace_temporary-span"); // Can remove span later
|
|
628
728
|
endSpan.textContent = childText.substring(startIndex);
|
|
629
729
|
if(endSpan.textContent[0] == "\n") {
|
|
630
730
|
// Newline at start - make clear
|
|
@@ -649,4 +749,4 @@ codeInput.plugins.FindAndReplace.FindMatchState = class {
|
|
|
649
749
|
endIndex -= childText.length;
|
|
650
750
|
}
|
|
651
751
|
}
|
|
652
|
-
}
|
|
752
|
+
}
|
|
@@ -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("role","button"),q.setAttribute("aria-label",this.instructions.closeDialog),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,m.setAttribute("aria-label",this.instructions.findNext),n.innerText="\u2191",n.title=this.instructions.findPrevious,m.setAttribute("aria-label",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');
|
|
@@ -78,14 +90,20 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin {
|
|
|
78
90
|
|
|
79
91
|
const dialog = document.createElement('div');
|
|
80
92
|
const input = document.createElement('input');
|
|
93
|
+
|
|
94
|
+
// TODO: Make a button element (semantic HTML for accessibility) in next major version
|
|
81
95
|
const cancel = document.createElement('span');
|
|
96
|
+
cancel.setAttribute("role", "button");
|
|
97
|
+
cancel.setAttribute("aria-label", this.instructions.closeDialog);
|
|
98
|
+
cancel.setAttribute("tabindex", 0); // Visible to keyboard navigation
|
|
99
|
+
cancel.setAttribute("title", this.instructions.closeDialog);
|
|
82
100
|
|
|
83
101
|
dialog.appendChild(input);
|
|
84
102
|
dialog.appendChild(cancel);
|
|
85
103
|
|
|
86
104
|
dialog.className = 'code-input_go-to-line_dialog';
|
|
87
105
|
input.spellcheck = false;
|
|
88
|
-
input.placeholder =
|
|
106
|
+
input.placeholder = this.instructions.input;
|
|
89
107
|
dialog.codeInput = codeInput;
|
|
90
108
|
dialog.textarea = textarea;
|
|
91
109
|
dialog.input = input;
|
|
@@ -97,12 +115,16 @@ codeInput.plugins.GoToLine = class extends codeInput.Plugin {
|
|
|
97
115
|
|
|
98
116
|
input.addEventListener('keyup', (event) => { return this.checkPrompt(dialog, event); });
|
|
99
117
|
cancel.addEventListener('click', (event) => { this.cancelPrompt(dialog, event); });
|
|
118
|
+
cancel.addEventListener('keypress', (event) => { if (event.key == "Space" || event.key == "Enter") this.cancelPrompt(dialog, event); });
|
|
100
119
|
|
|
101
120
|
codeInput.dialogContainerElement.appendChild(dialog);
|
|
102
121
|
codeInput.pluginData.goToLine = {dialog: dialog};
|
|
103
122
|
input.focus();
|
|
104
123
|
} else {
|
|
105
124
|
codeInput.pluginData.goToLine.dialog.classList.remove("code-input_go-to-line_hidden-dialog");
|
|
125
|
+
codeInput.pluginData.goToLine.dialog.removeAttribute("inert"); // Show to keyboard navigation when open.
|
|
126
|
+
codeInput.pluginData.goToLine.dialog.setAttribute("tabindex", 0); // Show to keyboard navigation when open.
|
|
127
|
+
codeInput.pluginData.goToLine.dialog.removeAttribute("aria-hidden"); // Show to screen reader when open.
|
|
106
128
|
codeInput.pluginData.goToLine.dialog.input.focus();
|
|
107
129
|
}
|
|
108
130
|
}
|
|
@@ -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("role","button"),e.setAttribute("aria-label",this.instructions.closeDialog),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))}};
|