PageSourceSearch

https://www.sachemlibrary.org/wp-content/themes/sachem/js/customAudioPlayer.js?ver=1.0.0.1790915887

js sachemlibrary.org collected 2026-10-02 05:34:46 UTC 9,673 bytes, 411 lines download raw bytes

1jQuery(function ($) {
2	
3	//alert("Found players:" + $('.audioPlayer').length);
4
5	// --------------------------------------------
6	// Track all audio elements so we can pause others
7	// --------------------------------------------
8	var allAudios = [];
9
10	// The player the user last interacted with
11	var activeWrapper = null;
12
13	// --------------------------------------------
14	// Initialize each custom audio player
15	// --------------------------------------------
16	$('.audioPlayer').each(function () {
17
18		var $wrapper = $(this);
19		var audioSelector = $wrapper.data('audio');
20		var audio = $(audioSelector).get(0);
21
22		// If something is wrong or missing, skip this one
23		if (!audio) {
24			return;
25		}
26
27		// Keep a reference so we can auto-pause the others
28		allAudios.push(audio);
29
30		// Cache UI elements
31		var $playToggle = $wrapper.find('.playToggle');
32		var $scrubBar = $wrapper.find('.scrubBar');
33		var $scrubProgress = $wrapper.find('.scrubProgress');
34		var $volumeControl = $wrapper.find('.volumeControl');
35		var $volumeIcon = $wrapper.find('.volumeIcon');
36		var $volumeSlider = $wrapper.find('.volumeSlider');
37
38		var $currentTime = $wrapper.find('.currentTime');
39		var $duration = $wrapper.find('.duration');
40
41		// Default volume
42		audio.volume = 1;
43
44		// ---------------------------------------------------
45		// Mark this player as the active one (for keystrokes)
46		// ---------------------------------------------------
47		function setActive() {
48			activeWrapper = $wrapper;
49			$('.audioPlayer').removeClass('is-active');
50			$wrapper.addClass('is-active');
51		}
52
53		// ---------------------------------------------------
54		// Format seconds → mm:ss
55		// ---------------------------------------------------
56		function formatTime(secs) {
57			var seconds = Math.floor(secs);
58			var minutes = Math.floor(seconds / 60);
59			var remaining = seconds % 60;
60			var result = minutes + ':';
61
62			if (remaining < 10) {
63				result += '0' + remaining;
64			} else {
65				result += remaining;
66			}
67
68			return result;
69		}
70
71		// ---------------------------------------------------
72		// Update the play/pause icon
73		// ---------------------------------------------------
74		function updatePlayIcon() {
75			if (audio.paused) {
76				$playToggle.html('<i class="fa-solid fa-play"></i>');
77			} else {
78				$playToggle.html('<i class="fa-solid fa-pause"></i>');
79			}
80		}
81
82		// ---------------------------------------------------
83		// Update the volume icon based on current volume level
84		// ---------------------------------------------------
85		function updateVolumeIcon() {
86			var value;
87
88			if (audio.muted === true) {
89				value = 0;
90			} else {
91				value = audio.volume;
92			}
93
94			if (value === 0) {
95				$volumeIcon.html('<i class="fa-solid fa-volume-xmark"></i>');
96			} else if (value < 0.5) {
97				$volumeIcon.html('<i class="fa-solid fa-volume-low"></i>');
98			} else {
99				$volumeIcon.html('<i class="fa-solid fa-volume-high"></i>');
100			}
101		}
102
103		// Highlight this player when clicked
104		$wrapper.on('mousedown click', function () {
105			setActive();
106		});
107
108		// ---------------------------------------------------
109		// Play/pause button
110		// ---------------------------------------------------
111		$playToggle.on('click', function () {
112			setActive();
113			//alert("clicked");
114			if (audio.paused) {
115				audio.play();
116			} else {
117				audio.pause();
118			}
119		});
120
121		// ---------------------------------------------------
122		// Auto-pause other players when this one plays
123		// ---------------------------------------------------
124		$(audio).on('play', function () {
125			var i;
126			for (i = 0; i < allAudios.length; i++) {
127				if (allAudios[i] !== audio) {
128					if (allAudios[i].paused === false) {
129						allAudios[i].pause();
130					}
131				}
132			}
133			updatePlayIcon();
134		});
135
136		// Update icon when paused
137		$(audio).on('pause', function () {
138			updatePlayIcon();
139		});
140
141		// ---------------------------------------------------
142		// Duration (set once metadata is loaded)
143		// ---------------------------------------------------
144		$(audio).on('loadedmetadata', function () {
145			if ($duration.length > 0) {
146				$duration.text(formatTime(audio.duration));
147			}
148		});
149
150		// ---------------------------------------------------
151		// As audio plays, update the progress bar + time label
152		// ---------------------------------------------------
153		$(audio).on('timeupdate', function () {
154			if (!audio.duration) {
155				return;
156			}
157
158			var percent = (audio.currentTime / audio.duration) * 100;
159			$scrubProgress.css('width', percent + '%');
160
161			if ($currentTime.length > 0) {
162				$currentTime.text(formatTime(audio.currentTime));
163			}
164		});
165
166		// ---------------------------------------------------
167		// Click-to-seek on the scrub bar
168		// ---------------------------------------------------
169		$scrubBar.on('click', function (e) {
170			if (!audio.duration) {
171				return;
172			}
173
174			setActive();
175
176			var rect = this.getBoundingClientRect();
177			var offsetX = e.clientX - rect.left;
178			var ratio = offsetX / rect.width;
179
180			if (ratio < 0) {
181				ratio = 0;
182			}
183
184			if (ratio > 1) {
185				ratio = 1;
186			}
187
188			audio.currentTime = ratio * audio.duration;
189		});
190
191		// ---------------------------------------------------
192		// Volume slider changes
193		// ---------------------------------------------------
194		$volumeSlider.on('input change', function () {
195			var value = parseFloat(this.value);
196
197			if (isNaN(value)) {
198				return;
199			}
200
201			audio.volume = value;
202
203			if (value === 0) {
204				audio.muted = true;
205			} else {
206				audio.muted = false;
207			}
208
209			updateVolumeIcon();
210		});
211
212		// ---------------------------------------------------
213		// Keep slider + icon synced with actual volume
214		// ---------------------------------------------------
215		$(audio).on('volumechange', function () {
216			if (audio.muted === true) {
217				$volumeSlider.val(0);
218			} else {
219				$volumeSlider.val(audio.volume);
220			}
221			updateVolumeIcon();
222		});
223
224		// ---------------------------------------------------
225		// Volume popup toggle
226		// ---------------------------------------------------
227		/*
228		$volumeIcon.on('click', function (e) {
229			e.stopPropagation();
230			setActive();
231
232			if ($volumeControl.hasClass('is-open')) {
233				$volumeControl.removeClass('is-open');
234			} else {
235				$volumeControl.addClass('is-open');
236			}
237		});
238		*/
239		
240		// replace the old $volumeIcon.on('click', ...) block with this:
241		$volumeControl.on('click', function (e) {
242			//alert("clicked");
243			e.stopPropagation();
244			setActive();
245		
246			if ($volumeControl.hasClass('is-open')) {
247				$volumeControl.removeClass('is-open');
248			} else {
249				$volumeControl.addClass('is-open');
250			}
251		});
252
253
254		// Store for keyboard shortcuts
255		$wrapper.data('audioElement', audio);
256		$wrapper.data('updatePlayIcon', updatePlayIcon);
257		$wrapper.data('updateVolumeIcon', updateVolumeIcon);
258
259		// Initial icon states
260		updatePlayIcon();
261		updateVolumeIcon();
262	});
263
264	// If user hasn’t interacted yet, make the first one active
265	if (!activeWrapper || activeWrapper.length === 0) {
266		activeWrapper = $('.audioPlayer').first();
267		activeWrapper.addClass('is-active');
268	}
269
270	// ---------------------------------------------------
271	// Close any open volume popup when clicking outside
272	// ---------------------------------------------------
273	$(document).on('click', function (e) {
274		var $target = $(e.target);
275		var insideVolume = $target.closest('.volumeControl').length > 0;
276
277		if (!insideVolume) {
278			$('.volumeControl').removeClass('is-open');
279		}
280	});
281
282	// ---------------------------------------------------
283	// GLOBAL KEYBOARD SHORTCUTS
284	// Applies to whichever player was last clicked
285	// ---------------------------------------------------
286	$(document).on('keydown', function (e) {
287
288		if (!activeWrapper) {
289			return;
290		}
291
292		// Don’t hijack keyboard when typing in a form field
293		var tagName = e.target.tagName;
294
295		if (tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
296			return;
297		}
298
299		var audio = activeWrapper.data('audioElement');
300		var updatePlayIcon = activeWrapper.data('updatePlayIcon');
301		var updateVolumeIcon = activeWrapper.data('updateVolumeIcon');
302
303		if (!audio) {
304			return;
305		}
306
307		var key = e.key;
308
309		// Space or "K"
310		if (key === ' ' || key === 'k') {
311			e.preventDefault();
312
313			if (audio.paused) {
314				audio.play();
315			} else {
316				audio.pause();
317			}
318
319			updatePlayIcon();
320		}
321
322		// Right arrow (seek forward)
323		else if (key === 'ArrowRight') {
324			e.preventDefault();
325
326			if (audio.duration) {
327				var forwardTime = audio.currentTime + 5;
328
329				if (forwardTime > audio.duration) {
330					forwardTime = audio.duration;
331				}
332
333				audio.currentTime = forwardTime;
334			}
335		}
336
337		// Left arrow (seek backward)
338		else if (key === 'ArrowLeft') {
339			e.preventDefault();
340
341			var backwardTime = audio.currentTime - 5;
342
343			if (backwardTime < 0) {
344				backwardTime = 0;
345			}
346
347			audio.currentTime = backwardTime;
348		}
349
350		// Volume up
351		else if (key === 'ArrowUp') {
352			e.preventDefault();
353
354			var upVolume = audio.volume + 0.05;
355
356			if (upVolume > 1) {
357				upVolume = 1;
358			}
359
360			audio.volume = upVolume;
361
362			if (audio.volume === 0) {
363				audio.muted = true;
364			} else {
365				audio.muted = false;
366			}
367
368			updateVolumeIcon();
369		}
370
371		// Volume down
372		else if (key === 'ArrowDown') {
373			e.preventDefault();
374
375			var downVolume = audio.volume - 0.05;
376
377			if (downVolume < 0) {
378				downVolume = 0;
379			}
380
381			audio.volume = downVolume;
382
383			if (audio.volume === 0) {
384				audio.muted = true;
385			} else {
386				audio.muted = false;
387			}
388
389			updateVolumeIcon();
390		}
391
392		// Mute toggle
393		else if (key === 'm' || key === 'M') {
394			e.preventDefault();
395
396			if (audio.muted === true) {
397				audio.muted = false;
398			} else {
399				audio.muted = true;
400			}
401
402			updateVolumeIcon();
403		}
404	});
405
406
407
408	
409});
410
411

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.