main
js 166 lines 4.24 KB
Raw
1 function extractDOM([
2 selectorLabel = "",
3 selectorName = "data-a0sel3ct0r",
4 guidName = "data-a0gu1d",
5 ]) {
6 let elementCounter = 0;
7 const now = new Date();
8 const time = [
9 String(now.getHours()).padStart(2, "0"),
10 String(now.getMinutes()).padStart(2, "0"),
11 String(now.getSeconds()).padStart(2, "0"),
12 String(now.getMilliseconds()).padStart(3, "0"),
13 ].join("");
14 const ignoredTags = [
15 "style",
16 "script",
17 "meta",
18 "link",
19 "svg",
20 "noscript",
21 "path",
22 ];
23
24 // Convert number to base64 and trim unnecessary chars
25 function toBase64(num) {
26 const chars =
27 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
28 let result = "";
29
30 do {
31 result = chars[num & 63] + result;
32 num = num >> 6;
33 } while (num > 0);
34
35 return result;
36 }
37
38 function isElementVisible(element) {
39 // Return true for non-element nodes
40 if (element.nodeType !== Node.ELEMENT_NODE) {
41 return true;
42 }
43
44 const computedStyle = window.getComputedStyle(element);
45
46 // Check if element is hidden via CSS
47 if (
48 computedStyle.display === "none" ||
49 computedStyle.visibility === "hidden" ||
50 computedStyle.opacity === "0"
51 ) {
52 return false;
53 }
54
55 // Check for hidden input type
56 if (element.tagName === "INPUT" && element.type === "hidden") {
57 return false;
58 }
59
60 // Check for hidden attribute
61 if (
62 element.hasAttribute("hidden") ||
63 element.getAttribute("aria-hidden") === "true"
64 ) {
65 return false;
66 }
67
68 return true;
69 }
70
71 function convertAttribute(tag, attr) {
72 let out = {
73 name: attr.name,
74 value:
75 typeof attr.value == "string" ? attr.value : JSON.stringify(attr.value),
76 };
77
78 //excluded attributes
79 if (["srcset"].includes(out.name)) return null;
80 if (out.name.startsWith("data-") && out.name != selectorName) return null;
81
82 if (out.name == "src" && out.value.startsWith("data:"))
83 out.value = "data...";
84
85 return out;
86 }
87
88 function traverseNodes(node, depth = 0, visited = new Set()) {
89 // Safety checks
90 if (!node) return "";
91 if (depth > 1000) return "<!-- Max depth exceeded -->";
92
93 const guid = node.getAttribute?.(guidName);
94 if (guid && visited.has(guid)) {
95 return `<!-- Circular reference detected at guid: ${guid} -->`;
96 }
97
98 let content = "";
99 const tagName = node.tagName ? node.tagName.toLowerCase() : "";
100
101 // Skip ignored tags
102 if (tagName && ignoredTags.includes(tagName)) {
103 return "";
104 }
105
106 if (node.nodeType === Node.ELEMENT_NODE) {
107 // Add unique ID to the actual DOM element
108 if (tagName) {
109 const no = elementCounter++;
110 const selector = `${no}${selectorLabel}`;
111 const guid = `${time}-${selector}`;
112 node.setAttribute(selectorName, selector);
113 node.setAttribute(guidName, guid);
114 visited.add(guid);
115 }
116
117 content += `<${tagName}`;
118
119 // Add invisible attribute if element is not visible
120 if (!isElementVisible(node)) {
121 content += " invisible";
122 }
123
124 for (let attr of node.attributes) {
125 const out = convertAttribute(tagName, attr);
126 if (out) content += ` ${out.name}="${out.value}"`;
127 }
128
129 content += ">";
130
131 // Handle iframes
132 if (tagName === "iframe") {
133 try {
134 const frameId = elementCounter++;
135 node.setAttribute(selectorName, frameId);
136 content += `<!-- IFrame Content Placeholder ${frameId} -->`;
137 } catch (e) {
138 console.warn("Error marking iframe:", e);
139 }
140 }
141
142 if (node.shadowRoot) {
143 content += "<!-- Shadow DOM Start -->";
144 for (let shadowChild of node.shadowRoot.childNodes) {
145 content += traverseNodes(shadowChild, depth + 1, visited);
146 }
147 content += "<!-- Shadow DOM End -->";
148 }
149
150 for (let child of node.childNodes) {
151 content += traverseNodes(child, depth + 1, visited);
152 }
153
154 content += `</${tagName}>`;
155 } else if (node.nodeType === Node.TEXT_NODE) {
156 content += node.textContent;
157 } else if (node.nodeType === Node.COMMENT_NODE) {
158 content += `<!--${node.textContent}-->`;
159 }
160
161 return content;
162 }
163
164 const fullHTML = traverseNodes(document.documentElement);
165 return fullHTML;
166 }