/*
 * Simple HTML5 Audio Player V1
 * http://codecanyon.net/item/simple-html5-audio-player-v1/495024
 *
 * Author: Ivan da Silveira
 * Version: 1.1.0
 * Copyright 2011
 *
 * Only for the sale at the envato marketplaces
 */



(function($)
{
// global variables
var win							= window;
var $win						= $(win);
var doc							= document;
var initCount					= 0;
var collection					= [];
var animationFrameCollection	= [];


var AUDIO_PLAYER_ID		= 'AudioPlayerV1';

var TEMPLATE			= '<ul class="AudioPlayerV1 APV1_wrapper"><li><div class="APV1_play_button"></div></li><li><div class="APV1_seperator APV1_for_play"></div></li><li><div class="APV1_time_text"> 00:00 </div></li><li><div class="APV1_seperator APV1_for_time"></div></li><li class="APV1_container"><div class="APV1_progress_bar_container"><div class="APV1_progress_bar_wrapper"><div class="APV1_seek_bar"></div><div class="APV1_play_bar APV1_transition"></div></div></div></li><li><div class="APV1_seperator APV1_for_duration"></div></li><li><div class="APV1_duration_text"> 00:00 </div></li><li><div class="APV1_seperator APV1_for_volume"></div></li><li><div class="APV1_volume_button"></div></li><li><div class="APV1_volume_bar_container"><div class="APV1_volume_bar"></div></div></li></ul>';



var animationFrame = (
	((win['requestAnimationFrame'] && win['cancelRequestAnimationFrame'])				? win['requestAnimationFrame']			: undefined) ||
	((win['webkitRequestAnimationFrame'] && win['webkitCancelRequestAnimationFrame'])	? win['webkitRequestAnimationFrame']	: undefined) ||
	((win['mozRequestAnimationFrame'] && win['mozCancelRequestAnimationFrame'])			? win['mozRequestAnimationFrame']		: undefined) ||
	((win['oRequestAnimationFrame'] && win['oCancelRequestAnimationFrame'])				? win['oRequestAnimationFrame']			: undefined) ||
	((win['msRequestAnimationFrame'] && win['msCancelRequestAnimationFrame'])			? win['msRequestAnimationFrame']		: undefined) ||
	undefined
);

var cancelAnimationFrame = (
	win['cancelRequestAnimationFrame']			||
	win['webkitCancelRequestAnimationFrame']	||
	win['mozCancelRequestAnimationFrame']		||
	win['oCancelRequestAnimationFrame']			||
	win['msCancelRequestAnimationFrame']		||
	undefined
);

/**
 * Enum for media event values.
 * @enum {string}
 */
var MediaEvent = 
{
	PROGRESS		: 'progress',
	TIME_UPDATE		: 'timeupdate', 
	CAN_PLAY		: 'canplay',
	RATE_CHANGE		: 'ratechange',
	PLAYING			: 'playing',
	WAITING			: 'waiting',
	STARTED			: 'started',
	PLAY			: 'play',
	PAUSE			: 'pause',
	ENDED			: 'ended',
	VOLUME_CHANGE	: 'volumechange',
	DURATION_CHANGE	: 'durationchange',
	ERROR			: 'error'
};

/**
 * Enum for mouse event values.
 * @enum {string}
 */
var MouseEvent =
{
	CLICK			: 'click',
	MOUSE_DOWN		: 'mousedown',
	MOUSE_UP		: 'mouseup',
	MOUSE_MOVE		: 'mousemove',
	CONTEXT_MENU	: 'contextmenu'
};

/**
 * Enum for class values.
 * @enum {string}
 */
var Classes =
{
	TRANSITION	: 'APV1_transition',
	PLAYING		: 'APV1_playing',
	MUTE		: 'APV1_mute',
	ERROR		: 'APV1_error'
};

/**
 * Enum for css values.
 * @enum {string}
 */
var Values =
{
	DISPLAY_BLOCK	: 'display:block !important;',
	DISPLAY_NONE	: 'display:none !important;',
	ZERO			: '0 !important;',
	STYLE			: 'style'
};

/**
 * Enum for attributes values.
 * @enum {string}
 */
var Attributes =
{
	DATA_CONTROLS_TIME		: 'data-controls-time',
	DATA_CONTROLS_VOLUME	: 'data-controls-volume',
	DATA_CONTROLS_DURATION	: 'data-controls-duration',
	DATA_VOLUME				: 'data-volume',
	DATA_FALLBACK			: 'data-fallback',
	WIDTH					: 'width',
	HEIGHT					: 'height',
	CONTROLS				: 'controls'
};



function callAnimationFrame(callback, index)
{
	if(isDefined(animationFrame))
	{
		if(isDefined(index))
		{
			callCancelAnimationFrame(index);
			
			var id = animationFrame(function()
			{				
				animationFrameCollection[index] = null;
				callback.call();
			});
			animationFrameCollection[index] = id;
			return id;
		}
		else
		{
			return animationFrame(callback);
		}
	}
	else
	{
		return callback.call();
	}
}

function callCancelAnimationFrame(index)
{
	if(index && isDefined(cancelAnimationFrame))
	{
		var id = animationFrameCollection[index];
		if(id)
		{
			cancelAnimationFrame(id);
		}
	}
}



function getAudioTypeBySource(source)
{
	var ext = getFileExtension(source);
	var type;
	
	if(ext)
	{
		if(ext.match(/m(p3|3a)/i))								type = 'mpeg';
		else if(ext.match(/m4a|mp4/i))							type = 'mp4';
		else if(ext.match(/webm/i))								type = 'webm';
		else if(ext.match(/wav/i))								type = 'wav';
		else if(ext.match(/og[ga]/i))							type = 'ogg';
		//else													type = 'mpeg';
	}
	
	return isNull(type) ? "" : 'audio/'+type;
}

function getFileExtension(filename)
{
	var ext = /^.+\.([^.]+)$/.exec(filename);
	return isNull(ext) ? null : ext[1];
}


/**
 * Get a string formatted as time.
 * @param {number} time.
 * @param {number=} opt_duration (optional).
 * @return {string}
 */
function getCurrentTime(time, opt_duration)
{
	var t = parseInt(time, 10),
		h = Math.floor(t / 3600),
		m = Math.floor(t % 3600 / 60),
		s = Math.floor(t % 3600 % 60);
	
	if(opt_duration)
	{
		var d = parseInt(opt_duration, 10),
			dh = Math.floor(d / 3600);
		
		return ((h > 0 ? (dh < 10 || h > 9 ? h + ":" : "0" + h + ":") : (dh > 0 ? (dh < 10 ? "0:" : "00:") : "")) + (m > 0 ? (m < 10 ? "0" : "") + m + ":" : "00:") + (s < 10 ? "0" : "") + s);
	}
	else
		return ((h > 0 ? h + ":" : "") + (m > 0 ? (m < 10 ? "0" : "") + m + ":" : "00:") + (s < 10 ? "0" : "") + s);
}


/**
 * keepInBound
 * @param {number} value.
 * @param {number=} max (optional).
 * @param {number=} min (optional).
 * @return {number}
 */
function keepInBound(value, max, min)
{
	var number = Number(value);
	
	max = Number(max) || 1;
	min = Number(min) || 0;
	
	return number >= max ? max : (number <= min ? min : number);
}

/**
 * pauseAllAudioExcept
 * @param {AudioPlayer} element.
 */
function pauseAllAudioExcept(element)
{
	each(collection, function()
	{
		if(element != this && !this.paused())
		{
			this.pause();
		}
	});
}


function getOffsetLeft(object)
{
	var x = 0;
	var element = object['get'](0);
	
	while(element && !isNaN(element.offsetLeft))
	{
		x += element.offsetLeft;
		element = element.offsetParent;
	}
	
	return x;
}

function roundNumber(value)
{
	return Math.round(Number(value)*10)/10;
}



function createCookie(name,value,days)
{
	if(days)
	{
		var date = new Date();
			date.setTime( date.getTime() + (days*86400000) );
			
		var expires = '; expires=' + date.toGMTString();
	}
	else var expires = '';
	
	doc.cookie = name + "=" + value+expires + "; path=/";
}

function readCookie(name)
{
	var nameEQ = name + "=";
	var ca = doc.cookie.split(';');
	
	for(var i=0;i < ca.length;i++)
	{
		var c = ca[i];
		
		while (c.charAt(0)==' ')
			c = c.substring(1,c.length);
		
		if (c.indexOf(nameEQ) == 0)
			return c.substring(nameEQ.length,c.length);
	}
	return null;
}

function setVolumeState(volume)
{		
	if(isDefined(win.localStorage))
	{
		try { localStorage.setItem(AUDIO_PLAYER_ID, volume); }
		catch(e){ createCookie(AUDIO_PLAYER_ID, volume, 30); }
	}
	else
		createCookie(AUDIO_PLAYER_ID, volume, 30);
}

function getVolumeState()
{
	var volume;
	if(isDefined(win.localStorage))
	{
		try { volume = localStorage.getItem(AUDIO_PLAYER_ID); }
		catch(e){}
	}
	
	if(!isDefined(volume) || isNull(volume)) volume = readCookie(AUDIO_PLAYER_ID);
			
	return isDefined(volume) && !isNull(volume) ? volume : 1;
}





function after(element, html)
{
	element['after'](html);
}

function remove(element)
{
	return $(element)['remove']();
}

/**
 * Get or set a attribute.
 * @param {object} element.
 * @param {string} style.
 * @param {string=} opt_value (optional).
 * @return {string}
 */
function css(element, style, opt_value)
{
	if(opt_value)
		return element['css'](style, opt_value);
	else
		return element['css'](style);
}

/**
 * Get or set an attribute.
 * @param {object} element.
 * @param {string} attribute.
 * @param {string=} opt_value (optional).
 * @return {string}
 */
function attr(element, attribute, opt_value)
{
	return element['attr'](attribute, opt_value);
}

function removeAttr(element, attribute)
{
	return element['removeAttr'](attribute);
}

function text(element, value)
{
	return element['text'](value);
}

function addClass(element, className)
{
	element['addClass'](className);
}

function removeClass(element, className)
{
	element['removeClass'](className);
}

/**
 * Bind listeners to an element.
 * @param {Object} element.
 * @param {string} eventName.
 * @param {Function|Object} fn.
 */
function bind(element, eventName, fn)
{
	element['bind'](eventName, fn);
}

/**
 * Unbind listeners to an element.
 * @param {Object} element.
 * @param {string=} opt_eventName (optional).
 * @param {Function|Object=} opt_fn (optional).
 */
function unbind(element, opt_eventName, opt_fn)
{
	element['unbind'](opt_eventName, opt_fn);
}

function find(element, selector)
{
	return element['find'](selector);
}

function each(object, callback)
{
	return $(object)['each'](callback);
}

function is(element, selector)
{
	return element['is'](selector);
}

function proxy(fn, object)
{
	return $['proxy'](fn, object);
}

/**
 * Get or set the width of an element.
 * @param {object} element.
 * @param {number|string=} opt_value (optional).
 * @return {number|string}
 */
function width(element, opt_value)
{
	return element['width'](opt_value);
}

/**
 * Get or set the height of an element.
 * @param {object} element.
 * @param {number|string=} opt_value (optional).
 * @return {number|string}
 */
function height(element, opt_value)
{
	return element['height'](opt_value);
}




function isFunction(val)
{
	return typeof val === 'function';
}

function isDefined(val)
{
	return typeof val !== 'undefined';
}

function isNull(val)
{
	return val == null;
}

function isNaN(val)
{
	return !(val >= 0 || val < 0);
}

function inherits(childCtor, parentCtor)
{
	/** @constructor */
	function tempCtor() {};
	tempCtor.prototype = parentCtor.prototype;
	childCtor.superClass_ = parentCtor.prototype;
	childCtor.prototype = new tempCtor();
	childCtor.prototype.constructor = childCtor;
}











/**
 * Event target object
 * @override
 * @constructor
 */
function EventTargetController()
{
	/** @private */
	this.events_ = [];
}

EventTargetController.prototype.addEventListener = function(type, callback)
{
	this.events_[type] = this.events_[type] || [];
	this.events_[type].push(callback);
};

EventTargetController.prototype.removeEventListener = function(type, callback)
{
	var listeners = this.events_[type];
	
	if(listeners)
	{
		each(listeners, function(i)
		{
			if(this === callback)
			{
				listeners.splice(i,1);
			}
		});
	}
};

EventTargetController.prototype.hasEventListener = function(type)
{
	return Boolean(this.events_[type] && this.events_[type].length > 0);
};

/**
 * dispatchEvent
 * @param {string} type.
 */
EventTargetController.prototype.dispatchEvent = function(type)
{
	var listeners = this.events_[type];

	if(listeners)
	{
		var self = this;
		each(listeners, function()
		{
			this.call(self, type);
		});		
	}
};

EventTargetController.prototype.destroy = function()
{
	delete this.events_;
};






/**
 * The media controller class.
 * @param {Object} object
 * @param {Object} element
 * @extends EventTargetController
 * @constructor
 */
function MediaController(object, element)
{
	EventTargetController.call(this);
	
	this.element_	= element;
	this.object_	= object;
	
	this.available 	= this.hasProperty('canPlayType');
	
	if(!this.available)return;
	
	this.hackTimerInterval_		= 1000/10;
	this.hackTimerId_			= null;
	
	this.isBindEvents_		= false;
	this.isUnloading_		= false;
	
	this.started		= false;
	this.canplay		= false;
	
	this.currentTime_	= 0;
	this.progress_		= 0;
	this.duration_		= 0;
	
	this.bindEvents_();
	
	if(element['playing'])this.playingHandler__();
}

inherits(MediaController, EventTargetController);

MediaController.prototype.bindEvents_ = function()
{
	if(this.isBindEvents_)return;
	
	this.isBindEvents_ = true;
	
	bind(this.object_, MediaEvent.CAN_PLAY,			proxy(this.canplayHandler__,		this));
	
	bind(this.object_, MediaEvent.PLAYING,			proxy(this.playingHandler__,		this));
	bind(this.object_, MediaEvent.PLAY,				proxy(this.playingHandler__,		this));
	
	bind(this.object_, MediaEvent.PAUSE,			proxy(this.pauseHandler__,			this));
	bind(this.object_, MediaEvent.ENDED,			proxy(this.endedHandler__,			this));
	//bind(this.object_, MediaEvent.WAITING,			proxy(this.waitingHandler__,		this));

	bind(this.object_, MediaEvent.TIME_UPDATE,		proxy(this.timeupdateHandler__,		this));
	bind(this.object_, MediaEvent.PROGRESS,			proxy(this.progressHandler__,		this));

	bind(this.object_, MediaEvent.DURATION_CHANGE,	proxy(this.durationchangeHandler__, this));
	bind(this.object_, MediaEvent.VOLUME_CHANGE,	proxy(this.volumechangeHandler__,	this));
	//bind(this.object_, MediaEvent.RATE_CHANGE,		proxy(this.ratechangeHandler__,		this));
	
	bind(this.object_, MediaEvent.ERROR,			proxy(this.errorHandler__,			this));
};

MediaController.prototype.unbindEvents_ = function()
{
	if(!this.isBindEvents_)return;
	
	this.isBindEvents_ = false;
	
	unbind(this.object_, MediaEvent.CAN_PLAY,			this.canplayHandler__);
	
	unbind(this.object_, MediaEvent.PLAYING,			this.playingHandler__);
	unbind(this.object_, MediaEvent.PLAY,				this.playingHandler__);
	
	unbind(this.object_, MediaEvent.PAUSE,				this.pauseHandler__);
	unbind(this.object_, MediaEvent.ENDED,				this.endedHandler__);
	//unbind(this.object_, MediaEvent.WAITING,			this.waitingHandler__);

	unbind(this.object_, MediaEvent.TIME_UPDATE,		this.timeupdateHandler__);
	unbind(this.object_, MediaEvent.PROGRESS,			this.progressHandler__);

	unbind(this.object_, MediaEvent.DURATION_CHANGE,	this.durationchangeHandler__);
	unbind(this.object_, MediaEvent.VOLUME_CHANGE,		this.volumechangeHandler__);
	//unbind(this.object_, MediaEvent.RATE_CHANGE,		this.ratechangeHandler__);

	unbind(this.object_, MediaEvent.ERROR,				this.errorHandler__);
};

MediaController.prototype.destroy = function()
{
	this.unbindEvents_();
};

MediaController.prototype.reset_ = function()
{
	this.canplay		= false;
	this.started		= false;
	
	this.currentTime_	= 0;
	this.progress_		= 0;
	this.duration_		= 0;
};

MediaController.prototype.hasPlayableSource_ = function()
{
	var sources 	= find(this.object_, 'source');
	var self 		= this;
	var newSource;

	each(sources, function(i)
	{
		var src		= attr($(this), 'src');
		var type	= getAudioTypeBySource(src);
		var canplay	= self.canPlayType(type);
							
		if(canplay == 'maybe' || canplay == 'probably')
		{
			newSource = src;
			return false;
		}
	});
	
	return newSource || false;
};

MediaController.prototype.hasProperty = function(property)
{
	return isDefined(this.element_[property]);
};

MediaController.prototype.canPlayType = function(type)
{
	return this.element_['canPlayType'](type);
};

MediaController.prototype.autoplay = function()
{
	return this.element_['autoplay'];
};

MediaController.prototype.setAutoplay = function(value)
{
	this.element_['autoplay'] = value;
};

MediaController.prototype.loop = function()
{
	return this.element_['loop'] || is(this.object_, '[loop]');
};

MediaController.prototype.setLoop = function(value)
{
	this.element_['loop'] = value;
};

MediaController.prototype.preload = function()
{
	return this.element_['preload'];
};

MediaController.prototype.setPreload = function(value)
{
	this.element_['preload'] = value;
};

MediaController.prototype.duration = function()
{
	return this.duration_ || this.element_['duration'];
}

MediaController.prototype.ended = function()
{
	return this.element_['ended'];
}

MediaController.prototype.paused = function()
{
	return this.element_['paused'];
}

MediaController.prototype.currentSrc = function()
{
	return this.currentSource_ || this.element_['currentSrc'];
};

MediaController.prototype.src = function()
{
	return this.element_['src'];
};

MediaController.prototype.setSrc = function(source)
{
	this.element_['src'] = source;
	this.currentSource_ = source;
};

MediaController.prototype.volume = function()
{
	return this.element_['volume'];
};

MediaController.prototype.setVolume = function(value)
{
	this.element_['volume'] = keepInBound(value);
};

MediaController.prototype.progress = function()
{
	return this.progress_;
};

MediaController.prototype.currentTime = function()
{
	return this.currentTime_ || this.element_['currentTime'];
};

MediaController.prototype.setCurrentTime = function(value)
{
	if(this.element_['currentTime'] != value)
	{
		this.element_['currentTime'] = keepInBound(value, this.duration());
	}
};

/**
 * start playing the media.
 * @param {Object=} opt_element (optional).
 */
MediaController.prototype.play = function(opt_element)
{
	if(isDefined(opt_element))
	{
		var element;
		if(typeof opt_element === 'object')
			element = opt_element;
		else if(typeof opt_element === 'string')
			element = win['getElementById'](opt_element);
		
		if(element && element.tagName.toLowerCase() == 'audio')
			this.load(element['currentSrc'] || element['src']);
	}
	
	this.element_['play']();
};

MediaController.prototype.pause = function()
{
	this.element_['pause']();
};

MediaController.prototype.load = function(opt_source)
{
	var src = opt_source || this.currentSource_;
	
	if(src)
	{
		this.setSrc(src);
	}
	
	this.reset_();
	this.bindEvents_();
	
	this.element_['load']();
};

MediaController.prototype.unload = function()
{	
	this.isUnloading_ = true;
	
	this.pause();
	this.setCurrentTime(0);
	
	this.unbindEvents_();
	this.reset_();
	
	this.element_['src'] = '';
	this.element_['load']();
};





MediaController.prototype.startTimer_ = function()
{
	if(!this.hackTimerId_)
	{
		//this.hackTimerId_ = requestInterval(proxy(this.timerHandler_, this), this.hackTimerInterval_);
		this.hackTimerId_ = setInterval(proxy(this.timerHandler_, this), this.hackTimerInterval_);
		
		this.timerHandler_();
	}
};

MediaController.prototype.stopTimer_ = function()
{
	if(this.hackTimerId_)
	{
		//clearRequestInterval(this.hackTimerId_);
		clearInterval(this.hackTimerId_);
		this.hackTimerId_ = null;
	}	
};

MediaController.prototype.timerHandler_ = function()
{
	this.timeupdateHandler__();
	this.progressHandler__();
	this.canplayHandler__();
	this.startedHandler__();
};






/**
 * progress handler
 * @param {Object=} e (optional).
 */
MediaController.prototype.progressHandler__ = function(e)
{	
	if(this.hasEventListener(MediaEvent.PROGRESS))
	{
		var buffered = this.element_['buffered'];
		var duration = this.element_['duration'];

		if(buffered && duration)
		{
			var length = buffered.length;
			if(length)
			{
				var diff = duration;
				var index = length - 1;
				var number = this.element_['currentTime'];

				for(var i=0; i<length; i++)
				{
					var buffer = buffered['end'](i);
					if(buffer >= number && buffer < diff)
					{
						diff = buffer;
						index = i;
					}
				}
				
				var progress = parseInt(buffered['end'](index) / duration * 100, 10);
				
				if(this.progress_ != progress)
				{
					this.progress_ = progress;
					this.dispatchEvent(MediaEvent.PROGRESS);
				}
			}
		}
		else if(e)
		{
			var originalEvent	= e['originalEvent'];
			var bytesLoaded		= originalEvent['loaded'];
			var bytesTotal		= originalEvent['total'];

			if(bytesLoaded && bytesTotal)
			{
				var progress = parseInt(bytesLoaded / bytesTotal * 100, 10);
				
				if(this.progress_ != progress)
				{
					this.progress_ = progress;
					this.dispatchEvent(MediaEvent.PROGRESS);
				}	
			}
		}
	}
};


MediaController.prototype.timeupdateHandler__ = function()
{
	var time = this.element_['currentTime'];
	if(this.currentTime_ != time)
	{
		this.currentTime_ = time;
		this.dispatchEvent(MediaEvent.TIME_UPDATE);
	}
};

MediaController.prototype.durationchangeHandler__ = function()
{
	var duration = this.element_['duration'];
	if(this.duration_ != duration)
	{
		this.duration_ = duration;
		this.dispatchEvent(MediaEvent.DURATION_CHANGE);
	}
};

MediaController.prototype.ratechangeHandler__ = function()
{
	this.dispatchEvent(MediaEvent.RATE_CHANGE);
};

MediaController.prototype.volumechangeHandler__ = function()
{
	this.dispatchEvent(MediaEvent.VOLUME_CHANGE);
};

MediaController.prototype.waitingHandler__ = function()
{
	this.dispatchEvent(MediaEvent.WAITING);
};

/**
 * canplay handler
 * @param {Object=} e (optional).
 */
MediaController.prototype.canplayHandler__ = function(e)
{
	if(this.hasEventListener(MediaEvent.CAN_PLAY))
	{
		if(!this.canplay)
		{			
			if(isDefined(e) || !isNaN(this.element_['duration']))
			{
				this.canplay = true;
				this.dispatchEvent(MediaEvent.CAN_PLAY);
			}
		}
	}
};

/**
 * started handler
 * @param {Object=} e (optional).
 */
MediaController.prototype.startedHandler__ = function(e)
{
	if(!this.started)
	{	
		if(isDefined(e) || this.currentTime()>0)
		{
			this.started = true;
			this.dispatchEvent(MediaEvent.STARTED);
		}
	}
};

MediaController.prototype.playingHandler__ = function()
{
	this.startTimer_();
	this.canplayHandler__();
	this.startedHandler__();
	this.dispatchEvent(MediaEvent.PLAYING);
};

MediaController.prototype.pauseHandler__ = function()
{
	this.stopTimer_();
	this.dispatchEvent(MediaEvent.PAUSE);
};

MediaController.prototype.endedHandler__ = function()
{
	this.stopTimer_();
	this.started = false;
	this.dispatchEvent(MediaEvent.ENDED);
};

MediaController.prototype.errorHandler__ = function()
{
	this.stopTimer_();
	
	if(this.isUnloading_)
	{
		this.isUnloading_ = false;
	}
	else
	{
		this.dispatchEvent(MediaEvent.ERROR);
	}
};



/**
 * The audio controller class.
 * @param {Object} object
 * @param {Object} element
 * @extends MediaController
 * @constructor
 */
function AudioPlayer(object, element)
{
	MediaController.call(this, object, element);
	
	//if(!is(object, 'audio'))return;
	
	if(!this.available && !this.hasBooleanAttribute_(Attributes.DATA_FALLBACK))return;
		
	this.identifier_		= initCount++;
	this.offset_			= 0;
	this.time_				= 0;
	this.isSeekingTimeline_	= false;
	this.isSeekingVolume_	= false;
	this.isMuted_			= false;
	//var canplay = false;
	
	var attrWidth = attr(object, Attributes.WIDTH);
	var attrHeight = attr(object, Attributes.HEIGHT);
	
	this.playerWidth_		= attrWidth || $['fn'][AUDIO_PLAYER_ID]['defaultOptions'][Attributes.WIDTH];
	this.playerHeight_		= attrHeight || $['fn'][AUDIO_PLAYER_ID]['defaultOptions'][Attributes.HEIGHT];
	
	var controlsDisplay = this.controlsDisplay_ = $(TEMPLATE);
						
	this.playButton_				= find(controlsDisplay, '.APV1_play_button');
	this.timeTextDisplay_			= find(controlsDisplay, '.APV1_time_text');
	this.containerDisplay_			= find(controlsDisplay, '.APV1_container');
	this.progressContainerDisplay_	= find(controlsDisplay, '.APV1_progress_bar_container');
	this.progressWrapperDisplay_	= find(controlsDisplay, '.APV1_progress_bar_wrapper');
	this.seekBarDisplay_			= find(controlsDisplay, '.APV1_seek_bar');
	this.playBarDisplay_			= find(controlsDisplay, '.APV1_play_bar');
	this.durationTextDisplay_		= find(controlsDisplay, '.APV1_duration_text');
	this.volumeButton_				= find(controlsDisplay, '.APV1_volume_button');
	this.volumeContainerDisplay_	= find(controlsDisplay, '.APV1_volume_bar_container');
	this.volumeBarDisplay_			= find(controlsDisplay, '.APV1_volume_bar');
	
	width(controlsDisplay, this.playerWidth_);
	height(controlsDisplay, this.playerHeight_);
	
	this.bindControllerEvents_();
	
	
	this.checkAttributes_();

	
	after(object, 		controlsDisplay);
	
	var cssObject					= {};
	cssObject[Attributes.HEIGHT]	= Values.ZERO;
	cssObject[Attributes.WIDTH]		= Values.ZERO;	
	
	css(object, 		cssObject);
	removeAttr(object,	Attributes.CONTROLS);

	if(!this.checkForAvailability_())return;

	this.setContainerWidth_();	
	
	this.bindAudioEvents_();
	

	var vol = (
		(keepInBound(attr(object, Attributes.DATA_VOLUME), 100) / 100) ||
		getVolumeState() ||
		($['fn'][AUDIO_PLAYER_ID]['defaultOptions'][Attributes.DATA_VOLUME] / 100)
	);
	if(isDefined(vol)) this.setVolume(vol);
	else vol = this.volume();
	
	this.setVolumeView_(vol);
	

	this.bindStartingEvents_();
	this.bindVolumeEvents_();
	
		
	collection[this.identifier_] = this;
}

inherits(AudioPlayer, MediaController);


AudioPlayer.prototype.destroy = function()
{
	this.unbindAudioEvents_();
	this.unbindControllerEvents_();
	this.unbindVolumeEvents_();
	this.unbindStartingEvents_();
	this.unbindPlayEvents_();
	
	remove(this.controlsDisplay_);
	
	collection.splice(this.identifier_, 1);
	
	AudioPlayer.superClass_.destroy.call(this);
};

AudioPlayer.prototype.bindControllerEvents_ = function()
{
	bind(this.controlsDisplay_, MouseEvent.MOUSE_DOWN,		false);
	bind(this.controlsDisplay_, MouseEvent.CONTEXT_MENU,	false);
};

AudioPlayer.prototype.unbindControllerEvents_ = function()
{
	unbind(this.controlsDisplay_, MouseEvent.MOUSE_DOWN);
	unbind(this.controlsDisplay_, MouseEvent.CONTEXT_MENU);
};

AudioPlayer.prototype.bindPlayEvents_ = function()
{
	bind(this.progressWrapperDisplay_,	MouseEvent.MOUSE_DOWN,	proxy(this.onSeekDownHandler_, this));	
	bind(this.playButton_,				MouseEvent.CLICK,		proxy(this.toggleAudio_, this));
};

AudioPlayer.prototype.unbindPlayEvents_ = function()
{
	unbind(this.progressWrapperDisplay_,	MouseEvent.MOUSE_DOWN,	this.onSeekDownHandler_);	
	unbind(this.playButton_,				MouseEvent.CLICK,		this.toggleAudio_);
};

AudioPlayer.prototype.bindStartingEvents_ = function()
{
	bind(this.playButton_, MouseEvent.CLICK, proxy(this.startAudio_, this));
};

AudioPlayer.prototype.unbindStartingEvents_ = function()
{
	unbind(this.playButton_, MouseEvent.CLICK, this.startAudio_);
};

AudioPlayer.prototype.bindVolumeEvents_ = function()
{
	bind(this.volumeContainerDisplay_,	MouseEvent.MOUSE_DOWN,	proxy(this.onVolumeDownHandler_,this));
	bind(this.volumeButton_,			MouseEvent.CLICK,		proxy(this.toggleVolume_,this));
};

AudioPlayer.prototype.unbindVolumeEvents_ = function()
{
	unbind(this.volumeContainerDisplay_,	MouseEvent.MOUSE_DOWN,	this.onVolumeDownHandler_);
	unbind(this.volumeButton_,				MouseEvent.CLICK,		this.toggleVolume_);
};

AudioPlayer.prototype.bindAudioEvents_ = function()
{	
	this.addEventListener(MediaEvent.PROGRESS,			this.progressHandler_);
	this.addEventListener(MediaEvent.TIME_UPDATE,		this.timeupdateHandler_);
	this.addEventListener(MediaEvent.CAN_PLAY,			this.canplayHandler_);
	this.addEventListener(MediaEvent.DURATION_CHANGE,	this.durationchangeHandler_);
	this.addEventListener(MediaEvent.PLAYING,			this.playHandler_);
	this.addEventListener(MediaEvent.PAUSE,				this.pauseHandler_);
	this.addEventListener(MediaEvent.ENDED,				this.endedHandler_);
	this.addEventListener(MediaEvent.ERROR,				this.errorHandler_);
	this.addEventListener(MediaEvent.VOLUME_CHANGE,		this.volumechangeHandler_);
};

AudioPlayer.prototype.unbindAudioEvents_ = function()
{
	this.removeEventListener(MediaEvent.PROGRESS,			this.progressHandler_);
	this.removeEventListener(MediaEvent.TIME_UPDATE,		this.timeupdateHandler_);
	this.removeEventListener(MediaEvent.CAN_PLAY,			this.canplayHandler_);
	this.removeEventListener(MediaEvent.DURATION_CHANGE,	this.durationchangeHandler_);
	this.removeEventListener(MediaEvent.PLAYING,			this.playHandler_);
	this.removeEventListener(MediaEvent.PAUSE,				this.pauseHandler_);
	this.removeEventListener(MediaEvent.ENDED,				this.endedHandler_);
	this.removeEventListener(MediaEvent.ERROR,				this.errorHandler_);
	this.removeEventListener(MediaEvent.VOLUME_CHANGE,		this.volumechangeHandler_);
};

AudioPlayer.prototype.checkAttributes_ = function()
{				
	if(!this.hasBooleanAttribute_(Attributes.DATA_CONTROLS_TIME))
	{					
		attr(this.timeTextDisplay_, Values.STYLE, Values.DISPLAY_NONE);
		
		var obj = find(this.controlsDisplay_, '.APV1_for_time');
		attr(obj, Values.STYLE, Values.DISPLAY_NONE);
	}

	if(!this.hasBooleanAttribute_(Attributes.DATA_CONTROLS_DURATION))
	{
		attr(this.durationTextDisplay_, Values.STYLE, Values.DISPLAY_NONE);
		
		var obj = find(this.controlsDisplay_, '.APV1_for_duration');
		attr(obj, Values.STYLE, Values.DISPLAY_NONE);
	}

	if(!this.hasBooleanAttribute_(Attributes.DATA_CONTROLS_VOLUME))
	{
		attr(this.volumeButton_,			Values.STYLE, Values.DISPLAY_NONE);
		attr(this.volumeContainerDisplay_,	Values.STYLE, Values.DISPLAY_NONE);
		
		var obj = find(this.controlsDisplay_, '.APV1_for_volume');
		attr(obj, Values.STYLE, Values.DISPLAY_NONE);
	}
};

AudioPlayer.prototype.hasBooleanAttribute_ = function(attributeName)
{
	var val = attr(this.object_, attributeName);
	
	if(val)
	{
		if(val == 'false' || val == 'off' || val == 'no')
			return false;
			
		else if(val == 'true' || val == 'on' || val == 'yes')
			return true;
	}
	
	return $['fn'][AUDIO_PLAYER_ID]['defaultOptions'][attributeName];
};

AudioPlayer.prototype.setContainerWidth_ = function()
{	
	var widthMin = 0;
	var obj = find(this.controlsDisplay_, 'li:not(.APV1_container)');
	
	each(obj, function()
	{
		widthMin += width($(this));
	});
					
	if(widthMin > this.playerWidth_)
	{
		$win.load(proxy(this.setContainerWidth_, this));
		return;
	}
	
	width(this.containerDisplay_, this.playerWidth_ - widthMin);
}; 

AudioPlayer.prototype.checkForAvailability_ = function()
{
	if(!this.available)
	{
		this.reportError_('Please upgrade your browser.');
		return;
	}
	else if(this.currentSrc())
	{
		return true;
	}
	else if(!this.hasPlayableSource_())
	{
		this.reportError_('Audio file not found.');
	}
	
	return true;
};

AudioPlayer.prototype.canplayHandler_ = function()
{	
	this.unbindStartingEvents_();
	this.bindPlayEvents_();
};

AudioPlayer.prototype.startAudio_ = function()
{
	this.setPreload('none');
	this.play();
	
	this.unbindStartingEvents_();
};

AudioPlayer.prototype.toggleAudio_ = function()
{	
	if(this.paused() || this.ended()) 
	{
		this.play();
		addClass(this.controlsDisplay_, Classes.PLAYING);
	}
	else 
	{
		this.pause();
		removeClass(this.controlsDisplay_, Classes.PLAYING);
	}
};

AudioPlayer.prototype.toggleVolume_ = function()
{
	if(this.volume()) this.setVolume(0);
	else this.setVolume(1);
};

AudioPlayer.prototype.onVolumeDownHandler_ = function(e)
{
	if(e.which != 1)return;
	
	this.isSeekingVolume_ = true;
	
	this.offset_ = getOffsetLeft(this.volumeContainerDisplay_);
	
	var pos = (e.pageX - this.offset_) / width(this.volumeContainerDisplay_);						
	this.setVolume(pos);
	this.setVolumeView_(pos);

	bind($win, MouseEvent.MOUSE_MOVE,	proxy(this.onVolumeMoveHandler_, this));
	bind($win, MouseEvent.MOUSE_UP,		proxy(this.onVolumeUpHandler_, this));
	
	return false;
}

AudioPlayer.prototype.onVolumeUpHandler_ = function()
{
	this.isSeekingVolume_ = false;

	setVolumeState(this.volume());
		
	unbind($win, MouseEvent.MOUSE_MOVE,	this.onVolumeMoveHandler_);
	unbind($win, MouseEvent.MOUSE_UP,	this.onVolumeUpHandler_);
};

AudioPlayer.prototype.onVolumeMoveHandler_ = function(e)
{
	var pos = keepInBound((e.pageX - this.offset_) / width(this.volumeContainerDisplay_));
						
	this.setVolume(pos);
	this.setVolumeView_(pos);
};

AudioPlayer.prototype.setVolumeView_ = function(volume)
{		
	width(this.volumeBarDisplay_, volume * 100 + '%');

	if(volume)
	{
		if(this.isMuted_)
		{
			this.isMuted_ = false;
			removeClass(this.controlsDisplay_, Classes.MUTE);
		}
	}
	else if(!this.isMuted_)
	{
		this.isMuted_ = true;
		addClass(this.controlsDisplay_, Classes.MUTE);
	}
};

AudioPlayer.prototype.onSeekDownHandler_ = function(e)
{
	if(e.which != 1)return;
	
	this.isSeekingTimeline_ = true;
	this.offset_ = getOffsetLeft(this.seekBarDisplay_);
		
	var pos = keepInBound((e.pageX - this.offset_) / width(this.progressContainerDisplay_));
	var duration = this.duration();
	var time = duration * pos;
	this.time_ = time;
		
	removeClass(this.playBarDisplay_, Classes.TRANSITION);
	width(this.playBarDisplay_, pos * 100 + '%');
	
	text(this.timeTextDisplay_, getCurrentTime(time, duration));
	
	bind($win, MouseEvent.MOUSE_MOVE,	proxy(this.onSeekMoveHandler_, this));
	bind($win, MouseEvent.MOUSE_UP,		proxy(this.onSeekUpHandler_, this));

	this.pause();	
	
	return false;
};

AudioPlayer.prototype.onSeekUpHandler_ = function()
{
	addClass(this.playBarDisplay_,	Classes.TRANSITION);
	
	unbind($win, MouseEvent.MOUSE_MOVE,	this.onSeekMoveHandler_);
	unbind($win, MouseEvent.MOUSE_UP,	this.onSeekUpHandler_);
	
	this.setCurrentTime(this.time_);
	this.play();
	
	this.isSeekingTimeline_ = false;
};

AudioPlayer.prototype.onSeekMoveHandler_ = function(e)
{
	var pos = keepInBound((e.pageX - this.offset_) / width(this.progressContainerDisplay_));
	
	var duration = this.duration();
	var time = duration * pos;
	this.time_ = time;
	
	width(this.playBarDisplay_, pos * 100 + '%');
	text(this.timeTextDisplay_, getCurrentTime(time, duration));
};

AudioPlayer.prototype.timeupdateHandler_ = function()
{
	var self = this;
	
	if(self.isSeekingTimeline_)return;

	var duration	= self.duration();
	var time		= self.currentTime();
	var pos			= keepInBound(time / duration);
	
	callAnimationFrame(function()
	{
		width(self.playBarDisplay_, pos * 100 + '%');
		text(self.timeTextDisplay_,	getCurrentTime(time, duration));
	}, '1');
};

AudioPlayer.prototype.progressHandler_ = function()
{
	var self = this;
	callAnimationFrame(function()
	{
		width(self.seekBarDisplay_, self.progress() + '%');
	}, '2');
};

AudioPlayer.prototype.volumechangeHandler_ = function()
{
	if(!this.isSeekingVolume_)
	{
		var vol = roundNumber(this.volume());

		setVolumeState(vol);
		this.setVolumeView_(vol);
	}
};

AudioPlayer.prototype.durationchangeHandler_ = function()
{
	text(this.durationTextDisplay_, getCurrentTime(this.duration()));
};

AudioPlayer.prototype.playHandler_ = function()
{
	if(!this.isSeekingTimeline_)
	{						
		pauseAllAudioExcept(this);
		addClass(this.controlsDisplay_, Classes.PLAYING);
	}
};

AudioPlayer.prototype.pauseHandler_ = function()
{
	if(!this.isSeekingTimeline_)
	{
		removeClass(this.controlsDisplay_, Classes.PLAYING);
	}
};

AudioPlayer.prototype.endedHandler_ = function()
{	
	if(!this.isSeekingTimeline_)
	{
		removeClass(this.controlsDisplay_, Classes.PLAYING);
		
		if(this.loop())
		{
			this.setCurrentTime(0);
			this.play();
		}
	}
};

AudioPlayer.prototype.errorHandler_ = function()
{
	this.reportError_('An error occurred.');
};

AudioPlayer.prototype.reportError_ = function(msg)
{
	this.unbindAudioEvents_();
	this.unbindControllerEvents_();
	this.unbindVolumeEvents_();
	this.unbindStartingEvents_();
	this.unbindPlayEvents_();
	
	attr(this.timeTextDisplay_, Values.STYLE, Values.DISPLAY_BLOCK);
	text(this.timeTextDisplay_, msg);
	
	addClass(this.controlsDisplay_, Classes.ERROR);
};





$(function()
{
	$('.'+AUDIO_PLAYER_ID)[AUDIO_PLAYER_ID]();
});


$['widget']('ui.' + AUDIO_PLAYER_ID,
{
	'widgetEventPrefix': 'audioplayer.',
	
	'_create': function()
	{
		var self = this;
		var data = self['element'];
		var element = data[0];
		var object = $(element);
				
		if(is(object, 'audio'))
		{
			data.player_ = new AudioPlayer(object, element);
			
			data.dispatcher_ = function(e)
			{ self['_trigger'](e); }
			
			data.player_.addEventListener(MediaEvent.STARTED,	data.dispatcher_);
			data.player_.addEventListener(MediaEvent.ENDED,		data.dispatcher_);
		}	
	},

	'destroy': function()
	{
		var data = this['element'];
			
			data.player_.removeEventListener(MediaEvent.STARTED,	data.dispatcher_);
			data.player_.removeEventListener(MediaEvent.ENDED,		data.dispatcher_);
			
			delete data.dispatcher_;
			data.dispatcher_ = null;
			
			data.player_.destroy();
			
			delete data.player_;
			data.player_ = null;
	},
	
	'play': function(opt_audioElement)
	{
		var data = this['element'];
			data.player_.play(opt_audioElement);			
	},
	
	'pause': function()
	{
		var data = this['element'];
			data.player_.pause();		
	},
	
	'volume': function(value)
	{
		var data = this['element'];
		
		if(value)
		{
			var volume = parseInt(value, 10) / 100;
			data.player_.setVolume(volume);
		}
		else
			return data.player_.volume();
	}
});

var defaultOptions = {}
	defaultOptions[Attributes.WIDTH]					= 220;
	defaultOptions[Attributes.HEIGHT]					= 29;
	defaultOptions[Attributes.DATA_CONTROLS_TIME]		= true;
	defaultOptions[Attributes.DATA_CONTROLS_VOLUME]		= true;
	defaultOptions[Attributes.DATA_CONTROLS_DURATION]	= false;
	defaultOptions[Attributes.DATA_VOLUME]				= 100;
	
$['fn'][AUDIO_PLAYER_ID]['defaultOptions'] = defaultOptions;

})(jQuery);
