/* 
 * plugin: TJVideo
 * author: Alex Prokop
 * version: 1.3
 * description: jQuery plugin for creating ThinkJam Nonverblaster players, which fallback to HTML5 video elements - the exposed API and Events will work the same on either version
 * dependencies: jQuery, SWFObject, ThinkJam Nonverblaster player
 * methods: togglePlay(boolean), toggleMute(boolean), loadVideo(string teaserUrl, string videoUrl, int id (for video options xml), boolean videoOptions (whether to show video options button))
 * options: see below for default options, including passing callback functions
 * events: TJVideo.VIDEO_PLAYING, TJVideo.VIDEO_PAUSED, TJVideo.VIDEO_ENDED, TJVideo.VIDEO_MUTED, TJVideo.VIDEO_UNMUTED
 *
 * usage:
 *
 * $jQueryElement.TJVideo({src:'/path/to/source', poster:'/path/to/poster'});	// see below for additional options
 *
 * when using callbacks, the TJVideo instance itself is returned as a parameter, so you can call methods of it and access it's properties (see TJVideo constructor below);
 * you can also access the TJVideo instance of a jQuery element using the jQuery data function on that element, like so: $jQueryElement.data('TJVideo');
 *
 * changelog:
 *
 * v 1.1
 * Added ability to override default options and js options with data attributes on the DOM element
 * 
 * v 1.2
 * Added onFlashReady attribute, event is sent from flash when it's ready, added onFlashReady to default options
 *
 * v 1.2.1
 * Don't create video image tag if poster is empty
 *
 * v 1.3
 * Try to fix Safari bug for only firing ended event once for HTML5 Video
 */

(function($) {
	$.fn.TJVideo = function (opts) {		
		var video;
		var options = $.extend({}, TJVideo.defaultOptions, opts);
		var instanceOptions;
				
		this.each(function() {
			var $this = $(this);
									
			$.extend(options, getOptionsFromDataAttributes($this));
						
			video = new TJVideo($(this), options);
			$(this).data('TJVideo', video);
			video.init();
		});
		
		function getOptionsFromDataAttributes ($element) {
			var options = {};
			var attrs = $element[0].attributes;
						
			var key;
			var value;
			
			$.each(attrs, function () {
				key = this.nodeName;
				value = this.nodeValue;
								
				if(key.indexOf('data-') > -1) {
					
					key = toCamelCase(key);
										
					if(key in TJVideo.defaultOptions) {
						value = (value == "true") ? true : value;
						value = (value == "false") ? false : value;
						options[key] = value;
					}
				}
			});
			
			return options;
		}
		
		function toCamelCase(key) {
			var a = key.substring(5).split('-');
			var str = "";
			
			$.each(a, function (i) {
				if(i > 0) {
					str += ucFirst(this);	
				}
				else {
					str += this;
				}
			});
			
			return str;
		}
		
		function ucFirst(string) {
			return string.charAt(0).toUpperCase() + string.slice(1);
		}
				
		return this;
	}
})(jQuery);

var TJVideo = function ($element, options) {
		
	if(!$element.length) return;
					
	this.$element = $element;
	this.options = options;
	
	// public vars - not really necessary to list here but useful as a reference:
	this.videoTag;
	this.videoFlash;
	this.videoEndedCalled = false;
	this.isFlash;
	this.isMuted = this.options.muted;
	this.initialised = false;
	this.id = this.$element.attr('id');
	this.flashId = this.id + 'Flash';
	this.index = TJVideo.index;
	
	// in order for flash to call callbacks of specific TJVideo instances we need to store references to each instance statically using an index value which is sent with the callback (see TJVideo.flashCallback)
	TJVideo.instances[TJVideo.index++] = this;
}

// static

TJVideo.defaultOptions = {
	src: "",
	poster: "",
	xml: "",										// if using video options overlay (for standard Warner sites), contains data for multiple videos and uses and id to distinguish which section to load (see loadTJVideo)
	autoplay: false,
	controls: true,
	loop: false,
	muted: false,
	scaleToFit: false,								// if you want 100% width and height, set to true otherwise the current css width and height of the $element will be used
	showVideoOptions: false,						// show video options overlay button (for standard Warner sites)
	skipFlash: false,								// if you want to use HTML5 video without even trying Flash (mainly for testing)
	controlColour: "0xffffff",
	pathToNonverblaster: "./NonverBlaster.swf",
	onVideoPlaying: null,
	onVideoPaused: null,
	onVideoEnded: null,
	onVideoMuted: null,
	onVideoUnmuted: null,
	onFlashReady: null,
	defaultVideoId:"0",
	useDeviceFonts: "false",
	moreVideosText: "Video Options"
};

TJVideo.flashCallback = function (callbackName, index, param) {
	TJVideo.instances[index][callbackName](param);
}

TJVideo.index = 0;
TJVideo.instances = new Array();

TJVideo.VIDEO_PLAYING = "onTJTJVideoPlaying";
TJVideo.VIDEO_PAUSED = "onTJTJVideoPaused";
TJVideo.VIDEO_ENDED = "onTJTJVideoEnded";
TJVideo.VIDEO_MUTED = "onTJTJVideoMuted";
TJVideo.VIDEO_UNMUTED = "onTJTJVideoUnmuted";

// public

TJVideo.prototype.init = function () {	
	if(this.options.skipFlash) {
		this.onSWFEmbed({success:false});
	}
	else {
		this.initFlashVideo();	
	}
}

TJVideo.prototype.initFlashVideo = function () {
	this.$element.append($('<div></div>').attr('id', this.flashId));
	
	var width, height;
	
	if(this.options.scaleToFit) {
		width = "100%";
		height = "100%";
	}
	else {
		width = this.$element.css('width');
		height = this.$element.css('height');
	}
				 
	var flashvars = {
		mediaURL: this.options.src,
		teaserURL: this.options.poster,
		allowSmoothing: "true",
		autoPlay: this.options.autoplay,
		buffer: "6",
		showTimecode: "false",
		loop: this.options.loop,
		controlColor: this.options.controlColour,
		controlBackColor: "0x000000",
		scaleIfFullScreen: "true",
		showScalingButton: "true",
		defaultVolume: "100",
		mute: this.options.muted,
		xmlurl: this.options.xml,
		currentvideo: this.options.defaultVideoId,
		crop: "true",
		callback:"TJVideo.flashCallback",
		onplay:"onVideoPlaying",
		oncomplete: "onVideoEnded",
		onpause: "onVideoPaused",
		//onstart: "onVideoStarted",
		onmute: "onFlashVideoMuted",
		onflashready: "onFlashReady",
		enablevideooptions: this.options.showVideoOptions,
		alwaysshowcontrols: "false",
		controlsenabled: this.options.controls,
		index:this.index,
		usedevicefonts: this.options.useDeviceFonts,
		morevideostext: this.options.moreVideosText
	}
 
	var params = {
		menu: "false",
		allowFullScreen: "true",
		allowScriptAccess: "sameDomain",
		wmode: "opaque",
		bgcolor: "#000000"
	}
 
	var attributes = {
		id: this.flashId,
		name: this.flashId
	}
	
	var self = this;
 
	swfobject.embedSWF(this.options.pathToNonverblaster, this.flashId, width, height, "9", "/js/expressInstall.swf", flashvars, params, attributes, function(e) {self.onSWFEmbed(e)});
}

TJVideo.prototype.onSWFEmbed = function (e) {
	if(!e.success) {
		this.isFlash = false;
		this.initHTML5Video();
		this.initialised = true;
	}
	else {
		this.isFlash = true;
	}
}

TJVideo.prototype.onFlashReady = function(e) {
	this.videoFlash = this.getFlashMovie(this.flashId);
	this.initialised = true;

	this.callCallback(this.options.onFlashReady);
}


TJVideo.prototype.getFlashMovie = function (movieName) {
    var isIE = navigator.appName.indexOf("Microsoft") != -1;
    return (isIE) ? window[movieName] : document[movieName];
}

TJVideo.prototype.initHTML5Video = function () {
	var $video = $('<video></video>').attr({preload:"preload", autobuffer:"autobuffer"});
	
	if(this.options.poster.length) {
		$video.attr('poster', this.options.poster);	
	}
	if(this.options.controls) {
		$video.attr('controls', 'controls');
	}
	if(this.options.autoplay) {
		$video.attr('autoplay', 'autoplay');
	}
	
	var $src = $('<source />').attr({src:this.options.src, type:'video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\"'});
	$video.append($src);
	
	if(this.options.poster.length) {
		var $img = $('<img />').attr({src:this.options.poster, alt:"Poster Image", title:"No video playback capabilities."});
		$video.append($img);
	}
	
	this.$element.append($video);
	
	this.videoTag = $video[0];
	this.videoTag.muted = this.options.muted;
			
	this.createEventDelegate('playing', this.onVideoPlaying);
	this.createEventDelegate('pause', this.onVideoPaused);
	this.createEventDelegate('ended', this.onVideoEnded);
	this.createEventDelegate('seeked', this.onHTML5VideoSeeked);
	this.createEventDelegate('timeupdate', this.onHTML5VideoTimeUpdate);
	this.createEventDelegate('volumechange', this.onHTML5VideoVolumeChange);
}

TJVideo.prototype.createEventDelegate = function (event, callback) {
	var self = this;
	
	this.videoTag.addEventListener(event, function (e) {
		callback.call(self, e);
	}, false);
}

TJVideo.prototype.loadVideo = function (videoUrl, teaserUrl, videoOptions, videoOptionsId) {
	teaserUrl = typeof(teaserUrl) == "undefined" ? '' : teaserUrl;
	videoOptions = typeof(videoOptions) == "undefined" ? false : videoOptions;
	videoOptionsId = typeof(videoOptionsId) == "undefined" ? 0 : videoOptionsId;
		
	if(this.isFlash) {
		var json = {
			'teaserUrl':teaserUrl,
			'videoUrl':videoUrl,
			'videoOptions':videoOptions,
			'id':videoOptionsId
		}
		this.videoFlash.sendToActionScript('changeVideo:' + JSON.stringify(json));
	}
	else {
		this.videoEndedCalled = false;
		this.videoTag.src = videoUrl;
		$(this.videoTag).attr('poster', teaserUrl).find('img').attr('src', teaserUrl);
	}
}

TJVideo.prototype.toggleMute = function (bool) {	
	if(bool != this.isMuted) {
		this.isMuted = bool;
		
		if(this.isFlash) {
			this.videoFlash.sendToActionScript('mute:' + bool);
		}
		else {
			this.videoTag.muted = bool;
		}	
	}
}
	
TJVideo.prototype.togglePlay = function (bool) {	
	if(this.isFlash) {
		if(bool) {
			this.videoFlash.sendToActionScript('play');
		}
		else if(!bool) {
			this.videoFlash.sendToActionScript('pause');
		}
	}
	else {
		this.videoEndedCalled = false;
		
		if(bool && this.videoTag.paused) {
			this.videoTag.play();
		}
		else if(!bool && !this.videoTag.paused) {
			this.videoTag.pause();
		}
	}
}

TJVideo.prototype.callCallback = function (callback) {
	if($.isFunction(callback)) {
		callback(this);
	}
}

TJVideo.prototype.onVideoPlaying = function (e) {
	this.callCallback(this.options.onVideoPlaying);
}

TJVideo.prototype.onVideoPaused = function (e) {
	this.callCallback(this.options.onVideoPaused);
}

TJVideo.prototype.onVideoEnded = function (e) {	
	if(!this.isFlash) {		
		if(this.options.loop) {
			this.togglePlay(true);	
		}
		
		this.HTML5VideoEnded();
	}
	else {
		this.callCallback(this.options.onVideoEnded);
	}
}

TJVideo.prototype.onHTML5VideoTimeUpdate = function (e) {
	if(this.videoTag.currentTime == this.videoTag.duration) {
		this.HTML5VideoEnded();
	}
}

TJVideo.prototype.HTML5VideoEnded = function () {
	if(!this.videoEndedCalled) {
		this.videoEndedCalled = true;
		this.callCallback(this.options.onVideoEnded);
	}
}

TJVideo.prototype.onFlashVideoMuted = function (bool) {
	this.isMuted = bool;
	
	if(bool) {
		this.callCallback(this.options.onVideoMuted);
	}
	else {
		this.callCallback(this.options.onVideoUnmuted);
	}
}

TJVideo.prototype.onHTML5VideoVolumeChange = function (e) {
	if(this.videoTag.muted != this.isMuted) {
		this.isMuted = this.videoTag.muted;
		
		if(this.videoTag.muted) {
			this.callCallback(this.options.onVideoMuted);
		}
		else {
			this.callCallback(this.options.onVideoUnmuted);
		}
		
		this.wasLastMuted = this.videoTag.muted;
	}
}

TJVideo.prototype.onHTML5VideoSeeked = function (e) {
	// if play is pressed when at the end, neither play or playing event is fired only seeked
	if(this.videoTag.currentTime == 0 && this.videoTag.paused == false) {
		this.videoEndedCalled = false;
		this.callCallback(this.options.onVideoPlaying);
	}
}
