PageSourceSearch

https://cdn.whole-dog-journal.com/wp-content/themes/Newspaper-child/js/compare.js?ver=6.8

js whole-dog-journal.com collected 2026-10-02 02:54:54 UTC 16,200 bytes, 520 lines download raw bytes

1(function () {
2  // ---------------------------
3  // Compare bar: state helpers
4  // ---------------------------
5  const KEY = 'compareItems';
6  const getItems = () => JSON.parse(localStorage.getItem(KEY) || '[]');
7  const setItems = (items) => localStorage.setItem(KEY, JSON.stringify(items));
8
9  // Elements (safe-optional)
10  const compareBar = document.getElementById('compare-bar');
11  const compareItemsContainer = compareBar ? compareBar.querySelector('.compare-items') : null;
12  const compareCount = compareBar ? compareBar.querySelector('.compare-count') : null;
13  const compareLink = document.getElementById('compare-link');
14
15  function rebuildCompareBar() {
16    const items = getItems();
17
18    if (!compareBar) return;
19
20    if (items.length === 0) {
21      compareBar.classList.add('hidden');
22      if (compareItemsContainer) compareItemsContainer.innerHTML = '';
23      if (compareCount) compareCount.textContent = '0 items to compare';
24      if (compareLink) compareLink.href = '/dog-food-search/dog-food-compare/';
25      return;
26    }
27
28    compareBar.classList.remove('hidden');
29
30    if (compareCount) {
31      compareCount.textContent = `${items.length} item${items.length > 1 ? 's' : ''} to compare`;
32    }
33
34    if (compareItemsContainer) {
35      compareItemsContainer.innerHTML = '';
36      items.forEach(item => {
37        const el = document.createElement('div');
38        el.className = 'compare-item';
39        el.innerHTML = `
40          ${item.thumb ? `<img src="${item.thumb}" alt="${item.title}">` : ''}
41          <span>${item.title.substring(0, 20)}</span>
42          <button data-id="${item.id}" class="remove-compare" aria-label="Remove from compare">x</button>
43        `;
44        compareItemsContainer.appendChild(el);
45      });
46    }
47
48    if (compareLink) {
49      compareLink.href =
50        `/dog-food-search/dog-food-compare/?` + items.map((it, i) => `ids[${i}]=${encodeURIComponent(it.id)}`).join('&');
51    }
52  }
53
54  function syncCheckboxesFromState() {
55    const saved = new Set(getItems().map(i => String(i.id)));
56    document.querySelectorAll('.dog-food-compare-checkbox').forEach(cb => {
57      const id = cb.dataset.postId || cb.getAttribute('data-post-id');
58      cb.checked = saved.has(String(id));
59      const card = cb.closest('.dogfood-card');
60      if (card) card.classList.toggle('in-compare', cb.checked);
61    });
62  }
63
64  function addItemFromCheckbox(cb) {
65    const postId = cb.dataset.postId || cb.getAttribute('data-post-id');
66    let items = getItems();
67    if (items.find(i => String(i.id) === String(postId))) return; // already in
68
69    if (items.length >= 4) {
70      alert('You can compare up to 4 items.');
71      cb.checked = false;
72      return;
73    }
74
75    const card = cb.closest('.dogfood-card');
76    const title = card?.dataset.title || `#${postId}`;
77    const thumb = card?.dataset.thumb || '';
78
79    items.push({ id: String(postId), title, thumb });
80    setItems(items);
81    card && card.classList.add('in-compare');
82    rebuildCompareBar();
83  }
84
85  function removeItem(id) {
86    let items = getItems().filter(i => String(i.id) !== String(id));
87    setItems(items);
88    // Uncheck card if visible
89    const cb = document.querySelector(`.dog-food-compare-checkbox[data-post-id="${CSS.escape(String(id))}"]`);
90    if (cb) cb.checked = false;
91    const card = document.querySelector(`.dogfood-card[data-post-id="${CSS.escape(String(id))}"]`);
92    if (card) card.classList.remove('in-compare');
93    rebuildCompareBar();
94  }
95
96  // ---------------------------
97  // Delegated events
98  // ---------------------------
99  document.addEventListener('change', (e) => {
100    const cb = e.target.closest('.dog-food-compare-checkbox');
101    if (!cb) return;
102    const postId = cb.dataset.postId || cb.getAttribute('data-post-id');
103    if (cb.checked) {
104      addItemFromCheckbox(cb);
105    } else {
106      removeItem(postId);
107    }
108  });
109
110  document.addEventListener('click', (e) => {
111    const removeBtn = e.target.closest('.remove-compare');
112    if (removeBtn) {
113      removeItem(removeBtn.dataset.id);
114      return;
115    }
116    const clearBtn = e.target.closest('.clear-compare');
117    if (clearBtn) {
118      setItems([]);
119      document.querySelectorAll('.dog-food-compare-checkbox').forEach(cb => (cb.checked = false));
120      document.querySelectorAll('.dogfood-card.in-compare').forEach(c => c.classList.remove('in-compare'));
121      rebuildCompareBar();
122      return;
123    }
124  });
125
126  // ---------------------------
127  // FacetWP integration
128  // ---------------------------
129
130// --- Only auto-apply when the user interacts with the SEARCH facet ---
131// Adjust 'dog_food_search' in the selector if your facet name differs
132// Only auto-apply when the user interacts with the SEARCH facet.
133// Everything else still requires the "Apply Filters" button.
134(function ($) {
135  var allowAuto = false;
136  var prevVal = '';
137  var t;
138
139  // Helpers
140  function triggerSearchRefresh() {
141    allowAuto = true;              // allow auto-refresh for THIS cycle only
142    FWP.refresh();                 // kick off a refresh now
143  }
144
145  // 1) Typing in the search box (debounced)
146  //$(document).on('input', '.facetwp-facet-dog_food_search .facetwp-search', function () {
147  //  var val = this.value;
148  //  if (val === prevVal) return;
149  //  prevVal = val;
150  //  clearTimeout(t);
151  //  t = setTimeout(triggerSearchRefresh, 350); // debounce typing
152  //});
153
154  // 2) Pressing Enter in the search box
155  $(document).on('keydown', '.facetwp-facet-dog_food_search .facetwp-search', function (e) {
156    if (e.key === 'Enter') {
157      e.preventDefault();
158      clearTimeout(t);
159      triggerSearchRefresh();
160    }
161  });
162
163  // 3) Clicking the magnifier icon
164  $(document).on('click', '.facetwp-facet-dog_food_search .facetwp-icon', function (e) {
165    e.preventDefault();
166    clearTimeout(t);
167    triggerSearchRefresh();
168  });
169
170  // Gate FacetWP's auto refresh: ON only for the search-triggered cycle
171  //$(document).on('facetwp-refresh', function () {
172  //  FWP.auto_refresh = !!allowAuto;
173  //  allowAuto = false; // reset so other facet clicks require Apply
174 // });
175
176  // Apply button still works for all other facets
177  //$(document).on('click', '.facetwp-submit', function () {
178  //  FWP.refresh();
179  //});
180  
181})(jQuery);
182
183
184
185
186  // Re-sync compare UI after every FacetWP AJAX refresh
187  document.addEventListener('facetwp-loaded', function () {
188    syncCheckboxesFromState();
189    rebuildCompareBar();
190  });
191
192  // Initial paint
193  document.addEventListener('DOMContentLoaded', function () {
194    syncCheckboxesFromState();
195    rebuildCompareBar();
196  });
197
198  // ---------------------------
199  // Advanced Filters accordion
200  // ---------------------------
201  document.addEventListener('DOMContentLoaded', function () {
202    const toggle = document.querySelector('.accordion-toggle');
203    const panel = document.querySelector('.accordion-panel');
204    if (!toggle || !panel) return;
205
206    toggle.addEventListener('click', function () {
207      const expanded = this.getAttribute('aria-expanded') === 'true';
208      this.setAttribute('aria-expanded', expanded ? 'false' : 'true');
209      panel.classList.toggle('open');
210    });
211  });
212
213  // ---------------------------
214  // Loading spinner
215  // ---------------------------
216  (function ($) {
217    $(document).on('facetwp-refresh', function () {
218      $('#facetwp-loading').show();
219    });
220    $(document).on('facetwp-loaded', function () {
221      $('#facetwp-loading').hide();
222    });
223  })(jQuery);
224})();
225
226(function () {
227  const aside   = document.getElementById('dogfood-filters');
228  const toggle  = document.querySelector('.filters-toggle');
229  const closeBtn= document.querySelector('.filters-close');
230  const overlay = document.querySelector('.filters-overlay');
231
232  function openDrawer() {
233    if (!aside) return;
234    aside.classList.add('is-open');
235    document.body.classList.add('body-no-scroll');
236    if (overlay) overlay.hidden = false;
237    if (toggle) toggle.setAttribute('aria-expanded', 'true');
238    aside.setAttribute('aria-hidden', 'false');
239  }
240
241  function closeDrawer() {
242    if (!aside) return;
243    aside.classList.remove('is-open');
244    document.body.classList.remove('body-no-scroll');
245    if (overlay) overlay.hidden = true;
246    if (toggle) toggle.setAttribute('aria-expanded', 'false');
247    aside.setAttribute('aria-hidden', 'true');
248  }
249
250  toggle?.addEventListener('click', openDrawer);
251  closeBtn?.addEventListener('click', closeDrawer);
252  overlay?.addEventListener('click', closeDrawer);
253
254  // Close on ESC
255  document.addEventListener('keydown', (e) => {
256    if (e.key === 'Escape' && aside?.classList.contains('is-open')) {
257      closeDrawer();
258    }
259  });
260
261  // Close drawer after user applies filters
262  document.addEventListener('click', (e) => {
263    if (e.target.closest('.facetwp-submit')) {
264      closeDrawer();
265    }
266  });
267
268  // Optional: close after FacetWP finishes refreshing
269  document.addEventListener('facetwp-loaded', () => {
270    if (aside?.classList.contains('is-open')) closeDrawer();
271  });
272})();
273
274// list view toggle
275(function () {
276  const STORAGE_KEY = 'wdj_view_mode'; // 'card' or 'list'
277  const resultsEl = document.querySelector('.dogfood-results');
278  const gridBtn   = document.querySelector('.view-btn[data-view="card"]');
279  const listBtn   = document.querySelector('.view-btn[data-view="list"]');
280
281  if (!resultsEl || !gridBtn || !listBtn) return;
282
283  function apply(mode) {
284    // classes
285    resultsEl.classList.toggle('card-layout', mode === 'card');
286    resultsEl.classList.toggle('list-layout', mode === 'list');
287    // aria/active
288    gridBtn.classList.toggle('is-active', mode === 'card');
289    gridBtn.setAttribute('aria-pressed', mode === 'card' ? 'true' : 'false');
290    listBtn.classList.toggle('is-active', mode === 'list');
291    listBtn.setAttribute('aria-pressed', mode === 'list' ? 'true' : 'false');
292  }
293
294  function setMode(mode) {
295    localStorage.setItem(STORAGE_KEY, mode);
296    apply(mode);
297  }
298
299  // initial
300  const saved = localStorage.getItem(STORAGE_KEY) || 'card';
301  apply(saved);
302
303  // clicks
304  gridBtn.addEventListener('click', () => setMode('card'));
305  listBtn.addEventListener('click', () => setMode('list'));
306
307  // Re-apply after FacetWP refresh
308  document.addEventListener('facetwp-loaded', () => apply(localStorage.getItem(STORAGE_KEY) || 'card'));
309})();
310
311document.addEventListener("DOMContentLoaded", function() {
312    const url = new URL(window.location.href);
313    const term = url.searchParams.get('_dog_food_search');
314
315    if (term) {
316        window.dataLayer = window.dataLayer || [];
317        dataLayer.push({
318            event: "dog_food_search",
319            search_term: term
320        });
321    }
322});
323
324//Free JS Code Addition
325(function () {
326  function qs(sel, ctx) { return (ctx || document).querySelector(sel); }
327
328  var gate = qs('#df-results-gate');            // cards-area blur overlay
329  var panelWrap = qs('#df-results-gate-panel'); // viewport CTA overlay
330  if (!gate || !panelWrap) return;
331
332  var COOKIE_NAME = 'wdj_df_free';
333  var FORM_ID = 14;
334
335  function getCookie(name) {
336    var parts = ('; ' + document.cookie).split('; ' + name + '=');
337    if (parts.length === 2) return parts.pop().split(';').shift();
338    return '';
339  }
340
341  function hasFreeCookie() {
342    return !!getCookie(COOKIE_NAME);
343  }
344
345  function isLoggedIn() {
346    // WP typically adds body.logged-in. This works for subscribers and any logged-in user.
347    return document.body.classList.contains('member-logged-in');
348  }
349
350  function currentPaged() {
351    var url = new URL(window.location.href);
352    var p = parseInt(url.searchParams.get('_paged') || '1', 10);
353    return isNaN(p) ? 1 : Math.max(1, p);
354  }
355
356  function isUnknownGuest() {
357    // Free cookie OR logged in => allow pagination
358    if (hasFreeCookie()) return false;
359    if (isLoggedIn()) return false;
360    return true;
361  }
362
363  function setVisible(visible) {
364    if (visible) {
365      gate.classList.add('is-visible');
366      panelWrap.classList.add('is-visible');
367      gate.setAttribute('aria-hidden', 'false');
368      panelWrap.setAttribute('aria-hidden', 'false');
369    } else {
370      gate.classList.remove('is-visible');
371      panelWrap.classList.remove('is-visible');
372      gate.setAttribute('aria-hidden', 'true');
373      panelWrap.setAttribute('aria-hidden', 'true');
374    }
375  }
376
377  function setHard(hard) {
378    gate.classList.toggle('is-hard', !!hard);
379    panelWrap.classList.toggle('is-hard', !!hard);
380
381    // Close button hidden in hard mode
382    var closeBtn = qs('.df-results-gate__close', panelWrap);
383    if (closeBtn) closeBtn.style.display = hard ? 'none' : '';
384  }
385
386  function showGate(hard) {
387    setVisible(true);
388    setHard(!!hard);
389  }
390
391  function hideGate(force) {
392    // force=true closes even if hard (used after cookie appears)
393    if (!force && panelWrap.classList.contains('is-hard')) return;
394    setVisible(false);
395    setHard(false);
396  }
397
398  function hideIfJustRegistered() {
399    // If GF shows confirmation and cookie exists, do not keep overlay open
400    var gfConfirm = qs('#gform_confirmation_wrapper_' + FORM_ID);
401    if (gfConfirm && hasFreeCookie()) {
402      hideGate(true);
403    }
404  }
405
406  // Close button (soft overlay only)
407  document.addEventListener('click', function (e) {
408    var btn = e.target && e.target.closest ? e.target.closest('.df-results-gate__close') : null;
409    if (!btn) return;
410
411    e.preventDefault();
412    hideGate(false);
413    try { sessionStorage.setItem('df_gate_closed', '1'); } catch (err) {}
414  });
415
416  // Intercept FacetWP pager clicks on page 1 for unknown guests
417  document.addEventListener('click', function (e) {
418    var a = e.target && e.target.closest ? e.target.closest('.facetwp-pager a') : null;
419    if (!a) return;
420
421    if (!isUnknownGuest()) return;
422
423    // Only block attempts to leave page 1
424    if (currentPaged() === 1) {
425      e.preventDefault();
426      e.stopPropagation();
427      showGate(false);
428    }
429  }, true);
430
431  document.addEventListener('click', function (e) {
432    var wrap = e.target && e.target.closest ? e.target.closest('.wdj-open-register') : null;
433    if (!wrap) return;
434
435    var link = e.target && e.target.closest ? e.target.closest('a') : null;
436    if (!link) return;
437
438    if (!isUnknownGuest()) return;
439
440    e.preventDefault();
441    e.stopPropagation();
442
443    showGate(false);
444  }, true);
445
446  // Halfway scroll trigger on page 1 (unknown guests only), once per session
447  var scrollTriggered = false;
448
449  function onScroll() {
450    if (scrollTriggered) return;
451    if (!isUnknownGuest()) return;
452    if (currentPaged() !== 1) return;
453
454    try {
455      if (sessionStorage.getItem('df_gate_closed') === '1') return;
456      if (sessionStorage.getItem('df_gate_shown') === '1') return;
457    } catch (err) {}
458
459    var doc = document.documentElement;
460    var scrollTop = window.pageYOffset || doc.scrollTop || 0;
461    var max = (doc.scrollHeight - window.innerHeight);
462    if (max <= 0) return;
463
464    if ((scrollTop / max) >= 0.5) {
465      scrollTriggered = true;
466      showGate(false);
467      try { sessionStorage.setItem('df_gate_shown', '1'); } catch (err) {}
468    }
469  }
470
471  window.addEventListener('scroll', onScroll, { passive: true });
472
473  // Core enforcement: run on load, facet refresh, and back/forward navigation
474  function enforce() {
475    // If they have the cookie or are logged in, overlays should be gone no matter what
476    if (!isUnknownGuest()) {
477      hideGate(true);
478      return;
479    }
480
481    // Unknown guests: hard gate any page 2+
482    if (currentPaged() >= 2) {
483      showGate(true);
484      return;
485    }
486
487    // Unknown guests on page 1: default hidden until scroll/click triggers
488    // (Do not auto-show)
489    hideIfJustRegistered();
490  }
491
492  // FacetWP AJAX refresh hook
493  document.addEventListener('facetwp-loaded', function () {
494    enforce();
495    hideIfJustRegistered();
496  });
497
498  // Back/forward changes
499  window.addEventListener('popstate', function () {
500    enforce();
501  });
502
503  // If cookie appears right after redirect/submit, hide overlay quickly
504  (function cookieWatch() {
505    var attempts = 0;
506    var t = setInterval(function () {
507      attempts++;
508      if (hasFreeCookie() || isLoggedIn()) {
509        hideGate(true);
510        clearInterval(t);
511      }
512      if (attempts >= 10) clearInterval(t);
513    }, 300);
514  })();
515
516  // Initial run
517  enforce();
518  hideIfJustRegistered();
519
520})();

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.