PageSourceSearch

https://www.bamboohr.com/blocks/milo-fragment/fragment.js

js bamboohr.com collected 2026-10-01 07:06:34 UTC 5,416 bytes, 194 lines download raw bytes

1/* eslint-disable import/no-cycle */
2/* eslint-disable max-classes-per-file */
3/* eslint-disable no-return-assign */
4/* eslint-disable no-restricted-syntax */
5import { createTag, getConfig, loadArea, localizeLink } from '../../milo-utils/utils.js';
6
7const fragMap = {};
8
9const removeHash = (url) => {
10  const urlNoHash = url.split('#')[0];
11  return url.includes('#_dnt') ? `${urlNoHash}#_dnt` : urlNoHash;
12};
13
14const isCircularRef = (href) => [...Object.values(fragMap)]
15  .some((tree) => {
16    const node = tree.find(href);
17    return node ? !(node.isLeaf) : false;
18  });
19
20const updateFragMap = (fragment, a, href) => {
21  const fragLinks = [...fragment.querySelectorAll('a')]
22    .filter((link) => localizeLink(link.href).includes('/fragments/'));
23  if (!fragLinks.length) return;
24
25  if (document.body.contains(a)) { // is fragment on page (not nested)
26    // eslint-disable-next-line no-use-before-define
27    fragMap[href] = new Tree(href);
28    fragLinks.forEach((link) => fragMap[href].insert(href, localizeLink(removeHash(link.href))));
29  } else {
30    Object.values(fragMap).forEach((tree) => {
31      if (tree.find(href)) {
32        fragLinks.forEach((link) => tree.insert(href, localizeLink(removeHash(link.href))));
33      }
34    });
35  }
36};
37
38const setManifestIdOnChildren = (sections, manifestId) => {
39  [...sections[0].children].forEach(
40    (child) => (child.dataset.manifestId = manifestId),
41  );
42};
43
44const insertInlineFrag = (sections, a, relHref) => {
45  // Inline fragments only support one section, other sections are ignored
46  const fragChildren = [...sections[0].children];
47  fragChildren.forEach((child) => child.setAttribute('data-path', relHref));
48  if (a.parentElement.nodeName === 'DIV' && !a.parentElement.attributes.length) {
49    a.parentElement.replaceWith(...fragChildren);
50  } else {
51    a.replaceWith(...fragChildren);
52  }
53};
54
55function replaceDotMedia(path, doc) {
56  const resetAttributeBase = (tag, attr) => {
57    doc.querySelectorAll(`${tag}[${attr}^="./media_"]`).forEach((el) => {
58      el[attr] = new URL(el.getAttribute(attr), new URL(path, window.location)).href;
59    });
60  };
61  resetAttributeBase('img', 'src');
62  resetAttributeBase('source', 'srcset');
63}
64
65export default async function init(a) {
66  const { expFragments, decorateArea, mep } = getConfig();
67  let relHref = localizeLink(a.href);
68  let inline = false;
69
70  if (a.parentElement?.nodeName === 'P') {
71    const children = a.parentElement.childNodes;
72    const div = createTag('div');
73    for (const attr of a.parentElement.attributes) div.setAttribute(attr.name, attr.value);
74    a.parentElement.replaceWith(div);
75    div.append(...children);
76  }
77
78  if (a.href.includes('#_inline')) {
79    inline = true;
80    a.href = a.href.replace('#_inline', '');
81    relHref = relHref.replace('#_inline', '');
82  }
83
84  const path = new URL(a.href).pathname;
85  if (expFragments?.[path] && mep) {
86    relHref = mep.handleFragmentCommand(expFragments[path], a);
87    if (!relHref) return;
88  }
89
90  if (isCircularRef(relHref)) {
91    window.lana?.log(`ERROR: Fragment Circular Reference loading ${a.href}`);
92    return;
93  }
94
95  const { customFetch } = await import('../../milo-utils/helpers.js');
96  const resp = await customFetch({ resource: `${a.href}.plain.html`, withCacheRules: true })
97    .catch(() => ({}));
98
99  if (!resp.ok) {
100    window.lana?.log(`Could not get fragment: ${a.href}.plain.html`);
101    return;
102  }
103
104  const html = await resp.text();
105  const doc = new DOMParser().parseFromString(html, 'text/html');
106  replaceDotMedia(a.href, doc);
107  if (decorateArea) decorateArea(doc, { fragmentLink: a });
108
109  const sections = doc.querySelectorAll('body > div');
110
111  if (!sections.length) {
112    window.lana?.log(`Could not make fragment: ${a.href}.plain.html`);
113    return;
114  }
115
116  const fragment = createTag('div', { class: 'fragment', 'data-path': relHref });
117
118  if (!inline) {
119    fragment.append(...sections);
120  }
121
122  updateFragMap(fragment, a, relHref);
123
124  if (a.dataset.manifestId) {
125    if (inline) {
126      setManifestIdOnChildren(sections, a.dataset.manifestId);
127    } else {
128      fragment.dataset.manifestId = a.dataset.manifestId;
129    }
130  }
131
132  if (inline) {
133    insertInlineFrag(sections, a, relHref);
134  } else {
135    a.parentElement.replaceChild(fragment, a);
136    await loadArea(fragment);
137  }
138}
139
140class Node {
141  constructor(key, value = key, parent = null) {
142    this.key = key;
143    this.value = value;
144    this.parent = parent;
145    this.children = [];
146  }
147
148  get isLeaf() {
149    return this.children.length === 0;
150  }
151}
152
153export class Tree {
154  constructor(key, value = key) {
155    this.root = new Node(key, value);
156  }
157
158  * traverse(node = this.root) {
159    yield node;
160    if (node.children.length) {
161      for (const child of node.children) {
162        yield* this.traverse(child);
163      }
164    }
165  }
166
167  insert(parentNodeKey, key, value = key) {
168    for (const node of this.traverse()) {
169      if (node.key === parentNodeKey) {
170        node.children.push(new Node(key, value, node));
171        return true;
172      }
173    }
174    return false;
175  }
176
177  remove(key) {
178    for (const node of this.traverse()) {
179      const filtered = node.children.filter((c) => c.key !== key);
180      if (filtered.length !== node.children.length) {
181        node.children = filtered;
182        return true;
183      }
184    }
185    return false;
186  }
187
188  find(key) {
189    for (const node of this.traverse()) {
190      if (node.key === key) return node;
191    }
192    return undefined;
193  }
194}

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.