PageSourceSearch

https://www.brotherssupply.com/wp-content/plugins/hozio-studio/com…/rate-menu/rate-menu.js?ver=e87649424d51

js brotherssupply.com collected 2026-10-09 00:30:27 UTC 4,736 bytes, 95 lines download raw bytes

1/* ═══════════════════════════════════════════════════════════════════════════
2   SERVICE RATE MENU (W-080) — measurement-only progressive enhancement.
3
4   The rate card is fully functional without this file: the categorized
5   dotted-leader list, every price, the "what's included" native <details>
6   disclosure, and the Service/Offer JSON-LD are ALL server-rendered
7   (rate-menu.php). This script adds exactly ONE additive layer — the fleet's
8   highest-value untracked action, per-row CTA conversion tracking (§13):
9
10     · a `call` CTA (tel:) tap → dataLayer `phone_call`
11     · a book / quote / link CTA tap → dataLayer `select_content`
12
13   It mirrors the DOM anchors directly (the widget emits no internal hzo:* event
14   to mirror — cf. nap.js's identical tel: adapter) and is a strict no-op when
15   window.dataLayer is never created. There is NO filter/scroll-spy behavior in
16   this pass (the widget is Category-B / no-JS complete); the reserved
17   filter=chips option can land later behind a data-attr without touching this.
18
19   The listener is delegated + bound once (idempotent via a namespace flag), so a
20   second script load or an AJAX-injected menu needs no re-init. Reduced-motion,
21   focus, and print are all CSS/native concerns — this file only measures.
22   ═══════════════════════════════════════════════════════════════════════════ */
23(function () {
24    'use strict';
25
26    window.hzo = window.hzo || {};
27
28    // ── Measurement: per-row CTA tap → dataLayer ───────────────────────────────
29    // Delegated + idempotent (bound once). A `call` desktop tel: no-ops silently,
30    // so the number is ALSO the anchor's selectable link text (server-rendered) —
31    // this only measures the intent, it is not the desktop fallback.
32    function bindMeasurement() {
33        if (window.hzo.__rateMenuMeasureBound) { return; }
34        window.hzo.__rateMenuMeasureBound = true;
35
36        document.addEventListener('click', function (e) {
37            var a = (e.target && e.target.closest)
38                ? e.target.closest('[data-hzo-rate-menu] a[data-hzo-rate-cta]')
39                : null;
40            if (!a) { return; }
41
42            // Respect modifier / middle clicks — never swallow "open in new tab"
43            // and never mis-attribute a non-navigating gesture (§11.4).
44            if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) {
45                return;
46            }
47
48            var kind = a.getAttribute('data-hzo-rate-cta') || '';
49            var name = ctaServiceName(a);
50            window.dataLayer = window.dataLayer || [];
51
52            if (kind === 'call') {
53                window.dataLayer.push({
54                    event: 'phone_call',
55                    phone_number: (a.getAttribute('href') || '').replace(/^tel:/, ''),
56                    service_name: name,
57                    source: 'rate-menu'
58                });
59            } else {
60                // book | quote | link — the destination form owns generate_lead.
61                window.dataLayer.push({
62                    event: 'select_content',
63                    content_type: 'rate_menu_cta',
64                    cta_kind: kind,
65                    service_name: name,
66                    source: 'rate-menu'
67                });
68            }
69        }, false);
70    }
71
72    // Best-effort service label for the pushed event: the row's name cell.
73    function ctaServiceName(a) {
74        var row = a.closest ? a.closest('.hzo-rate-row') : null;
75        if (!row) { return ''; }
76        var nameEl = row.querySelector('.hzo-rate-row__name');
77        return nameEl ? (nameEl.textContent || '').replace(/\s+/g, ' ').trim() : '';
78    }
79
80    // ── Init (idempotent) ──────────────────────────────────────────────────────
81    function init() {
82        bindMeasurement();
83    }
84
85    // Expose on the shared namespace (mirrors the widget convention) so an
86    // AJAX-injected menu can re-assert the binding; both calls are no-ops after
87    // the first because the delegated listener lives on document.
88    window.hzo.rateMenu = { init: init };
89
90    if (document.readyState === 'loading') {
91        document.addEventListener('DOMContentLoaded', init);
92    } else {
93        init();
94    }
95})();

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.