PageSourceSearch

https://thescoove.africa/public/dhws-data-injector.js

js thescoove.africa collected 2026-10-03 00:53:06 UTC 6,536 bytes, 288 lines download raw bytes

1
2(function() {
3  // Configuration
4  const CONFIG = {
5    attributePrefix: 'data-component',
6    includeContentAttribute: true,
7    maxContentLength: 1000,
8    includeLegacyAttributes: true,
9    includeElements: [],
10    excludeElements: [],
11  };
12
13  const TAG_SHOULD_EXCLUDE = [
14  "base",
15  "object",
16  "link",
17  "meta",
18  "noscript",
19  "script",
20  "style",
21  "title",
22  "animate",
23  "animateMotion",
24  "animateTransform",
25  "circle",
26  "clipPath",
27  "defs",
28  "desc",
29  "ellipse",
30  "feBlend",
31  "feColorMatrix",
32  "feComponentTransfer",
33  "feComposite",
34  "feConvolveMatrix",
35  "feDiffuseLighting",
36  "feDisplacementMap",
37  "feDistantLight",
38  "feDropShadow",
39  "feFlood",
40  "feFuncA",
41  "feFuncB",
42  "feFuncG",
43  "feFuncR",
44  "feGaussianBlur",
45  "feImage",
46  "feMerge",
47  "feMergeNode",
48  "feMorphology",
49  "feOffset",
50  "fePointLight",
51  "feSpecularLighting",
52  "feSpotLight",
53  "feTile",
54  "feTurbulence",
55  "filter",
56  "foreignObject",
57  "g",
58  "image",
59  "line",
60  "linearGradient",
61  "marker",
62  "mask",
63  "metadata",
64  "mpath",
65  "path",
66  "pattern",
67  "polygon",
68  "polyline",
69  "radialGradient",
70  "rect",
71  "set",
72  "stop",
73  "switch",
74  "symbol",
75  "text",
76  "textPath",
77  "tspan",
78  "use",
79  "view",
80  "body",
81  "param",
82]
83
84  // Map to track per-class indices for unique component IDs
85  const classIndexMap = new Map();
86
87  /**
88   * Sanitizes attribute values to prevent XSS
89   */
90  function sanitizeAttributeValue(value) {
91    return String(value)
92      .replace(/&/g, '&')
93      .replace(/"/g, '"')
94      .replace(/'/g, ''')
95      .replace(/</g, '&lt;')
96      .replace(/>/g, '&gt;');
97  }
98
99  /**
100   * Creates a component ID based on class name and index
101   */
102  function createComponentId(el) {
103
104    const path = [];
105    let current = el;
106
107    while (current && current.parentElement) {
108      const siblings = Array.from(current.parentElement.children)
109        .filter(child => child.tagName);
110      const index = siblings.indexOf(current);
111      path.unshift(index);
112      current = current.parentElement;
113
114      if (current.tagName === 'BODY') break;
115    }
116
117    const line = path.join('-')
118
119    return sanitizeAttributeValue(line);
120  }
121
122  /**
123   * Extracts text content from an element
124   */
125  function extractTextContent(element) {
126    let text = '';
127    for (const node of element.childNodes) {
128      if (node.nodeType === Node.TEXT_NODE) {
129        text += node.textContent.trim() + ' ';
130      }
131    }
132    return text.trim();
133  }
134
135  /**
136   * Extracts attributes from an element
137   */
138  function extractAttributes(element) {
139    const attributes = {};
140    const defaultAttrs = [
141      'class', 'id', 'src', 'alt', 'href', 'type', 'name', 'value'
142    ];
143
144    // Extract default attributes
145    for (const attr of defaultAttrs) {
146      if (element.hasAttribute(attr)) {
147        if (attr === 'class') {
148          attributes['className'] = element.getAttribute(attr);
149        } else {
150          attributes[attr] = element.getAttribute(attr);
151        }
152      }
153    }
154
155    const textContent = extractTextContent(element);
156    if (textContent) {
157      attributes['textContent'] = textContent;
158    }
159
160    return attributes;
161  }
162
163  /**
164   * Determines if an element should be tagged
165   */
166  function shouldTagElement(element) {
167    // Skip non-element nodes
168    if (element.nodeType !== Node.ELEMENT_NODE) {
169      return false;
170    }
171
172    const tagName = element.tagName.toLowerCase();
173
174    if (CONFIG.includeElements.length > 0) {
175      return CONFIG.includeElements.includes(tagName);
176    }
177
178    if (CONFIG.excludeElements.includes(tagName) || TAG_SHOULD_EXCLUDE.includes(tagName)) {
179      return false;
180    }
181
182    return true;
183  }
184
185  /**
186   * Tags an element with component metadata
187   */
188  function tagElement(element) {
189    // Skip if already tagged
190    if (element.hasAttribute(`${CONFIG.attributePrefix}-id`)) {
191      return;
192    }
193
194    // Get element name
195    const elementName = element.tagName.toLowerCase();
196
197    // Create component ID
198    const componentId = createComponentId(element);
199
200    // Set ID attribute
201    element.setAttribute(`${CONFIG.attributePrefix}-id`, componentId);
202
203    // Add legacy attributes if enabled
204    if (CONFIG.includeLegacyAttributes) {
205      element.setAttribute(`${CONFIG.attributePrefix}-path`, window.location.pathname);
206      element.setAttribute(`${CONFIG.attributePrefix}-name`, elementName);
207      element.setAttribute(`${CONFIG.attributePrefix}-file`, window.location.pathname.split('/').pop());
208    }
209
210    // Add content attribute if enabled
211    if (CONFIG.includeContentAttribute) {
212      const attributes = extractAttributes(element);
213      const content = {
214        elementName,
215        ...attributes
216      };
217
218      let encodedContent = encodeURIComponent(JSON.stringify(content));
219
220      // Truncate if needed
221      if (encodedContent.length > CONFIG.maxContentLength) {
222        encodedContent = encodedContent.substring(0, CONFIG.maxContentLength) + '...';
223      }
224
225      element.setAttribute(`${CONFIG.attributePrefix}-content`, encodedContent);
226    }
227  }
228
229  /**
230   * Process all elements in the document
231   */
232  function processAllElements() {
233    const elements = document.querySelectorAll('*');
234    let taggedCount = 0;
235
236    elements.forEach((element, index) => {
237      if (shouldTagElement(element)) {
238        tagElement(element, index);
239        taggedCount++;
240      }
241    });
242
243  }
244
245  /**
246   * Initialize the component tagger
247   */
248  function initialize() {
249    // Wait for DOM to be fully loaded
250    if (document.readyState === 'loading') {
251      document.addEventListener('DOMContentLoaded', processAllElements);
252    } else {
253      processAllElements();
254    }
255
256    // Also process on dynamic content changes
257    const observer = new MutationObserver((mutations) => {
258
259        for (const mutation of mutations) {
260        if (mutation.type === 'childList') {
261            for (const node of mutation.addedNodes) {
262            if (node.nodeType === Node.ELEMENT_NODE) {
263                if (shouldTagElement(node)) {
264                tagElement(node);
265                }
266
267                // Process child elements
268                const childElements = node.querySelectorAll('*');
269                for (const child of childElements) {
270                if (shouldTagElement(child)) {
271                    tagElement(child);
272                }
273                }
274            }
275            }
276        }
277        }
278    });
279
280    observer.observe(document.body, {
281      childList: true,
282      subtree: true
283    });
284  }
285
286  // Start the component tagger
287  initialize();
288})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.