/*
 * HTML5 Video Player V1
 * http://codecanyon.net/item/html5-video-player-v1/758636
 *
 * Author: Ivan da Silveira
 * Version: 1.0.5
 * Copyright 2011
 *
 * Only for the sale at the envato marketplaces
 *
 * Depends:
 *  jquery.js
 *  jquery.ui.widget.js
 */



/** @define {boolean} */ 
var DEBUG = false;

(function($)
{
	
// global variables
//var parent						= window.parent;
var win							= window;
var $win						= $(win);
var doc							= document;
var nav							= navigator;
var initCount					= 0;
var collection					= [];
var animationFrameCollection	= [];
var bodyElement;
var transformProperty;
var animationProperty;

var isMobile	= nav['appVersion']['indexOf']("Mobile") > -1;
var isIOS		= nav['userAgent']['match'](/(iPod|iPhone|iPad)/);
var simpleView	= isMobile || isIOS;

var VIDEO_PLAYER_ID		= 'VideoPlayerV1';
var PLAYBACK_VALUES		= [-8, -4, -2, 1, 2, 4, 8];


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

/**
 * Enum for keyboard event values.
 * @enum {string}
 */
var KeyboardEvent = 
{
	KEY_DOWN		: 'keydown'
};

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


/**
 * Enum for template values.
 * @enum {string}
 */
var Template = 
{
	VIDEO_ELEMENT_WRAPPER	: '<div class="vpv1_wrapper vpv1_smallscreen"><div class="vpv1_content"><div class="vpv1_big_play_button"></div></div></div>',
	
	VIDEO_ELEMENT_CONTROLS	: '<div class="vpv1_video_controls"><div class="vpv1_fast_backward_button"></div><div class="vpv1_play_pause_button"></div><div class="vpv1_fast_forward_button"></div><div class="vpv1_time_display_wrapper"><em class="vpv1_elapsed_time">00:00</em><em class="vpv1_remaining_time">-00:00</em></div><div class="vpv1_progress_wrapper"><div class="vpv1_progress_back"><div class="vpv1_progress_bounds"><div class="vpv1_progress_loading_wrapper"><div class="vpv1_progress_loading_middel"><div class="vpv1_progress_loading_strips"></div></div></div><div class="vpv1_progress_buffered"></div><div class="vpv1_progress_elapsed_time"><div class="vpv1_progress_indicator"></div></div></div></div></div><div class="vpv1_volume_wrapper"><div class="vpv1_volume_slider_wrapper"><div class="vpv1_volume_slider_back"><div class="vpv1_volume_slider_bounds"><div class="vpv1_volume_slider_bar"><div class="vpv1_volume_slider_indicator"></div></div></div></div></div><div class="vpv1_volume_button"></div></div><div class="vpv1_fullscreen_button"></div><div class="vpv1_playbackrate_display"></div></div>',
	
	LIGHTBOX_ELEMENT_WRAPPER	: '<div class="vpv1_lightbox_wrapper"><div class="vpv1_blackbox"></div><div class="vpv1_lightbox_content"><div class="vpv1_close_button"></div></div></div>'
	
};

/**
 * Enum for class values.
 * @enum {string}
 */
var Classes =
{
	PAUSED					: 'vpv1_paused',
	SEEKING					: 'vpv1_seeking',
	SEEKING_VOL				: 'vpv1_seeking_volume',
	SEEKING_CONTROLS		: 'vpv1_seeking_controls',
	STARTED					: 'vpv1_started',
	LOADING					: 'vpv1_loading',
	MUTE					: 'vpv1_mute',
	FULLSCREEN				: 'vpv1_fullscreen',
	SMALLSCREEN				: 'vpv1_smallscreen',
	PLAYBACK_RATE			: 'vpv1_playbackrate_avaliable',
	PLAYBACK_CHANGED		: 'vpv1_playbackrate_changed',
	NOT_FOUND				: 'vpv1_not_found',
	UPGRADE					: 'vpv1_upgrade',
	BODY					: 'vpv1_body',
	HIDE_CONTROLS			: 'vpv1_hide_controls',
	LIGHTBOX_ANIMATE_OPEN	: 'vpv1_lightbox_animate_open',
	LIGHTBOX_ANIMATE_CLOSE	: 'vpv1_lightbox_animate_close'
};

var animationFrame =
		win['requestAnimationFrame']		||
		win['webkitRequestAnimationFrame']	||
		win['mozRequestAnimationFrame']		||
		win['oRequestAnimationFrame']		||
		win['msRequestAnimationFrame']		||
		undefined;

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




if($['fn'][VIDEO_PLAYER_ID])return;

(function fixIE()
{
	doc.createElement('video');
	doc.createElement('source');
})();





/**
 * Inherit the prototype methods from one constructor into another.
 * @param {Function} childCtor Child class.
 * @param {Function} parentCtor Parent class.
 */
function inherits(childCtor, parentCtor)
{
	/** @constructor */
	function tempCtor() {};
	tempCtor.prototype = parentCtor.prototype;
	childCtor.superClass_ = parentCtor.prototype;
	childCtor.prototype = new tempCtor();
	childCtor.prototype.constructor = childCtor;
}

/**
 * Returns true if the specified value is not |undefined|.
 * WARNING: Do not use this to test if an object has a property. Use the in
 * operator instead.  Additionally, this function assumes that the global
 * undefined variable has not been redefined.
 * @param {*} val Variable to test.
 * @return {boolean} Whether variable is defined.
 */
function isDefined(val)
{
	return typeof val !== 'undefined';
}

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

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

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


function getVideoTypeBySource(source)
{
	var ext = getFileExtension(source);
	var type;
	
	if(ext)
	{
		if(ext.match(/m(p4|ov|4v)/i) || ext.match(/x-m4v/i))	type = 'mp4';
		else if(ext.match(/webm/i))								type = 'webm';
		else if(ext.match(/og[gv]/i))							type = 'ogg';
		//else													type = 'mp4';
	}
	
	return isNull(type) ? "" : 'video/'+type;
}

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




function isMozilla()
{
	return $['browser']['mozilla'];
}

function isOpera()
{
	return $['browser']['opera'];
}

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

function data(element, key, value)
{
	return $['data'](element, key, value);
}

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

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

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

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

/**
 * 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);
}

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

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

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


/**
 * 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);
}

/**
 * Remove an attribute.
 * @param {object} element.
 * @param {string} attribute.
 */
function removeAttr(element, attribute)
{
	element['removeAttr'](attribute);
}

/**
 * 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 each(object, callback)
{
	return $(object)['each'](callback);
}

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

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

/**
 * 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 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(VIDEO_PLAYER_ID, volume); }
		catch(e){ createCookie(VIDEO_PLAYER_ID, volume, 30); }
	}
	else
		createCookie(VIDEO_PLAYER_ID, volume, 30);
}

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




function requestInterval(callback, delay)
{
	if(!isDefined(animationFrame) || !isDefined(cancelAnimationFrame))
	{
		return win.setInterval(callback, delay);
	}
	
	var start = Date.now(),
		handle = {};

	function loop()
	{
		var current = Date.now(),
			delta = current - start;

		if(delta >= delay)
		{
			callback.call();
			start = Date.now();
		}

		handle.v = animationFrame(loop);
	};

	handle.v = animationFrame(loop);
	return handle;
}

function clearRequestInterval(handle)
{
	if(handle)
	{
		if(isDefined(cancelAnimationFrame))
			cancelAnimationFrame(handle.v);
		else
			clearInterval(handle);
	}
}


function callAnimationFrame(callback, index)
{
	if(isDefined(animationFrame) && isDefined(cancelAnimationFrame))
	{
		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 getRemainingTime(time, duration)
{
	if(duration)
		return '-' + getCurrentTime(duration - time, duration);
}

/**
 * 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);
}



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

/**
 * 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);
}


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


function pauseAllVideosExcept(element)
{
	each(collection, function()
	{
		if(element != this && !this.paused())
		{
			this.pause();
		}
	});
}

function getTransformProperty(object)
{
	var style = object['get'](0)['style'];

	if(isDefined(style))
	{
		return	(isDefined(style['transform'])			? 'transform' 		: undefined) ||
				(isDefined(style['WebkitTransform'])	? 'WebkitTransform'	: undefined) ||
				(isDefined(style['MozTransform'])		? 'MozTransform'	: undefined) ||
				(isDefined(style['OTransform'])			? 'OTransform'		: undefined) ||
				(isDefined(style['msTransform'])		? 'msTransform'		: undefined);
	}
}

function getAnimationProperty(object)
{
	var style = object['get'](0)['style'];
	
	if(isDefined(style))
	{
		return	(isDefined(style['animation'])			? 'animation' 		: undefined) ||
				(isDefined(style['WebkitAnimation'])	? 'WebkitAnimation'	: undefined);
		
		/*
		return	(isDefined(style.animation)			? 'animation' 		: undefined) ||
				(isDefined(style.WebkitAnimation)	? 'WebkitAnimation'	: undefined) ||
				(isDefined(style.MozAnimation)		? 'MozAnimation'	: undefined);
		//*/
	}
}







/**
 * Event target object
 * @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 video controller class.
 * @param {Object} videoObject
 * @param {Object} videoElement
 * @extends EventTargetController
 * @constructor
 */
function VideoController(videoObject, videoElement)
{
	EventTargetController.call(this);
	
	this.videoElement_	= videoElement;
	this.videoObject_	= videoObject;
	
	this.available 		= videoElement['canPlayType'];
	
	if(!this.available)return;
	
	this.hackTimerInterval_		= 1000/10; // 10x times each second.
	this.hackTimerId_			= null;
	
	this.isBindVideoEvents_		= false;
	this.isPrepared_			= false;
	this.isUnloading_			= false;
	
	this.started				= false;
	this.canplay				= false;
	
	this.currentTime_			= 0;
	this.progress_				= 0;
		
	this.bindVideoEventController_();
}

inherits(VideoController, EventTargetController);

VideoController.prototype.bindVideoEventController_ = function()
{
	if(this.isBindVideoEvents_)return;
	
	this.isBindVideoEvents_ = true;
	
	bind(this.videoObject_, MediaEvent.CAN_PLAY,		proxy(this.canplayHandler__,		this));
	
	bind(this.videoObject_, MediaEvent.PLAYING,			proxy(this.playingHandler__,		this));
	bind(this.videoObject_, MediaEvent.PLAY,			proxy(this.playingHandler__,		this));
		
	bind(this.videoObject_, MediaEvent.PAUSE,			proxy(this.pauseHandler__,			this));
	bind(this.videoObject_, MediaEvent.ENDED,			proxy(this.endedHandler__,			this));
	bind(this.videoObject_, MediaEvent.WAITING,			proxy(this.waitingHandler__,		this));

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

	bind(this.videoObject_, MediaEvent.VOLUME_CHANGE,	proxy(this.volumechangeHandler__,	this));
	bind(this.videoObject_, MediaEvent.RATE_CHANGE,		proxy(this.ratechangeHandler__,		this));
	
	bind(this.videoObject_, MediaEvent.ERROR,			proxy(this.errorHandler__,			this));
};

VideoController.prototype.unbindVideoEventController_ = function()
{
	if(!this.isBindVideoEvents_)return;
	
	this.isBindVideoEvents_ = false;
	
	unbind(this.videoObject_, MediaEvent.CAN_PLAY,			this.canplayHandler__);
	
	unbind(this.videoObject_, MediaEvent.PLAYING,			this.playingHandler__);
	unbind(this.videoObject_, MediaEvent.PLAY,				this.playingHandler__);
		
	unbind(this.videoObject_, MediaEvent.PAUSE,				this.pauseHandler__);
	unbind(this.videoObject_, MediaEvent.ENDED,				this.endedHandler__);
	unbind(this.videoObject_, MediaEvent.WAITING,			this.waitingHandler__);

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

	unbind(this.videoObject_, MediaEvent.VOLUME_CHANGE,		this.volumechangeHandler__);
	unbind(this.videoObject_, MediaEvent.RATE_CHANGE,		this.ratechangeHandler__);

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

VideoController.prototype.destroy = function()
{
	this.unbindVideoEventController_();
	VideoController.superClass_.destroy.call(this);	
};

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

VideoController.prototype.prepare = function()
{
	if(this.isPrepared_)
	
	this.isPrepared_ = true;
	this.setPlayableSource_();
};

VideoController.prototype.setPlayableSource_ = function()
{
	var sources 	= find(this.videoObject_, 'source');
	var self 		= this;
	var newSource;

	each(sources, function(i)
	{
		var src		= attr($(this), 'src');
		var type	= getVideoTypeBySource(src);
		var canplay	= self.canPlayType(type);
							
		if(canplay == 'maybe' || canplay == 'probably')
		{
			newSource = src;
			
			// some source types have priority.
			if(type == 'video/mp4' || type == 'video/ogg' || type == 'video/ogv')
				return false;
		}
	});
		
	if(newSource)
	{
		if(DEBUG)
		{
			console.log('VideoController', 'setPlayableSource_', newSource);
		}
		
		remove(sources);
		this.setSrc(newSource);
	}
};

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

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

VideoController.prototype.playbackRate = function()
{
	return this.videoElement_['playbackRate'];
};

VideoController.prototype.setPlaybackRate = function(value)
{
	this.videoElement_['playbackRate'] = value;
};

VideoController.prototype.autoplay = function()
{
	return this.videoElement_['autoplay'];
};

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

VideoController.prototype.loop = function()
{
	return this.videoElement_['loop'] || is(this.videoObject_, '[loop]');
};

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

VideoController.prototype.preload = function()
{
	return this.videoElement_['preload'];
};

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

VideoController.prototype.duration = function()
{
	return this.videoElement_['duration'];
}

VideoController.prototype.ended = function()
{
	return this.videoElement_['ended'];
}

VideoController.prototype.paused = function()
{
	return this.videoElement_['paused'];
}

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

VideoController.prototype.src = function()
{
	return this.videoElement_['src'];
};

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

VideoController.prototype.volume = function()
{
	return this.videoElement_['volume'];
};

VideoController.prototype.setVolume = function(value)
{
	this.videoElement_['volume'] = value;
};

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

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

VideoController.prototype.setCurrentTime = function(value)
{
	if(this.videoElement_['currentTime'] != value)
	{
		this.videoElement_['currentTime'] = value;
	}
};

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

VideoController.prototype.pause = function()
{
	this.videoElement_['pause']();
};

/**
 * start loading the video.
 * @param {Object=} opt_source (optional).
 */
VideoController.prototype.load = function(opt_source)
{
	var src = opt_source || this.currentSource_;
	
	if(src)
	{
		this.setSrc(src);
	}
	
	this.reset_();
	this.bindVideoEventController_();
	
	this.videoElement_['load']();
};

VideoController.prototype.unload = function()
{	
	this.isUnloading_ = true;
	
	this.pause();
	this.setCurrentTime(0);
	
	this.unbindVideoEventController_();
	this.reset_();
	
	this.videoElement_['src'] = '';
	this.videoElement_['load']();
};





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

VideoController.prototype.stopTimer_ = function()
{
	if(this.hackTimerId_)
	{
		clearInterval(this.hackTimerId_);
		this.hackTimerId_ = null;
	}	
};

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

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

		if(buffered && duration)
		{
			var length = buffered.length;
			if(length)
			{
				var diff = duration;
				var index = length - 1;
				var number = this.videoElement_['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);
				}	
			}
		}
	}
};

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

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

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

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

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

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

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

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

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

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










/**
 * The core video class.
 * @param {Object} videoObject
 * @param {Object} videoElement
 * @extends VideoController
 * @constructor
 */
function VideoCore(videoObject, videoElement)
{
	VideoController.call(this, videoObject, videoElement);

	if(simpleView && this.available)
	{
		videoElement.controls = 'controls';		
		attr(videoObject, 'x-webkit-airplay', 'allow');
		return;
	}
	
	this.wrapperDisplay_	= $(Template.VIDEO_ELEMENT_WRAPPER);
	this.contentDisplay_	= find(this.wrapperDisplay_, '.vpv1_content');
	this.bigButton_			= find(this.wrapperDisplay_, '.vpv1_big_play_button');

	this.identifier_		= initCount++;
	
	this.setSizeFromVideoElement_();
	
	if(!this.available)
	{
		addClass(this.wrapperDisplay_, Classes.UPGRADE);
		return;
	}
	
	// render
	after(videoObject, this.wrapperDisplay_);
	prepend(this.contentDisplay_, videoObject);
	
	
	if(this.autoplay())
	{
		this.play();
	}
	
	// save to the collection
	collection[this.identifier_] = this;
};

inherits(VideoCore, VideoController);

VideoCore.prototype.setSizeFromVideoElement_ = function()
{
	var w = attr(this.videoObject_, 'width') || width(this.videoObject_);
	var h = attr(this.videoObject_, 'height') || height(this.videoObject_);
	
	this.setSize_(w, h);
};

VideoCore.prototype.setSize_ = function(w, h)
{
	attr(this.wrapperDisplay_, 'style', 'width:'+w+'px!important;height:'+h+'px!important;');
};

VideoCore.prototype.destroy = function()
{
	after(this.wrapperDisplay_, this.videoObject_);
	remove(this.wrapperDisplay_);
				
	collection.splice(this.identifier_, 1);
	
	VideoCore.superClass_.destroy.call(this);	
};












/**
 * The video player class.
 * @param {Object} videoObject
 * @param {Object} videoElement
 * @extends VideoCore
 * @constructor
 */
function VideoPlayer(videoObject, videoElement)
{
	//var fallback = attr(videoObject, 'data-fallback');
	//if(fallback == 'true' || val == 'on' || val == 'yes') fallback = true;
	//else fallback = false;
	
	if(!isDefined(videoElement.canPlayType) && isDefined(attr(videoObject, 'data-fallback')))
		return;
	
	VideoCore.call(this, videoObject, videoElement);
	
	if(!this.available || simpleView)return;
	
	this.hasControllerView_ = false;
	
	this.addEventListener(MediaEvent.PLAYING, this.onStartHandler_);
	
	//bind(this.videoObject_,		MediaEvent.PLAYING,		proxy(this.onStartHandler_, this));					
	bind(this.bigButton_,		MouseEvent.CLICK,		proxy(this.createControllerView_, this));
	
	bind(this.wrapperDisplay_,	MouseEvent.CONTEXT_MENU,	false);
	bind(this.wrapperDisplay_,	MouseEvent.MOUSE_DOWN,		false);	
	
	
	var vol = (keepInBound(attr(videoObject, 'data-volume'), 100) / 100) || getVolumeState();	
	if(isDefined(vol))this.setVolume(vol);
};

inherits(VideoPlayer, VideoCore);

VideoPlayer.prototype.destroy = function()
{
	//unbind(this.videoObject_,		MediaEvent.PLAYING,		this.onStartHandler_);					
	unbind(this.bigButton_,			MouseEvent.CLICK,		this.createControllerView_);
	unbind(this.wrapperDisplay_,	MouseEvent.CONTEXT_MENU);
	unbind(this.wrapperDisplay_,	MouseEvent.MOUSE_DOWN);
	
	if(this.hasControllerView_)
	{
		this.unbindVideoEvents_();

		this.unbindPlayControls_();		
		this.unbindVolumeControls_();
		this.unbindFullScreenControls_();
		this.unbindPlaybackRateControls_();
		
		
		this.stopLoadingAnimation_();
		
		if(this.isFullwindowMode_)
		{
			this.unbindKeyboardControls_();
			this.unbindMovingControllerControls_();
			this.unbindTimeoutControllerControls_();
			
			this.clearControllerTimeout_();
		}
	}
	
	VideoPlayer.superClass_.destroy.call(this);	
};


VideoPlayer.prototype.unload = function()
{
	this.stopLoadingAnimation_();
	
	VideoPlayer.superClass_.unload.call(this);	
};

VideoPlayer.prototype.onStartHandler_ = function()
{
	this.createControllerView_();
	if(this.canplay)this.canplayHandler_();
};

VideoPlayer.prototype.createControllerView_ = function()
{
	this.removeEventListener(MediaEvent.PLAYING, this.onStartHandler_);
	
	//unbind(this.videoObject_,		MediaEvent.PLAYING,		this.onStartHandler_);					
	unbind(this.bigButton_,			MouseEvent.CLICK,		this.createControllerView_);
		
	// If the currentSrc is not set.
	// It means there is no video found.
// 	if(!this.currentSrc())
// 	{
// 		addClass(this.wrapperDisplay_, Classes.NOT_FOUND);
// 		return;
// 	}
		
	this.hasControllerView_ = true;
	
	var controlsDisplay		= this.controlsDisplay_	= $(Template.VIDEO_ELEMENT_CONTROLS);
		
	this.isSeekingTimeline_			= false;
	this.isSeekingVolume_			= false;
	this.isSeekingController_		= false;
	this.isFullwindowMode_			= false;
	this.isMuted_					= false;
	this.isWaiting_					= false;
	this.isFixedPosition_			= false;
	this.isControllerHidding_		= false;
	
	
	this.controllerPositionTop_		= 0;
	this.controllerPositionLeft_	= 0;
	
	this.loadingAnimationInterval_	= null;
	this.controllerTimeout_			= null;
	this.offsetLeft_				= null;
	this.offsetTop_					= null;
	this.offsetWidth_				= null;
	
	this.currentPlaybackIndex_		= 3;
	
	

		
	this.playPauseButton_				= find(controlsDisplay, '.vpv1_play_pause_button');
	this.fastBackwardButton_			= find(controlsDisplay, '.vpv1_fast_backward_button');
	this.fastForwardButton_				= find(controlsDisplay, '.vpv1_fast_forward_button');
	
	this.elapsedTimeTextField_			= find(controlsDisplay, '.vpv1_elapsed_time');
	this.remainingTimeTextField_		= find(controlsDisplay, '.vpv1_remaining_time');
	
	this.progressBoundsDisplay_			= find(controlsDisplay, '.vpv1_progress_bounds');
	this.progressLoadingDisplay_		= find(controlsDisplay, '.vpv1_progress_loading_strips');
	this.progressBufferedDisplay_		= find(controlsDisplay, '.vpv1_progress_buffered');
	this.progressElapsedTimeDisplay_	= find(controlsDisplay, '.vpv1_progress_elapsed_time');
	this.progressIndicatorDisplay_		= find(controlsDisplay, '.vpv1_progress_indicator');
	
	this.volumeSliderBoundsDisplay_		= find(controlsDisplay, '.vpv1_volume_slider_bounds');
	this.volumeSliderBarDisplay_		= find(controlsDisplay, '.vpv1_volume_slider_bar');
	this.volumeSliderIndicatorDisplay_	= find(controlsDisplay, '.vpv1_volume_slider_indicator');
	
	this.volumeButton_					= find(controlsDisplay, '.vpv1_volume_button');
	this.fullscreenButton_				= find(controlsDisplay, '.vpv1_fullscreen_button');
	this.playbackRateDisplay_			= find(controlsDisplay, '.vpv1_playbackrate_display');
	
		
	this.checkForPlaybackRateSupport_();
	
	this.bindVideoEvents_();
	this.bindVolumeControls_();
	this.bindFullScreenControls_();
	this.bindPlaybackRateControls_();
	
	
	//this.setVolume(getVolumeState());
	this.setVolumeView_(this.volume());

	
	//if(this.videoElement_['preload'] == 'auto')
	//this.progressHandler_();


	addClass(this.wrapperDisplay_, Classes.STARTED);
		
	
	this.startLoadingAnimation_();
	
	this.setPreload('auto');
	this.play();
							
	
	append(this.contentDisplay_, controlsDisplay);
};

VideoPlayer.prototype.resetContorls_ = function()
{
	width(this.progressBufferedDisplay_, 0);
	width(this.progressElapsedTimeDisplay_, 0);
	
	text(this.elapsedTimeTextField_, '00:00');
	text(this.remainingTimeTextField_, '00:00');
};

VideoPlayer.prototype.startLoadingAnimation_ = function()
{
	if(!this.loadingAnimationInterval_ && !this.isSeekingTimeline_)
	{
		addClass(this.wrapperDisplay_, Classes.LOADING);
		
		var i=0;
		var self = this;
		this.loadingAnimationInterval_ = requestInterval(function()
		{
			if(i <= -20)i=0;
			
			attr(self.progressLoadingDisplay_, 'style', 'margin-left:'+ (--i) + 'px !important;');
		}, 50);
	}
}

VideoPlayer.prototype.stopLoadingAnimation_ = function()
{
	if(this.loadingAnimationInterval_)
	{
		removeClass(this.wrapperDisplay_, Classes.LOADING);
		
		clearRequestInterval(this.loadingAnimationInterval_);
		this.loadingAnimationInterval_ = null;
	}
}

VideoPlayer.prototype.bindVideoEvents_ = function()
{	
	this.addEventListener(MediaEvent.PROGRESS,			this.progressHandler_);
	this.addEventListener(MediaEvent.TIME_UPDATE,		this.timeupdateHandler_);
	this.addEventListener(MediaEvent.CAN_PLAY,			this.canplayHandler_);
	this.addEventListener(MediaEvent.RATE_CHANGE,		this.ratechangeHandler_);
	this.addEventListener(MediaEvent.WAITING,			this.waitingHandler_);
	this.addEventListener(MediaEvent.PLAYING,			this.playHandler_);
	this.addEventListener(MediaEvent.PAUSE,				this.pauseHandler_);
	this.addEventListener(MediaEvent.ENDED,				this.endedHandler_);
	this.addEventListener(MediaEvent.VOLUME_CHANGE,		this.volumechangeHandler_);
};

VideoPlayer.prototype.unbindVideoEvents_ = function()
{
	this.removeEventListener(MediaEvent.PROGRESS,			this.progressHandler_);
	this.removeEventListener(MediaEvent.TIME_UPDATE,		this.timeupdateHandler_);
	this.removeEventListener(MediaEvent.CAN_PLAY,			this.canplayHandler_);
	this.removeEventListener(MediaEvent.RATE_CHANGE,		this.ratechangeHandler_);
	this.removeEventListener(MediaEvent.WAITING,			this.waitingHandler_);
	this.removeEventListener(MediaEvent.PLAYING,			this.playHandler_);
	this.removeEventListener(MediaEvent.PAUSE,				this.pauseHandler_);
	this.removeEventListener(MediaEvent.ENDED,				this.endedHandler_);
	this.removeEventListener(MediaEvent.VOLUME_CHANGE,		this.volumechangeHandler_);
};

VideoPlayer.prototype.bindPlayControls_ = function()
{
	bind(this.progressBoundsDisplay_,MouseEvent.MOUSE_DOWN,	proxy(this.onSeekDownHandler_, this));
	bind(this.playPauseButton_, MouseEvent.CLICK, proxy(this.toggleVideo_, this));
};

VideoPlayer.prototype.unbindPlayControls_ = function()
{
	unbind(this.progressBoundsDisplay_,	MouseEvent.MOUSE_DOWN, this.onSeekDownHandler_);
	unbind(this.playPauseButton_, MouseEvent.CLICK, proxy(this.toggleVideo_, this));
	
	if(this.isSeekingTimeline_)
	{
		unbind($win, MouseEvent.MOUSE_MOVE,	this.onSeekMoveHandler_);
		unbind($win, MouseEvent.MOUSE_UP,	this.onSeekUpHandler_);
	}
};

VideoPlayer.prototype.bindVolumeControls_ = function()
{
	bind(
		this.volumeSliderBoundsDisplay_,
		MouseEvent.MOUSE_DOWN,
		proxy(this.onVolumeDownHandler_, this)
	);
	
	bind(this.volumeButton_, MouseEvent.CLICK, proxy(this.toggleVolume_, this));
};

VideoPlayer.prototype.unbindVolumeControls_ = function()
{
	unbind(this.volumeSliderBoundsDisplay_, MouseEvent.MOUSE_DOWN, this.onVolumeDownHandler_);
	unbind(this.volumeButton_, MouseEvent.CLICK, this.toggleVolume_);
	
	if(this.isSeekingVolume_)
	{
		unbind($win, MouseEvent.MOUSE_MOVE,	this.onVolumeMoveHandler_);
		unbind($win, MouseEvent.MOUSE_UP,	this.onVolumeUpHandler_);
	}
};


VideoPlayer.prototype.bindFullScreenControls_ = function()
{
	bind(this.bigButton_,		MouseEvent.DOUBLE_CLICK,proxy(this.toggleFullwindowMode_, this));
	bind(this.fullscreenButton_,MouseEvent.CLICK,		proxy(this.toggleFullwindowMode_, this));
};

VideoPlayer.prototype.unbindFullScreenControls_ = function()
{
	unbind(this.bigButton_,			MouseEvent.DOUBLE_CLICK,	this.toggleFullwindowMode_);
	unbind(this.fullscreenButton_,	MouseEvent.CLICK,			this.toggleFullwindowMode_);
};

VideoPlayer.prototype.bindTimeoutControllerControls_ = function()
{
	bind($win, MouseEvent.MOUSE_MOVE, proxy(this.setNewControllerTimeout_, this));
};

VideoPlayer.prototype.unbindTimeoutControllerControls_ = function()
{
	unbind($win, MouseEvent.MOUSE_MOVE, this.setNewControllerTimeout_);
};

VideoPlayer.prototype.bindKeyboardControls_ = function()
{
	bind($win, KeyboardEvent.KEY_DOWN, proxy(this.onKeyDownHandler_, this));
};

VideoPlayer.prototype.unbindKeyboardControls_ = function()
{
	unbind($win, KeyboardEvent.KEY_DOWN, this.onKeyDownHandler_);
};

VideoPlayer.prototype.bindMovingControllerControls_ = function()
{
	if(transformProperty)
	{
		bind(this.controlsDisplay_, MouseEvent.MOUSE_DOWN, proxy(this.onControllerDownHandler_, this));
	}
};

VideoPlayer.prototype.unbindMovingControllerControls_ = function()
{
	if(transformProperty)
	{
		unbind(this.controlsDisplay_, MouseEvent.MOUSE_DOWN, this.onControllerDownHandler_);
	
		if(this.isSeekingController_)
		{
			unbind($win, MouseEvent.MOUSE_UP,		this.onControllerUpHandler_);
			unbind($win, MouseEvent.MOUSE_MOVE,		this.onControllerMoveHandler_);
		}
	}
};

VideoPlayer.prototype.bindPlaybackRateControls_ = function()
{		
	if(this.playbackRateAvailable_)
	{
		bind(this.fastForwardButton_,	MouseEvent.CLICK,	proxy(this.forwardVideoPlaybackRate_, this));
		bind(this.fastBackwardButton_,	MouseEvent.CLICK,	proxy(this.backwardVideoPlaybackRate_, this));
	}
};

VideoPlayer.prototype.unbindPlaybackRateControls_ = function()
{		
	if(this.playbackRateAvailable_)
	{		
		unbind(this.fastForwardButton_,		MouseEvent.CLICK,	this.forwardVideoPlaybackRate_);
		unbind(this.fastBackwardButton_,	MouseEvent.CLICK,	this.backwardVideoPlaybackRate_);
	}
};

VideoPlayer.prototype.checkForPlaybackRateSupport_ = function()
{
	// for some reason Opera makes the playbackRate available.
	// But you cant change it.
	// So... it's useless.
	// I hate Opera.
	
	this.playbackRateAvailable_ = isDefined(this.playbackRate()) && !isOpera();
	
	if(this.playbackRateAvailable_)
	{
		addClass(this.wrapperDisplay_,	Classes.PLAYBACK_RATE);
	}

	return this.playbackRateAvailable_;
};

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


VideoPlayer.prototype.toggleVideo_ = function()
{
	this.setPlaybackRate(3);
	
	if(this.paused() || this.ended()) 
	{
		this.play();
		removeClass(this.wrapperDisplay_, Classes.PAUSED);
	}
	else 
	{
		this.pause();
		addClass(this.wrapperDisplay_, Classes.PAUSED);
	}
};

VideoPlayer.prototype.toggleFullwindowMode_ = function()
{	
	if(this.isFullwindowMode_)
	{
		this.exitFullwindowMode_();
	}
	else
	{
		this.goFullwindowMode_();
	}
};

VideoPlayer.prototype.goFullwindowMode_ = function()
{
	if(!this.isFullwindowMode_)
	{
		this.isFullwindowMode_ = true;
	
		removeClass(this.wrapperDisplay_,	Classes.SMALLSCREEN);
		addClass(this.wrapperDisplay_, 		Classes.FULLSCREEN);
		addClass(bodyElement, 				Classes.BODY);
		
		this.setPlaybackRate(3);
	
		this.bindMovingControllerControls_();
		this.bindKeyboardControls_();
		this.bindTimeoutControllerControls_();
	
		this.setNewControllerTimeout_();
	}
};

VideoPlayer.prototype.exitFullwindowMode_ = function()
{
	if(this.isFullwindowMode_)
	{
		this.isFullwindowMode_ = false;
		
		addClass(this.wrapperDisplay_,		Classes.SMALLSCREEN);
		removeClass(this.wrapperDisplay_,	Classes.FULLSCREEN);
		removeClass(bodyElement,			Classes.BODY);
	
		this.setPlaybackRate(3);
	
		if(transformProperty)
		{
			css(this.controlsDisplay_, transformProperty, 'none');
			this.controllerPositionLeft_ = 0;
			this.controllerPositionTop_ = 0;
		}
	
		this.unbindMovingControllerControls_();
		this.unbindKeyboardControls_();
		this.unbindTimeoutControllerControls_();
	
		this.showController_();
		this.clearControllerTimeout_();
	}
};

VideoPlayer.prototype.setNewControllerTimeout_ = function()
{
	this.clearControllerTimeout_();
	
	this.controllerTimeout_ = setTimeout(proxy(this.hideController_, this), 3000);
	
	this.showController_();
};

VideoPlayer.prototype.clearControllerTimeout_ = function()
{
	if(this.controllerTimeout_)
	{
		clearTimeout(this.controllerTimeout_);
		this.controllerTimeout_ = null;
	}
};

VideoPlayer.prototype.showController_ = function()
{
	if(this.isControllerHidding_)
	{
		this.isControllerHidding_ = false;
		removeClass(this.wrapperDisplay_, Classes.HIDE_CONTROLS);
	}
};

VideoPlayer.prototype.hideController_ = function()
{
	if(!this.isControllerHidding_)
	{
		this.isControllerHidding_= true;
		addClass(this.wrapperDisplay_, Classes.HIDE_CONTROLS);
	}
};

VideoPlayer.prototype.onControllerDownHandler_ = function(e)
{
	if(e.which != 1)return;
	
	this.isSeekingController_ = true;
	
	this.offsetLeft_ = e.pageX;
	this.offsetTop_ = e.pageY;
	
	addClass(this.wrapperDisplay_, Classes.SEEKING_CONTROLS);
	
	bind($win, MouseEvent.MOUSE_UP,		proxy(this.onControllerUpHandler_, this));
	bind($win, MouseEvent.MOUSE_MOVE,	proxy(this.onControllerMoveHandler_, this));
	
	return false;
};

VideoPlayer.prototype.onControllerUpHandler_ = function(e)
{
	this.isSeekingController_ = false;
	
	this.controllerPositionLeft_ = e.pageX - this.offsetLeft_ + this.controllerPositionLeft_;
	this.controllerPositionTop_ = e.pageY - this.offsetTop_ + this.controllerPositionTop_;
	
	removeClass(this.wrapperDisplay_, Classes.SEEKING_CONTROLS);
	
	unbind($win, MouseEvent.MOUSE_UP,		this.onControllerUpHandler_);
	unbind($win, MouseEvent.MOUSE_MOVE,		this.onControllerMoveHandler_);
};

VideoPlayer.prototype.onControllerMoveHandler_ = function(e)
{
	var self = this;				
	var x = e.pageX - self.offsetLeft_ + self.controllerPositionLeft_;
	var y = e.pageY - self.offsetTop_ + self.controllerPositionTop_;

	css(self.controlsDisplay_, transformProperty, 'translate('+x+'px, '+y+'px)');
	/*
	callAnimationFrame(function()
	{
		css(self.controlsDisplay_, transformProperty, 'translate('+x+'px, '+y+'px)');
	},'0');
	*/
};



VideoPlayer.prototype.onKeyDownHandler_ = function(e)
{
	if(this.isSeekingVolume_ || this.isSeekingTimeline_ || this.isSeekingController_)
		return;
	
	
	switch(e.which)
	{
		case 27:
			this.exitFullwindowMode_();
			this.setNewControllerTimeout_();
			return false;
		break;
			
		case 32:
			if(this.canplay)
			{
				this.toggleVideo_();
				this.setNewControllerTimeout_();
				return false;
			}
		break;
		
		case 37: 
			if(this.playbackRateAvailable_)
			{
				this.backwardVideoPlaybackRate_();
				this.setNewControllerTimeout_();
				return false;
			}
		break;
		
		case 38:
			this.setVolume( keepInBound(roundNumber(this.volume())+0.2) );
			this.setNewControllerTimeout_();
			return false;
		break;
		
		case 39:
			if(this.playbackRateAvailable_)
			{
				this.forwardVideoPlaybackRate_();
				this.setNewControllerTimeout_();
				return false;
			}
		break;
		
		case 40:
			this.setVolume( keepInBound(roundNumber(this.volume())-0.2) );
			this.setNewControllerTimeout_();
			return false;
		break;
	}
};

VideoPlayer.prototype.forwardVideoPlaybackRate_ = function()
{
	if(!this.canplay)return;
	
	if(this.paused || this.ended)
	{
		this.play();
		removeClass(this.wrapperDisplay_, Classes.PAUSED);
	}
		
	this.setPlaybackRate(this.currentPlaybackIndex_ + 1);
};

VideoPlayer.prototype.backwardVideoPlaybackRate_ = function()
{
	if(!this.canplay)return;
	
	if(this.paused || this.ended)
	{
		this.play();
		removeClass(this.wrapperDisplay_, Classes.PAUSED);
	}
	
	this.setPlaybackRate(this.currentPlaybackIndex_ - 1);
};

VideoPlayer.prototype.setPlaybackRate = function(value)
{
	var newRate = PLAYBACK_VALUES[value];
	var currentRate = this.playbackRate();
							
	if(this.canplay && isDefined(currentRate) && isDefined(newRate) && newRate != currentRate)
	{
		this.currentPlaybackIndex_ = value;
		VideoPlayer.superClass_.setPlaybackRate.call(this, newRate);
	}
};

VideoPlayer.prototype.onVolumeDownHandler_ = function(e)
{
	if(e.which != 1)return;
	
	if(this.isFixedPosition_ || this.isFullwindowMode_)
	{
		this.offsetLeft_ = getOffsetLeft(this.volumeSliderBoundsDisplay_) + $win.scrollLeft();
	}
	else
	{
		this.offsetLeft_ = getOffsetLeft(this.volumeSliderBoundsDisplay_);
	}
	
	this.offsetWidth_ = width(this.volumeSliderBoundsDisplay_);
	
	this.isSeekingVolume_ = true;
	
	var pos = keepInBound((e.pageX - this.offsetLeft_ - this.controllerPositionLeft_) / this.offsetWidth_);
	
	this.setVolume(pos);
	
	this.setVolumeView_(pos);
	
	addClass(this.wrapperDisplay_, Classes.SEEKING_VOL);
	
	bind($win, MouseEvent.MOUSE_MOVE,	proxy(this.onVolumeMoveHandler_, this));
	bind($win, MouseEvent.MOUSE_UP,		proxy(this.onVolumeUpHandler_, this));

	return false;
};

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

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

VideoPlayer.prototype.onVolumeMoveHandler_ = function(e)
{
	var pos = keepInBound((e.pageX - this.offsetLeft_ - this.controllerPositionLeft_) / this.offsetWidth_);
					
	this.setVolume(pos);
	this.setVolumeView_(pos);
};

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

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


VideoPlayer.prototype.onSeekDownHandler_ = function(e)
{
	// check if right mouse click is used.
	if(e.which != 1)return;
	
	var indicatorValue = (width(this.progressIndicatorDisplay_) + parseInt(css(this.progressIndicatorDisplay_, 'right'), 10) * 2) / 2;
					
	// save the current mouse offset.
	if(this.isFixedPosition_ || this.isFullwindowMode_)
	{
		this.offsetLeft_ = getOffsetLeft(this.progressBufferedDisplay_) + $win.scrollLeft() - (indicatorValue ? indicatorValue : 0);
	}
	else
	{
		this.offsetLeft_ = getOffsetLeft(this.progressBufferedDisplay_) - (indicatorValue ? indicatorValue : 0);
	}
					
	
	// save the boundary width.
	this.offsetWidth_ = width(this.progressBoundsDisplay_);
					

	
	var pos	= keepInBound((e.pageX - this.offsetLeft_ - this.controllerPositionLeft_) / this.offsetWidth_);
				
	var duration = this.duration();
	var time = duration * pos;
	
	this.isSeekingTimeline_ = true;
	
	addClass(this.wrapperDisplay_, Classes.SEEKING);
	
	width(this.progressElapsedTimeDisplay_, pos * 100 + '%');						
	
	text(this.elapsedTimeTextField_,	getCurrentTime(time, duration));
	text(this.remainingTimeTextField_,	getRemainingTime(time, duration));
	
	bind($win, MouseEvent.MOUSE_MOVE,	proxy(this.onSeekMoveHandler_, this));
	bind($win, MouseEvent.MOUSE_UP,		proxy(this.onSeekUpHandler_, this));

	this.pause();						
	this.setCurrentTime(time);
	
	
	return false;
};

VideoPlayer.prototype.onSeekUpHandler_ = function()
{
	removeClass(this.wrapperDisplay_,	Classes.SEEKING);
	
	unbind($win, MouseEvent.MOUSE_MOVE,	this.onSeekMoveHandler_);
	unbind($win, MouseEvent.MOUSE_UP,	this.onSeekUpHandler_);
	
	this.play();
	
	this.isSeekingTimeline_ = false;
};


VideoPlayer.prototype.onSeekMoveHandler_ = function(e)
{
	var pos	= keepInBound((e.pageX - this.offsetLeft_ - this.controllerPositionLeft_) / this.offsetWidth_);
	
	var duration = this.duration();
	var time = duration * pos;

	width(this.progressElapsedTimeDisplay_, pos * 100 + '%');

	text(this.elapsedTimeTextField_,	getCurrentTime(time, duration));
	text(this.remainingTimeTextField_,	getRemainingTime(time, duration));
	
	this.setCurrentTime(time);
};



VideoPlayer.prototype.canplayHandler_ = function()
{	
	this.stopLoadingAnimation_();
		
	this.bindPlayControls_();
	
	this.play();
};

VideoPlayer.prototype.progressHandler_ = function()
{
	var self = this;
	
	callAnimationFrame(function()
	{
		width(self.progressBufferedDisplay_, self.progress() + '%');
	},'1');
};

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

	var time		= self.currentTime();
	var duration	= self.duration();
	var pos			= keepInBound(time / duration);
	
	callAnimationFrame(function()
	{
		width(self.progressElapsedTimeDisplay_, pos * 100 + '%');

		text(self.elapsedTimeTextField_,	getCurrentTime(time, duration));
		text(self.remainingTimeTextField_,	getRemainingTime(time, duration));
	},'2');
};

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

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

VideoPlayer.prototype.ratechangeHandler_ = function()
{
	var rate = this.playbackRate();
	
	if(rate == 1) removeClass(this.wrapperDisplay_, Classes.PLAYBACK_CHANGED);
	else addClass(this.wrapperDisplay_, Classes.PLAYBACK_CHANGED);
	
	this.playbackRateDisplay_.text(Math.abs(parseInt(rate, 10))+'x');
};

VideoPlayer.prototype.waitingHandler_ = function()
{
	this.isWaiting_ = true;
	this.startLoadingAnimation_();
};

VideoPlayer.prototype.playHandler_ = function()
{
	if(this.isWaiting_)
	{
		this.isWaiting_ = false;
		this.stopLoadingAnimation_();
	}
	
	if(!this.isSeekingTimeline_)
	{						
		pauseAllVideosExcept(this);
		removeClass(this.wrapperDisplay_, Classes.PAUSED);
	}
};

VideoPlayer.prototype.pauseHandler_ = function()
{
	if(!this.isSeekingTimeline_)
	{
		addClass(this.wrapperDisplay_, Classes.PAUSED);
	}
};

VideoPlayer.prototype.endedHandler_ = function()
{	
	if(!this.isSeekingTimeline_)
	{
		addClass(this.wrapperDisplay_, Classes.PAUSED);
		
		if(this.loop())
		{
			this.setCurrentTime(0);
			this.play();
		}
	}
};

VideoPlayer.prototype.errorHandler_ = function()
{	
	this.stopLoadingAnimation_();
	addClass(this.wrapperDisplay_, Classes.NOT_FOUND);
};








/**
 * The lightbox player class.
 * @param {Object} anchorObject
 * @param {Object} anchorElement
 * @extends VideoPlayer
 * @constructor
 */
function LightBoxPlayer(anchorObject, anchorElement)
{	
	var videoObject		= find(anchorObject, 'video');
	var videoElement	= videoObject['get'](0);
	
	if(!videoElement)return;
	
	VideoPlayer.call(this, videoObject, videoElement);
	
	if(!this.available || simpleView)return;
		
	
	this.anchorObject_			= anchorObject;
	this.anchorElement_			= anchorElement;
	this.isLightboxInitialized_	= false;
	
	if(videoElement.canPlayType)
	{
		bind(anchorObject, MouseEvent.CLICK, proxy(this.openLightBoxHandler_, this));
	}
};

inherits(LightBoxPlayer, VideoPlayer);

LightBoxPlayer.prototype.destroy = function()
{
	unbind(this.anchorObject_, MouseEvent.CLICK, this.openLightBoxHandler_);
	
	if(this.isLightboxInitialized_)
	{
		unbindLightBoxClosingControls_();
	}
	
	LightBoxPlayer.superClass_.destroy.call(this);	
};

LightBoxPlayer.prototype.bindLightBoxClosingControls_ = function()
{
	bind(this.lightboxBlackBoxDisplay_, MouseEvent.CLICK,proxy(this.closeLightBoxHandler_, this));
	bind(this.lightboxCloseButton_, MouseEvent.CLICK, proxy(this.closeLightBoxHandler_, this));
	
	bind($win, KeyboardEvent.KEY_DOWN, proxy(this.onLightboxKeyDownHandler_, this));
};

LightBoxPlayer.prototype.unbindLightBoxClosingControls_ = function()
{
	unbind(this.lightboxBlackBoxDisplay_, MouseEvent.CLICK, this.closeLightBoxHandler_);
	unbind(this.lightboxCloseButton_, MouseEvent.CLICK, this.closeLightBoxHandler_);
	
	unbind($win, KeyboardEvent.KEY_DOWN, this.onLightboxKeyDownHandler_);
};

LightBoxPlayer.prototype.openLightBoxHandler_ = function()
{	
	if(!this.currentSrc())
	{
		return false;
	}
	
	//this.videoElement_.poster = null;
	removeAttr(this.videoObject_, 'poster');
	
	
	this.createControllerView_();
	this.setLightBoxSize_();

	if(animationProperty)
	{
		removeClass(this.lightboxWrapperDisplay_, Classes.LIGHTBOX_ANIMATE_CLOSE);
		addClass(this.lightboxWrapperDisplay_, Classes.LIGHTBOX_ANIMATE_OPEN);

		this.unbindFullScreenControls_();
		
		var self = this;
		setTimeout(function()
		{
			self.bindFullScreenControls_();
			self.bindLightBoxClosingControls_();
		}, 600);
		
	}
	else
	{
		this.bindLightBoxClosingControls_();
	}
	
		
	
	after(bodyElement, this.lightboxWrapperDisplay_);
	//append(bodyElement, this.lightboxWrapperDisplay_);
	
	this.load();
	this.play();
	
	
	return false;
};

LightBoxPlayer.prototype.closeLightBoxHandler_ = function()
{
	this.unbindLightBoxClosingControls_();
	this.unload();
		
	if(animationProperty)
	{		
		this.unbindFullScreenControls_();
		
		removeClass(this.lightboxWrapperDisplay_, Classes.LIGHTBOX_ANIMATE_OPEN);
		addClass(this.lightboxWrapperDisplay_, Classes.LIGHTBOX_ANIMATE_CLOSE);

		var self = this;
		setTimeout(function()
		{		
			append(self.anchorObject_, self.lightboxWrapperDisplay_);
			self.resetContorls_();
		}, 500);
	}
	else
	{
		append(this.anchorObject_, this.lightboxWrapperDisplay_);
		this.resetContorls_();
	}
	
	return false;
};

LightBoxPlayer.prototype.createControllerView_ = function()
{
	if(this.isLightboxInitialized_)return;

	this.isLightboxInitialized_		= true;

	this.lightboxWrapperDisplay_	= $(Template.LIGHTBOX_ELEMENT_WRAPPER);
	this.lightboxContentDisplay_	= find(this.lightboxWrapperDisplay_, '.vpv1_lightbox_content');
	this.lightboxBlackBoxDisplay_	= find(this.lightboxWrapperDisplay_, '.vpv1_blackbox');
	this.lightboxCloseButton_		= find(this.lightboxWrapperDisplay_, '.vpv1_close_button');
	
	
	// Mozilla displays a white border around the video element for X seconds.
	// If the poster image is not loaded.
	// So we wait 100 milliseconds before showing the video element.
	// Stupid FireFox...
	if(isMozilla())
	{
		//*
		var self = this;
		attr(self.videoObject_, 'style', 'visibility:hidden!important;');
		
		setTimeout(function()
		{
			attr(self.videoObject_, 'style', 'visibility:visible!important;');
		},100);
		//*/
	}
	
	this.setPlayableSource_();
	
	prepend(this.lightboxContentDisplay_, this.wrapperDisplay_);
	
	LightBoxPlayer.superClass_.createControllerView_.call(this);
	
	this.isFixedPosition_			= true;
};

LightBoxPlayer.prototype.setLightBoxSize_ = function()
{
	var videoWidth	= attr(this.videoObject_, 'width') || width(this.videoObject_);
	var videoHeight	= attr(this.videoObject_, 'height') || height(this.videoObject_);
	var maxWidth	= width($win) - 100;
	var maxHeight	= height($win) - 100;
						
	if(videoWidth > maxWidth || videoHeight > maxHeight)
	{
		var HSX = maxWidth / videoWidth;
		var HSY = maxHeight / videoHeight;
	
		var newSize = 1;
		
		if (HSX < HSY)
			newSize = HSX;
		
		else if (HSX > HSY)
			newSize = HSY;

		videoWidth = parseInt(videoWidth * newSize, 10);
		videoHeight = parseInt(videoHeight * newSize, 10);
	}
	
	width(this.lightboxContentDisplay_, videoWidth);
	height(this.lightboxContentDisplay_, videoHeight);
	
	this.setSize_(videoWidth, videoHeight);
};

LightBoxPlayer.prototype.onLightboxKeyDownHandler_ = function(e)
{
	if(
		!this.isFullwindowMode_		&&
		!this.isSeekingVolume_		&&
		!this.isSeekingTimeline_	&&
		!this.isSeekingController_	&&
		e.which == 27
	)
	{
		this.closeLightBoxHandler_();
		return false;
	}
};














$(function()
{	
	//bodyElement			= parent ? $('body', parent.document) : $('body');
	bodyElement			= $('body');
	
	transformProperty	= getTransformProperty(bodyElement);
	animationProperty	= getAnimationProperty(bodyElement);
	
	$('.'+VIDEO_PLAYER_ID)[VIDEO_PLAYER_ID]();
});

$['widget']('ui.' + VIDEO_PLAYER_ID,
{
	'widgetEventPrefix': 'videoplayer.',
	
	'_create': function()
	{
		var self = this;
		var data = self['element'];
		var element = data[0];
		var object = $(element);
				
		if(is(object, 'video'))
		{
			data.player_ = new VideoPlayer(object, element);
		}
		else if(is(object, 'a'))
		{
			data.player_ = new LightBoxPlayer(object, element);
		}
		
		if(data.player_)
		{			
			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_videoElement)
	{
		this['element'].player_.play(opt_videoElement);			
	},
	
	'pause': function()
	{
		this['element'].player_.pause();		
	},
	
	'volume': function(value)
	{
		var player = this['element'].player_;
		
		if(value)
		{
			var volume = parseInt(value, 10) / 100;
			player.setVolume(volume);
		}
		else
		{
			return player.volume();
		}
	},
	
	'enterFullwindow': function()
	{
		var player = this['element'].player_;
		
		if(!player.hasControllerView_)
			player.createControllerView_();
			
		player.goFullwindowMode_();
	},
	
	'exitFullwindow': function()
	{
		var player = this['element'].player_;
		
		if(!player.hasControllerView_)
			player.createControllerView_();
			
		player.exitFullwindowMode_();
	}
});

})(jQuery);

