PageSourceSearch

https://www.yorknational.com/js/guest_prefrence_log.js?v=1787758387

js yorknational.com collected 2026-09-27 10:05:19 UTC 8,578 bytes, 324 lines download raw bytes

1(function () {
2  // globals
3  let isNavigationOrRefreshEvent = false;
4  let isOnline = true;
5  let idle = initIdleTimer({ idle: 1800000 }); // 30m idle timeout
6  const HEARTBEAT_TIME_MIN = 5;
7
8  idle.start();
9
10  // events
11  window.addEventListener(
12    "pageshow",
13    function () {
14      handleActiveNow({ name: "pageshow", active: 1 });
15    },
16    { capture: true }
17  );
18
19  window.addEventListener(
20    "pagehide",
21    function () {
22      handleActiveNow({ name: "pagehide", active: 0 });
23    },
24    { capture: true }
25  );
26
27  document.addEventListener(
28    "visibilitychange",
29    function () {
30      handleActiveNow({
31        name: "visibility",
32        active: document.visibilityState === "visible" ? 1 : 0,
33      });
34    },
35    { capture: true }
36  );
37
38  document.addEventListener(
39    "focus",
40    function () {
41      handleActiveNow({ name: "focus", active: 1 });
42    },
43    { capture: true }
44  );
45
46  const debouncedScrollEvent = debouncedHandleActiveNow({ name: "scroll", active: 1 }, { delay: 1000 });
47  document.addEventListener(
48    "scroll",
49    function () {
50      debouncedScrollEvent();
51    },
52    { capture: true }
53  );
54
55  const debouncedMouseupEvent = debouncedHandleActiveNow({ name: "mouseup", active: 1 }, { delay: 1000 });
56  document.addEventListener(
57    "mouseup",
58    function (mouseEvent) {
59      debouncedMouseupEvent();
60      handleIsNavigationEvent(mouseEvent);
61    },
62    { capture: true }
63  );
64
65  const debouncedKeyupEvent = debouncedHandleActiveNow({ name: "touchend", active: 1 }, { delay: 1000 });
66  document.addEventListener(
67    "touchend",
68    function (touchendEvent) {
69      debouncedKeyupEvent();
70      handleIsNavigationEvent(touchendEvent);
71    },
72    { capture: true }
73  );
74
75  window.addEventListener(
76    "online",
77    function () {
78      isOnline = true;
79    },
80    { capture: true }
81  );
82
83  window.addEventListener(
84    "online",
85    function () {
86      isOnline = false;
87    },
88    { capture: true }
89  );
90
91  // FUNCTIONS/API
92
93  /**
94   * Saves guest preference in our system
95   *
96   * @param {string} name Name of the event that triggered status change
97   * @param {number} status  Status to change lead status to
98   * @param {FormData} data  Data to send enapoint to
99   * @returns
100   */
101  function saveGuestPreference(name, data) {
102    // Activity belongs to a lead. Anonymous visitors and crawlers without a
103    // current or historical lead cookie must not call the Drupal endpoint.
104    if (!getCookie("lead_id") && !getCookie("lead_ids")) {
105      return;
106    }
107
108    const lastEvent = getPreviousEvent();
109    const currentEvent = {
110      type: name,
111      activeNow: Number(data.get("active_now")),
112    };
113
114    // if event has not changed do not send
115    if (!isValidEvent(lastEvent, currentEvent)) {
116      isNavigationOrRefreshEvent = false;
117      localStorage.setItem("_iwm_gp_last_event_type_denied", currentEvent.type);
118      return;
119    } else {
120      localStorage.removeItem("_iwm_gp_last_event_type_denied");
121    }
122
123    // sendBeacon is a more reliable way of sending HTTP request than ajax on page close.
124    navigator.sendBeacon("/active_now/save", data);
125    localStorage.setItem("_iwm_gp_last_event_time", Date.now());
126    localStorage.setItem("_iwm_gp_last_event_type", currentEvent.type);
127    localStorage.setItem("_iwm_gp_last_active_now", currentEvent.activeNow);
128
129    if (currentEvent.type !== "idle") {
130      idle.reset();
131    }
132  }
133
134  /**
135   * Read a cookie value by its exact name.
136   *
137   * @param {string} name
138   * @returns {string}
139   */
140  function getCookie(name) {
141    const prefix = name + "=";
142    const cookies = document.cookie ? document.cookie.split(";") : [];
143
144    for (let index = 0; index < cookies.length; index += 1) {
145      const cookie = cookies[index].trim();
146      if (cookie.indexOf(prefix) === 0) {
147        return cookie.substring(prefix.length);
148      }
149    }
150
151    return "";
152  }
153
154  /**
155   * Retrieve the last event that triggered a lead status change
156   *
157   */
158  function getPreviousEvent() {
159    const previousEvtTypeDenied = localStorage.getItem("_iwm_gp_last_event_type_denied");
160    const previousEvtTime = localStorage.getItem("_iwm_gp_last_event_time");
161    const previousEvtType = localStorage.getItem("_iwm_gp_last_event_type");
162    const previousEvtActiveNow = localStorage.getItem("_iwm_gp_last_active_now");
163    const previousEvtObject = {
164      timeElapsed: 0,
165      type: previousEvtType,
166      eventTime: previousEvtTime,
167      activeNow: previousEvtActiveNow,
168      denied: previousEvtTypeDenied || "",
169    };
170
171    if (!previousEvtTime) {
172      return false;
173    }
174
175    // calculate time since last event
176    previousEvtObject.timeElapsed = Math.abs(Math.round((Date.now() - previousEvtTime) / 60000));
177
178    return previousEvtObject;
179  }
180
181  /**
182   * Handle lead active status toggle
183   *
184   * @param {object} event
185   * @param {string} event.name The event that triggered the toggle
186   * @param {number} event.active Wether user is active or not. 0 and 1 for inactive and active respectively.
187   * @returns
188   */
189  function handleActiveNow(event) {
190    const name = event.name;
191    const status = event.active;
192    // data sent needs to be form data type for endpoint to ingest
193    const data = new FormData();
194    data.append("active_now", status);
195    saveGuestPreference(name, data);
196  }
197
198  /**
199   * Debounced handle active now
200   * 
201   * @param {*} event 
202   * @param {*} options 
203   * @returns 
204   */
205  function debouncedHandleActiveNow(event, options) {
206    return debounce(function () {
207      handleActiveNow(event);
208    }, options);
209  }
210
211  /**
212   * General debounce fn impl.
213   * 
214   * @param {*} fn 
215   * @param {*} options 
216   * @returns 
217   */
218  function debounce(fn, options = {}) {
219    const delay = options.delay || 500;
220    let timeout;
221    return function (...args) {
222      clearTimeout(timeout);
223      timeout = setTimeout(() => {
224        fn(...args);
225      }, delay);
226    };
227  }
228
229  /**
230   * Determiness if an event is valid and should be sent
231   * 
232   * @param {*} previousEvent 
233   * @param {*} currentEvent 
234   * @returns 
235   */
236  function isValidEvent(previousEvent, currentEvent) {
237    // first load allow event right away
238    if (!previousEvent) {
239      return true;
240    }
241
242    // dont send events when offline
243    if (!isOnline) {
244      return false;
245    }
246
247    // dont send events on page navigation or reload
248    if (!currentEvent.activeNow && isNavigationOrRefreshEvent) {
249      return false;
250    }
251
252    // if previous event was pagehide and it was denied, then deny next visibility hidden
253    // some browsers do both pagehide and visibility on navigation
254    if (
255      !currentEvent.activeNow &&
256      currentEvent.type === "visibility" &&
257      previousEvent.denied &&
258      previousEvent.denied === "pagehide"
259    ) {
260      return false;
261    }
262
263    if (previousEvent.activeNow != currentEvent.activeNow) {
264      // on event change allow event
265      return true;
266    }
267
268    // only allow active events within heartbeat timeframe
269    if (previousEvent.timeElapsed < HEARTBEAT_TIME_MIN) {
270      return false;
271    }
272
273    return true;
274  }
275
276  /**
277   *  Handles checking if refresh is due to navigation. Refresh is considered navigation if a link tag was clicked.
278   *
279   * @param {MouseEvent | TouchEvent} event
280   */
281  function handleIsNavigationEvent(event) {
282    // only process user initiated events
283    if (!event.isTrusted) {
284      return;
285    }
286
287    if (!event.target) {
288      return;
289    }
290
291    if (!event.target.nodeName) {
292      return;
293    }
294
295    const nodeName = event.target.nodeName;
296    const href = event.target.href ? event.target.href : "";
297    if (nodeName === "A" && href.length && href != "#") {
298      isNavigationOrRefreshEvent = true;
299    }
300  }
301
302  // idle timer
303  function initIdleTimer(options = {}) {
304    const delay = options.idle || 1800000;
305    var idleTimer;
306
307    return {
308      start: function () {
309        idleTimer = setTimeout(() => {
310          handleActiveNow({ name: "idle", active: 0 });
311        }, delay);
312      },
313      reset: function () {
314        clearTimeout(idleTimer);
315        idleTimer = setTimeout(() => {
316          handleActiveNow({ name: "idle", active: 0 });
317        }, delay);
318      },
319      stop: function () {
320        clearTimeout(idleTimer);
321      },
322    };
323  }
324})();

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.