(function ($) {

	var videoPlayer = function (selector, options) {

		this.selector	= selector;
		this.ua = ua();

		this.options	= options;
		this.playing	= false;

		this.init();
	};

	videoPlayer.prototype = {

		init: function () {
			this.selector.addClass( this.setClass('player') );
			this.selector.attr( 'tabindex', 1 );

			this.options.video.sort(function(a,b) { return b.size.width - a.size.width } );

			this.fullscreen = this.selector.hasClass('fullscreen');

			if ( this.ua.isiOS || this.ua.isAndroid ) {
				this.options.controls.splice(this.options.controls.indexOf('playback'), 1);
			}

			if ( this.ua.isiOS ) {
				this.options.controls.splice(this.options.controls.indexOf('volume'), 1);
			}

			var elem = document.createElement('video');
			if ( elem.canPlayType ) this.start();
		},

		start: function () {

			this.createVideoElement();
			this.createControls();
			this.bindControls();
			this.bindEvents();

			this.waitUntilDOMReady();
		},

		waitUntilDOMReady: function(){
			if ( typeof this.options.onVideoReady !== false ) {
				if (this.video.get(0).readyState === 4) {
					this.options.onVideoReady( this );
					return;
				}
				setTimeout(this.waitUntilDOMReady.bind(this), 100);
			}
		},

		bindControls: function () {
			var _this = this;

			for (var i = 0; i < this.options.controls.length; i++) {

				switch ( this.options.controls[i] ) {
					case 'play'			: this.setupPlay();			break;
					case 'progress'		: this.setupProgress();		break;
					case 'volume'		: this.setupVolume();		break;
					case 'resolution'	: this.setupResolution();	break;
					case 'fullscreen'	: this.setupFullscreen();	break;
					case 'stretch'		: this.setupStretch();		break;
					case 'storyboard'	: this.setupStoryboard();	break;
					case 'playback'		: this.setupPlayback();		break;
					case 'index'		:
						this.setupIndex();
						this.setupIndexPager();
						break;
					case 'time'			: this.setupTime();			break;
					default:	break;
				}
			}
		},

		resetControls: function(){ this.controls.toggleClass('control-hide', false); this.controls.toggleClass('control-show', false); },

		hideControls: function(){ this.selector.toggleClass('control-hide', true);  this.controls.toggleClass('control-show', false); },
		showControls: function(){ this.controls.toggleClass('control-hide', false); this.controls.toggleClass('control-show', true);  },

		appendElement	: function(o){ $(this.selector).append( o ); },
		showLoading		: function() { this.loading.show();	},
		hideLoading		: function() { this.loading.hide(); },



		showMsg			: function(text) {
			var _this = this;

			this.msg.text( text );
			this.msg.fadeIn('fast');
		},
		hideMsg			: function(duration) {
			var _this = this;

			setTimeout(function(){
				_this.msg.fadeOut('fast', function(){
					_this.msg.text( '' );
				});
			}, duration);
		},

		bindEvents: function () {
			var _this = this,
				video = this.video.get(0);

			this.video
				.bind('loadedmetadata', function() { _this.eventsToBind(); })
				.bind('playing seeked', function() { _this.draw( true );   })
				.bind('play seeked',	function() { _this.hidePoster();   })
				.bind('ended', function () {
					if ( typeof _this.options.onEnd === 'function') _this.options.onEnd();
				})
				.bind('canplay', function () {
					if ( typeof _this.options.onCanplay === 'function' ) {
						_this.options.onCanplay();
					}
				})
				.bind('waiting seeking', function() {
					if ( _this.paused() ) return;
					_this.showLoading();
				})
				.bind('timeupdate', function() {
					if (typeof _this.options.onTimeUpdate === 'function')
						_this.options.onTimeUpdate( _this.getCurrTime() );

					_this.selector.trigger('player.timeUpdate');
				})
				.bind('seeked durationchange suspend abort error', function() {
					_this.hideLoading();
				});

			this.playBtn.one('click', function(e){
				e.stopPropagation();
				_this.play();
			});

			$(document)
				.unbind('webkitfullscreenchange mozfullscreenchange')
				.bind('webkitfullscreenchange mozfullscreenchange',	function() {
					_this.fullscreenEvent();
				});

			var enter = 'mouseenter', leave = 'mouseleave';
			if ( typeof document.body.ontouchstart != "undefined" ){
				enter = 'touchstart';
				leave = 'touchend';
			}

			this.selector
				.bind('keypress', function(e){

					if ( e.keyCode != 32 ) return;

					_this.playPause();
					e.preventDefault();
				})
				.bind( leave, function(e) {







					_this.inactivateTimer = setTimeout(function() {

						if ( !_this.playing ) return;
						clearTimeout(_this.inactivateTimer);

						_this.selector.find(_this.getClass('video-controls')).addClass('inactive');
					}, 1500);
				})
				.bind( enter, function (e) {


					clearTimeout(_this.inactivateTimer);
					_this.selector.find(_this.getClass('video-controls')).removeClass('inactive');
				});

			this.selector.bind('contextmenu mousedown',	function () {return false;});

			this.selector.find(_this.getClass('video-controls')).bind('click', function (e) {e.preventDefault();});

			this.selector.unbind('click').bind('click', function(e){
				if ( $(e.target).attr('type') != 'screen' ) return;
				_this.playPause();
			});

			this.poster.bind('player.resize load error', function(){


				$(this).position({
					'my' : 'center center',
					'at' : 'center center',
					'of' : _this.selector
				});

				_this.videoRepos();
			});

			$(window).bind('orientationchange', function() { _this.videoRepos(); });

			$(window).bind('resize', function () {
				_this.videoRepos();


			});
		},

		videoRepos : function(){

			this.video.css('max-width', this.getResolution().width + 'px' );
			this.video.css('width', '100%');
			this.video.height( '100%' );

			this.video.position({
				'my' : 'top left',
				'at' : 'top left',
				'of' : this.poster
			});

			
			if ( parseInt(this.video.css('top'),  10) < 0 )	this.video.css('top',  0);

			if ( parseInt(this.video.css('left'), 10) < 0 )	this.video.css('left', 0);


			this.canvasRepos();
			this.resizeProgressBar();
		},

		draw: function( force ){

			if ( !this.canvasMode ) 		return;
			if ( !force && this.paused() )	return;

			var canvas  = $(this.canvas)[0].getContext('2d');

			var width	= $(this.video).width(),
				height	= $(this.video).height();

			$(this.canvas).attr('width',  width);
			$(this.canvas).attr('height', height);


			this.canvasRepos();

			var size = this.getResolution();
			var x = 0,		y = 0,
				w = width,	h = height;

			if ( !this.stretch && width > size.width ) {
				x = (width-size.width) / 2;
				w = size.width;
			}

			if ( !this.stretch && height > size.height ) {
				y = (height-size.height) / 2;
				h = size.height;
			}

			canvas.drawImage( $(this.video)[0], x, y, w, h);

			var _this = this;
			window.setTimeout(function(){ _this.draw(); }, 1000 / 33);
		},

		canvasRepos: function(){
			this.canvas.position({
				'my' : 'center center',
				'at' : 'center center',
				'of' : $(this.video)
			});
		},

		getResolution: function(){
			return this.options.video[ this.currResolution ].size;
		},
		blockSelection: function () {
			this.volumeFlag = true;
			document.onselectstart = function () {
				return false;
			};
		},

		createButton: function (labelName, className) {
			var btn = $('<div>').addClass(this.setClass(className));
			var label = $('<span>').html(labelName);
			btn.append(label);

			return btn;
		},

		createControls: function () {

			this.controls = $('<div>').addClass(this.setClass(this.options.controlsClass));

			for (var key in this.options.controls) {

				switch ( this.options.controls[key] ) {

					case 'play'			: this.controls.append( this.createPlay() );			break;
					case 'progress'		: this.controls.append( this.createProgressBar() );		break;
					case 'storyboard'	: this.controls.append( this.createStoryboard() );		break;
					case 'time'			: this.controls.append( this.createTimeDisplay() );		break;
					case 'volume'		: this.controls.append( this.createVolumeBar() );		break;
					case 'playback'		: this.controls.append( this.createPlayback() );		break;
					case 'fullscreen'	: this.controls.append( this.createFullscreen() );		break;
					case 'stretch'		: this.controls.append( this.createStretch() );			break;
					case 'resolution'	: this.controls.append( this.createResolution() );		break;

					case 'index':

						this.index = new index();
						this.index.init( this.options.index );

						this.controls.append( this.createIndex() );
						this.createIndexPager();
						break;

					case 'separator'	: this.controls.append('<div class="separator">&nbsp;</div>');			break;
					case 'leftRadius'	: this.controls.append('<div class="boderRadius left">&nbsp;</div>');	break;
					case 'rightRadius'	: this.controls.append('<div class="boderRadius right">&nbsp;</div>');	break;

					default: break;
				}
			}

			this.selector.append(this.controls);
		},

		createResolution : function(){

			if ( this.options.video.length <= 1 ) return '';

			var _this = this;

			var wrap	= $('<div>').addClass( this.setClass('resolution-wrap') );
			var btn		= $('<div>').addClass( this.setClass('resolution-button') );

			var ul = $('<ul>', {'class': 'list'});

			$.each( this.options.video, function(key, itm){

				var cls		= ( _this.currResolution == key ) ? 'active' : '';
				var title	= itm.size.width + ' x ' + itm.size.height;

				ul.append( $('<li>', {
					'class'			: cls,
					'key'			: key,
					'resolution'	: title,
					'text'			: title
				}) );
			});

			wrap.append( btn );
			wrap.append( ul );

			return wrap;
		},

		setupResolution: function(){
			var _this = this;

			var btn  = this.selector.find( this.getClass('resolution-button') ),
				list = this.selector.find( this.getClass('resolution-wrap') ).find('.list');

			$(document).bind('click', function(e){ _this.resolutionToggle( false ); });

			btn.bind('click', function (e) {
				_this.resolutionToggle( true );
				list.position({
					'of' : $(this),
					'my' : 'right bottom',
					'at' : 'right top'
				});

				e.stopPropagation();
			});

			list.find('li').bind('click', function (e){
				e.stopPropagation();

				_this.resolutionToggle( false );
				_this.setResolution( $(this).attr('key') );
			});

			this.selector.bind('player.changeResolution', function(e, key){
				var list = $(this).find( _this.getClass('resolution-wrap') ).find('.list');
				list.find('.active').removeClass('active');

				list.find('[key="' + key + '"]').addClass('active');
			});
		},

		currResolution : null,

		setResolution : function( key ){

			if ( this.currResolution == key ) return false;

			var _this = this,
				video = this.options.video[ key ];

			if ( !video ) return false;

			/* replace video */

			var curr	= this.getCurrTime();

			if ( this.currResolution ) {
				var res = this.getResolution();

				if ( parseInt(video.size.width, 10) > parseInt(res.width, 10) ) {
					this.showMsg('loading high-quality video ' + video.size.width + 'x' + video.size.height);
				} else {
					this.showMsg('loading video ' + video.size.width + 'x' + video.size.height);
				}
			}

			this.showLoading();

			var isEnd = this.ended()

			this.video.attr( {'src'	: video.src} );
			this.video.css('max-width', video.size.width + 'px');

			if ( this.currResolution == null ){ // first time
				this.currResolution = key;
				return;
			}

			this.currResolution = key;
			this.selector.trigger('player.changeResolution', key);

			if ( typeof this.options.onVideoChange === 'function') this.options.onVideoChange();

			/* reset to curr time */

			this.pause();
			this.resetVideo();
			this.videoRepos();

		//	this.video.removeAttr('autoplay').prop('preload', true);

			this.video.one('loadedmetadata', function() {
				_this.hideLoading();
				_this.hideMsg(1000);

				_this.video.one('canplay', function(){
					_this.setCurrTime(curr);

					if ( !isEnd ) _this.play();
				});
			});

			return true;
		},

		resolutionToggle : function( toggle ){
			var resolution = this.selector.find(this.getClass('resolution-wrap')).find('.list');
			resolution.toggleClass('in', toggle);
		},

		play   : function(){ this.video.get(0).play();  },
		pause  : function(){ this.video.get(0).pause(); },
		paused : function(){ return this.video.get(0).paused; },
		ended  : function(){ return this.video.get(0).ended; },

		setupFullscreen: function(){
			var _this = this;
			this.selector.find(this.getClass('fullscreen')).bind('click',	function(){ _this.toFullscreen(); });
		},

		createFullscreen : function(){
			var wrap	= $('<div>', {'class': this.setClass('fullscreen')});
			var btn		= $('<div>', {'class': 'tog', 'title': 'Fullscreen'});

			wrap.append(btn);
			return wrap;
		},

		stretch: false,
		setupStretch: function(){
			var wrap	= this.selector.find(this.getClass('stretch'));

			var _this	= this;

			this.selector.bind('player.resize player.fullscreen player.changeResolution', function(){
				var res = _this.getResolution();
				( _this.selector.width() > res.width ) ? wrap.show() : wrap.hide();
			});

			wrap.click(function(){
				( _this.selector.hasClass('stretch') )
					? _this.disableStretch()
					: _this.enableStretch();

				_this.videoRepos();
			});
		},

		enableStretch: function(){
			var btn = this.selector.find(this.getClass('stretch-btn'));

			this.stretch = true;
			this.selector.addClass('stretch');
			this.selector.find(this.getClass('stretch-btn')).addClass('active');
		},

		disableStretch: function(){
			var btn = this.selector.find(this.getClass('stretch-btn'));

			this.stretch = false;
			this.selector.removeClass('stretch');
			this.selector.find(this.getClass('stretch-btn')).removeClass('active');



		},

		createStretch : function(){

			var wrap	= $('<div>', {'class': this.setClass('stretch')}),
				btn		= $('<div>', {'class': this.setClass('stretch-btn')});

			btn.attr('title', 'Stretch');

			var res = this.getResolution();
			if ( this.selector.width() <= res.width ) wrap.hide();

			wrap.append(btn);
			this.selector.append( wrap );

			return '';
		},

		createPlay: function() {
			var wrap	= $('<div>', {'class': this.setClass('play-wrap')});
			var btn		= $('<div>', {'class': 'play paused', 'title': 'Play / Pause'});

			wrap.append(btn);

			return wrap;
		},

		setupPlay : function(){
			var _this = this;
			var wrap  = this.selector.find(this.getClass('play-wrap'));

			this.video
				.bind('playing',	function (){ _this.playEvent(this);  })
				.bind('pause',		function (){ _this.pauseEvent(this); });

			wrap.click(function(){ _this.playPause(); });
		},

		pauseEvent: function (elm) {
			var play = $(elm).parent().find('.play');

			play.toggleClass('paused',	true);
			play.toggleClass('playing', false);

			if (this.options.onPause) this.options.onPause();

			this.playing = false;
		},

		playEvent: function (elm) {
			var play = $(elm).parent().find('.play');

			play.toggleClass('paused',	false);
			play.toggleClass('playing', true);

			if (this.options.onPlay) this.options.onPlay();

			this.playing = true;
		},

		playPause: function () {
			var _this = this;

			if ( this.paused() ) {
				this.play();
				this.bezel.find('div').removeClass( 'pause' );
				this.bezel.find('div').addClass( 'play' );

			} else {
				this.pause();
				this.bezel.find('div').removeClass( 'play' );
				this.bezel.find('div').addClass( 'pause' );
			}

			this.bezel.show();
			this.bezel.addClass( this.setClass('bezel-fade') );

			this.bezel.bind('transitionend', function(){
				_this.bezel.removeClass( _this.setClass('bezel-fade') );
				_this.bezel.find('div').removeAttr( 'class' );
			});
		},

		createProgressBar: function () {
			var progressBar		= $('<div>').addClass(this.setClass('progress-bar'));

			var progressWrapper = $('<div>').addClass(this.setClass('progress-wrapper'));

			var progressPlay	= $('<div>').addClass(this.setClass('progress-play'));
			var progressPointer	= $('<div>').addClass(this.setClass('progress-pointer'));
			var progressBuffer	= $('<div>').addClass(this.setClass('progress-buffer'));

			progressWrapper.append(progressPlay);
			progressWrapper.append(progressPointer);
			progressWrapper.append(progressBuffer);
			progressBar.append(progressWrapper);

			return progressBar;
		},

		createStoryboard: function(){

			this.storyboard = $('<div>', {'id' : 'storyboard', 'class': this.setClass('storyboard-wrapper')});

			var	thumbnail		 = $('<div>', {'class'	: this.setClass('thumbnail')}),
				timestamp		 = $('<div>', {'class'	: this.setClass('timestamp')}),
				arrow			 = $('<div>', {'class'	: 'arrow'});

			if ( this.options.storyboard.path ) {

				this.storyboard.addClass('with-thumbnail');

				var dim = this.options.storyboard.dimension.split('x');

				this.storyboard.ratio = 1;

				thumbnail.css({
					'background-image'		: 'url(' + this.options.storyboard.path + ')',
					'background-size'		: dim[0] * this.storyboard.ratio * this.options.storyboard.size.width + 'px ' + dim[1] * this.storyboard.ratio*this.options.storyboard.size.height + 'px',
					'background-position'	: '0 0',
					'width'			  		: this.storyboard.ratio * this.options.storyboard.size.width,
					'height'			  	: this.storyboard.ratio * this.options.storyboard.size.height
				});

			} else {
				this.storyboard.addClass('with-text');
			}

			this.storyboard.append( thumbnail );
			this.storyboard.append( timestamp );
			this.storyboard.append( arrow );

			return this.storyboard;




		},

		setupStoryboard : function(){

			var	timestamp	= this.storyboard.find( this.getClass('timestamp') ),
				_this		= this;

			var progBar = this.selector.find( this.getClass('progress-wrapper') );

			progBar
				.bind('mouseenter', function(){ _this.storyboard.show();})
				.bind('mouseleave', function(){ _this.storyboard.hide();});

			this.selector.bind('player.progressBarMoved', function( ev, e ){

				var progBarX = _this.findPosX( progBar );

				var width	= Math.max(0, Math.min(1, ( e.pageX - progBarX ) / progBar.width() )),
					sec		= width * _this.getDuration();

				if ( _this.storyboard.hasClass('with-thumbnail')) {

					var pps = _this.getDuration() / _this.options.storyboard.count;
					var no  = Math.ceil(sec / pps)+1;

					if ( no > _this.options.storyboard.count ) no = _this.options.storyboard.count;

					var dim			= _this.options.storyboard.dimension.split('x'),
						thumbnail	= _this.storyboard.find( _this.getClass('thumbnail') );

					var x = -1 * _this.storyboard.ratio * _this.options.storyboard.size.width  * ( ( no-1 ) % dim[0] ) + 'px',
						y = -1 * _this.storyboard.ratio * _this.options.storyboard.size.height * ( Math.floor((no-1) / dim[0]) ) + 'px';

					thumbnail.css({
						'background-position' : x + ' ' + y
					});
				}

				timestamp.html( _this.formatTime( parseInt(sec, 10) ) );

				var xpos = (e.offsetX === undefined) ? e.originalEvent.layerX : e.offsetX;

				_this.storyboard.css({
					'left': xpos + (progBarX - _this.findPosX(_this.selector)) - ( _this.storyboard.width() / 2)
				});
			});
		},

		createTimeDisplay: function () {
			var time		= $('<div>').addClass(this.setClass('time-display')),
				current		= $('<span>', {'class': 'current'}),
				separator	= $('<span>', {'class': 'separator'}).html( '/' ),
				total		= $('<span>', {'class': 'total'});

			var duration = this.getDuration();

			time.css('width', ( duration < 3600 ) ? 80 : 100 );

			current.html( this.formatTime(0) );

			time.append(current);
			time.append(separator);
			time.append(total);

			var _this = this;

			this.selector.bind('player.timeUpdate', function(){
				current.html( _this.formatTime( _this.getCurrTime() ) );
			});

			return time;
		},

		canvasMode : false,

		enableCanvasMode: function(){
			this.canvasMode = true;
			this.canvas.css('visibility', 'visible');
			this.video.css('visibility', 'hidden');

			this.selector.trigger('player.enableCanvasMode');

			if ( typeof this.options.onModeChange != false )
				this.options.onModeChange( true );
		},

		disableCanvasMode: function(){
			this.canvasMode = false;
			this.canvas.css('visibility', 'hidden');
			this.video.css('visibility', 'visible');

			this.selector.trigger('player.disableCanvasMode');

			if ( typeof this.options.onModeChange != false )
				this.options.onModeChange( false );
		},

		createVideoElement: function () {

			this.playBtn	= $('<div>', {'class' : this.setClass('playBtn')})
			this.bezel		= $('<div>', {'id' : 'bezel', 'class' : this.setClass('center-overlay'), 'html': $('<div>') });
			this.video		= $('<video>',  {'preload'	: 'auto', 'type' : 'screen', 'x-webkit-airplay': 'allow', 'airplay': "allow"});
			this.canvas		= $('<canvas>', {'class'	: this.setClass('canvas'), 'type' : 'screen'});
			this.msg		= $('<div>',	{'class'	: this.setClass('msg') });

			this.loading = $('<div>', {'class':this.setClass('loading')});
			this.loading.append( $('<div>', {'class': 'msg'}) );

			if ( this.options.poster ) {
				this.poster = $('<img>', {'src':this.options.poster, 'class':this.setClass('poster')});
				$(this.selector).append( this.poster );
			}

			if ( this.options.autoplay ) this.video.prop('autoplay', true);

			$(this.selector).append(this.bezel);

			$(this.selector).append(this.video);
			$(this.selector).append(this.canvas);
			$(this.selector).append(this.loading);
			$(this.selector).append(this.playBtn);
			$(this.selector).append(this.msg);

			var videoKey	= this.autoSelectVideo();
			this.setResolution( videoKey );
		},

		createPlayback : function(){

			var playback			= $('<div>').addClass(this.setClass('playback')),
				playbackBtn			= $('<div>').addClass(this.setClass('playback-button')).html( this.options.playback.def.toFixed(1) + 'x' ),
				playbackBar			= $('<div>').addClass(this.setClass('playback-bar')),
				playbackWrapper		= $('<div>').addClass(this.setClass('playback-wrapper')),
				playbackPosition	= $('<div>').addClass(this.setClass('playback-position'));

			playback.addClass('inactive');
			playbackWrapper.append(playbackPosition);

			playbackBar.append(playbackWrapper);

			playback.append(playbackBtn);
			playback.append(playbackBar);

			return playback;
		},

		playbackTimer : null,
		setupPlayback: function () {

			var wrap	= this.selector.find(this.getClass('playback-wrapper')),
				btn		= this.selector.find(this.getClass('playback-button'));

			this.setPlaybackrate(1);

			var _this	= this;

			btn.click(function(){ _this.setPlaybackrate(1); });

			wrap
				.bind('mousedown', function (e) {
					e.preventDefault();

					_this.blockSelection();

					$(document).bind('mousemove', function(e){
						e.preventDefault();
						_this.playbackTo( e.pageY );
					});

					$(document).bind('mouseup', function(e) {
						e.preventDefault();

						$(document).unbind('mousemove');
						$(document).unbind('mouseup');
					});
				})
				.bind('mouseup', function (e) { _this.playbackTo( e.pageY ); });

			var playback = this.selector.find(this.getClass('playback'));

			playback
				.bind('mouseleave', function(e) {
					e.preventDefault();

					clearTimeout(_this.inactivateTimer);
					_this.playbackTimer = setTimeout(function() {
						playback.addClass('inactive');
					}, 1000);
				})
				.bind('mouseenter', function (e) {
					e.preventDefault();
					clearTimeout(_this.playbackTimer);
					playback.removeClass('inactive');
				});
		},

		playbackTo: function ( yPos ) {

			var wrap		= this.selector.find(this.getClass('playback-wrapper')),
				wrapHeight	= Math.max(0, Math.min(1, ( yPos - wrap.offset().top ) / wrap.height() )),
				bar			= $(this.getClass('playback-position'));

			var playback	= this.options.playback;
			var invertedPercent = Math.abs( (wrapHeight-1) * -1 );

			var	pb		= Math.round( invertedPercent * playback.gaps.length ),
				rate	= playback.gaps[ pb ];

			if ( !rate ) return;

			this.setPlaybackrate(rate);
		},

		setPlaybackrate: function(val){

			if ( !$.inArray(val, this.options.playback) ) return;

			this.setPlaybackrateBar(val);

			var video = this.video.get(0);
			video.playbackRate = val;

			if ( typeof this.options.onPlaybackChange == 'function' )	this.options.onPlaybackChange( val );
			if ( typeof video.defaultPlaybackRate != 'undefined' )		video.defaultPlaybackRate = val;
		},

		setPlaybackrateBar: function( val ){

			var playback	= this.options.playback;

			var	step	= playback.gaps.length,
				pb		= playback.gaps.indexOf(val);

			var bar = $(this.getClass('playback-position'));
			bar.height( pb/(step-1)*100 + '%');

			$(this.getClass('playback-button')).html( val.toFixed(1) + 'x');
		},

		createVolumeBar: function () {

			var volume			= $('<div>').addClass(this.setClass('volume')),
				volumeBtn		= $('<div>').addClass(this.setClass('volume-button')).html('Volume'),
				volumeBar		= $('<div>').addClass(this.setClass('volume-bar')),
				volumeWrapper	= $('<div>').addClass(this.setClass('volume-wrapper')),
				volumePosition	= $('<div>').addClass(this.setClass('volume-position'));

			volume.addClass('inactive');
			volumeWrapper.append(volumePosition);

			volumeBar.append(volumeWrapper);

			volume.append(volumeBtn);
			volume.append(volumeBar);

			return volume;
		},

		setupIndexPager: function () {

			var _this = this;

			var index = this.index.get();
			if ( !index || index.length <= 1) return;


			this.selector.bind('player.indexChange', function(){

				var index = _this.index.get();

				_this.pager.prev.toggleClass('active', true);
				_this.pager.next.toggleClass('active', true);

				var prev = index[ _this.currentIndex-1 ],
					next = index[ _this.currentIndex+1 ];

				if ( !prev ) _this.pager.prev.toggleClass('active', false);
				if ( !next ) _this.pager.next.toggleClass('active', false);
			} );

			$(this.selector).find( this.getClass('prev') ).click(function(){
				if ( _this.video.get(0).seeking ) return;

				var index = _this.index.get();
				_this.setIndex( index[ _this.currentIndex - 1 ] );
			});

			$(this.selector).find( this.getClass('next') ).click(function(){
				if ( _this.video.get(0).seeking ) return;

				var index = _this.index.get();
				_this.setIndex( index[ _this.currentIndex + 1 ] );
			});
		},

		createIndexPager: function () {

			var index = this.index.get();
			if ( !index || index.length <= 1) return;

			var _this = this;

			this.pager = {
				'prev' : $('<div>').addClass( this.setClass('prev') ),
				'next' : $('<div>').addClass( this.setClass('next') )
			};

			this.pager.next.addClass('active');

			$(this.selector).append( this.pager.prev );
			$(this.selector).append( this.pager.next );
		},

		createIndex: function () {

			var index = this.index.get();

			if ( !index || index.length == 0) return;

			var wrap = $('<div>').addClass(this.setClass('index-wrap')),
				btn  = $('<div>', {
				'class' : 'paragraph',
				'html'	: '<span class="curr">0</span>' + '/' + '<span class="total">' + index.length + '</span>'
			});

			var ul = $('<ul>', {'class': 'list'});

			for ( var key in index ) {
				var sn = parseInt(key, 10) + 1;
				ul.append( $('<li>', {
					'idxID'	: index[ key ].id,
					'key'	: key,
					'class' : this.setClass('index-li'),
					'text'	: sn + '. ' + index[ key ].title
				}) );
			}

			wrap.append( ul );
			wrap.append( btn );

			return wrap;
		},

		indexPanelToggle : function( toggle ){
			var list = this.selector.find(this.getClass('index-wrap')).find('.list');
			list.toggleClass('in', toggle);
		},

		currentIndex : null,

		setupIndex : function(){

			var indexWrap	= this.selector.find( this.getClass('index-wrap') ),
				indexList	= indexWrap.find('.list');

			if ( indexList.length == 0 ) return;

			var _this = this;

			$(document).bind('click', function(e){ _this.indexPanelToggle( false ); });

			indexWrap.bind('click', function(e) {
				indexList.toggleClass('in');
				indexList.position({
					'of' : $(this),
					'my' : 'left bottom',
					'at' : 'left top'
				});

				e.stopPropagation();
			});

			indexList.find('li').bind('click', function (e){
				_this.indexPanelToggle( false );
				_this.setIndex( _this.index.get( $(this).attr('key') ) );
				e.stopPropagation();
			});

			this.selector.bind('player.timeUpdate', function(){

				var index		= _this.index.get().slice(0),
					currentKey	= _this.currentIndex;

				index.reverse();

				currentTime		= _this.getCurrTime();

				$.each( index, function( key, itm ){

					if ( ( parseFloat(itm.time) / 1000 ) > currentTime ) return;
					currentKey = key;

					return false;
				});

				if ( index[ currentKey ] && index[ currentKey ].key != _this.currentIndex) {
					_this.currentIndex = index[ currentKey ].key;

					_this.setIndexNotifier( index[ currentKey ] );

					_this.selector.trigger('player.indexChange', index[ currentKey ]);

					if ( typeof _this.options.onIndexChange === 'function' )
						_this.options.onIndexChange( index[ currentKey ] );
				}
			});
		},

		setIndex : function( index ){
			this.setCurrTime( parseFloat( index.time / 1000 ) );
		},

		setIndexNotifier : function( index ){

			var list = this.selector.find(this.getClass('index-wrap')).find('.list');

			list.find('li').removeClass('active');
			list.find('[idxID=' + index.id + ']').addClass('active');

			this.selector.find(this.getClass('index-wrap')).find('.curr').text( index.sn );
		},

		eventsToBind: function () {
			if ($.inArray('progress',	this.options.controls))	this.setProgressEvents(this.video.get(0));
		},

		findPosX: function (obj) {
			obj = obj.get(0);
		//	var curleft = obj.offsetLeft;
		//	while(obj = obj.offsetParent) curleft += obj.offsetLeft;

			return $(obj).offset().left;
		},

		findPosY: function (obj) {
			obj = obj.get(0);
			/*
			var curtop = obj.offsetTop;
			while(obj = obj.offsetParent) {
				curtop += obj.offsetTop;
			}
*/
			return $(obj).offset().top;
		},

		formatTime: function (secs) {

			secs	= Math.floor(secs);
			var hr	= Math.floor(secs / 3600);
			var min = Math.floor((secs - (hr * 3600))/60);
			var sec = secs - (hr * 3600) - (min * 60);

			if (min < 10) {min = '0' + min;}
			if (sec < 10) {sec = '0' + sec;}

			return ( parseInt(hr, 10) > 0 )
				? hr  + ':' + min + ':' + sec
				: min + ':' + sec;
		},

		resetVideo: function () {
			this.controls.find(this.getClass('video-controls') + ', ' + this.getClass('progress-buffer')).css('width', 0);
			clearInterval(this.bufferInterval);
		},

		fullscreenEvent: function () {

			this.hidePoster();

			var fullScreenBtn = $(this.getClass('fullscreen'));
			fullScreenBtn.toggleClass('fullscreen');

			this.disableStretch();
			if ( !this.fullscreen ) {
				this.selector.addClass('fullscreen');
				this.fullscreen = true;
			}
			else {
				this.selector.removeClass('fullscreen');
				this.fullscreen = false;
			}

			$(window).trigger('resize');




			var key = this.autoSelectVideo();
			this.setResolution( key );

			this.videoRepos();

			if ( !this.ended() ) this.play();

			this.selector.trigger('player.fullscreen');

			if ( typeof this.options.onFullscreen === 'function' )
				this.options.onFullscreen( this.fullscreen );
		},

		getCurrTime : function() {
			return this.video.get(0).currentTime;
		},

		setCurrTime : function( pos ){

			var flag = true;
			try {
				this.video.get(0).currentTime = parseFloat(pos) + .1;
				flag = false;
			}
			catch(e){
				flag = false;
			}

			return flag;
		},

		getClass : function (name)	{ return '.' + this.options.prefix + name; },

		getControlsSize: function () {
			var size  = 0,
				_this = this;

			this.selector.find(this.getClass('video-controls')).children().each(function () {
				var element = $(this);

				if ( element.is(':visible') && !element.hasClass(_this.setClass('progress-bar')) ) size += element.outerWidth(true);
			});

			return size;
		},

		autoSelectVideo: function () {

			var width = this.selector.width();

			if ( this.fullscreen ) {
				width = window.screen.width;

				if (window.devicePixelRatio) width *= window.devicePixelRatio;
			}


			var	videos	= this.options.video.slice(0),
				key		= 0;

			var vs = new Array();
			$.each(videos, function(k, i){ i.key = k; vs.push(i); });

			$.each(vs, function(k, i){
				if ( i.size.width > width ) return;
				key = i.key;
				return false;
			});

			return key;
		},

		hidePoster: function () {
			this.video.removeAttr('poster');
			this.poster.css('visibility', 'hidden');
			this.playBtn.remove();
		},

		mute: function () {

			var volumeButton	= this.selector.find(this.getClass('volume-button'));
			var volumePosition	= this.selector.find(this.getClass('volume-position'));
			var video			= this.video.get(0);

			volumeButton.removeClass('muted');
			volumeButton.removeClass('half');

			if ( video.muted ) {
				video.muted = false;
				volumePosition.height('80%');
			}
			else {
				video.muted = true;
				volumeButton.addClass('muted');
				volumePosition.height(0);
			}
		},

		seekTo: function (xPos, progWrapper, video) {

			this.hidePoster();
			var progressBar = $(this.getClass('progress-play'));
			var progWidth	= Math.max(0, Math.min(1, ( xPos-this.findPosX(progWrapper) ) / progWrapper.width() ));
			var pointer		= progressBar.find(this.getClass('progress-pointer'));

			var seekTo = progWidth * this.getDuration();
			this.setCurrTime(seekTo);

			var progPos = (seekTo*100 / this.getDuration()) + '%';

			pointer.css('left', progPos);
			progressBar.width(progPos);
		},

		seekVideoSetup: function () {
			var video = this.video.get(0);
			var progWrapper = $(video).parent().find(this.getClass('progress-wrapper'));

			if (!progWrapper.length > 0) return;

			var _this = this;

			progWrapper.bind('click', function (e) {
				e.stopPropagation();
				_this.blockSelection();
				_this.seekTo(e.pageX, progWrapper, video);
				_this.unblockSelection();
			});
		},

		setClass: function (name) { return this.options.prefix + name; },

		setProgressEvents: function (video) {
			var _this		= this;
			var scrubbing	= $(this.getClass('progress-play'));
			var buffer		= $(this.getClass('progress-buffer'));

			this.video.bind('ended', function () {

				scrubbing.width('100%');
				clearInterval(_this.progressInterval);

				this.pause();
				_this.playing = false;
			});

			this.selector.bind('player.timeUpdate', function(){

				var pointer			= scrubbing.parent().find(_this.getClass('progress-pointer')),
					scrubbingWidth	= _this.getCurrTime()*100 / video.duration;

				scrubbing.width(scrubbingWidth + '%');
				pointer.css('left', scrubbingWidth + '%');
			});
		},

		setupProgress : function(){
			var _this = this;

			this.bufferInterval = setInterval(function () {
				_this.updateBuffer();
			}, 1500);

			this.selector.find( this.getClass('progress-pointer') ).bind('mousemove', function(e){ e.stopPropagation(); });
			this.seekVideoSetup();

			this.resizeProgressBar();
		},

		resizeProgressBar: function () {
			var _this = this;

			var progressBar		= this.selector.find(_this.getClass('progress-bar')),
				progBarWidth	= this.selector.width(),
				progWrapper		= this.selector.find(_this.getClass('progress-wrapper'));

			progWrapper.unbind('mousemove').bind('mousemove', function(e){ _this.selector.trigger('player.progressBarMoved', e); });

	//		setTimeout(function(){

				var controlsSize	= progBarWidth - parseInt(_this.getControlsSize(), 10);

				progressBar.parent().width(progBarWidth); // avoid resize issues on responsive templates
				progressBar.width(controlsSize);
	//		}, 500);
		},

		getDuration: function(){
			return ( !isNaN(this.options.duration) && this.options.duration != 0 )
				? this.options.duration
				: this.video.get(0).duration;
		},

		setupTime: function () {

			var time		= this.selector.find(this.getClass('time-display')),
				_this		= this;

			var duration	= this.getDuration();

			if ( isNaN(duration) ) duration = '';
			time.find('.current').html( this.formatTime(0) );
			time.find('.total').html( this.formatTime( parseInt(duration, 10) ));

			time.css('width', ( duration < 3600 ) ? 80 : 100 );

			this.selector.bind('player.timeUpdate', function(){
				time.find('.total').html( _this.formatTime( parseInt(_this.getDuration(), 10) ));
				time.find('.current').html( _this.formatTime( _this.getCurrTime() ) );
			});
		},

		timecodeToSec: function (tc) {
			if (!tc) return;
			tc1 = tc.split(',');
			tc2 = tc1[0].split(':');
			seconds = Math.floor(tc2[0] * 60 * 60) + Math.floor(tc2[1] * 60) + Math.floor(tc2[2]);

			return (seconds*1000+tc1[1]*100) / 1000;
		},

		toFullscreen: function () {
			clearTimeout(this.fullscreenTimeout);

			if ( !this.fullscreen ) {

				if (this.selector.get(0).webkitRequestFullScreen) {
					this.selector.get(0).webkitRequestFullScreen();
				}
				else if (this.selector.get(0).mozRequestFullScreen) {
					this.selector.get(0).mozRequestFullScreen();
				} else {
					this.fullscreenEvent();
				}
			}
			else {
				if (document.webkitCancelFullScreen) {
					document.webkitCancelFullScreen();
				}
				else if (document.mozCancelFullScreen) {
					document.mozCancelFullScreen();
				} else {
					this.fullscreenEvent();
				}
			}
		},

		unblockSelection: function () {
			this.volumeFlag = false;
			document.onselectstart = function () {
				return true;
			};
		},




		updateBuffer: function () {
			var video = this.video.get(0);
			var buffer	= this.selector.find(this.getClass('progress-buffer'));

			var buffered;
			if ( video.buffered && video.buffered.length > 0 ) {
			
				buffered = video.buffered.end(0) / video.duration;
				buffer.width( (buffered * 100) + '%' );

				if (buffered == 1) clearInterval( this.bufferInterval );
			}
			else {
				buffer.width('100%');
				clearInterval( this.bufferInterval );
			}
		},

		volumeTimer: null,
		setupVolume: function () {

			var video		= this.video.get(0),
				volWrapper	= this.selector.find(this.getClass('volume-wrapper')),
				volArea		= $(this.getClass('volume'));

			var _this		= this;

			this.volumeFlag = false;

			this.selector.find(_this.getClass('volume-button')).bind('click', function(){ _this.mute(); });
			volWrapper.bind('mousedown', function (e){
				e.preventDefault();

				_this.blockSelection();

				$(document).bind('mousemove', function (e) {
					e.preventDefault();

					var volHeight = Math.max(0, Math.min(1, ( e.pageY - volWrapper.offset().top ) / volWrapper.height() ));
					var invertedPercent = (volHeight-1) * -1;

					_this.setVolume( invertedPercent );
				});

				$(document).bind('mouseup', function (e) {
					e.preventDefault();

					if (e.target.className.search('volume') < 0)
						volArea.removeClass('hover')

					_this.unblockSelection();

					$(document).unbind('mousemove');
					$(document).unbind('mouseup');
				});
			});

			volWrapper.bind('mouseup', function (e) {
				var volHeight = Math.max(0, Math.min(1, ( e.pageY - volWrapper.offset().top ) / volWrapper.height() ));
				var invertedPercent = (volHeight-1) * -1;

				_this.setVolume( invertedPercent );
			});

			volArea
				.bind('mouseleave', function(e) {
					e.preventDefault();

					clearTimeout(_this.inactivateTimer);
					_this.volumeTimer = setTimeout(function() {
						volArea.addClass('inactive');
					}, 1000);
				})
				.bind('mouseenter', function (e) {
					e.preventDefault();
					clearTimeout(_this.volumeTimer);
					volArea.removeClass('inactive');
				});

		},

		setVolume: function ( vol ) {


			var volWrapper	= this.selector.find(this.getClass('volume-wrapper'));

			var volumeBar = $(this.getClass('volume-position'));

			if ( typeof this.options.onVolumeChange == 'function' )	this.options.onVolumeChange( vol );




			this.video.get(0).volume = vol;
			volumeBar.height(vol * 100 + '%');

			var volumeButton = $(this.getClass('volume-button'));

			volumeButton.removeClass('muted');
			volumeButton.removeClass('half');

			if		(vol <=  0)	volumeButton.addClass('muted');
			else if (vol <= .5)	volumeButton.addClass('half');
		},

		resize : function(){

			var videoKey	= this.autoSelectVideo();
			this.setResolution( videoKey );
			this.videoRepos();

			this.enableStretch();

			this.selector.trigger('player.resize');
		}
	};

	function ua(){

		var userAgent = window.navigator.userAgent.toLowerCase(),
			t = new Object;

		t.isiPad	= (null !== userAgent.match(/ipad/i) );
		t.isiPhone	= (null !== userAgent.match(/iphone/i) );
		t.isiOS		= ( t.isiPhone || t.isiPad );

		t.isAndroid				= ( null !== userAgent.match(/android/i) );
		t.isBustedAndroid		= ( null !== userAgent.match(/android 2\.[12]/) );
		t.isBustedNativeHTTPS	= ( "https:" === location.protocol && (null !== userAgent.match(/android [12]\./) || null !== userAgent.match(/macintosh.* version.* safari/)) );

		return t;
	}

	function index() {

		var _index = new Array();

		this.init = function( idx ) {
			var _this		= this;

			var s = 1, k = 0;
			$.each(idx, function( key, itm ){
				itm.sn	= s;
				itm.key = k;

				_index[k] = itm;

				s++; k++;
			});
		}

		this.get = function(key) { return (key) ? _index[ key ] : _index; }
	}

	$.fn.jQPlayer = function(options) {

		var defaults = {
			controls		: ['separator', 'play', 'progress', 'time', 'volume', 'fullscreen', 'separator'],
			controlsClass	: 'video-controls',

			playback		: {def:1.0, gaps:[.5, .6, .7, .8, .9, 1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.3, 3.6, 4.0, 4.5, 5.0]},

			volume			: 1,

			onBeforeInit	: false,
			onAfterInit		: false,
			onSetup			: false,
			onPlay			: false,
			onSeek			: false,
			onPause			: false,
			onEnd			: false,

			onVolumeChange	: false,
			onVideoChange	: false,
			onVideoReady	: false,
			onIndexChange	: false,

			onModeChange	: false,

			onCanplay		: false,
			onSourceChange	: false,

			onPlaybackChange : false,

			onFullscreen	: false,
			prefix			: 'fs-',
			timeSeparator	: '/'
		};

		var options = $.extend(defaults, options);
		return new videoPlayer(this, options);
	};
})(jQuery);
