Upgrade web assets (#219)

* Update assets

* Upgrade linting and other improvments

* Correct linting

* Correction and type check improvements

* Correct type check lib

* Fix lint pathing for VSCode

* Remove duplicate babel config

* Remove editorconfig root attribute from web subdir

* Use double quotes around message

* Simplify ESLint config

* Update web assets

* Allow AMD loader in WebPack

* Bump web dependencies

* Only include FA icons in-use
This commit is contained in:
Tyler Vigario 2020-11-25 06:08:12 -08:00 committed by GitHub
parent c79d4cf977
commit ff5b1cb1ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 8517 additions and 5744 deletions

View File

@ -28,7 +28,7 @@ certificate =
[bot] [bot]
username = botamusique username = botamusique
comment = Hi, I'm here to play radio, local music or youtube/soundcloud music. Have fun! comment = "Hi, I'm here to play radio, local music or youtube/soundcloud music. Have fun!"
# default volume from 0 to 1. # default volume from 0 to 1.
volume = 0.1 volume = 0.1
stereo = True stereo = True
@ -126,10 +126,10 @@ jazz = http://jazz-wr04.ice.infomaniak.ch/jazz-wr04-128.mp3 "Jazz Yeah !"
# ======================================================== # =========================================================
# WARNING: WE DO NOT SUGGEST YOU MODIFY THE FOLLOWING # WARNING: WE DO NOT SUGGEST YOU MODIFY THE FOLLOWING
# PARTS, EXCEPT YOU KNOW WHAT YOU ARE DOING. # PARTS, EXCEPT IF YOU KNOW WHAT YOU ARE DOING.
# ======================================================== # =========================================================
[commands] [commands]
# This is a list of characters the bot recognizes as command prefix. # This is a list of characters the bot recognizes as command prefix.
command_symbol = !: command_symbol = !:

View File

@ -22,7 +22,7 @@ port = 64738
# 'username' is the user name of the bot. # 'username' is the user name of the bot.
# 'comment' is the comment displayed by the bot. # 'comment' is the comment displayed by the bot.
#username = botamusique #username = botamusique
#comment = Hi, I'm here to play radio, local music or youtube/soundcloud music. Have fun! #comment = "Hi, I'm here to play radio, local music or youtube/soundcloud music. Have fun!"
# 'language': Available languages can be found inside lang/ folder. # 'language': Available languages can be found inside lang/ folder.
#language=en_US #language=en_US

View File

@ -1,5 +1,3 @@
root = true
[*] [*]
charset = utf-8 charset = utf-8
insert_final_newline = true insert_final_newline = true

View File

@ -1,38 +1,40 @@
{ {
"parser": "@babel/eslint-parser",
"env": { "env": {
"browser": true, "browser": true,
"es6": true, "es6": true,
"es2017": true, "es2017": true,
"es2020": true, "es2020": true,
"es2021": true,
"jquery": true "jquery": true
}, },
"plugins": [ "plugins": [
"node", "@babel",
"import", "import",
"jsdoc" "jsdoc",
"jquery"
], ],
"extends": [ "extends": [
"eslint:recommended", "eslint:recommended",
"google",
"plugin:node/recommended-module",
"plugin:import/errors", "plugin:import/errors",
"plugin:import/warnings", "plugin:import/warnings",
"plugin:jsdoc/recommended" "plugin:jsdoc/recommended",
"plugin:jquery/deprecated"
], ],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parser": "babel-eslint",
"rules": { "rules": {
"require-jsdoc": "off",
"valid-jsdoc": "off",
"jsdoc/require-jsdoc": "off",
"max-len": ["warn", { "max-len": ["warn", {
"code": 120 "code": 120
}], }],
"linebreak-style": "off",
"jsdoc/require-jsdoc": "off",
"import/unambiguous": "error",
"import/no-commonjs": "error", "import/no-commonjs": "error",
"import/no-amd": "error", "import/no-amd": "error",
"linebreak-style": "off" "import/no-nodejs-modules": "error",
"import/no-deprecated": "error",
"import/extensions": ["error", "always"],
"import/no-unresolved": ["error", {
"commonjs": true
}]
} }
} }

1
web/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
package-lock.json text eol=lf

3
web/.gitignore vendored
View File

@ -1 +1,2 @@
node_modules/ !*
node_modules/

5
web/babel.config.json Normal file
View File

@ -0,0 +1,5 @@
{
"plugins": [
"@babel/plugin-proposal-class-properties"
]
}

View File

@ -1,13 +1,23 @@
import {library, dom} from '@fortawesome/fontawesome-svg-core/index.es'; import {library, dom} from '@fortawesome/fontawesome-svg-core/index.es.js';
import {fas} from '@fortawesome/free-solid-svg-icons/index.es'; import {
import {far} from '@fortawesome/free-regular-svg-icons/index.es'; faTimesCircle, faPlus, faCheck, faUpload, faTimes, faTrash, faPlay, faPause, faFastForward, faPlayCircle, faLightbulb,
library.add(fas, far); faTrashAlt, faDownload, faSyncAlt, faEdit, faVolumeUp, faVolumeDown, faRobot, faRedo, faRandom, faTasks
} from '@fortawesome/free-solid-svg-icons/index.es.js';
import {faFileAlt} from '@fortawesome/free-regular-svg-icons/index.es.js';
library.add(
// Solid
faTimesCircle, faPlus, faCheck, faUpload, faTimes, faTrash, faPlay, faPause, faFastForward, faPlayCircle, faLightbulb,
faTrashAlt, faDownload, faSyncAlt, faEdit, faVolumeUp, faVolumeDown, faRobot, faRedo, faRandom, faTasks,
// Regular
faFileAlt
);
// Old application code // Old application code
import './main.mjs'; import './main.mjs';
// New application code // New application code
import Theme from './theme.mjs'; import Theme from './lib/theme.mjs';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
Theme.init(); Theme.init();

42
web/js/lib/text.mjs Normal file
View File

@ -0,0 +1,42 @@
import {validateString, validateNumber} from './type.mjs';
/**
* Truncate string length by characters.
*
* @param {string} text String to format.
* @param {number} limit Maximum number of characters in resulting string.
* @param {string} ending Ending to use if string is trucated.
*
* @returns {string} Formatted string.
*/
export function limitChars(text, limit = 50, ending = '...') {
validateString(text);
validateNumber(limit);
validateString(ending);
// Check if string is already below limit
if (text.length <= limit) {
return text;
}
// Limit string length by characters
return text.substring(0, limit - ending.length) + ending;
}
/**
* Truncate string length by words.
*
* @param {string} text String to format.
* @param {number} limit Maximum number of words in resulting string.
* @param {string} ending Ending to use if string is trucated.
*
* @returns {string} Formatted string.
*/
export function limitWords(text, limit = 10, ending = '...') {
validateString(text);
validateNumber(limit);
validateString(ending);
// Limit string length by words
return text.split(' ').splice(0, limit).join(' ') + ending;
}

View File

@ -1,6 +1,6 @@
export default class { export default class {
/** /**
* @property {boolean} #dark Interal state for dark theme activation. * @property {boolean} dark Interal state for dark theme activation.
* @private * @private
*/ */
static #dark = false; static #dark = false;

65
web/js/lib/type.mjs Normal file
View File

@ -0,0 +1,65 @@
/**
* Checks if `value` is the type `Object` excluding `Function` and `null`
*
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, otherwise `false`.
*/
export function isObject(value) {
return (Object.prototype.toString.call(value) === '[object Object]');
}
/**
* Checks if `value` is the type `string`
*
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a string, otherwise `false`.
*/
export function isString(value) {
return (typeof value === 'string');
}
/**
* Checks if `value` is the type `number`
*
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is a number, otherwise `false`.
*/
export function isNumber(value) {
return (typeof value === 'number');
}
/**
* Validate parameter is of type object.
*
* @param {string} value Variable to validate.
* @throws Error if not an object.
*/
export function validateObject(value) {
if (!isObject(value)) {
throw new TypeError('Parameter "value" must be of type object.');
}
}
/**
* Validate parameter is of type string.
*
* @param {string} value Variable to validate.
* @throws Error if not an string.
*/
export function validateString(value) {
if (!isString(value)) {
throw new TypeError('Parameter "value" must be of type string.');
}
}
/**
* Validate parameter is of type number.
*
* @param {number} value Variable to validate.
* @throws Error if not an number.
*/
export function validateNumber(value) {
if (!isNumber(value)) {
throw new TypeError('Parameter "value" must be of type number.');
}
}

View File

@ -3,13 +3,13 @@ export function isOverflown(element) {
} }
export function hash(string) { export function hash(string) {
if (typeof string != "string") return 0; if (typeof string != 'string') return 0;
let hash = 0; let hash = 0;
if (string.length === 0) { if (string.length === 0) {
return hash; return hash;
} }
for (let i = 0; i < string.length; i++) { for (let i = 0; i < string.length; i++) {
let char = string.charCodeAt(i); const char = string.charCodeAt(i);
hash = ((hash<<5)-hash)+char; hash = ((hash<<5)-hash)+char;
hash = hash & hash; // Convert to 32bit integer hash = hash & hash; // Convert to 32bit integer
} }
@ -17,24 +17,25 @@ export function hash(string) {
} }
export function getColor(string) { export function getColor(string) {
let num = hash(string) % 8; const num = hash(string) % 8;
switch(num) {
switch (num) {
case 0: case 0:
return "primary"; return 'primary';
case 1: case 1:
return "secondary"; return 'secondary';
case 2: case 2:
return "success"; return 'success';
case 3: case 3:
return "danger"; return 'danger';
case 4: case 4:
return "warning"; return 'warning';
case 5: case 5:
return "info"; return 'info';
case 6: case 6:
return "light"; return 'light';
case 7: case 7:
return "dark"; return 'dark';
} }
} }
@ -52,20 +53,3 @@ export function secondsToStr(seconds) {
const secs = seconds % 60; const secs = seconds % 60;
return ('00' + mins).slice(-2) + ':' + ('00' + secs).slice(-2); return ('00' + mins).slice(-2) + ':' + ('00' + secs).slice(-2);
} }
export function coverArtString(title) {
let nameOfSong = "";
// The maximum length before we start truncating
const maxLength = 50;
if (title.length > maxLength) {
// Name = longTitleTooLongToBeAGoodAltTex...
nameOfSong = title.substr(0, maxLength) + "\u2026";
} else {
// Name = shortTitle
nameOfSong = title;
}
return 'Cover art for ' + nameOfSong;
}

View File

@ -1,20 +1,20 @@
import 'jquery/src/jquery'; import 'jquery/src/jquery.js';
import 'jquery-migrate/src/migrate'; import 'jquery-migrate/src/migrate.js';
import Popper from 'popper.js/dist/esm/popper'; import Popper from 'popper.js/dist/esm/popper.js';
import { import {
Modal, Modal,
Toast, Toast,
Tooltip, Tooltip,
} from 'bootstrap/js/src/index'; } from 'bootstrap/js/src/index.js';
import { import {
getColor, getColor,
isOverflown, isOverflown,
setProgressBar, setProgressBar,
secondsToStr, secondsToStr,
coverArtString, } from './lib/util.mjs';
} from './util'; import {limitChars} from './lib/text.mjs';
$('#uploadSelectFile').on('change', function () { $('#uploadSelectFile').on('change', function() {
// get the file name // get the file name
const fileName = $(this).val().replace('C:\\fakepath\\', ' '); const fileName = $(this).val().replace('C:\\fakepath\\', ' ');
// replace the "Choose a file" label // replace the "Choose a file" label
@ -85,7 +85,7 @@ fastForwardBtn.on('click', () => {
}); });
document.getElementById('clear-playlist-btn').addEventListener('click', () => { document.getElementById('clear-playlist-btn').addEventListener('click', () => {
request('post', { action: 'clear' }); request('post', {action: 'clear'});
}); });
// eslint-disable-next-line guard-for-in // eslint-disable-next-line guard-for-in
@ -102,14 +102,14 @@ function request(_url, _data, refresh = false) {
url: _url, url: _url,
data: _data, data: _data,
statusCode: { statusCode: {
200: function (data) { 200: function(data) {
if (data.ver !== playlist_ver) { if (data.ver !== playlist_ver) {
checkForPlaylistUpdate(); checkForPlaylistUpdate();
} }
updateControls(data.empty, data.play, data.mode, data.volume); updateControls(data.empty, data.play, data.mode, data.volume);
updatePlayerPlayhead(data.playhead); updatePlayerPlayhead(data.playhead);
}, },
403: function () { 403: function() {
location.reload(true); location.reload(true);
}, },
}, },
@ -125,7 +125,7 @@ function addPlaylistItem(item) {
pl_title_element.html(item.title); pl_title_element.html(item.title);
pl_artist_element.html(item.artist); pl_artist_element.html(item.artist);
pl_thumb_element.attr('src', item.thumbnail); pl_thumb_element.attr('src', item.thumbnail);
pl_thumb_element.attr('alt', coverArtString(item.title)); pl_thumb_element.attr('alt', limitChars(item.title));
pl_type_element.html(item.type); pl_type_element.html(item.type);
pl_path_element.html(item.path); pl_path_element.html(item.path);
@ -137,13 +137,13 @@ function addPlaylistItem(item) {
tags.empty(); tags.empty();
const tag_edit_copy = pl_tag_edit_element.clone(); const tag_edit_copy = pl_tag_edit_element.clone();
tag_edit_copy.click(function () { tag_edit_copy.click(function() {
addTagModalShow(item.id, item.title, item.tags); addTagModalShow(item.id, item.title, item.tags);
}); });
tag_edit_copy.appendTo(tags); tag_edit_copy.appendTo(tags);
if (item.tags.length > 0) { if (item.tags.length > 0) {
item.tags.forEach(function (tag_tuple) { item.tags.forEach(function(tag_tuple) {
const tag_copy = tag_element.clone(); const tag_copy = tag_element.clone();
tag_copy.html(tag_tuple[0]); tag_copy.html(tag_tuple[0]);
tag_copy.addClass('badge-' + tag_tuple[1]); tag_copy.addClass('badge-' + tag_tuple[1]);
@ -160,14 +160,14 @@ function addPlaylistItem(item) {
function displayPlaylist(data) { function displayPlaylist(data) {
playlist_table.animate({ playlist_table.animate({
opacity: 0, opacity: 0,
}, 200, function () { }, 200, function() {
playlist_loading.hide(); playlist_loading.hide();
$('.playlist-item').remove(); $('.playlist-item').remove();
const items = data.items; const items = data.items;
const length = data.length; const length = data.length;
if (items.length === 0){ if (items.length === 0) {
playlist_empty.removeClass('d-none'); playlist_empty.removeClass('d-none');
playlist_table.animate({ opacity: 1 }, 200); playlist_table.animate({opacity: 1}, 200);
return; return;
} }
playlist_items = {}; playlist_items = {};
@ -188,9 +188,9 @@ function displayPlaylist(data) {
} }
items.forEach( items.forEach(
function (item) { function(item) {
addPlaylistItem(item); addPlaylistItem(item);
}, },
); );
if (items.length < length && start_from + items.length < length) { if (items.length < length && start_from + items.length < length) {
@ -205,7 +205,7 @@ function displayPlaylist(data) {
displayActiveItem(data.current_index); displayActiveItem(data.current_index);
updatePlayerInfo(playlist_items[data.current_index]); updatePlayerInfo(playlist_items[data.current_index]);
bindPlaylistEvent(); bindPlaylistEvent();
playlist_table.animate({ opacity: 1 }, 200); playlist_table.animate({opacity: 1}, 200);
}); });
} }
@ -227,7 +227,7 @@ function insertExpandPrompt(real_from, real_to, display_from, display_to, total_
expand_copy.addClass('playlist-item'); expand_copy.addClass('playlist-item');
expand_copy.appendTo(playlist_table); expand_copy.appendTo(playlist_table);
expand_copy.click(function () { expand_copy.click(function() {
playlist_range_from = real_from; playlist_range_from = real_from;
playlist_range_to = real_to; playlist_range_to = real_to;
updatePlaylist(); updatePlaylist();
@ -237,7 +237,7 @@ function insertExpandPrompt(real_from, real_to, display_from, display_to, total_
function updatePlaylist() { function updatePlaylist() {
playlist_table.animate({ playlist_table.animate({
opacity: 0, opacity: 0,
}, 200, function () { }, 200, function() {
playlist_empty.addClass('d-none'); playlist_empty.addClass('d-none');
playlist_loading.show(); playlist_loading.show();
playlist_table.find('.playlist-item').css('opacity', 0); playlist_table.find('.playlist-item').css('opacity', 0);
@ -267,7 +267,7 @@ function checkForPlaylistUpdate() {
type: 'POST', type: 'POST',
url: 'post', url: 'post',
statusCode: { statusCode: {
200: function (data) { 200: function(data) {
if (data.ver !== playlist_ver) { if (data.ver !== playlist_ver) {
playlist_ver = data.ver; playlist_ver = data.ver;
playlist_range_from = 0; playlist_range_from = 0;
@ -298,18 +298,18 @@ function checkForPlaylistUpdate() {
function bindPlaylistEvent() { function bindPlaylistEvent() {
$('.playlist-item-play').unbind().click( $('.playlist-item-play').unbind().click(
function (e) { function(e) {
request('post', { request('post', {
'play_music': ($(e.currentTarget).parent().parent().parent().find('.playlist-item-index').html() - 1), 'play_music': ($(e.currentTarget).parent().parent().parent().find('.playlist-item-index').html() - 1),
}); });
}, },
); );
$('.playlist-item-trash').unbind().click( $('.playlist-item-trash').unbind().click(
function (e) { function(e) {
request('post', { request('post', {
'delete_music': ($(e.currentTarget).parent().parent().parent().find('.playlist-item-index').html() - 1), 'delete_music': ($(e.currentTarget).parent().parent().parent().find('.playlist-item-index').html() - 1),
}); });
}, },
); );
} }
@ -416,10 +416,10 @@ function setFilterType(event, type) {
} }
filter_dir.change(function () { filter_dir.change(function() {
updateResults(); updateResults();
}); });
filter_keywords.change(function () { filter_keywords.change(function() {
updateResults(); updateResults();
}); });
@ -427,69 +427,69 @@ const item_template = $('#library-item');
function bindLibraryResultEvent() { function bindLibraryResultEvent() {
$('.library-thumb-col').unbind().hover( $('.library-thumb-col').unbind().hover(
function (e) { function(e) {
$(e.currentTarget).find('.library-thumb-grp').addClass('library-thumb-grp-hover'); $(e.currentTarget).find('.library-thumb-grp').addClass('library-thumb-grp-hover');
}, },
function (e) { function(e) {
$(e.currentTarget).find('.library-thumb-grp').removeClass('library-thumb-grp-hover'); $(e.currentTarget).find('.library-thumb-grp').removeClass('library-thumb-grp-hover');
}, },
); );
$('.library-info-title').unbind().hover( $('.library-info-title').unbind().hover(
function (e) { function(e) {
$(e.currentTarget).parent().find('.library-thumb-grp').addClass('library-thumb-grp-hover'); $(e.currentTarget).parent().find('.library-thumb-grp').addClass('library-thumb-grp-hover');
}, },
function (e) { function(e) {
$(e.currentTarget).parent().find('.library-thumb-grp').removeClass('library-thumb-grp-hover'); $(e.currentTarget).parent().find('.library-thumb-grp').removeClass('library-thumb-grp-hover');
}, },
); );
$('.library-item-play').unbind().click( $('.library-item-play').unbind().click(
function (e) { function(e) {
request('post', { request('post', {
'add_item_at_once': $(e.currentTarget).parent().parent().parent().find('.library-item-id').val(), 'add_item_at_once': $(e.currentTarget).parent().parent().parent().find('.library-item-id').val(),
}); });
}, },
); );
$('.library-item-trash').unbind().click( $('.library-item-trash').unbind().click(
function (e) { function(e) {
request('post', { request('post', {
'delete_item_from_library': $(e.currentTarget).parent().parent().find('.library-item-id').val(), 'delete_item_from_library': $(e.currentTarget).parent().parent().find('.library-item-id').val(),
}); });
updateResults(active_page); updateResults(active_page);
}, },
); );
$('.library-item-download').unbind().click( $('.library-item-download').unbind().click(
function (e) { function(e) {
const id = $(e.currentTarget).parent().parent().find('.library-item-id').val(); const id = $(e.currentTarget).parent().parent().find('.library-item-id').val();
// window.open('/download?id=' + id); // window.open('/download?id=' + id);
downloadId(id); downloadId(id);
}, },
); );
$('.library-item-add-next').unbind().click( $('.library-item-add-next').unbind().click(
function (e) { function(e) {
const id = $(e.currentTarget).parent().parent().find('.library-item-id').val(); const id = $(e.currentTarget).parent().parent().find('.library-item-id').val();
request('post', { request('post', {
'add_item_next': id, 'add_item_next': id,
}); });
}, },
); );
$('.library-item-add-bottom').unbind().click( $('.library-item-add-bottom').unbind().click(
function (e) { function(e) {
const id = $(e.currentTarget).parent().parent().find('.library-item-id').val(); const id = $(e.currentTarget).parent().parent().find('.library-item-id').val();
request('post', { request('post', {
'add_item_bottom': id, 'add_item_bottom': id,
}); });
}, },
); );
} }
const lib_filter_tag_group = $("#filter-tags"); const lib_filter_tag_group = $('#filter-tags');
const lib_filter_tag_element = $(".filter-tag"); const lib_filter_tag_element = $('.filter-tag');
const lib_group = $('#library-group'); const lib_group = $('#library-group');
const id_element = $('.library-item-id'); const id_element = $('.library-item-id');
@ -503,62 +503,62 @@ const tag_edit_element = $('.library-item-edit');
// var notag_element = $(".library-item-notag"); // var notag_element = $(".library-item-notag");
// var tag_element = $(".library-item-tag"); // var tag_element = $(".library-item-tag");
let library_tags = []; const library_tags = [];
function updateLibraryControls(){ function updateLibraryControls() {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
url: 'library/info', url: 'library/info',
statusCode: { statusCode: {
200: displayLibraryControls, 200: displayLibraryControls,
403: function () { 403: function() {
location.reload(true); location.reload(true);
}, },
}, },
}); });
} }
function displayLibraryControls(data){ function displayLibraryControls(data) {
$("#maxUploadFileSize").val(data.max_upload_file_size); $('#maxUploadFileSize').val(data.max_upload_file_size);
if (data.upload_enabled) { if (data.upload_enabled) {
$("#uploadDisabled").val("false"); $('#uploadDisabled').val('false');
$("#upload").show(); $('#upload').show();
} else { } else {
$("#uploadDisabled").val("true"); $('#uploadDisabled').val('true');
$("#upload").hide(); $('#upload').hide();
} }
if (data.delete_allowed) { if (data.delete_allowed) {
$("#deleteAllowed").val("true"); $('#deleteAllowed').val('true');
$(".library-delete").show(); $('.library-delete').show();
} else { } else {
$("#uploadDisabled").val("false"); $('#uploadDisabled').val('false');
$(".library-delete").hide(); $('.library-delete').hide();
} }
let select = $("#filter-dir"); const select = $('#filter-dir');
let dataList = $("#upload-target-dirs"); const dataList = $('#upload-target-dirs');
select.find("option").remove(); select.find('option').remove();
dataList.find("option").remove(); dataList.find('option').remove();
if (data.dirs.length > 0) { if (data.dirs.length > 0) {
console.log(data.dirs); console.log(data.dirs);
data.dirs.forEach(function (dir) { data.dirs.forEach(function(dir) {
$("<option value=\"" + dir + "\">" + dir + "</option>").appendTo(select); $('<option value="' + dir + '">' + dir + '</option>').appendTo(select);
$("<option value=\"" + dir + "\">").appendTo(dataList); $('<option value="' + dir + '">').appendTo(dataList);
}); });
} }
// ----- Tag filters ----- // ----- Tag filters -----
let tags = []; const tags = [];
let tags_dict = {}; const tags_dict = {};
$(".filter-tag").each(function(i, tag_element){ $('.filter-tag').each(function(i, tag_element) {
tags_dict[tag_element.innerHTML] = tag_element; tags_dict[tag_element.innerHTML] = tag_element;
tags.push(tag_element.innerHTML); tags.push(tag_element.innerHTML);
}); });
if (data.tags.length > 0) { if (data.tags.length > 0) {
for (const tag of data.tags){ for (const tag of data.tags) {
if (tags.includes(tag)){ if (tags.includes(tag)) {
let index = tags.indexOf(tag); const index = tags.indexOf(tag);
tags.splice(index, 1); tags.splice(index, 1);
} else { } else {
const tag_copy = lib_filter_tag_element.clone(); const tag_copy = lib_filter_tag_element.clone();
@ -566,7 +566,7 @@ function displayLibraryControls(data){
tag_copy.addClass('badge-' + getColor(tag)); tag_copy.addClass('badge-' + getColor(tag));
tag_copy.appendTo(lib_filter_tag_group); tag_copy.appendTo(lib_filter_tag_group);
// Bind Event // Bind Event
tag_copy.click(function (e) { tag_copy.click(function(e) {
const tag = $(e.currentTarget); const tag = $(e.currentTarget);
if (!tag.hasClass('tag-clicked')) { if (!tag.hasClass('tag-clicked')) {
tag.addClass('tag-clicked'); tag.addClass('tag-clicked');
@ -583,7 +583,7 @@ function displayLibraryControls(data){
tags_dict[tag].remove(); tags_dict[tag].remove();
} }
} else { } else {
$(".filter-tag").remove(); $('.filter-tag').remove();
} }
} }
@ -592,7 +592,7 @@ function addResultItem(item) {
title_element.html(item.title); title_element.html(item.title);
artist_element.html(item.artist ? ('- ' + item.artist) : ''); artist_element.html(item.artist ? ('- ' + item.artist) : '');
thumb_element.attr('src', item.thumb); thumb_element.attr('src', item.thumb);
thumb_element.attr('alt', coverArtString(item.title)); thumb_element.attr('alt', limitChars(item.title));
type_element.html('[' + item.type + ']'); type_element.html('[' + item.type + ']');
path_element.html(item.path); path_element.html(item.path);
@ -603,13 +603,13 @@ function addResultItem(item) {
tags.empty(); tags.empty();
const tag_edit_copy = tag_edit_element.clone(); const tag_edit_copy = tag_edit_element.clone();
tag_edit_copy.click(function () { tag_edit_copy.click(function() {
addTagModalShow(item.id, item.title, item.tags); addTagModalShow(item.id, item.title, item.tags);
}); });
tag_edit_copy.appendTo(tags); tag_edit_copy.appendTo(tags);
if (item.tags.length > 0) { if (item.tags.length > 0) {
item.tags.forEach(function (tag_tuple) { item.tags.forEach(function(tag_tuple) {
const tag_copy = tag_element.clone(); const tag_copy = tag_element.clone();
tag_copy.html(tag_tuple[0]); tag_copy.html(tag_tuple[0]);
tag_copy.addClass('badge-' + tag_tuple[1]); tag_copy.addClass('badge-' + tag_tuple[1]);
@ -627,7 +627,7 @@ function addResultItem(item) {
function getFilters(dest_page = 1) { function getFilters(dest_page = 1) {
const tags = $('.tag-clicked'); const tags = $('.tag-clicked');
const tags_list = []; const tags_list = [];
tags.each(function (index, tag) { tags.each(function(index, tag) {
tags_list.push(tag.innerHTML); tags_list.push(tag.innerHTML);
}); });
@ -658,14 +658,14 @@ function updateResults(dest_page = 1) {
lib_group.animate({ lib_group.animate({
opacity: 0, opacity: 0,
}, 200, function () { }, 200, function() {
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: 'library', url: 'library',
data: data, data: data,
statusCode: { statusCode: {
200: processResults, 200: processResults,
403: function () { 403: function() {
location.reload(true); location.reload(true);
}, },
}, },
@ -731,7 +731,7 @@ document.getElementById('library-download-btn').addEventListener('click', () =>
}); });
document.getElementById('library-rescan-btn').addEventListener('click', () => { document.getElementById('library-rescan-btn').addEventListener('click', () => {
request('post', { action: 'rescan' }); request('post', {action: 'rescan'});
updateResults(); updateResults();
}); });
@ -751,7 +751,7 @@ const page_no = $('.library-page-no');
function processResults(data) { function processResults(data) {
lib_group.animate({ lib_group.animate({
opacity: 0, opacity: 0,
}, 200, function () { }, 200, function() {
lib_loading.hide(); lib_loading.hide();
const total_pages = data.total_pages; const total_pages = data.total_pages;
const active_page = data.active_page; const active_page = data.active_page;
@ -760,14 +760,14 @@ function processResults(data) {
lib_loading.hide(); lib_loading.hide();
lib_empty.show(); lib_empty.show();
page_ul.empty(); page_ul.empty();
lib_group.animate({ opacity: 1 }, 200); lib_group.animate({opacity: 1}, 200);
return; return;
} }
items.forEach( items.forEach(
function (item) { function(item) {
addResultItem(item); addResultItem(item);
bindLibraryResultEvent(); bindLibraryResultEvent();
}, },
); );
page_ul.empty(); page_ul.empty();
@ -785,7 +785,7 @@ function processResults(data) {
page_no_copy = page_no.clone(); page_no_copy = page_no.clone();
page_no_copy.html('&laquo;'); page_no_copy.html('&laquo;');
page_no_copy.click(function (e) { page_no_copy.click(function(e) {
updateResults(1); updateResults(1);
}); });
@ -801,7 +801,7 @@ function processResults(data) {
if (active_page === i) { if (active_page === i) {
page_li_copy.addClass('active'); page_li_copy.addClass('active');
} else { } else {
page_no_copy.click(function (e) { page_no_copy.click(function(e) {
const _page_no = $(e.currentTarget).html(); const _page_no = $(e.currentTarget).html();
updateResults(_page_no); updateResults(_page_no);
}); });
@ -815,14 +815,14 @@ function processResults(data) {
page_no_copy = page_no.clone(); page_no_copy = page_no.clone();
page_no_copy.html('&raquo;'); page_no_copy.html('&raquo;');
page_no_copy.click(function (e) { page_no_copy.click(function(e) {
updateResults(total_pages); updateResults(total_pages);
}); });
page_no_copy.appendTo(page_li_copy); page_no_copy.appendTo(page_li_copy);
page_li_copy.appendTo(page_ul); page_li_copy.appendTo(page_ul);
} }
lib_group.animate({ opacity: 1 }, 200); lib_group.animate({opacity: 1}, 200);
}); });
} }
@ -841,11 +841,11 @@ function addTagModalShow(_id, _title, _tag_tuples) {
add_tag_modal_title.html(_title); add_tag_modal_title.html(_title);
add_tag_modal_item_id.val(_id); add_tag_modal_item_id.val(_id);
add_tag_modal_tags.empty(); add_tag_modal_tags.empty();
_tag_tuples.forEach(function (tag_tuple) { _tag_tuples.forEach(function(tag_tuple) {
modal_tag_text.html(tag_tuple[0]); modal_tag_text.html(tag_tuple[0]);
const tag_copy = modal_tag.clone(); const tag_copy = modal_tag.clone();
const modal_tag_remove = tag_copy.find('.modal-tag-remove'); const modal_tag_remove = tag_copy.find('.modal-tag-remove');
modal_tag_remove.click(function (e) { modal_tag_remove.click(function(e) {
$(e.currentTarget).parent().remove(); $(e.currentTarget).parent().remove();
}); });
tag_copy.show(); tag_copy.show();
@ -856,14 +856,14 @@ function addTagModalShow(_id, _title, _tag_tuples) {
} }
document.getElementById('addTagModalAddBtn').addEventListener('click', () => { document.getElementById('addTagModalAddBtn').addEventListener('click', () => {
const new_tags = add_tag_modal_input.val().split(',').map(function (str) { const new_tags = add_tag_modal_input.val().split(',').map(function(str) {
return str.trim(); return str.trim();
}); });
new_tags.forEach(function (tag) { new_tags.forEach(function(tag) {
modal_tag_text.html(tag); modal_tag_text.html(tag);
const tag_copy = modal_tag.clone(); const tag_copy = modal_tag.clone();
const modal_tag_remove = tag_copy.find('.modal-tag-remove'); const modal_tag_remove = tag_copy.find('.modal-tag-remove');
modal_tag_remove.click(function (e) { modal_tag_remove.click(function(e) {
$(e.currentTarget).parent().remove(); $(e.currentTarget).parent().remove();
}); });
tag_copy.show(); tag_copy.show();
@ -876,7 +876,7 @@ document.getElementById('addTagModalAddBtn').addEventListener('click', () => {
document.getElementById('addTagModalSubmit').addEventListener('click', () => { document.getElementById('addTagModalSubmit').addEventListener('click', () => {
const all_tags = $('.modal-tag-text'); const all_tags = $('.modal-tag-text');
const tags = []; const tags = [];
all_tags.each(function (i, element) { all_tags.each(function(i, element) {
if (element.innerHTML) { if (element.innerHTML) {
tags.push(element.innerHTML); tags.push(element.innerHTML);
} }
@ -890,7 +890,9 @@ document.getElementById('addTagModalSubmit').addEventListener('click', () => {
id: add_tag_modal_item_id.val(), id: add_tag_modal_item_id.val(),
tags: tags.join(','), tags: tags.join(','),
}, },
complete: function () { updateResults(active_page); }, complete: function() {
updateResults(active_page);
},
}); });
}); });
@ -904,7 +906,7 @@ let volume_popover_instance = null;
let volume_popover_show = false; let volume_popover_show = false;
let volume_update_timer; let volume_update_timer;
volumePopoverBtn.addEventListener('click', function (e) { volumePopoverBtn.addEventListener('click', function(e) {
e.stopPropagation(); e.stopPropagation();
if (!volume_popover_show) { if (!volume_popover_show) {
@ -926,7 +928,7 @@ volumePopoverBtn.addEventListener('click', function (e) {
} }
volume_popover_show = !volume_popover_show; volume_popover_show = !volume_popover_show;
document.addEventListener('click', function () { document.addEventListener('click', function() {
volumePopoverDiv.removeAttribute('data-show'); volumePopoverDiv.removeAttribute('data-show');
if (volume_popover_instance) { if (volume_popover_instance) {
volume_popover_instance.destroy(); volume_popover_instance.destroy();
@ -938,7 +940,7 @@ volumePopoverBtn.addEventListener('click', function (e) {
}); });
}); });
volumePopoverBtn.addEventListener('click', function (e) { volumePopoverBtn.addEventListener('click', function(e) {
e.stopPropagation(); e.stopPropagation();
}); });
@ -954,11 +956,11 @@ volumeSlider.addEventListener('change', (e) => {
}); });
document.getElementById('volume-down-btn').addEventListener('click', () => { document.getElementById('volume-down-btn').addEventListener('click', () => {
request('post', { action: 'volume_down' }); request('post', {action: 'volume_down'});
}); });
document.getElementById('volume-up-btn').addEventListener('click', () => { document.getElementById('volume-up-btn').addEventListener('click', () => {
request('post', { action: 'volume_up' }); request('post', {action: 'volume_up'});
}); });
// --------------------- // ---------------------
@ -1054,7 +1056,7 @@ function uploadNextFile() {
const file = filesToProceed.shift(); const file = filesToProceed.shift();
const file_progress_item = filesProgressItem[file.name]; const file_progress_item = filesProgressItem[file.name];
req.addEventListener('load', function () { req.addEventListener('load', function() {
if (this.status === 200) { if (this.status === 200) {
setProgressBar(file_progress_item.progress, 1); setProgressBar(file_progress_item.progress, 1);
file_progress_item.progress.classList.add('bg-success'); file_progress_item.progress.classList.add('bg-success');
@ -1087,7 +1089,7 @@ function uploadNextFile() {
} }
}); });
req.upload.addEventListener('progress', function (e) { req.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) { if (e.lengthComputable) {
const percent = e.loaded / e.total; const percent = e.loaded / e.total;
setProgressBar(file_progress_item.progress, percent, Math.floor(percent * 100) + '%'); setProgressBar(file_progress_item.progress, percent, Math.floor(percent * 100) + '%');
@ -1134,12 +1136,12 @@ const musicUrlInput = document.getElementById('music-url-input');
const radioUrlInput = document.getElementById('radio-url-input'); const radioUrlInput = document.getElementById('radio-url-input');
document.getElementById('add-music-url').querySelector('button').addEventListener('click', () => { document.getElementById('add-music-url').querySelector('button').addEventListener('click', () => {
request('post', { add_url: musicUrlInput.value }); request('post', {add_url: musicUrlInput.value});
musicUrlInput.value = ''; musicUrlInput.value = '';
}); });
document.getElementById('add-radio-url').querySelector('button').addEventListener('click', () => { document.getElementById('add-radio-url').querySelector('button').addEventListener('click', () => {
request('post', { add_radio: radioUrlInput.value }); request('post', {add_radio: radioUrlInput.value});
radioUrlInput.value = ''; radioUrlInput.value = '';
}); });
@ -1161,15 +1163,15 @@ const playerSkipBtn = document.getElementById('playerSkipBtn');
let currentPlayingItem = null; let currentPlayingItem = null;
playerPlayBtn.addEventListener('click', () => { playerPlayBtn.addEventListener('click', () => {
request('post', { action: 'resume' }); request('post', {action: 'resume'});
}); });
playerPauseBtn.addEventListener('click', () => { playerPauseBtn.addEventListener('click', () => {
request('post', { action: 'pause' }); request('post', {action: 'pause'});
}); });
playerSkipBtn.addEventListener('click', () => { playerSkipBtn.addEventListener('click', () => {
request('post', { action: 'next' }); request('post', {action: 'next'});
}); });
document.getElementById('player-toast').addEventListener('click', () => { document.getElementById('player-toast').addEventListener('click', () => {
@ -1196,7 +1198,7 @@ function updatePlayerInfo(item) {
playerTitle.textContent = item.title; playerTitle.textContent = item.title;
playerArtist.textContent = item.artist; playerArtist.textContent = item.artist;
playerArtwork.setAttribute('src', item.thumbnail); playerArtwork.setAttribute('src', item.thumbnail);
playerArtwork.setAttribute('alt', coverArtString(item.title)); playerArtwork.setAttribute('alt', limitChars(item.title));
if (isOverflown(playerTitle)) { if (isOverflown(playerTitle)) {
playerTitle.classList.add('scrolling'); playerTitle.classList.add('scrolling');
@ -1245,7 +1247,7 @@ function updatePlayerPlayhead(playhead) {
player_playhead_position = playhead; player_playhead_position = playhead;
setProgressBar(playerBar, player_playhead_position / currentPlayingItem.duration, secondsToStr(player_playhead_position)); setProgressBar(playerBar, player_playhead_position / currentPlayingItem.duration, secondsToStr(player_playhead_position));
if (playing) { if (playing) {
playhead_timer = setInterval(function () { playhead_timer = setInterval(function() {
player_playhead_position += 0.3; player_playhead_position += 0.3;
setProgressBar(playerBar, player_playhead_position / currentPlayingItem.duration, secondsToStr(player_playhead_position)); setProgressBar(playerBar, player_playhead_position / currentPlayingItem.duration, secondsToStr(player_playhead_position));
}, 300); // delay in milliseconds }, 300); // delay in milliseconds
@ -1260,7 +1262,7 @@ function updatePlayerPlayhead(playhead) {
} }
} }
playerBarBox.addEventListener('mousedown', function () { playerBarBox.addEventListener('mousedown', function() {
if (currentPlayingItem && currentPlayingItem.duration > 0) { if (currentPlayingItem && currentPlayingItem.duration > 0) {
playerBarBox.addEventListener('mousemove', playheadDragged); playerBarBox.addEventListener('mousemove', playheadDragged);
clearInterval(playhead_timer); clearInterval(playhead_timer);
@ -1268,7 +1270,7 @@ playerBarBox.addEventListener('mousedown', function () {
} }
}); });
playerBarBox.addEventListener('mouseup', function (event) { playerBarBox.addEventListener('mouseup', function(event) {
playerBarBox.removeEventListener('mousemove', playheadDragged); playerBarBox.removeEventListener('mousemove', playheadDragged);
const percent = (event.clientX - playerBarBox.getBoundingClientRect().x) / playerBarBox.clientWidth; const percent = (event.clientX - playerBarBox.getBoundingClientRect().x) / playerBarBox.clientWidth;
request('post', { request('post', {

13655
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@
"type": "module", "type": "module",
"scripts": { "scripts": {
"lint": "eslint --config .eslintrc.json js/ --ext .mjs", "lint": "eslint --config .eslintrc.json js/ --ext .mjs",
"lint:fix": "npm run lint -- --fix",
"build": "webpack --config webpack.config.cjs --progress", "build": "webpack --config webpack.config.cjs --progress",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
@ -19,38 +18,36 @@
}, },
"homepage": "https://github.com/azlux/botamusique#readme", "homepage": "https://github.com/azlux/botamusique#readme",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.11.1", "@babel/core": "^7.12.9",
"@babel/plugin-proposal-class-properties": "^7.10.4", "@babel/eslint-parser": "^7.12.1",
"@babel/preset-env": "^7.11.0", "@babel/eslint-plugin": "^7.12.1",
"autoprefixer": "^9.8.6", "@babel/plugin-proposal-class-properties": "^7.12.1",
"babel-eslint": "^10.1.0", "@babel/preset-env": "^7.12.7",
"babel-loader": "^8.1.0", "autoprefixer": "^10.0.2",
"core-js": "^3.6.5", "babel-loader": "^8.2.1",
"css-loader": "^4.2.1", "core-js": "^3.7.0",
"eslint": "^7.7.0", "css-loader": "^5.0.1",
"eslint-config-google": "^0.14.0", "eslint": "^7.14.0",
"eslint-plugin-import": "^2.22.0", "eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsdoc": "^30.2.2", "eslint-plugin-jquery": "^1.5.1",
"eslint-plugin-node": "^11.1.0", "eslint-plugin-jsdoc": "^30.7.8",
"html-webpack-plugin": "^4.3.0", "html-webpack-plugin": "^4.5.0",
"mini-css-extract-plugin": "^0.10.0", "mini-css-extract-plugin": "^1.3.1",
"postcss-loader": "^3.0.0", "postcss-loader": "^4.1.0",
"regenerator-runtime": "^0.13.7", "regenerator-runtime": "^0.13.7",
"sass": "^1.26.10", "sass": "^1.29.0",
"sass-loader": "^9.0.3", "sass-loader": "^10.1.0",
"webpack": "^4.44.1", "webpack": "^5.6.0",
"webpack-cli": "^3.3.12" "webpack-cli": "^4.2.0"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.30", "@fortawesome/fontawesome-svg-core": "^1.2.32",
"@fortawesome/free-brands-svg-icons": "^5.14.0", "@fortawesome/free-regular-svg-icons": "^5.15.1",
"@fortawesome/free-regular-svg-icons": "^5.14.0", "@fortawesome/free-solid-svg-icons": "^5.15.1",
"@fortawesome/free-solid-svg-icons": "^5.14.0", "bootstrap": "^4.5.3",
"bootstrap": "^4.5.2", "bootswatch": "^4.5.3",
"bootswatch": "^4.5.2",
"jquery": "^3.5.1", "jquery": "^3.5.1",
"jquery-migrate": "^3.3.1", "jquery-migrate": "^3.3.2",
"popper.js": "^1.16.1" "popper.js": "^1.16.1"
} }
} }

7
web/vscode.eslintrc.json Normal file
View File

@ -0,0 +1,7 @@
{
"parserOptions": {
"babelOptions": {
"configFile": "./web/babel.config.json"
}
}
}

View File

@ -4,6 +4,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = { module.exports = {
mode: 'production', mode: 'production',
devtool: 'source-map',
entry: { entry: {
main: [ main: [
'./js/app.mjs', './js/app.mjs',
@ -13,16 +14,9 @@ module.exports = {
'./sass/app-dark.scss', './sass/app-dark.scss',
], ],
}, },
devtool: 'source-map',
/*optimization: {
splitChunks: {
chunks: 'all',
},
},*/
output: { output: {
filename: 'static/js/[name].js', filename: 'static/js/[name].js',
path: path.resolve(__dirname, '../'), path: path.resolve(__dirname, '../'),
// ecmaVersion: 5,
}, },
plugins: [ plugins: [
new MiniCssExtractPlugin({ new MiniCssExtractPlugin({
@ -48,10 +42,15 @@ module.exports = {
{ {
loader: 'postcss-loader', loader: 'postcss-loader',
options: { options: {
plugins: function() { postcssOptions: {
return [ plugins: [
require('autoprefixer'), [
]; 'autoprefixer',
{
// Options
},
],
],
}, },
}, },
}, },
@ -61,6 +60,9 @@ module.exports = {
{ {
test: /\.m?js$/, test: /\.m?js$/,
exclude: /(node_modules|bower_components)/, exclude: /(node_modules|bower_components)/,
resolve: {
fullySpecified: false,
},
use: { use: {
loader: 'babel-loader', loader: 'babel-loader',
options: { options: {
@ -73,15 +75,9 @@ module.exports = {
}, },
], ],
], ],
plugins: [
'@babel/plugin-proposal-class-properties',
],
}, },
}, },
}, },
], ],
}, },
/* experiments: {
mjs: true,
},*/
}; };