Last active
October 13, 2015 09:17
-
-
Save tomasdev/4173215 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(global, $){ | |
var YoutubeVideo = function(options) { | |
var self = this; | |
if (!options.id || typeof options.container.jquery === 'undefined') { | |
return false; | |
} | |
self.videoId = options.id; | |
self.container = options.container; | |
self.key = options.key; | |
self.name = options.name; | |
self.containerKey = self.key + '-container'; | |
self.video = false; | |
self.first_play = false; | |
self.keypointsTagging = [25,50,75]; | |
// Array to validate unique tagging. | |
self.keypointsTaggingUnique = [true,true,true]; | |
// Default video for unsupported list (IE7, mobile, ipad) | |
if ($(global).width() < 768 | |
|| navigator.userAgent.toLowerCase().indexOf('ipad') > -1 | |
|| navigator.userAgent.toLowerCase().indexOf('msie 7') > -1) { | |
$('<img src="http://img.youtube.com/vi/' + self.videoId + '/maxresdefault.jpg" alt="" style="z-index:0" />').appendTo(self.container); | |
return false; | |
} | |
self.keypointsMarkers = (function(keys) { | |
// TODO: add support for Object keys | |
// { | |
// sec: 10, | |
// title: 'Difusing Hair', | |
// callback: function() {} | |
// } | |
if (!keys || typeof keys != 'string') { | |
return false; | |
} | |
var template = self.getMarkup('KeyPoints'), | |
$elems = $(keys).hide(), | |
markup = ''; | |
self.keypoints = $elems.map(function(i, element){ | |
var $el = $(element), | |
data = $el.data(); | |
markup += template.replace('{{TITLE}}', data.title); | |
return data; | |
}).get(); | |
self.keypointsElements = $elems; | |
return markup; | |
}(options.keypoints)) || ''; | |
self.fallbackMarkup = self.container.html() + '<img src="http://img.youtube.com/vi/' + self.videoId + '/maxresdefault.jpg" alt="" style="z-index:0" />'; | |
self.init(); | |
}; | |
var YoutubeVideoUI = function(self) { | |
return { | |
build: function() { | |
self.container.empty(); | |
var $video = $('<div id="' + self.containerKey + '"></div>').appendTo(self.container), | |
$preview = $('<img src="http://img.youtube.com/vi/' + self.videoId + '/maxresdefault.jpg" alt="" />').appendTo(self.container), | |
$playButton = $('<a href="#" class="play">Play</a>').appendTo(self.container), | |
$playerUI = $('<div class="player">' + | |
self.getMarkup('UIPlayPause') + | |
self.getMarkup('Seeker') + | |
self.getMarkup('Volume') + | |
'</div>').appendTo(self.container); | |
return { | |
preview: $preview, | |
playButton: $playButton, | |
playpause: self.container.find('.play-pause'), | |
seeker: self.container.find('.seeker'), | |
volume: self.container.find('.volume'), | |
clickableSeeker: self.container.find('.clickable-progress') | |
}; | |
}, | |
bindings: function($playButton, $playpause, $volume, $preview) { | |
$playButton.bind('click', function(evt) { | |
evt.preventDefault(); | |
self.play(); | |
}); | |
$playpause.bind('click', function(evt) { | |
evt.preventDefault(); | |
if (self.isPlaying()) { | |
self.pause(); | |
} else { | |
self.play(); | |
} | |
}); | |
$volume.bind('mousedown', function(evt) { | |
if (self.video.setVolume) { | |
var offsetX = evt.pageX - Math.round($volume.offset().left); | |
self.video.setVolume(offsetX * 100 / parseInt($volume.width(), 10)); | |
$volume.data('start', offsetX); | |
$volume.find('.percent').width(offsetX); | |
} | |
}); | |
$volume.bind('mousemove', function(evt) { | |
if ($volume.data('start')) { | |
var offsetX = evt.pageX - Math.round($volume.offset().left); | |
self.video.setVolume(offsetX * 100 / parseInt($volume.width(), 10)); | |
$volume.find('.percent').width(offsetX); | |
} | |
}); | |
$volume.bind('mouseup mouseout', function(evt) { | |
$volume.removeData('start'); | |
}); | |
}, | |
keypoints: function() { | |
var ui = self.uiElements; | |
ui.seeker.prepend(self.keypointsMarkers); | |
ui.kpTriggers = ui.seeker.find('.key-point'); | |
var total = self.video.getDuration(); | |
ui.kpTriggers.each(function(i) { | |
var $this = $(this), | |
at = self.keypoints[i].time; | |
$this.css('left', Math.round(at * 100 / total) + '%'); | |
$this.bind('click', function() { | |
var $tooltip = $this.find('.tooltip'); | |
ui.kpTriggers.find('.tooltip').hide(); | |
$tooltip.show(); | |
self.keypointsElements.hide().eq(i).show(); | |
}); | |
}); | |
}, | |
onReady: function() { | |
var total = self.video.getDuration(), | |
ui = self.uiElements;; | |
ui.clickableSeeker.bind('click', function(evt) { | |
evt.preventDefault(); | |
var start = evt.pageX - Math.round(ui.clickableSeeker.offset().left), | |
fraction = start / ui.clickableSeeker.width(), | |
at = Math.round(fraction * total); | |
self.video.seekTo(at); | |
}); | |
} | |
}; | |
}; | |
YoutubeVideo.prototype = new function() { | |
this.init = function() { | |
var instance = this; | |
instance.ui = YoutubeVideoUI(instance); | |
instance.uiElements = instance.ui.build(); | |
instance.loaded = false; | |
var onThumbLoad = function() { | |
if (instance.uiElements.preview.height() && !instance.loaded) { | |
instance.video = instance.create(); | |
instance.ui.bindings(instance.uiElements.playButton, instance.uiElements.playpause, instance.uiElements.volume, instance.uiElements.preview); | |
instance.loaded = true; | |
} | |
}; | |
// non cache case | |
instance.uiElements.preview.load(onThumbLoad); | |
// cache case | |
onThumbLoad(); | |
}; | |
this.getMarkup = new function() { | |
// TODO: support options.markup for custom HTML | |
var defaults = { | |
KeyPoints: '<div class="key-point">' + | |
'<div class="marker"></div>' + | |
'<div class="tooltip">' + | |
'<strong>{{TITLE}}</strong>' + | |
'</div>' + | |
'</div>', | |
Seeker: '<div class="seeker">' + | |
'<div class="load-progress"></div>' + | |
'<div class="progress"></div>' + | |
'<a href="#" class="clickable-progress"></a>' + | |
'</div>', | |
UIPlayPause: '<a href="#" class="play-pause paused"></a>', | |
Volume: '<div class="volume">' + | |
'<div class="percent"></div>' + | |
'</div>' | |
}; | |
return function(key) { | |
return defaults[key] || ''; | |
}; | |
}; | |
this.create = function() { | |
var instance = this; | |
return new YT.Player(instance.containerKey, { | |
height: '100%', | |
width: '100%', | |
videoId: instance.videoId, | |
playerVars: { | |
rel: 0, | |
showinfo: 0, | |
modestbranding: 1, | |
wmode: 'transparent', | |
controls: 0 | |
}, | |
events: { | |
'onReady': function() { | |
instance.onReady.apply(instance, arguments); | |
}, | |
'onStateChange': function() { | |
instance.onStateChange.apply(instance, arguments); | |
} | |
} | |
}); | |
}; | |
this.updatePlayerInfo = function() { | |
var loaded, | |
played, | |
instance = this, | |
ui = instance.uiElements; | |
if (instance.video.getVideoLoadedFraction && (loaded = instance.video.getVideoLoadedFraction())) { | |
instance.uiElements.seeker.find('.load-progress').width(Math.round(loaded * 100) + '%'); | |
} | |
if (instance.isPlaying() && (played = instance.video.getCurrentTime())) { | |
var percentage = Math.round(played / instance.video.getDuration() * 100) ; | |
instance.uiElements.seeker.find('.progress').width(percentage + '%'); | |
if (played = parseInt(played, 10)) { | |
for (var i = 0, keypoint; keypoint = instance.keypoints[i]; i++) { | |
if (keypoint.time === played) { | |
instance.uiElements.kpTriggers.eq(i).click(); | |
break; | |
} | |
} | |
} | |
// Tagging | |
for (var i = 0, keypointTag; keypointTag = instance.keypointsTagging[i]; i++) { | |
if(percentage == keypointTag && instance.keypointsTaggingUnique[i] == true ) { | |
instance.keypointsTaggingUnique[i] = false; | |
instance.tagging_percentage(percentage); | |
} | |
} | |
} | |
setTimeout(function(){ | |
instance.updatePlayerInfo(); | |
}, 250); | |
}; | |
this.onReady = function() { | |
var instance = this; | |
if (!instance.video.cueVideoById) { | |
return setTimeout(instance.onReady, 200); | |
} | |
instance.ui.keypoints(); | |
instance.ui.onReady(); | |
instance.updatePlayerInfo(); | |
instance.video.cueVideoById(instance.videoId); | |
instance.tagging_load(); | |
}; | |
this.onStateChange = function(evt) { | |
var instance = this, | |
ui = instance.uiElements; | |
if (evt.data === YT.PlayerState.BUFFERING || evt.data === YT.PlayerState.PLAYING) { | |
ui.preview.css('z-index', 1); | |
ui.playButton.hide(); | |
} | |
if (+evt.data === +YT.PlayerState.ENDED) { | |
ui.preview.removeAttr('style'); | |
ui.playButton.show(); | |
instance.tagging_finish(); | |
} | |
// -1 unstarted | |
// 0 YT.PlayerState.ENDED | |
// 1 YT.PlayerState.PLAYING | |
// 2 YT.PlayerState.PAUSED | |
// 3 YT.PlayerState.BUFFERING | |
// 5 YT.PlayerState.CUED | |
}; | |
this.isPlaying = function() { | |
var instance = this; | |
return instance.video.getPlayerState && (instance.video.getPlayerState() === YT.PlayerState.PLAYING); | |
}; | |
this.play = function(second) { | |
var instance = this; | |
if (!instance.video.playVideo) { | |
return setTimeout(function() { | |
instance.play(second); | |
}, 300); | |
} | |
try { | |
if (second) { | |
instance.video.seekTo(second); | |
} | |
instance.video.playVideo(); | |
instance.tagging_start(); | |
} catch (e) { | |
instance.container.html(instance.fallbackMarkup); | |
} | |
instance.uiElements.playpause.removeClass('paused'); | |
}; | |
this.pause = function() { | |
var instance = this; | |
instance.video.pauseVideo(); | |
instance.uiElements.playpause.addClass('paused'); | |
}; | |
this.setVolume = function(volume) { | |
var instance = this; | |
if(!isNaN(volume) && volume >= 0 && volume <= 100) { | |
instance.video.setVolume(volume); | |
} | |
}; | |
this.tagging_load = function() { | |
var instance = this; | |
// TODO: Implement the tagging JS on this method | |
// console.log("Tagging Load: ", instance.name); | |
}; | |
this.tagging_start = function() { | |
var instance = this; | |
if(instance.first_play == false) { | |
// TODO: Implement the tagging for Play | |
// console.log("Tagging Start: ", instance.name); | |
instance.first_play = true; | |
} else { | |
// TODO: Implement the tagging for Replay | |
// console.log("Tagging Replay: ", instance.name); | |
// Re-init Tagging Check Array. | |
instance.keypointsTaggingUnique = [true,true,true]; | |
} | |
}; | |
this.tagging_finish = function() { | |
var instance = this; | |
// TODO: Implement the tagging JS on this method | |
// console.log("Tagging Finish: ", instance.name); | |
}; | |
this.tagging_percentage = function(percentage) { | |
var instance = this; | |
// TODO: Implement the tagging JS on this method | |
// console.log("Tagging Percentage: ", percentage, " - " ,instance.name); | |
}; | |
}; | |
var YoutubeSkin = function() { | |
var _videos = {}, | |
_queue = [], | |
count = 0, | |
self = this, | |
API_LOADED = false; | |
var newId = function() { | |
return 'video-' + (++count).toString(16); | |
}; | |
self.get = function(key) { | |
return _videos[key] || false; | |
}; | |
self.set = function(key, video) { | |
return _videos[key] = video; | |
}; | |
self.addQueue = function(options) { | |
_queue.push(options); | |
}; | |
// FIXME: only for DEVELOPMENT | |
self.getAll = function() { | |
return _videos; | |
}; | |
self.create = function(options) { | |
var key = newId(); | |
options.key = key; | |
if (API_LOADED) { | |
self.set(key, new YoutubeVideo(options)); | |
} else { | |
self.addQueue(options); | |
} | |
return key; | |
}; | |
self.destroy = function(key) { | |
// TODO | |
}; | |
// event handlers | |
self.onYouTubeIframeAPIReady = function() { | |
API_LOADED = true; | |
// exec queue | |
for (var i = 0, options; options = _queue[i++]; ) { | |
self.set(options.key, new YoutubeVideo(options)); | |
} | |
}; | |
// load async the API if the video exists. | |
if($('.video').length > 0) { | |
$(function() { | |
var firstScriptTag = document.getElementsByTagName('script')[0], | |
tag = document.createElement('script'); | |
tag.src = "//www.youtube.com/iframe_api"; | |
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); | |
}); | |
} | |
}; | |
global.YoutubeSkin = new YoutubeSkin(); | |
global.onYouTubeIframeAPIReady = global.YoutubeSkin.onYouTubeIframeAPIReady; | |
$.fn.youtubeSkin = function(options) { | |
return this.each(function() { | |
var id, name, | |
$elem = $(this); | |
if (id = $elem.data('youtube-id')) { | |
name = $elem.data('youtube-name'); | |
var key = global.YoutubeSkin.create({ | |
id: id, | |
name: name, | |
container: $elem, | |
keypoints: options.keypoints | |
}); | |
$elem.data('youtube-skin-id', key); | |
} | |
}); | |
}; | |
}(window, jQuery)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment