PageSourceSearch

https://www.brotherssupply.com/wp-content/plugins/hozio-studio/components/popover/popover.js?ver=699663569bef

js brotherssupply.com collected 2026-10-09 00:31:02 UTC 6,349 bytes, 127 lines download raw bytes

1/* ═════════════════════════════════════════════════════════════════════════
2   POPOVER — widget behavior (Phase-2 widget system; moved out of main.js)
3
4   Self-contained, no hard dependencies. As a shortcode widget the popover
5   ALWAYS loads: popover.php un-gates it via the hzostudio_component_markers
6   filter, because [hzo_popover] can render from surfaces the loader's
7   post_content scan can't see. The popover IIFE was removed from main.js so
8   behavior lives here. The edge-flip positioner stays in main.js
9   (hzo.flipIfClipped) — it is a cross-component primitive shared with the
10   tooltip — so its use is guarded at event time (never a hard dep; main.js is
11   conditionally enqueued and an unregistered dep would silently drop this
12   script).
13
14   BEHAVIOR (unchanged from main.js): a richer dropdown surface — click/focus
15   toggles a panel that MAY hold interactive content. NOT trapped (Tab c
15an
16   leave); Esc + click-outside close; focus returns to the trigger on Esc;
17   one popover open at a time. Markup contract (component-patterns §44):
18   .hzo-popover > .hzo-popover__trigger[aria-controls][aria-expanded]
19   + .hzo-popover__panel[id]. Add .hzo-popover__panel--right to right-align.
20
21   OPTION CONTRACT — every setting is a data-attribute on `.hzo-popover`, so
22   the block pattern (author writes attributes) and the [hzo_popover] shortcode
23   (popover.php generates them from settings) drive the SAME behavior. Every
24   option defaults OFF/legacy — attribute-free markup behaves exactly as before:
25
26     data-hzo-popover-flip   "false" → skip the viewport edge-flip check on
27                             open (panel stays in its authored placement even
28                             when it would clip). Default: flip when clipped,
29                             exactly as before.
30
31   A11y (WAI-ARIA APG Disclosure): trigger carries aria-expanded (+
32   aria-controls in markup); Esc closes and returns focus to the trigger;
33   click-outside closes without moving focus; the panel is NOT a modal so
34   focus is never trapped. Esc/click-outside dismiss is NOT configurable —
35   a popover can never become an inescapable surface (WCAG 2.1.2).
36
37   Public API (new — the old main.js IIFE exposed nothing):
38     hzo.popover = { init, setup, open, close }   (init(scope) for AJAX content)
39   ═════════════════════════════════════════════════════════════════════════ */
40(function () {
41    'use strict';
42
43    var openPop = null;   // one popover open at a time (module-wide)
44
45    function boolAttr(el, name, def) {
46        var v = el.getAttribute('data-hzo-popover-' + name);
47        if (v === null || v === '') { return def; }
48        return v !== 'false' && v !== '0';
49    }
50
51    function panelOf(pop) { return pop.querySelector('.hzo-popover__panel'); }
52    function triggerOf(pop) { return pop.querySelector('.hzo-popover__trigger'); }
53
54    function open(pop) {
55        if (!pop) { return; }
56        if (openPop && openPop !== pop) { close(openPop, false); }
57        pop.classList.add('is-open');
58        var t = triggerOf(pop);
59        if (t) { t.setAttribute('aria-expanded', 'true'); }
60        // Edge-flip via the shared main.js primitive — guarded at event time
61        // (main.js is conditionally enqueued; missing primitive = no flip,
62        // the popover still opens in its default placement).
63        var panel = panelOf(pop);
64        if (panel && boolAttr(pop, 'flip', true) &&
65            window.hzo && typeof window.hzo.flipIfClipped === 'function') {
66            window.hzo.flipIfClipped(panel, 'hzo-popover__panel--flip', 'hzo-popover__panel--flip-x');
67        }
68        openPop = pop;
69    }
70
71    function close(pop, returnFocus) {
72        if (!pop) { return; }
73        pop.classList.remove('is-open');
74        var t = triggerOf(pop);
75        if (t) { t.setAttribute('aria-expanded', 'false'); }
76        if (returnFocus && t) { t.focus(); }
77        if (openPop === pop) { openPop = null; }
78    }
79
80    /* Wire one popover's trigger + Esc handling. Idempotent via the ready
81       guard (only set once a trigger exists, so a popover whose trigger is
82       injected later can still be wired by a re-init). */
83    function setup(pop) {
84        if (!pop || pop.getAttribute('data-hzo-popover-ready') === 'true') { return; }
85        var t = triggerOf(pop);
86        if (!t) { return; }   // no trigger → nothing to wire (matches old behavior)
87        pop.setAttribute('data-hzo-popover-ready', 'true');
88
89        if (!t.hasAttribute('aria-expanded')) { t.setAttribute('aria-expanded', 'false'); }
90
91        t.addEventListener('click', function () {
92            if (pop.classList.contains('is-open')) { close(pop, false); } else { open(pop); }
93        });
94
95        // Esc closes + returns focus to the trigger. stopPropagation so an
96        // enclosing overlay (modal/drawer) doesn't also close on the same
97        // keypress — the popover is the innermost dismissable surface.
98        pop.addEventListener('keydown', function (e) {
99            if (e.key === 'Escape' && pop.classList.contains('is-open')) {
100                e.stopPropagation();
101                close(pop, true);
102            }
103        });
104    }
105
106    function init(scope) {
107        var pops = (scope || document).querySelectorAll('.hzo-popover');
108        for (var i = 0; i < pops.length; i++) { setup(pops[i]); }
109    }
110
111    // Click-outside closes the open popover (no focus move) — document-level,
112    // so it also covers popovers injected after init.
113    document.addEventListener('click', function (e) {
114        if (openPop && !openPop.contains(e.target)) { close(openPop, false); }
115    });
116
117    if (document.readyState === 'loading') {
118        document.addEventListener('DOMContentLoaded', function () { init(); });
119    } else {
120        init();
121    }
122
123    // Shared engine namespace (widget-convention surface; the old main.js
124    // popover IIFE exposed no public names, so nothing historical to preserve).
125    window.hzo = window.hzo || {};
126    window.hzo.popover = { init: init, setup: setup, open: open, close: close };
127})();

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.