PageSourceSearch

https://gg.ca/modules/custom/osgg_custom_overrides/js/quicktabs_deep_link.js?tm0uop

js gg.ca collected 2026-10-01 08:52:49 UTC 4,344 bytes, 164 lines download raw bytes

1(function (Drupal, once) {
2  'use strict';
3
4  Drupal.behaviors.osggQuicktabsDeepLink = {
5    attach(context) {
6      const url = new URL(window.location.href);
7
8      once('osgg-quicktabs-deep-link', 'div.quicktabs-wrapper[id^="quicktabs-"]', context).forEach((wrapper) => {
9        const instanceId = getInstanceId(wrapper);
10        const tabs = getTabs(wrapper);
11        if (!instanceId || !tabs.length) {
12          return;
13        }
14
15        const targetIndex = getTargetIndex(url, instanceId, tabs);
16        if (targetIndex === null || !tabs[targetIndex]) {
17          return;
18        }
19
20        setActiveTabCookie(instanceId, targetIndex);
21
22        // Defer the click until other behaviors have finished attaching.
23        window.setTimeout(() => {
24          if (wrapper.isConnected) {
25            tabs[targetIndex].click();
26          }
27        }, 0);
28      });
29    }
30  };
31
32  function getInstanceId(wrapper) {
33    return wrapper.id.startsWith('quicktabs-') ? wrapper.id.substring('quicktabs-'.length) : '';
34  }
35
36  function getTabs(wrapper) {
37    return Array.from(wrapper.querySelectorAll('ul.quicktabs-tabs li a'));
38  }
39
40  function getTargetIndex(url, instanceId, tabs) {
41    const explicitValue = getExplicitValue(url, instanceId);
42    if (explicitValue !== null) {
43      return resolveExplicitValue(explicitValue, tabs);
44    }
45
46    const legacyValue = getLegacyValue(url, instanceId);
47    if (legacyValue !== null) {
48      return resolveLegacyValue(legacyValue, tabs);
49    }
50
51    return getHashIndex(url.hash, instanceId, tabs);
52  }
53
54  function getExplicitValue(url, instanceId) {
55    const candidates = [
56      `qt-${instanceId}`,
57      `qt_${instanceId}`
58    ];
59
60    for (const candidate of candidates) {
61      const value = url.searchParams.get(candidate);
62      if (value !== null && value !== '') {
63        return value;
64      }
65    }
66
67    return null;
68  }
69
70  function getLegacyValue(url, instanceId) {
71    const normalizedInstanceId = normalizeKey(instanceId);
72
73    for (const [key, value] of url.searchParams.entries()) {
74      if (!value || !/^qt[-_]/i.test(key)) {
75        continue;
76      }
77
78      const normalizedKey = normalizeKey(key);
79      if (normalizedKey.includes(normalizedInstanceId)) {
80        return value;
81      }
82    }
83
84    return null;
85  }
86
87  function resolveExplicitValue(value, tabs) {
88    const trimmedValue = value.trim();
89    if (/^\d+$/.test(trimmedValue)) {
90      const numericValue = parseInt(trimmedValue, 10);
91      const index = numericValue === 0 ? 0 : numericValue - 1;
92      return index >= 0 && index < tabs.length ? index : null;
93    }
94
95    return resolveSlug(trimmedValue, tabs);
96  }
97
98  function resolveLegacyValue(value, tabs) {
99    const trimmedValue = value.trim();
100    if (/^\d+$/.test(trimmedValue)) {
101      const index = parseInt(trimmedValue, 10);
102      return index >= 0 && index < tabs.length ? index : null;
103    }
104
105    return resolveSlug(trimmedValue, tabs);
106  }
107
108  function getHashIndex(hash, instanceId, tabs) {
109    const decodedHash = decodeURIComponent(hash.replace(/^#/, ''));
110    const patterns = [
111      new RegExp(`^quicktabs-tab(?:page)?-${escapeRegExp(instanceId)}-(\\d+)$`, 'i'),
112      new RegExp(`^qt[-_]${escapeRegExp(instanceId)}[-_](\\d+)$`, 'i')
113    ];
114
115    for (const pattern of patterns) {
116      const match = decodedHash.match(pattern);
117      if (!match) {
118        continue;
119      }
120
121      const index = parseInt(match[1], 10);
122      if (index >= 0 && index < tabs.length) {
123        return index;
124      }
125    }
126
127    return null;
128  }
129
130  function resolveSlug(value, tabs) {
131    const normalizedValue = slugify(value);
132    if (!normalizedValue) {
133      return null;
134    }
135
136    return tabs.findIndex((tab) => slugify(tab.textContent) === normalizedValue);
137  }
138
139  function setActiveTabCookie(instanceId, index) {
140    document.cookie = [
141      `Drupal-quicktabs-active-tab-id-${instanceId}=${index}`,
142      'path=/',
143      'SameSite=Lax'
144    ].join('; ');
145  }
146
147  function normalizeKey(value) {
148    return value.toLowerCase().replace(/[^a-z0-9]+/g, '_');
149  }
150
151  function slugify(value) {
152    return value
153      .normalize('NFD')
154      .replace(/[\u0300-\u036f]/g, '')
155      .toLowerCase()
156      .replace(/[^a-z0-9]+/g, '-')
157      .replace(/^-+|-+$/g, '');
158  }
159
160  function escapeRegExp(value) {
161    return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
162  }
163
164})(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.