Skip to content

Instantly share code, notes, and snippets.

@tomasdev
Last active October 13, 2015 09:17
Show Gist options
  • Save tomasdev/4173215 to your computer and use it in GitHub Desktop.
Save tomasdev/4173215 to your computer and use it in GitHub Desktop.
(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