PageSourceSearch

https://mountaincabinsutah.com/wp-content/plugins/homerunner-toolkit/assets/js/lra-no-dates.js?ver=3.309.18

js mountaincabinsutah.com collected 2026-10-10 02:02:16 UTC 5,275 bytes, 147 lines download raw bytes

1/**
2 * HomeRunner Toolkit — Lowest Rate Availability (frontend)
3 *
4 * Swaps the explorer card's price for a "No dates available" label on
5 * listings the module flagged as having zero bookable nights
6 * (payload key from config.flag, stamped server-side in Core.php).
7 *
8 * The explorer registers every grid instance on
9 * window.hrExplorer.instances and renders each card through the
10 * instance's getPropertyHtml(). Wrapping that method is the only
11 * per-listing hook the plugin exposes — its `no_dates_price` option is
12 * global, so suppressing the price there would blank every card on the
13 * site. The registry is wrapped in a Proxy so instances registered
14 * before OR after this script runs both get patched; card HTML itself
15 * is never produced before the explorer's first async fetch resolves,
16 * which cannot happen while footer scripts are still executing.
17 *
18 * Config (window.hrtkLraNoDates):
19 *   - label string  Label text (plain text; inserted via textContent).
20 *   - flag  string  Payload key marking a zero-availability listing.
21 */
22(function () {
23	'use strict';
24
25	var cfg = window.hrtkLraNoDates || {};
26	var label = typeof cfg.label === 'string' ? cfg.label : '';
27	if (!label) return;
28
29	var FLAG = typeof cfg.flag === 'string' && cfg.flag ? cfg.flag : 'hrtk_no_availability';
30
31	// Parse the card HTML and replace the price container's content with
32	// the label. DOM parsing rather than a regex: the price container can
33	// hold nested markup (tax-note divs), which a lazy regex mis-slices.
34	function relabel(html) {
35		var tpl = document.createElement('template');
36		tpl.innerHTML = html;
37
38		var container = tpl.content.querySelector('.hfse-property-price-container');
39		// No container means the site renders cards without prices
40		// (no_dates_price = "none"); nothing to swap.
41		if (!container) return html;
42
43		var span = document.createElement('span');
44		span.className = 'hfse-property-price hrtk-no-availability';
45		span.textContent = label;
46
47		container.textContent = '';
48		container.appendChild(span);
49
50		return tpl.innerHTML;
51	}
52
53	function patchInstance(inst) {
54		if (!inst || typeof inst.getPropertyHtml !== 'function' || inst.__hrtkNoDatesPatched) {
55			return;
56		}
57
58		var orig = inst.getPropertyHtml;
59		inst.getPropertyHtml = function (property) {
60			var html = orig.apply(this, arguments);
61			// The flag lives top-level on full payloads, but the explorer
62			// fetches with a `_fields` list that strips unknown top-level
63			// keys — there it survives only inside `acf`. Check both.
64			var flagged = property && (property[FLAG] || (property.acf && property.acf[FLAG]));
65			if (!flagged || typeof html !== 'string') {
66				return html;
67			}
68			return relabel(html);
69		};
70		inst.__hrtkNoDatesPatched = true;
71	}
72
73	// Patch instances already registered, and intercept future
74	// registrations (explorer.js assigns window.hrExplorer.instances[id]
75	// fresh on every init, so a set-trap sees each one).
76	function wrapRegistry(registry) {
77		Object.keys(registry).forEach(function (key) {
78			patchInstance(registry[key]);
79		});
80
81		return new Proxy(registry, {
82			set: function (target, prop, value) {
83				target[prop] = value;
84				patchInstance(value);
85				return true;
86			}
87		});
88	}
89
90	window.hrExplorer = window.hrExplorer || { instances: {} };
91	if (!window.hrExplorer.instances || typeof window.hrExplorer.instances !== 'object') {
92		window.hrExplorer.instances = {};
93	}
94	window.hrExplorer.instances = wrapRegistry(window.hrExplorer.instances);
95
96	// --- Booking widget ---------------------------------------------------
97	// The widget reads its rates inline from the shortcode config, bypassing
98	// every server-side filter (homerunner-plugin#754), so the flag never
99	// reaches it. The only reachable signal is the rendered price itself: a
100	// "Starting at $0.00" is never legitimate (a zero rate means "no bookable
101	// rate" — same invariant the REST nulling relies on). The widget fills
102	// the server-rendered empty .rent-unit divs client-side and re-fills
103	// them when dates change, so each unit gets a MutationObserver.
104
105	// Zero in any locale/currency: digits present, none of them 1-9.
106	function isZeroPrice(text) {
107		return /\d/.test(text) && !/[1-9]/.test(text);
108	}
109
110	function swapUnit(unit) {
111		var span = document.createElement('span');
112		span.className = 'hrtk-no-availability';
113		span.textContent = label;
114		unit.textContent = '';
115		unit.appendChild(span);
116	}
117
118	function checkUnit(unit) {
119		// Only act on a rendered rent amount, never on loaders or the label
120		// itself (the swap removes .rent-amount, so it can't re-trigger).
121		var amount = unit.querySelector('.rent-amount');
122		if (amount && isZeroPrice(amount.textContent || '')) {
123			swapUnit(unit);
124		}
125	}
126
127	function watchUnit(unit) {
128		if (unit.__hrtkNoDatesWatched) return;
129		unit.__hrtkNoDatesWatched = true;
130		new MutationObserver(function () {
131			checkUnit(unit);
132		}).observe(unit, { childList: true, subtree: true, characterData: true });
133		checkUnit(unit);
134	}
135
136	function initWidgetUnits() {
137		var units = document.querySelectorAll('.rent-unit');
138		for (var i = 0; i < units.length; i++) {
139			watchUnit(units[i]);
140		}
141	}
142
143	initWidgetUnits();
144	if (document.readyState === 'loading') {
145		document.addEventListener('DOMContentLoaded', initWidgetUnits);
146	}
147})();

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.