PageSourceSearch

https://www.backroads.com/modules/custom/bkr_recently_viewed_trips/js/rvt_widget.js?v=1790789873

js backroads.com collected 2026-10-02 04:23:09 UTC 4,946 bytes, 169 lines download raw bytes

1/**
2 * Hydrates Recently Viewed Trips via a cacheable, cookie-less request.
3 * Only renders anything if there is at least one trip to show.
4 */
5(function (Drupal, once) {
6  "use strict";
7
8  const RVT_TITLE = "Recently Viewed Trips";
9  const INIT_RETRY_MS = 50;
10  const INIT_RETRY_MAX = 60;
11
12  function getSkeletonCardCount() {
13    return window.matchMedia("(min-width: 960px)").matches ? 3 : 1;
14  }
15
16  function getCookie(name) {
17    const parts = document.cookie.split("; ");
18    for (let i = 0; i < parts.length; i++) {
19      const [k, v] = parts[i].split("=");
20      if (k === name) return v || "";
21    }
22    return "";
23  }
24
25  function skeletonTripCardHtml() {
26    return `
27      <div class="skeleton-trip-card">
28        <div class="skeleton-trip-card__image"></div>
29        <div class="skeleton-trip-card__info-1"></div>
30        <div class="skeleton-trip-card__info-2"></div>
31        <div class="skeleton-trip-card__info-3"></div>
32        <div class="skeleton-trip-card__bottom-wrapper">
33          <div class="skeleton-trip-card__price"></div>
34          <div class="skeleton-trip-card__button"></div>
35        </div>
36      </div>
37    `;
38  }
39
40  function renderLoadingShell(el) {
41    const skeletons = Array.from({ length: getSkeletonCardCount() }, skeletonTripCardHtml).join("");
42    el.innerHTML = `
43      <section class="recently-viewed-trips" data-rvt-section="">
44        <hr />
45        <h2 class="recently-viewed-trips__title">${RVT_TITLE}</h2>
46        <div class="recently-viewed-trips__cards">
47          <div class="view view-id-recently_viewed_trips view-display-id-trip_cards">
48            <div class="view__content">
49              <div class="view--unformatted carousel-loading">${skeletons}</div>
50            </div>
51          </div>
52        </div>
53        <div class="recently-viewed-trips__modal-container"></div>
54      </section>
55    `;
56  }
57
58  function swiperReady() {
59    return typeof window.Swiper === "function";
60  }
61
62  function carouselInitialized() {
63    return !!document.querySelector(
64      ".recently-viewed-trips__cards .view--unformatted.swiper-initialized"
65    );
66  }
67
68  function revealCardsWithoutCarousel() {
69    document
70      .querySelectorAll(".recently-viewed-trips .trip-card__card")
71      .forEach((card) => {
72        card.style.opacity = "1";
73      });
74  }
75
76  function initRvtCarousel() {
77    const run = () => {
78      if (!Drupal.recently_viewed_trips || !swiperReady()) {
79        return false;
80      }
81      if (carouselInitialized()) {
82        return true;
83      }
84      try {
85        Drupal.recently_viewed_trips.init();
86        if (Drupal.behaviors.card_list && Drupal.behaviors.card_list.init) {
87          Drupal.behaviors.card_list.init();
88        }
89      } catch (e) {
90        return false;
91      }
92      return carouselInitialized();
93    };
94
95    if (run()) {
96      return;
97    }
98
99    let attempts = 0;
100    const retry = window.setInterval(() => {
101      if (run() || ++attempts >= INIT_RETRY_MAX) {
102        window.clearInterval(retry);
103        // Avoid a permanent blank block if Swiper never attaches.
104        if (!carouselInitialized()) {
105          revealCardsWithoutCarousel();
106        }
107      }
108    }, INIT_RETRY_MS);
109  }
110
111  function hydrateRvtSlots(context) {
112    const slots = once("bkrRvtWidget", "#rvt-slot", context);
113    slots.forEach((el) => {
114      const endpoint = el.getAttribute("data-rvt-endpoint");
115      if (!endpoint) return;
116
117      const cookieName =
118        el.getAttribute("data-rvt-cookie") || "recently_viewed_trips";
119      const rv = getCookie(cookieName) || "";
120
121      // If no cookie, remove the slot and bail (nothing to render).
122      if (!rv.trim()) {
123        el.remove();
124        return;
125      }
126
127      renderLoadingShell(el);
128
129      const url = endpoint + "?rv=" + encodeURIComponent(rv);
130
131      fetch(url, { credentials: "same-origin", cache: "no-store" })
132        .then((res) => (res.ok ? res.text() : ""))
133        .then((html) => {
134          // If empty response, remove the slot and bail.
135          if (!html || !html.trim()) {
136            el.remove();
137            return;
138          }
139
140          const cardsContainer = el.querySelector(".recently-viewed-trips__cards");
141          if (!cardsContainer) {
142            el.remove();
143            return;
144          }
145
146          cardsContainer.innerHTML = html;
147          initRvtCarousel();
148        })
149        .catch(() => {
150          // On error, remove the slot to avoid empty chrome.
151          el.remove();
152        });
153    });
154  }
155
156  Drupal.behaviors.bkrRvtWidget = {
157    attach(context) {
158      hydrateRvtSlots(context);
159    },
160  };
161
162  // Self-bootstrap: this file is loaded via a bare <script defer> (not a Drupal
163  // library) so it can register after core/footer attachBehaviors has already
164  // run — common after Azure auth return (?check_logged_in=1). once() keeps a
165  // later attach() call from double-hydrating.
166  if (document.querySelector("#rvt-slot")) {
167    hydrateRvtSlots(document);
168  }
169})(Drupal, once);

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.