1/* 2 Youtube Player with Playlist (v2.18) 3 https://github.com/carloscabo/responsive-youtube-player-with-playlist 4 by Carlos Cabo (@putuko) 5*/ 6 7var RYPP = (function($, undefined) { 8 'use strict'; 9 10 function Rypp(el, api_key, options) { 11 12 if (typeof api_key === 'undefined') { 13 console.log("Youtube API V3 requires a valid API KEY.\nFollow the instructions at: https://developers.google.com/youtube/v3/getting-started"); 14 return false; 15 } 16 17 // DOM Elements container 18 this.DOM = {}; 19 20 // Default settings container 21 this.options = {}; 22 23 // Data / urls 24 this.data = { 25 // Playlist url 26 ytapi: { 27 playlist_info: 'https://www.googleapis.com/youtube/v3/playlists?part=snippet&id={{RESOURCES_ID}}&key={{YOUR_API_KEY}}', 28 playlist: 'https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId={{RESOURCES_ID}}&key={{YOUR_API_KEY}}', 29 videolist: 'https://www.googleapis.com/youtube/v3/videos?part=snippet,status&maxResults=50&id={{RESOURCES_ID}}&key={{YOUR_API_KEY}}' 30 }, 31 firsttime: true, 32 ismobile: (typeof window.orientation !== 'undefined') 33 }; 34 35 // Initialize 36 this.init(el, api_key, options); 37 } 38 39 // Prototype for the instance 40 Rypp.prototype = { 41 42 init: function(el, api_key, options) { 43 44 // Api key 45 this.api_key = api_key; 46 47 // Default options 48 this.options = { 49 update_title_desc: false, 50 autoplay: true, 51 autonext: true, 52 loop: true, 53 mute: false 54 }; 55 56 // Merge initial options 57 if (typeof options !== 'undefined') { 58 $.extend(this.options, options); 59 } 60 61 // YT Player object 62 this.ytplayer = null; 63 64 // DOM elements 65 this.DOM = {}; 66 this.DOM.$el = $(el); 67 this.DOM.$playlc = this.DOM.$el.find('.RYPP-playlist'); 68 this.DOM.$items = this.DOM.$el.find('.RYPP-items'); 69 this.DOM.$videoc = this.DOM.$el.find('.RYPP-video'); 70 this.DOM.$title = this.DOM.$el.find('.RYPP-title'); 71 this.DOM.$desc = this.DOM.$el.find('.RYPP-desc'); 72 73 // Unique player ID 74 this.data.player_uid = (Math.random().toString(16).substr(2,8)); 75 this.DOM.$el.attr('data-rypp',this.data.player_uid).find('.RYPP-video-player').attr('id','RYPP-vp-'+this.data.player_uid); 76 77 // Link JS only once 78 if (typeof YT === 'undefined') { 79 var 80 tag = document.createElement('script'), 81 hID = document.getElementsByTagName('head')[0]; 82 // Add youtube API in HEAD 83 // tag.src = "https://www.youtube.com/iframe_api"; 84 tag.src = 'https://www.youtube.com/player_api'; 85 hID.appendChild(tag); 86 } 87 88 }, 89 90 onYouTubeIframeAPIReady: function() { 91 if( this.options.update_title_desc ) { 92 this.updateTitleDesc(); 93 } 94 this.populatePlaylist(); 95 }, 96 97 updateTitleDesc: function() { 98 var 99 that = this, 100 resources_id = this.DOM.$el.attr('data-playlist'), 101 url = this.data.ytapi.playlist_info.replace('{{RESOURCES_ID}}', resources_id).replace('{{YOUR_API_KEY}}', this.api_key); 102 103 $.ajaxSetup ({cache: false}); 104 $.ajax(url, { 105 context: this, 106 dataType: 'json', 107 crossDomain: true, 108 error: function(){ 109 // Not successful 110 }, 111 success: function(data){ 112 // console.log(data); 113 this.DOM.$title.html( data.items[0].snippet.title ); 114 this.DOM.$desc.html( data.items[0].snippet.description ); 115 } 116 }); 117 }, 118 119 populatePlaylist: function() { 120 // Empty playlist 121 this.DOM.$items.html('').append($('<ol>')); 122 123 // Now we read the video list from playlist data or from IDs... 124 if (this.DOM.$el.attr('data-playlist')) { 125 this.getVideosFrom( 126 'playlist', 127 this.DOM.$el.attr('data-playlist') 128 ); 129 } else if (this.DOM.$el.attr('data-ids')) { 130 var vl = this.DOM.$el.attr('data-ids'); 131 // Clean spaces 132 vl = ($.map(vl.split(','),$.trim)).join(','); 133 this.getVideosFrom( 134 'videolist', 135 vl 136 ); 137 } 138 }, 139 140 addAPIPlayer: function() { 141 var that = this; 142 143 this.ytplayer = new YT.Player('RYPP-vp-'+this.data.player_uid, { 144 // height: '390', 145 // width: '640', 146 playerVars: { 147 // controls: 0, 148 // showinfo: 0 , 149 // autoplay: 0, 150 enablejsapi: 1, 151 rel: 0, 152 modestbranding: 1, 153 wmode: 'transparent' 154 }, 155 events: { 156 'onReady': function(){ 157 that.onPlayerReady(); 158 }, 159 'onStateChange': function(e){ 160 that.onPlayerStateChange(e); 161 }, 162 'onError': function(e) { 163 console.log(e); 164 } 165 } 166 }); 167 }, 168 169 // Ready to play 170 onPlayerReady: function() { 171 this.startPlayList(); 172 }, 173 174 // When video finish 175 onPlayerStateChange: function(e){ 176 var 177 that = this; 178 179 if (typeof e !== 'undefined') { 180 181 // On video loaded? 182 if(e.data === -1 && this.data.firsttime) { 183 if(!this.options.autoplay && !this.data.ismobile) { // Is desktop 184 this.ytplayer.stopVideo(); 185 this.data.firsttime = false;
186 } 187 if(this.options.mute) { 188 this.ytplayer.mute(); 189 } 190 } 191 192 // If mobile and stored in buffer we STOP the video in mobile devices 193 if(e.data === 3 && this.data.ismobile && this.data.firsttime) { 194 setTimeout(function(){ 195 that.ytplayer.stopVideo(); 196 that.data.firsttime = false; 197 }, 500); 198 } 199 200 // Play next only if not mobile 201 var next = null; 202 if(e.data === 0 && !this.data.ismobile && this.options.autonext) { 203 next = this.DOM.$items.find('li.selected').next(); 204 if (next.length === 0 && this.options.loop) { 205 next = this.DOM.$items.find('li').first(); 206 } 207 next.click(); 208 } 209 210 } 211 212 }, 213 214 215 // Get video from data-ids or playlist 216 // It's impossible to know if a video in a playlist its available or currently deleted. So we do 2 request, first we get all the video IDs an then we ask for info about them. 217 getVideosFrom: function(kind, resources_id) { 218 var 219 that = this, 220 url = this.data.ytapi[kind].replace('{{RESOURCES_ID}}', resources_id).replace('{{YOUR_API_KEY}}', this.api_key); 221 222 $.ajaxSetup ({cache: false}); 223 $.ajax(url, { 224 context: this, 225 dataType: 'json', 226 crossDomain: true, 227 error: function(){ 228 // Not successful 229 }, 230 success: function(data){ 231 232 // We queried for a playlist 233 if (data.kind === 'youtube#playlistItemListResponse') { 234 235 // We get the video IDs and query gain, its the only way to be sure that all the videos are available, and not were deleted :( 236 var vl = $.map(data.items, function(val,idx) { 237 if (typeof val.snippet.resourceId.videoId !== 'undefined') { 238 return val.snippet.resourceId.videoId; 239 } 240 }).join(','); 241 242 that.getVideosFrom('videolist',vl); 243 244 } else if (data.kind === 'youtube#videoListResponse') { 245 246 // Videos froma Videolist 247 for (var i = 0, len = data.items.length; i < len; i++) { 248 var item = data.items[i]; 249 250 // Videos without thumbnail, deleted or rejected are not included in the player! 251 if ( 252 $.inArray(item.status.uploadStatus, ['rejected', 'deleted', 'failed']) === -1 && 253 typeof item.snippet.thumbnails !== 'undefined' 254 ) { 255 var 256 vid = item.id, 257 tit = item.snippet.title, 258 aut = item.snippet.channelTitle, 259 thu = item.snippet.thumbnails.default.url; 260 that.addVideo2Playlist(vid, tit, aut, thu); 261 } 262 } 263 that.addAPIPlayer(); 264 } 265 } 266 }); 267 }, 268 269 // All videos are supossed to be loaded 270 // lets start the playlist 271 startPlayList: function() { 272 273 var 274 D = this.DOM, 275 vid = null, 276 that = this; 277 278 // Click on playlist elemnts 279 D.$items.on('click', 'li', function(e) { 280 e.preventDefault(); 281 D.$items.find('li').removeClass('selected'); 282 $(this).addClass('selected'); 283 vid = $(this).data('video-id'); 284 // Call YT API function 285 that.ytplayer.loadVideoById(vid); 286 // If we are in mobile we must stop 287 if (that.data.ismobile) { 288 that.data.firsttime = true; 289 } 290 }); 291 292 // Select first if none 293 if (D.$items.find('li.selected').length === 0) { 294 D.$items.find('li').first().click(); 295 } 296 }, 297 298 // Add video block to playlist 299 addVideo2Playlist: function(vid, tit, aut, thu) { 300 var 301 D = this.DOM; 302 $('<li data-video-id="'+vid+'"><p class="title">'+tit+'<small class="author"><br>'+aut+'</small></p><img src="'+thu+'" class="thumb"></li>').appendTo(D.$items.find('ol')); 303 }, 304 305 }; // prototypes 306 307 return Rypp; 308 309}(jQuery)); 310 311// YOUTUBE API CALLBACK 312function onYouTubeIframeAPIReady() { 313 $('[data-rypp]').each(function(idx, el) { 314 $(el)[0].rypp_data_obj.onYouTubeIframeAPIReady(); 315 }); 316} 317 318// JQuery hook 319$.fn.rypp = function(api_key, options) { 320 return this.each(function() { 321 // Store object in DOM element 322 this.rypp_data_obj = new RYPP(this, api_key, options); 323 }); 324};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.