PageSourceSearch

https://faddom.com/wp-content/plugins/faddom-plugin/assets/js/faddom-tabs-deeplink.js?ver=1.0.0

js faddom.com collected 2026-10-02 06:25:50 UTC 4,354 bytes, 176 lines download raw bytes

1/**
2 * Deep-link Elementor tabs: scroll to a section and open a specific tab.
3 *
4 * Elementor setup:
5 *   1. Tabs section → Advanced → CSS ID, e.g. usecases
6 *   2. Box link → #usecases-tab-2  (1-based tab index)
7 *
8 * Also supported:
9 *   #usecases + custom attribute data-faddom-tab="2"
10 *   #e-n-tab-title-… (copied tab button id)
11 */
12(function () {
13	'use strict';
14
15	var TAB_HASH = /^#([A-Za-z][\w-]*)-tab-(\d+)$/;
16
17	function isEditor() {
18		return (
19			document.documentElement.classList.contains('faddom-el-edit') ||
20			(document.body && document.body.classList.contains('faddom-el-edit'))
21		);
22	}
23
24	function headerOffset() {
25		return window.matchMedia('(max-width: 1024px)').matches ? 51 : 76;
26	}
27
28	function toUrl(href) {
29		try {
30			return new URL(href, window.location.href);
31		} catch (e) {
32			return null;
33		}
34	}
35
36	function isSamePage(url) {
37		var here = window.location.pathname.replace(/\/+$/, '') || '/';
38		var there = url.pathname.replace(/\/+$/, '') || '/';
39		return here === there;
40	}
41
42	function activateTab(el) {
43		if (!el) {
44			return;
45		}
46		el.click();
47	}
48
49	function tabsIn(scope) {
50		var nested = scope.querySelectorAll('.e-n-tab-title');
51		if (nested.length) {
52			return nested;
53		}
54		return scope.querySelectorAll('.elementor-tab-title');
55	}
56
57	function scrollToEl(el) {
58		if (!el) {
59			return;
60		}
61		var top = el.getBoundingClientRect().top + window.pageYOffset - headerOffset() - 8;
62		window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
63	}
64
65	function resolve(href, trigger) {
66		var url = toUrl(href);
67		if (!url || !isSamePage(url)) {
68			return null;
69		}
70
71		var hash = url.hash || '';
72		var tabIndex = 0;
73		var section = null;
74		var tabEl = null;
75
76		if (trigger) {
77			tabIndex = parseInt(trigger.getAttribute('data-faddom-tab'), 10) || 0;
78		}
79		if (!tabIndex) {
80			tabIndex = parseInt(url.searchParams.get('tab'), 10) || 0;
81		}
82
83		var named = hash.match(TAB_HASH);
84		if (named) {
85			section = document.getElementById(named[1]);
86			tabIndex = parseInt(named[2], 10) || tabIndex;
87		} else if (hash.length > 1) {
88			var byId = document.getElementById(hash.slice(1));
89			if (byId && (byId.classList.contains('e-n-tab-title') || byId.classList.contains('elementor-tab-title'))) {
90				tabEl = byId;
91				section = byId.closest('.elementor-widget-n-tabs, .elementor-widget-tabs, .elementor-element') || byId;
92			} else if (byId && tabIndex) {
93				section = byId;
94			}
95		}
96
97		if (!tabEl && section && tabIndex > 0) {
98			var titles = tabsIn(section);
99			tabEl = titles[tabIndex - 1] || null;
100		}
101
102		if (!tabEl && !section) {
103			return null;
104		}
105
106		return { section: section, tabEl: tabEl };
107	}
108
109	function openTarget(target) {
110		if (!target) {
111			return false;
112		}
113		activateTab(target.tabEl);
114		window.requestAnimationFrame(function () {
115			scrollToEl(target.section || target.tabEl);
116		});
117		return true;
118	}
119
120	function openFromUrl(href, trigger) {
121		return openTarget(resolve(href, trigger));
122	}
123
124	function init() {
125		if (isEditor()) {
126			return;
127		}
128
129		document.addEventListener(
130			'click',
131			function (event) {
132				var link = event.target.closest('a[href]');
133				if (!link || event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
134					return;
135				}
136				var target = resolve(link.getAttribute('href'), link);
137				if (!target || !target.tabEl) {
138					return;
139				}
140				event.preventDefault();
141				openTarget(target);
142			},
143			true
144		);
145
146		window.addEventListener('hashchange', function () {
147			openFromUrl(window.location.href, null);
148		});
149
150		var openedFromLoad = false;
151		function fromLoad() {
152			var target = resolve(window.location.href, null);
153			if (!target || !target.tabEl) {
154				return;
155			}
156			var alreadyOn = target.tabEl.getAttribute('aria-selected') === 'true';
157			if (alreadyOn && openedFromLoad) {
158				return;
159			}
160			openedFromLoad = true;
161			openTarget(target);
162		}
163
164		fromLoad();
165		window.addEventListener('load', fromLoad);
166		window.addEventListener('elementor/frontend/init', function () {
167			window.setTimeout(fromLoad, 50);
168		});
169	}
170
171	if (document.readyState === 'loading') {
172		document.addEventListener('DOMContentLoaded', init);
173	} else {
174		init();
175	}
176})();

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.