JAVASCRIPT   102

mp3-player.js

Guest on 29th August 2021 06:00:46 PM

  1. var audioPlayer = function() {
  2.   "use strict";
  3.  
  4.   // Private variables
  5.   var _currentTrack = null;
  6.   var _elements = {
  7.     audio: document.getElementById("audio"),
  8.     playerButtons: {
  9.       largeToggleBtn: document.querySelector(".large-toggle-btn"),
  10.       nextTrackBtn: document.querySelector(".next-track-btn"),
  11.       previousTrackBtn: document.querySelector(".previous-track-btn"),
  12.       smallToggleBtn: document.getElementsByClassName("small-toggle-btn")
  13.     },
  14.     progressBar: document.querySelector(".progress-box"),
  15.     playListRows: document.getElementsByClassName("play-list-row"),
  16.     trackInfoBox: document.querySelector(".track-info-box")
  17.   };
  18.   var _playAHead = false;
  19.   var _progressCounter = 0;
  20.   var _progressBarIndicator = _elements.progressBar.children[0].children[0].children[1];
  21.   var _trackLoaded = false;
  22.  
  23.   /**
  24.    * Determines the buffer progress.
  25.    *
  26.    * @param audio The audio element on the page.
  27.    **/
  28.   var _bufferProgress = function(audio) {
  29.     var bufferedTime = (audio.buffered.end(0) * 100) / audio.duration;
  30.     var progressBuffer = _elements.progressBar.children[0].children[0].children[0];
  31.  
  32.     progressBuffer.style.width = bufferedTime + "%";
  33.   };
  34.  
  35.   /**
  36.    * A utility function for getting the event cordinates based on browser type.
  37.    *
  38.    * @param e The JavaScript event.
  39.    **/
  40.   var _getXY = function(e) {
  41.     var containerX = _elements.progressBar.offsetLeft;
  42.     var containerY = _elements.progressBar.offsetTop;
  43.  
  44.     var coords = {
  45.       x: null,
  46.       y: null
  47.     };
  48.  
  49.     var isTouchSuopported = "ontouchstart" in window;
  50.  
  51.     if (isTouchSuopported) { //For touch devices
  52.       coords.x = e.clientX - containerX;
  53.       coords.y = e.clientY - containerY;
  54.  
  55.       return coords;
  56.     } else if (e.offsetX || e.offsetX === 0) { // For webkit browsers
  57.       coords.x = e.offsetX;
  58.       coords.y = e.offsetY;
  59.  
  60.       return coords;
  61.     } else if (e.layerX || e.layerX === 0) { // For Mozilla firefox
  62.       coords.x = e.layerX;
  63.       coords.y = e.layerY;
  64.  
  65.       return coords;
  66.     }
  67.   };
  68.  
  69.   var _handleProgressIndicatorClick = function(e) {
  70.     var progressBar = document.querySelector(".progress-box");
  71.     var xCoords = _getXY(e).x;
  72.  
  73.     return (xCoords - progressBar.offsetLeft) / progressBar.children[0].offsetWidth;
  74.   };
  75.  
  76.   /**
  77.    * Initializes the html5 audio player and the playlist.
  78.    *
  79.    **/
  80.   var initPlayer = function() {
  81.  
  82.     if (_currentTrack === 1 || _currentTrack === null) {
  83.       _elements.playerButtons.previousTrackBtn.disabled = true;
  84.     }
  85.  
  86.     //Adding event listeners to playlist clickable elements.
  87.     for (var i = 0; i < _elements.playListRows.length; i++) {
  88.       var smallToggleBtn = _elements.playerButtons.smallToggleBtn[i];
  89.       var playListLink = _elements.playListRows[i].children[2].children[0];
  90.  
  91.       //Playlist link clicked.
  92.       playListLink.addEventListener("click", function(e) {
  93.         e.preventDefault();
  94.         var selectedTrack = parseInt(this.parentNode.parentNode.getAttribute("data-track-row"));
  95.  
  96.         if (selectedTrack !== _currentTrack) {
  97.           _resetPlayStatus();
  98.           _currentTrack = null;
  99.           _trackLoaded = false;
  100.         }
  101.  
  102.         if (_trackLoaded === false) {
  103.           _currentTrack = parseInt(selectedTrack);
  104.           _setTrack();
  105.         } else {
  106.           _playBack(this);
  107.         }
  108.       }, false);
  109.  
  110.       //Small toggle button clicked.
  111.       smallToggleBtn.addEventListener("click", function(e) {
  112.         e.preventDefault();
  113.         var selectedTrack = parseInt(this.parentNode.getAttribute("data-track-row"));
  114.  
  115.         if (selectedTrack !== _currentTrack) {
  116.           _resetPlayStatus();
  117.           _currentTrack = null;
  118.           _trackLoaded = false;
  119.         }
  120.  
  121.         if (_trackLoaded === false) {
  122.           _currentTrack = parseInt(selectedTrack);
  123.           _setTrack();
  124.         } else {
  125.           _playBack(this);
  126.         }
  127.  
  128.       }, false);
  129.     }
  130.  
  131.     //Audio time has changed so update it.
  132.     _elements.audio.addEventListener("timeupdate", _trackTimeChanged, false);
  133.  
  134.     //Audio track has ended playing.
  135.     _elements.audio.addEventListener("ended", function(e) {
  136.       _trackHasEnded();
  137.     }, false);
  138.  
  139.     //Audio error.
  140.     _elements.audio.addEventListener("error", function(e) {
  141.       switch (e.target.error.code) {
  142.         case e.target.error.MEDIA_ERR_ABORTED:
  143.           alert('You aborted the video playback.');
  144.           break;
  145.         case e.target.error.MEDIA_ERR_NETWORK:
  146.           alert('A network error caused the audio download to fail.');
  147.           break;
  148.         case e.target.error.MEDIA_ERR_DECODE:
  149.           alert('The audio playback was aborted due to a corruption problem or because the video used features your browser did not support.');
  150.           break;
  151.         case e.target.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
  152.           alert('The video audio not be loaded, either because the server or network failed or because the format is not supported.');
  153.           break;
  154.         default:
  155.           alert('An unknown error occurred.');
  156.           break;
  157.       }
  158.       trackLoaded = false;
  159.       _resetPlayStatus();
  160.     }, false);
  161.  
  162.     //Large toggle button clicked.
  163.     _elements.playerButtons.largeToggleBtn.addEventListener("click", function(e) {
  164.       if (_trackLoaded === false) {
  165.         _currentTrack = parseInt(1);
  166.         _setTrack()
  167.       } else {
  168.         _playBack();
  169.       }
  170.     }, false);
  171.  
  172.     //Next track button clicked.
  173.     _elements.playerButtons.nextTrackBtn.addEventListener("click", function(e) {
  174.       if (this.disabled !== true) {
  175.         _currentTrack++;
  176.         _trackLoaded = false;
  177.         _resetPlayStatus();
  178.         _setTrack();
  179.       }
  180.     }, false);
  181.  
  182.     //Previous track button clicked.
  183.     _elements.playerButtons.previousTrackBtn.addEventListener("click", function(e) {
  184.       if (this.disabled !== true) {
  185.         _currentTrack--;
  186.         _trackLoaded = false;
  187.         _resetPlayStatus();
  188.         _setTrack();
  189.       }
  190.     }, false);
  191.  
  192.     //User is moving progress indicator.
  193.     _progressBarIndicator.addEventListener("mousedown", _mouseDown, false);
  194.  
  195.     //User stops moving progress indicator.
  196.     window.addEventListener("mouseup", _mouseUp, false);
  197.   };
  198.  
  199.   /**
  200.    * Handles the mousedown event by a user and determines if the mouse is being moved.
  201.    *
  202.    * @param e The event object.
  203.    **/
  204.   var _mouseDown = function(e) {
  205.     window.addEventListener("mousemove", _moveProgressIndicator, true);
  206.     audio.removeEventListener("timeupdate", _trackTimeChanged, false);
  207.  
  208.     _playAHead = true;
  209.   };
  210.  
  211.   /**
  212.    * Handles the mouseup event by a user.
  213.    *
  214.    * @param e The event object.
  215.    **/
  216.   var _mouseUp = function(e) {
  217.     if (_playAHead === true) {
  218.       var duration = parseFloat(audio.duration);
  219.       var progressIndicatorClick = parseFloat(_handleProgressIndicatorClick(e));
  220.       window.removeEventListener("mousemove", _moveProgressIndicator, true);
  221.  
  222.       audio.currentTime = duration * progressIndicatorClick;
  223.       audio.addEventListener("timeupdate", _trackTimeChanged, false);
  224.       _playAHead = false;
  225.     }
  226.   };
  227.  
  228.   /**
  229.    * Moves the progress indicator to a new point in the audio.
  230.    *
  231.    * @param e The event object.
  232.    **/
  233.   var _moveProgressIndicator = function(e) {
  234.     var newPosition = 0;
  235.     var progressBarOffsetLeft = _elements.progressBar.offsetLeft;
  236.     var progressBarWidth = 0;
  237.     var progressBarIndicator = _elements.progressBar.children[0].children[0].children[1];
  238.     var progressBarIndicatorWidth = _progressBarIndicator.offsetWidth;
  239.     var xCoords = _getXY(e).x;
  240.  
  241.     progressBarWidth = _elements.progressBar.children[0].offsetWidth - progressBarIndicatorWidth;
  242.     newPosition = xCoords - progressBarOffsetLeft;
  243.  
  244.     if ((newPosition >= 1) && (newPosition <= progressBarWidth)) {
  245.       progressBarIndicator.style.left = newPosition + ".px";
  246.     }
  247.     if (newPosition < 0) {
  248.       progressBarIndicator.style.left = "0";
  249.     }
  250.     if (newPosition > progressBarWidth) {
  251.       progressBarIndicator.style.left = progressBarWidth + "px";
  252.     }
  253.   };
  254.  
  255.   /**
  256.    * Controls playback of the audio element.
  257.    *
  258.    **/
  259.   var _playBack = function() {
  260.     if (_elements.audio.paused) {
  261.       _elements.audio.play();
  262.       _updatePlayStatus(true);
  263.       document.title = "\u25B6 " + document.title;
  264.     } else {
  265.       _elements.audio.pause();
  266.       _updatePlayStatus(false);
  267.       document.title = document.title.substr(2);
  268.     }
  269.   };
  270.  
  271.   /**
  272.    * Sets the track if it hasn't already been loaded yet.
  273.    *
  274.    **/
  275.   var _setTrack = function() {
  276.     var songURL = _elements.audio.children[_currentTrack - 1].src;
  277.  
  278.     _elements.audio.setAttribute("src", songURL);
  279.     _elements.audio.load();
  280.  
  281.     _trackLoaded = true;
  282.  
  283.     _setTrackTitle(_currentTrack, _elements.playListRows);
  284.  
  285.     _setActiveItem(_currentTrack, _elements.playListRows);
  286.  
  287.     _elements.trackInfoBox.style.visibility = "visible";
  288.  
  289.     _playBack();
  290.   };
  291.  
  292.   /**
  293.    * Sets the activly playing item within the playlist.
  294.    *
  295.    * @param currentTrack The current track number being played.
  296.    * @param playListRows The playlist object.
  297.    **/
  298.   var _setActiveItem = function(currentTrack, playListRows) {
  299.     for (var i = 0; i < playListRows.length; i++) {
  300.       playListRows[i].children[2].className = "track-title";
  301.     }
  302.  
  303.     playListRows[currentTrack - 1].children[2].className = "track-title active-track";
  304.   };
  305.  
  306.   /**
  307.    * Sets the text for the currently playing song.
  308.    *
  309.    * @param currentTrack The current track number being played.
  310.    * @param playListRows The playlist object.
  311.    **/
  312.   var _setTrackTitle = function(currentTrack, playListRows) {
  313.     var trackTitleBox = document.querySelector(".player .info-box .track-info-box .track-title-text");
  314.     var trackTitle = playListRows[currentTrack - 1].children[2].outerText;
  315.  
  316.     trackTitleBox.innerHTML = null;
  317.  
  318.     trackTitleBox.innerHTML = trackTitle;
  319.  
  320.     document.title = trackTitle;
  321.   };
  322.  
  323.   /**
  324.    * Plays the next track when a track has ended playing.
  325.    *
  326.    **/
  327.   var _trackHasEnded = function() {
  328.     parseInt(_currentTrack);
  329.     _currentTrack = (_currentTrack === _elements.playListRows.length) ? 1 : _currentTrack + 1;
  330.     _trackLoaded = false;
  331.  
  332.     _resetPlayStatus();
  333.  
  334.     _setTrack();
  335.   };
  336.  
  337.   /**
  338.    * Updates the time for the song being played.
  339.    *
  340.    **/
  341.   var _trackTimeChanged = function() {
  342.     var currentTimeBox = document.querySelector(".player .info-box .track-info-box .audio-time .current-time");
  343.     var currentTime = audio.currentTime;
  344.     var duration = audio.duration;
  345.     var durationBox = document.querySelector(".player .info-box .track-info-box .audio-time .duration");
  346.     var trackCurrentTime = _trackTime(currentTime);
  347.     var trackDuration = _trackTime(duration);
  348.  
  349.     currentTimeBox.innerHTML = null;
  350.     currentTimeBox.innerHTML = trackCurrentTime;
  351.  
  352.     durationBox.innerHTML = null;
  353.     durationBox.innerHTML = trackDuration;
  354.  
  355.     _updateProgressIndicator(audio);
  356.     _bufferProgress(audio);
  357.   };
  358.  
  359.   /**
  360.    * A utility function for converting a time in miliseconds to a readable time of minutes and seconds.
  361.    *
  362.    * @param seconds The time in seconds.
  363.    *
  364.    * @return time The time in minutes and/or seconds.
  365.    **/
  366.   var _trackTime = function(seconds) {
  367.     var min = 0;
  368.     var sec = Math.floor(seconds);
  369.     var time = 0;
  370.  
  371.     min = Math.floor(sec / 60);
  372.  
  373.     min = min >= 10 ? min : '0' + min;
  374.  
  375.     sec = Math.floor(sec % 60);
  376.  
  377.     sec = sec >= 10 ? sec : '0' + sec;
  378.  
  379.     time = min + ':' + sec;
  380.  
  381.     return time;
  382.   };
  383.  
  384.   /**
  385.    * Updates both the large and small toggle buttons accordingly.
  386.    *
  387.    * @param audioPlaying A booean value indicating if audio is playing or paused.
  388.    **/
  389.   var _updatePlayStatus = function(audioPlaying) {
  390.     if (audioPlaying) {
  391.       _elements.playerButtons.largeToggleBtn.children[0].className = "large-pause-btn";
  392.  
  393.       _elements.playerButtons.smallToggleBtn[_currentTrack - 1].children[0].className = "small-pause-btn";
  394.     } else {
  395.       _elements.playerButtons.largeToggleBtn.children[0].className = "large-play-btn";
  396.  
  397.       _elements.playerButtons.smallToggleBtn[_currentTrack - 1].children[0].className = "small-play-btn";
  398.     }
  399.  
  400.     //Update next and previous buttons accordingly
  401.     if (_currentTrack === 1) {
  402.       _elements.playerButtons.previousTrackBtn.disabled = true;
  403.       _elements.playerButtons.previousTrackBtn.className = "previous-track-btn disabled";
  404.     } else if (_currentTrack > 1 && _currentTrack !== _elements.playListRows.length) {
  405.       _elements.playerButtons.previousTrackBtn.disabled = false;
  406.       _elements.playerButtons.previousTrackBtn.className = "previous-track-btn";
  407.       _elements.playerButtons.nextTrackBtn.disabled = false;
  408.       _elements.playerButtons.nextTrackBtn.className = "next-track-btn";
  409.     } else if (_currentTrack === _elements.playListRows.length) {
  410.       _elements.playerButtons.nextTrackBtn.disabled = true;
  411.       _elements.playerButtons.nextTrackBtn.className = "next-track-btn disabled";
  412.     }
  413.   };
  414.  
  415.   /**
  416.    * Updates the location of the progress indicator according to how much time left in audio.
  417.    *
  418.    **/
  419.   var _updateProgressIndicator = function() {
  420.     var currentTime = parseFloat(_elements.audio.currentTime);
  421.     var duration = parseFloat(_elements.audio.duration);
  422.     var indicatorLocation = 0;
  423.     var progressBarWidth = parseFloat(_elements.progressBar.offsetWidth);
  424.     var progressIndicatorWidth = parseFloat(_progressBarIndicator.offsetWidth);
  425.     var progressBarIndicatorWidth = progressBarWidth - progressIndicatorWidth;
  426.  
  427.     indicatorLocation = progressBarIndicatorWidth * (currentTime / duration);
  428.  
  429.     _progressBarIndicator.style.left = indicatorLocation + "px";
  430.   };
  431.  
  432.   /**
  433.    * Resets all toggle buttons to be play buttons.
  434.    *
  435.    **/
  436.   var _resetPlayStatus = function() {
  437.     var smallToggleBtn = _elements.playerButtons.smallToggleBtn;
  438.  
  439.     _elements.playerButtons.largeToggleBtn.children[0].className = "large-play-btn";
  440.  
  441.     for (var i = 0; i < smallToggleBtn.length; i++) {
  442.       if (smallToggleBtn[i].children[0].className === "small-pause-btn") {
  443.         smallToggleBtn[i].children[0].className = "small-play-btn";
  444.       }
  445.     }
  446.   };
  447.  
  448.   return {
  449.     initPlayer: initPlayer
  450.   };
  451. };
  452.  
  453. (function() {
  454.   var player = new audioPlayer();
  455.  
  456.   player.initPlayer();
  457. })();

Raw Paste


Login or Register to edit or fork this paste. It's free.