audioplayer.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /*
  2. By Osvaldas Valutis, www.osvaldas.info
  3. Available for use under the MIT License
  4. */
  5. ;(function( $, window, document, undefined )
  6. {
  7. var isTouch = 'ontouchstart' in window,
  8. eStart = isTouch ? 'touchstart' : 'mousedown',
  9. eMove = isTouch ? 'touchmove' : 'mousemove',
  10. eEnd = isTouch ? 'touchend' : 'mouseup',
  11. eCancel = isTouch ? 'touchcancel' : 'mouseup',
  12. secondsToTime = function( secs )
  13. {
  14. var hoursDiv = secs / 3600, hours = Math.floor( hoursDiv ), minutesDiv = secs % 3600 / 60, minutes = Math.floor( minutesDiv ), seconds = Math.ceil( secs % 3600 % 60 );
  15. if( seconds > 59 ) { seconds = 0; minutes = Math.ceil( minutesDiv ); }
  16. if( minutes > 59 ) { minutes = 0; hours = Math.ceil( hoursDiv ); }
  17. return ( hours == 0 ? '' : hours > 0 && hours.toString().length < 2 ? '0'+hours+':' : hours+':' ) + ( minutes.toString().length < 2 ? '0'+minutes : minutes ) + ':' + ( seconds.toString().length < 2 ? '0'+seconds : seconds );
  18. },
  19. canPlayType = function( file )
  20. {
  21. var audioElement = document.createElement( 'audio' );
  22. return !!( audioElement.canPlayType && audioElement.canPlayType( 'audio/' + file.split( '.' ).pop().toLowerCase() + ';' ).replace( /no/, '' ) );
  23. };
  24. $.fn.audioPlayer = function( params )
  25. {
  26. var params = $.extend( { classPrefix: 'audioplayer', strPlay: 'Play', strPause: 'Pause', strVolume: 'Volume' }, params ),
  27. cssClass = {},
  28. cssClassSub =
  29. {
  30. playPause: 'playpause',
  31. playing: 'playing',
  32. stopped: 'stopped',
  33. time: 'time',
  34. timeCurrent: 'time-current',
  35. timeDuration: 'time-duration',
  36. bar: 'bar',
  37. barLoaded: 'bar-loaded',
  38. barPlayed: 'bar-played',
  39. volume: 'volume',
  40. volumeButton: 'volume-button',
  41. volumeAdjust: 'volume-adjust',
  42. noVolume: 'novolume',
  43. muted: 'muted',
  44. mini: 'mini'
  45. };
  46. for( var subName in cssClassSub )
  47. cssClass[ subName ] = params.classPrefix + '-' + cssClassSub[ subName ];
  48. this.each( function()
  49. {
  50. if( $( this ).prop( 'tagName' ).toLowerCase() != 'audio' )
  51. return false;
  52. var $this = $( this ),
  53. audioFile = $this.attr( 'src' ),
  54. isAutoPlay = $this.get( 0 ).getAttribute( 'autoplay' ), isAutoPlay = isAutoPlay === '' || isAutoPlay === 'autoplay' ? true : false,
  55. isLoop = $this.get( 0 ).getAttribute( 'loop' ), isLoop = isLoop === '' || isLoop === 'loop' ? true : false,
  56. isSupport = false;
  57. if( typeof audioFile === 'undefined' )
  58. {
  59. $this.find( 'source' ).each( function()
  60. {
  61. audioFile = $( this ).attr( 'src' );
  62. if( typeof audioFile !== 'undefined' && canPlayType( audioFile ) )
  63. {
  64. isSupport = true;
  65. return false;
  66. }
  67. });
  68. }
  69. else if( canPlayType( audioFile ) ) isSupport = true;
  70. var thePlayer = $( '<div class="' + params.classPrefix + '">' + ( isSupport ? $( '<div>' ).append( $this.eq( 0 ).clone() ).html() : '<embed src="' + audioFile + '" width="0" height="0" volume="100" autostart="' + isAutoPlay.toString() +'" loop="' + isLoop.toString() + '" />' ) + '<div class="' + cssClass.playPause + '" title="' + params.strPlay + '"><a href="#">' + params.strPlay + '</a></div></div>' ),
  71. theAudio = isSupport ? thePlayer.find( 'audio' ) : thePlayer.find( 'embed' ), theAudio = theAudio.get( 0 );
  72. if( isSupport )
  73. {
  74. thePlayer.find( 'audio' ).css( { 'width': 0, 'height': 0, 'visibility': 'hidden' } );
  75. thePlayer.append( '<div class="' + cssClass.time + ' ' + cssClass.timeCurrent + '"></div><div class="' + cssClass.bar + '"><div class="' + cssClass.barLoaded + '"></div><div class="' + cssClass.barPlayed + '"></div></div><div class="' + cssClass.time + ' ' + cssClass.timeDuration + '"></div><div class="' + cssClass.volume + '"><div class="' + cssClass.volumeButton + '" title="' + params.strVolume + '"><a href="#">' + params.strVolume + '</a></div><div class="' + cssClass.volumeAdjust + '"><div><div></div></div></div></div>' );
  76. var theBar = thePlayer.find( '.' + cssClass.bar ),
  77. barPlayed = thePlayer.find( '.' + cssClass.barPlayed ),
  78. barLoaded = thePlayer.find( '.' + cssClass.barLoaded ),
  79. timeCurrent = thePlayer.find( '.' + cssClass.timeCurrent ),
  80. timeDuration = thePlayer.find( '.' + cssClass.timeDuration ),
  81. volumeButton = thePlayer.find( '.' + cssClass.volumeButton ),
  82. volumeAdjuster = thePlayer.find( '.' + cssClass.volumeAdjust + ' > div' ),
  83. volumeDefault = 0,
  84. adjustCurrentTime = function( e )
  85. {
  86. theRealEvent = isTouch ? e.originalEvent.touches[ 0 ] : e;
  87. theAudio.currentTime = Math.round( ( theAudio.duration * ( theRealEvent.pageX - theBar.offset().left ) ) / theBar.width() );
  88. },
  89. adjustVolume = function( e )
  90. {
  91. theRealEvent = isTouch ? e.originalEvent.touches[ 0 ] : e;
  92. theAudio.volume = Math.abs( ( theRealEvent.pageY - ( volumeAdjuster.offset().top + volumeAdjuster.height() ) ) / volumeAdjuster.height() );
  93. },
  94. updateLoadBar = function()
  95. {
  96. var interval = setInterval( function()
  97. {
  98. if( theAudio.buffered.length < 1 ) return true;
  99. barLoaded.width( ( theAudio.buffered.end( 0 ) / theAudio.duration ) * 100 + '%' );
  100. if( Math.floor( theAudio.buffered.end( 0 ) ) >= Math.floor( theAudio.duration ) ) clearInterval( interval );
  101. }, 100 );
  102. };
  103. var volumeTestDefault = theAudio.volume, volumeTestValue = theAudio.volume = 0.111;
  104. if( Math.round( theAudio.volume * 1000 ) / 1000 == volumeTestValue ) theAudio.volume = volumeTestDefault;
  105. else thePlayer.addClass( cssClass.noVolume );
  106. timeDuration.html( '&hellip;' );
  107. timeCurrent.html( secondsToTime( 0 ) );
  108. theAudio.addEventListener( 'loadeddata', function()
  109. {
  110. updateLoadBar();
  111. timeDuration.html( $.isNumeric( theAudio.duration ) ? secondsToTime( theAudio.duration ) : '&hellip;' );
  112. volumeAdjuster.find( 'div' ).height( theAudio.volume * 100 + '%' );
  113. volumeDefault = theAudio.volume;
  114. });
  115. theAudio.addEventListener( 'timeupdate', function()
  116. {
  117. timeCurrent.html( secondsToTime( theAudio.currentTime ) );
  118. barPlayed.width( ( theAudio.currentTime / theAudio.duration ) * 100 + '%' );
  119. });
  120. theAudio.addEventListener( 'volumechange', function()
  121. {
  122. volumeAdjuster.find( 'div' ).height( theAudio.volume * 100 + '%' );
  123. if( theAudio.volume > 0 && thePlayer.hasClass( cssClass.muted ) ) thePlayer.removeClass( cssClass.muted );
  124. if( theAudio.volume <= 0 && !thePlayer.hasClass( cssClass.muted ) ) thePlayer.addClass( cssClass.muted );
  125. });
  126. theAudio.addEventListener( 'ended', function()
  127. {
  128. thePlayer.removeClass( cssClass.playing ).addClass( cssClass.stopped );
  129. });
  130. theBar.on( eStart, function( e )
  131. {
  132. adjustCurrentTime( e );
  133. theBar.on( eMove, function( e ) { adjustCurrentTime( e ); } );
  134. })
  135. .on( eCancel, function()
  136. {
  137. theBar.unbind( eMove );
  138. });
  139. volumeButton.on( 'click', function()
  140. {
  141. if( thePlayer.hasClass( cssClass.muted ) )
  142. {
  143. thePlayer.removeClass( cssClass.muted );
  144. theAudio.volume = volumeDefault;
  145. }
  146. else
  147. {
  148. thePlayer.addClass( cssClass.muted );
  149. volumeDefault = theAudio.volume;
  150. theAudio.volume = 0;
  151. }
  152. return false;
  153. });
  154. volumeAdjuster.on( eStart, function( e )
  155. {
  156. adjustVolume( e );
  157. volumeAdjuster.on( eMove, function( e ) { adjustVolume( e ); } );
  158. })
  159. .on( eCancel, function()
  160. {
  161. volumeAdjuster.unbind( eMove );
  162. });
  163. }
  164. else thePlayer.addClass( cssClass.mini );
  165. thePlayer.addClass( isAutoPlay ? cssClass.playing : cssClass.stopped );
  166. thePlayer.find( '.' + cssClass.playPause ).on( 'click', function()
  167. {
  168. if( thePlayer.hasClass( cssClass.playing ) )
  169. {
  170. $( this ).attr( 'title', params.strPlay ).find( 'a' ).html( params.strPlay );
  171. thePlayer.removeClass( cssClass.playing ).addClass( cssClass.stopped );
  172. isSupport ? theAudio.pause() : theAudio.Stop();
  173. }
  174. else
  175. {
  176. $( this ).attr( 'title', params.strPause ).find( 'a' ).html( params.strPause );
  177. thePlayer.addClass( cssClass.playing ).removeClass( cssClass.stopped );
  178. isSupport ? theAudio.play() : theAudio.Play();
  179. }
  180. return false;
  181. });
  182. $this.replaceWith( thePlayer );
  183. });
  184. return this;
  185. };
  186. })( jQuery, window, document );