PageSourceSearch

https://www.beautyrest.com/scripts/aem.js

js beautyrest.com collected 2026-10-02 12:47:01 UTC 23,793 bytes, 764 lines download raw bytes

1/*
2 * Copyright 2024 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12
13/* eslint-env browser */
14function sampleRUM(checkpoint, data) {
15  // eslint-disable-next-line max-len
16  const timeShift = () => (window.performance ? window.performance.now() : Date.now() - window.hlx.rum.firstReadTime);
17  try {
18    window.hlx = window.hlx || {};
19    if (!window.hlx.rum || !window.hlx.rum.collector) {
20      sampleRUM.enhance = () => { };
21      const params = new URLSearchParams(window.location.search);
22      const { currentScript } = document;
23      const rate = params.get('rum')
24        || window.SAMPLE_PAGEVIEWS_AT_RATE
25        || params.get('optel')
26        || (currentScript && currentScript.dataset.rate);
27      const rateValue = {
28        on: 1,
29        off: 0,
30        high: 10,
31        low: 1000,
32      }[rate];
33      const weight = rateValue !== undefined ? rateValue : 100;
34      const id = (window.hlx.rum && window.hlx.rum.id) || crypto.randomUUID().slice(-9);
35      const isSelected = (window.hlx.rum && window.hlx.rum.isSelected)
36        || (weight > 0 && Math.random() * weight < 1);
37      // eslint-disable-next-line object-curly-newline, max-len
38      window.hlx.rum = {
39        weight,
40        id,
41        isSelected,
42        firstReadTime: window.performance ? window.performance.timeOrigin : Date.now(),
43        sampleRUM,
44        queue: [],
45        collector: (...args) => window.hlx.rum.queue.push(args),
46      };
47      if (isSelected) {
48        const dataFromErrorObj = (error) => {
49          const errData = { source: 'undefined error' };
50          try {
51            errData.target = error.toString();
52            if (error.stack) {
53              errData.source = error.stack
54                .split('\n')
55                .filter((line) => line.match(/https?:\/\//))
56                .shift()
57                .replace(/at ([^ ]+) \((.+)\)/, '$1@$2')
58                .replace(/ at /, '@')
59                .trim();
60            }
61          } catch (err) {
62            /* error structure was not as expected */
63          }
64          return errData;
65        };
66
67        window.addEventListener('error', ({ error }) => {
68          const errData = dataFromErrorObj(error);
69          sampleRUM('error', errData);
70        });
71
72        window.addEventListener('unhandledrejection', ({ reason }) => {
73          let errData = {
74            source: 'Unhandled Rejection',
75            target: reason || 'Unknown',
76          };
77          if (reason instanceof Error) {
78            errData = dataFromErrorObj(reason);
79          }
80          sampleRUM('error', errData);
81        });
82
83        window.addEventListener('securitypolicyviolation', (e) => {
84          if (e.blockedURI.includes('helix-rum-enhancer') && e.disposition === 'enforce') {
85            const errData = {
86              source: 'csp',
87              target: e.blockedURI,
88            };
89            sampleRUM.sendPing('error', timeShift(), errData);
90          }
91        });
92
93        sampleRUM.baseURL = sampleRUM.baseURL || new URL(window.RUM_BASE || '/', new URL('https://ot.aem.live'));
94        sampleRUM.collectBaseURL = sampleRUM.collectBaseURL || sampleRUM.baseURL;
95        sampleRUM.sendPing = (ck, time, pingData = {}) => {
96          // eslint-disable-next-line max-len, object-curly-newline
97          const rumData = JSON.stringify({
98            weight,
99            id,
100            referer: window.location.href,
101            checkpoint: ck,
102            t: time,
103            ...pingData,
104          });
105          const urlParams = window.RUM_PARAMS
106            ? new URLSearchParams(window.RUM_PARAMS).toString() || ''
107            : '';
108          const { href: url, origin } = new URL(
109            `.rum/${weight}
109${urlParams ? `?${urlParams}` : ''}`,
110            sampleRUM.collectBaseURL,
111          );
112          const body = origin === window.location.origin
113            ? new Blob([rumData], { type: 'application/json' })
114            : rumData;
115          navigator.sendBeacon(url, body);
116          // eslint-disable-next-line no-console
117          console.debug(`ping:${ck}`, pingData);
118        };
119        sampleRUM.sendPing('top', timeShift());
120
121        sampleRUM.enhance = () => {
122          // only enhance once
123          if (document.querySelector('script[src*="rum-enhancer"]')) return;
124          const { enhancerVersion, enhancerHash } = sampleRUM.enhancerContext || {};
125          const script = document.createElement('script');
126          if (enhancerHash) {
127            script.integrity = enhancerHash;
128            script.setAttribute('crossorigin', 'anonymous');
129          }
130          script.src = new URL(
131            `.rum/@adobe/helix-rum-enhancer@${enhancerVersion || '^2'}/src/index.js`,
132            sampleRUM.baseURL,
133          ).href;
134          document.head.appendChild(script);
135        };
136        if (!window.hlx.RUM_MANUAL_ENHANCE) {
137          sampleRUM.enhance();
138        }
139      }
140    }
141    if (window.hlx.rum && window.hlx.rum.isSelected && checkpoint) {
142      window.hlx.rum.collector(checkpoint, data, timeShift());
143    }
144    document.dispatchEvent(new CustomEvent('rum', { detail: { checkpoint, data } }));
145  } catch (error) {
146    // something went awry
147  }
148}
149
150/**
151 * Setup block utils.
152 */
153function setup() {
154  window.hlx = window.hlx || {};
155  window.hlx.RUM_MASK_URL = 'full';
156  window.hlx.RUM_MANUAL_ENHANCE = true;
157  window.hlx.codeBasePath = '';
158  window.hlx.lighthouse = new URLSearchParams(window.location.search).get('lighthouse') === 'on';
159
160  const scriptEl = document.querySelector('script[src$="/scripts/scripts.js"]');
161  if (scriptEl) {
162    try {
163      [window.hlx.codeBasePath] = new URL(scriptEl.src).pathname.split('/scripts/scripts.js');
164    } catch (error) {
165      // eslint-disable-next-line no-console
166      console.log(error);
167    }
168  }
169}
170
171/**
172 * Auto initialization.
173 */
174
175function init() {
176  setup();
177  sampleRUM.collectBaseURL = window.origin;
178  sampleRUM();
179}
180
181/**
182 * Sanitizes a string for use as class name.
183 * @param {string} name The unsanitized string
184 * @returns {string} The class name
185 */
186function toClassName(name) {
187  return typeof name === 'string'
188    ? name
189      .toLowerCase()
190      .replace(/[^0-9a-z]/gi, '-')
191      .replace(/-+/g, '-')
192      .replace(/^-|-$/g, '')
193    : '';
194}
195
196/**
197 * Sanitizes a string for use as a js property name.
198 * @param {string} name The unsanitized string
199 * @returns {string} The camelCased name
200 */
201function toCamelCase(name) {
202  return toClassName(name).replace(/-([a-z])/g, (g) => g[1].toUpperCase());
203}
204
205/**
206 * Extracts the config from a block.
207 * @param {Element} block The block element
208 * @returns {object} The block config
209 */
210// eslint-disable-next-line import/prefer-default-export
211function readBlockConfig(block) {
212  const config = {};
213  [...block.children].forEach((row) => {
214    if (row.children) {
215      const cols = [...row.children];
216      if (cols[1]) {
217        const col = cols[1];
218        const name = toClassName(cols[0].textContent);
219        let value = '';
220        if (col.querySelector('a')) {
221          const as = [...col.querySelectorAll('a')];
222          if (as.length === 1) {
223            value = as[0].href;
224          } else {
225            value = as.map((a) => a.href);
226          }
227        } else if (col.querySelector('img')) {
228          const imgs = [...col.querySelectorAll('img')];
229          if (imgs.length === 1) {
230            value = imgs[0].src;
231          } else {
232            value = imgs.map((img) => img.src);
233          }
234        } else if (col.querySelector('p')) {
235          const ps = [...col.querySelectorAll('p')];
236          if (ps.length === 1) {
237            value = ps[0].textContent;
238          } else {
239            value = ps.map((p) => p.textContent);
240          }
241        } else value = row.children[1].textContent;
242        config[name] = value;
243      }
244    }
245  });
246  return config;
247}
248
249/**
250 * Loads a CSS file.
251 * @param {string} href URL to the CSS file
252 */
253async function loadCSS(href) {
254  return new Promise((resolve, reject) => {
255    if (!document.querySelector(`head > link[href="${href}"]`)) {
256      const link = document.createElement('link');
257      link.rel = 'stylesheet';
258      link.href = href;
259      link.onload = resolve;
260      link.onerror = reject;
261      document.head.append(link);
262    } else {
263      resolve();
264    }
265  });
266}
267
268/**
269 * Loads a non module JS file.
270 * @param {string} src URL to the JS file
271 * @param {Object} attrs additional optional attributes
272 */
273async function loadScript(src, attrs) {
274  return new Promise((resolve, reject) => {
275    if (!document.querySelector(`head > script[src="${src}"]`)) {
276      const script = document.createElement('script');
277      script.src = src;
278      if (attrs) {
279        // eslint-disable-next-line no-restricted-syntax, guard-for-in
280        for (const attr in attrs) {
281          script.setAttribute(attr, attrs[attr]);
282        }
283      }
284      script.onload = resolve;
285      script.onerror = reject;
286      document.head.append(script);
287    } else {
288      resolve();
289    }
290  });
291}
292
293/**
294 * Retrieves the content of metadata tags.
295 * @param {string} name The metadata name (or property)
296 * @param {Document} doc Document object to query for metadata. Defaults to the window's document
297 * @returns {string} The metadata value(s)
298 */
299function getMetadata(name, doc = document) {
300  const attr = name && name.includes(':') ? 'property' : 'name';
301  const meta = [...doc.head.querySelectorAll(`meta[${attr}="${name}"]`)]
302    .map((m) => m.content)
303    .join(', ');
304  return meta || '';
305}
306
307/**
308 * Returns a picture element with webp and fallbacks
309 * @param {string} src The image URL
310 * @param {string} [alt] The image alternative text
311 * @param {boolean} [eager] Set loading attribute to eager
312 * @param {Array} [breakpoints] Breakpoints and corresponding params (eg. width)
313 * @returns {Element} The picture element
314 */
315function createOptimizedPicture(
316  src,
317  alt = '',
318  eager = false,
319  breakpoints = [{ media: '(min-width: 600px)', width: '2000' }, { width: '750' }],
320) {
321  const url = !src.startsWith('http') ? new URL(src, window.location.href) : new URL(src);
322  const picture = document.createElement('picture');
323  const { origin, pathname } = url;
324  const ext = pathname.split('.').pop();
325
326  // webp
327  breakpoints.forEach((br) => {
328    const source = document.createElement('source');
329    if (br.media) source.setAttribute('media', br.media);
330    source.setAttribute('type', 'image/webp');
331    source.setAttribute('srcset', `${origin}${pathname}?width=${br.width}&format=webply&optimize=medium`);
332    picture.appendChild(source);
333  });
334
335  // fallback
336  breakpoints.forEach((br, i) => {
337    if (i < breakpoints.length - 1) {
338      const source = document.createElement('source');
339      if (br.media) source.setAttribute('media', br.media);
340      source.setAttribute('srcset', `${origin}${pathname}?width=${br.width}&format=${ext}&optimize=medium`);
341      picture.appendChild(source);
342    } else {
343      const img = document.createElement('img');
344      img.setAttribute('loading', eager ? 'eager' : 'lazy');
345      img.setAttribute('alt', alt);
346      picture.appendChild(img);
347      img.setAttribute('src', `${origin}${pathname}?width=${br.width}&format=${ext}&optimize=medium`);
348    }
349  });
350
351  return picture;
352}
353
354/**
355 * Set template (page structure) and theme (page styles).
356 */
357function decorateTemplateAndTheme() {
358  const addClasses = (element, classes) => {
359    classes.split(',').forEach((c) => {
360      element.classList.add(toClassName(c.trim()));
361    });
362  };
363  const template = getMetadata('template');
364  if (template) addClasses(document.body, template);
365  const theme = getMetadata('theme');
366  if (theme) addClasses(document.body, theme);
367}
368
369/**
370 * Wrap inline text content of block cells within a <p> tag.
371 * @param {Element} block the block element
372 */
373function wrapTextNodes(block) {
374  const validWrappers = [
375    'P',
376    'PRE',
377    'UL',
378    'OL',
379    'PICTURE',
380    'TABLE',
381    'H1',
382    'H2',
383    'H3',
384    'H4',
385    'H5',
386    'H6',
387  ];
388
389  const wrap = (el) => {
390    const wrapper = document.createElement('p');
391    wrapper.append(...el.childNodes);
392    el.append(wrapper);
393  };
394
395  block.querySelectorAll(':scope > div > div').forEach((blockColumn) => {
396    if (blockColumn.hasChildNodes()) {
397      const hasWrapper = !!blockColumn.firstElementChild
398        && validWrappers.some((tagName) => blockColumn.firstElementChild.tagName === tagName);
399      if (!hasWrapper) {
400        wrap(blockColumn);
401      } else if (
402        blockColumn.firstElementChild.tagName === 'PICTURE'
403        && (blockColumn.children.length > 1 || !!blockColumn.textContent.trim())
404      ) {
405        wrap(blockColumn);
406      }
407    }
408  });
409}
410
411// DEFAULT decorateButtons implementation.
412/**
413 * Decorates paragraphs containing a single link as buttons.
414 * @param {Element} element container element
415 */
416function decorateButtons(element) {
417  element.querySelectorAll('a').forEach((a) => {
418    a.title = a.title || a.textContent;
419    if (a.href !== a.textContent) {
420      const up = a.parentElement;
421      const twoup = a.parentElement.parentElement;
422      if (!a.querySelector('img')) {
423        if (up.childNodes.length === 1 && (up.tagName === 'P' || up.tagName === 'DIV')) {
424          a.className = 'button'; // default
425          up.classList.add('button-container');
426        }
427        if (
428          up.childNodes.length === 1
429          && up.tagName === 'STRONG'
430          && twoup.childNodes.length === 1
431          && twoup.tagName === 'P'
432        ) {
433          a.className = 'button primary';
434          twoup.classList.add('button-container');
435        }
436        if (
437          up.childNodes.length === 1
438          && up.tagName === 'EM'
439          && twoup.childNodes.length === 1
440          && twoup.tagName === 'P'
441        ) {
442          a.className = 'button secondary';
443          twoup.classList.add('button-container');
444        }
445      }
446    }
447  });
448}
449
450/**
451 * Add <img> for icon, prefixed with codeBasePath and optional prefix.
452 * @param {Element} [span] span element with icon classes
453 * @param {string} [prefix] prefix to be added to icon src
454 * @param {string} [alt] alt text to be added to icon
455 */
456function decorateIcon(span, prefix = '', alt = '') {
457  const iconName = Array.from(span.classList)
458    .find((c) => c.startsWith('icon-'))
459    .substring(5);
460  const img = document.createElement('img');
461  img.dataset.iconName = iconName;
462  img.src = `${window.hlx.codeBasePath}${prefix}/icons/${iconName}.svg`;
463  img.alt = alt;
464  img.loading = 'lazy';
465  span.append(img);
466}
467
468/**
469 * Add <img> for icons, prefixed with codeBasePath and optional prefix.
470 * @param {Element} [element] Element containing icons
471 * @param {string} [prefix] prefix to be added to icon the src
472 */
473function decorateIcons(element, prefix = '') {
474  const icons = [...element.querySelectorAll('span.icon')];
475  icons.forEach((span) => {
476    decorateIcon(span, prefix);
477  });
478}
479
480/**
481 * Decorates all sections in a container element.
482 * @param {Element} main The container element
483 */
484function decorateSections(main) {
485  main.querySelectorAll(':scope > div').forEach((section) => {
486    const wrappers = [];
487    let defaultContent = false;
488    [...section.children].forEach((e) => {
489      if (e.tagName === 'DIV' || !defaultContent) {
490        const wrapper = document.createElement('div');
491        wrappers.push(wrapper);
492        defaultContent = e.tagName !== 'DIV';
493        if (defaultContent) wrapper.classList.add('default-content-wrapper');
494      }
495      wrappers[wrappers.length - 1].append(e);
496    });
497    wrappers.forEach((wrapper) => section.append(wrapper));
498    section.classList.add('section');
499    section.dataset.sectionStatus = 'initialized';
500    section.style.display = 'none';
501
502    // Process section metadata
503    const sectionMeta = section.querySelector('div.section-metadata');
504    if (sectionMeta) {
505      const meta = readBlockConfig(sectionMeta);
506      Object.keys(meta).forEach((key) => {
507        if (key === 'style') {
508          const styles = meta.style
509            .split(',')
510            .filter((style) => style)
511            .map((style) => toClassName(style.trim()));
512          styles.forEach((style) => section.classList.add(style));
513        } else {
514          section.dataset[toCamelCase(key)] = meta[key];
515        }
516      });
517      sectionMeta.parentNode.remove();
518    }
519  });
520}
521
522/**
523 * Gets placeholders object.
524 * @param {string} [prefix] Location of placeholders
525 * @returns {object} Window placeholders object
526 */
527// eslint-disable-next-line import/prefer-default-export
528async function fetchPlaceholders(prefix = 'default') {
529  window.placeholders = window.placeholders || {};
530  if (!window.placeholders[prefix]) {
531    window.placeholders[prefix] = new Promise((resolve) => {
532      fetch(`${prefix === 'default' ? '' : prefix}/placeholders.json`)
533        .then((resp) => {
534          if (resp.ok) {
535            return resp.json();
536          }
537          return {};
538        })
539        .then((json) => {
540          const placeholders = {};
541          json.data
542            .filter((placeholder) => placeholder.Key)
543            .forEach((placeholder) => {
544              placeholders[toCamelCase(placeholder.Key)] = placeholder.Text;
545            });
546          window.placeholders[prefix] = placeholders;
547          resolve(window.placeholders[prefix]);
548        })
549        .catch(() => {
550          // error loading placeholders
551          window.placeholders[prefix] = {};
552          resolve(window.placeholders[prefix]);
553        });
554    });
555  }
556  return window.placeholders[`${prefix}`];
557}
558
559/**
560 * Builds a block DOM Element from a two dimensional array, string, or object
561 * @param {string} blockName name of the block
562 * @param {*} content two dimensional array or string or object of content
563 */
564function buildBlock(blockName, content) {
565  const table = Array.isArray(content) ? content : [[content]];
566  const blockEl = document.createElement('div');
567  // build image block nested div structure
568  blockEl.classList.add(blockName);
569  table.forEach((row) => {
570    const rowEl = document.createElement('div');
571    row.forEach((col) => {
572      const colEl = document.createElement('div');
573      const vals = col.elems ? col.elems : [col];
574      vals.forEach((val) => {
575        if (val) {
576          if (typeof val === 'string') {
577            colEl.innerHTML += val;
578          } else {
579            colEl.appendChild(val);
580          }
581        }
582      });
583      rowEl.appendChild(colEl);
584    });
585    blockEl.appendChild(rowEl);
586  });
587  return blockEl;
588}
589
590/**
591 * Loads JS and CSS for a block.
592 * @param {Element} block The block element
593 */
594async function loadBlock(block) {
595  const status = block.dataset.blockStatus;
596  const isSSR = block.dataset.ssr === 'true';
597  if (status !== 'loading' && (status !== 'loaded' || isSSR)) {
598    if (!isSSR) block.dataset.blockStatus = 'loading';
599    const { blockName } = block.dataset;
600    try {
601      const cssLoaded = loadCSS(`${window.hlx.codeBasePath}/blocks/${blockName}/${blockName}.css`);
602      const decorationComplete = new Promise((resolve) => {
603        (async () => {
604          try {
605            const mod = await import(
606              `${window.hlx.codeBasePath}/blocks/${blockName}/${blockName}.js`
607            );
608            if (mod.default) {
609              await mod.default(block);
610            }
611          } catch (error) {
612            // eslint-disable-next-line no-console
613            console.log(`failed to load module for ${blockName}`, error);
614          }
615          resolve();
616        })();
617      });
618      await Promise.all([cssLoaded, decorationComplete]);
619    } catch (error) {
620      // eslint-disable-next-line no-console
621      console.log(`failed to load block ${blockName}`, error);
622    }
623    block.dataset.blockStatus = 'loaded';
624  }
625  return block;
626}
627
628/**
629 * Decorates a block.
630 * @param {Element} block The block element
631 */
632function decorateBlock(block) {
633  const shortBlockName = block.classList[0];
634  if (shortBlockName) {
635    block.classList.add('block');
636    block.dataset.blockName = shortBlockName;
637    // Preserve SSR block status — skip re-initialization and wrapTextNodes
638    if (block.dataset.ssr === 'true' && block.dataset.blockStatus === 'loaded') {
639      // SSR'd block: keep status as 'loaded', skip wrapTextNodes to preserve rendered HTML
640    } else {
641      block.dataset.blockStatus = 'initialized';
642      wrapTextNodes(block);
643    }
644    const blockWrapper = block.parentElement;
645    blockWrapper.classList.add(`${shortBlockName}-wrapper`);
646    const section = block.closest('.section');
647    if (section) section.classList.add(`${shortBlockName}-container`);
648  }
649}
650
651/**
652 * Decorates all blocks in a container element.
653 * @param {Element} main The container element
654 */
655function decorateBlocks(main) {
656  main.querySelectorAll('div.section > div > div').forEach(decorateBlock);
657}
658
659/**
660 * Loads a block named 'header' into header
661 * @param {Element} header header element
662 * @returns {Promise}
663 */
664async function loadHeader(header) {
665  const headerBlock = buildBlock('header', '');
666  header.append(headerBlock);
667  decorateBlock(headerBlock);
668  return loadBlock(headerBlock);
669}
670
671/**
672 * Loads a block named 'footer' into footer
673 * @param footer footer element
674 * @returns {Promise}
675 */
676async function loadFooter(footer) {
677  const footerBlock = buildBlock('footer', '');
678  footer.append(footerBlock);
679  decorateBlock(footerBlock);
680  return loadBlock(footerBlock);
681}
682
683/**
684 * Wait for Image.
685 * @param {Element} section section element
686 */
687async function waitForFirstImage(section) {
688  const lcpCandidate = section.querySelector('img');
689  await new Promise((resolve) => {
690    if (lcpCandidate && !lcpCandidate.complete) {
691      lcpCandidate.setAttribute('loading', 'eager');
692      lcpCandidate.addEventListener('load', resolve);
693      lcpCandidate.addEventListener('error', resolve);
694    } else {
695      resolve();
696    }
697  });
698}
699
700/**
701 * Loads all blocks in a section.
702 * @param {Element} section The section element
703 */
704
705async function loadSection(section, loadCallback) {
706  const status = section.dataset.sectionStatus;
707  if (!status || status === 'initialized') {
708    section.dataset.sectionStatus = 'loading';
709    const blocks = [...section.querySelectorAll('div.block')];
710    for (let i = 0; i < blocks.length; i += 1) {
711      // eslint-disable-next-line no-await-in-loop
712      await loadBlock(blocks[i]);
713    }
714    if (loadCallback) await loadCallback(section);
715    section.dataset.sectionStatus = 'loaded';
716    section.style.display = null;
717  }
718}
719
720/**
721 * Loads all sections.
722 * @param {Element} element The parent element of sections to load
723 */
724
725async function loadSections(element) {
726  const sections = [...element.querySelectorAll('div.section')];
727  for (let i = 0; i < sections.length; i += 1) {
728    // eslint-disable-next-line no-await-in-loop
729    await loadSection(sections[i]);
730    if (i === 0 && sampleRUM.enhance) {
731      sampleRUM.enhance();
732    }
733  }
734}
735
736init();
737
738export {
739  buildBlock,
740  createOptimizedPicture,
741  decorateBlock,
742  decorateBlocks,
743  decorateButtons,
744  decorateIcons,
745  decorateSections,
746  decorateTemplateAndTheme,
747  fetchPlaceholders,
748  getMetadata,
749  init,
750  loadBlock,
751  loadCSS,
752  loadFooter,
753  loadHeader,
754  loadScript,
755  loadSection,
756  loadSections,
757  readBlockConfig,
758  sampleRUM,
759  setup,
760  toCamelCase,
761  toClassName,
762  waitForFirstImage,
763  wrapTextNodes,
764};

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.