Skip to content
This repository has been archived by the owner on Sep 26, 2024. It is now read-only.

Commit

Permalink
run lint
Browse files Browse the repository at this point in the history
  • Loading branch information
NicoMorenoSirius committed Jun 18, 2024
1 parent 4249258 commit 2a98653
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 134 deletions.
2 changes: 1 addition & 1 deletion apps/mocksi-lite/content/ContentApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default function ContentApp({ isOpen, email }: ContentProps) {

const onChecked = () => {
setAreChangesHighlighted((prevValue) => {
ContentHighlighter.showHideHighlights(!prevValue)
ContentHighlighter.showHideHighlights(!prevValue);
return !prevValue;
});
};
Expand Down
36 changes: 24 additions & 12 deletions apps/mocksi-lite/content/EditMode/actions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import UniversalReplace from '../../universalReplace'
import UniversalReplace from "../../universalReplace";

export function cancelEditWithoutChanges(nodeWithTextArea: HTMLElement | null) {
if (nodeWithTextArea) {
Expand All @@ -18,38 +18,50 @@ export function applyChanges(
oldValue: string,
) {
if (nodeWithTextArea) {
cancelEditWithoutChanges(nodeWithTextArea)
UniversalReplace.addPattern(oldValue, newValue)
cancelEditWithoutChanges(nodeWithTextArea);
UniversalReplace.addPattern(oldValue, newValue);
}
}

export function fragmentTextNode(fragmentsToHighlight: Node[], matches: RegExpMatchArray[], textNode: Node, newText: string) {
const baseFragment = document.createDocumentFragment()
export function fragmentTextNode(
fragmentsToHighlight: Node[],
matches: RegExpMatchArray[],
textNode: Node,
newText: string,
) {
const baseFragment = document.createDocumentFragment();
let cursor = 0;
let index = 0;
for (const match of matches) {
// match.index may be undefined? in which cases?????
const [startOffset, endOffset] = [match.index || 0, (match.index || 0) + match[0].length]
const [startOffset, endOffset] = [
match.index || 0,
(match.index || 0) + match[0].length,
];
if (cursor < startOffset) {
baseFragment.appendChild(
//@ts-ignore nodeValue wont be null
document.createTextNode(textNode.nodeValue.substring(cursor, startOffset)),
document.createTextNode(
textNode.nodeValue.substring(cursor, startOffset),
),
);
}
const selectedTextFragment = document.createTextNode(newText);
fragmentsToHighlight.push(selectedTextFragment)
baseFragment.appendChild(selectedTextFragment)
cursor = endOffset
fragmentsToHighlight.push(selectedTextFragment);
baseFragment.appendChild(selectedTextFragment);
cursor = endOffset;
if (index === matches.length - 1 && cursor !== textNode.nodeValue?.length) {
// end of matches
baseFragment.appendChild(
//@ts-ignore nodeValue wont be null
document.createTextNode(textNode.nodeValue.substring(endOffset, textNode.nodeValue?.length)),
document.createTextNode(
textNode.nodeValue.substring(endOffset, textNode.nodeValue?.length),
),
);
}
index++;
}
return baseFragment
return baseFragment;
}

function replaceValueInDOM(
Expand Down
2 changes: 1 addition & 1 deletion apps/mocksi-lite/content/EditMode/editMode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const setEditorMode = (turnOn: boolean, recordingId?: string) => {
restoreNodes();
ContentHighlighter.hideHighlights();
cancelEditWithoutChanges(document.getElementById("mocksiSelectedText"));
document.normalize()
document.normalize();
}
};

Expand Down
12 changes: 6 additions & 6 deletions apps/mocksi-lite/content/EditMode/highlighter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@ import { MOCKSI_HIGHLIGHTER_ID } from "../../consts";

class Highlighter {
private contentRanger = document.createRange();
private highlightedNodes = []
private highlightedNodes = [];

highlightNode = (elementToHighlight: Node) => {
this.contentRanger.selectNodeContents(elementToHighlight);
const { x, y, width, height } =
this.contentRanger.getBoundingClientRect() || {};
const textHighlight = highlight({ x, y, width, height });
document.body.appendChild(textHighlight);
//@ts-ignore just don't know what is meaning here
this.highlightedNodes.push(elementToHighlight)
//@ts-ignore just don't know what is meaning here
this.highlightedNodes.push(elementToHighlight);
};

showHideHighlights = (show: boolean) => {
for (const node of document.querySelectorAll(
showHideHighlights = (show: boolean) => {
for (const node of document.querySelectorAll(
`div.${MOCKSI_HIGHLIGHTER_ID}`,
)) {
(node as HTMLElement).style.display = show ? "block" : "none";
}
}
};
hideHighlights = () => {
for (const node of document.querySelectorAll(
`div.${MOCKSI_HIGHLIGHTER_ID}`,
Expand Down
5 changes: 1 addition & 4 deletions apps/mocksi-lite/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,7 @@
],
"web_accessible_resources": [
{
"resources": [
"./content/content.tsx",
"/*.map"
],
"resources": ["./content/content.tsx", "/*.map"],
"matches": ["<all_urls>"]
}
],
Expand Down
245 changes: 135 additions & 110 deletions apps/mocksi-lite/universalReplace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,129 +3,154 @@ import { ContentHighlighter } from "./content/EditMode/highlighter";
import { saveModification } from "./utils";

class UniversalReplace {
observer: MutationObserver | undefined;
patterns: { pattern: RegExp, replace: string }[] = [];
observer: MutationObserver | undefined;
patterns: { pattern: RegExp; replace: string }[] = [];

addPattern(pattern: string | RegExp, replace: string) {
const replacePattern = { pattern: toRegExpPattern(pattern), replace }
this.patterns.push(replacePattern);
this.seekAndReplace(replacePattern.pattern, replacePattern.replace)
}
addPattern(pattern: string | RegExp, replace: string) {
const replacePattern = { pattern: toRegExpPattern(pattern), replace };
this.patterns.push(replacePattern);
this.seekAndReplace(replacePattern.pattern, replacePattern.replace);
}

removePattern(pattern: string | RegExp) {
const pattern_ = toRegExpPattern(pattern);
let idx = this.patterns.findIndex(p => p.pattern.source === pattern_.source);
if (idx >= 0) this.patterns.splice(idx, 1);
removePattern(pattern: string | RegExp) {
const pattern_ = toRegExpPattern(pattern);
const idx = this.patterns.findIndex(
(p) => p.pattern.source === pattern_.source,
);
if (idx >= 0) this.patterns.splice(idx, 1);

if (this.patterns.length === 0) {
this.observer?.disconnect();
this.observer = undefined;
}
}
if (this.patterns.length === 0) {
this.observer?.disconnect();
this.observer = undefined;
}
}

seekAndReplace(pattern: RegExp, newText: string) {
const body = document.querySelector('body')
if (body) {
// TODO createTreeWalker provides a filter function, see if we can filter chunk textNodes
const treeWalker = document.createTreeWalker(
body,
NodeFilter.SHOW_TEXT,
(node) => {
if ((node.parentElement instanceof HTMLScriptElement || node.parentElement instanceof HTMLStyleElement)) return NodeFilter.FILTER_REJECT
else return NodeFilter.FILTER_ACCEPT
}
);
let textNode: Node
const fragmentsToHighlight: Node[] = []
const replacements: {nodeToReplace: Node, replacement: Node}[] = []
do {
textNode = treeWalker.currentNode;
if (textNode.nodeValue === null || !textNode?.textContent?.trim()) continue;
const matches = [...textNode.nodeValue.matchAll(pattern)]
if (matches.length > 0) {
const fragmentedTextNode = fragmentTextNode(fragmentsToHighlight, matches, textNode, newText)
replacements.push({
nodeToReplace: textNode,
replacement: fragmentedTextNode
})
saveModification(
textNode.parentElement as HTMLElement,
newText,
cleanPattern(pattern)
)
}
} while (treeWalker.nextNode());
replacements.forEach(({nodeToReplace, replacement}) => {
if (nodeToReplace.parentElement) nodeToReplace.parentElement.replaceChild(replacement, nodeToReplace)
// TODO: see how we should manage if has no parent, couldnt find a case for this.
})
fragmentsToHighlight.forEach(fragment => ContentHighlighter.highlightNode(fragment))
}
}
seekAndReplace(pattern: RegExp, newText: string) {
const body = document.querySelector("body");
if (body) {
// TODO createTreeWalker provides a filter function, see if we can filter chunk textNodes
const treeWalker = document.createTreeWalker(
body,
NodeFilter.SHOW_TEXT,
(node) => {
if (
node.parentElement instanceof HTMLScriptElement ||
node.parentElement instanceof HTMLStyleElement
)
return NodeFilter.FILTER_REJECT;
return NodeFilter.FILTER_ACCEPT;
},
);
let textNode: Node;
const fragmentsToHighlight: Node[] = [];
const replacements: { nodeToReplace: Node; replacement: Node }[] = [];
do {
textNode = treeWalker.currentNode;
if (textNode.nodeValue === null || !textNode?.textContent?.trim())
continue;
const matches = [...textNode.nodeValue.matchAll(pattern)];
if (matches.length > 0) {
const fragmentedTextNode = fragmentTextNode(
fragmentsToHighlight,
matches,
textNode,
newText,
);
replacements.push({
nodeToReplace: textNode,
replacement: fragmentedTextNode,
});
saveModification(
textNode.parentElement as HTMLElement,
newText,
cleanPattern(pattern),
);
}
} while (treeWalker.nextNode());
//biome-ignore lint/complexity/noForEach: I'll replace later
replacements.forEach(({ nodeToReplace, replacement }) => {
if (nodeToReplace.parentElement)
nodeToReplace.parentElement.replaceChild(replacement, nodeToReplace);
// TODO: see how we should manage if has no parent, couldnt find a case for this.
});
//biome-ignore lint/complexity/noForEach: I'll replace later
fragmentsToHighlight.forEach((fragment) =>
ContentHighlighter.highlightNode(fragment),
);
}
}

// TODO need to execute this when the user presses "play"
createObserver() {
this.observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes != null && mutation.addedNodes.length > 0) {
for (const node of mutation.addedNodes) {
if (
node instanceof Text &&
node.parentElement &&
!(node.parentElement instanceof HTMLScriptElement) &&
!(node.parentElement instanceof HTMLStyleElement) &&
node.textContent !== null &&
!/^\s*$/.test(node.textContent)
) {
const replace = this.matchReplacePattern(node.textContent);
if (replace) {
const treeWalker = document.createTreeWalker(
node,
NodeFilter.SHOW_TEXT,
);
let textNode: Node;
do {
textNode = treeWalker.currentNode;
if (textNode.nodeValue === null) continue;
textNode.nodeValue = textNode.nodeValue.replace(
replace.pattern,
replaceFirstLetterCase(replace.replace),
);
} while (treeWalker.nextNode());
}
}
}
}
}
});

// TODO need to execute this when the user presses "play"
createObserver() {
this.observer = new MutationObserver((mutations) => {
for (let mutation of mutations) {
if (mutation.addedNodes != null && mutation.addedNodes.length > 0) {
for (let node of mutation.addedNodes) {
if (node instanceof Text &&
node.parentElement &&
!(node.parentElement instanceof HTMLScriptElement) &&
!(node.parentElement instanceof HTMLStyleElement) &&
node.textContent !== null &&
!(/^\s*$/.test(node.textContent)))
{
const replace = this.matchReplacePattern(node.textContent);
if (replace) {
const treeWalker = document.createTreeWalker(node, NodeFilter.SHOW_TEXT);
let textNode: Node
do {
textNode = treeWalker.currentNode;
if (textNode.nodeValue === null) continue;
textNode.nodeValue = textNode.nodeValue.replace(replace.pattern, replaceFirstLetterCase(replace.replace));
} while (treeWalker.nextNode());
}
}
}
}
}
});
this.observer.observe(document, { childList: true, subtree: true });
}

this.observer.observe(document, { childList: true, subtree: true });
}
matchReplacePattern(
text: string,
): { pattern: RegExp; replace: string } | null {
for (const pattern of this.patterns) {
if (pattern.pattern.test(text)) {
return { pattern: pattern.pattern, replace: pattern.replace };
}
}

matchReplacePattern(text: string): { pattern: RegExp, replace: string } | null {
for (let pattern of this.patterns) {
if (pattern.pattern.test(text)) {
return { pattern: pattern.pattern, replace: pattern.replace }
}
}

return null
}
return null;
}
}

const cleanPattern = (pattern: RegExp) => pattern.toString().replaceAll('/', '').replace('gi', '')
const cleanPattern = (pattern: RegExp) =>
pattern.toString().replaceAll("/", "").replace("gi", "");

const replaceFirstLetterCase = (value: string) => {
return (match: string) => {
// Check if the first letter in the match is uppercase
if (match[0] === match[0].toUpperCase()) {
return value.charAt(0).toUpperCase() + value.slice(1);
} else {
return value;
}
}
}
return (match: string) => {
// Check if the first letter in the match is uppercase
if (match[0] === match[0].toUpperCase()) {
return value.charAt(0).toUpperCase() + value.slice(1);
}
return value;
};
};

const toRegExpPattern = (pattern: string | RegExp) => {
if (typeof pattern === "string") {
return new RegExp(pattern, "ig");
}
if (typeof pattern === "string") {
return new RegExp(pattern, "ig");
}

return pattern;
}
return pattern;
};

export default new UniversalReplace();
export default new UniversalReplace();

0 comments on commit 2a98653

Please sign in to comment.