Skip to content

Commit

Permalink
Merge pull request #69 from WebCoder49/fix-ctrl-f
Browse files Browse the repository at this point in the history
Fix Ctrl-F in Chromium
  • Loading branch information
WebCoder49 committed Dec 8, 2023
2 parents 5c989ae + dc3f4b4 commit 98c5cc5
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 26 deletions.
20 changes: 17 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,23 @@ The next step is to set up a `template` to link `code-input` to your syntax-high
function(result_element) { /* Highlight function - with `pre code` code element */
/* Highlight code in result_element - code is already escaped so it doesn't become HTML */
},
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 */
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>` */
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 */,

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 */

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>` */

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 */,

true /* Optional - 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. */

[] // Array of plugins (see below)
));
```
Expand Down
8 changes: 8 additions & 0 deletions code-input.css
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,11 @@ code-input:not(.code-input_registered)::after {
display: block;
color: grey;
}

/* To prevent Ctrl+F in result element */
code-input pre .code-input_searching-disabled::before {
content: attr(data-content);
}
code-input pre .code-input_searching-disabled {
font-size: 0;
}
15 changes: 13 additions & 2 deletions code-input.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,11 @@ export class Template {
* @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.
* @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]'.
* @param {false} includeCodeInputInHighlightFunc - Setting this to true passes the `<code-input>` element as a second argument to the highlight function.
* @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.
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.Plugin`
* @returns template object
*/
constructor(highlight?: (code: HTMLElement) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: false, plugins?: Plugin[])
constructor(highlight?: (code: HTMLElement) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: false, autoDisableDuplicateSearching?: boolean, plugins?: Plugin[])
/**
* **When `includeCodeInputInHighlightFunc` is `true`, `highlight` takes two parameters: the `<pre><code>` element, and the `<code-input>` element.**
*
Expand All @@ -177,15 +178,25 @@ export class Template {
* @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.
* @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]'.
* @param {true} includeCodeInputInHighlightFunc - Setting this to true passes the `<code-input>` element as a second argument to the highlight function.
* @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.
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.Plugin`
* @returns template object
*/
constructor(highlight?: (code: HTMLElement, codeInput: CodeInput) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: true, plugins?: Plugin[])
constructor(highlight?: (code: HTMLElement, codeInput: CodeInput) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: true, autoDisableDuplicateSearching?: boolean, plugins?: Plugin[])
highlight: Function
preElementStyled: boolean
isCode: boolean
includeCodeInputInHighlightFunc: boolean
autoDisableDuplicateSearching: boolean
plugins: Plugin[]
/**
* @deprecated Please give a value for the `autoDisableDuplicateSearching` parameter.
*/
constructor(highlight?: (code: HTMLElement) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: false, plugins?: Plugin[])
/**
* @deprecated Please give a value for the `autoDisableDuplicateSearching` parameter.
*/
constructor(highlight?: (code: HTMLElement, codeInput: CodeInput) => void, preElementStyled?: boolean, isCode?: boolean, includeCodeInputInHighlightFunc?: true, plugins?: Plugin[])
}

/**
Expand Down
111 changes: 90 additions & 21 deletions code-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,12 @@ var codeInput = {
* @param {Object} template - a Template object instance - see `codeInput.templates`
*/
registerTemplate: function (templateName, template) {
if(!(typeof templateName == "string" || templateName instanceof String)) throw TypeError(`code-input: Template for "${templateName}" must be a string.`);
if(!(typeof templateName == "string" || templateName instanceof String)) throw TypeError(`code-input: Name of template "${templateName}" must be a string.`);
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.`);
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.`);
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.`);
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.`);
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.`);
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.`);

template.plugins.forEach((plugin, i) => {
Expand Down Expand Up @@ -163,13 +164,20 @@ var codeInput = {
* @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]'.
* @param {boolean} includeCodeInputInHighlightFunc - Setting this to true passes the `<code-input>` element as a second argument to the highlight function.
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.Plugin`
* @returns template object
* @returns {codeInput.Template} template object
*/
constructor(highlight = function () { }, preElementStyled = true, isCode = true, includeCodeInputInHighlightFunc = false, plugins = []) {
constructor(highlight = function () { }, preElementStyled = true, isCode = true, includeCodeInputInHighlightFunc = false, autoDisableDuplicateSearching = true, plugins = []) {
// @deprecated to support old function signature without autoDisableDuplicateSearching
if(Array.isArray(autoDisableDuplicateSearching)) {
plugins = autoDisableDuplicateSearching;
autoDisableDuplicateSearching = true;
}

this.highlight = highlight;
this.preElementStyled = preElementStyled;
this.isCode = isCode;
this.includeCodeInputInHighlightFunc = includeCodeInputInHighlightFunc;
this.autoDisableDuplicateSearching = autoDisableDuplicateSearching;
this.plugins = plugins;
}

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

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

/**
* 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.
*/
autoDisableDuplicateSearching = true;

/**
* An array of plugin objects to add extra features -
* see `codeInput.Plugin`.
Expand All @@ -226,31 +243,36 @@ var codeInput = {
* Constructor to create a template that uses Prism.js syntax highlighting (https://prismjs.com/)
* @param {Object} prism Import Prism.js, then after that import pass the `Prism` object as this parameter.
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.plugins`
* @returns template object
* @returns {codeInput.Template} template object
*/
prism(prism, plugins = []) { // Dependency: Prism.js (https://prismjs.com/)
return {
includeCodeInputInHighlightFunc: false,
highlight: prism.highlightElement,
preElementStyled: true,
isCode: true,
plugins: plugins,
};
return new codeInput.Template(
prism.highlightElement, // highlight
true, // preElementStyled
true, // isCode
false, // includeCodeInputInHighlightFunc
true, // autoDisableDuplicateSearching
plugins
);
},
/**
* Constructor to create a template that uses highlight.js syntax highlighting (https://highlightjs.org/)
* @param {Object} hljs Import highlight.js, then after that import pass the `hljs` object as this parameter.
* @param {codeInput.Plugin[]} plugins - An array of plugin objects to add extra features - see `codeInput.plugins`
* @returns template object
* @returns {codeInput.Template} template object
*/
hljs(hljs, plugins = []) { // Dependency: Highlight.js (https://highlightjs.org/)
return {
includeCodeInputInHighlightFunc: false,
highlight: hljs.highlightElement,
preElementStyled: false,
isCode: true,
plugins: plugins,
};
return new codeInput.Template(
function(codeElement) {
codeElement.removeAttribute("data-highlighted");
hljs.highlightElement(codeElement);
}, // highlight
false, // preElementStyled
true, // isCode
false, // includeCodeInputInHighlightFunc
true, // autoDisableDuplicateSearching
plugins
);
},

/**
Expand All @@ -275,6 +297,7 @@ var codeInput = {
includeCodeInputInHighlightFunc: true,
preElementStyled: true,
isCode: false,
autoDisableDuplicateSearching: true,
plugins: plugins,
}
},
Expand All @@ -299,8 +322,11 @@ var codeInput = {
includeCodeInputInHighlightFunc: true,
preElementStyled: true,
isCode: false,
autoDisableDuplicateSearching: true,

rainbowColors: rainbowColors,
delimiter: delimiter,

plugins: plugins,
}
},
Expand Down Expand Up @@ -507,7 +533,6 @@ var codeInput = {
this.ignoreValueUpdate = false;
if (this.textareaElement.value != value) this.textareaElement.value = value;


let resultElement = this.codeElement;

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

this.pluginEvt("afterHighlight");

if(this.template.autoDisableDuplicateSearching) {
if(this.codeElement.querySelector("*") === null) {
// Fix for tries-to-disable-searching-before-highlighting-possible bug:
// Wait until user interaction so can expect
// highlight before disabling searching
let listenerKeydown = window.addEventListener("keydown", () => {
this.resultElementDisableSearching();
window.removeEventListener("keydown", listenerKeydown);
window.removeEventListener("mousemove", listenerMousemove);
});
let listenerMousemove = window.addEventListener("mousemove", () => {
this.resultElementDisableSearching();
window.removeEventListener("keydown", listenerKeydown);
window.removeEventListener("mousemove", listenerMousemove);
});
} else {
this.resultElementDisableSearching();
}
}
}

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

/**
* Make the text contents of highlighted code in the `<pre><code>` result element invisible to Ctrl+F by moving them to a data attribute
* then the CSS `::before` pseudo-element on span elements with the class code-input_searching-disabled. This function is called recursively
* on all child elements of the <code> element.
*
* @param {HTMLElement} element The element on which this is carried out recursively. Optional - defaults to the `<pre><code>`'s `<code>` element.
*/
resultElementDisableSearching(element=this.preElement) {
for (let i = 0; i < element.childNodes.length; i++) {
let content = element.childNodes[i].textContent;

if (element.childNodes[i].nodeType == 3) {
// Turn plain text node into span element
element.replaceChild(document.createElement('span'), element.childNodes[i]);
element.childNodes[i].classList.add("code-input_searching-disabled")
element.childNodes[i].setAttribute("data-content", content);
element.childNodes[i].innerText = '';
} else {
// Recurse deeper
this.resultElementDisableSearching(element.childNodes[i]);
}
}
}

/**
* Get the template object this code-input element is using.
* @returns {Object} - Template object
Expand Down

0 comments on commit 98c5cc5

Please sign in to comment.