PageSourceSearch

https://sidravi1.github.io/resume_sid_files/paged-0.13/js/hooks.js

js sidravi1.github.io collected 2026-10-03 09:57:07 UTC 18,878 bytes, 510 lines download raw bytes

1// Hooks for paged.js
2{
3  // Utils
4  let pandocMeta, pandocMetaToString;
5  {
6    let el = document.getElementById('pandoc-meta');
7    pandocMeta = el ? JSON.parse(el.firstChild.data) : {};
8  }
9
10  pandocMetaToString = meta => {
11    let el = document.createElement('div');
12    el.innerHTML = meta;
13    return el.innerText;
14  };
15
16  let isString = value => {
17    return typeof value === 'string' || value instanceof String;
18  };
19
20  let isArray = value => {
21    return value && typeof value === 'object' && value.constructor === Array;
22  };
23
24  // This hook is an attempt to fix https://github.com/rstudio/pagedown/issues/131
25  // Sometimes, the {break-after: avoid;} declaration applied on headers
26  // lead to duplicated headers. I hate this bug.
27  // This is linked to the way the HTML source is written
28  // When we have the \n character like this: <div>\n<h1>...</h1>
29  // the header may be duplicated.
30  // But, if we have <div><h1>...</h1> without any \n, the problem disappear
31  // I think this handler can fix most of cases
32  // Obviously, we cannot suppress all the \n in the HTML document
33  // because carriage returns are important in <pre> elements.
34  // Tested with Chrome 76.0.3809.100/Windows
35  Paged.registerHandlers(class extends Paged.Handler {
36    constructor(chunker, polisher, caller) {
37      super(chunker, polisher, caller);
38      this.carriageReturn = String.fromCharCode(10);
39    }
40
41    checkNode(node) {
42      if (!node) return;
43      if (node.nodeType !== 3) return;
44      if (node.textContent === this.carriageReturn) {
45        node.remove();
46      }
47    }
48
49    afterParsed(parsed) {
50      let template = document.querySelector('template').content;
51      const breakAfterAvoidElements = template.querySelectorAll('[data-break-after="avoid"], [data-break-before="avoid"]');
52      for (let el of breakAfterAvoidElements) {
53        this.checkNode(el.previousSibling);
54        this.checkNode(el.nextSibling);
55      }
56    }
57  });
58
59  // This hook creates a list of abbreviations
60  // Note: we also could implement this feature using a Pandoc filter
61  Paged.registerHandlers(class extends Paged.Handler {
62    constructor(chunker, polisher, caller) {
63      super(chunker, polisher, caller);
64    }
65    beforeParsed(content) {
66      const abbreviations = content.querySelectorAll('abbr');
67      if(abbreviations.length === 0) return;
68      const loaTitle = pandocMeta['loa-title'] ? pandocMetaToString(pandocMeta['loa-title']) : 'List of Abbreviations';
69      const loaId = 'LOA';
70      const tocList = content.querySelector('.toc ul');
71      let listOfAbbreviations = document.createElement('div');
72      let descriptionList = document.createElement('dl');
73      content.appendChild(listOfAbbreviations);
74      listOfAbbreviations.id = loaId;
75      listOfAbbreviations.classList.add('section', 'front-matter', 'level1', 'loa');
76      listOfAbbreviations.innerHTML = '<h1>' + loaTitle + '</h1>';
77      listOfAbbreviations.appendChild(descriptionList);
78      for(let abbr of abbreviations) {
79        if(!abbr.title) continue;
80        let term = document.createElement('dt');
81        let definition = document.createElement('dd');
82        descriptionList.appendChild(term);
83        descriptionList.appendChild(definition);
84        term.innerHTML = abbr.innerHTML;
85        definition.innerText = abbr.title;
86      }
87      if (tocList) {
88        const loaTOCItem = document.createElement('li');
89        loaTOCItem.innerHTML = '<a href="#' + loaId + '">' + loaTitle + '</a>';
90        tocList.appendChild(loaTOCItem);
91      }
92    }
93  });
94
95  // This hook moves the sections of class front-matter in the div.front-matter-container
96  Paged.registerHandlers(class extends Paged.Handler {
97    constructor(chunker, polisher, caller) {
98      super(chunker, polisher, caller);
99    }
100
101    beforeParsed(content) {
102      const frontMatter = content.querySelector('.front-matter-container');
103      if (!frontMatter) return;
104
105      // move front matter sections in the front matter container
106      const frontMatterSections = content.querySelectorAll('.level1.front-matter');
107      for (const section of frontMatterSections) {
108        frontMatter.appendChild(section);
109      }
110
111      // add the class front-matter-ref to any <a></a> element
112      // referring to an entry in the front matter
113      const anchors = content.querySelectorAll('a[href^="#"]:not([href*=":"])');
114      for (const a of anchors) {
115        const ref = a.getAttribute('href').replace(/^#/, '');
116        const element = content.getElementById(ref);
117        if (frontMatter.contains(element)) a.classList.add('front-matter-ref');
118      }
119
120      // update the toc, lof and lot for front matter sections
121      const frontMatterSectionsLinks = content.querySelectorAll('.toc .front-matter-ref, .lof .front-matter-ref, .lot .front-matter-ref');
122      for (let i = frontMatterSectionsLinks.length - 1; i >= 0; i--) {
123        const listItem = frontMatterSectionsLinks[i].parentNode;
124        const list = listItem.parentNode;
125        list.insertBefore(listItem, list.firstChild);
126      }
127    }
128  });
129
130  // This hook expands the links in the lists of figures and tables
131  Paged.registerHandlers(class extends Paged.Handler {
132    constructor(chunker, polisher, caller) {
133      super(chunker, polisher, caller);
134    }
135
136    beforeParsed(content) {
137      const items = content.querySelectorAll('.lof li, .lot li');
138      for (const item of items) {
139        const anchor = item.firstChild;
140        anchor.innerText = item.innerText;
141        item.innerText = '';
142        item.append(anchor);
143      }
144    }
145  });
146
147  // This hook adds spans for leading symbols
148  Paged.registerHandlers(class extends Paged.Handler {
149    constructor(chunker, polisher, caller) {
150      super(chunker, polisher, caller);
151    }
152
153    beforeParsed(content) {
154      const anchors = content.querySelectorAll('.toc a, .lof a, .lot a');
155      for (const a of anchors) {
156        a.innerHTML = a.innerHTML + '<span class="leaders"></span>';
157      }
158    }
159  });
160
161  // This hook appends short titles spans
162  Paged.registerHandlers(class extends Paged.Handler {
163    constructor(chunker, polisher, caller) {
164      super(chunker, polisher, caller);
165    }
166
167    beforeParsed(content) {
168    /* A factory returning a function that appends short titles spans.
169       The text content of these spans are reused for running titles (see default.css).
170       Argument: level - An integer between 1 and 6.
171    */
172    function appendShortTitleSpans(level) {
173      return () => {
174        const divs = Array.from(content.querySelectorAll('.level' + level));
175
176        function addSpan(div) {
177          const mainHeader = div.getElementsByTagName('h' + level)[0];
178          if (!mainHeader) return;
179          const mainTitle = mainHeader.textContent;
180          const spanSectionNumber = mainHeader.getElementsByClassName('header-section-number')[0];
181          const mainNumber = !!spanSectionNumber ? spanSectionNumber.textContent : '';
182          const runningTitle = 'shortTitle' in div.dataset ? mainNumber + ' ' + div.dataset.shortTitle : mainTitle;
183          const span = document.createElement('span');
184          span.className = 'shorttitle' + level;
185          span.innerText = runningTitle;
186          span.style.display = "none";
187          mainHeader.appendChild(span);
188          if (level == 1 && div.querySelector('.level2') === null) {
189            let span2 = document.createElement('span');
190            span2.className = 'shorttitle2';
191            span2.innerText = ' ';
192            span2.style.display = "none";
193            span.insertAdjacentElement('afterend', span2);
194          }
195        }
196
197        for (const div of divs) {
198          addSpan(div);
199        }
200      };
201    }
202
203    appendShortTitleSpans(1)();
204    appendShortTitleSpans(2)();
205    }
206  });
207
208  // Footnotes support
209  Paged.registerHandlers(class extends Paged.Handler {
210    constructor(chunker, polisher, caller) {
211      super(chunker, polisher, caller);
212
213      this.splittedParagraphRefs = [];
214    }
215
216    beforeParsed(content) {
217      // remove footnotes in toc, lof, lot
218      // see https://github.com/rstudio/pagedown/issues/54
219      let removeThese = content.querySelectorAll('.toc .footnote, .lof .footnote, .lot .footnote');
220      for (const el of removeThese) {
221        el.remove();
222      }
223
224      let footnotes = content.querySelectorAll('.footnote');
225
226      for (let footnote of footnotes) {
227        let parentElement = footnote.parentElement;
228        let footnoteCall = document.createElement('a');
229        let footnoteNumber = footnote.dataset.pagedownFootnoteNumber;
230
231        footnoteCall.className = 'footnote-ref'; // same class as Pandoc
232        footnoteCall.setAttribute('id', 'fnref' + footnoteNumber); // same notation as Pandoc
233        footnoteCall.setAttribute('href', '#' + footnote.id);
234        footnoteCall.innerHTML = '<sup>' + footnoteNumber +'</sup>';
235        parentElement.insertBefore(footnoteCall, footnote);
236
237        // Here comes a hack. Fortunately, it works with Chrome and FF.
238        let handler = document.createElement('p');
239        handler.className = 'footnoteHandler';
240        parentElement.insertBefore(handler, footnote);
241        handler.appendChild(footnote);
242        handler.style.display = 'inline-block';
243        handler.style.width = '100%';
244        handler.style.float = 'right';
245        handler.style.pageBreakInside = 'avoid';
246      }
247    }
248
249    afterPageLayout(pageFragment, page, breakToken) {
250      function hasItemParent(node) {
251        if (node.parentElement === null) {
252          return false;
253        } else {
254          if (node.parentElement.tagName === 'LI') {
255            return true;
256          } else {
257            return hasItemParent(node.parentElement);
258          }
259        }
260      }
261      // If a li item is broken, we store the reference of the p child element
262      // see https://github.com/rstudio/pagedown/issues/23#issue-376548000
263      if (breakToken !== undefined) {
264        if (breakToken.node.nodeName === "#text" && hasItemParent(breakToken.node)) {
265          this.splittedParagraphRefs.push(breakToken.node.parentElement.dataset.ref);
266        }
267      }
268    }
269
270    afterRendered(pages) {
271      for (let page of pages) {
272        const footnotes = page.element.querySelectorAll('.footnote');
273        if (footnotes.length === 0) {
274          continue;
275        }
276
277        const pageContent = page.element.querySelector('.pagedjs_page_content');
278        let hr = document.createElement('hr');
279        let footnoteArea = document.createElement('div');
280
281        pageContent.style.display = 'flex';
282        pageContent.style.flexDirection = 'column';
283
284        hr.className = 'footnote-break';
285        hr.style.marginTop = 'auto';
286        hr.style.marginBottom = 0;
287        hr.style.marginLeft = 0;
288        hr.style.marginRight = 'auto';
289        pageContent.appendChild(hr);
290
291        footnoteArea.className = 'footnote-area';
292        pageContent.appendChild(footnoteArea);
293
294        for (let footnote of footnotes) {
295          let handler = footnote.parentElement;
296
297          footnoteArea.appendChild(footnote);
298          handler.parentNode.removeChild(handler);
299
300          footnote.innerHTML = '<sup>' + footnote.dataset.pagedownFootnoteNumber + '</sup>' + footnote.innerHTML;
301          footnote.style.fontSize = 'x-small';
302          footnote.style.marginTop = 0;
303          footnote.style.marginBottom = 0;
304          footnote.style.paddingTop = 0;
305          footnote.style.paddingBottom = 0;
306          footnote.style.display = 'block';
307        }
308      }
309
310      for (let ref of this.splittedParagraphRefs) {
311        let paragraphFirstPage = document.querySelector('[data-split-to="' + ref + '"]');
312        // We test whether the paragraph is empty
313        // see https://github.com/rstudio/pagedown/issues/23#issue-376548000
314        if (paragraphFirstPage.innerText === "") {
315          paragraphFirstPage.parentElement.style.display = "none";
316          let paragraphSecondPage = document.querySelector('[data-split-from="' + ref + '"]');
317          paragraphSecondPage.parentElement.style.setProperty('list-style', 'inherit', 'important');
318        }
319      }
320    }
321  });
322
323  // Support for "Chapter " label on section with class `.chapter`
324  Paged.registerHandlers(class extends Paged.Handler {
325    constructor(chunker, polisher, caller) {
326      super(chunker, polisher, caller);
327
328      this.options = pandocMeta['chapter_name'];
329
330      let styles;
331      if (isString(this.options)) {
332        this.options = pandocMetaToString(this.options);
333        styles = `
334        :root {
335          --chapter-name-before: "${this.options}";
336        }
337        `;
338      }
339      if (isArray(this.options)) {
340        this.options = this.options.map(pandocMetaToString);
341        styles = `
342        :root {
343          --chapter-name-before: "${this.options[0]}";
344          --chapter-name-after: "${this.options[1]}";
345        }
346        `;
347      }
348      if (styles) polisher.insert(styles);
349    }
350
351    beforeParsed(content) {
352      const tocAnchors = content.querySelectorAll('.toc a[href^="#"]:not([href*=":"]');
353      for (const anchor of tocAnchors) {
354        const ref = anchor.getAttribute('href').replace(/^#/, '');
355        const element = content.getElementById(ref);
356        if (element.classList.contains('chapter')) {
357          anchor.classList.add('chapter-ref');
358        }
359      }
360    }
361  });
362
363  // Main text line numbering,
364  // see https://github.com/rstudio/pagedown/issues/115
365  // Original idea: Julien Taquet, thanks!
366  Paged.registerHandlers(class extends Paged.Handler {
367    constructor(chunker, polisher, caller) {
368      super(chunker, polisher, caller);
369
370      // get the number-lines option from Pandoc metavariables
371      this.options = pandocMeta['number-lines'];
372      // quit early if the "number-lines" option is false or missing
373      if (!this.options) return;
374      // retrieve the selector if provided, otherwise use the default selector
375      this.selector = this.options.selector ? pandocMetaToString(this.options.selector) : '.level1:not(.front-matter) h1, .level1 h2, .level1 h3, .level1 p';
376
377      const styles = `
378      :root {
379        --line-numbers-padding-right: 10px;
380        --line-numbers-font-size: 8pt;
381      }
382      .pagedown-linenumbers-container {
383        position: absolute;
384        margin-top: var(--pagedjs-margin-top);
385        right: calc(var(--pagedjs-width) - var(--pagedjs-margin-left));
386      }
387      .maintextlinenumbers {
388        position: absolute;
389        right: 0;
390        text-align: right;
391        padding-right: var(--line-numbers-padding-right);
392        font-size: var(--line-numbers-font-size);
393      }
394      `;
395      polisher.insert(styles);
396
397      this.resetLinesCounter();
398    }
399
400    appendLineNumbersContainer(page) {
401      const pagebox = page.element.querySelector('.pagedjs_pagebox');
402      const lineNumbersContainer = document.createElement('div');
403      lineNumbersContainer.classList.add('pagedown-linenumbers-container');
404
405      return pagebox.appendChild(lineNumbersContainer);
406    }
407
408    lineHeight(element) {
409      // If the document stylesheet does not define a value for line-height,
410      // Blink returns "normal". Therefore, parseInt may return NaN.
411      return parseInt(getComputedStyle(element).lineHeight);
412    }
413
414    innerHeight(element) {
415      let outerHeight = element.getBoundingClientRect().height;
416      let {borderTopWidth,
417           borderBottomWidth,
418           paddingTop,
419           paddingBottom} = getComputedStyle(element);
420
421      borderTopWidth = parseFloat(borderTopWidth);
422      borderBottomWidth = parseFloat(borderBottomWidth);
423      paddingTop = parseFloat(paddingTop);
424      paddingBottom = parseFloat(paddingBottom);
425
426      return Math.round(outerHeight - borderTopWidth - borderBottomWidth - paddingTop - paddingBottom);
427    }
428
429    arrayOfInt(from, length) {
430      // adapted from https://stackoverflow.com/a/50234108/6500804
431      return Array.from(Array(length).keys(), n => n + from);
432    }
433
434    incrementLinesCounter(value) {
435      this.linesCounter = this.linesCounter + value;
436    }
437
438    resetLinesCounter() {
439      this.linesCounter = 0;
440    }
441
442    isDisplayMath(element) {
443      const nodes = element.childNodes;
444      if (nodes.length != 1) return false;
445      return (nodes[0].nodeName === 'SPAN') && (nodes[0].classList.value === 'math display');
446    }
447
448    afterRendered(pages) {
449      if (!this.options) return;
450
451      for (let page of pages) {
452        const lineNumbersContainer = this.appendLineNumbersContainer(page);
453        const pageAreaY = page.area.getBoundingClientRect().y;
454        let elementsToNumber = page.area.querySelectorAll(this.selector);
455
456        for (let element of elementsToNumber) {
457          // Do not add line numbers for display math environment
458          if (this.isDisplayMath(element)) continue;
459
460          // Try to retrieve line height
461          const lineHeight = this.lineHeight(element);
462          // Test against lineHeight method returns NaN
463          if (!lineHeight) {
464            console.warn('Failed to compute line height value on "' + page.id + '".');
465            continue;
466          }
467
468          const innerHeight = this.innerHeight(element);
469
470          // Number of lines estimation
471          // There is no built-in method to detect the number of lines in a block.
472          // The main caveat is that an actual line height can differ from
473          // the line-height CSS property.
474          // Mixed fonts, subscripts, superscripts, inline math... can increase
475          // the actual line height.
476          // Here, we divide the inner height of the block by the line-height
477          // computed property and round to the floor to take into account that
478          // sometimes the actual line height is greater than its property value.
479          // This is far from perfect and can be easily broken especially by
480          // inline math.
481          const nLines = Math.floor(innerHeight / lineHeight);
482
483          // do not add line numbers for void paragraphs
484          if (nLines <= 0) continue;
485
486          const linenumbers = document.createElement('div');
487          lineNumbersContainer.appendChild(linenumbers);
488          linenumbers.classList.add('maintextlinenumbers');
489
490          const elementY = element.getBoundingClientRect().y;
491          linenumbers.style.top = (elementY - pageAreaY) + 'px';
492
493          const cs = getComputedStyle(element);
494          const paddingTop = parseFloat(cs.paddingTop) + parseFloat(cs.borderTopWidth);
495          linenumbers.style.paddingTop = paddingTop + 'px';
496
497          linenumbers.style.lineHeight = cs.lineHeight;
498
499          linenumbers.innerHTML = this.arrayOfInt(this.linesCounter + 1, nLines)
500                                      .reduce((t, v) => t + '<br />' + v);
501          this.incrementLinesCounter(nLines);
502        }
503
504        if (this.options['reset-page']) {
505          this.resetLinesCounter();
506        }
507      }
508    }
509  });
510}

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.