audio_player-1.0.x-dev/js/single-audio/skin-three.js
js/single-audio/skin-three.js
(function ($, Drupal, drupalSettings) {
'use strict';
// --- Common Helper Functions (moved outside the loop) ---
/**
* Formats time from seconds to MM:SS format.
* @param {number} seconds - The time in seconds.
* @returns {string} Formatted time string (MM:SS).
*/
function formatTime(seconds) {
const minutes = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${minutes}:${secs < 10 ? '0' : ''}${secs}`;
}
// --- Drupal Behavior ---
Drupal.behaviors.audio_player_skin_three = {
attach: function (context, settings) {
$(function () {
once('audio_player_skin_three', '.audio-player.skin-three', context).forEach(function (playerElement) {
const $player = $(playerElement);
// Get references to all necessary DOM elements using jQuery
const $audioElement = $player.find('.audio-player-element');
const audioElement = $audioElement[0]; // Get the native DOM element for media events and properties
const $playPauseBtn = $player.find('.audio-player-play-pause-button');
const $playIcon = $player.find('.audio-player-play-icon');
const $pauseIcon = $player.find('.audio-player-pause-icon');
const $rewindBtn = $player.find('.audio-player-rewind-button');
const $forwardBtn = $player.find('.audio-player-forward-button');
const $muteUnmuteBtn = $player.find('.audio-player-mute-unmute-button');
const $volumeHighIcon = $player.find('.audio-player-volume-high-icon');
const $volumeMuteIcon = $player.find('.audio-player-volume-mute-icon');
const $volumeSlider = $player.find('.audio-player-volume-slider');
const $progressBarContainer = $player.find('.audio-player-progress-bar-container');
const $progressBar = $player.find('.audio-player-progress-bar');
const $bufferedBar = $player.find('.audio-player-buffered-bar');
const $currentTimeSpan = $player.find('.audio-player-current-time');
const $durationSpan = $player.find('.audio-player-duration');
const $songNameDisplay = $player.find('.audio-player-song-name');
// State variables for playback and mute status
let isPlaying = 0;
let isMuted = 0;
let prevVolume = 1; // Stores volume before muting
let isDraggingProgressBar = 0; // Flag to track if the progress bar is being dragged
$durationSpan.text(formatTime(audioElement.duration));
// Function to update the buffered bar
function updateBufferedBar() {
const duration = audioElement.duration;
if (!isNaN(duration) && duration > 0) {
if (audioElement.buffered.length > 0) {
const bufferedEnd = audioElement.buffered.end(audioElement.buffered.length - 1);
const bufferedPercent = (bufferedEnd / duration) * 100;
$bufferedBar.css('width', bufferedPercent + '%');
} else {
$bufferedBar.css('width', '0%');
}
} else {
$bufferedBar.css('width', '0%');
}
}
// Toggles between play and pause states
function togglePlayPause() {
if (isPlaying) {
audioElement.pause();
$playIcon.show();
$pauseIcon.hide();
} else {
audioElement.play();
$playIcon.hide();
$pauseIcon.show();
}
isPlaying = !isPlaying;
}
// Event listener for play/pause button click
$playPauseBtn.on('click', togglePlayPause);
// Updates the progress bar and current time display as audio plays
$audioElement.on('timeupdate', () => {
// Only update if not currently dragging to avoid jerky movement
if (!isDraggingProgressBar && !isNaN(audioElement.duration) && audioElement.duration > 0) {
const progress = (audioElement.currentTime / audioElement.duration) * 100;
$progressBar.css('width', progress + '%');
}
$currentTimeSpan.text(formatTime(audioElement.currentTime));
updateBufferedBar(); // Call updateBufferedBar on timeupdate for a more responsive feel
});
// Update buffered bar as audio buffers (primary event for buffering)
$audioElement.on('progress', updateBufferedBar);
// Sets the total duration once audio metadata is loaded
$audioElement.on('loadedmetadata', () => {
$durationSpan.text(formatTime(audioElement.duration));
updateBufferedBar(); // Initial update for buffered bar on metadata load
});
// Ensures buffered bar is updated when enough data to start playing is available
$audioElement.on('loadeddata', updateBufferedBar);
// Function to update audio current time based on mouse position
function updateProgressBar(e) {
const rect = $progressBarContainer[0].getBoundingClientRect();
const clickX = e.clientX - rect.left;
const containerWidth = rect.width;
const newProgress = (clickX / containerWidth) * 100;
$progressBar.css('width', newProgress + '%');
const newTime = (clickX / containerWidth) * audioElement.duration;
if (!isNaN(audioElement.duration)) {
audioElement.currentTime = newTime;
}
}
// Event listener for when the user starts dragging the progress bar
$progressBarContainer.on('mousedown', (e) => {
isDraggingProgressBar = 1;
if (isPlaying) {
audioElement.pause();
}
updateProgressBar(e); // Update immediately on mousedown
e.preventDefault();
});
// Event listener for dragging movement (attached to document for broader coverage)
$(document).on('mousemove', (e) => {
if (isDraggingProgressBar) {
updateProgressBar(e);
}
});
// Event listener for when the user releases the mouse button
$(document).on('mouseup', () => {
if (isDraggingProgressBar) {
isDraggingProgressBar = 0;
if (isPlaying) {
audioElement.play();
}
if (isPlaying) {
$playIcon.hide();
$pauseIcon.show();
} else {
$playIcon.show();
$pauseIcon.hide();
}
}
});
// Rewind functionality (skips back 10 seconds)
$rewindBtn.on('click', () => {
audioElement.currentTime = Math.max(0, audioElement.currentTime - 10);
});
// Forward functionality (skips forward 10 seconds)
$forwardBtn.on('click', () => {
audioElement.currentTime = Math.min(audioElement.duration, audioElement.currentTime + 10);
});
// Toggles between mute and unmute states
function toggleMuteUnmute() {
if (isMuted) {
audioElement.volume = prevVolume;
$volumeSlider.val(prevVolume);
$volumeHighIcon.show();
$volumeMuteIcon.hide();
} else {
prevVolume = audioElement.volume;
audioElement.volume = 0;
$volumeSlider.val(0);
$volumeHighIcon.hide();
$volumeMuteIcon.show();
}
isMuted = !isMuted;
}
// Event listener for mute/unmute button click
$muteUnmuteBtn.on('click', toggleMuteUnmute);
// Handles volume slider input changes
$volumeSlider.on('input', (e) => {
audioElement.volume = e.target.value;
if (audioElement.volume === 0) {
isMuted = 1;
$volumeHighIcon.hide();
$volumeMuteIcon.show();
} else {
isMuted = 0;
$volumeHighIcon.show();
$volumeMuteIcon.hide();
prevVolume = audioElement.volume;
}
});
// Resets player state when audio finishes
$audioElement.on('ended', () => {
isPlaying = 0;
$playIcon.show();
$pauseIcon.hide();
$progressBar.css('width', '0%');
$bufferedBar.css('width', '0%');
$currentTimeSpan.text('0:00');
audioElement.currentTime = 0;
});
// Set initial song name from the audio source URL
const audioSrc = audioElement.src;
const fileName = audioSrc.substring(audioSrc.lastIndexOf('/') + 1);
$songNameDisplay.text(decodeURIComponent(fileName));
// Initial call to set buffered bar if audio is already partly loaded (e.g., from cache)
updateBufferedBar();
});
});
}
};
})(jQuery, Drupal, drupalSettings);