/ajax/libs/jplayer/2.2.0/add-on/jplayer.playlist.js
JavaScript | 452 lines | 361 code | 48 blank | 43 comment | 68 complexity | db507525d94792ed399ce529b11f7475 MD5 | raw file
- /*
- * Playlist Object for the jPlayer Plugin
- * http://www.jplayer.org
- *
- * Copyright (c) 2009 - 2011 Happyworm Ltd
- * Dual licensed under the MIT and GPL licenses.
- * - http://www.opensource.org/licenses/mit-license.php
- * - http://www.gnu.org/copyleft/gpl.html
- *
- * Author: Mark J Panaghiston
- * Version: 2.1.0 (jPlayer 2.1.0)
- * Date: 1st September 2011
- */
- /* Code verified using http://www.jshint.com/ */
- /*jshint asi:false, bitwise:false, boss:false, browser:true, curly:true, debug:false, eqeqeq:true, eqnull:false, evil:false, forin:false, immed:false, jquery:true, laxbreak:false, newcap:true, noarg:true, noempty:true, nonew:true, nomem:false, onevar:false, passfail:false, plusplus:false, regexp:false, undef:true, sub:false, strict:false, white:false */
- /*global jPlayerPlaylist: true, jQuery:false, alert:false */
- (function($, undefined) {
- jPlayerPlaylist = function(cssSelector, playlist, options) {
- var self = this;
- this.current = 0;
- this.loop = false; // Flag used with the jPlayer repeat event
- this.shuffled = false;
- this.removing = false; // Flag is true during remove animation, disabling the remove() method until complete.
- this.cssSelector = $.extend({}, this._cssSelector, cssSelector); // Object: Containing the css selectors for jPlayer and its cssSelectorAncestor
- this.options = $.extend(true, {}, this._options, options); // Object: The jPlayer constructor options for this playlist and the playlist options
- this.playlist = []; // Array of Objects: The current playlist displayed (Un-shuffled or Shuffled)
- this.original = []; // Array of Objects: The original playlist
- this._initPlaylist(playlist); // Copies playlist to this.original. Then mirrors this.original to this.playlist. Creating two arrays, where the element pointers match. (Enables pointer comparison.)
- // Setup the css selectors for the extra interface items used by the playlist.
- this.cssSelector.title = this.cssSelector.cssSelectorAncestor + " .jp-title"; // Note that the text is written to the decendant li node.
- this.cssSelector.playlist = this.cssSelector.cssSelectorAncestor + " .jp-playlist";
- this.cssSelector.next = this.cssSelector.cssSelectorAncestor + " .jp-next";
- this.cssSelector.previous = this.cssSelector.cssSelectorAncestor + " .jp-previous";
- this.cssSelector.shuffle = this.cssSelector.cssSelectorAncestor + " .jp-shuffle";
- this.cssSelector.shuffleOff = this.cssSelector.cssSelectorAncestor + " .jp-shuffle-off";
- // Override the cssSelectorAncestor given in options
- this.options.cssSelectorAncestor = this.cssSelector.cssSelectorAncestor;
- // Override the default repeat event handler
- this.options.repeat = function(event) {
- self.loop = event.jPlayer.options.loop;
- };
- // Create a ready event handler to initialize the playlist
- $(this.cssSelector.jPlayer).bind($.jPlayer.event.ready, function(event) {
- self._init();
- });
- // Create an ended event handler to move to the next item
- $(this.cssSelector.jPlayer).bind($.jPlayer.event.ended, function(event) {
- self.next();
- });
- // Create a play event handler to pause other instances
- $(this.cssSelector.jPlayer).bind($.jPlayer.event.play, function(event) {
- $(this).jPlayer("pauseOthers");
- });
- // Create a resize event handler to show the title in full screen mode.
- $(this.cssSelector.jPlayer).bind($.jPlayer.event.resize, function(event) {
- if(event.jPlayer.options.fullScreen) {
- $(self.cssSelector.title).show();
- } else {
- $(self.cssSelector.title).hide();
- }
- });
- // Create click handlers for the extra buttons that do playlist functions.
- $(this.cssSelector.previous).click(function() {
- self.previous();
- $(this).blur();
- return false;
- });
- $(this.cssSelector.next).click(function() {
- self.next();
- $(this).blur();
- return false;
- });
- $(this.cssSelector.shuffle).click(function() {
- self.shuffle(true);
- return false;
- });
- $(this.cssSelector.shuffleOff).click(function() {
- self.shuffle(false);
- return false;
- }).hide();
- // Put the title in its initial display state
- if(!this.options.fullScreen) {
- $(this.cssSelector.title).hide();
- }
- // Remove the empty <li> from the page HTML. Allows page to be valid HTML, while not interfereing with display animations
- $(this.cssSelector.playlist + " ul").empty();
- // Create .live() handlers for the playlist items along with the free media and remove controls.
- this._createItemHandlers();
- // Instance jPlayer
- $(this.cssSelector.jPlayer).jPlayer(this.options);
- };
- jPlayerPlaylist.prototype = {
- _cssSelector: { // static object, instanced in constructor
- jPlayer: "#jquery_jplayer_1",
- cssSelectorAncestor: "#jp_container_1"
- },
- _options: { // static object, instanced in constructor
- playlistOptions: {
- autoPlay: false,
- loopOnPrevious: false,
- shuffleOnLoop: true,
- enableRemoveControls: false,
- displayTime: 'slow',
- addTime: 'fast',
- removeTime: 'fast',
- shuffleTime: 'slow',
- itemClass: "jp-playlist-item",
- freeGroupClass: "jp-free-media",
- freeItemClass: "jp-playlist-item-free",
- removeItemClass: "jp-playlist-item-remove"
- }
- },
- option: function(option, value) { // For changing playlist options only
- if(value === undefined) {
- return this.options.playlistOptions[option];
- }
- this.options.playlistOptions[option] = value;
- switch(option) {
- case "enableRemoveControls":
- this._updateControls();
- break;
- case "itemClass":
- case "freeGroupClass":
- case "freeItemClass":
- case "removeItemClass":
- this._refresh(true); // Instant
- this._createItemHandlers();
- break;
- }
- return this;
- },
- _init: function() {
- var self = this;
- this._refresh(function() {
- if(self.options.playlistOptions.autoPlay) {
- self.play(self.current);
- } else {
- self.select(self.current);
- }
- });
- },
- _initPlaylist: function(playlist) {
- this.current = 0;
- this.shuffled = false;
- this.removing = false;
- this.original = $.extend(true, [], playlist); // Copy the Array of Objects
- this._originalPlaylist();
- },
- _originalPlaylist: function() {
- var self = this;
- this.playlist = [];
- // Make both arrays point to the same object elements. Gives us 2 different arrays, each pointing to the same actual object. ie., Not copies of the object.
- $.each(this.original, function(i,v) {
- self.playlist[i] = self.original[i];
- });
- },
- _refresh: function(instant) {
- /* instant: Can be undefined, true or a function.
- * undefined -> use animation timings
- * true -> no animation
- * function -> use animation timings and excute function at half way point.
- */
- var self = this;
- if(instant && !$.isFunction(instant)) {
- $(this.cssSelector.playlist + " ul").empty();
- $.each(this.playlist, function(i,v) {
- $(self.cssSelector.playlist + " ul").append(self._createListItem(self.playlist[i]));
- });
- this._updateControls();
- } else {
- var displayTime = $(this.cssSelector.playlist + " ul").children().length ? this.options.playlistOptions.displayTime : 0;
- $(this.cssSelector.playlist + " ul").slideUp(displayTime, function() {
- var $this = $(this);
- $(this).empty();
-
- $.each(self.playlist, function(i,v) {
- $this.append(self._createListItem(self.playlist[i]));
- });
- self._updateControls();
- if($.isFunction(instant)) {
- instant();
- }
- if(self.playlist.length) {
- $(this).slideDown(self.options.playlistOptions.displayTime);
- } else {
- $(this).show();
- }
- });
- }
- },
- _createListItem: function(media) {
- var self = this;
- // Wrap the <li> contents in a <div>
- var listItem = "<li><div>";
- // Create remove control
- listItem += "<a href='javascript:;' class='" + this.options.playlistOptions.removeItemClass + "'>×</a>";
- // Create links to free media
- if(media.free) {
- var first = true;
- listItem += "<span class='" + this.options.playlistOptions.freeGroupClass + "'>(";
- $.each(media, function(property,value) {
- if($.jPlayer.prototype.format[property]) { // Check property is a media format.
- if(first) {
- first = false;
- } else {
- listItem += " | ";
- }
- listItem += "<a class='" + self.options.playlistOptions.freeItemClass + "' href='" + value + "' tabindex='1'>" + property + "</a>";
- }
- });
- listItem += ")</span>";
- }
- // The title is given next in the HTML otherwise the float:right on the free media corrupts in IE6/7
- listItem += "<a href='javascript:;' class='" + this.options.playlistOptions.itemClass + "' tabindex='1'>" + media.title + (media.artist ? " <span class='jp-artist'>by " + media.artist + "</span>" : "") + "</a>";
- listItem += "</div></li>";
- return listItem;
- },
- _createItemHandlers: function() {
- var self = this;
- // Create .live() handlers for the playlist items
- $(this.cssSelector.playlist + " a." + this.options.playlistOptions.itemClass).die("click").live("click", function() {
- var index = $(this).parent().parent().index();
- if(self.current !== index) {
- self.play(index);
- } else {
- $(self.cssSelector.jPlayer).jPlayer("play");
- }
- $(this).blur();
- return false;
- });
- // Create .live() handlers that disable free media links to force access via right click
- $(self.cssSelector.playlist + " a." + this.options.playlistOptions.freeItemClass).die("click").live("click", function() {
- $(this).parent().parent().find("." + self.options.playlistOptions.itemClass).click();
- $(this).blur();
- return false;
- });
- // Create .live() handlers for the remove controls
- $(self.cssSelector.playlist + " a." + this.options.playlistOptions.removeItemClass).die("click").live("click", function() {
- var index = $(this).parent().parent().index();
- self.remove(index);
- $(this).blur();
- return false;
- });
- },
- _updateControls: function() {
- if(this.options.playlistOptions.enableRemoveControls) {
- $(this.cssSelector.playlist + " ." + this.options.playlistOptions.removeItemClass).show();
- } else {
- $(this.cssSelector.playlist + " ." + this.options.playlistOptions.removeItemClass).hide();
- }
- if(this.shuffled) {
- $(this.cssSelector.shuffleOff).show();
- $(this.cssSelector.shuffle).hide();
- } else {
- $(this.cssSelector.shuffleOff).hide();
- $(this.cssSelector.shuffle).show();
- }
- },
- _highlight: function(index) {
- if(this.playlist.length && index !== undefined) {
- $(this.cssSelector.playlist + " .jp-playlist-current").removeClass("jp-playlist-current");
- $(this.cssSelector.playlist + " li:nth-child(" + (index + 1) + ")").addClass("jp-playlist-current").find(".jp-playlist-item").addClass("jp-playlist-current");
- $(this.cssSelector.title + " li").html(this.playlist[index].title + (this.playlist[index].artist ? " <span class='jp-artist'>by " + this.playlist[index].artist + "</span>" : ""));
- }
- },
- setPlaylist: function(playlist) {
- this._initPlaylist(playlist);
- this._init();
- },
- add: function(media, playNow) {
- $(this.cssSelector.playlist + " ul").append(this._createListItem(media)).find("li:last-child").hide().slideDown(this.options.playlistOptions.addTime);
- this._updateControls();
- this.original.push(media);
- this.playlist.push(media); // Both array elements share the same object pointer. Comforms with _initPlaylist(p) system.
- if(playNow) {
- this.play(this.playlist.length - 1);
- } else {
- if(this.original.length === 1) {
- this.select(0);
- }
- }
- },
- remove: function(index) {
- var self = this;
- if(index === undefined) {
- this._initPlaylist([]);
- this._refresh(function() {
- $(self.cssSelector.jPlayer).jPlayer("clearMedia");
- });
- return true;
- } else {
- if(this.removing) {
- return false;
- } else {
- index = (index < 0) ? self.original.length + index : index; // Negative index relates to end of array.
- if(0 <= index && index < this.playlist.length) {
- this.removing = true;
- $(this.cssSelector.playlist + " li:nth-child(" + (index + 1) + ")").slideUp(this.options.playlistOptions.removeTime, function() {
- $(this).remove();
- if(self.shuffled) {
- var item = self.playlist[index];
- $.each(self.original, function(i,v) {
- if(self.original[i] === item) {
- self.original.splice(i, 1);
- return false; // Exit $.each
- }
- });
- self.playlist.splice(index, 1);
- } else {
- self.original.splice(index, 1);
- self.playlist.splice(index, 1);
- }
- if(self.original.length) {
- if(index === self.current) {
- self.current = (index < self.original.length) ? self.current : self.original.length - 1; // To cope when last element being selected when it was removed
- self.select(self.current);
- } else if(index < self.current) {
- self.current--;
- }
- } else {
- $(self.cssSelector.jPlayer).jPlayer("clearMedia");
- self.current = 0;
- self.shuffled = false;
- self._updateControls();
- }
- self.removing = false;
- });
- }
- return true;
- }
- }
- },
- select: function(index) {
- index = (index < 0) ? this.original.length + index : index; // Negative index relates to end of array.
- if(0 <= index && index < this.playlist.length) {
- this.current = index;
- this._highlight(index);
- $(this.cssSelector.jPlayer).jPlayer("setMedia", this.playlist[this.current]);
- } else {
- this.current = 0;
- }
- },
- play: function(index) {
- index = (index < 0) ? this.original.length + index : index; // Negative index relates to end of array.
- if(0 <= index && index < this.playlist.length) {
- if(this.playlist.length) {
- this.select(index);
- $(this.cssSelector.jPlayer).jPlayer("play");
- }
- } else if(index === undefined) {
- $(this.cssSelector.jPlayer).jPlayer("play");
- }
- },
- pause: function() {
- $(this.cssSelector.jPlayer).jPlayer("pause");
- },
- next: function() {
- var index = (this.current + 1 < this.playlist.length) ? this.current + 1 : 0;
- if(this.loop) {
- // See if we need to shuffle before looping to start, and only shuffle if more than 1 item.
- if(index === 0 && this.shuffled && this.options.playlistOptions.shuffleOnLoop && this.playlist.length > 1) {
- this.shuffle(true, true); // playNow
- } else {
- this.play(index);
- }
- } else {
- // The index will be zero if it just looped round
- if(index > 0) {
- this.play(index);
- }
- }
- },
- previous: function() {
- var index = (this.current - 1 >= 0) ? this.current - 1 : this.playlist.length - 1;
- if(this.loop && this.options.playlistOptions.loopOnPrevious || index < this.playlist.length - 1) {
- this.play(index);
- }
- },
- shuffle: function(shuffled, playNow) {
- var self = this;
- if(shuffled === undefined) {
- shuffled = !this.shuffled;
- }
- if(shuffled || shuffled !== this.shuffled) {
- $(this.cssSelector.playlist + " ul").slideUp(this.options.playlistOptions.shuffleTime, function() {
- self.shuffled = shuffled;
- if(shuffled) {
- self.playlist.sort(function() {
- return 0.5 - Math.random();
- });
- } else {
- self._originalPlaylist();
- }
- self._refresh(true); // Instant
- if(playNow || !$(self.cssSelector.jPlayer).data("jPlayer").status.paused) {
- self.play(0);
- } else {
- self.select(0);
- }
- $(this).slideDown(self.options.playlistOptions.shuffleTime);
- });
- }
- }
- };
- })(jQuery);