Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,17 @@
</label>
<input id="word-rep" autocomplete="name" title="The word you want to replace with" type="text" class="rep" placeholder="Enter word to replace..." />
</div>
<div class="checkbox-container">
<label>
<input type="checkbox" id="whole-word" /> Whole Word Only
</label>
<label>
<input type="checkbox" id="case-sensitive" /> Case Insensitive
</label>
<label>
<input type="checkbox" id="replace-notation" /> Use Escape Sequences
</label>
</div>
<p class="res" title="The matches of the word across the document">
No results found!
</p>
Expand All @@ -252,7 +263,6 @@
</div>
</div>
</div>
<!--File Info Overlay-->
<div class="f-bg hide">
<div class="f-card up hide">
<div class="f-head">
Expand Down
36 changes: 36 additions & 0 deletions public/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,42 @@ body{
opacity: .55;
pointer-events: none;
}
.checkbox-container {
display: flex;
align-items: center;
margin: 10px 0;
}
.checkbox-container input[type="checkbox"] {
appearance: none;
width: 20px;
height: 20px;
border: 2px solid #ccc;
border-radius: 4px;
margin-right: 10px;
cursor: pointer;
transition: all 0.3s ease;
}
.checkbox-container input[type="checkbox"]:checked {
background-color: #aaa;
border-color: #aaa;
}
.checkbox-container input[type="checkbox"]:hover {
border-color: #fff;
}
.checkbox-container label {
font-size: 16px;
font-weight: 500;
}
.checkbox-container input[type="checkbox"]:checked::before {
content: '✔';
font-size: 14px;
position: absolute;
left: 4px;
top: 2px;
}
.checkbox-container:hover {
border-radius: 8px;
}

@keyframes pop {
from{
Expand Down
288 changes: 165 additions & 123 deletions public/js/search.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,159 +5,201 @@
const replaceWordInput = document.querySelector(".rep");

searchCard.addEventListener("input", (e) => {
if (replaceWordInput.value.length <= 0) return replaceBtn.classList.add("no");
else {
return replaceBtn.classList.remove("no");
}
if (replaceWordInput.value.length <= 0) return replaceBtn.classList.add("no");
else {
return replaceBtn.classList.remove("no");
}
});

const openSearchMenu = () => {
searchBg.style.display = 'flex';
searchBg.classList.remove("hide");
searchCard.classList.add("ani");
searchCard.classList.remove("hide");

setTimeout(() => {
searchCard.classList.remove("up");
searchWordInput.focus();
}, 500);
searchBg.style.display = "flex";
searchBg.classList.remove("hide");
searchCard.classList.add("ani");
searchCard.classList.remove("hide");

setTimeout(() => {
searchCard.classList.remove("up");
searchWordInput.focus();
}, 500);
};

const closeSearchMenu = () => {
searchCard.classList.add("down");
searchCard.classList.add("anti-ani");
setTimeout(() => {
searchCard.classList.add("hide");
}, 700);
setTimeout(() => {
searchBg.classList.add("hide");
searchCard.classList.remove("anti-ani");
searchCard.classList.add("hide");
}, 1000);

setTimeout(() => {
searchCard.classList.add("up");
searchCard.classList.remove("down");
searchCard.classList.remove("ani");
searchBg.style.display = 'none';
}, 1200);
searchCard.classList.add("down");
searchCard.classList.add("anti-ani");
setTimeout(() => {
searchCard.classList.add("hide");
}, 700);
setTimeout(() => {
searchBg.classList.add("hide");
searchCard.classList.remove("anti-ani");
searchCard.classList.add("hide");
}, 1000);

setTimeout(() => {
searchCard.classList.add("up");
searchCard.classList.remove("down");
searchCard.classList.remove("ani");
searchBg.style.display = "none";
}, 1200);
};

searchBtn.addEventListener("click", (e) => {
try {
if (textInput.innerText.length <= 0) {
throw new Error("There isn't any text available to search!\nTry entering some text for this to work!");
}
openSearchMenu();
} catch (error) {
alert(error.message);
console.error(error);
try {
if (textInput.innerText.length <= 0) {
throw new Error(
"There isn't any text available to search!\nTry entering some text for this to work!"
);
}
openSearchMenu();
} catch (error) {
alert(error.message);
console.error(error);
}
});

closeSearchCardBtn.addEventListener("click", (e) => {
return closeSearchMenu();
return closeSearchMenu();
});

const replaceWord = (string, oldWord, newWord) => {
if (!string) {
throw new Error("No string input provided!");
}
if (!oldWord) {
throw new Error("No word supplied for checking!");
}
if (!newWord) {
throw new Error("No word supplied for replacing!");
}
const searchRefresh = function (e) {
try {
if (e.target.value.length <= 0) return;

const regex = new RegExp(`\\b${oldWord.replace(/\//g, "\\/")}\\b`, `g`);
const matches = string.match(regex);
if (!matches) {
throw new Error(`No matches found for the word '${oldWord}' in the text!`);
}
resultMatch.classList.remove("ok");
resultMatch.classList.remove("err");
resultMatch.innerText = `Searching...`;

wordsCount.innerText = `Total Words: ${string.length}`;
resultMatch.innerText = `Replaced ${matches.length} occurrences`;
return string.replace(regex, newWord);
setTimeout(() => {
searchString(textInput.innerText, searchWordInput.value);
}, 1000);
} catch (error) {
alert(error.message);
return console.error(error);
}
};

const wholeWordCheckbox = document.getElementById("whole-word");
const caseSensitiveCheckbox = document.getElementById("case-sensitive");
const replaceNotationCheckbox = document.getElementById("replace-notation");

wholeWordCheckbox.addEventListener("click", searchRefresh);
caseSensitiveCheckbox.addEventListener("click", searchRefresh);
replaceNotationCheckbox.addEventListener("click", searchRefresh);

const replaceWord = (string, oldWord, newWord) => {
if (!string) throw new Error("No string input provided!");
if (!oldWord) throw new Error("No word supplied for checking!");
if (!newWord) throw new Error("No word supplied for replacing!");

if (replaceNotationCheckbox.checked) {
oldWord = oldWord.replace(/\\n/g, "\n").replace(/\\t/g, "\t");
newWord = newWord.replace(/\\n/g, "\n").replace(/\\t/g, "\t");
}

let flags = "g";
if (caseSensitiveCheckbox.checked) {
flags += "i";
}

const wordBoundary = wholeWordCheckbox.checked ? "\\b" : "";
const regex = new RegExp(
`${wordBoundary}${oldWord.replace(/\//g, "\\/")}${wordBoundary}`,

Check failure

Code scanning / CodeQL

Incomplete string escaping or encoding High

This does not escape backslash characters in the input.

Copilot Autofix

AI 12 months ago

To fix the problem, we need to ensure that backslashes in oldWord are properly escaped before using it in a regular expression. This can be done by adding a replace call to escape backslashes before escaping forward slashes. This ensures that all occurrences of backslashes are correctly handled.

We will modify the line where oldWord is processed to escape backslashes first, followed by forward slashes. This change will be made in the replaceWord function.

Suggested changeset 1
public/js/search.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/public/js/search.js b/public/js/search.js
--- a/public/js/search.js
+++ b/public/js/search.js
@@ -105,3 +105,3 @@
   const regex = new RegExp(
-    `${wordBoundary}${oldWord.replace(/\//g, "\\/")}${wordBoundary}`,
+    `${wordBoundary}${oldWord.replace(/\\/g, "\\\\").replace(/\//g, "\\/")}${wordBoundary}`,
     flags
EOF
@@ -105,3 +105,3 @@
const regex = new RegExp(
`${wordBoundary}${oldWord.replace(/\//g, "\\/")}${wordBoundary}`,
`${wordBoundary}${oldWord.replace(/\\/g, "\\\\").replace(/\//g, "\\/")}${wordBoundary}`,
flags
Copilot is powered by AI and may make mistakes. Always verify output.
flags
);

const matches = string.match(regex);
if (!matches)
throw new Error(`No matches found for the word '${oldWord}' in the text!`);

wordsCount.innerText = `Total Words: ${string.length}`;
resultMatch.innerText = `Replaced ${matches.length} occurrences`;

return string.replace(regex, newWord);
};

const searchString = (string, word) => {
if (!string) {
throw new Error("No value available to search!");
}
if (!word) {
throw new Error("No word supplied for checking!");
}
if (!string) throw new Error("No value available to search!");
if (!word) throw new Error("No word supplied for checking!");

let flags = "g";
if (caseSensitiveCheckbox.checked) {
flags += "i";
}

const wordBoundary = wholeWordCheckbox.checked ? "\\b" : "";
if (!replaceNotationCheckbox.checked) {
word = escapeRegExp(word);
}
let regex;
try{
regex = new RegExp(`${wordBoundary}${word}${wordBoundary}`, flags);
}catch(e){
alert("Please enter correct notation");
return;
}

const matches = string.match(regex);
if (matches) {
resultMatch.classList.add("ok");
resultMatch.classList.remove("err");
resultMatch.innerText = `${matches.length} matches found!`;
} else {
resultMatch.classList.remove("ok");
resultMatch.classList.add("err");
resultMatch.innerText = `No matches found!`;
}
};

const regex = new RegExp(`\\b${word.replace(/\//g, "\\/")}\\b`, `g`);
const matches = string.match(regex);
if (matches) {
resultMatch.classList.add("ok");
resultMatch.classList.remove("err");
resultMatch.innerText = `${matches.length} matches found!`;
} else {
resultMatch.classList.remove("ok");
resultMatch.classList.add("err");
resultMatch.innerText = `No matches found!`;
}
const escapeRegExp = (string) => {
return string.replace(/[.*+?^=!:${}()|\[\]\/\\]/g, "\\$&");
};

replaceBtn.addEventListener("click", async (e) => {
try {
if (!textInput.innerText.includes(searchWordInput.value)) {
throw new Error(`No match found for the word '${searchWordInput.value.trim()}' in the active document!`);
}

const output = replaceWord(textInput.innerText, searchWordInput.value, replaceWordInput.value);
textInput.innerText = output;
} catch (error) {
alert(error.message);
console.error(error);
try {
let oldWord = searchWordInput.value;
let newWord = replaceWordInput.value;
if (replaceNotationCheckbox.checked) {
oldWord = oldWord.replace(/\\n/g, "\n").replace(/\\t/g, "\t");
newWord = newWord.replace(/\\n/g, "\n").replace(/\\t/g, "\t");
}
if (!replaceNotationCheckbox.checked) {
oldWord = escapeRegExp(oldWord);
}
let flags = "g";
if (caseSensitiveCheckbox.checked) {
flags += "i";
}
const wordBoundary = wholeWordCheckbox.checked ? "\\b" : "";
const regex = new RegExp(`${wordBoundary}${oldWord}${wordBoundary}`, flags);
if (!regex.test(textInput.innerText)) {
throw new Error(
`No match found for the word '${searchWordInput.value.trim()}' in the active document!`
);
}
const output = replaceWord(textInput.innerText, oldWord, newWord);
textInput.innerText = output;
} catch (error) {
alert(error.message);
console.error(error);
}
});

searchWordInput.addEventListener("input", (e) => {
try {
if (e.target.value.length <= 0) return;

resultMatch.classList.remove("ok");
resultMatch.classList.remove("err");
resultMatch.innerText = `Searching...`;

setTimeout(() => {
searchString(textInput.innerText, searchWordInput.value);
}, 1000);
} catch (error) {
alert(error.message);
return console.error(error);
}
searchRefresh(e);
});

searchWordInput.addEventListener("keydown", (e) => {
try {
if (e.key === 'Backspace') {
console.log("Yes");
}
} catch (error) {
alert(error);
return console.error(error);
}
})

searchWordInput.addEventListener("keydown", (e) => {
try {
if(e.target.value.length <= 0) return;
if(e.keyCode === 13) return replaceWordInput.focus();
} catch (error) {
alert(error);
return console.error(error);
}
})

try {
if (e.target.value.length <= 0) return;
if (e.keyCode === 13) return replaceWordInput.focus();
} catch (error) {
alert(error);
return console.error(error);
}
});

replaceWordInput.addEventListener("keydown", (e) => {
if(e.keyCode === 13) {
return replaceBtn.click();
}
})

if (e.keyCode === 13) {
return replaceBtn.click();
}
});
Loading