player-56s
Version:
Web audio-player with playlist and minimalistic view as option
673 lines (564 loc) • 25.3 kB
JavaScript
/*!
* Player 56s v0.5.1
* Copyright 2015 by Ivan Dymkov (http://dymio.net)
and 56 STUFF (http://www.56stuff.com/)
* Licensed under the MIT license
* Inspired by Jouele 2.0.0
* https://github.com/ilyabirman/Jouele
* In fact, code of this plugin is reworked code of Jouele =)
* based on commit 49ec04bfeb14c5617ef06b91746bdc3a5e940660
*/
(function($) {
"use strict";
var isSVGSupported = false;
var checkSVGSupport = function () {
/* https://css-tricks.com/a-complete-guide-to-svg-fallbacks/ */
var div = document.createElement("div");
div.innerHTML = "<svg/>";
return (div.firstChild && div.firstChild.namespaceURI) == "http://www.w3.org/2000/svg";
};
var setSVGSupport = function() {
if ((typeof Modernizr === "object" && typeof Modernizr.inlinesvg === "boolean" && Modernizr.inlinesvg) || checkSVGSupport()) {
isSVGSupported = true;
}
return this;
};
var splitFilenameToTrackAndAuthor = function(filename) {
var delimeter = ' - ';
if (filename.indexOf(' \u2014 ') > -1) { delimeter = ' \u2014 '; }
return filename.toString().split(delimeter);
}
var getTrackTitle = function(filename) {
var parts = splitFilenameToTrackAndAuthor(filename);
return parts.length > 1 ? parts[1] : parts[0]
}
var getTrackAuthor = function(filename) {
var parts = splitFilenameToTrackAndAuthor(filename);
return parts.length > 1 ? ('by ' + parts[0]) : ""
}
var formatTime = function(rawSeconds) {
if (typeof rawSeconds !== "number") {
return rawSeconds;
}
var seconds = Math.round(rawSeconds) % 60,
minutes = ((Math.round(rawSeconds) - seconds) % 3600) / 60,
hours = (Math.round(rawSeconds) - seconds - (minutes * 60)) / 3600;
return (hours ? (hours + ":") : "") + ((hours && (minutes < 10)) ? "0" + minutes : minutes) + ":" + (seconds < 10 ? "0" + seconds : seconds);
};
var makeSeconds = function(time) {
if (typeof time === "number") {
return time;
}
var array = time.split(":").reverse(),
seconds = 0;
for (var i = 0; i < array.length; i++) {
seconds += array[i] * Math.pow(60, i);
}
return seconds;
};
var showPreloader = function(instance, timeout) {
return instance; // we haven't the preloader
};
var hidePreloader = function(instance) {
return instance; // we haven't the preloader
};
var updateLoadBar = function(instance, status) {
if ((instance.fullyLoaded && instance.totalTime) || !instance.isPlayed) {
return instance;
}
var roundedSeekPercent = Math.round(status.seekPercent); // because Safari sometimes shows 99.999999999
if (roundedSeekPercent >= 100) {
instance.fullyLoaded = true;
instance.totalTime = status.duration;
} else if (roundedSeekPercent > 0) {
instance.totalTime = status.duration;
} else {
instance.totalTime = 0;
}
instance.$container.find(".player56s-timeline-load").css({"width": Math.floor(Math.min(100, roundedSeekPercent)) + "%"});
return instance;
};
var updatePlayBar = function(instance, percents) {
if (instance.seekTime && !instance.isSeeking) {
return instance;
}
// instance.$container.find(".player56s-play-lift").css("left", percents + "%");
instance.$container.find(".player56s-timeline-done").css("width", percents + "%");
return instance;
};
var updateTimeDisplay = function(instance, seconds) {
if (instance.$container.find(".player56s-time").length < 1) {
return instance;
}
if (instance.totalTime <= 0 && !instance.options.length) {
return instance;
}
var totalSeconds = instance.totalTime || makeSeconds(instance.options.length);
// we haven't total time info
if ((instance.isPlaying || instance.waitForLoad) && (totalSeconds || instance.seekTime)) {
instance.$container.find(".player56s-time").html(formatTime(totalSeconds - (instance.seekTime ? instance.seekTime : seconds)));
}
return instance;
};
var willSeekTo = function(instance, seekPercent) {
var percent = seekPercent.toFixed(2);
if (percent < 0) {
percent = 0;
} else if (percent > 100) {
percent = 100;
}
updatePlayBar(instance, percent);
showPreloader(instance);
instance.waitForLoad = true;
instance.$jPlayer.jPlayer("playHead", percent);
instance.pseudoPlay();
if (instance.fullTimeDisplayed) {
instance.seekTime = (instance.totalTime / 100) * percent;
updateTimeDisplay(instance, instance.seekTime);
} else if (instance.options.length) {
instance.seekTime = (makeSeconds(instance.options.length) / 100) * percent;
updateTimeDisplay(instance, instance.seekTime);
} else {
instance.seekTime = 0.01;
}
return instance;
};
var checkAndRunTicker = function(instance) {
var titleCont = instance.$container.find(".player56s-title");
if (!titleCont.length) { return instance; }
var innerSpan = titleCont.children("span");
if (innerSpan.length && ((innerSpan.height() - 10) > titleCont.height())) {
// we need to activate ticker for title
innerSpan.css({ 'display': 'block', 'position' : 'relative' }).width(titleCont.width());
while ((innerSpan.height() - 10) > titleCont.height()) {
innerSpan.width( innerSpan.width() + 20 );
}
var treset = function() {
if (!this) { return false; }
var $this = $(this);
var parCont = $this.parent();
if (!parCont.length) { return false; }
var diff = $this.width() - parCont.width();
$this.animate({ "left": $this.css("left") }, 1000, function() {
$this.css("left", "0");
$this.animate({ "left": "0" }, 2000, function() {
$this.animate({
"left": "-" + diff + "px"
}, (diff * 40), 'linear', treset);
});
});
};
treset.call(innerSpan[0]);
}
return instance;
};
$.fn.player56s = function(options) {
var relGroups = [];
return this.each(function() {
var $this = $(this),
thisClass = $this.attr("class"),
thisRel = $this.attr("rel"),
thisIsMinimal = (thisClass.indexOf("minimal") > -1),
canHaveGroup = (!thisIsMinimal && thisRel),
skinClassPosition = thisClass.indexOf("player56s-skin-"),
player56sInstance = $this.data("player56s"),
skin = "",
goAndCreate = true;
if (canHaveGroup) {
for (var i = 0; i < relGroups.length; i++) {
if (relGroups[i].group == thisRel) {
var audiofileLink = $this.attr("href");
var filename = $this.html();
relGroups[i].pl56s.addTrack(audiofileLink, filename, $.extend({}, $this.data()));
$this.detach();
goAndCreate = false;
break;
}
}
}
if (goAndCreate) {
if (player56sInstance) {
/* Update current instance (soon) */
} else {
/* Create new instance */
if (skinClassPosition > 0) {
skin = thisClass.substr(skinClassPosition + 12, thisClass.indexOf(" ", skinClassPosition) > 0 ? thisClass.indexOf(" ", skinClassPosition) : thisClass.length);
}
var pl56si = new Player56s($this, $.extend({}, $.fn.player56s.defaults, options, $this.data(), {skin: skin}));
if (canHaveGroup) {
relGroups.push({ group: thisRel, pl56s: pl56si });
}
}
}
});
};
$.fn.player56s.defaults = {
swfPath: "./vendor/",
swfFilename: "jquery.jplayer.swf",
supplied: "mp3",
volume: 0.6,
length: 0,
scrollOnSpace: false,
pauseOnSpace: true,
hideTimelineOnPause: false,
skin: ""
};
function Player56s($link, options) {
this.version = "0.5.1";
this.$link = $link;
this.options = options;
this.minimal = $link.hasClass('minimal');
this.isPlaying = false;
this.isPlayed = false;
this.totalTime = 0;
this.fullyLoaded = false;
this.fullTimeDisplayed = false;
this.waitForLoad = false;
this.seekTime = 0;
this.preloaderTimeout = null;
this.isSeeking = false;
this.tracks = [];
this.currentTrack = 0;
this.init();
}
Player56s.prototype.init = function init() {
this.tracks.push({
audiofileLink: this.$link.attr("href"),
filename: this.$link.html(),
length: this.options.length
});
this.checkOptions();
this.createDOM();
this.initPlayerPlugin();
this.bindEvents();
this.insertDOM();
};
Player56s.prototype.addTrack = function addTrack(audiofileLink, filename, trackOptions) {
this.tracks.push({
audiofileLink: audiofileLink,
filename: filename,
length: (trackOptions ? trackOptions.length : null)
});
this.$container.find(".player56s-track-next").addClass('enabled');
};
Player56s.prototype.destroy = function destroy() {
var uniqueID = this.$container.attr("id");
this.$container.after(this.$link).remove();
$(document).off("." + uniqueID);
this.$link.removeData("player56s");
return this.$link;
};
Player56s.prototype.pause = function pause() {
hidePreloader(this);
if (!this.seekTime) {
this.waitForLoad = false;
}
if (typeof this.$jPlayer !== "undefined" && this.$jPlayer.jPlayer) {
if (this.isPlaying || this.waitForLoad) {
this.isPlaying = false;
this.waitForLoad = false;
this.$jPlayer.jPlayer("pause");
}
}
if (!this.isPlaying) {
this.pseudoPause();
}
return this;
};
Player56s.prototype.pseudoPause = function pseudoPause() {
this.$container.removeClass("player56s-status-playing");
};
Player56s.prototype.stop = function stop() {
if (typeof this.$jPlayer !== "undefined" && this.$jPlayer.jPlayer) {
if (this.isPlaying) {
this.$jPlayer.jPlayer("stop");
}
}
return this;
};
Player56s.prototype.play = function play() {
showPreloader(this, this.seekTime ? false : 500); // 500 is enough to play the loaded fragment, if it's loaded; if isn't — preloader will appear after 500ms
this.isPlayed = true;
if (typeof this.$jPlayer !== "undefined" && this.$jPlayer.jPlayer) {
if (!this.isPlaying) {
this.$jPlayer.jPlayer("play");
}
}
return this;
};
Player56s.prototype.pseudoPlay = function pseudoPlay() {
$(document).trigger("player56s-pause", this);
this.isPlayed = true;
this.$container.addClass("player56s-status-playing");
};
Player56s.prototype.setVolume = function setVolume(lvl, maxLvl) {
var changed = false;
if (typeof this.$jPlayer !== "undefined" && this.$jPlayer.jPlayer) {
lvl = lvl || 0;
maxLvl = maxLvl || 1;
if (lvl > maxLvl) { lvl = maxLvl; }
this.$jPlayer.jPlayer("volume", lvl / maxLvl);
changed = true;
}
return changed;
};
Player56s.prototype.switchTrack = function switchTrack(to_next) {
if (to_next === undefined) { to_next = true; } // next by default
if (typeof this.$jPlayer !== "undefined" && this.$jPlayer.jPlayer) {
if (to_next && (this.currentTrack > this.tracks.length - 2)) {
return false;
}
if (!to_next && this.currentTrack < 1) {
return false;
}
this.pseudoPause();
this.pause();
this.stop();
this.$jPlayer.jPlayer("clearMedia");
this.currentTrack = this.currentTrack + (to_next ? 1 : -1);
var track = this.tracks[this.currentTrack];
this.$jPlayer.jPlayer("setMedia", {
mp3: track.audiofileLink
});
this.$container.find(".player56s-title").html('<span>' + getTrackTitle(track.filename) + '</span>');
this.$container.find(".player56s-author").html('<span>' + getTrackAuthor(track.filename) + '</span>');
this.waitForLoad = true;
this.pseudoPlay();
this.play();
this.$container.find(".player56s-track-prev").toggleClass('enabled', this.currentTrack > 0);
this.$container.find(".player56s-track-next").toggleClass('enabled', this.currentTrack < (this.tracks.length - 1));
checkAndRunTicker(this);
}
}
Player56s.prototype.onPause = function onPause() {
this.isPlaying = false;
this.isSeeking = false;
this.waitForLoad = false;
this.$container.removeClass("player56s-status-playing");
};
Player56s.prototype.onStop = function onStop() {
console.log("stop");
this.isPlaying = false;
this.seekTime = 0;
this.isSeeking = false;
this.waitForLoad = false;
this.$container.removeClass("player56s-status-playing");
};
Player56s.prototype.onPlay = function onPlay() {
$(document).trigger("player56s-pause", this);
this.$container.addClass("player56s-status-playing");
this.waitForLoad = false;
this.isPlaying = true;
this.isPlayed = true;
};
Player56s.prototype.checkOptions = function checkOptions() {
if (!parseInt(this.options.length)) {
this.options.length = 0;
}
};
Player56s.prototype.createDOM = function createDOM() {
var $container = $(document.createElement("div")),
$invisibleObject = $(document.createElement("div")),
$infoArea = $(document.createElement("div")),
filename = this.tracks[0].filename,
self = this;
var createMinimanContentDOM = function() {
return [
$(document.createElement("div")).addClass("player56s-timeline").append(
$(document.createElement("div")).addClass("player56s-timeline-load"),
$(document.createElement("div")).addClass("player56s-timeline-done")
),
$(document.createElement("div")).addClass("player56s-button"),
$(document.createElement("div")).addClass("player56s-volume").append(
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("active").addClass("zero-vol"),
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("active"),
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("active"),
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("active"),
$(document.createElement("div")).addClass("player56s-vol-pin"),
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("max-vol")
),
$(document.createElement("div")).addClass("player56s-time").html(self.options.length ? formatTime(makeSeconds(self.options.length)) : "")
];
};
var createNormalContentDOM = function() {
return [
$(document.createElement("div")).addClass("player56s-title").html('<span>' + getTrackTitle(filename) + '</span>'),
$(document.createElement("div")).addClass("player56s-author").html('<span>' + getTrackAuthor(filename) + '</span>'),
$(document.createElement("div")).addClass("player56s-timeline").append(
$(document.createElement("div")).addClass("player56s-timeline-load"),
$(document.createElement("div")).addClass("player56s-timeline-done")
),
$(document.createElement("div")).addClass("player56s-button"),
$(document.createElement("div")).addClass("player56s-volume").append(
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("max-vol"),
$(document.createElement("div")).addClass("player56s-vol-pin"),
$(document.createElement("div")).addClass("player56s-vol-pin"),
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("active"),
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("active"),
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("active"),
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("active"),
$(document.createElement("div")).addClass("player56s-vol-pin").addClass("active").addClass("zero-vol")
),
$(document.createElement("div")).addClass("player56s-tracks").append(
$(document.createElement("div")).addClass("player56s-track-nav").addClass("player56s-track-prev"),
$(document.createElement("div")).addClass("player56s-track-nav").addClass("player56s-track-next")
)
];
};
var createContentAreaDOM = function() {
return (self.minimal ? createMinimanContentDOM() : createNormalContentDOM());
}
this.$container = $container
.data("player56s", this)
.addClass("player56s")
.addClass(self.minimal ? "minimal" : "normal")
.attr("id", "player56s-ui-zone-" + (1000 + Math.round(Math.random() * 8999)))
.append(
$invisibleObject.addClass("player56s-invisible-object"),
$infoArea.addClass("player56s-content").append(createContentAreaDOM())
);
return this;
};
Player56s.prototype.initPlayerPlugin = function initPlayerPlugin() {
var self = this,
$jPlayer = self.$container.find(".player56s-invisible-object");
this.$jPlayer = $jPlayer.jPlayer({
solution: "html",
wmode: "window",
preload: "metadata",
swfPath: self.options.swfPath + self.options.swfFilename,
supplied: self.options.supplied,
volume: self.options.volume,
ready: function() {
var audiofileLink = self.tracks[0].audiofileLink,
uniqueID = self.$container.attr("id");
$jPlayer.jPlayer("setMedia", {
mp3: audiofileLink
});
self.$container.find(".player56s-button").on("click", function(event) {
event.preventDefault();
event.stopPropagation();
if (self.isPlaying) {
self.pseudoPause.call(self);
self.pause.call(self);
}
else {
self.waitForLoad = true;
self.pseudoPlay.call(self);
self.play.call(self);
}
});
self.$container.find(".player56s-volume .player56s-vol-pin").on("click", function(event) {
event.preventDefault();
event.stopPropagation();
var $pin = $(this);
var $pinsBefore = self.minimal ? $pin.prevAll() : $pin.nextAll();
var lvl = $pinsBefore.length;
var maxLvl = $pin.siblings().length;
if (self.setVolume.call(self, lvl, maxLvl)) {
$pinsBefore.addClass('active');
$pin.addClass('active');
var $pinsAfter = self.minimal ? $pin.nextAll() : $pin.prevAll();
$pinsAfter.removeClass('active');
}
});
self.$container.find(".player56s-track-nav").on("click", function(event) {
event.preventDefault();
event.stopPropagation();
self.switchTrack.call(self, $(this).hasClass('player56s-track-next'));
});
self.$container.find(".player56s-timeline").on("mousedown." + uniqueID, function(event) {
if (event.which !== 1) {
return false;
}
event.stopPropagation();
event.preventDefault();
self.isSeeking = true;
var $this = $(this),
clickPoint = ((event.pageX - $this.offset().left) / $this.width()) * 100;
$(document).off("mouseup." + uniqueID).one("mouseup." + uniqueID, function() {
self.isSeeking = false;
showPreloader(self);
});
$(document).off("mousemove." + uniqueID).on("mousemove." + uniqueID, function(event) {
event.stopPropagation();
event.preventDefault();
if (!self.isSeeking) {
return false;
}
var clickPoint = ((event.pageX - $this.offset().left) / $this.width()) * 100;
willSeekTo(self, clickPoint);
});
willSeekTo(self, clickPoint);
});
},
pause: function() {
self.onPause.call(self);
},
stop: function() {
self.onStop.call(self);
},
ended: function() {
self.switchTrack.call(self);
},
play: function() {
self.onPlay.call(self);
},
progress: function(event) {
updateLoadBar(self, event.jPlayer.status);
updateTimeDisplay(self, event.jPlayer.status.currentTime);
},
timeupdate: function(event) {
updateLoadBar(self, event.jPlayer.status);
updateTimeDisplay(self, event.jPlayer.status.currentTime);
updatePlayBar(self, event.jPlayer.status.currentPercentAbsolute.toFixed(2));
hidePreloader(self);
if (self.waitForLoad) {
self.waitForLoad = false;
self.seekTime = 0;
self.play();
hidePreloader(self);
}
}
});
// Remove volume changer and add special class if have
// no ability to change a volume
if (this.$jPlayer.data('jPlayer').status.noVolume) {
self.$container.addClass("volumeless");
self.$container.find(".player56s-volume").remove();
this.setVolume(1, 1); // set maximum volume
}
return this;
};
Player56s.prototype.insertDOM = function insertDOM() {
this.$link.after(this.$container);
this.$link.data("player56s", this);
this.$link.detach();
checkAndRunTicker(this);
return this;
};
Player56s.prototype.bindEvents = function bindEvents() {
var self = this,
uniqueID = self.$container.attr("id");
$(document).on("player56s-pause." + uniqueID, function(event, triggeredPlayer56s) {
if (self !== triggeredPlayer56s) {
self.pause();
}
});
$(document).on("keydown." + uniqueID, function(event) {
if (event.keyCode === 32) {
if (self.isPlaying && self.options.pauseOnSpace) {
self.$jPlayer.jPlayer("pause");
}
}
});
return this;
};
/* It's time to know if SVG supported */
setSVGSupport();
/* Autoload Player56s */
var autoLoadPlayer56s = function() {
$(".player56s").player56s();
};
$(autoLoadPlayer56s);
}(jQuery));