244 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
		
		
			
		
	
	
			244 lines
		
	
	
		
			7.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
|  | /* | ||
|  |  * CirclePlayer for the jPlayer Plugin (jQuery) | ||
|  |  * http://www.jplayer.org
 | ||
|  |  * | ||
|  |  * Copyright (c) 2009 - 2012 Happyworm Ltd | ||
|  |  * Dual licensed under the MIT and GPL licenses. | ||
|  |  *  - http://www.opensource.org/licenses/mit-license.php
 | ||
|  |  *  - http://www.gnu.org/copyleft/gpl.html
 | ||
|  |  * | ||
|  |  * Version: 1.0.1 (jPlayer 2.1.0+) | ||
|  |  * Date: 30th May 2011 | ||
|  |  * | ||
|  |  * Author: Mark J Panaghiston @thepag | ||
|  |  * | ||
|  |  * CirclePlayer prototype developed by: | ||
|  |  * Mark Boas @maboa | ||
|  |  * Silvia Benvenuti @aulentina | ||
|  |  * Jussi Kalliokoski @quinirill | ||
|  |  * | ||
|  |  * Inspired by : | ||
|  |  * Neway @imneway http://imneway.net/ http://forrst.com/posts/Untitled-CPt
 | ||
|  |  * and | ||
|  |  * Liam McKay @liammckay http://dribbble.com/shots/50882-Purple-Play-Pause
 | ||
|  |  * | ||
|  |  * Standing on the shoulders of : | ||
|  |  * John Resig @jresig | ||
|  |  * Mark Panaghiston @thepag | ||
|  |  * Louis-Rémi Babé @Louis_Remi | ||
|  |  */ | ||
|  | 
 | ||
|  | 
 | ||
|  | var CirclePlayer = function(jPlayerSelector, media, options) { | ||
|  | 	var	self = this, | ||
|  | 
 | ||
|  | 		defaults = { | ||
|  | 			// solution: "flash, html", // For testing Flash with CSS3
 | ||
|  | 			supplied: "m4a, oga", | ||
|  | 			// Android 2.3 corrupts media element if preload:"none" is used.
 | ||
|  | 			// preload: "none", // No point preloading metadata since no times are displayed. It helps keep the buffer state correct too.
 | ||
|  | 			cssSelectorAncestor: "#cp_container_1", | ||
|  | 			cssSelector: { | ||
|  | 				play: ".cp-play", | ||
|  | 				pause: ".cp-pause" | ||
|  | 			} | ||
|  | 		}, | ||
|  | 
 | ||
|  | 		cssSelector = { | ||
|  | 			bufferHolder: ".cp-buffer-holder", | ||
|  | 			buffer1: ".cp-buffer-1", | ||
|  | 			buffer2: ".cp-buffer-2", | ||
|  | 			progressHolder: ".cp-progress-holder", | ||
|  | 			progress1: ".cp-progress-1", | ||
|  | 			progress2: ".cp-progress-2", | ||
|  | 			circleControl: ".cp-circle-control" | ||
|  | 		}; | ||
|  | 
 | ||
|  | 	this.cssClass = { | ||
|  | 		gt50: "cp-gt50", | ||
|  | 		fallback: "cp-fallback" | ||
|  | 	}; | ||
|  | 
 | ||
|  | 	this.spritePitch = 104; | ||
|  | 	this.spriteRatio = 0.24; // Number of steps / 100
 | ||
|  | 
 | ||
|  | 	this.player = $(jPlayerSelector); | ||
|  | 	this.media = $.extend({}, media); | ||
|  | 	this.options = $.extend(true, {}, defaults, options); // Deep copy
 | ||
|  | 
 | ||
|  | 	this.cssTransforms = Modernizr.csstransforms; | ||
|  | 	this.audio = {}; | ||
|  | 	this.dragging = false; // Indicates if the progressbar is being 'dragged'.
 | ||
|  | 
 | ||
|  | 	this.eventNamespace = ".CirclePlayer"; // So the events can easily be removed in destroy.
 | ||
|  | 
 | ||
|  | 	this.jq = {}; | ||
|  | 	$.each(cssSelector, function(entity, cssSel) { | ||
|  | 		self.jq[entity] = $(self.options.cssSelectorAncestor + " " + cssSel); | ||
|  | 	}); | ||
|  | 
 | ||
|  | 	this._initSolution(); | ||
|  | 	this._initPlayer(); | ||
|  | }; | ||
|  | 
 | ||
|  | CirclePlayer.prototype = { | ||
|  | 	_createHtml: function() { | ||
|  | 	}, | ||
|  | 	_initPlayer: function() { | ||
|  | 		var self = this; | ||
|  | 		this.player.jPlayer(this.options); | ||
|  | 
 | ||
|  | 		this.player.bind($.jPlayer.event.ready + this.eventNamespace, function(event) { | ||
|  | 			if(event.jPlayer.html.used && event.jPlayer.html.audio.available) { | ||
|  | 				self.audio = $(this).data("jPlayer").htmlElement.audio; | ||
|  | 			} | ||
|  | 			$(this).jPlayer("setMedia", self.media); | ||
|  | 			self._initCircleControl(); | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		this.player.bind($.jPlayer.event.play + this.eventNamespace, function(event) { | ||
|  | 			$(this).jPlayer("pauseOthers"); | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		// This event fired as play time increments
 | ||
|  | 		this.player.bind($.jPlayer.event.timeupdate + this.eventNamespace, function(event) { | ||
|  | 			if (!self.dragging) { | ||
|  | 				self._timeupdate(event.jPlayer.status.currentPercentAbsolute); | ||
|  | 			} | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		// This event fired as buffered time increments
 | ||
|  | 		this.player.bind($.jPlayer.event.progress + this.eventNamespace, function(event) { | ||
|  | 			var percent = 0; | ||
|  | 			if((typeof self.audio.buffered === "object") && (self.audio.buffered.length > 0)) { | ||
|  | 				if(self.audio.duration > 0) { | ||
|  | 					var bufferTime = 0; | ||
|  | 					for(var i = 0; i < self.audio.buffered.length; i++) { | ||
|  | 						bufferTime += self.audio.buffered.end(i) - self.audio.buffered.start(i); | ||
|  | 						// console.log(i + " | start = " + self.audio.buffered.start(i) + " | end = " + self.audio.buffered.end(i) + " | bufferTime = " + bufferTime + " | duration = " + self.audio.duration);
 | ||
|  | 					} | ||
|  | 					percent = 100 * bufferTime / self.audio.duration; | ||
|  | 				} // else the Metadata has not been read yet.
 | ||
|  | 				// console.log("percent = " + percent);
 | ||
|  | 			} else { // Fallback if buffered not supported
 | ||
|  | 				// percent = event.jPlayer.status.seekPercent;
 | ||
|  | 				percent = 0; // Cleans up the inital conditions on all browsers, since seekPercent defaults to 100 when object is undefined.
 | ||
|  | 			} | ||
|  | 			self._progress(percent); // Problem here at initial condition. Due to the Opera clause above of buffered.length > 0 above... Removing it means Opera's white buffer ring never shows like with polyfill.
 | ||
|  | 			// Firefox 4 does not always give the final progress event when buffered = 100%
 | ||
|  | 		}); | ||
|  | 
 | ||
|  | 		this.player.bind($.jPlayer.event.ended + this.eventNamespace, function(event) { | ||
|  | 			self._resetSolution(); | ||
|  | 		}); | ||
|  | 	}, | ||
|  | 	_initSolution: function() { | ||
|  | 		if (this.cssTransforms) { | ||
|  | 			this.jq.progressHolder.show(); | ||
|  | 			this.jq.bufferHolder.show(); | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			this.jq.progressHolder.addClass(this.cssClass.gt50).show(); | ||
|  | 			this.jq.progress1.addClass(this.cssClass.fallback); | ||
|  | 			this.jq.progress2.hide(); | ||
|  | 			this.jq.bufferHolder.hide(); | ||
|  | 		} | ||
|  | 		this._resetSolution(); | ||
|  | 	}, | ||
|  | 	_resetSolution: function() { | ||
|  | 		if (this.cssTransforms) { | ||
|  | 			this.jq.progressHolder.removeClass(this.cssClass.gt50); | ||
|  | 			this.jq.progress1.css({'transform': 'rotate(0deg)'}); | ||
|  | 			this.jq.progress2.css({'transform': 'rotate(0deg)'}).hide(); | ||
|  | 		} | ||
|  | 		else { | ||
|  | 			this.jq.progress1.css('background-position', '0 ' + this.spritePitch + 'px'); | ||
|  | 		} | ||
|  | 	}, | ||
|  | 	_initCircleControl: function() { | ||
|  | 		var self = this; | ||
|  | 		this.jq.circleControl.grab({ | ||
|  | 			onstart: function(){ | ||
|  | 				self.dragging = true; | ||
|  | 			}, onmove: function(event){ | ||
|  | 				var pc = self._getArcPercent(event.position.x, event.position.y); | ||
|  | 				self.player.jPlayer("playHead", pc).jPlayer("play"); | ||
|  | 				self._timeupdate(pc); | ||
|  | 			}, onfinish: function(event){ | ||
|  | 				self.dragging = false; | ||
|  | 				var pc = self._getArcPercent(event.position.x, event.position.y); | ||
|  | 				self.player.jPlayer("playHead", pc).jPlayer("play"); | ||
|  | 			} | ||
|  | 		}); | ||
|  | 	}, | ||
|  | 	_timeupdate: function(percent) { | ||
|  | 		var degs = percent * 3.6+"deg"; | ||
|  | 
 | ||
|  | 		var spriteOffset = (Math.floor((Math.round(percent))*this.spriteRatio)-1)*-this.spritePitch; | ||
|  | 
 | ||
|  | 		if (percent <= 50) { | ||
|  | 			if (this.cssTransforms) { | ||
|  | 				this.jq.progressHolder.removeClass(this.cssClass.gt50); | ||
|  | 				this.jq.progress1.css({'transform': 'rotate(' + degs + ')'}); | ||
|  | 				this.jq.progress2.hide(); | ||
|  | 			} else { // fall back
 | ||
|  | 				this.jq.progress1.css('background-position', '0 '+spriteOffset+'px'); | ||
|  | 			} | ||
|  | 		} else if (percent <= 100) { | ||
|  | 			if (this.cssTransforms) { | ||
|  | 				this.jq.progressHolder.addClass(this.cssClass.gt50); | ||
|  | 				this.jq.progress1.css({'transform': 'rotate(180deg)'}); | ||
|  | 				this.jq.progress2.css({'transform': 'rotate(' + degs + ')'}); | ||
|  | 				this.jq.progress2.show(); | ||
|  | 			} else { // fall back
 | ||
|  | 				this.jq.progress1.css('background-position', '0 '+spriteOffset+'px'); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	}, | ||
|  | 	_progress: function(percent) { | ||
|  | 		var degs = percent * 3.6+"deg"; | ||
|  | 
 | ||
|  | 		if (this.cssTransforms) { | ||
|  | 			if (percent <= 50) { | ||
|  | 				this.jq.bufferHolder.removeClass(this.cssClass.gt50); | ||
|  | 				this.jq.buffer1.css({'transform': 'rotate(' + degs + ')'}); | ||
|  | 				this.jq.buffer2.hide(); | ||
|  | 			} else if (percent <= 100) { | ||
|  | 				this.jq.bufferHolder.addClass(this.cssClass.gt50); | ||
|  | 				this.jq.buffer1.css({'transform': 'rotate(180deg)'}); | ||
|  | 				this.jq.buffer2.show(); | ||
|  | 				this.jq.buffer2.css({'transform': 'rotate(' + degs + ')'}); | ||
|  | 			} | ||
|  | 		} | ||
|  | 	}, | ||
|  | 	_getArcPercent: function(pageX, pageY) { | ||
|  | 		var	offset	= this.jq.circleControl.offset(), | ||
|  | 			x	= pageX - offset.left - this.jq.circleControl.width()/2, | ||
|  | 			y	= pageY - offset.top - this.jq.circleControl.height()/2, | ||
|  | 			theta	= Math.atan2(y,x); | ||
|  | 
 | ||
|  | 		if (theta > -1 * Math.PI && theta < -0.5 * Math.PI) { | ||
|  | 			theta = 2 * Math.PI + theta; | ||
|  | 		} | ||
|  | 
 | ||
|  | 		// theta is now value between -0.5PI and 1.5PI
 | ||
|  | 		// ready to be normalized and applied
 | ||
|  | 
 | ||
|  | 		return (theta + Math.PI / 2) / 2 * Math.PI * 10; | ||
|  | 	}, | ||
|  | 	setMedia: function(media) { | ||
|  | 		this.media = $.extend({}, media); | ||
|  | 		this.player.jPlayer("setMedia", this.media); | ||
|  | 	}, | ||
|  | 	play: function(time) { | ||
|  | 		this.player.jPlayer("play", time); | ||
|  | 	}, | ||
|  | 	pause: function(time) { | ||
|  | 		this.player.jPlayer("pause", time); | ||
|  | 	}, | ||
|  | 	destroy: function() { | ||
|  | 		this.player.unbind(this.eventNamespace); | ||
|  | 		this.player.jPlayer("destroy"); | ||
|  | 	} | ||
|  | }; |