PageSourceSearch

https://www.fdd-global.com/static/home/js/ibi-tracker/site-v2.js?v=2.0.0

js fdd-global.com collected 2026-09-24 20:22:41 UTC 11,107 bytes, 215 lines download raw bytes

1/* International site integration. SDK owns transport, identity, sessions and durations. */
2(function (window, document) {
3  'use strict';
4  var site = window.IbiSite = {};
5  var currentViewId, initialUrl, activeSearch, config, metadata, sourceViewId;
6  var memory = {};
7  function safely(fn) { try { return fn(); } catch (_) { return undefined; } }
8  function diagnostic(code) {
9    safely(function () { if (window.console) window.console.warn('[IBI]', code); });
10  }
11  function saved(key, value) {
12    key = 'ibi-site:' + (config && config.platformId || '') + ':' + key;
13    if (arguments.length > 1) { memory[key] = value; safely(function () { window.sessionStorage.setItem(key, value); }); return value; }
14    return safely(function () { return window.sessionStorage.getItem(key); }) || memory[key];
15  }
16  function id() {
17    return window.crypto && window.crypto.randomUUID ? window.crypto.randomUUID() :
18      Date.now().toString(36) + '-' + Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
19  }
20  function url(value) { return new URL(value, window.location.href); }
21  function fingerprint(value) {
22    var target = url(value);
23    ['search_id', 'page', 'pageNumber', 'pageSize', 'order', 'sort', 'ibi_source_page_view_id'].forEach(function (key) { target.searchParams.delete(key); });
24    target.searchParams.sort();
25    return target.pathname.replace(/\/page\/\d+(?:\.html)?\/?$/, '').replace(/(?:\.html|\/)$/, '') + '?' + target.searchParams.toString();
26  }
27  function searchContext(value, fresh) {
28    var target = url(value), key = fingerprint(value);
29    var searchId = fresh ? id() : target.searchParams.get('search_id') || saved('search:' + key) || id();
30    saved('search:' + key, searchId);
31    return { search_id: searchId, key: key, offset: 0 };
32  }
33  site.searchUrl = function (value, fresh) {
34    return safely(function () {
35      var target = url(value), context = searchContext(value, fresh);
36      target.searchParams.set('search_id', context.search_id);
37      return target.href;
38    }) || value;
39  };
40  site.prepareSearchRequest = function (params, fresh) {
41    return safely(function () {
42      var target = url('/part-search.html');
43      Object.keys(params).forEach(function (key) { target.searchParams.set(key, params[key]); });
44      var context = searchContext(target.href, fresh);
45      params.search_id = context.search_id;
46      return params;
47    }) || params;
48  };
49  site.flowUrl = function (value) {
50    safely(function () {
51      var target = url(value);
52      if (currentViewId && target.origin === window.location.origin) {
53        saved('source:' + target.href.split('#')[0], JSON.stringify({ id: currentViewId, from: window.location.href, at: Date.now() }));
54      }
55    });
56    return value;
57  };
58  site.track = function (name, properties, flush, options) {
59    safely(function () {
60      if (!window.tracker) return;
61      window.tracker.track(name, properties, options);
62      if (flush) Promise.resolve(window.tracker.flush()).catch(function () { });
63    });
64  };
65  site.success = function (response, source) {
66    safely(function () {
67      // Bonded registration has its own string code, and is adapted explicitly below.
68      if (!response || response.code !== 200 || !response.ibi_tracking) return;
69      var payload = response.ibi_tracking, registration = payload.registration;
70      if (registration && registration.user_id && registration.registration_id && window.tracker) {
71        window.tracker.identify(String(registration.user_id));
72        site.track('sign_up_success', { registration_id: String(registration.registration_id) }, true);
73      }
74      (payload.rfqs || []).forEach(function (rfq) {
75        if (rfq.rfq_id) site.track('rfq_submit_success', Object.assign({}, rfq, source ? { source_page_view_id: source } : {}), true);
76      });
77      (payload.orders || []).forEach(function (order) {
78        if (order.order_id && Array.isArray(order.items) && order.items.length && order.items.every(function (item) { return item.product_id && item.category_id; })) {
79          site.track('order_created', Object.assign({}, order, source ? { source_page_view_id: source } : {}), true);
80        }
81      });
82    });
83  };
84  site.searchResults = function (result, context) {
85    return safely(function () {
86      if (!result || !Number.isInteger(result.result_count) || result.result_count < 0 || !result.query || !context) return;
87      activeSearch = context;
88      activeSearch.offset = Number(result.offset) || 0;
89      // Preserve whole Unicode characters within the collector's 1024-byte limit.
90      var query = '', bytes = 0;
91      for (var character of String(result.query)) {
92        var size = new TextEncoder().encode(character).length;
93        if (bytes + size > 1024) break;
94        query += character; bytes += size;
95      }
96      // Repeated responses use SDK business-key deduplication and its original event snapshot.
97      site.track('search_results', { search_id: context.search_id, query: query, result_count: result.result_count });
98    });
99  };
100  site.init = function (settings, data) {
101    config = settings;
102    metadata = data || { page: { page_type: 'other' } };
103    safely(function () {
104      // Canonicalise search context BEFORE installing automatic PV/history listeners.
105      if (metadata.page.page_type === 'search') {
106        activeSearch = searchContext(window.location.href, !url(window.location.href).searchParams.has('search_id'));
107        var target = url(window.location.href);
108        target.searchParams.set('search_id', activeSearch.search_id);
109        window.history.replaceState(window.history.state, '', target.href);
110      }
111    });
112    safely(function () {
113      var key = 'source:' + window.location.href.split('#')[0];
114      var source = JSON.parse(saved(key) || 'null');
115      saved(key, '');
116      if (source && Date.now() - source.at >= 0 && Date.now() - source.at < 1800000 &&
117        document.referrer && url(document.referrer).origin === window.location.origin &&
118        url(document.referrer).href.split('#')[0] === url(source.from).href.split('#')[0]) sourceViewId = source.id;
119    });
120    initialUrl = window.location.href;
121    safely(function () {
122      window.tracker = window.IbiTracker.init(Object.assign({
123        engagementHeartbeat: false
124      }, settings, {
125        autoPageview: true, autoPerformance: true,
126        resolvePage: function () {
127          var page = Object.assign({}, window.location.href === initialUrl ? metadata.page : { page_type: 'other' });
128          // search_id identifies an attempt, not a page asset.
129          if (!page.page_id && url(window.location.href).searchParams.has('search_id')) {
130            var target = url(window.location.href);
131            Array.from(target.searchParams.keys()).forEach(function (key) { if (key === 'search_id' || key.indexOf('utm_') === 0) target.searchParams.delete(key); });
132            if (!/^#(?:\/|!\/)/.test(target.hash)) target.hash = '';
133            page.page_id = target.href;
134          }
135          return page;
136        },
137        onDiagnostic: function (event) { diagnostic(event.code); }
138      }));
139      currentViewId = window.tracker.getInitialPageViewId();
140      var pageview = window.tracker.pageview;
141      window.tracker.pageview = function () { currentViewId = pageview.apply(this, arguments); return currentViewId; };
142      if (metadata.search) site.searchResults(metadata.search, activeSearch);
143    });
144    if (!window.tracker) diagnostic('sdk_unavailable');
145    // Compatibility only: old dashboards retain their original one-shot wall duration.
146    var entered = window.performance.now(), pageUrl = window.location.href, left = false;
147    function leave() {
148      if (left) return;
149      left = true;
150      site.track('$page_leave', { page_url: pageUrl, duration_ms: Math.round(window.performance.now() - entered) }, true);
151    }
152    window.addEventListener('pagehide', leave);
153    document.addEventListener('visibilitychange', function () { if (document.visibilityState === 'hidden') leave(); });
154  };
155  site.currentPageViewId = function () { return currentViewId; };
156  // Prefilter attaches ahead of per-page success handlers, including utils/formHelper wrappers.
157  if (window.jQuery) window.jQuery.ajaxPrefilter(function (options, original, xhr) {
158    var requestUrl = safely(function () { return url(options.url); });
159    if (!requestUrl || requestUrl.origin !== window.location.origin) return;
160    var isPartSearch = /(?:\/part-search\.html|\/part\/search(?:\.html)?)$/.test(requestUrl.pathname);
161    var context;
162    var requestSource = sourceViewId;
163    if (isPartSearch) {
164      safely(function () {
165        var params = new URLSearchParams(typeof options.data === 'string' ? options.data : options.data || {});
166        params.forEach(function (value, key) { requestUrl.searchParams.set(key, value); });
167        context = searchContext(requestUrl.href, false);
168      });
169    }
170    xhr.done(function (response) {
171      safely(function () {
172        if (response && response.ibi_tracking && /\/login\/bondedReg(?:\.html)?$/i.test(requestUrl.pathname)) {
173          site.success({ code: 200, ibi_tracking: response.ibi_tracking });
174        } else site.success(response, requestSource);
175        if (context && response && response.code === 200 && response.ibi_tracking && response.ibi_tracking.search) {
176          site.searchResults(response.ibi_tracking.search, context);
177        }
178      });
179    });
180  });
181  function onLinkClick(event) {
182    safely(function () {
183      var link = event.target.closest('a');
184      if (!link) return;
185      if (link.href && /(?:member[./](?:inquiry\/send|order\/sub)|page-quote)/.test(url(link.href).pathname)) site.flowUrl(link.href);
186      var resultId = link.getAttribute('data-ibi-result-id');
187      if (resultId && activeSearch) {
188        var rank = Number(link.getAttribute('data-ibi-rank')) + activeSearch.offset;
189        if (Number.isInteger(rank) && rank > 0) site.track('search_result_click', { search_id: activeSearch.search_id, result_id: resultId, rank: rank }, true);
190      }
191      if (activeSearch && link.href && !resultId) {
192        var target = url(link.href);
193        if (target.origin === window.location.origin && fingerprint(target.href) === activeSearch.key) {
194          target.searchParams.set('search_id', activeSearch.search_id);
195          link.href = target.href;
196        }
197      }
198    });
199  }
200  document.addEventListener('click', onLinkClick, true);
201  document.addEventListener('auxclick', function (event) { if (event.button === 1) onLinkClick(event); }, true);
202  document.addEventListener('DOMContentLoaded', function () {
203    safely(function () {
204      if (!activeSearch) return;
205      document.querySelectorAll('a[href]').forEach(function (link) {
206        if (link.hasAttribute('data-ibi-result-id')) return;
207        var target = url(link.href);
208        if (target.origin === window.location.origin && fingerprint(target.href) === activeSearch.key) {
209          target.searchParams.set('search_id', activeSearch.search_id);
210          link.href = target.href;
211        }
212      });
213    });
214  });
215})(window, document);

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.