1/* global mejs:false */ 2/** 3 * Component Playlist 4 * @module Playlist 5 * @param {jQuery} $ Instance of jQuery 6 * @return {Object} List of Playlist component methods 7 */ 8XA.component.playlist = (function ($) { 9 /** 10 * This object stores all public api methods 11 * @type {Object.<Methods>} 12 * @memberOf module:Playlist 13 * */ 14 var api = {}; 15 /** 16 * This class is used by [Playlist]{@link module:Playlist} module 17 * @class Playlist 18 * @memberOf module:Playlist 19 * @param {jQuery} playlist Root DOM element of playlist component wrapped by jQuery 20 * @param {Object} properties Properties set in data attribute 21 */ 22 function Playlist(playlist, properties) { 23 this.properties = properties; 24 this.playlist = playlist; 25 this.activeVideo = 0; 26 this.playlistItems = 0; 27 } 28 /** 29 * Creates new source element, sets up it with type & src. Created element 30 * append to Video container (take it from videoContainer parameter) 31 * @method 32 * @alias createNewSources 33 * @memberOf module:Playlist.Playlist 34 * @param {$OrderedList} source takes from properties.sources[itemIndex].src - 35 * source of playlist element 36 * @param {$OrderedList} videoContainer container where source should be added 37 */ 38 Playlist.prototype.createNewSources = function (source, videoContainer) { 39 var newSource; 40 41 var sourceBuilder = function (path) { 42 var newSource = $("<source>"), 43 type; 44 45 if (path.match(/\.(mp4)$/)) { 46 type = "video/mp4"; 47 } else if (path.match(/\.(webm)$/)) { 48 type = "video/webm"; 49 } else if (path.match(/\.(ogv)$/)) { 50 type = "video/ogg"; 51 } else { 52 type = "video/youtube"; 53 } 54 55 newSource.attr({ 56 type: type, 57 src: path 58 }); 59 60 return newSource; 61 }; 62 63 if (source instanceof Array) { 64 for (var i = 0; i < source.length; i++) { 65 newSource = sourceBuilder(source[i]); 66 videoContainer.find("video").append(newSource); 67 } 68 } else { 69 newSource = sourceBuilder(source); 70 videoContainer.find("video").append(newSource); 71 } 72 }; 73 /** 74 * Replaces source of each video element under play list component. 75 * After replacing it re-initializes video component by calling 76 * [XA.component.video.initVideoFromPlaylist]{@link module:Video.initVideoFromPlaylist} 77 * @name replaceSource 78 * @function 79 * @param {number} itemIndex position of active video in video list 80 * @param {boolean} loadFromEvent 81 * @memberOf module:Playlist.Playlist 82 */ 83 84 Playlist.prototype.replaceSource = function (itemIndex, loadFromEvent) { 85 var inst = this, 86 videoContainer, 87 videoClone, 88 newSrc = inst.properties.sources[itemIndex].src, 89 sources, 90 videoId, 91 videoContainerHeight = 0; 92 93 $(inst.properties.playlistId).each(function () { 94 videoContainer = $(this); 95 videoId = inst.properties.playlistId; 96 97 if (videoContainer.is(videoId) && newSrc.length) { 98 videoContainer.addClass("show"); 99 100 sources = videoContainer.find("source"); 101 sources.remove(); 102 inst.createNewSources(newSrc, videoContainer); 103 videoContainer 104 .find("video") 105 .attr({ 106 src: "" 107 }) 108 .show(); 109 110 var autoplayVideo = false; 111 if (loadFromEvent) { 112 if (inst.properties.autoPlaySelected) { 113 autoplayVideo = true; 114 } 115 } else { 116 if (inst.properties.autoPlay) { 117 autoplayVideo = true; 118 } 119 } 120 121 if (autoplayVideo) { 122 videoContainer.find("video").attr({ 123 autoplay: "" 124 }); 125 } 126 127 videoClone = videoContainer.find("video").clone(); 128 videoContainerHeight = videoContainer.height(); 129 videoContainer.css({ 130 height: videoContainerHeight 131 }); 132 133 var id = videoContainer.find(".mejs-container").attr("id"); 134 if (id) { 135 $("#" + id).remove(); 136 delete mejs.players[id]; 137 videoContainer 138 .find(".component-content") 139 .append(videoClone); 140 } 141 142 XA.component.video.initVideoFromPlaylist( 143 videoContainer, 144 inst.playlist 145 ); 146 videoContainer.css({ 147 height: "auto" 148 }); 149 } 150 }); 151 }; 152 153 /** 154 * Initializes videos under component. On call and on 155 * [change-video]{@link module:Playlist.Playlist#change-video} event 156 * calls [replaceSource]{@link module:Playlist.Playlist.replaceSource} 157 * @name loadPlaylistVideo 158 * @function 159 * @listeners module:Playlist.Playlist#change-video 160 * @memberOf module:Playlist.Playlist 161 */ 162 163 Playlist.prototype.loadPlaylistVideo = function () { 164 var inst = this, 165 playlistItems = $(inst.playlist).find(".playlist-item"), 166 activeListItem; 167 168 inst.playlistItems = playlistItems.length; 169 var loadVideoFromPlaylist = function (loadFromEvent) { 170 inst.replaceSource(inst.activeVideo, loadFromEvent); 171 activeListItem = playlistItems.eq(inst.activeVideo); 172 activeListItem.addClass("active"); 173 activeListItem.siblings().removeClass("active"); 174 }; 175 176 loadVideoFromPlaylist(); 177 $(inst.playlist).on("change-video", function (event, properties) { 178 var loadNewVideo = false; 179 180 if (properties) { 181 if (properties.hasOwnProperty("back")) { 182 inst.activeVideo--; 183 if (inst.activeVideo < 0) { 184 inst.activeVideo = 0; 185 } else { 186 loadNewVideo = true; 187 } 188 } else { 189 inst.activeVideo++; 190 if (inst.activeVideo === inst.playlistItems) { 191 if (inst.properties.repeatAfterAll) { 192 inst.activeVideo = 0; 193 loadNewVideo = true; 194 } else { 195 inst.activeVideo = inst.playlistItems - 1; 196 } 197 } else { 198 loadNewVideo = true; 199 } 200 } 201 } else { 202 if (inst.properties.playNext) { 203 if (inst.activeVideo + 1 <= inst.playlistItems) { 204 inst.activeVideo++; 205 206 if (inst.activeVideo === inst.playlistItems) { 207 inst.activeVideo = 0; 208 209 if (inst.properties.repeatAfterAll) { 210 loadNewVideo = true; 211 } 212 } else { 213 inst.actiVideo--; 214 loadNewVideo = true; 215 } 216 } 217 } 218 } 219
220 if (loadNewVideo) { 221 loadVideoFromPlaylist(true); 222 } 223 }); 224 }; 225 /** 226 * Attaches events to playlist-section and navigation items 227 * @name attachEvents 228 * @function 229 * @fires module:Playlist.Playlist#change-video 230 * @memberOf module:Playlist.Playlist 231 */ 232 Playlist.prototype.attachEvents = function () { 233 var inst = this, 234 link = $(inst.playlist).find(".playlist-section"), 235 navItems = $(inst.playlist).find(".playlist-nav a"), 236 playlistItem; 237 link.on("click", function (event) { 238 event.preventDefault(); 239 if (!inst.locked) { 240 inst.locked = true; 241 playlistItem = $(this).parents(".playlist-item"); 242 var itemIndex = playlistItem.index(); 243 244 if (itemIndex !== inst.activeVideo) { 245 playlistItem.addClass("active"); 246 playlistItem.siblings().removeClass("active"); 247 inst.replaceSource(itemIndex, true); 248 inst.activeVideo = itemIndex; 249 } 250 //Change video source not more often 251 //than once per second 252 setTimeout(function(){ 253 inst.locked = false; 254 },1000); 255 } 256 257 }); 258 259 navItems.on("click", function (event) { 260 event.preventDefault(); 261 var properties = {}; 262 263 if ( 264 $(this) 265 .parent() 266 .hasClass("playlist-prev") 267 ) { 268 properties.back = true; 269 } 270 /** 271 * Change video event fires when user clicks 272 * a ".playlist-nav a" element. 273 * 274 * @event module:Playlist.Playlist#change-video 275 * @type {object} 276 * @property {Object} properties - Indicates whether playlist has back button 277 */ 278 $(inst.playlist).trigger("change-video", properties); 279 }); 280 }; 281 /** 282 * For each playlist component creates new instance of 283 * ["Playlist"]{@link module:Playlist.Playlist} and calls 284 * ["loadPlaylistVideo"]{@link module:Playlist.Playlist.loadPlaylistVideo}, 285 * ["attachEvents"]{@link module:Playlist.Playlist.attachEvents} 286 * methods 287 * @memberOf module:Playlist 288 * @method 289 * @param {jQuery} component Root DOM element of playlist component wrapped by jQuery 290 * @param {Object} prop Properties set in data attribute 291 * of playlist component 292 * @alias module:Playlist.initInstance 293 */ 294 api.initInstance = function (component, prop) { 295 var playlist; 296 $(prop.playlistId).addClass("initialized"); //prevents video init in component-video.js 297 if (prop.sources.length) { 298 playlist = new Playlist(component, prop); 299 playlist.loadPlaylistVideo(); 300 playlist.attachEvents(); 301 } 302 }; 303 /** 304 * Finds all not initialized yet 305 * Playlist components and in a loop for each of them 306 * run ["initInstance"]{@link module:Playlist.initInstance} 307 * method. 308 * @memberOf module:Playlist 309 * @alias module:Playlist.init 310 */ 311 api.init = function () { 312 var playlists = $(".playlist.component:not(.initialized)"), 313 properties; 314 315 playlists.each(function () { 316 properties = $(this).data("properties"); 317 api.initInstance(this, properties); 318 $(this).addClass("initialized"); 319 }); 320 }; 321 322 return api; 323})(jQuery, document); 324 325XA.register("playlist", XA.component.playlist);
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.