web/static/res/js/incplayer.js
author ymh <ymh.work@gmail.com>
Tue, 12 Feb 2013 10:54:34 +0100
changeset 134 678b07d351ef
parent 124 8d2376eb825c
permissions -rw-r--r--
Added tag V01.25 for changeset fe9ca5cd905e

function IncPlayer()
{
	// --------------------------------------------------------------------------------------------------------------------
	// Members
	// --------------------------------------------------------------------------------------------------------------------

	// Global
	this.initDone = false;
	this.playerIsReady = false;
	this.playerWaitForBeingReady = false; // used on ipad
	this.mobile;
	this.ipad;
	this.nextPage;
	this.reloadPage;

	// Sequences
	this.allSequencesData = [];
	this.sequences = [];
	this.singleVideo;

	// Popcorn objects
	this.videoDivId = "";
	this.videoExt = "";
	this.popSeq = null;

	// Controls
	this.playButton = null;
	this.hdSdButton = null;
	this.progressCurrent = null;
	this.progressDuration = null;
	this.progressCursor = null;
	this.progressBar = null;
	this.progressCursorX = 0; 
	this.progressCursorDrag;
	this.hd;
	this.seekTime = -1;

	// Tools
	this.logiEnable = true;
	this.jsonData = [];

	// --------------------------------------------------------------------------------------------------------------------
	// Functions
	// --------------------------------------------------------------------------------------------------------------------

	this.init = function(nextPage, videoDivId, playButtonId, hdSdButtonId, progressCurrentId, progressDurationId, progressCursorId, progressBarId, jsonFile)
	{
		this.nextPage = nextPage;
		this.videoDivId = videoDivId;

		// Control
		this.playButton = $("#" + playButtonId).get(0);
		this.hdSdButton = $("#" + hdSdButtonId).get(0);
		this.progressCurrent = $("#" + progressCurrentId);
		this.progressDuration = $("#" + progressDurationId);
		this.progressCursor = $("#" + progressCursorId);
		this.progressBar = $("#" + progressBarId);
		this.ctrlSetCursorDragFunction();
		this.setCursorPosition(0);

		// HD ?
		this.hd = incChoice.getHD();
		$(this.hdSdButton).css({"background-position" : this.hd ? '0 -12px' : '0 0'});			

		// Video extention
		this.videoExt = this.getSupportedVideoExt();
		if (this.videoExt === "") {
			this.loge("your browser don't support HTML5 videos");
			return false;
		}

		if (jsonFile !== null & jsonFile !== undefined) {
			// Load all sequences data
			this.allSequencesData = this.loadJson(jsonFile);

			// Add index to the videos
			for (var i = 0; i < this.allSequencesData.videos.length; ++i) {
				this.allSequencesData.videos[i].index = i;
			}			
		}

		this.initDone = true;
		this.mobile = IsSmartphone();
		this.ipad = !this.mobile && IsIpad();

		return true;
	};

	this.destroySequence = function()
	{
		if (this.popSeq !== null) {
			for (var i = 0; i < this.sequences.length; ++i) {			
				this.popSeq.eq(i).destroy();			
			}
			this.sequences = [];
			this.popSeq.remove();
		}		
	};

	this.createPopSequence = function(words, videosIndex, paramIndex)
	{
		if (!this.initDone) {
			this.loge("incplayer not initialized");
			return;
		}

		// Delete previous popcorn objects
		this.destroySequence();

		// Choose the 3 videos
		this.choosePopSequence(words, videosIndex, paramIndex);

		// And init the popcorn sequence
		this.initPopSequence();
	};

	this.playSingleVideo = function(videoUrl)
	{
		this.sequences = [];
		this.singleVideo = true;
       	var video = { name : "xxx", src: videoUrl, in: 0, out: "1.44" };
       	this.sequences.push(video);
       	this.formatPopSequence();
		this.initPopSequence();
	}

	this.choosePopSequence = function(wordsIndex, videosIndex, paramIndex)
	{
		this.sequences = [];

		if (paramIndex.length == 3) {
			// This is a shared link, we get the videos index from the url
			for (var i = 0; i < paramIndex.length; ++i) {
				this.sequences.push(this.allSequencesData.videos[paramIndex[i]]);		
			}
		} else if (videosIndex.length == 3) {
			// The player got reloded, we use the previously choosen videos
			for (var i = 0; i < videosIndex.length; ++i) {
				this.sequences.push(this.allSequencesData.videos[videosIndex[i]]);		
			}
		} else if (wordsIndex[0] == null) {
			// No reason to be here, redirection
			location.href = "index.html";

		} else {
			// We choose new videos

			// Get previous used videos
			var var32bits = incChoice.getChoosenVideosFlags();

			// Choose videos according to the chosen words
			for (var i = 0; i < wordsIndex.length; ++i) {
				var v = this.getRandomVideos(wordsIndex[i], var32bits, this.sequences);
				if (v == null) {
					// We didn't find free video
					// So we mark some as unplayed, we must get flags again and retry
					var32bits = incChoice.getChoosenVideosFlags();
					v = this.getRandomVideos(wordsIndex[i], var32bits, this.sequences);

					if (v == null) {
						this.loge("We didn't find a free video");
					}
				}
				this.sequences.push(v);		
			}

			// Save played videos with cookies
			this.savePlayedVideos(this.sequences, true);		

			// Save chosen video with cookies
			this.saveChosenVideos(this.sequences);
		}

		// Format pop sequence
		this.formatPopSequence();
		
		// Debug
		this.logi("choosed sequences:");
		for (i = 0; i < this.sequences.length; ++i) {
			this.logi(this.sequenceToString(i));
		}
	};

	this.formatPopSequence = function()
	{
		// Set the video file name 
		var i;
		for (i = 0; i < this.sequences.length; ++i) {
			var v = this.sequences[i];
			
			// HD
			if(!this.hd) {
				v.src = v.src.replace("HD", "SD"); 
			}

			// Extention
			v.src += "." + this.videoExt;

			// Set the final file
			this.sequences[i] = v;
		}

		for (i = 0; i < this.sequences.length; ++i) {
			
			// Get the sequence
			var seq = this.sequences[i];

			// Compute time in seconds
			var integer = Math.floor(seq.duration);
			var decimal = Math.floor((seq.duration - integer) * 100);
			var duration = integer * 60 + decimal;

			this.sequences[i] = { src: seq.src, in: 0, out: duration };
		}		
	}

	this.getRandomVideos = function(wordIndex, var32bits, otherVideos)
	{
		var i;
		var index = parseInt(wordIndex);
		var videos = [];
		var allVideosPlayed = true;
		var videosMatchWord = [];
		
		// Debug
		var freeVideosCount = 0;
		var wordScoreAllVideos = 0;

		// Get all videos affected by this word
		for (i = 0; i < this.allSequencesData.videos.length; ++i) {
			var video = this.allSequencesData.videos[i];
			var wordScore = video.scoreWord[index];

			if (wordScore <= 0) {
				// This video can't be choosen with this word
				continue;				
			}

			videosMatchWord.push(video); // Debug

			video.choosen = incChoice.IsThisVideoWasPlayed(var32bits, video.index);
			if (video.choosen) {
				// This video was already played in an another session
				continue;
			}

			allVideosPlayed = false;

			// Compare categories
			video.samecat = false;
			for (var j = 0; j < otherVideos.length; ++j) { 
				if (video.cat == otherVideos[j].cat) {
					video.samecat = true;
					break;
				}
			}
			if (video.samecat == true) {
				// This video have the same cat that an other selected video
				continue;
			}

			// We push has many time the video that the score for the word
			for (var j = 0; j < wordScore; ++j) {
				videos.push(video);
			}

			// Debug
			wordScoreAllVideos += wordScore;
			++freeVideosCount;
		}

		if (allVideosPlayed || !videos.length) {
			// All the videos asociated with the word have been played
			// or there is no videos to choose because we skipped the one with the same cat
			// We must mark the videos asociated with the word as unplayed
			this.savePlayedVideos(videosMatchWord, false);
			return null;
		}

		// Choose random video
		var v = videos[this.random(0, videos.length)];

		// Debug
		/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

		this.logi("---------------------------------------------------------------------");
		this.logi("The word " + this.allSequencesData.mots[index] + "(" + index + ")" +" give us " + videosMatchWord.length + " videos, we can choose " + freeVideosCount);
		this.logi("---------------------------------------------------------------------");

		for (i = 0; i < videosMatchWord.length; ++i) {
			var video = videosMatchWord[i];
			var message = this.resizeString(video.name, 15) + this.resizeString(" cat:" + video.cat, 15);
			if (video.choosen) {
				message += this.resizeString("played", 15);
			} else if (video.samecat) {
				message += this.resizeString("same cat", 15);
			} else {
				message += this.resizeString("OK", 15) + this.resizeString("proba:" + video.scoreWord[index] + "/" + wordScoreAllVideos, 15);
			}
			this.logi(message);
		}
		this.logi("");
		this.logi("We choose " + v.name + "(" + v.index + ")" + " cat:" + v.cat + " proba: " + v.scoreWord[index] + "/" + wordScoreAllVideos +  "\n\n");

		return v;
	}

	this.savePlayedVideos = function(videos, markPlayed)
	{
		var indexs = [];
		for (var i = 0; i < videos.length; ++i) {
			indexs.push(videos[i].index);
		}
		incChoice.savePlayedVideos(indexs, markPlayed);
	};

	this.saveChosenVideos = function(videos)
	{
		var indexs = [];
		for (var i = 0; i < videos.length; ++i) {
			indexs.push(videos[i].index);
		}
		incChoice.saveChosenVideos(indexs);
	};
	
	this.resizeString = function (str, size)
	{
		while (str.length < size) {
			str += " ";
		}
		return str;
	};

	this.getWordIndex = function(word)
	{
		var words = this.allSequencesData.mots;
		for (var i = 0; i < words.length; ++i) {
			if (words[i] == word) {
				return i;
			}
		}
		this.loge("getWordIndex erreur");
		return -1;
	};

	this.debugDisplayWordsIndex = function()
	{
		var words = this.allSequencesData.mots;
		for (var i = 0; i < words.length; ++i) {
			this.logi(words[i] + " -> " + i);
		}
	};

	this.initPopSequence = function()
	{
		var self = this;

		// Create the popcorn sequencer
		self.popSeq = Popcorn.sequence(self.videoDivId, self.sequences);

		for (var i = 0; i < self.sequences.length; ++i) {
			
			var pop = self.popSeq.eq(i);

			// Hide controls
			pop.controls(false);

			// Add poster on ipad
			if (self.ipad && !this.singleVideo) {
				$(pop.media).attr('poster', 'static/res/img/poster' + (i + 1) + '_video.png');
			}

			// Play event
			self.listenEvent(pop, "playing", false, i, function(index) {
				self.displayPlayButton(false);
				self.logi("play sequence: " + self.sequenceToString(self.popSeq.active));
			});

			// Pause event
			self.listenEvent(pop, "pause", false, i, function(index) {
				self.displayPlayButton(true);
				self.logi("pause sequence: " + self.sequenceToString(self.popSeq.active));
			});			

			// Ended event
			self.listenEvent(pop, "ended", false, i, function(index) {
				self.logi("ended sequence: " + self.sequenceToString(self.popSeq.active));
			});

			// Timeupdate event
			self.listenEvent(pop, "timeupdate", false, i, function(index) {

				var currentTime = self.popSeq.currentTime();
				if (!self.progressCursorDrag) {
					// Move timeline and display the time
					self.progressCursorX = self.setCursorPosition(currentTime);
				}

				// Detect the sequence end
				if (self.popSeq.active == self.sequences.length-1 && currentTime >= self.popSeq.duration() - 1) {
					setTimeout(function() { 
								location.href = self.nextPage;
							}, 1500);
				}
			});

			/*
			// Canplaythrough event
			self.listenEvent(pop, "canplaythrough", true, i, function(index) {
			});
			*/				
		}

		if (!this.ipad) {
			self.popSeq.on("canplaythrough", function() {

				self.playerIsReady = true;

				// Recompute Duration
				self.recomputeDuration();

				// Automatic play
				self.ctrlPlay();

				// Unlisten event
				self.popSeq.off("canplaythrough");
				self.logi("the player is ready");
			});
		}

		self.popSeq.on("cycle", function() {
		});

		self.popSeq.on("ended", function() {
		});

	};

	this.listenEvent = function(pop, event, unlisten, index, func)
	{
		pop.on(event, function() {
			// Execute the function
			func(index);
	
			if (unlisten) {		
				// Unlisten event
				pop.off(event);
			}
		});
	};

	this.recomputeDuration = function()
	{
		// Recompute duration
		var totalDuration = 0;
		for (var j = 0; j < this.sequences.length; ++j) {			
			var duration = this.popSeq.eq(j).duration();

			// ofVideos
			this.popSeq.inOuts.ofVideos[j].out = duration;

			// ofClips
			this.popSeq.inOuts.ofClips[j].in = totalDuration;
			totalDuration += duration;
			this.popSeq.inOuts.ofClips[j].out = totalDuration;
		}

		// Set total duration
		$(this.progressDuration).html(this.secondsToTime(this.popSeq.duration()));			
	};

	this.recomputeDurationNoAutoPlay = function()
	{
		// Recompute duration
		var totalDuration = 0;
		for (var j = 0; j <= this.popSeq.active; ++j) {			
			var duration = this.popSeq.eq(j).duration();

			// ofVideos
			this.popSeq.inOuts.ofVideos[j].out = duration;

			// ofClips
			this.popSeq.inOuts.ofClips[j].in = totalDuration;
			totalDuration += duration;
			this.popSeq.inOuts.ofClips[j].out = totalDuration;
		}

		// Set total duration
		$(this.progressDuration).html(this.secondsToTime(this.popSeq.duration()));

		if (incHideBar !== undefined) {
			incHideBar.showBarPointerOnAction(6000);					
		}
	};

	this.getCurrentPop = function()
	{
		var index = this.popSeq.active;
		if (index >= this.sequences.length) {
			index = this.sequences.length-1;
		}
		return this.popSeq.eq(index);
	};

	this.setCursorPosition = function(time)
	{
		// Display time
		$(this.progressCurrent).html(this.secondsToTime(time));

		// Move cursor timeline
		var progressCursorX = time ? ((time / this.popSeq.duration()) * (240 - 16)) : 0;
		$(this.progressCursor).css({"left" : progressCursorX });
		$("#progress").css({"width" : progressCursorX + 8});

		return progressCursorX;
	}

	this.forceCheckPlayerIsReady = function()
	{
		this.playerIsReady = false;
		this.playerWaitForBeingReady = false;
		this.checkPlayerIsReady();
	}

	this.checkPlayerIsReady = function()
	{		
		if (this.playerIsReady) {
			return true;
		}

		if (this.ipad) {

			// We "manualy" play the video (we are in a click event handler)
			var media = this.popSeq.eq(this.popSeq.active).media;
			media.play();

			if (media.readystate === 4) {
				this.playerIsReady = true;
				this.recomputeDurationNoAutoPlay();
				return false; // Ready but we don't want to do anything after
			}

			if (this.playerWaitForBeingReady) {
				return false;
			}

			this.playerWaitForBeingReady = true;
			
			var self = this;
			var mediaCanPlay = function () {
				// Remove event
				media.removeEventListener('canplaythrough', mediaCanPlay, false);
				media.removeEventListener('load', mediaCanPlay, false);
				
				// The video is ready, play
				media.play();
				self.playerIsReady = true;				
				self.recomputeDurationNoAutoPlay();
				self.logi("ipad play event");
			}			

			media.addEventListener('canplaythrough', mediaCanPlay, false);
			media.addEventListener('load', mediaCanPlay, false);

			setTimeout(function() {
				media.pause(); // Block play so it buffers before playing
			}, 1);				
		}

		this.logi("can't play, the player is not ready");
		return false;
	}	

	this.ctrlSetCursorDragFunction = function()
	{
		var self = this;
		var origineX = null;
		var posX;
		var maxX = 240 - 16;

		var setCursorPositionX = function (x) {
			if (x < 0) {
				x = 0;
			} else if (x > maxX) {
				x = maxX;
			}
			var time = self.popSeq.duration() * x / maxX;
			self.setCursorPosition(time);
			return x;			
		}

		var setCursorPosition = function (x) {
	        self.progressCursorDrag = true;
			if (origineX == null) {
				origineX = x;	
			} else {
				posX = self.progressCursorX + x - origineX;
				posX = setCursorPositionX(posX);
			}
		}

		this.progressCursor.on({
	        mousedown: function() {

				if (!self.checkPlayerIsReady()) {
					return;
				}

	        	$(document).on(
		        	self.ipad ? {
		        		touchmove: function (e) {
		        			e.preventDefault();
							var touches = ( typeof( event.touches ) != "undefined" ) ? event.touches : event.changedTouches;		
		        			setCursorPosition(touches[0].pageX);
	   					}
		        	} : {
		        		mousemove: function (e) {
		        			self.progressCursorDrag = true;
		        			setCursorPosition(e.pageX);
	   					}
		        	}
		        );
	        }
	    });

		var progressBarX = $(self.progressBar).position().left;
	    if (self.ipad) {
			this.progressBar.on({
		        touchdown: function(e) {
					if (!self.checkPlayerIsReady()) {
						return;
					}

		        	e.preventDefault();
					var touches = ( typeof( event.touches ) != "undefined" ) ? event.touches : event.changedTouches;		
		        	PosX = touches[0].pageX - progressBarX - 8;
		        	PosX = setCursorPositionX(PosX);

	        		// Seek time
					var jumpTime = self.popSeq.duration() * PosX / maxX;
					self.popSeq.jumpTo(jumpTime);
		        }
		    });				    	
		} else {
			this.progressBar.on({
		        mousedown: function(e) {
					if (!self.checkPlayerIsReady()) {
						return;
					}

		        	PosX = e.pageX - progressBarX - 8;
		        	PosX = setCursorPositionX(PosX);

	        		// Seek time
					var jumpTime = self.popSeq.duration() * PosX / maxX;
					self.popSeq.jumpTo(jumpTime);
		        }
		    });
		}

		$(document).mouseup(function(){
			if (self.progressCursorDrag) {
		        self.progressCursorDrag = false;
				origineX = null;

		        // Remove event
				$(document).off(self.ipad ? "touchmove" : "mousemove");

				// Seek time
				var jumpTime = self.popSeq.duration() * posX / maxX;
				self.popSeq.jumpTo(jumpTime);
			}
   		});
	}

	this.ctrlPlay = function()
	{
		if (!this.checkPlayerIsReady()) {
			return;
		}

		if (this.getCurrentPop().paused()) {
			// Play
			this.popSeq.play();
		} else {
			// Pause
			this.popSeq.pause();
		}
	};

	this.ctrlNext = function()
	{
		if (!this.checkPlayerIsReady()) {
			return;
		}

		if (this.popSeq.active == this.sequences.length - 1) {
			// We are at the last video
			location.href = this.nextPage;			
			return;
		}

		if (this.ipad) {
			this.playerIsReady = false;
			this.playerWaitForBeingReady = false;	
		}		

		// Go to the next video
		var jumpTime = this.popSeq.durationSeqs(this.popSeq.active + 1) + 0.001;
		this.popSeq.jumpTo(jumpTime);
	};

	this.ctrlPrev = function()
	{
		if (!this.checkPlayerIsReady()) {
			return;
		}

		var videoIndex = this.popSeq.active;
		if (videoIndex === 0) {
			// We jump to the start
			this.popSeq.jumpTo(0);
		} else {

			// If we are a less than 1 sec from the sequence start, we jump to the prev sequence
			// else we jump to the start of the current sequence
			var jumpTime = this.popSeq.durationSeqs(videoIndex);
			if (this.popSeq.currentTime() - jumpTime < 1) {
				jumpTime = this.popSeq.durationSeqs(videoIndex-1);
			} 
			this.popSeq.jumpTo(jumpTime + 0.001);
		}
	};
	
	this.ctrlHdSd = function()
	{
		// We swape between hd/sd
		incChoice.setHD(this.hd ? 0 : 1);

		// We record the current time		

		// Reload
		location.href = document.URL;
	};

	this.displayPlayButton = function(playIcon)
	{
		$(this.playButton).css({"background-position" : playIcon ? '-77px 0' : '-77px -17px'});
	};
		 
	// --------------------------------------------------------------------------------------------------------------------
	// Tools Functions
	// --------------------------------------------------------------------------------------------------------------------

	this.logi = function(txt)
	{
		if (this.logiEnable) {
			console.log("info: " + txt);
		}
	};

	this.loge = function(txt)
	{
		console.log("error: " + txt);
	};

	this.loadJson = function(jsonFile)
	{
		var txt = this.loadTxtFile(jsonFile);
		var obj = null;

        $.ajax({
            url: jsonFile,
            async: false,
            success: function (data){
				obj = data;
            }
        });

		return obj;
	};

	this.loadTxtFile = function(jsonFile)
	{
		var xhr = new XMLHttpRequest();
		xhr.open("GET", jsonFile, false); 
		if (xhr.overrideMimeType) {
			xhr.overrideMimeType('text/plain; charset=x-user-defined');
		}

		try {
			xhr.send(null); 
		} catch(e) {
			return "";
		}

		if (xhr.status == 404) {
			return "";
		}
		
		var buffer;
		if (xhr.responseType == "arraybuffer") {
			buffer = xhr.response;
		} else if (xhr.mozResponseArrayBuffer === null) {
			buffer = xhr.mozResponseArrayBuffer;  
		} else {
			buffer = xhr.response;
		}
		return buffer;
	};

	this.random = function(min, max)
	{
		return Math.floor((Math.random()*(max-min))+min);
	};

	this.secondsToTime = function(sec)
	{
		var minutes = Math.floor(sec / 60);
		var seconds = Math.floor(sec - minutes * 60);
		if (seconds < 10) {
			seconds = "0" + seconds;
		}
		return "" + minutes + "'" + seconds + "''";
	};

	this.sequenceToString = function(index)
	{
		return JSON.stringify(this.sequences[index]);
	};

	this.getSupportedVideoExt = function()
	{
		var v = document.createElement("video");

		if (v.canPlayType) {		    
		    // Check for Webm support
		    if (v.canPlayType('video/webm; codecs="vp8, vorbis"') !== "") {
		    	return "webm";
		    }
		    // Check for MPEG-4 support
		    if (v.canPlayType('video/mp4; codecs="mp4v.20.8"' ) !== "") {
				return "mp4";
		    }
		    // Check for h264 support
		    if ((v.canPlayType('video/mp4; codecs="avc1.42E01E"' ) !== "" || v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'))) {
		    	return "mp4";
		    }
		}
		return "";
	};
}

var incPlayer = new IncPlayer();