PageSourceSearch

https://giaiphapdokiem.vn/wp-content/plugins/wp-rocket/assets/js/wpr-beacon.js

js giaiphapdokiem.vn collected 2026-09-24 12:29:55 UTC 20,550 bytes, 541 lines download raw bytes

vendor: 14 bytes, lines 1-2
1(() => {
2  // 
2src/Utils.js
3  var BeaconUtils = class {
4    static getScreenWidth() {
5      return window.innerWidth || document.documentElement.clientWidth;
6    }
7    static getScreenHeight() {
8      return window.innerHeight || document.documentElement.clientHeight;
9    }
10    static isNotValidScreensize(is_mobile, threshold) {
11      const screenWidth = this.getScreenWidth();
12      const screenHeight = this.getScreenHeight();
13      const isNotValidForMobile = is_mobile && (screenWidth > threshold.width || screenHeight > threshold.height);
14      const isNotValidForDesktop = !is_mobile && (screenWidth < threshold.width || screenHeight < threshold.height);
15      return isNotValidForMobile || isNotValidForDesktop;
16    }
17    static isPageCached() {
18      const signature = document.documentElement.nextSibling && document.documentElement.nextSibling.data ? document.documentElement.nextSibling.data : "";
19      return signature && signature.includes("Debug: cached");
20    }
21    static isIntersecting(rect) {
22      return rect.bottom >= 0 && rect.right >= 0 && rect.top <= (window.innerHeight || document.documentElement.clientHeight) && rect.left <= (window.innerWidth || document.documentElement.clientWidth);
23    }
24    static isPageScrolled() {
25      return window.pageYOffset > 0 || document.documentElement.scrollTop > 0;
26    }
27  };
28  var Utils_default = BeaconUtils;
29
30  
30// src/BeaconLcp.js
31  var BeaconLcp = class {
32    constructor(config, logger) {
33      this.config = config;
34      this.performanceImages = [];
35      this.logger = logger;
36    }
37    async run() {
38      try {
39        const above_the_fold_images = this._generateLcpCandidates(Infinity);
40        if (above_the_fold_images) {
41          this._initWithFirstElementWithInfo(above_the_fold_images);
42          this._fillATFWithoutDuplications(above_the_fold_images);
43        }
44      } catch (err) {
45        this.errorCode = "script_error";
46        this.logger.logMessage("Script Error: " + err);
47      }
48    }
49    _generateLcpCandidates(count) {
50      const lcpElements = document.querySelectorAll(this.config.elements);
51      if (lcpElements.length <= 0) {
52        return [];
53      }
54      const potentialCandidates = Array.from(lcpElements);
55      const topCandidates = potentialCandidates.map((element) => {
56        if ("img" === element.nodeName.toLowerCase() && "picture" === element.parentElement.nodeName.toLowerCase()) {
57          return null;
58        }
59        let rect;
60        if ("picture" === element.nodeName.toLowerCase()) {
61          const imgElement = element.querySelector("img");
62          if (imgElement) {
63            rect = imgElement.getBoundingClientRect();
64          } else {
65            return null;
66          }
67        } else {
68          rect = element.getBoundingClientRect();
69        }
70        return {
71          element,
72          rect
73        };
74      }).filter((item) => item !== null).filter((item) => {
75        return item.rect.width > 0 && item.rect.height > 0 && Utils_default.isIntersecting(item.rect);
76      }).map((item) => ({
77        item,
78        area: this._getElementArea(item.rect),
79        elementInfo: this._getElementInfo(item.element)
80      })).sort((a, b) => b.area - a.area).slice(0, count);
81      return topCandidates.map((candidate) => ({
82        element: candidate.item.element,
83        elementInfo: candidate.elementInfo
84      }));
85    }
86    _getElementArea(rect) {
87      const visibleWidth = Math.min(rect.width, (window.innerWidth || document.documentElement.clientWidth) - rect.left);
88      const visibleHeight = Math.min(rect.height, (window.innerHeight || document.documentElement.clientHeight) - rect.top);
89      return visibleWidth * visibleHeight;
90    }
91    _getElementInfo(element) {
92      const nodeName = element.nodeName.toLowerCase();
93      const element_info = {
94        type: "",
95        src: "",
96        srcset: "",
97        sizes: "",
98        sources: [],
99        bg_set: [],
100        current_src: ""
101      };
102      const css_bg_url_rgx = /url\(\s*?['"]?\s*?(.+?)\s*?["']?\s*?\)/ig;
103      if (nodeName === "img" && element.srcset) {
104        element_info.type = "img-srcset";
105        element_info.src = element.src;
106        element_info.srcset = element.srcset;
107        element_info.sizes = element.sizes;
108        element_info.current_src = element.currentSrc;
109      } else if (nodeName === "img") {
110        element_info.type = "img";
111        element_info.src = element.src;
112        element_info.current_src = element.currentSrc;
113      } else if (nodeName === "video") {
114        element_info.type = "img";
115        const source = element.querySelector("source");
116        element_info.src = element.poster || (source ? source.src : "");
117        element_info.current_src = element_info.src;
118      } else if (nodeName === "svg") {
119        const imageElement = element.querySelector("image");
120        if (imageElement) {
121          element_info.type = "img";
122          element_info.src = imageElement.getAttribute("href") || "";
123          element_info.current_src = element_info.src;
124        }
125      } else if (nodeName === "picture") {
126        element_info.type = "picture";
127        const img = element.querySelector("img");
128        element_info.src = img ? img.src : "";
129        element_info.sources = Array.from(element.querySelectorAll("source")).map((source) => ({
130          srcset: source.srcset || "",
131          media: source.media || "",
132          type: source.type || "",
133          sizes: source.sizes || ""
134        }));
135      } else {
136        const computed_style = window.getComputedStyle(element, null);
137        const bg_props = [
138          computed_style.getPropertyValue("background-image"),
139          getComputedStyle(element, ":after").getPropertyValue("background-image"),
140          getComputedStyle(element, ":before").getPropertyValue("background-image")
141        ].filter((prop) => prop !== "none");
142        if (bg_props.length === 0) {
143          return null;
144        }
145        const full_bg_prop = bg_props[0];
146        element_info.type = "bg-img";
147        if (full_bg_prop.includes("image-set(")) {
148          element_info.type = "bg-img-set";
149        }
150        if (!full_bg_prop || full_bg_prop === "" || full_bg_prop.includes("data:image")) {
151          return null;
152        }
153        const matches = [...full_bg_prop.matchAll(css_bg_url_rgx)];
154        element_info.bg_set = matches.map((m) => m[1] ? { src: m[1].trim() + (m[2] ? " " + m[2].trim() : "") } : {});
155        if (element_info.bg_set.every((item) => item.src === "")) {
156          element_info.bg_set = matches.map((m) => m[1] ? { src: m[1].trim() } : {});
157        }
158        if (element_info.bg_set.length <= 0) {
159          return null;
160        }
161        if (element_info.bg_set.length > 0) {
162          element_info.src = element_info.bg_set[0].src;
163          if (element_info.type === "bg-img-set") {
164            element_info.src = element_info.bg_set;
165          }
166        }
167      }
168      return element_info;
169    }
170    _initWithFirstElementWithInfo(elements) {
171      const firstElementWithInfo = elements.find((item) => {
172        return item.elementInfo !== null && (item.elementInfo.src || item.elementInfo.srcset);
173      });
174      if (!firstElementWithInfo) {
175        this.logger.logMessage("No LCP candidate found.");
176        this.performanceImages = [];
177        return;
178      }
179      this.performanceImages = [{
180        ...firstElementWithInfo.elementInfo,
181        label: "lcp"
182      }];
183    }
184    _fillATFWithoutDuplications(elements) {
185      elements.forEach(({ element, elementInfo }) => {
186        if (this._isDuplicateImage(element) || !elementInfo) {
187          return;
188        }
189        this.performanceImages.push({ ...elementInfo, label: "above-the-fold" });
190      });
191    }
192    _isDuplicateImage(image) {
193      const elementInfo = this._getElementInfo(image);
194      if (elementInfo === null) {
195        return false;
196      }
197      const isImageOrVideo = elementInfo.type === "img" || elementInfo.type === "img-srcset" || elementInfo.type === "video";
198      const isBgImageOrPicture = elementInfo.type === "bg-img" || elementInfo.type === "bg-img-set" || elementInfo.type === "picture";
199      return (isImageOrVideo || isBgImageOrPicture) && this.performanceImages.some((item) => item.src === elementInfo.src);
200    }
201    getResults() {
202      return this.performanceImages;
203    }
204  };
205  var BeaconLcp_default = BeaconLcp;
206
207  
207// src/BeaconLrc.js
208  var BeaconLrc = class {
209    constructor(config, logger) {
210      this.config = config;
211      this.logger = logger;
212      this.lazyRenderElements = [];
213    }
214    async run() {
215      try {
216        const elementsInView = this._getLazyRenderElements();
217        if (elementsInView) {
218          this._processElements(elementsInView);
219        }
220      } catch (err) {
221        this.errorCode = "script_error";
222        this.logger.logMessage("Script Error: " + err);
223      }
224    }
225    _getLazyRenderElements() {
226      const elements = document.querySelectorAll("[data-rocket-location-hash]");
227      const svgUseTargets = this._getSvgUseTargets();
228      if (elements.length <= 0) {
229        return [];
230      }
231      const validElements = Array.from(elements).filter((element) => {
232        if (this._skipElement(element)) {
233          return false;
234        }
235        if (svgUseTargets.includes(element)) {
236          this.logger.logColoredMessage(`Element skipped because of SVG: ${element.tagName}`, "orange");
237          return false;
238        }
239        return true;
240      });
241      return validElements.map((element) => ({
242        element,
243        depth: this._getElementDepth(element),
244        distance: this._getElementDistance(element),
245        hash: this._getLocationHash(element)
246      }));
247    }
248    _getElementDepth(element) {
249      let depth = 0;
250      let parent = element.parentElement;
251      while (parent) {
252        depth++;
253        parent = parent.parentElement;
254      }
255      return depth;
256    }
257    _getElementDistance(element) {
258      const rect = element.getBoundingClientRect();
259      const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
260      return Math.max(0, rect.top + scrollTop - Utils_default.getScreenHeight());
261    }
262    _skipElement(element) {
263      const skipStrings = this.config.skipStrings || ["memex"];
264      if (!element || !element.id) return false;
265      return skipStrings.some((str) => element.id.toLowerCase().includes(str.toLowerCase()));
266    }
267    _shouldSkipElement(element, exclusions) {
268      if (!element) return false;
269      for (let i = 0; i < exclusions.length; i++) {
270        const [attribute, pattern] = exclusions[i];
271        const attributeValue = element.getAttribute(attribute);
272        if (attributeValue && new RegExp(pattern, "i").test(attributeValue)) {
273          return true;
274        }
275      }
276      return false;
277    }
278    _checkLcrConflict(element) {
279      const conflictingElements = [];
280      const computedStyle = window.getComputedStyle(element);
281      const validMargins = ["marginTop", "marginRight", "marginBottom", "marginLeft"];
282      const negativeMargins = validMargins.some((margin) => parseFloat(computedStyle[margin]) < 0);
283      const currentElementConflicts = negativeMargins || computedStyle.contentVisibility === "auto" || computedStyle.contentVisibility === "hidden";
284      if (currentElementConflicts) {
285        conflictingElements.push({
286          element,
287          conflicts: [
288            negativeMargins && "negative margin",
289            computedStyle.contentVisibility === "auto" && "content-visibility:auto",
290            computedStyle.contentVisibility === "hidden" && "content-visibility:hidden"
291          ].filter(Boolean)
292        });
293      }
294      Array.from(element.children).forEach((child) => {
295        const childStyle = window.getComputedStyle(child);
296        const validMargins2 = ["marginTop", "marginRight", "marginBottom", "marginLeft"];
297        const childNegativeMargins = validMargins2.some((margin) => parseFloat(childStyle[margin]) < 0);
298        const childConflicts = childNegativeMargins || childStyle.position === "absolute" || childStyle.position === "fixed";
299        if (childConflicts) {
300          conflictingElements.push({
301            element: child,
302            conflicts: [
303              childNegativeMargins && "negative margin",
304              childStyle.position === "absolute" && "position:absolute",
305              childStyle.position === "fixed" && "position:fixed"
306            ].filter(Boolean)
307          });
308        }
309      });
310      return conflictingElements;
311    }
312    _processElements(elements) {
313      elements.forEach(({ element, depth, distance, hash }) => {
314        if (this._shouldSkipElement(element, this.config.exclusions || [])) {
315          return;
316        }
317        if ("No hash detected" === hash) {
318          return;
319        }
320        const conflicts = this._checkLcrConflict(element);
321        if (conflicts.length > 0) {
322          this.logger.logMessage("Skipping element due to conflicts:", conflicts);
323          return;
324        }
325        const can_push_hash = element.parentElement && this._getElementDistance(element.parentElement) < this.config.lrc_threshold && distance >= this.config.lrc_threshold;
326        const color = can_push_hash ? "green" : distance === 0 ? "red" : "";
327        this.logger.logColoredMessage(`${"	".repeat(depth)}${element.tagName} (Depth: ${depth}, Distance from viewport bottom: ${distance}px)`, color);
328        this.logger.logColoredMessage(`${"	".repeat(depth)}Location hash: ${hash}`, color);
329        this.logger.logColoredMessage(`${"	".repeat(depth)}Dimensions Client Height: ${element.clientHeight}`, color);
330        if (can_push_hash) {
331          this.lazyRenderElements.push(hash);
332          this.logger.logMessage(`Element pushed with hash: ${hash}`);
333        }
334      });
335    }
336    _getXPath(element) {
337      if (element && element.id !== "") {
338        return `//*[@id="${element.id}"]`;
339      }
340      return this._getElementXPath(element);
341    }
342    _getElementXPath(element) {
343      if (element === document.body) {
344        return "/html/body";
345      }
346      const position = this._getElementPosition(element);
347      return `${this._getElementXPath(element.parentNode)}/${element.nodeName.toLowerCase()}[${position}]`;
348    }
349    _getElementPosition(element) {
350      let pos = 1;
351      let sibling = element.previousElementSibling;
352      while (sibling) {
353        if (sibling.nodeName === element.nodeName) {
354          pos++;
355        }
356        sibling = sibling.previousElementSibling;
357      }
358      return pos;
359    }
360    _getLocationHash(element) {
361      return element.hasAttribute("data-rocket-location-hash") ? element.getAttribute("data-rocket-location-hash") : "No hash detected";
362    }
363    _getSvgUseTargets() {
364      const useElements = document.querySelectorAll("use");
365      const targets = /* @__PURE__ */ new Set();
366      useElements.forEach((use) => {
367        let parent = use.parentElement;
368        while (parent && parent !== document.body) {
369          targets.add(parent);
370          parent = parent.parentElement;
371        }
372      });
373      return Array.from(targets);
374    }
375    getResults() {
376      return this.lazyRenderElements;
377    }
378  };
379  var BeaconLrc_default = BeaconLrc;
380
381  
381// src/Logger.js
382  var Logger = class {
383    constructor(enabled) {
384      this.enabled = enabled;
385    }
386    logMessage(msg) {
387      if (!this.enabled) {
388        return;
389      }
390      console.log(msg);
391    }
392    logColoredMessage(msg, color = "green") {
393      if (!this.enabled) {
394        return;
395      }
396      console.log(`%c${msg}`, `color: ${color};`);
397    }
398  };
399  var Logger_default = Logger;
400
401  
401// src/BeaconManager.js
402  var BeaconManager = class {
403    constructor(config) {
404      this.config = config;
405      this.lcpBeacon = null;
406      this.lrcBeacon = null;
407      this.infiniteLoopId = null;
408      this.errorCode = "";
409      this.logger = new Logger_default(this.config.debug);
410    }
411    async init() {
412      this.scriptTimer = /* @__PURE__ */ new Date();
413      if (!await this._isValidPreconditions()) {
414        this._finalize();
415        return;
416      }
417      if (Utils_default.isPageScrolled()) {
418        this.logger.logMessage("Bailing out because the page has been scrolled");
419        this._finalize();
420        return;
421      }
422      this.infiniteLoopId = setTimeout(() => {
423        this._handleInfiniteLoop();
424      }, 1e4);
425      const isGeneratedBefore = await this._getGeneratedBefore();
426      const shouldGenerateLcp = this.config.status.atf && (isGeneratedBefore === false || isGeneratedBefore.lcp === false);
427      const shouldGeneratelrc = this.config.status.lrc && (isGeneratedBefore === false || isGeneratedBefore.lrc === false);
428      if (shouldGenerateLcp) {
429        this.lcpBeacon = new BeaconLcp_default(this.config, this.logger);
430        await this.lcpBeacon.run();
431      } else {
432        this.logger.logMessage("Not running BeaconLcp because data is already available or feature is disabled");
433      }
434      if (shouldGeneratelrc) {
435        this.lrcBeacon = new BeaconLrc_default(this.config, this.logger);
436        await this.lrcBeacon.run();
437      } else {
438        this.logger.logMessage("Not running BeaconLrc because data is already available or feature is disabled");
439      }
440      if (shouldGenerateLcp || shouldGeneratelrc) {
441        this._saveFinalResultIntoDB();
442      } else {
443        this.logger.logMessage("Not saving results into DB as no beacon features ran.");
444        this._finalize();
445      }
446    }
447    async _isValidPreconditions() {
448      const threshold = {
449        width: this.config.width_threshold,
450        height: this.config.height_threshold
451      };
452      if (Utils_default.isNotValidScreensize(this.config.is_mobile, threshold)) {
453        this.logger.logMessage("Bailing out because screen size is not acceptable");
454        return false;
455      }
456      return true;
457    }
458    async _getGeneratedBefore() {
459      if (!Utils_default.isPageCached()) {
460        return false;
461      }
462      let data_check = new FormData();
463      data_check.append("action", "rocket_check_beacon");
464      data_check.append("rocket_beacon_nonce", this.config.nonce);
465      data_check.append("url", this.config.url);
466      data_check.append("is_mobile", this.config.is_mobile);
467      const beacon_data_response = await fetch(this.config.ajax_url, {
468        method: "POST",
469        credentials: "same-origin",
470        body: data_check
471      }).then((data) => data.json());
472      return beacon_data_response.data;
473    }
474    _saveFinalResultIntoDB() {
475      const results = {
476        lcp: this.lcpBeacon ? this.lcpBeacon.getResults() : null,
477        lrc: this.lrcBeacon ? this.lrcBeacon.getResults() : null
478      };
479      const data = new FormData();
480      data.append("action", "rocket_beacon");
481      data.append("rocket_beacon_nonce", this.config.nonce);
482      data.append("url", this.config.url);
483      data.append("is_mobile", this.config.is_mobile);
484      data.append("status", this._getFinalStatus());
485      data.append("results", JSON.stringify(results));
486      fetch(this.config.ajax_url, {
487        method: "POST",
488        credentials: "same-origin",
489        body: data,
490        headers: {
491          "wpr-saas-no-intercept": true
492        }
493      }).then((response) => response.json()).then((data2) => {
494        this.logger.logMessage(data2.data.lcp);
495      }).catch((error) => {
496        this.logger.logMessage(error);
497      }).finally(() => {
498        this._finalize();
499      });
500    }
501    _getFinalStatus() {
502      if ("" !== this.errorCode) {
503        return this.errorCode;
504      }
505      const scriptTime = (/* @__PURE__ */ new Date() - this.scriptTimer) / 1e3;
506      if (10 <= scriptTime) {
507        return "timeout";
508      }
509      return "success";
510    }
511    _handleInfiniteLoop() {
512      this._saveFinalResultIntoDB();
513    }
514    _finalize() {
515      const beaconscript = document.querySelector('[data-name="wpr-wpr-beacon"]');
516      beaconscript.setAttribute("beacon-completed", "true");
517      clearTimeout(this.infiniteLoopId);
518    }
519  };
520  var BeaconManager_default = BeaconManager;
521
522  
522// src/BeaconEntryPoint.js
523  ((rocket_beacon_data) => {
524    if (!rocket_beacon_data) {
525      return;
526    }
527    const instance = new BeaconManager_default(rocket_beacon_data);
528    if (document.readyState !== "loading") {
529      setTimeout(() => {
530        instance.init();
531      }, rocket_beacon_data.delay);
532      return;
533    }
534    document.addEventListener("DOMContentLoaded", () => {
535      setTimeout(() => {
536        instance.init();
537      }, rocket_beacon_data.delay);
538    });
539  })(window.rocket_beacon_data);
540  var BeaconEntryPoint_default = BeaconManager_default;
541})();

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.