/*!
* WMPlayer v1.0.0
* Copyright 2016-2024 Marcin Walczak
*This file is part of WMPlayer which is released under MIT license.
*See LICENSE for full license details.
*/
//jquery plugin
if (window.jQuery) $.fn.WMPlayer = function(options) {
var args = $.makeArray(arguments), after = args.slice(1);
return this.each(function() {
var instance = $.data(this, "wmplayer");
if (instance) {
if (instance[options]) {
instance[options].apply(instance, after);
if (options == "parent") {
if (options.length > 0) {
var parent = after[0];
if (parent.length > 0) parent = parent[0];
$.removeData(this, "wmplayer");
if ($.data(parent, "wmplayer") !== undefined) $.data(parent, "wmplayer").destroy();
$.data(parent, "wmplayer", instance);
}
}
} else {
$.error("Method " + options + " does not exist on WMPlayer");
}
} else if (typeof options !== "string") {
var config = options;
if (config === undefined) config = {
parent: this
}; else if (config.parent === undefined) config.parent = this;
var wmp = new WMPlayer(config);
$.data(this, "wmplayer", wmp);
return wmp;
}
});
};
*/
this.view.setCover = (function(){
return function(filenameOrEmpty) {
var imgEl = this.root.querySelector('.wmp-cover-img');
if (!imgEl) return;
var opts = this.rootPlayer.options || {};
var fallback = opts.coverFallback || '';
var exts = opts.coverExtensions && opts.coverExtensions.length ? opts.coverExtensions : ['png','jpg','jpeg'];
var basePath = (opts.coverPath || '').replace(/\/?$/, '/');
if (!filenameOrEmpty) {
imgEl.src = fallback;
return;
}
// normalize base filename (strip path & extension)
var name = filenameOrEmpty.split('/').pop().split('?')[0].replace(/\.[^/.]+$/, '');
var idx = 0;
var self = this;
function tryNext() {
if (idx >= exts.length) {
imgEl.src = fallback;
return;
}
var ext = exts[idx++];
var url = basePath + name + '.' + ext;
var tester = new Image();
tester.onload = function() { imgEl.src = url; };
tester.onerror = tryNext;
tester.src = url;
}
tryNext();
};
})().bind(this.view);
function WMPlayer($config) {
var self = this;
var mouseDown = -1;
this.currentScript = document.currentScript || function() {
var scripts = document.getElementsByTagName("script");
return scripts[scripts.length - 1];
}();
this.parentNode = (this.currentScript[this.currentScript.length - 1] || this.currentScript).parentNode;
//WMPlayer container
this.container = document.createElement("div");
if (this.container.classList !== undefined) {
this.container.classList.add("wmplayer");
this.container.classList.add("default");
} else {
this.container.className += " wmplayer default";
}
var playerBody = document.createElement("div");
if (playerBody.classList !== undefined) playerBody.classList.add("wmplayer-body"); else playerBody.className += " wmplayer-body";
this.container.appendChild(playerBody);
//media container
var mediaContainer = document.createElement("div");
mediaContainer.className += "wmplayer-media";
this.container.appendChild(mediaContainer);
var yt = document.createElement("div");
yt.className += "wmplayer-yt";
mediaContainer.appendChild(yt);
mediaContainer.setAttribute("style", "width: 0; height: 0; overflow: hidden; opacity: 0;");
//load You Tube iframe api
/*
if (document.querySelectorAll('script[src="https://www.youtube.com/iframe_api"]').length == 0) {
var yt = document.createElement("script");
yt.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(yt, firstScriptTag);
}
*/
var $playlistDoubleClickSelect = false;
if ($config !== undefined) {
if ($config.playlistDoubleClickSelect !== undefined) $playlistDoubleClickSelect = $config.playlistDoubleClickSelect;
}
/* 1) Defaults: add cover configuration to options (insert near existing defaults) */
this.options = Object.assign({
// ...existing options...
coverPath: 'music/covers/', // folder where cover files are stored (ensure trailing slash or it will be normalized)
coverFallback: '', // optional fallback image path ('' to show blank)
coverExtensions: ['png','jpg','jpeg']
}, this.options || {});
//default config
this.started = false;
this.model = new this._Model();
this.view = new this._View({
playlist: "wmp-playlist",
playButton: "wmp-play",
stopButton: "wmp-stop",
fastForwardButton: "wmp-fast-forward",
rewindButton: "wmp-rewind",
progressBar: "wmp-progress-bar",
volumeBar: "wmp-volume-bar",
muteButton: "wmp-mute",
currentTrackTime: "wmp-current-track-time",
currentTrackDuration: "wmp-current-track-duration",
currentTrackTitle: "wmp-current-track-title",
container: this.container,
playlistDoubleClickSelect: $playlistDoubleClickSelect
});
this.view.rootPlayer = this;
this.view.setTemplate('
');
this.view.setPlaylistPattern('$index.$title$duration
');
//config arguments
if ($config !== undefined) {
//if config is string, node or jquery object
if (typeof $config == 'string' || $config instanceof String || $config.nodeType !== undefined && $config.nodeType === 1 || window.jQuery && $config instanceof jQuery)
this.parent($config);
else {
if ($config.parent !== undefined) this.parent($config.parent, $config.parentAsTemplate);
if ($config.template !== undefined) this.template($config.template);
if ($config.theme !== undefined) this.theme($config.theme);
if ($config.playlistPattern !== undefined) this.playlistPattern($config.playlistPattern);
if ($config.controls !== undefined) this.controls($config.controls);
if ($config.autoplay !== undefined) this.autoplay($config.autoplay);
if ($config.loop !== undefined) this.loop($config.loop);
if ($config.volume !== undefined) this.volume($config.volume);
if ($config.mute !== undefined) this.mute($config.mute);
if ($config.YTApiKey !== undefined) this.model.setYTApiKey($config.YTApiKey);
if ($config.YTAutoload !== undefined && $config.YTAutoload) {
//add YT Iframe API
if (document.querySelectorAll('script[src="https://www.youtube.com/iframe_api"]').length == 0) {
var yt = document.createElement("script");
yt.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName("script")[0];
firstScriptTag.parentNode.insertBefore(yt, firstScriptTag);
}
}
if ($config.playlist !== undefined) {
$config.playlist.forEach(function($audioTrack) {
self.addTrack($audioTrack.url, $audioTrack.title, $audioTrack.duration);
});
}
if ($config.showPlaylist !== undefined) this.showPlaylist($config.showPlaylist);
if (!($config.start === false)) this.start();
}
}
//attaching event listeners
//audio track added
this.model.audioTrackAdded.attach(function() {
if (self.started) {
var playlist = self.model.getPlaylist();
var currentTrackIndex = self.model.getCurrentTrackIndex();
var currentTrackTitle = self.model.getCurrentTrackTitle();
var currentTrackDuration = self.model.getCurrentTrackDuration();
self.view.renderPlaylist(playlist, currentTrackIndex);
self.view.setCurrentTrackData(currentTrackTitle, currentTrackDuration);
}
});
//audio track removed
this.model.audioTrackRemoved.attach(function() {
if (self.started) {
var playlist = self.model.getPlaylist();
var currentTrackIndex = self.model.getCurrentTrackIndex();
self.view.renderPlaylist(playlist, currentTrackIndex);
}
});
//current track change
this.model.currentTrackChanged.attach(function() {
if (self.started) {
var playlist = self.model.getPlaylist();
var currentTrackIndex = self.model.getCurrentTrackIndex();
var currentTrackTitle = self.model.getCurrentTrackTitle();
var currentTrackDuration = self.model.getCurrentTrackDuration();
self.view.renderPlaylist(playlist, currentTrackIndex);
self.view.setCurrentTrackData(currentTrackTitle, currentTrackDuration);
}
});
//volume change
this.model.volumeChanged.attach(function() {
if (self.started) {
self.view.setVolumeBar(self.model.getVolume());
if (self.model.getMute()) self.view.addContainerClass("muted"); else self.view.removeContainerClass("muted");
}
});
//play
this.model.audioTrackPlaying.attach(function() {
self.view.removeContainerClass("paused");
self.view.addContainerClass("playing");
});
//pause
this.model.audioTrackPaused.attach(function() {
self.view.removeContainerClass("playing");
self.view.addContainerClass("paused");
});
//stop
this.model.audioTrackStopped.attach(function() {
self.view.removeContainerClass("playing");
self.view.removeContainerClass("paused");
});
//audio track end
this.model.audioTrackEnded.attach(function() {
var playlistEnded = self.model.nextTrack();
if (playlistEnded && !self.model.getLoop()) self.model.stop(); else self.model.play();
});
//current track time/duration change
this.model.durationChanged.attach(function() {
var currentTime = self.model.getCurrentTime();
var duration = self.model.getCurrentTrackDuration();
self.view.setProgressBar(currentTime, duration);
self.view.setCurrentTime(currentTime);
});
//audio track error
this.model.audioTrackError.attach(function() {
self.model.setCurrentTrackError();
self.nextTrack();
});
//play button click
this.view.playButtonClicked.attach(function() {
if (self.model.playing) self.model.pause(); else self.model.play();
});
//stop button click
this.view.stopButtonClicked.attach(function() {
self.model.stop();
});
//fast-forward button click/press
this.view.fastForwardButtonPressed.attach(function() {
setTimeout(function() {
if (self.view.mousedownFlag) {
self.mouseDown = setInterval(function() {
if (self.view.mousedownFlag) self.model.fastForward(); else {
clearInterval(self.mouseDown);
self.mouseDown = -1;
}
}, 500);
} else self.nextTrack();
}, 200);
});
//rewind button click/press
this.view.rewindButtonPressed.attach(function() {
setTimeout(function() {
if (self.view.mousedownFlag) {
self.mouseDown = setInterval(function() {
if (self.view.mousedownFlag) self.model.rewind(); else {
clearInterval(self.mouseDown);
self.mouseDown = -1;
}
}, 500);
} else self.previousTrack();
}, 200);
});
//progress bar click/press
this.view.progressBarClicked.attach(function($sender, $args) {
var duration = self.model.getCurrentTrackDuration();
var time = Math.floor($args.offsetX / $args.width * duration);
self.model.setTime(time);
});
//volume bar click/press
this.view.volumeBarClicked.attach(function($sender, $args) {
var volume = $args.offsetX / $args.width;
self.model.setVolume(volume);
});
//mute button click
this.view.muteButtonClicked.attach(function($sender, $args) {
self.model.setMute();
});
//playlist item double click/touch
this.view.playlistElementDoubleClicked.attach(function($sender, $args) {
self.model.setAudioTrack($args.trackId);
self.model.play();
});
//template change
this.view.templateModified.attach(function() {
if (self.started) {
var currentTrackTitle = self.model.getCurrentTrackTitle();
var currentTrackDuration = self.model.getCurrentTrackDuration();
var playlist = self.model.getPlaylist();
var currentTrackIndex = self.model.getCurrentTrackIndex();
self.view.renderPlayer();
self.view.setCurrentTrackData(currentTrackTitle, currentTrackDuration);
self.view.renderPlaylist(playlist, currentTrackIndex);
}
});
//playlist pattern change
this.view.playlistPatternModified.attach(function() {
if (self.started) {
var playlist = self.model.getPlaylist();
var currentTrackIndex = self.model.getCurrentTrackIndex();
self.view.renderPlaylist(playlist, currentTrackIndex);
}
});
return this;
}
WMPlayer.prototype = {
//add new audio track
addTrack: function($url, $title, $duration) {
if ($title === undefined) $title = "N/A";
if ($duration === undefined) $duration = "N/A";
this.model.addAudioTrack($url, $title, $duration);
return this;
},
//remove audio track
removeTrack: function($index) {
this.model.delAudioTrack($index);
return this;
},
//set current track
track: function($index) {
if ($index === undefined) $index = 0;
this.model.setAudioTrack($index);
if (this.started) this.model.play();
return this;
},
//next track
nextTrack: function() {
var playlistEnded = this.model.nextTrack();
if (playlistEnded && !this.model.getLoop()) this.model.stop(); else if (this.model.playing) this.model.play();
return this;
},
//previous track
previousTrack: function() {
this.model.previousTrack();
if (this.model.playing) this.model.play();
return this;
},
//set volume
volume: function($volume) {
this.model.setVolume($volume);
return this;
},
//set/toggle mute
mute: function($mute) {
this.model.setMute($mute);
return this;
},
//set template
template: function($template) {
if ($template === undefined) $template = false;
if ($template.length > 0) $template = $template[0];
this.view.setTemplate($template);
return this;
},
//set playlist pattern
playlistPattern: function($pattern) {
if ($pattern !== undefined) this.view.setPlaylistPattern($pattern);
return this;
},
//set player parent node
parent: function($parent, $setAsTemplate) {
//if parent argument is not provided, set script parent node as parent
if ($parent === undefined) {
$parent = (document.currentScript || function() {
var scripts = document.getElementsByTagName("script");
return scripts[scripts.length - 1];
}()).parentNode;
}
if (typeof $parent === 'string' || $parent instanceof String)
$parent = document.querySelector($parent);
if($parent === null) {
console.log('DOM element not found.');
return this;
}
if ($parent.length > 0)
$parent = $parent[0];
this.parentNode = $parent;
if ($setAsTemplate == true) {
this.view.setTemplate($parent.innerHTML);
$parent.innerHTML = "";
}
if (this.started)
this.parentNode.appendChild(this.container);
return this;
},
//set/toggle loop
loop: function($loop) {
this.model.setLoop($loop);
return this;
},
//set/toogle autoplay
autoplay: function($autoplay) {
this.model.setAutoplay($autoplay);
return this;
},
//set controls classes
controls: function($elements) {
this.view.setControls($elements);
return this;
},
//set/toggle playlist display
showPlaylist: function($show) {
this.view.setShowPlaylist($show);
return this;
},
//set theme - player's container class
theme: function($class) {
this.view.setPlayerClass($class);
return this;
},
//destroy player
destroy: function() {
if (this.container.parentNode == this.parentNode) this.parentNode.removeChild(this.container);
this.started = false;
return this;
},
//start player
start: function() {
if (!this.started) {
this.started = true;
this.parentNode.appendChild(this.container);
var mediaContainer = this.container.querySelector(".wmplayer-media");
mediaContainer.appendChild(this.model.audio);
//yt iframe
var yt = mediaContainer.querySelector(".wmplayer-yt");
yt.id = "wmplayer-yt-" + document.querySelectorAll(".wmplayer .wmplayer-yt").length;
this.model.YTIframeId = yt.id;
var currentTrackTitle = this.model.getCurrentTrackTitle();
var currentTrackDuration = this.model.getCurrentTrackDuration();
var playlist = this.model.getPlaylist();
var currentTrackIndex = this.model.getCurrentTrackIndex();
var volume = this.model.getVolume();
var mute = this.model.getMute();
var autoplay = this.model.getAutoplay();
this.view.renderPlayer();
this.view.setProgressBar();
this.view.setVolumeBar(volume);
if (mute) this.view.addContainerClass("muted");
this.view.setCurrentTrackData(currentTrackTitle, currentTrackDuration);
*/
(function(){
var src = null;
if (typeof this.model !== 'undefined' && typeof this.model.getCurrentTrackSrc === 'function') {
src = this.model.getCurrentTrackSrc();
} else if (this.currentTrack && this.currentTrack.src) {
src = this.currentTrack.src;
} else if (this._currentTrack && this._currentTrack.src) {
src = this._currentTrack.src;
}
if (src) {
var filename = src.split('/').pop().split('?')[0];
this.view.setCover(filename);
} else {
this.view.setCover('');
}
}).call(this);
/* Notes on integration:
- Ensure the .wmp-cover img element exists in the template before calling setCover.
- The code above assumes the player code path that updates the UI runs in the context of the player instance (this).
- If your project wraps WMPlayer differently, call playerInstance.view.setCover(filename) after selecting/starting a track.
- coverPath is relative to the page; in your site you might set coverPath: 'music/covers/' and place images like:
music/covers/Spaghetti Skeletti - 2026 - The Cookies [Maxi-Single] - 01 - The Cookies (Original Version).png
(i.e., same base as the mp3 file name, but with .png/.jpg/.jpeg).
*/
this.view.renderPlaylist(playlist, currentTrackIndex);
if (autoplay) {
this.model.play();
} else {
this.view.addContainerClass("paused");
}
}
return this;
}
};