Skip to content

Commit 98c5cc5

Browse files
authored
Merge pull request #69 from WebCoder49/fix-ctrl-f
Fix Ctrl-F in Chromium
2 parents 5c989ae + dc3f4b4 commit 98c5cc5

File tree

4 files changed

+128
-26
lines changed

4 files changed

+128
-26
lines changed

README.md

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,23 @@ The next step is to set up a `template` to link `code-input` to your syntax-high
7676
function(result_element) { /* Highlight function - with `pre code` code element */
7777
/* Highlight code in result_element - code is already escaped so it doesn't become HTML */
7878
},
79-
true, /* Optional - Is the `pre` element styled as well as the `code` element? Changing this to false uses the code element as the scrollable one rather than the pre element */
80-
true, /* Optional - This is used for editing code - setting this to true sets the `code` element's class to `language-<the code-input's lang attribute>` */
81-
false /* Optional - Setting this to true passes the `<code-input>` element as a second argument to the highlight function to be used for getting data- attribute values and using the DOM for the code-input */,
79+
80+
true, /* Optional - Is the `pre` element styled as well as the `code` element?
81+
* Changing this to false uses the code element as the scrollable one rather
82+
* than the pre element */
83+
84+
true, /* Optional - This is used for editing code - setting this to true sets the `code`
85+
* element's class to `language-<the code-input's lang attribute>` */
86+
87+
false /* Optional - Setting this to true passes the `<code-input>` element as a second
88+
* argument to the highlight function to be used for getting data- attribute values
89+
* and using the DOM for the code-input */,
90+
91+
true /* Optional - Leaving this as true uses code-input's default fix for preventing duplicate
92+
* results in Ctrl+F searching from the input and result elements, and setting this to false
93+
* indicates your highlighting function implements its own fix. The default fix works by moving
94+
* text content from elements to CSS ::before pseudo-elements after highlighting. */
95+
8296
[] // Array of plugins (see below)
8397
));
8498
```

code-input.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,11 @@ code-input:not(.code-input_registered)::after {
116116
display: block;
117117
color: grey;
118118
}
119+
120+
/* To prevent Ctrl+F in result element */
121+
code-input pre .code-input_searching-disabled::before {
122+
content: attr(data-content);
123+
}
124+
code-input pre .code-input_searching-disabled {
125+
font-size: 0;
126+
}

code-input.d.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,11 @@ export class Template {
164164
* @param {boolean} preElementStyled - is the `<pre>` element CSS-styled as well as the `<code>` element? If true, `<pre>` element's scrolling is synchronised; if false, `<code>` element's scrolling is synchronised.
165165
* @param {boolean} isCode - is this for writing code? If true, the code-input's lang HTML attribute can be used, and the `<code>` element will be given the class name 'language-[lang attribute's value]'.
166166
* @param {false} includeCodeInputInHighlightFunc - Setting this to true passes the `<code-input>` element as a second argument to the highlight function.
167+
* @param {boolean} autoDisableDuplicateSearching - Leaving this as true uses code-input's default fix for preventing duplicate results in Ctrl+F searching from the input and result elements, and setting this to false indicates your highlighting function implements its own fix. The default fix works by moving text content from elements to CSS `::before` pseudo-elements after highlighting.
167168
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.Plugin`
168169
* @returns template object
169170
*/
170-
constructor(highlight?: (code: HTMLElement) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: false, plugins?: Plugin[])
171+
constructor(highlight?: (code: HTMLElement) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: false, autoDisableDuplicateSearching?: boolean, plugins?: Plugin[])
171172
/**
172173
* **When `includeCodeInputInHighlightFunc` is `true`, `highlight` takes two parameters: the `<pre><code>` element, and the `<code-input>` element.**
173174
*
@@ -177,15 +178,25 @@ export class Template {
177178
* @param {boolean} preElementStyled - is the `<pre>` element CSS-styled as well as the `<code>` element? If true, `<pre>` element's scrolling is synchronised; if false, `<code>` element's scrolling is synchronised.
178179
* @param {boolean} isCode - is this for writing code? If true, the code-input's lang HTML attribute can be used, and the `<code>` element will be given the class name 'language-[lang attribute's value]'.
179180
* @param {true} includeCodeInputInHighlightFunc - Setting this to true passes the `<code-input>` element as a second argument to the highlight function.
181+
* @param {boolean} autoDisableDuplicateSearching - Leaving this as true uses code-input's default fix for preventing duplicate results in Ctrl+F searching from the input and result elements, and setting this to false indicates your highlighting function implements its own fix. The default fix works by moving text content from elements to CSS `::before` pseudo-elements after highlighting.
180182
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.Plugin`
181183
* @returns template object
182184
*/
183-
constructor(highlight?: (code: HTMLElement, codeInput: CodeInput) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: true, plugins?: Plugin[])
185+
constructor(highlight?: (code: HTMLElement, codeInput: CodeInput) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: true, autoDisableDuplicateSearching?: boolean, plugins?: Plugin[])
184186
highlight: Function
185187
preElementStyled: boolean
186188
isCode: boolean
187189
includeCodeInputInHighlightFunc: boolean
190+
autoDisableDuplicateSearching: boolean
188191
plugins: Plugin[]
192+
/**
193+
* @deprecated Please give a value for the `autoDisableDuplicateSearching` parameter.
194+
*/
195+
constructor(highlight?: (code: HTMLElement) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: false, plugins?: Plugin[])
196+
/**
197+
* @deprecated Please give a value for the `autoDisableDuplicateSearching` parameter.
198+
*/
199+
constructor(highlight?: (code: HTMLElement, codeInput: CodeInput) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: true, plugins?: Plugin[])
189200
}
190201

191202
/**

code-input.js

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,12 @@ var codeInput = {
103103
* @param {Object} template - a Template object instance - see `codeInput.templates`
104104
*/
105105
registerTemplate: function (templateName, template) {
106-
if(!(typeof templateName == "string" || templateName instanceof String)) throw TypeError(`code-input: Template for "${templateName}" must be a string.`);
106+
if(!(typeof templateName == "string" || templateName instanceof String)) throw TypeError(`code-input: Name of template "${templateName}" must be a string.`);
107107
if(!(typeof template.highlight == "function" || template.highlight instanceof Function)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the highlight function provided is not a function; it is "${template.highlight}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
108108
if(!(typeof template.includeCodeInputInHighlightFunc == "boolean" || template.includeCodeInputInHighlightFunc instanceof Boolean)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the includeCodeInputInHighlightFunc value provided is not a true or false; it is "${template.includeCodeInputInHighlightFunc}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
109109
if(!(typeof template.preElementStyled == "boolean" || template.preElementStyled instanceof Boolean)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the preElementStyled value provided is not a true or false; it is "${template.preElementStyled}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
110110
if(!(typeof template.isCode == "boolean" || template.isCode instanceof Boolean)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the isCode value provided is not a true or false; it is "${template.isCode}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
111+
if(!(typeof template.autoDisableDuplicateSearching == "boolean" || template.autoDisableDuplicateSearching instanceof Boolean)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the autoDisableDuplicateSearching value provided is not a true or false; it is "${template.autoDisableDuplicateSearching}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
111112
if(!Array.isArray(template.plugins)) throw TypeError(`code-input: Template for "${templateName}" invalid, because the plugin array provided is not an array; it is "${template.plugins}". Please make sure you use one of the constructors in codeInput.templates, and that you provide the correct arguments.`);
112113

113114
template.plugins.forEach((plugin, i) => {
@@ -163,13 +164,20 @@ var codeInput = {
163164
* @param {boolean} isCode - is this for writing code? If true, the code-input's lang HTML attribute can be used, and the `<code>` element will be given the class name 'language-[lang attribute's value]'.
164165
* @param {boolean} includeCodeInputInHighlightFunc - Setting this to true passes the `<code-input>` element as a second argument to the highlight function.
165166
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.Plugin`
166-
* @returns template object
167+
* @returns {codeInput.Template} template object
167168
*/
168-
constructor(highlight = function () { }, preElementStyled = true, isCode = true, includeCodeInputInHighlightFunc = false, plugins = []) {
169+
constructor(highlight = function () { }, preElementStyled = true, isCode = true, includeCodeInputInHighlightFunc = false, autoDisableDuplicateSearching = true, plugins = []) {
170+
// @deprecated to support old function signature without autoDisableDuplicateSearching
171+
if(Array.isArray(autoDisableDuplicateSearching)) {
172+
plugins = autoDisableDuplicateSearching;
173+
autoDisableDuplicateSearching = true;
174+
}
175+
169176
this.highlight = highlight;
170177
this.preElementStyled = preElementStyled;
171178
this.isCode = isCode;
172179
this.includeCodeInputInHighlightFunc = includeCodeInputInHighlightFunc;
180+
this.autoDisableDuplicateSearching = autoDisableDuplicateSearching;
173181
this.plugins = plugins;
174182
}
175183

@@ -179,7 +187,7 @@ var codeInput = {
179187
* `<code-input>` element parameter if `this.includeCodeInputInHighlightFunc` is
180188
* `true`.
181189
*/
182-
highlight = function() {};
190+
highlight = function(codeElement) {};
183191

184192
/**
185193
* Is the <pre> element CSS-styled as well as the `<code>` element?
@@ -202,6 +210,15 @@ var codeInput = {
202210
*/
203211
includeCodeInputInHighlightFunc = false;
204212

213+
/**
214+
* Leaving this as true uses code-input's default fix for preventing duplicate results in Ctrl+F searching
215+
* from the input and result elements, and setting this to false indicates your highlighting function implements
216+
* its own fix.
217+
*
218+
* The default fix works by moving text content from elements to CSS ::before pseudo-elements after highlighting.
219+
*/
220+
autoDisableDuplicateSearching = true;
221+
205222
/**
206223
* An array of plugin objects to add extra features -
207224
* see `codeInput.Plugin`.
@@ -226,31 +243,36 @@ var codeInput = {
226243
* Constructor to create a template that uses Prism.js syntax highlighting (https://prismjs.com/)
227244
* @param {Object} prism Import Prism.js, then after that import pass the `Prism` object as this parameter.
228245
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.plugins`
229-
* @returns template object
246+
* @returns {codeInput.Template} template object
230247
*/
231248
prism(prism, plugins = []) { // Dependency: Prism.js (https://prismjs.com/)
232-
return {
233-
includeCodeInputInHighlightFunc: false,
234-
highlight: prism.highlightElement,
235-
preElementStyled: true,
236-
isCode: true,
237-
plugins: plugins,
238-
};
249+
return new codeInput.Template(
250+
prism.highlightElement, // highlight
251+
true, // preElementStyled
252+
true, // isCode
253+
false, // includeCodeInputInHighlightFunc
254+
true, // autoDisableDuplicateSearching
255+
plugins
256+
);
239257
},
240258
/**
241259
* Constructor to create a template that uses highlight.js syntax highlighting (https://highlightjs.org/)
242260
* @param {Object} hljs Import highlight.js, then after that import pass the `hljs` object as this parameter.
243261
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.plugins`
244-
* @returns template object
262+
* @returns {codeInput.Template} template object
245263
*/
246264
hljs(hljs, plugins = []) { // Dependency: Highlight.js (https://highlightjs.org/)
247-
return {
248-
includeCodeInputInHighlightFunc: false,
249-
highlight: hljs.highlightElement,
250-
preElementStyled: false,
251-
isCode: true,
252-
plugins: plugins,
253-
};
265+
return new codeInput.Template(
266+
function(codeElement) {
267+
codeElement.removeAttribute("data-highlighted");
268+
hljs.highlightElement(codeElement);
269+
}, // highlight
270+
false, // preElementStyled
271+
true, // isCode
272+
false, // includeCodeInputInHighlightFunc
273+
true, // autoDisableDuplicateSearching
274+
plugins
275+
);
254276
},
255277

256278
/**
@@ -275,6 +297,7 @@ var codeInput = {
275297
includeCodeInputInHighlightFunc: true,
276298
preElementStyled: true,
277299
isCode: false,
300+
autoDisableDuplicateSearching: true,
278301
plugins: plugins,
279302
}
280303
},
@@ -299,8 +322,11 @@ var codeInput = {
299322
includeCodeInputInHighlightFunc: true,
300323
preElementStyled: true,
301324
isCode: false,
325+
autoDisableDuplicateSearching: true,
326+
302327
rainbowColors: rainbowColors,
303328
delimiter: delimiter,
329+
304330
plugins: plugins,
305331
}
306332
},
@@ -507,7 +533,6 @@ var codeInput = {
507533
this.ignoreValueUpdate = false;
508534
if (this.textareaElement.value != value) this.textareaElement.value = value;
509535

510-
511536
let resultElement = this.codeElement;
512537

513538
// Handle final newlines
@@ -524,6 +549,26 @@ var codeInput = {
524549
else this.template.highlight(resultElement);
525550

526551
this.pluginEvt("afterHighlight");
552+
553+
if(this.template.autoDisableDuplicateSearching) {
554+
if(this.codeElement.querySelector("*") === null) {
555+
// Fix for tries-to-disable-searching-before-highlighting-possible bug:
556+
// Wait until user interaction so can expect
557+
// highlight before disabling searching
558+
let listenerKeydown = window.addEventListener("keydown", () => {
559+
this.resultElementDisableSearching();
560+
window.removeEventListener("keydown", listenerKeydown);
561+
window.removeEventListener("mousemove", listenerMousemove);
562+
});
563+
let listenerMousemove = window.addEventListener("mousemove", () => {
564+
this.resultElementDisableSearching();
565+
window.removeEventListener("keydown", listenerKeydown);
566+
window.removeEventListener("mousemove", listenerMousemove);
567+
});
568+
} else {
569+
this.resultElementDisableSearching();
570+
}
571+
}
527572
}
528573

529574
/**
@@ -555,6 +600,30 @@ var codeInput = {
555600
return text.replace(new RegExp("&amp;", "g"), "&").replace(new RegExp("&lt;", "g"), "<").replace(new RegExp("&gt;", "g"), ">"); /* Global RegExp */
556601
}
557602

603+
/**
604+
* Make the text contents of highlighted code in the `<pre><code>` result element invisible to Ctrl+F by moving them to a data attribute
605+
* then the CSS `::before` pseudo-element on span elements with the class code-input_searching-disabled. This function is called recursively
606+
* on all child elements of the <code> element.
607+
*
608+
* @param {HTMLElement} element The element on which this is carried out recursively. Optional - defaults to the `<pre><code>`'s `<code>` element.
609+
*/
610+
resultElementDisableSearching(element=this.preElement) {
611+
for (let i = 0; i < element.childNodes.length; i++) {
612+
let content = element.childNodes[i].textContent;
613+
614+
if (element.childNodes[i].nodeType == 3) {
615+
// Turn plain text node into span element
616+
element.replaceChild(document.createElement('span'), element.childNodes[i]);
617+
element.childNodes[i].classList.add("code-input_searching-disabled")
618+
element.childNodes[i].setAttribute("data-content", content);
619+
element.childNodes[i].innerText = '';
620+
} else {
621+
// Recurse deeper
622+
this.resultElementDisableSearching(element.childNodes[i]);
623+
}
624+
}
625+
}
626+
558627
/**
559628
* Get the template object this code-input element is using.
560629
* @returns {Object} - Template object

0 commit comments

Comments
 (0)