/* global mejs:false */
/**
* Component Playlist
* @module Playlist
* @param {jQuery} $ Instance of jQuery
* @return {Object} List of Playlist component methods
*/
XA.component.playlist = (function ($) {
/**
* This object stores all public api methods
* @type {Object.<Methods>}
* @memberOf module:Playlist
* */
var api = {};
/**
* This class is used by [Playlist]{@link module:Playlist} module
* @class Playlist
* @memberOf module:Playlist
* @param {jQuery} playlist Root DOM element of playlist component wrapped by jQuery
* @param {Object} properties Properties set in data attribute
*/
function Playlist(playlist, properties) {
this.properties = properties;
this.playlist = playlist;
this.activeVideo = 0;
this.playlistItems = 0;
}
/**
* Creates new source element, sets up it with type & src. Created element
* append to Video container (take it from videoContainer parameter)
* @method
* @alias createNewSources
* @memberOf module:Playlist.Playlist
* @param {$OrderedList} source takes from properties.sources[itemIndex].src -
* source of playlist element
* @param {$OrderedList} videoContainer container where source should be added
*/
Playlist.prototype.createNewSources = function (source, videoContainer) {
var newSource;
var sourceBuilder = function (path) {
var newSource = $("<source>"),
type;
if (path.match(/\.(mp4)$/)) {
type = "video/mp4";
} else if (path.match(/\.(webm)$/)) {
type = "video/webm";
} else if (path.match(/\.(ogv)$/)) {
type = "video/ogg";
} else {
type = "video/youtube";
}
newSource.attr({
type: type,
src: path
});
return newSource;
};
if (source instanceof Array) {
for (var i = 0; i < source.length; i++) {
newSource = sourceBuilder(source[i]);
videoContainer.find("video").append(newSource);
}
} else {
newSource = sourceBuilder(source);
videoContainer.find("video").append(newSource);
}
};
/**
* Replaces source of each video element under play list component.
* After replacing it re-initializes video component by calling
* [XA.component.video.initVideoFromPlaylist]{@link module:Video.initVideoFromPlaylist}
* @name replaceSource
* @function
* @param {number} itemIndex position of active video in video list
* @param {boolean} loadFromEvent
* @memberOf module:Playlist.Playlist
*/
Playlist.prototype.replaceSource = function (itemIndex, loadFromEvent) {
var inst = this,
videoContainer,
videoClone,
newSrc = inst.properties.sources[itemIndex].src,
sources,
videoId,
videoContainerHeight = 0;
$(inst.properties.playlistId).each(function () {
videoContainer = $(this);
videoId = inst.properties.playlistId;
if (videoContainer.is(videoId) && newSrc.length) {
videoContainer.addClass("show");
sources = videoContainer.find("source");
sources.remove();
inst.createNewSources(newSrc, videoContainer);
videoContainer
.find("video")
.attr({
src: ""
})
.show();
var autoplayVideo = false;
if (loadFromEvent) {
if (inst.properties.autoPlaySelected) {
autoplayVideo = true;
}
} else {
if (inst.properties.autoPlay) {
autoplayVideo = true;
}
}
if (autoplayVideo) {
videoContainer.find("video").attr({
autoplay: ""
});
}
videoClone = videoContainer.find("video").clone();
videoContainerHeight = videoContainer.height();
videoContainer.css({
height: videoContainerHeight
});
var id = videoContainer.find(".mejs-container").attr("id");
if (id) {
$("#" + id).remove();
delete mejs.players[id];
videoContainer
.find(".component-content")
.append(videoClone);
}
XA.component.video.initVideoFromPlaylist(
videoContainer,
inst.playlist
);
videoContainer.css({
height: "auto"
});
}
});
};
/**
* Initializes videos under component. On call and on
* [change-video]{@link module:Playlist.Playlist#change-video} event
* calls [replaceSource]{@link module:Playlist.Playlist.replaceSource}
* @name loadPlaylistVideo
* @function
* @listeners module:Playlist.Playlist#change-video
* @memberOf module:Playlist.Playlist
*/
Playlist.prototype.loadPlaylistVideo = function () {
var inst = this,
playlistItems = $(inst.playlist).find(".playlist-item"),
activeListItem;
inst.playlistItems = playlistItems.length;
var loadVideoFromPlaylist = function (loadFromEvent) {
inst.replaceSource(inst.activeVideo, loadFromEvent);
activeListItem = playlistItems.eq(inst.activeVideo);
activeListItem.addClass("active");
activeListItem.siblings().removeClass("active");
};
loadVideoFromPlaylist();
$(inst.playlist).on("change-video", function (event, properties) {
var loadNewVideo = false;
if (properties) {
if (properties.hasOwnProperty("back")) {
inst.activeVideo--;
if (inst.activeVideo < 0) {
inst.activeVideo = 0;
} else {
loadNewVideo = true;
}
} else {
inst.activeVideo++;
if (inst.activeVideo === inst.playlistItems) {
if (inst.properties.repeatAfterAll) {
inst.activeVideo = 0;
loadNewVideo = true;
} else {
inst.activeVideo = inst.playlistItems - 1;
}
} else {
loadNewVideo = true;
}
}
} else {
if (inst.properties.playNext) {
if (inst.activeVideo + 1 <= inst.playlistItems) {
inst.activeVideo++;
if (inst.activeVideo === inst.playlistItems) {
inst.activeVideo = 0;
if (inst.properties.repeatAfterAll) {
loadNewVideo = true;
}
} else {
inst.actiVideo--;
loadNewVideo = true;
}
}
}
}
if (loadNewVideo) {
loadVideoFromPlaylist(true);
}
});
};
/**
* Attaches events to playlist-section and navigation items
* @name attachEvents
* @function
* @fires module:Playlist.Playlist#change-video
* @memberOf module:Playlist.Playlist
*/
Playlist.prototype.attachEvents = function () {
var inst = this,
link = $(inst.playlist).find(".playlist-section"),
navItems = $(inst.playlist).find(".playlist-nav a"),
playlistItem;
link.on("click", function (event) {
event.preventDefault();
if (!inst.locked) {
inst.locked = true;
playlistItem = $(this).parents(".playlist-item");
var itemIndex = playlistItem.index();
if (itemIndex !== inst.activeVideo) {
playlistItem.addClass("active");
playlistItem.siblings().removeClass("active");
inst.replaceSource(itemIndex, true);
inst.activeVideo = itemIndex;
}
//Change video source not more often
//than once per second
setTimeout(function(){
inst.locked = false;
},1000);
}
});
navItems.on("click", function (event) {
event.preventDefault();
var properties = {};
if (
$(this)
.parent()
.hasClass("playlist-prev")
) {
properties.back = true;
}
/**
* Change video event fires when user clicks
* a ".playlist-nav a" element.
*
* @event module:Playlist.Playlist#change-video
* @type {object}
* @property {Object} properties - Indicates whether playlist has back button
*/
$(inst.playlist).trigger("change-video", properties);
});
};
/**
* For each playlist component creates new instance of
* ["Playlist"]{@link module:Playlist.Playlist} and calls
* ["loadPlaylistVideo"]{@link module:Playlist.Playlist.loadPlaylistVideo},
* ["attachEvents"]{@link module:Playlist.Playlist.attachEvents}
* methods
* @memberOf module:Playlist
* @method
* @param {jQuery} component Root DOM element of playlist component wrapped by jQuery
* @param {Object} prop Properties set in data attribute
* of playlist component
* @alias module:Playlist.initInstance
*/
api.initInstance = function (component, prop) {
var playlist;
$(prop.playlistId).addClass("initialized"); //prevents video init in component-video.js
if (prop.sources.length) {
playlist = new Playlist(component, prop);
playlist.loadPlaylistVideo();
playlist.attachEvents();
}
};
/**
* Finds all not initialized yet
* Playlist components and in a loop for each of them
* run ["initInstance"]{@link module:Playlist.initInstance}
* method.
* @memberOf module:Playlist
* @alias module:Playlist.init
*/
api.init = function () {
var playlists = $(".playlist.component:not(.initialized)"),
properties;
playlists.each(function () {
properties = $(this).data("properties");
api.initInstance(this, properties);
$(this).addClass("initialized");
});
};
return api;
})(jQuery, document);
XA.register("playlist", XA.component.playlist);