PageSourceSearch

https://nadinechang.com/assets/js/tooltips-setup.js?v=fe487e99db6c43751e6e7bdb2d6a0611

js nadinechang.com collected 2026-09-28 07:06:31 UTC 3,398 bytes, 116 lines download raw bytes

1(() => {
2  const state = {
3    tooltip: null,
4    popover: null,
5  };
6
7  const createFloating = (className) => {
8    const node = document.createElement("div");
9    node.className = className;
10    node.hidden = true;
11    document.body.appendChild(node);
12    return node;
13  };
14
15  const positionFloating = (trigger, bubble) => {
16    const rect = trigger.getBoundingClientRect();
17    const left = rect.left + window.scrollX + rect.width / 2 - bubble.offsetWidth / 2;
18    const top = rect.top + window.scrollY - bubble.offsetHeight - 8;
19    bubble.style.left = `${Math.max(8, left)}px`;
20    bubble.style.top = `${Math.max(8, top)}px`;
21  };
22
23  const initFallbackTooltips = (root = document) => {
24    root.querySelectorAll('[data-toggle="tooltip"]').forEach((trigger) => {
25      if (trigger.dataset.afTooltipBound === "true") {
26        return;
27      }
28      trigger.dataset.afTooltipBound = "true";
29
30      const show = () => {
31        const text = trigger.getAttribute("title") || trigger.getAttribute("data-original-title") || "";
32        if (!text) {
33          return;
34        }
35        if (!state.tooltip) {
36          state.tooltip = createFloating("af-tooltip");
37        }
38        state.tooltip.textContent = text;
39        state.tooltip.hidden = false;
40        positionFloating(trigger, state.tooltip);
41      };
42
43      const hide = () => {
44        if (state.tooltip) {
45          state.tooltip.hidden = true;
46        }
47      };
48
49      trigger.addEventListener("mouseenter", show);
50      trigger.addEventListener("focus", show);
51      trigger.addEventListener("mouseleave", hide);
52      trigger.addEventListener("blur", hide);
53    });
54  };
55
56  const initFallbackPopovers = (root = document) => {
57    root.querySelectorAll('[data-toggle="popover"]').forEach((trigger) => {
58      if (trigger.dataset.afPopoverBound === "true") {
59        return;
60      }
61      trigger.dataset.afPopoverBound = "true";
62
63      const show = () => {
64        const title = trigger.getAttribute("title") || "";
65        const content = trigger.getAttribute("data-content") || "";
66        const text = [title, content].filter(Boolean).join("\n");
67        if (!text) {
68          return;
69        }
70        if (!state.popover) {
71          state.popover = createFloating("af-popover");
72        }
73        state.popover.textContent = text;
74        state.popover.hidden = false;
75        positionFloating(trigger, state.popover);
76      };
77
78      const hide = () => {
79        if (state.popover) {
80          state.popover.hidden = true;
81        }
82      };
83
84      trigger.addEventListener("mouseenter", show);
85      trigger.addEventListener("focus", show);
86      trigger.addEventListener("mouseleave", hide);
87      trigger.addEventListener("blur", hide);
88    });
89  };
90
91  const initTooltips = (root = document) => {
92    if (window.AlFolioCompat && typeof window.AlFolioCompat.initTooltips === "function") {
93      window.AlFolioCompat.initTooltips(root);
94      return;
95    }
96    initFallbackTooltips(root);
97  };
98
99  const initPopovers = (root = document) => {
100    if (window.AlFolioCompat && typeof window.AlFolioCompat.initPopovers === "function") {
101      window.AlFolioCompat.initPopovers(root);
102      return;
103    }
104    initFallbackPopovers(root);
105  };
106
107  window.AlFolioUi = {
108    initTooltips,
109    initPopovers,
110  };
111
112  document.addEventListener("DOMContentLoaded", () => {
113    initTooltips(document);
114    initPopovers(document);
115  });
116})();

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.