PageSourceSearch

https://republiek.org/wp-content/plugins/wings-anchors/assets/js/frontend.js?ver=1.0.0

js republiek.org collected 2026-09-25 06:59:04 UTC 5,375 bytes, 218 lines download raw bytes

1/**
2 * Wings Universal Anchor System - Frontend JavaScript
3 */
4
5(function ($) {
6	'use strict';
7
8	// Initialize when DOM is ready
9	$(document).ready(function () {
10		WingsAnchors.init();
11	});
12
13	var WingsAnchors = {
14		init: function () {
15			this.bindEvents();
16			this.handleInitialAnchor();
17			this.setupHoverEffects();
18		},
19
20		bindEvents: function () {
21			// Copy link functionality
22			$(document).on('click', '.wings-anchor-copy', function (e) {
23				e.preventDefault();
24				WingsAnchors.copyAnchorLink($(this));
25			});
26
27			// Smooth scroll for anchor links
28			$(document).on('click', 'a[href^="#"]', function (e) {
29				const href = $(this).attr('href');
30				if (href !== '#' && href.length > 1) {
31					e.preventDefault();
32					WingsAnchors.smoothScrollTo(href);
33				}
34			});
35
36			// Handle hash changes
37			$(window).on('hashchange', function () {
38				WingsAnchors.handleHashChange();
39			});
40		},
41
42		setupHoverEffects: function () {
43			// Add hover effects for better UX
44			$(document).on(
45				'mouseenter',
46				'h1, h2, h3, h4, h5, h6, .wings-cta, .wings-faq, .wings-image, .wings-blockquote, .wings-callout, .wings-single-node, .wings-file-download, .wings-nodes-grid, .wings-people-organizations-grid',
47				function () {
48					const $element = $(this);
49					const $anchorButton = $element.find('.wings-anchor-copy');
50
51					if ($anchorButton.length) {
52						$anchorButton.addClass('hover-active');
53					}
54				}
55			);
56
57			$(document).on(
58				'mouseleave',
59				'h1, h2, h3, h4, h5, h6, .wings-cta, .wings-faq, .wings-image, .wings-blockquote, .wings-callout, .wings-single-node, .wings-file-download, .wings-nodes-grid, .wings-people-organizations-grid',
60				function () {
61					const $element = $(this);
62					const $anchorButton = $element.find('.wings-anchor-copy');
63
64					if ($anchorButton.length && !$anchorButton.hasClass('copied')) {
65						$anchorButton.removeClass('hover-active');
66					}
67				}
68			);
69		},
70
71		copyAnchorLink: function ($button) {
72			const anchor = $button.data('anchor');
73			const url = window.location.href.split('#')[0] + '#' + anchor;
74
75			// Use modern clipboard API if available
76			if (navigator.clipboard && window.isSecureContext) {
77				navigator.clipboard
78					.writeText(url)
79					.then(function () {
80						WingsAnchors.showCopyFeedback($button);
81					})
82					.catch(function () {
83						WingsAnchors.fallbackCopy(url, $button);
84					});
85			} else {
86				WingsAnchors.fallbackCopy(url, $button);
87			}
88		},
89
90		fallbackCopy: function (text, $button) {
91			// Fallback for older browsers
92			const textArea = document.createElement('textarea');
93			textArea.value = text;
94			textArea.style.position = 'fixed';
95			textArea.style.left = '-999999px';
96			textArea.style.top = '-999999px';
97			document.body.appendChild(textArea);
98			textArea.focus();
99			textArea.select();
100
101			try {
102				document.execCommand('copy');
103				WingsAnchors.showCopyFeedback($button);
104			} catch (err) {
105				console.error('Failed to copy: ', err);
106			}
107
108			document.body.removeChild(textArea);
109		},
110
111		showCopyFeedback: function ($button) {
112			const originalText = $button.text();
113			const originalClass = $button.attr('class');
114
115			$button.text('✓').addClass('copied');
116
117			setTimeout(function () {
118				$button.text(originalText).removeClass('copied');
119			}, 2000);
120		},
121
122		smoothScrollTo: function (target) {
123			const $target = $(target);
124			if ($target.length) {
125				const offset = $target.offset().top - 80; // Account for fixed header
126
127				$('html, body').animate(
128					{
129						scrollTop: offset,
130					},
131					800,
132					'easeInOutQuart'
133				);
134
135				// Update URL without triggering hashchange
136				if (history.pushState) {
137					history.pushState(null, null, target);
138				}
139			}
140		},
141
142		handleInitialAnchor: function () {
143			if (window.location.hash) {
144				// Small delay to ensure DOM is fully loaded
145				setTimeout(function () {
146					WingsAnchors.smoothScrollTo(window.location.hash);
147				}, 100);
148			}
149		},
150
151		handleHashChange: function () {
152			WingsAnchors.smoothScrollTo(window.location.hash);
153		},
154
155		// Generate table of contents
156		generateTOC: function ($container) {
157			const $headings = $container.find('h1, h2, h3, h4, h5, h6');
158			const toc = [];
159
160			$headings.each(function () {
161				const $heading = $(this);
162				const level = parseInt($heading.prop('tagName').substr(1));
163				const text = $heading.text();
164				const id = $heading.attr('id');
165
166				if (id) {
167					toc.push({
168						level: level,
169						text: text,
170						id: id,
171					});
172				}
173			});
174
175			return toc;
176		},
177
178		renderTOC: function (toc) {
179			if (toc.length === 0) {
180				return '<p>No headings found.</p>';
181			}
182
183			let html = '<ul class="wings-toc-list">';
184			let currentLevel = 0;
185
186			toc.forEach(function (item) {
187				// Close previous levels
188				while (currentLevel > item.level) {
189					html += '</ul>';
190					currentLevel--;
191				}
192
193				// Open new levels
194				while (currentLevel < item.level) {
195					html += '<ul>';
196					currentLevel++;
197				}
198
199				html += '<li><a href="#' + item.id + '">' + item.text + '</a></li>';
200			});
201
202			// Close remaining levels
203			while (currentLevel > 0) {
204				html += '</ul>';
205				currentLevel--;
206			}
207
208			html += '</ul>';
209			return html;
210		},
211	};
212
213	// Add easing function for smooth scrolling
214	$.easing.easeInOutQuart = function (x, t, b, c, d) {
215		if ((t /= d / 2) < 1) return (c / 2) * t * t * t * t + b;
216		return (-c / 2) * ((t -= 2) * t * t * t - 2) + b;
217	};
218})(jQuery);

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.