PageSourceSearch

https://www.ltd.org/wp-content/themes/planeteriaweb/js/expand.js?ver=7.1

js ltd.org collected 2026-09-24 11:08:37 UTC 10,664 bytes, 324 lines download raw bytes

1/**
2 * jQuery Expanding Grid plugin.
3 *
4 * By Dan Boulet - https://danboulet.com
5 */
6(function ($, window, document) {
7
8// Enable strict mode
9"use strict";
10
11/**
12 * Return the last element in the current row of a grid layout.
13 */
14var getLastSiblingInRow = function (element) {
15	var candidate = element,
16			elementTop = element.offsetTop;
17
18	// Loop through the element’s next siblings and look for the first one which
19	// is positioned further down the page.
20	while (candidate.nextElementSibling !== null) {
21		if (candidate.nextElementSibling.offsetTop > elementTop) {
22			return candidate;
23		}
24		candidate = candidate.nextElementSibling;
25	}
26	return candidate;
27};
28
29/**
30 * Calculate the distance that we need to scroll the page to bring a
31 * section, defined as the area between the top and bottom, into view.
32 */
33var calculatePageScrollDistance = function (top, bottom) {
34	var windowScrollDistance = $(window).scrollTop(),
35			windowHeight = $(window).height(),
36			scrollDistanceToTop,
37			scrollDistanceToBottom;
38
39	// Scroll to the top of the section if the we are already scrolled past it.
40	if (windowScrollDistance >= top) {
41		return top - windowScrollDistance;
42	}
43	// Do nothing if there is enough space to show the section without having to scroll.
44	else if ((windowScrollDistance + windowHeight) >= bottom) {
45		return 0;
46	}
47	else {
48		// Find the maximum distance we can scroll without passing the top of the section.
49		scrollDistanceToTop = top - windowScrollDistance;
50		// Find the distance we need to scroll to reveal the entire section.
51		scrollDistanceToBottom = bottom - (windowScrollDistance + windowHeight);
52
53		return Math.min(scrollDistanceToTop, scrollDistanceToBottom);
54	}
55};
56
57/**
58 * Create the expanding preview grid.
59 */
60var expandingGrid = function (context, options) {
61	var defaults = {
62		animationDuration: 250,
63		linksSelector: '.links a',
64		expandingAreaSelector: '.expanding-container',
65		closeButtonMarkup: '<a href="#" class="close-button">Close</a>',
66		spacerMarkup: '<span class="spacer" aria-hidden="true"/>',
67		elementActiveClass: 'active',
68		elementExpandedClass: 'expanded',
69		onExpandBefore: false,
70		onExpandAfter: false
71	};
72
73	var settings = $.extend({}, defaults, options);
74
75	var isExpanded = false;
76	var activeLink = false;
77	var activeExpandedArea = false;
78	var activeExpandedAreaTop = false;
79	var activeExpandedAreaHeight = false;
80	var lastItemInActiveRow = false;
81	var activeRowChanged = false;
82	var checkExpandedAreaResize = false;
83	var $links = $(settings.linksSelector, context);
84	var $expandingAreas = $(settings.expandingAreaSelector, context);
85	var $closeButton = $(settings.closeButtonMarkup);
86	var $spacer = $(settings.spacerMarkup);
87	var $secondarySpacer = $spacer.clone();
88
89	/**
90	 * Scroll a section of the page into view, using animation.
91	 */
92	var scrollSectionIntoView = function (top, bottom, duration, callback) {
93		var animate;
94		var scroll = 0;
95		var distance = calculatePageScrollDistance(top, bottom);
96		var windowScrollDistance = $(window).scrollTop();
97		var timeLeft;
98
99		// Set default duration.
100		duration = (typeof duration === 'undefined') ? settings.animationDuration : duration;
101		timeLeft = duration;
102
103		var start = new Date().getTime();
104		var last = start;
105		var tick = function() {
106			timeLeft = Math.max(duration - (new Date() - start), 0);
107
108			var x = (timeLeft === 0 || distance === 0) ? 0 : ((new Date() - last) / timeLeft * distance);
109			var diff = (distance > 0 ? Math.min(x, distance) : Math.max(x, distance));
110			distance = distance - diff;
111			scroll += diff;
112			window.scrollTo(0, windowScrollDistance + scroll);
113
114			last = new Date().getTime();
115
116			if (last - start <= duration) {
117				animate = (window.requestAnimationFrame && requestAnimationFrame(tick)) || setTimeout(tick, 16);
118			}
119			else {
120				if (typeof callback === 'function') {
121					callback();
122				}
123			}
124		};
125
126		tick();
127	};
128
129	// Process the links.
130	$links.each(function () {
131		var $this = $(this);
132		var targetId = $this.attr('href').match(/#([^\?]+)/)[1];
133		var target = document.getElementById(targetId);
134
135		if (target) {
136			$this.click(function (event) {
137				var clickedLink = this;
138				var scrollTargetOffset;
139				var closeButtonAnimationDelay;
140
141				event.preventDefault();
142
143				// Is this link already expanded?
144				if (isExpanded && activeLink === clickedLink) {
145					// Close it.
146					$closeButton.click();
147				}
148				// Otherwise, expand it.
149				else {
150					$links.removeClass(settings.elementActiveClass).filter($this).addClass(settings.elementActiveClass).parent('li').each(function () {
151						var lastSibling = getLastSiblingInRow(this);
152						activeRowChanged = lastSibling !== lastItemInActiveRow;
153						if (activeRowChanged) {
154							lastItemInActiveRow = lastSibling;
155						}
156						// If we are changing rows, replace spacer with secondary spacer.
157						if (isExpanded && activeRowChanged) {
158							$secondarySpacer.height($spacer.height());
159							$spacer.height(0).replaceWith($secondarySpacer);
160						}
161						$(lastItemInActiveRow).after($spacer);
162					});
163					if (isExpanded && activeRowChanged) {
164						$secondarySpacer.animate({height: 0}, settings.animationDuration, function () {
165							$(this).detach();
166						});
167						$closeButton.removeClass(settings.elementActiveClass).hide();
168					}
169					scrollTargetOffset = ($secondarySpacer.position().top < $spacer.position().top ? $secondarySpacer.height() : 0);
170					activeExpandedAreaTop = ($spacer.position().top - scrollTargetOffset);
171					$expandingAreas.removeClass(settings.elementExpandedClass).hide().filter(target).each(function () {
172							var $this = $(this);
173							var autoHeight = $this.height();
174							var autoOuterHeight = $this.outerHeight();
175							var initialHeight = (isExpanded && activeExpandedAreaHeight && (activeRowChanged === false)) ? activeExpandedAreaHeight : 0;
176
177							stopExpandedAreaMonitor();
178
179							$spacer.animate({height: autoHeight + 'px'}, settings.animationDuration);
180
181							$this.css({
182								height: initialHeight + 'px',
183								position: 'absolute',
184								left: 0,
185								top: $spacer.position().top + 'px'
186							}).show(0, function () {
187								// Callback.
188								if (typeof settings.onExpandBefore === 'function') {
189									settings.onExpandBefore.call(this);
190								}
191							}).animate({
192								height: autoHeight + 'px',
193								top: activeExpandedAreaTop + 'px'
194							}, settings.animationDuration, function () {
195								$this.css({height: 'auto'}).addClass(settings.elementExpandedClass);
196
197								// Set a timer to monitor changes to expanded area’s height.
198								activeExpandedAreaHeight = $this.height();
199								checkExpandedAreaResize = setInterval(function () {
200									var activeExpandedAreaNewHeight = $this.height();
201									if (activeExpandedAreaNewHeight !== activeExpandedAreaHeight) {
202										activeExpandedAreaHeight = activeExpandedAreaNewHeight;
203										syncExpandedAreaWithSpacer();
204									}
205								}, 1000);
206
207								// Callback.
208								if (typeof settings.onExpandAfter === 'function') {
209									settings.onExpandAfter.call(this);
210								}
211							});
212
213							// Scroll the page to bring the active link and preview into view.
214							var scrollTargetTop = $(clickedLink).offset().top - scrollTargetOffset;
215							var scrollTargetBottom = $this.offset().top + autoOuterHeight + 20 - scrollTargetOffset;
216							scrollSectionIntoView(scrollTargetTop, scrollTargetBottom);
217						});
218
219					// Activate close button.
220					closeButtonAnimationDelay = (isExpanded && activeRowChanged && ($this.parent().index() > $(activeLink).parent().index())) ? settings.animationDuration : (settings.animationDuration / 4);
221					$closeButton.css({
222							position: 'absolute',
223							right: 0,
224							top: activeExpandedAreaTop + 'px'
225						}).delay(closeButtonAnimationDelay).fadeIn(settings.animationDuration, function () {
226							$(this).addClass(settings.elementActiveClass);
227						});
228
229					// Set global variables.
230					activeLink = this;
231					activeExpandedArea = target;
232					isExpanded = true;
233				}
234			});
235		}
236	});
237
238	// Process the close button.
239	$closeButton.appendTo(context).hide().click(function (event) {
240		var $activeLink = $(activeLink);
241		var activeLinkTopOffset = $activeLink.offset().top;
242		var activeLinkBottomOffset = activeLinkTopOffset + $activeLink.outerHeight();
243
244		event.preventDefault();
245
246		// DOM manipulation and animations.
247		$links.removeClass(settings.elementActiveClass);
248		$expandingAreas.slideUp(settings.animationDuration).removeClass(settings.elementExpandedClass);
249		$closeButton.removeClass('active').hide();
250		$spacer.animate({height: 0}, settings.animationDuration, function () {
251			$spacer.detach();
252		});
253
254		// Scroll the page to bring the active link into view.
255		scrollSectionIntoView(activeLinkTopOffset, activeLinkBottomOffset);
256
257		stopExpandedAreaMonitor();
258
259		// Reset global variables.
260		isExpanded = false;
261		activeLink = false;
262		activeExpandedArea = false;
263	});
264
265	/**
266	 * Stop monitoring size of expanded area.
267	 */
268	var stopExpandedAreaMonitor = function () {
269		if (checkExpandedAreaResize) {
270			clearInterval(checkExpandedAreaResize);
271		}
272	};
273
274	/**
275	 * Match preview and spacer in height and position.
276	 */
277	var syncExpandedAreaWithSpacer = function () {
278		if (activeExpandedArea && isExpanded) {
279			$spacer.height($(activeExpandedArea).height());
280			activeExpandedAreaTop = $spacer.position().top;
281			$closeButton.add(activeExpandedArea).css({top: activeExpandedAreaTop + 'px'});
282		}
283	};
284
285	/**
286	 * Place spacer in proper position within grid.
287	 */
288	var positionSpacer = function () {
289		var lastSibling;
290		if (activeLink && lastItemInActiveRow && isExpanded) {
291			// Remove spacer.
292			$spacer.detach();
293			lastSibling = getLastSiblingInRow($(activeLink).parent()[0]);
294			// Reposition spacer, if necessary.
295			if (lastItemInActiveRow !== lastSibling) {
296				console.log(lastSibling);
297				lastItemInActiveRow = lastSibling;
298			}
299			// Restore spacer.
300			$(lastItemInActiveRow).after($spacer);
301		}
302	};
303
304	// React to window resize.
305	$(window).resize(function () {
306		if (isExpanded) {
307			positionSpacer();
308			syncExpandedAreaWithSpacer();
309		}
310	});
311};
312
313// Create the jQuery plugin.
314$.fn.expandingGrid = function (options) {
315	return this.each(function () {
316		expandingGrid(this, options);
317	});
318};
319
320})(jQuery, window, document);
321
322jQuery(document).ready(function ($) {
323	$('.expanding-grid').expandingGrid();
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.