PageRenderTime 41ms CodeModel.GetById 0ms RepoModel.GetById 1ms app.codeStats 0ms

/ajax/libs/jplayer/2.2.0/add-on/jplayer.playlist.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 452 lines | 361 code | 48 blank | 43 comment | 68 complexity | db507525d94792ed399ce529b11f7475 MD5 | raw file
  1. /*
  2. * Playlist Object for the jPlayer Plugin
  3. * http://www.jplayer.org
  4. *
  5. * Copyright (c) 2009 - 2011 Happyworm Ltd
  6. * Dual licensed under the MIT and GPL licenses.
  7. * - http://www.opensource.org/licenses/mit-license.php
  8. * - http://www.gnu.org/copyleft/gpl.html
  9. *
  10. * Author: Mark J Panaghiston
  11. * Version: 2.1.0 (jPlayer 2.1.0)
  12. * Date: 1st September 2011
  13. */
  14. /* Code verified using http://www.jshint.com/ */
  15. /*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 */
  16. /*global jPlayerPlaylist: true, jQuery:false, alert:false */
  17. (function($, undefined) {
  18. jPlayerPlaylist = function(cssSelector, playlist, options) {
  19. var self = this;
  20. this.current = 0;
  21. this.loop = false; // Flag used with the jPlayer repeat event
  22. this.shuffled = false;
  23. this.removing = false; // Flag is true during remove animation, disabling the remove() method until complete.
  24. this.cssSelector = $.extend({}, this._cssSelector, cssSelector); // Object: Containing the css selectors for jPlayer and its cssSelectorAncestor
  25. this.options = $.extend(true, {}, this._options, options); // Object: The jPlayer constructor options for this playlist and the playlist options
  26. this.playlist = []; // Array of Objects: The current playlist displayed (Un-shuffled or Shuffled)
  27. this.original = []; // Array of Objects: The original playlist
  28. 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.)
  29. // Setup the css selectors for the extra interface items used by the playlist.
  30. this.cssSelector.title = this.cssSelector.cssSelectorAncestor + " .jp-title"; // Note that the text is written to the decendant li node.
  31. this.cssSelector.playlist = this.cssSelector.cssSelectorAncestor + " .jp-playlist";
  32. this.cssSelector.next = this.cssSelector.cssSelectorAncestor + " .jp-next";
  33. this.cssSelector.previous = this.cssSelector.cssSelectorAncestor + " .jp-previous";
  34. this.cssSelector.shuffle = this.cssSelector.cssSelectorAncestor + " .jp-shuffle";
  35. this.cssSelector.shuffleOff = this.cssSelector.cssSelectorAncestor + " .jp-shuffle-off";
  36. // Override the cssSelectorAncestor given in options
  37. this.options.cssSelectorAncestor = this.cssSelector.cssSelectorAncestor;
  38. // Override the default repeat event handler
  39. this.options.repeat = function(event) {
  40. self.loop = event.jPlayer.options.loop;
  41. };
  42. // Create a ready event handler to initialize the playlist
  43. $(this.cssSelector.jPlayer).bind($.jPlayer.event.ready, function(event) {
  44. self._init();
  45. });
  46. // Create an ended event handler to move to the next item
  47. $(this.cssSelector.jPlayer).bind($.jPlayer.event.ended, function(event) {
  48. self.next();
  49. });
  50. // Create a play event handler to pause other instances
  51. $(this.cssSelector.jPlayer).bind($.jPlayer.event.play, function(event) {
  52. $(this).jPlayer("pauseOthers");
  53. });
  54. // Create a resize event handler to show the title in full screen mode.
  55. $(this.cssSelector.jPlayer).bind($.jPlayer.event.resize, function(event) {
  56. if(event.jPlayer.options.fullScreen) {
  57. $(self.cssSelector.title).show();
  58. } else {
  59. $(self.cssSelector.title).hide();
  60. }
  61. });
  62. // Create click handlers for the extra buttons that do playlist functions.
  63. $(this.cssSelector.previous).click(function() {
  64. self.previous();
  65. $(this).blur();
  66. return false;
  67. });
  68. $(this.cssSelector.next).click(function() {
  69. self.next();
  70. $(this).blur();
  71. return false;
  72. });
  73. $(this.cssSelector.shuffle).click(function() {
  74. self.shuffle(true);
  75. return false;
  76. });
  77. $(this.cssSelector.shuffleOff).click(function() {
  78. self.shuffle(false);
  79. return false;
  80. }).hide();
  81. // Put the title in its initial display state
  82. if(!this.options.fullScreen) {
  83. $(this.cssSelector.title).hide();
  84. }
  85. // Remove the empty <li> from the page HTML. Allows page to be valid HTML, while not interfereing with display animations
  86. $(this.cssSelector.playlist + " ul").empty();
  87. // Create .live() handlers for the playlist items along with the free media and remove controls.
  88. this._createItemHandlers();
  89. // Instance jPlayer
  90. $(this.cssSelector.jPlayer).jPlayer(this.options);
  91. };
  92. jPlayerPlaylist.prototype = {
  93. _cssSelector: { // static object, instanced in constructor
  94. jPlayer: "#jquery_jplayer_1",
  95. cssSelectorAncestor: "#jp_container_1"
  96. },
  97. _options: { // static object, instanced in constructor
  98. playlistOptions: {
  99. autoPlay: false,
  100. loopOnPrevious: false,
  101. shuffleOnLoop: true,
  102. enableRemoveControls: false,
  103. displayTime: 'slow',
  104. addTime: 'fast',
  105. removeTime: 'fast',
  106. shuffleTime: 'slow',
  107. itemClass: "jp-playlist-item",
  108. freeGroupClass: "jp-free-media",
  109. freeItemClass: "jp-playlist-item-free",
  110. removeItemClass: "jp-playlist-item-remove"
  111. }
  112. },
  113. option: function(option, value) { // For changing playlist options only
  114. if(value === undefined) {
  115. return this.options.playlistOptions[option];
  116. }
  117. this.options.playlistOptions[option] = value;
  118. switch(option) {
  119. case "enableRemoveControls":
  120. this._updateControls();
  121. break;
  122. case "itemClass":
  123. case "freeGroupClass":
  124. case "freeItemClass":
  125. case "removeItemClass":
  126. this._refresh(true); // Instant
  127. this._createItemHandlers();
  128. break;
  129. }
  130. return this;
  131. },
  132. _init: function() {
  133. var self = this;
  134. this._refresh(function() {
  135. if(self.options.playlistOptions.autoPlay) {
  136. self.play(self.current);
  137. } else {
  138. self.select(self.current);
  139. }
  140. });
  141. },
  142. _initPlaylist: function(playlist) {
  143. this.current = 0;
  144. this.shuffled = false;
  145. this.removing = false;
  146. this.original = $.extend(true, [], playlist); // Copy the Array of Objects
  147. this._originalPlaylist();
  148. },
  149. _originalPlaylist: function() {
  150. var self = this;
  151. this.playlist = [];
  152. // 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.
  153. $.each(this.original, function(i,v) {
  154. self.playlist[i] = self.original[i];
  155. });
  156. },
  157. _refresh: function(instant) {
  158. /* instant: Can be undefined, true or a function.
  159. * undefined -> use animation timings
  160. * true -> no animation
  161. * function -> use animation timings and excute function at half way point.
  162. */
  163. var self = this;
  164. if(instant && !$.isFunction(instant)) {
  165. $(this.cssSelector.playlist + " ul").empty();
  166. $.each(this.playlist, function(i,v) {
  167. $(self.cssSelector.playlist + " ul").append(self._createListItem(self.playlist[i]));
  168. });
  169. this._updateControls();
  170. } else {
  171. var displayTime = $(this.cssSelector.playlist + " ul").children().length ? this.options.playlistOptions.displayTime : 0;
  172. $(this.cssSelector.playlist + " ul").slideUp(displayTime, function() {
  173. var $this = $(this);
  174. $(this).empty();
  175. $.each(self.playlist, function(i,v) {
  176. $this.append(self._createListItem(self.playlist[i]));
  177. });
  178. self._updateControls();
  179. if($.isFunction(instant)) {
  180. instant();
  181. }
  182. if(self.playlist.length) {
  183. $(this).slideDown(self.options.playlistOptions.displayTime);
  184. } else {
  185. $(this).show();
  186. }
  187. });
  188. }
  189. },
  190. _createListItem: function(media) {
  191. var self = this;
  192. // Wrap the <li> contents in a <div>
  193. var listItem = "<li><div>";
  194. // Create remove control
  195. listItem += "<a href='javascript:;' class='" + this.options.playlistOptions.removeItemClass + "'>&times;</a>";
  196. // Create links to free media
  197. if(media.free) {
  198. var first = true;
  199. listItem += "<span class='" + this.options.playlistOptions.freeGroupClass + "'>(";
  200. $.each(media, function(property,value) {
  201. if($.jPlayer.prototype.format[property]) { // Check property is a media format.
  202. if(first) {
  203. first = false;
  204. } else {
  205. listItem += " | ";
  206. }
  207. listItem += "<a class='" + self.options.playlistOptions.freeItemClass + "' href='" + value + "' tabindex='1'>" + property + "</a>";
  208. }
  209. });
  210. listItem += ")</span>";
  211. }
  212. // The title is given next in the HTML otherwise the float:right on the free media corrupts in IE6/7
  213. listItem += "<a href='javascript:;' class='" + this.options.playlistOptions.itemClass + "' tabindex='1'>" + media.title + (media.artist ? " <span class='jp-artist'>by " + media.artist + "</span>" : "") + "</a>";
  214. listItem += "</div></li>";
  215. return listItem;
  216. },
  217. _createItemHandlers: function() {
  218. var self = this;
  219. // Create .live() handlers for the playlist items
  220. $(this.cssSelector.playlist + " a." + this.options.playlistOptions.itemClass).die("click").live("click", function() {
  221. var index = $(this).parent().parent().index();
  222. if(self.current !== index) {
  223. self.play(index);
  224. } else {
  225. $(self.cssSelector.jPlayer).jPlayer("play");
  226. }
  227. $(this).blur();
  228. return false;
  229. });
  230. // Create .live() handlers that disable free media links to force access via right click
  231. $(self.cssSelector.playlist + " a." + this.options.playlistOptions.freeItemClass).die("click").live("click", function() {
  232. $(this).parent().parent().find("." + self.options.playlistOptions.itemClass).click();
  233. $(this).blur();
  234. return false;
  235. });
  236. // Create .live() handlers for the remove controls
  237. $(self.cssSelector.playlist + " a." + this.options.playlistOptions.removeItemClass).die("click").live("click", function() {
  238. var index = $(this).parent().parent().index();
  239. self.remove(index);
  240. $(this).blur();
  241. return false;
  242. });
  243. },
  244. _updateControls: function() {
  245. if(this.options.playlistOptions.enableRemoveControls) {
  246. $(this.cssSelector.playlist + " ." + this.options.playlistOptions.removeItemClass).show();
  247. } else {
  248. $(this.cssSelector.playlist + " ." + this.options.playlistOptions.removeItemClass).hide();
  249. }
  250. if(this.shuffled) {
  251. $(this.cssSelector.shuffleOff).show();
  252. $(this.cssSelector.shuffle).hide();
  253. } else {
  254. $(this.cssSelector.shuffleOff).hide();
  255. $(this.cssSelector.shuffle).show();
  256. }
  257. },
  258. _highlight: function(index) {
  259. if(this.playlist.length && index !== undefined) {
  260. $(this.cssSelector.playlist + " .jp-playlist-current").removeClass("jp-playlist-current");
  261. $(this.cssSelector.playlist + " li:nth-child(" + (index + 1) + ")").addClass("jp-playlist-current").find(".jp-playlist-item").addClass("jp-playlist-current");
  262. $(this.cssSelector.title + " li").html(this.playlist[index].title + (this.playlist[index].artist ? " <span class='jp-artist'>by " + this.playlist[index].artist + "</span>" : ""));
  263. }
  264. },
  265. setPlaylist: function(playlist) {
  266. this._initPlaylist(playlist);
  267. this._init();
  268. },
  269. add: function(media, playNow) {
  270. $(this.cssSelector.playlist + " ul").append(this._createListItem(media)).find("li:last-child").hide().slideDown(this.options.playlistOptions.addTime);
  271. this._updateControls();
  272. this.original.push(media);
  273. this.playlist.push(media); // Both array elements share the same object pointer. Comforms with _initPlaylist(p) system.
  274. if(playNow) {
  275. this.play(this.playlist.length - 1);
  276. } else {
  277. if(this.original.length === 1) {
  278. this.select(0);
  279. }
  280. }
  281. },
  282. remove: function(index) {
  283. var self = this;
  284. if(index === undefined) {
  285. this._initPlaylist([]);
  286. this._refresh(function() {
  287. $(self.cssSelector.jPlayer).jPlayer("clearMedia");
  288. });
  289. return true;
  290. } else {
  291. if(this.removing) {
  292. return false;
  293. } else {
  294. index = (index < 0) ? self.original.length + index : index; // Negative index relates to end of array.
  295. if(0 <= index && index < this.playlist.length) {
  296. this.removing = true;
  297. $(this.cssSelector.playlist + " li:nth-child(" + (index + 1) + ")").slideUp(this.options.playlistOptions.removeTime, function() {
  298. $(this).remove();
  299. if(self.shuffled) {
  300. var item = self.playlist[index];
  301. $.each(self.original, function(i,v) {
  302. if(self.original[i] === item) {
  303. self.original.splice(i, 1);
  304. return false; // Exit $.each
  305. }
  306. });
  307. self.playlist.splice(index, 1);
  308. } else {
  309. self.original.splice(index, 1);
  310. self.playlist.splice(index, 1);
  311. }
  312. if(self.original.length) {
  313. if(index === self.current) {
  314. self.current = (index < self.original.length) ? self.current : self.original.length - 1; // To cope when last element being selected when it was removed
  315. self.select(self.current);
  316. } else if(index < self.current) {
  317. self.current--;
  318. }
  319. } else {
  320. $(self.cssSelector.jPlayer).jPlayer("clearMedia");
  321. self.current = 0;
  322. self.shuffled = false;
  323. self._updateControls();
  324. }
  325. self.removing = false;
  326. });
  327. }
  328. return true;
  329. }
  330. }
  331. },
  332. select: function(index) {
  333. index = (index < 0) ? this.original.length + index : index; // Negative index relates to end of array.
  334. if(0 <= index && index < this.playlist.length) {
  335. this.current = index;
  336. this._highlight(index);
  337. $(this.cssSelector.jPlayer).jPlayer("setMedia", this.playlist[this.current]);
  338. } else {
  339. this.current = 0;
  340. }
  341. },
  342. play: function(index) {
  343. index = (index < 0) ? this.original.length + index : index; // Negative index relates to end of array.
  344. if(0 <= index && index < this.playlist.length) {
  345. if(this.playlist.length) {
  346. this.select(index);
  347. $(this.cssSelector.jPlayer).jPlayer("play");
  348. }
  349. } else if(index === undefined) {
  350. $(this.cssSelector.jPlayer).jPlayer("play");
  351. }
  352. },
  353. pause: function() {
  354. $(this.cssSelector.jPlayer).jPlayer("pause");
  355. },
  356. next: function() {
  357. var index = (this.current + 1 < this.playlist.length) ? this.current + 1 : 0;
  358. if(this.loop) {
  359. // See if we need to shuffle before looping to start, and only shuffle if more than 1 item.
  360. if(index === 0 && this.shuffled && this.options.playlistOptions.shuffleOnLoop && this.playlist.length > 1) {
  361. this.shuffle(true, true); // playNow
  362. } else {
  363. this.play(index);
  364. }
  365. } else {
  366. // The index will be zero if it just looped round
  367. if(index > 0) {
  368. this.play(index);
  369. }
  370. }
  371. },
  372. previous: function() {
  373. var index = (this.current - 1 >= 0) ? this.current - 1 : this.playlist.length - 1;
  374. if(this.loop && this.options.playlistOptions.loopOnPrevious || index < this.playlist.length - 1) {
  375. this.play(index);
  376. }
  377. },
  378. shuffle: function(shuffled, playNow) {
  379. var self = this;
  380. if(shuffled === undefined) {
  381. shuffled = !this.shuffled;
  382. }
  383. if(shuffled || shuffled !== this.shuffled) {
  384. $(this.cssSelector.playlist + " ul").slideUp(this.options.playlistOptions.shuffleTime, function() {
  385. self.shuffled = shuffled;
  386. if(shuffled) {
  387. self.playlist.sort(function() {
  388. return 0.5 - Math.random();
  389. });
  390. } else {
  391. self._originalPlaylist();
  392. }
  393. self._refresh(true); // Instant
  394. if(playNow || !$(self.cssSelector.jPlayer).data("jPlayer").status.paused) {
  395. self.play(0);
  396. } else {
  397. self.select(0);
  398. }
  399. $(this).slideDown(self.options.playlistOptions.shuffleTime);
  400. });
  401. }
  402. }
  403. };
  404. })(jQuery);