From bfe4c8ae95c089baac07ba24c41416343731af78 Mon Sep 17 00:00:00 2001 From: Jeremy Plsek Date: Tue, 10 Jul 2018 19:10:28 -0400 Subject: [PATCH] Update code formatting 4 spaces -> 2 spaces + rc --- .editorconfig | 9 + .jshintrc | 9 + public/css/overwrite.less | 706 ++++----- public/css/themes/default-thin/main.less | 2 +- public/css/themes/default/vars.less | 14 +- server.js | 1516 +++++++++---------- src/browser.js | 1184 +++++++-------- src/contextmenu.js | 560 +++---- src/disconnect.js | 104 +- src/downloader.js | 488 +++---- src/draganddrop.js | 176 +-- src/history.js | 36 +- src/lazytoast.js | 82 +- src/library.js | 330 ++--- src/libraryalbums.js | 194 +-- src/libraryartists.js | 162 +- src/librarysongs.js | 303 ++-- src/main.js | 228 +-- src/pages.js | 296 ++-- src/player.js | 736 +++++----- src/playlist.js | 1707 +++++++++++----------- src/playlisteditor.js | 794 +++++----- src/progressbar.js | 78 +- src/settings.js | 612 ++++---- src/socket.js | 394 ++--- src/stored.js | 828 +++++------ src/tableheader.js | 96 +- src/users.js | 40 +- src/utils.js | 1166 +++++++-------- src/vote.js | 30 +- tests/browser.js | 92 +- tests/library.js | 90 +- tests/main.js | 60 +- tests/playlist.js | 300 ++-- tests/playlisteditor.js | 442 +++--- tests/settings.js | 86 +- tests/stored.js | 60 +- tests/utils.js | 544 +++---- views/404.pug | 6 +- views/index.pug | 984 ++++++------- 40 files changed, 7779 insertions(+), 7765 deletions(-) create mode 100644 .editorconfig create mode 100644 .jshintrc diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..69bf6b6 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +charset = utf-8 +insert_final_newline = true +trim_trailing_whitespace = true +end_of_line = lf +indent_style = space +indent_size = 2 diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..9b86568 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,9 @@ +{ + "camelcase": true, + "freeze": true, + "indent": 2, + "quotmark": "single", + "funcscope": true, + "node": true, + "esversion": 6 +} diff --git a/public/css/overwrite.less b/public/css/overwrite.less index 50ec6eb..38ef640 100644 --- a/public/css/overwrite.less +++ b/public/css/overwrite.less @@ -1,493 +1,493 @@ @minimumWidth: 360px; html { - min-width: @minimumWidth; - overflow-x: hidden; // fixes an issue with context-menus - overflow-y: hidden; // fixes random bugs that occur with changing the table widths dynamically + min-width: @minimumWidth; + overflow-x: hidden; // fixes an issue with context-menus + overflow-y: hidden; // fixes random bugs that occur with changing the table widths dynamically } #page-wrapper { - display: flex; - flex-direction: column; - height: 100vh; + display: flex; + flex-direction: column; + height: 100vh; } header { + display: -webkit-box; /* fallback for apple webkit */ + display: flex; + -webkit-align-items: center; + align-items: stretch; + min-width: @minimumWidth; + + #header-left { display: -webkit-box; /* fallback for apple webkit */ display: flex; -webkit-align-items: center; align-items: stretch; - min-width: @minimumWidth; - - #header-left { - display: -webkit-box; /* fallback for apple webkit */ - display: flex; - -webkit-align-items: center; - align-items: stretch; - margin: 0; - box-shadow: none; - border: none; - - #volume { - width: 30px; // width set for webkit - height: 60px; - -webkit-appearance: slider-vertical; - } - #media-buttons { - display: -webkit-box; /* fallback for apple webkit */ - display: flex; - -webkit-flex-direction: column; - flex-direction: column; - justify-content: space-around; - } + margin: 0; + box-shadow: none; + border: none; + + #volume { + width: 30px; // width set for webkit + height: 60px; + -webkit-appearance: slider-vertical; } - #header-middle { - display: -webkit-box; /* fallback for apple webkit */ - display: flex; - -webkit-align-items: center; - align-items: center; - flex: 2; - - // I want the height of the image to depend on the header, - // but only ff seems to do what I want atm. Might try to css hack my - // way to get it working later... - #album-art { - flex: 0; - height: 60px; - padding: 0 5px; - } + #media-buttons { + display: -webkit-box; /* fallback for apple webkit */ + display: flex; + -webkit-flex-direction: column; + flex-direction: column; + justify-content: space-around; } - #header-right { - display: -webkit-box; /* fallback for apple webkit */ - display: flex; - -webkit-flex-direction: column; - flex-direction: column; - justify-content: space-around; - width: 200px; + } + #header-middle { + display: -webkit-box; /* fallback for apple webkit */ + display: flex; + -webkit-align-items: center; + align-items: center; + flex: 2; - #header-right-top { - display: -webkit-box; /* fallback for apple webkit */ - display: flex; - align-items: center; - } + // I want the height of the image to depend on the header, + // but only ff seems to do what I want atm. Might try to css hack my + // way to get it working later... + #album-art { + flex: 0; + height: 60px; + padding: 0 5px; } + } + #header-right { + display: -webkit-box; /* fallback for apple webkit */ + display: flex; + -webkit-flex-direction: column; + flex-direction: column; + justify-content: space-around; + width: 200px; + + #header-right-top { + display: -webkit-box; /* fallback for apple webkit */ + display: flex; + align-items: center; + } + } } main { - display: flex; - flex: 2; - overflow-y: auto; - overflow-x: hidden; // dragging off screen + display: flex; + flex: 2; + overflow-y: auto; + overflow-x: hidden; // dragging off screen - #browser { - display: flex; - flex-direction: column; + #browser { + display: flex; + flex-direction: column; - -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; + -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; - #browser-header { - display: flex; - } + #browser-header { + display: flex; + } - #slwrap { - flex-grow: 2; - overflow-x: hidden; // in case of size issues with js - position: relative; - } + #slwrap { + flex-grow: 2; + overflow-x: hidden; // in case of size issues with js + position: relative; } - #library { - display: flex; - flex-direction: column; + } + #library { + display: flex; + flex-direction: column; - -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; + -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; - #library-top { - display: flex; - flex: 1; - overflow: auto; + #library-top { + display: flex; + flex: 1; + overflow: auto; - #library-artists-wrap, #library-albums-wrap { - overflow: auto; - } + #library-artists-wrap, #library-albums-wrap { + overflow: auto; + } - #library-artists, #library-albums { - flex: 1; - display: flex; - flex-direction: column; - overflow: auto; - } - } + #library-artists, #library-albums { + flex: 1; + display: flex; + flex-direction: column; + overflow: auto; + } + } - #library-songs { - flex: 1; - overflow-y: auto; - overflow-x: hidden; // fix issues with dragging - display: flex; - flex-direction: column; + #library-songs { + flex: 1; + overflow-y: auto; + overflow-x: hidden; // fix issues with dragging + display: flex; + flex-direction: column; - #library-songs-wrap { - overflow: auto; - } - } + #library-songs-wrap { + overflow: auto; + } } - #playlist { + } + #playlist { + display: flex; + flex-direction: column; + -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; + + #playlist-header { + display: -webkit-box; /* fallback for apple webkit */ + display: flex; + -webkit-align-items: center; + align-items: center; + text-align: center; + + #playlist-title { + overflow: hidden; + text-overflow: ellipsis; + } + } + + #pslwrap { + border-left: 1px solid #DDD; + overflow: auto; + display: flex; + flex-grow: 2; + + #playlist-song-list { display: flex; - flex-direction: column; - -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; - - #playlist-header { - display: -webkit-box; /* fallback for apple webkit */ - display: flex; - -webkit-align-items: center; - align-items: center; - text-align: center; - - #playlist-title { - overflow: hidden; - text-overflow: ellipsis; - } - } + flex: 1; - #pslwrap { - border-left: 1px solid #DDD; - overflow: auto; - display: flex; - flex-grow: 2; - - #playlist-song-list { - display: flex; - flex: 1; - - > tbody { - display: flex; - flex: 1; - flex-direction: column; - } - td { - position: relative; - } - tr:hover { - .song-play { - display: inline; - } - } - } + > tbody { + display: flex; + flex: 1; + flex-direction: column; + } + td { + position: relative; + } + tr:hover { + .song-play { + display: inline; + } } + } } + } } #pe { // playlist editor - width: 400px; - height: 300px; - position: fixed; - margin-bottom: 0; - bottom: 0; - right: 0; - z-index: 1002; // above floathead - // display: flex; // hidden by css by default, flex added by js - flex-direction: column; - - .panel-heading { - .panel-default-shown { - display: flex; - align-items: center; - } - - .panel-title { - flex: 1; - text-align: center; - cursor: move; - } + width: 400px; + height: 300px; + position: fixed; + margin-bottom: 0; + bottom: 0; + right: 0; + z-index: 1002; // above floathead + // display: flex; // hidden by css by default, flex added by js + flex-direction: column; + + .panel-heading { + .panel-default-shown { + display: flex; + align-items: center; } - #pe-main { - padding: 0; - overflow: auto; - display: flex; - flex: 1; + .panel-title { + flex: 1; + text-align: center; + cursor: move; } + } - #pewrap { - display: flex; - flex: 1; + #pe-main { + padding: 0; + overflow: auto; + display: flex; + flex: 1; + } - > table { - cursor: pointer; - } + #pewrap { + display: flex; + flex: 1; + + > table { + cursor: pointer; } + } - #pe-song-list { - margin-bottom: 0; - display: flex; - flex: 1; + #pe-song-list { + margin-bottom: 0; + display: flex; + flex: 1; - > tbody { - display: flex; - flex: 1; - flex-direction: column; - } + > tbody { + display: flex; + flex: 1; + flex-direction: column; } + } } #pe-tab { - position: fixed; - margin-bottom: 0; - bottom: 0; - right: 0; - z-index: 1002; // above floathead - background: #404040; - color: white; - padding: 8px; - cursor: pointer; + position: fixed; + margin-bottom: 0; + bottom: 0; + right: 0; + z-index: 1002; // above floathead + background: #404040; + color: white; + padding: 8px; + cursor: pointer; } #downloader { - min-width: 400px; + min-width: 400px; - #downloader-status { - min-height: 20px; - } + #downloader-status { + min-height: 20px; + } } #browser-selection { - flex: 0; - text-align: center; + flex: 0; + text-align: center; } #downloader-wrap { - overflow: auto; - height: ~"calc(100vh - 300px)"; - min-height: 100px; + overflow: auto; + height: ~"calc(100vh - 300px)"; + min-height: 100px; } #testing { // unit testing ouput - width: 400px; - height: 300px; - position: fixed; - margin-bottom: 0; - bottom: 0; - z-index: 1002; // above floathead - display: flex; - flex-direction: column; - - #testing-main { - padding: 0; - overflow: auto; - flex-grow: 2; - } + width: 400px; + height: 300px; + position: fixed; + margin-bottom: 0; + bottom: 0; + z-index: 1002; // above floathead + display: flex; + flex-direction: column; + + #testing-main { + padding: 0; + overflow: auto; + flex-grow: 2; + } } .navbar.pages { - margin-bottom: 0; + margin-bottom: 0; + display: flex; + align-items: center; + justify-content: space-around; + + .page { + width:200px; display: flex; align-items: center; justify-content: space-around; - .page { - width:200px; - display: flex; - align-items: center; - justify-content: space-around; - - .form-control { - max-width: 80px; - } + .form-control { + max-width: 80px; } + } } // floatThead table.floatThead-table { - border-top: none; - border-bottom: none; - background-color: #FFF; - cursor: pointer; + border-top: none; + border-bottom: none; + background-color: #FFF; + cursor: pointer; } .floatThead-wrapper { - flex-grow: 2; - display: flex; - overflow: auto; + flex-grow: 2; + display: flex; + overflow: auto; } .floatThead-container { - top: auto !important; + top: auto !important; } .flex-full { - -webkit-box-flex: 1; /* fallback for apple webkit */ - flex: 1; + -webkit-box-flex: 1; /* fallback for apple webkit */ + flex: 1; } .song-list, #downloader-folder-list, #playlist-song-list, #home, #downloader-home, .loc-dir, .downloader-loc-dir, .playlists, .faded, #library-artists-list, #library-albums-list, #downloader-home { - cursor: pointer; + cursor: pointer; } .faded { - opacity: 0.5; + opacity: 0.5; } .faded:hover { - opacity: 1; + opacity: 1; } .nowrap, .song-list-icons, .playlist-song-list-icons { - white-space:nowrap; + white-space:nowrap; } .song-list-icons { - // slightly less jarring when there there is an init width - min-width: 25px; + // slightly less jarring when there there is an init width + min-width: 25px; } .fixed-table { - /* magic */ - width: 100%; - table-layout: fixed; + /* magic */ + width: 100%; + table-layout: fixed; } .fixed-table td { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .notify { - position: fixed; - bottom: 0px; - z-index: 9999; + position: fixed; + bottom: 0px; + z-index: 9999; } .playlists, #pe-main, #downloader-folder-list { - user-select: none; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; + user-select: none; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .none { // like .hidden, except without !important - display: none; + display: none; } .dropdown-menu > li > a.no-hover:hover { - background: inherit; + background: inherit; } // bootstrap .modal-dialog { - min-width: @minimumWidth; + min-width: @minimumWidth; } .dropdown-menu { - z-index: 1003 !important; // above pe and floathead + z-index: 1003 !important; // above pe and floathead } // disables sorting for dragged item .song-list .no-placeholder { - display: none; + display: none; } // enables sorting for dragged item #playlist-song-list .no-placeholder, #pe-song-list .no-placeholder { - display: block; + display: block; } // context menu customizations .context-menu-list { - padding: 0 0 5px; - max-width: 500px; + padding: 0 0 5px; + max-width: 500px; } // context menu title .context-menu-item:first-child { - background: #EDEDED; - color: #7D7D7D; - font-size: 0.8em; - cursor: default; - margin-bottom: 5px; + background: #EDEDED; + color: #7D7D7D; + font-size: 0.8em; + cursor: default; + margin-bottom: 5px; } .space-between { - display: -webkit-box; /* fallback for apple webkit */ - display: flex; - justify-content: space-between; + display: -webkit-box; /* fallback for apple webkit */ + display: flex; + justify-content: space-between; } .main-link { - flex: 1; - text-align: center; + flex: 1; + text-align: center; } .glyphicon.spinning { - animation: spin 1s infinite linear; + animation: spin 1s infinite linear; } // having an odd issue where a table cell was very small in ff.. @-moz-document url-prefix() { - .width100 { - width: 100%; - } + .width100 { + width: 100%; + } } table.table-header { - // set display to break the table layout on purpose. It's so the js can - // set the width properly and keep the style of how a table header should - // look like from bootstrap (which is why we aren't using a plain div) - // I could probably inherit the stlye from bootstrap by including it in - // this css... - display: block; - margin-bottom: 0; - cursor: pointer; + // set display to break the table layout on purpose. It's so the js can + // set the width properly and keep the style of how a table header should + // look like from bootstrap (which is why we aren't using a plain div) + // I could probably inherit the stlye from bootstrap by including it in + // this css... + display: block; + margin-bottom: 0; + cursor: pointer; } table.table-header-fixed { - table-layout: fixed; - margin-bottom: 0; - cursor: pointer; + table-layout: fixed; + margin-bottom: 0; + cursor: pointer; } @keyframes spin { - from { transform: scale(1) rotate(0deg) } - to { transform: scale(1) rotate(360deg) } + from { transform: scale(1) rotate(0deg) } + to { transform: scale(1) rotate(360deg) } } .pulse { - animation: pulse ease 1s; + animation: pulse ease 1s; } .pulse2 { - animation: pulse ease 1s 2; + animation: pulse ease 1s 2; } @keyframes pulse { - 0%, 100% { - background-color: inherit; - } - 50% { - background-color: #bababa; - } + 0%, 100% { + background-color: inherit; + } + 50% { + background-color: #bababa; + } } // "mobile" @media (max-width: 768px) { - html { - overflow-y: auto; - overflow-x: auto; - } - body { - padding-bottom: 75px; - } - #page-wrapper { - height: auto; - } - main { - flex-direction: column; - height: auto; - align-items: center; - } - #album-art { - display: none; - } - // playlist editor - #pe, #pe-tab { - width: 100%; - } - #browser, #playlist, #library { - margin-top: 16px; - height: 500px; - } - // accessibility for scrolling - #pslwrap, #slwrap { - border-left: 1px solid #DDD; - border-right: 1px solid #DDD; - border-bottom: 1px solid #DDD; - } - header { - display: flex; - flex-direction: column; + html { + overflow-y: auto; + overflow-x: auto; + } + body { + padding-bottom: 75px; + } + #page-wrapper { + height: auto; + } + main { + flex-direction: column; + height: auto; + align-items: center; + } + #album-art { + display: none; + } + // playlist editor + #pe, #pe-tab { + width: 100%; + } + #browser, #playlist, #library { + margin-top: 16px; + height: 500px; + } + // accessibility for scrolling + #pslwrap, #slwrap { + border-left: 1px solid #DDD; + border-right: 1px solid #DDD; + border-bottom: 1px solid #DDD; + } + header { + display: flex; + flex-direction: column; - #header-left { - position: fixed; - bottom:0; - z-index: 2; - width: 100%; - min-width: .minimumWidth; - #media-buttons { - flex-direction: row; - width: 90%; - } - } - #header-right { - width: 100%; - } - #music-time { - position: fixed; - bottom: 5px; - z-index: 3; - left: 40px; - width: 80%; - } + #header-left { + position: fixed; + bottom:0; + z-index: 2; + width: 100%; + min-width: .minimumWidth; + #media-buttons { + flex-direction: row; + width: 90%; + } } - .space-between { - display: block; + #header-right { + width: 100%; } - #browser-selection { - display: flex; - width: 80%; + #music-time { + position: fixed; + bottom: 5px; + z-index: 3; + left: 40px; + width: 80%; } + } + .space-between { + display: block; + } + #browser-selection { + display: flex; + width: 80%; + } } diff --git a/public/css/themes/default-thin/main.less b/public/css/themes/default-thin/main.less index fa64b4c..50981bc 100644 --- a/public/css/themes/default-thin/main.less +++ b/public/css/themes/default-thin/main.less @@ -14,5 +14,5 @@ @import "../default/vars.less"; .radio, .checkbox { - margin: 5px auto; + margin: 5px auto; } diff --git a/public/css/themes/default/vars.less b/public/css/themes/default/vars.less index da7b7f4..2aaf488 100644 --- a/public/css/themes/default/vars.less +++ b/public/css/themes/default/vars.less @@ -20,23 +20,23 @@ // btn .btn { - margin: 1px; + margin: 1px; } .btn-group .btn { - margin-right: 0; + margin-right: 0; } .input-group-btn:last-child > .btn, .input-group-btn:last-child > .btn-group { - margin-top: 0; + margin-top: 0; } .btn-default:focus { - color: #333; - background-color: #FFF; - border-color: #CCC; + color: #333; + background-color: #FFF; + border-color: #CCC; } thead tr { - background: @body-bg; + background: @body-bg; } diff --git a/server.js b/server.js index 6a0a4e2..30fa272 100755 --- a/server.js +++ b/server.js @@ -1,244 +1,244 @@ -var express = require('express'), - app = express(), - http = require('http').Server(app), - WebSocketServer = require('ws').Server, - io = new WebSocketServer({ server: http }), - komponist = require('komponist'), - fs = require('fs'), - less = require('less-middleware'), - browserify = require('browserify-middleware'), - dns = require('dns'), - toml = require('toml'), - path = require('path'), - tilde = require('expand-tilde'), - glob = require('glob'), - // optional modules - youtubedl = null, - - // mpd: komponist mpd connection; pack: package.json; - mpd, pack, - - // some of these varaibles are saved so that a new client can quickly get - // unique information the server knows about. - - // save playlist title for future connections (because I have no idea how - // to get the playlist title on initial load) - playlisttitle = '', - // updated per connection - addresses = [], - // ip:hostname - hostnames = {}, - // checks if the song changed to clear user votes - currentSong = null, - // currently playing song's album art url - currentArt = null, - // set to true when making a release - release = false; +var express = require('express'), + app = express(), + http = require('http').Server(app), + WebSocketServer = require('ws').Server, + io = new WebSocketServer({ server: http }), + komponist = require('komponist'), + fs = require('fs'), + less = require('less-middleware'), + browserify = require('browserify-middleware'), + dns = require('dns'), + toml = require('toml'), + path = require('path'), + tilde = require('expand-tilde'), + glob = require('glob'), + // optional modules + youtubedl = null, + + // mpd: komponist mpd connection; pack: package.json; + mpd, pack, + + // some of these varaibles are saved so that a new client can quickly get + // unique information the server knows about. + + // save playlist title for future connections (because I have no idea how + // to get the playlist title on initial load) + playlisttitle = '', + // updated per connection + addresses = [], + // ip:hostname + hostnames = {}, + // checks if the song changed to clear user votes + currentSong = null, + // currently playing song's album art url + currentArt = null, + // set to true when making a release + release = false; io.broadcast = function(data) { - io.clients.forEach(function each(client) { - client.send(data, function (err) { - if (err) { - console.log('Error sending message to client via broadcast:'); - console.log(err); - } - }); + io.clients.forEach(function each(client) { + client.send(data, function (err) { + if (err) { + console.log('Error sending message to client via broadcast:'); + console.log(err); + } }); + }); }; function isEmpty(obj) { return Object.keys(obj).length === 0; } // little wrapper, sets ip if no hostname is found and sends function getHostname(ip, callback) { - dns.reverse(ip, function (err, hostname) { - if (err) { - // ENOTFOUND is okay. DNS just isn't registered for the ip - if (err.code != 'ENOTFOUND') { - console.log("DNS Failed for " + ip + ": "); - console.log(err); - } - } + dns.reverse(ip, function (err, hostname) { + if (err) { + // ENOTFOUND is okay. DNS just isn't registered for the ip + if (err.code != 'ENOTFOUND') { + console.log("DNS Failed for " + ip + ": "); + console.log(err); + } + } - // first hostname as a string - if (!hostname || hostname.length === 0) { - // in case response is not an array - hostname = []; - hostname[0] = ip; - } + // first hostname as a string + if (!hostname || hostname.length === 0) { + // in case response is not an array + hostname = []; + hostname[0] = ip; + } - //console.log('hostname: ' + hostname[0]); - if (typeof callback == 'function') callback(hostname[0], ip); - }); + //console.log('hostname: ' + hostname[0]); + if (typeof callback == 'function') callback(hostname[0], ip); + }); } var downloader = { - // some of these settings get set in config.cfg - enabled: true, - directory: 'Downloads', - keepVideo: false, - - // TODO check if file already exists - download: function (url, location, address, socket) { - if (location.includes('..')) { - console.log(address + " tried to access " + location + "!"); - socket.send(JSON.stringify({ - 'type': 'downloader-status', - 'info': 'You cannot have ".." in the location!' - })); - return; + // some of these settings get set in config.cfg + enabled: true, + directory: 'Downloads', + keepVideo: false, + + // TODO check if file already exists + download: function (url, location, address, socket) { + if (location.includes('..')) { + console.log(address + " tried to access " + location + "!"); + socket.send(JSON.stringify({ + 'type': 'downloader-status', + 'info': 'You cannot have ".." in the location!' + })); + return; + } + + location = downloader.getLocation(location); + + socket.send(JSON.stringify({ + 'type': 'downloader-status', + 'info': 'Downloading and converting video...' + })); + + var option = ['-x', '--audio-format', 'mp3']; + console.log('Requesting video download: ' + url + ' from ' + address + + ' to ' + location); + + if (this.keepVideo) option.push('-k'); + + // create the folder if it doesn't exist + fs.mkdir(location, function (err) { + // ignore exists error + if (err) { + if (err.code == 'EEXIST') { + console.log('Using directory for the ' + + 'Downloader: ' + location); + } else { + console.log('!!! Error creating directory "' + + location + '" for the Downloader.'); + console.log(err); + + socket.send(JSON.stringify({ + 'type': 'downloader-status', + 'info': 'Error creating directory!' + })); } + } else { + console.log('Creating directory for the ' + + 'Downloader: ' + location); + } - location = downloader.getLocation(location); + youtubedl.exec(url, option, {cwd: location}, + function exec(err, output) { + if (err) { + socket.send(JSON.stringify({ + 'type': 'downloader-status', + 'info': 'Error! Invalid url?' + })); + return console.log(err); + } + + console.log('============ start youtube-dl ============'); + console.log(output.join('\n')); + console.log('============ end youtube-dl ============'); + + //for (var item = 0; item < output.length; ++item) { + // if (~output[item].indexOf('.mp3')) { + // var str = output[item]; + // var colon = str.indexOf(':'); + // var newstr = str.substring(colon + 2); + // console.log(newstr); + // } + //} socket.send(JSON.stringify({ - 'type': 'downloader-status', - 'info': 'Downloading and converting video...' + 'type': 'downloader-status', + 'info': 'Done' })); + }); + + // I would like to use this instead... but it doesnt seem to work + // unless I use a write steam + //var ytdl = youtubedl(url, ['-x', '--audio-format', 'mp3'], + //{cwd: downloader.directory}); + //ytdl.on('info', function (info) { + //console.log('video download starting!'); + //}); + //ytdl.on('end', function (info) { + //console.log('video download complete!'); + //}); + }); + }, - var option = ['-x', '--audio-format', 'mp3']; - console.log('Requesting video download: ' + url + ' from ' + address + - ' to ' + location); - - if (this.keepVideo) option.push('-k'); - - // create the folder if it doesn't exist - fs.mkdir(location, function (err) { - // ignore exists error - if (err) { - if (err.code == 'EEXIST') { - console.log('Using directory for the ' + - 'Downloader: ' + location); - } else { - console.log('!!! Error creating directory "' + - location + '" for the Downloader.'); - console.log(err); - - socket.send(JSON.stringify({ - 'type': 'downloader-status', - 'info': 'Error creating directory!' - })); - } - } else { - console.log('Creating directory for the ' + - 'Downloader: ' + location); - } - - youtubedl.exec(url, option, {cwd: location}, - function exec(err, output) { - if (err) { - socket.send(JSON.stringify({ - 'type': 'downloader-status', - 'info': 'Error! Invalid url?' - })); - return console.log(err); - } - - console.log('============ start youtube-dl ============'); - console.log(output.join('\n')); - console.log('============ end youtube-dl ============'); - - //for (var item = 0; item < output.length; ++item) { - // if (~output[item].indexOf('.mp3')) { - // var str = output[item]; - // var colon = str.indexOf(':'); - // var newstr = str.substring(colon + 2); - // console.log(newstr); - // } - //} - - socket.send(JSON.stringify({ - 'type': 'downloader-status', - 'info': 'Done' - })); - }); - - // I would like to use this instead... but it doesnt seem to work - // unless I use a write steam - //var ytdl = youtubedl(url, ['-x', '--audio-format', 'mp3'], - //{cwd: downloader.directory}); - //ytdl.on('info', function (info) { - //console.log('video download starting!'); - //}); - //ytdl.on('end', function (info) { - //console.log('video download complete!'); - //}); - }); - }, - - getLocation: function (location) { - return path.normalize(tilde(config.mpd.library + path.sep + location)); - } + getLocation: function (location) { + return path.normalize(tilde(config.mpd.library + path.sep + location)); + } }; // song skipping var skip = { - votePercent: 0.75, - voting: true, - // user count - total: 1, - // user count for skip ammout - next: 0, - previous: 0, + votePercent: 0.75, + voting: true, + // user count + total: 1, + // user count for skip ammout + next: 0, + previous: 0, + // logs the addresses that have skipped + addressNext: [], + addressPrevious: [], + addressNextCancel: [], + addressPreviousCancel: [], + + reset: function () { + // reset arrays and vote ammounts? + this.next = 0; + this.previous = 0; // logs the addresses that have skipped - addressNext: [], - addressPrevious: [], - addressNextCancel: [], - addressPreviousCancel: [], - - reset: function () { - // reset arrays and vote ammounts? - this.next = 0; - this.previous = 0; - // logs the addresses that have skipped - this.addressNext = []; - this.addressPrevious = []; - this.addressNextCancel = []; - this.addressPreviousCancel = []; - // I would run sendUpdate(), but im not sure how - // to do with my current set up. - io.broadcast(JSON.stringify( - {'type': 'request-vote-update-from-server'})); - }, - - nextSuccess: function () { - mpd.next(function (err) { - if (err) return console.log(err); + this.addressNext = []; + this.addressPrevious = []; + this.addressNextCancel = []; + this.addressPreviousCancel = []; + // I would run sendUpdate(), but im not sure how + // to do with my current set up. + io.broadcast(JSON.stringify( + {'type': 'request-vote-update-from-server'})); + }, + + nextSuccess: function () { + mpd.next(function (err) { + if (err) return console.log(err); + + io.broadcast(JSON.stringify( + {'type': 'skipped', 'info': skip.addressNext})); + console.log('Song vote skip successful from ' + skip.addressNext); + skip.next = 0; + skip.addressNext = []; + }); + }, - io.broadcast(JSON.stringify( - {'type': 'skipped', 'info': skip.addressNext})); - console.log('Song vote skip successful from ' + skip.addressNext); - skip.next = 0; - skip.addressNext = []; - }); - }, + previousSuccess: function () { + mpd.previous(function (err) { + if (err) return console.log(err); - previousSuccess: function () { - mpd.previous(function (err) { - if (err) return console.log(err); - - io.broadcast(JSON.stringify( - {'type': 'skipped', 'info': skip.addressPrevious})); - console.log('Song vote skip successful from ' + skip.addressPrevious); - skip.previous = 0; - skip.addressPrevious = []; - }); - } + io.broadcast(JSON.stringify( + {'type': 'skipped', 'info': skip.addressPrevious})); + console.log('Song vote skip successful from ' + skip.addressPrevious); + skip.previous = 0; + skip.addressPrevious = []; + }); + } }; if (release) { - var minify = require('express-minify'); - // must be above express.static - app.use(minify()); - app.use(function(req, res, next) { - if (/mpcparty\.js/.test(req.url)) { - res._uglifyCompress = { - drop_console: true - }; - } - next(); - }); + var minify = require('express-minify'); + // must be above express.static + app.use(minify()); + app.use(function(req, res, next) { + if (/mpcparty\.js/.test(req.url)) { + res._uglifyCompress = { + drop_console: true + }; + } + next(); + }); } else { - app.locals.pretty = true; + app.locals.pretty = true; } app.disable('x-powered-by'); @@ -254,664 +254,664 @@ app.set('views', __dirname + '/views'); app.set('view engine', 'pug'); fs.readFile(__dirname + '/package.json', function (err, data) { - if (err) return console.log(err); - pack = JSON.parse(data); + if (err) return console.log(err); + pack = JSON.parse(data); }); // main pages app.get('/', function (req, res) { - res.render('index', { - pack: pack, - config: { - "showUsers": config.users.enabled, - "downloader": downloader.enabled, - "testing": config.testing.enabled - } - }); + res.render('index', { + pack: pack, + config: { + "showUsers": config.users.enabled, + "downloader": downloader.enabled, + "testing": config.testing.enabled + } + }); }); app.get('/browser/*', function (req, res) { - res.render('index', { - pack: pack, - config: { - "showUsers": config.users.enabled, - "downloader": downloader.enabled, - "testing": config.testing.enabled - } - }); + res.render('index', { + pack: pack, + config: { + "showUsers": config.users.enabled, + "downloader": downloader.enabled, + "testing": config.testing.enabled + } + }); }); app.get('/library/*', function (req, res) { - res.render('index', { - pack: pack, - config: { - "showUsers": config.users.enabled, - "downloader": downloader.enabled, - "testing": config.testing.enabled - } - }); + res.render('index', { + pack: pack, + config: { + "showUsers": config.users.enabled, + "downloader": downloader.enabled, + "testing": config.testing.enabled + } + }); }); app.get('/search/*', function (req, res) { - res.render('index', { - pack: pack, - config: { - "showUsers": config.users.enabled, - "downloader": downloader.enabled, - "testing": config.testing.enabled - } - }); + res.render('index', { + pack: pack, + config: { + "showUsers": config.users.enabled, + "downloader": downloader.enabled, + "testing": config.testing.enabled + } + }); }); // browser modules app.use('/bootstrap', - express.static(__dirname + '/bower_components/bootstrap/dist/')); + express.static(__dirname + '/bower_components/bootstrap/dist/')); app.use('/jquery', - express.static(__dirname + '/bower_components/jquery/dist/')); + express.static(__dirname + '/bower_components/jquery/dist/')); app.use('/floatthead', - express.static(__dirname + '/bower_components/jquery.floatThead/dist/')); + express.static(__dirname + '/bower_components/jquery.floatThead/dist/')); app.use('/toastr', - express.static(__dirname + '/bower_components/toastr/')); + express.static(__dirname + '/bower_components/toastr/')); app.use('/jquery-contextmenu', - express.static(__dirname + '/bower_components/jQuery-contextMenu/dist/')); + express.static(__dirname + '/bower_components/jQuery-contextMenu/dist/')); app.use('/dragula', - express.static(__dirname + '/bower_components/dragula.js/dist/')); + express.static(__dirname + '/bower_components/dragula.js/dist/')); // 404 requests // currently disabled until we can get dynamic urls to not 404 with this // enabled /*app.use(function (req, res, next) { - res.status(404); - - // respond with html page - if (req.accepts('html')) { - res.render('404', { url: req.url }); - console.log('Page 404 by ' + req.connection.remoteAddress + - ' on ' + req.originalUrl); - return; - } - - // respond with json - if (req.accepts('json')) { - res.send({ error: 'Not found' }); - return; - } - - // default to plain-text. send() - res.type('txt').send('Not found'); + res.status(404); + + // respond with html page + if (req.accepts('html')) { + res.render('404', { url: req.url }); + console.log('Page 404 by ' + req.connection.remoteAddress + + ' on ' + req.originalUrl); + return; + } + + // respond with json + if (req.accepts('json')) { + res.send({ error: 'Not found' }); + return; + } + + // default to plain-text. send() + res.type('txt').send('Not found'); });*/ // default config var config = { - server: { - port: 8081 - }, - mpd: { - url: 'localhost', - port: 6600, - library: '' - }, - users: { - enabled: false - }, - testing: { - enabled: false - } + server: { + port: 8081 + }, + mpd: { + url: 'localhost', + port: 6600, + library: '' + }, + users: { + enabled: false + }, + testing: { + enabled: false + } }; fs.readFile(__dirname + '/config.cfg', function (err, data) { - if (err) { - console.log('Unable to read config file. ' + - 'Custom config will not be used.'); - console.log(err); - } else { - data = toml.parse(data); - - // set all config (otherwise resort to defaults) - config.server.port = (data.server.port !== undefined ? - data.server.port : config.server.port); - config.mpd.url = (data.mpd.url !== undefined ? - data.mpd.url : config.mpd.url); - config.mpd.port = (data.mpd.port !== undefined ? - data.mpd.port : config.mpd.port); - config.mpd.library = (data.mpd.library !== undefined ? - data.mpd.library : config.mpd.library); - config.users.enabled = (data.users.enabled !== undefined ? - data.users.enabled : config.users.enabled); - config.testing.enabled = (data.testing.enabled !== undefined ? - data.testing.enabled : config.testing.enabled); - skip.voting = (data.vote.enabled !== undefined ? - data.vote.enabled : skip.voting); - skip.votePercent = (data.vote.percent !== undefined ? - data.vote.percent : skip.votePercent); - downloader.enabled = (data.downloader.enabled !== undefined ? - data.downloader.enabled : downloader.enabled); - downloader.directory = (data.downloader.directory !== undefined ? - data.downloader.directory : downloader.directory); - downloader.keepVideo = (data.downloader.keep_videos !== undefined ? - data.downloader.keep_videos : downloader.keepVideo); - } - - // if we don't know the location of the library, disable extra features - if (config.mpd.library === '') { - downloader.enabled = false; - } + if (err) { + console.log('Unable to read config file. ' + + 'Custom config will not be used.'); + console.log(err); + } else { + data = toml.parse(data); + + // set all config (otherwise resort to defaults) + config.server.port = (data.server.port !== undefined ? + data.server.port : config.server.port); + config.mpd.url = (data.mpd.url !== undefined ? + data.mpd.url : config.mpd.url); + config.mpd.port = (data.mpd.port !== undefined ? + data.mpd.port : config.mpd.port); + config.mpd.library = (data.mpd.library !== undefined ? + data.mpd.library : config.mpd.library); + config.users.enabled = (data.users.enabled !== undefined ? + data.users.enabled : config.users.enabled); + config.testing.enabled = (data.testing.enabled !== undefined ? + data.testing.enabled : config.testing.enabled); + skip.voting = (data.vote.enabled !== undefined ? + data.vote.enabled : skip.voting); + skip.votePercent = (data.vote.percent !== undefined ? + data.vote.percent : skip.votePercent); + downloader.enabled = (data.downloader.enabled !== undefined ? + data.downloader.enabled : downloader.enabled); + downloader.directory = (data.downloader.directory !== undefined ? + data.downloader.directory : downloader.directory); + downloader.keepVideo = (data.downloader.keep_videos !== undefined ? + data.downloader.keep_videos : downloader.keepVideo); + } + + // if we don't know the location of the library, disable extra features + if (config.mpd.library === '') { + downloader.enabled = false; + } + + // create video.directory folder + if (downloader.enabled) { + (function() { + youtubedl = require('youtube-dl'); + var location = downloader.getLocation(downloader.directory); + + fs.mkdir(location, function (err) { + // ignore exists error + if (err) { + if (err.code == 'EEXIST') { + console.log('Default directory for the ' + + 'Downloader: ' + location); + } else { + console.log('!!! Error creating directory "' + + location + '" for the ' + + 'Downloader, disabling...'); + downloader.enabled = false; + console.log(err); + } + } else { + console.log('Creating directory for the ' + + 'Downloader: ' + location); + } + }); + })(); + } - // create video.directory folder - if (downloader.enabled) { - (function() { - youtubedl = require('youtube-dl'); - var location = downloader.getLocation(downloader.directory); - - fs.mkdir(location, function (err) { - // ignore exists error - if (err) { - if (err.code == 'EEXIST') { - console.log('Default directory for the ' + - 'Downloader: ' + location); - } else { - console.log('!!! Error creating directory "' + - location + '" for the ' + - 'Downloader, disabling...'); - downloader.enabled = false; - console.log(err); - } - } else { - console.log('Creating directory for the ' + - 'Downloader: ' + location); - } - }); - })(); - } + http.listen(config.server.port, function () { + console.log('Web server listening on *:' + config.server.port); + console.log('Connecting to MPD server ' + config.mpd.url + ':' + + config.mpd.port + '...'); + }); - http.listen(config.server.port, function () { - console.log('Web server listening on *:' + config.server.port); - console.log('Connecting to MPD server ' + config.mpd.url + ':' + - config.mpd.port + '...'); - }); + // Open up a proxy on the HTTP server that points to MPD + komponist.install(http, config.mpd.url, config.mpd.port); - // Open up a proxy on the HTTP server that points to MPD - komponist.install(http, config.mpd.url, config.mpd.port); + mpd = komponist.createConnection(config.mpd.port, config.mpd.url, + function (err, client) { + if (err) { + console.log(err); + process.exit(-5); + } - mpd = komponist.createConnection(config.mpd.port, config.mpd.url, - function (err, client) { - if (err) { - console.log(err); - process.exit(-5); - } + console.log('Connected to MPD!'); + setSong(client); - console.log('Connected to MPD!'); + client.on('changed', function (system) { + //console.log('subsystem changed: ' + system); + if (system == 'player') { setSong(client); - - client.on('changed', function (system) { - //console.log('subsystem changed: ' + system); - if (system == 'player') { - setSong(client); - } else if (system == 'playlist') { - // running setSong with a playlist change to fix a vote issue - // where users could vote after no song was selected - setSong(client); - } - }); + } else if (system == 'playlist') { + // running setSong with a playlist change to fix a vote issue + // where users could vote after no song was selected + setSong(client); + } }); + }); }); function sendArtworkMessage() { - io.broadcast(JSON.stringify({'type': 'album-art', 'url': currentArt}), - function (err) { - if (err) { - console.log('Error sending album art information'); - console.log(err); - } - }); + io.broadcast(JSON.stringify({'type': 'album-art', 'url': currentArt}), + function (err) { + if (err) { + console.log('Error sending album art information'); + console.log(err); + } + }); } // get album art based on the directory the file is in function getImage(song) { - if (!song || config.mpd.library === '') { - currentArt = null; - sendArtworkMessage(); - return; - } + if (!song || config.mpd.library === '') { + currentArt = null; + sendArtworkMessage(); + return; + } - var subFolder = song.file.substr(0, song.file.lastIndexOf('/')), - folder = tilde(config.mpd.library + '/' + subFolder); + var subFolder = song.file.substr(0, song.file.lastIndexOf('/')), + folder = tilde(config.mpd.library + '/' + subFolder); - //console.log(folder); - glob('{*.jpg,*.png}', {cwd:folder}, function(err, files) { - if (err) return console.log(err); + //console.log(folder); + glob('{*.jpg,*.png}', {cwd:folder}, function(err, files) { + if (err) return console.log(err); - //console.log(files); - if (files.length === 0) { - currentArt = null; - sendArtworkMessage(); - return; - } + //console.log(files); + if (files.length === 0) { + currentArt = null; + sendArtworkMessage(); + return; + } - // just grab the first file - var imageLocation = tilde(folder + path.sep + files[0]); - //console.log(imageLocation); - - currentArt = encodeURI('/album-art/' + subFolder + '/' + files[0]) - // *sigh* https://stackoverflow.com/a/8143232 - .replace(/\(/g, "%28").replace(/\)/g, "%29"); - - console.log('Creating art URL: ' + currentArt); - - // TODO check if there is a way to remove the old url when a - // new one is created (or will the gc or express handle that?) - // create a new url - app.get(currentArt, function (req, res) { - res.sendFile(imageLocation, function (err) { - if (err) { - console.log(err); - res.status(err.status).end(); - } - }); - }); + // just grab the first file + var imageLocation = tilde(folder + path.sep + files[0]); + //console.log(imageLocation); + + currentArt = encodeURI('/album-art/' + subFolder + '/' + files[0]) + // *sigh* https://stackoverflow.com/a/8143232 + .replace(/\(/g, "%28").replace(/\)/g, "%29"); - sendArtworkMessage(); + console.log('Creating art URL: ' + currentArt); + + // TODO check if there is a way to remove the old url when a + // new one is created (or will the gc or express handle that?) + // create a new url + app.get(currentArt, function (req, res) { + res.sendFile(imageLocation, function (err) { + if (err) { + console.log(err); + res.status(err.status).end(); + } + }); }); + + sendArtworkMessage(); + }); } function setSong(client) { - client.currentsong(function (err, song) { - if (err) { - console.log('Error setting current song'); - return console.log(err); - } + client.currentsong(function (err, song) { + if (err) { + console.log('Error setting current song'); + return console.log(err); + } - //console.log('set song: ' + song); + //console.log('set song: ' + song); - if (isEmpty(song)) { - currentSong = null; - getImage(); - return console.log('No song selected'); - } + if (isEmpty(song)) { + currentSong = null; + getImage(); + return console.log('No song selected'); + } - if (currentSong != song.file) { - console.log('Now playing: ' + song.file); - skip.reset(); - getImage(song); - } + if (currentSong != song.file) { + console.log('Now playing: ' + song.file); + skip.reset(); + getImage(song); + } - currentSong = song.file; - }); + currentSong = song.file; + }); } // sendUpdate manages the users connected and sends vote information // to the clients var sendUpdate = function (address, connect, customSocket) { - var oldAddresses = addresses, - i; + var oldAddresses = addresses, + i; - addresses = []; + addresses = []; - for (i = 0; i < io.clients.length; ++i) { - var remAdd = io.clients[i]._socket.remoteAddress; - // check if remAdd is undefined - if (!~addresses.indexOf(remAdd) && remAdd) - addresses.push(remAdd); - } + for (i = 0; i < io.clients.length; ++i) { + var remAdd = io.clients[i]._socket.remoteAddress; + // check if remAdd is undefined + if (!~addresses.indexOf(remAdd) && remAdd) + addresses.push(remAdd); + } - var oldSize = oldAddresses.length, - newSize = addresses.length; + var oldSize = oldAddresses.length, + newSize = addresses.length; - i = 0; + i = 0; - function hostHandler(addr) { - return getHostname(addr, function (hostname, usedip) { - hostnames[usedip] = hostname; + function hostHandler(addr) { + return getHostname(addr, function (hostname, usedip) { + hostnames[usedip] = hostname; - if (i == addresses.length - 1 && config.users.enabled) { - io.broadcast(JSON.stringify( - {'type': 'hostnames', 'info': hostnames})); - } - ++i; - }); - } + if (i == addresses.length - 1 && config.users.enabled) { + io.broadcast(JSON.stringify( + {'type': 'hostnames', 'info': hostnames})); + } + ++i; + }); + } - // only get dns if the user is connecting - if (connect) { - for (var item = 0; item < addresses.length; ++item) { - hostHandler(addresses[item]); - } + // only get dns if the user is connecting + if (connect) { + for (var item = 0; item < addresses.length; ++item) { + hostHandler(addresses[item]); } + } - // if the address is undefined, compare addresses with oldAddresses - if (!address && oldSize != newSize) { - var diff = oldAddresses.filter(function(i) { - return addresses.indexOf(i) < 0; - }); + // if the address is undefined, compare addresses with oldAddresses + if (!address && oldSize != newSize) { + var diff = oldAddresses.filter(function(i) { + return addresses.indexOf(i) < 0; + }); - address = diff[0]; + address = diff[0]; - if (diff.length > 1) { - console.log('Address differences is > 1, I cant handle this GG.'); - console.log(diff); - } + if (diff.length > 1) { + console.log('Address differences is > 1, I cant handle this GG.'); + console.log(diff); + } + } + + // if there is an update to the ip address list + // (avoiding duplicate socket connections) + if (customSocket || oldSize != newSize) { + console.log('/---------------------------------------------------\\'); + + if (connect) + console.log('| ' + address + ' connected to the socket.'); + else { + console.log('| ' + address + ' disconnected from the socket.'); + + var index = ''; + + // removes skipped user from address list + if (~skip.addressNext.indexOf(address)) { + console.log('| Removing vote from disconnected user: ' + + address); + --skip.next; + index = skip.addressNext.indexOf(address); + skip.addressNext.splice(index, 1); + } + + if (~skip.addressPrevious.indexOf(address)) { + console.log('| Removing vote from disconnected user: ' + + address); + --skip.previous; + index = skip.addressPrevious.indexOf(address); + skip.addressPrevious.splice(index, 1); + } } - // if there is an update to the ip address list - // (avoiding duplicate socket connections) - if (customSocket || oldSize != newSize) { - console.log('/---------------------------------------------------\\'); - - if (connect) - console.log('| ' + address + ' connected to the socket.'); - else { - console.log('| ' + address + ' disconnected from the socket.'); - - var index = ''; - - // removes skipped user from address list - if (~skip.addressNext.indexOf(address)) { - console.log('| Removing vote from disconnected user: ' + - address); - --skip.next; - index = skip.addressNext.indexOf(address); - skip.addressNext.splice(index, 1); - } - - if (~skip.addressPrevious.indexOf(address)) { - console.log('| Removing vote from disconnected user: ' + - address); - --skip.previous; - index = skip.addressPrevious.indexOf(address); - skip.addressPrevious.splice(index, 1); - } - } + console.log(addresses); - console.log(addresses); + var totalClients = addresses.length, + songSkipFloat = totalClients * skip.votePercent; - var totalClients = addresses.length, - songSkipFloat = totalClients * skip.votePercent; + skip.total = parseInt((songSkipFloat), 10); - skip.total = parseInt((songSkipFloat), 10); + if (skip.total < 1) skip.total = 1; - if (skip.total < 1) skip.total = 1; + // in case a user votes, and another user disconnects that + // didn't vote, to check if the current votes are able to skip. + if ((skip.next >= skip.total) && (skip.next !== 0) && + (skip.total !== 0)) + skip.nextSuccess(); + else if ((skip.previous >= skip.total) && (skip.previous !== 0) && + (skip.total !== 0)) + skip.previousSuccess(); - // in case a user votes, and another user disconnects that - // didn't vote, to check if the current votes are able to skip. - if ((skip.next >= skip.total) && (skip.next !== 0) && - (skip.total !== 0)) - skip.nextSuccess(); - else if ((skip.previous >= skip.total) && (skip.previous !== 0) && - (skip.total !== 0)) - skip.previousSuccess(); + console.log('| Song skip total needed: ' + skip.total + ' (' + + skip.votePercent * 100 + '% = ' + songSkipFloat + + ' users) (next: ' + skip.next + ') (previous: ' + skip.previous + + ')'); - console.log('| Song skip total needed: ' + skip.total + ' (' + - skip.votePercent * 100 + '% = ' + songSkipFloat + - ' users) (next: ' + skip.next + ') (previous: ' + skip.previous + - ')'); + var send = JSON.stringify({ + 'type': 'current-info', 'total-clients': totalClients, + 'song-skip-total': skip.total, 'song-skip-next': skip.next, + 'song-skip-previous': skip.previous + }); - var send = JSON.stringify({ - 'type': 'current-info', 'total-clients': totalClients, - 'song-skip-total': skip.total, 'song-skip-next': skip.next, - 'song-skip-previous': skip.previous - }); + if (customSocket) + customSocket.send(send, function (err) { + if (err) { + console.log('Error sending message to client:'); + console.log(err); + } + }); + else + io.broadcast(send); - if (customSocket) - customSocket.send(send, function (err) { - if (err) { - console.log('Error sending message to client:'); - console.log(err); - } - }); - else - io.broadcast(send); - - console.log('\\---------------------------------------------------/'); - } + console.log('\\---------------------------------------------------/'); + } }; // io is for everyone, socket is for the single client io.on('connection', function (socket) { - var address = socket._socket.remoteAddress; - - // a bug occurs where when the client closes the browser, - // it sends a connection request, but the address is undefined. - // This causes issues when trying to send() something to the socket, - // causing an error - if (!address) { - console.log('Address is undefined (did a user disconnect?)'); - sendUpdate(address, false); - return; + var address = socket._socket.remoteAddress; + + // a bug occurs where when the client closes the browser, + // it sends a connection request, but the address is undefined. + // This causes issues when trying to send() something to the socket, + // causing an error + if (!address) { + console.log('Address is undefined (did a user disconnect?)'); + sendUpdate(address, false); + return; + } + + // if the user skipped in the past, add the skip back onto their client. + if (~skip.addressNext.indexOf(address)) + socket.send(JSON.stringify({'type': 'user-skip-next'}), + function (err) { + if (err) { + console.log('Error sending user-skip-next'); + console.log(err); + } + }); + + if (~skip.addressPrevious.indexOf(address)) + socket.send(JSON.stringify({'type': 'user-skip-previous'}), + function (err) { + if (err) { + console.log('Error sending user-skip-previous'); + console.log(err); + } + }); + + // on client connect, send update to everyone + sendUpdate(address, true); + + // on client connect, send init values to single client + socket.send(JSON.stringify({ + 'type': 'init', 'playlist-title': playlisttitle, + 'song-vote': skip.voting, + 'downloader-enabled': downloader.enabled, + 'downloader-location': downloader.directory, + 'album-art': currentArt, + }), + function (err) { + if (err) { + console.log('Error sending client current info'); + console.log(err); } + }); - // if the user skipped in the past, add the skip back onto their client. - if (~skip.addressNext.indexOf(address)) - socket.send(JSON.stringify({'type': 'user-skip-next'}), - function (err) { - if (err) { - console.log('Error sending user-skip-next'); - console.log(err); - } - }); + socket.on('message', function incoming(event) { + if (!event) return; + + var msg = JSON.parse(event), + index; - if (~skip.addressPrevious.indexOf(address)) - socket.send(JSON.stringify({'type': 'user-skip-previous'}), - function (err) { - if (err) { - console.log('Error sending user-skip-previous'); - console.log(err); - } + //console.log(msg); + + switch(msg.type) { + case 'playlist-title': + // sends the new playlist title to the other users + if (playlisttitle == msg.info) break; + + console.log('Sending new title of the playlist to all clients.'); + playlisttitle = msg.info; + io.broadcast(JSON.stringify( + {'type': 'playlist-title', 'info': msg.info})); + break; + + case 'stop-server': + console.log(address + ' closed the server.'); + process.exit(-1); + break; + + case 'clear-playlist': + console.log('Clearing the playlist for all clients.'); + + mpd.clear(function (err) { + if (err) return console.log(err); + playlisttitle = ''; + io.broadcast(JSON.stringify({'type': 'clear-playlist'})); }); + break; + + case 'update-playlist': + // used for updating the playlist when the client removes the + // currently playing song + console.log('Updating the playlist for all clients.'); + io.broadcast(JSON.stringify({'type': 'update-playlist'})); + break; + + case 'update-browser': + // used for updating the browser after updaing the database + console.log('Updating the browser for all clients.'); + io.broadcast(JSON.stringify({'type': 'update-browser'})); + break; + + case 'song-next': + console.log(address + ' skipped song'); + io.broadcast(JSON.stringify( + {'type': 'song-next', 'info': address})); + break; - // on client connect, send update to everyone - sendUpdate(address, true); + case 'song-previous': + console.log(address + ' skipped song'); + io.broadcast(JSON.stringify( + {'type': 'song-previous', 'info': address})); + break; - // on client connect, send init values to single client - socket.send(JSON.stringify({ - 'type': 'init', 'playlist-title': playlisttitle, - 'song-vote': skip.voting, - 'downloader-enabled': downloader.enabled, - 'downloader-location': downloader.directory, - 'album-art': currentArt, - }), - function (err) { - if (err) { - console.log('Error sending client current info'); - console.log(err); + case 'song-vote-next': + index = ''; + if (currentSong !== null && msg.info == 'yes' && + !~skip.addressNext.indexOf(address)) { + + // checks vote cancel + if (~skip.addressNextCancel.indexOf(address)) { + index = skip.addressNextCancel.indexOf(address); + skip.addressNextCancel.splice(index, 1); + } + + // checks vote + skip.addressNext.push(address); + + ++skip.next; + + if ((skip.next >= skip.total) && (skip.next !== 0) && + (skip.total !== 0)) { + skip.nextSuccess(); + } else { + io.broadcast(JSON.stringify( + {'type': 'song-vote-next', 'info': skip.next})); + } + + } else if (msg.info == 'no' && + !~skip.addressNextCancel.indexOf(address) && + ~skip.addressNext.indexOf(address)) { + + // checks vote cancel + skip.addressNextCancel.push(address); + + // checks vote + index = skip.addressNext.indexOf(address); + skip.addressNext.splice(index, 1); + + --skip.next; + io.broadcast(JSON.stringify( + {'type': 'song-vote-next', 'info': skip.next})); } - }); - socket.on('message', function incoming(event) { - if (!event) return; - - var msg = JSON.parse(event), - index; - - //console.log(msg); - - switch(msg.type) { - case 'playlist-title': - // sends the new playlist title to the other users - if (playlisttitle == msg.info) break; - - console.log('Sending new title of the playlist to all clients.'); - playlisttitle = msg.info; - io.broadcast(JSON.stringify( - {'type': 'playlist-title', 'info': msg.info})); - break; - - case 'stop-server': - console.log(address + ' closed the server.'); - process.exit(-1); - break; - - case 'clear-playlist': - console.log('Clearing the playlist for all clients.'); - - mpd.clear(function (err) { - if (err) return console.log(err); - playlisttitle = ''; - io.broadcast(JSON.stringify({'type': 'clear-playlist'})); - }); - break; - - case 'update-playlist': - // used for updating the playlist when the client removes the - // currently playing song - console.log('Updating the playlist for all clients.'); - io.broadcast(JSON.stringify({'type': 'update-playlist'})); - break; - - case 'update-browser': - // used for updating the browser after updaing the database - console.log('Updating the browser for all clients.'); - io.broadcast(JSON.stringify({'type': 'update-browser'})); - break; - - case 'song-next': - console.log(address + ' skipped song'); - io.broadcast(JSON.stringify( - {'type': 'song-next', 'info': address})); - break; - - case 'song-previous': - console.log(address + ' skipped song'); - io.broadcast(JSON.stringify( - {'type': 'song-previous', 'info': address})); - break; - - case 'song-vote-next': - index = ''; - if (currentSong !== null && msg.info == 'yes' && - !~skip.addressNext.indexOf(address)) { - - // checks vote cancel - if (~skip.addressNextCancel.indexOf(address)) { - index = skip.addressNextCancel.indexOf(address); - skip.addressNextCancel.splice(index, 1); - } - - // checks vote - skip.addressNext.push(address); - - ++skip.next; - - if ((skip.next >= skip.total) && (skip.next !== 0) && - (skip.total !== 0)) { - skip.nextSuccess(); - } else { - io.broadcast(JSON.stringify( - {'type': 'song-vote-next', 'info': skip.next})); - } - - } else if (msg.info == 'no' && - !~skip.addressNextCancel.indexOf(address) && - ~skip.addressNext.indexOf(address)) { - - // checks vote cancel - skip.addressNextCancel.push(address); - - // checks vote - index = skip.addressNext.indexOf(address); - skip.addressNext.splice(index, 1); - - --skip.next; - io.broadcast(JSON.stringify( - {'type': 'song-vote-next', 'info': skip.next})); - } - - break; - - case 'song-vote-previous': - index = ''; - if (currentSong !== null && msg.info == 'yes' && - !~skip.addressPrevious.indexOf(address)) { - // checks vote cancel - if (~skip.addressPreviousCancel.indexOf(address)) { - index = skip.addressPreviousCancel.indexOf(address); - skip.addressPreviousCancel.splice(index, 1); - } - - // checks vote - skip.addressPrevious.push(address); - - ++skip.previous; - - if ((skip.previous >= skip.total) && - (skip.previous !== 0) && (skip.total !== 0)) { - skip.previousSuccess(); - } else { - io.broadcast(JSON.stringify({ - 'type': 'song-vote-previous', - 'info': skip.previous - })); - } - } else if (msg.info == 'no' && - !~skip.addressPreviousCancel.indexOf(address) && - ~skip.addressPrevious.indexOf(address)) { - - // checks vote cancel - skip.addressPreviousCancel.push(address); - - // checks vote - index = skip.addressPrevious.indexOf(address); - skip.addressPrevious.splice(index, 1); - - --skip.previous; - io.broadcast(JSON.stringify( - {'type': 'song-vote-previous', 'info': skip.previous})); - } - - break; - - case 'get-votes': - console.log('Got vote request from ' + address + - ', sending updates...'); - sendUpdate(address, true, socket); - break; - - case 'playlist-reload': - console.log('Reloading the playlist for all clients.'); - mpd.clear(function (err) { - if (err) return console.log(err); - - mpd.load(msg.info, function (err) { - if (err) return console.log(err); - - io.broadcast(JSON.stringify( - {'type': 'playlist-title', 'info': msg.info})); - }); - }); - break; - - case 'downloader-download': - downloader.download(msg.url, msg.location, address, socket); - break; + break; + + case 'song-vote-previous': + index = ''; + if (currentSong !== null && msg.info == 'yes' && + !~skip.addressPrevious.indexOf(address)) { + // checks vote cancel + if (~skip.addressPreviousCancel.indexOf(address)) { + index = skip.addressPreviousCancel.indexOf(address); + skip.addressPreviousCancel.splice(index, 1); + } + + // checks vote + skip.addressPrevious.push(address); + + ++skip.previous; + + if ((skip.previous >= skip.total) && + (skip.previous !== 0) && (skip.total !== 0)) { + skip.previousSuccess(); + } else { + io.broadcast(JSON.stringify({ + 'type': 'song-vote-previous', + 'info': skip.previous + })); + } + } else if (msg.info == 'no' && + !~skip.addressPreviousCancel.indexOf(address) && + ~skip.addressPrevious.indexOf(address)) { + + // checks vote cancel + skip.addressPreviousCancel.push(address); + + // checks vote + index = skip.addressPrevious.indexOf(address); + skip.addressPrevious.splice(index, 1); + + --skip.previous; + io.broadcast(JSON.stringify( + {'type': 'song-vote-previous', 'info': skip.previous})); } - socket.on('close', function () { - sendUpdate(address, false); + break; + + case 'get-votes': + console.log('Got vote request from ' + address + + ', sending updates...'); + sendUpdate(address, true, socket); + break; + + case 'playlist-reload': + console.log('Reloading the playlist for all clients.'); + mpd.clear(function (err) { + if (err) return console.log(err); + + mpd.load(msg.info, function (err) { + if (err) return console.log(err); + + io.broadcast(JSON.stringify( + {'type': 'playlist-title', 'info': msg.info})); + }); }); + break; + + case 'downloader-download': + downloader.download(msg.url, msg.location, address, socket); + break; + } + + socket.on('close', function () { + sendUpdate(address, false); }); + }); }); // error handling http.on('error', function (err) { - if (err.code == 'EADDRINUSE') { - console.error('Web server port already in use! ' + - 'Edit config.cfg to change the port.'); - process.exit(-4); - } else { - console.log('Uncaught HTTP Exception!'); - console.error(err); - process.exit(-3); - } + if (err.code == 'EADDRINUSE') { + console.error('Web server port already in use! ' + + 'Edit config.cfg to change the port.'); + process.exit(-4); + } else { + console.log('Uncaught HTTP Exception!'); + console.error(err); + process.exit(-3); + } }); // catch other errors that I can't seem to catch properly... // comment out this process.on() to see full stack log process.on('uncaughtException', function(err) { - if (err.code == 'ECONNREFUSED') { - console.log('Connection refused! Is MPD running?'); - process.exit(-6); - } else { - console.log('Uncaught Exception!'); - console.log(err); - process.exit(-2); - } + if (err.code == 'ECONNREFUSED') { + console.log('Connection refused! Is MPD running?'); + process.exit(-6); + } else { + console.log('Uncaught Exception!'); + console.log(err); + process.exit(-2); + } }); diff --git a/src/browser.js b/src/browser.js index 32852fc..f5e147a 100644 --- a/src/browser.js +++ b/src/browser.js @@ -2,668 +2,668 @@ module.exports = function (mpcp) { // the file browser return { - tableid: 'file-browser-song-list', - table: '#file-browser-song-list', - tbody: '#file-browser-song-list .append', - tbodyid: 'file-browser-song-list-tbody', - // current directory the user is in - current: '/', - previous: '/', - searching: false, - searchTerm: '', - // local copy of html items - localFolders: [], - localFiles: [], - // selected items from multiselect - selected: [], - // whether to update the browser on the next update or not - doUpdate: true, - // toggle between library and browser - hidden: false, - // used for dragging while selected - clone: null, - - // check which dir the user is in. - // only update that dir - update: function (dir, poppedState, callback) { - if (mpcp.browser.hidden && !mpcp.library.hidden) { - // update library here. Just use mpcp.browser.update as a general - // library/browser update. May change later to reduce coupling. - mpcp.libraryArtists.update(mpcp.library.artist); - mpcp.libraryAlbums.update(mpcp.library.artist, mpcp.library.album); - if (callback) callback(); - return; - } + tableid: 'file-browser-song-list', + table: '#file-browser-song-list', + tbody: '#file-browser-song-list .append', + tbodyid: 'file-browser-song-list-tbody', + // current directory the user is in + current: '/', + previous: '/', + searching: false, + searchTerm: '', + // local copy of html items + localFolders: [], + localFiles: [], + // selected items from multiselect + selected: [], + // whether to update the browser on the next update or not + doUpdate: true, + // toggle between library and browser + hidden: false, + // used for dragging while selected + clone: null, + + // check which dir the user is in. + // only update that dir + update: function (dir, poppedState, callback) { + if (mpcp.browser.hidden && !mpcp.library.hidden) { + // update library here. Just use mpcp.browser.update as a general + // library/browser update. May change later to reduce coupling. + mpcp.libraryArtists.update(mpcp.library.artist); + mpcp.libraryAlbums.update(mpcp.library.artist, mpcp.library.album); + if (callback) callback(); + return; + } - // db update (ignore back / forward buttons) - if (!dir && !poppedState && !mpcp.browser.doUpdate) { - console.log('do not update this...'); - // doUpdate gets set to true in the onMessage update-browser - //mpcp.browser.doUpdate = true; - if (callback) callback(); - return; - } + // db update (ignore back / forward buttons) + if (!dir && !poppedState && !mpcp.browser.doUpdate) { + console.log('do not update this...'); + // doUpdate gets set to true in the onMessage update-browser + //mpcp.browser.doUpdate = true; + if (callback) callback(); + return; + } - if (dir) mpcp.browser.current = dir; + if (dir) mpcp.browser.current = dir; - //console.log('previous directory: ' + mpcp.browser.previous); - console.log('reloading directory: ' + mpcp.browser.current); + //console.log('previous directory: ' + mpcp.browser.previous); + console.log('reloading directory: ' + mpcp.browser.current); - if ((!poppedState && mpcp.browser.previous != mpcp.browser.current) || - (!poppedState && mpcp.browser.searching)) { - mpcp.browser.searching = false; - mpcp.browser.addToHistory(); - $('#slwrap').scrollTop($(mpcp.browser.table)); - } + if ((!poppedState && mpcp.browser.previous != mpcp.browser.current) || + (!poppedState && mpcp.browser.searching)) { + mpcp.browser.searching = false; + mpcp.browser.addToHistory(); + $('#slwrap').scrollTop($(mpcp.browser.table)); + } - mpcp.browser.updateBrowser(mpcp.browser.current, callback); + mpcp.browser.updateBrowser(mpcp.browser.current, callback); - if (dir) mpcp.browser.previous = dir; - }, + if (dir) mpcp.browser.previous = dir; + }, - addToHistory: function () { - if (this.current == '/' || this.current === '') { - console.log('adding /browser/ to history'); - window.history.pushState('', 'MPCParty', '/browser/'); - } else { - console.log('adding /browser/' + this.current + - ' to history'); - window.history.pushState('', this.current + ' - MPCParty', - '/browser/' + this.current); - } - }, - - // shows directories. use '/' for root - updateBrowser: function (directory, callback) { - // location bar: - // split directory based on /'s - // create a list item for each dir split - $('#location .loc-dir').remove(); - // toString incase of number only directories - var dirs = directory.toString().split('/'), - dirId = dirs[0], - html = '', - i; - - if (this.current != '/') - for (i = 0; i < dirs.length; ++i) { - html += '
  • ' + - dirs[i] + '
  • '; - dirId += '/' + dirs[i+1]; - } + addToHistory: function () { + if (this.current == '/' || this.current === '') { + console.log('adding /browser/ to history'); + window.history.pushState('', 'MPCParty', '/browser/'); + } else { + console.log('adding /browser/' + this.current + + ' to history'); + window.history.pushState('', this.current + ' - MPCParty', + '/browser/' + this.current); + } + }, + + // shows directories. use '/' for root + updateBrowser: function (directory, callback) { + // location bar: + // split directory based on /'s + // create a list item for each dir split + $('#location .loc-dir').remove(); + // toString incase of number only directories + var dirs = directory.toString().split('/'), + dirId = dirs[0], + html = '', + i; + + if (this.current != '/') + for (i = 0; i < dirs.length; ++i) { + html += '
  • ' + + dirs[i] + '
  • '; + dirId += '/' + dirs[i+1]; + } + + $('#location ol')[0].innerHTML += html; + + komponist.lsinfo(directory, function (err, files) { + //console.log(files); + if (err) return console.log(err); + + document.getElementById(mpcp.browser.tbodyid).innerHTML = ''; + mpcp.browser.localFolders = []; + mpcp.browser.localFiles = []; + files = mpcp.utils.toArray(files); + + if (!files.length) { + html = '' + + 'Empty directory'; + document.getElementById(mpcp.browser.tableid).innerHTML = html; + mpcp.pages.update('browser'); + console.log('Empty directory'); + if (callback) callback(); + return; + } - $('#location ol')[0].innerHTML += html; - - komponist.lsinfo(directory, function (err, files) { - //console.log(files); - if (err) return console.log(err); - - document.getElementById(mpcp.browser.tbodyid).innerHTML = ''; - mpcp.browser.localFolders = []; - mpcp.browser.localFiles = []; - files = mpcp.utils.toArray(files); - - if (!files.length) { - html = '' + - 'Empty directory'; - document.getElementById(mpcp.browser.tableid).innerHTML = html; - mpcp.pages.update('browser'); - console.log('Empty directory'); - if (callback) callback(); - return; - } + var html = ''; - var html = ''; + // initialize html for browser + for (i = 0; i < files.length; ++i) { + html = mpcp.browser.getHtmlFolders(files[i]); - // initialize html for browser - for (i = 0; i < files.length; ++i) { - html = mpcp.browser.getHtmlFolders(files[i]); + if (html !== '') + mpcp.browser.localFolders.push(html); + else { + html = mpcp.browser.getHtmlFiles(files[i]); + if (html !== '') mpcp.browser.localFiles.push(html); + } + } - if (html !== '') - mpcp.browser.localFolders.push(html); - else { - html = mpcp.browser.getHtmlFiles(files[i]); - if (html !== '') mpcp.browser.localFiles.push(html); - } - } + mpcp.browser.updateLocal(callback); + }); + }, - mpcp.browser.updateLocal(callback); - }); - }, + // replaces the browser with search results + // searches for all files based on file name and tag + search: function (name, poppedState, callback) { + console.log('mpcp.browser.search: ' + name); - // replaces the browser with search results - // searches for all files based on file name and tag - search: function (name, poppedState, callback) { - console.log('mpcp.browser.search: ' + name); + if (mpcp.browser.hidden) { + mpcp.browser.show(); + mpcp.library.hide(); + mpcp.library.bringBack = true; + } - if (mpcp.browser.hidden) { - mpcp.browser.show(); - mpcp.library.hide(); - mpcp.library.bringBack = true; + // search for tag + komponist.search('any', name, function (err, anyFiles) { + if (err) { + if (callback) callback(); + return console.log(err); + } + + // search for file name + komponist.search('file', name, function (err, files) { + if (err) { + if (callback) callback(); + return console.log(err); } - // search for tag - komponist.search('any', name, function (err, anyFiles) { - if (err) { - if (callback) callback(); - return console.log(err); - } - - // search for file name - komponist.search('file', name, function (err, files) { - if (err) { - if (callback) callback(); - return console.log(err); - } + anyFiles = mpcp.utils.toArray(anyFiles); + files = mpcp.utils.toArray(files); + + //console.log(anyFiles); + //console.log(files); + + var unique = mpcp.utils.concatDedupe(anyFiles, files); + + //console.log(unique); + callbackSearch(unique); + }); + }); + + function callbackSearch(files) { + if (mpcp.browser.searchTerm == name) { + // just don't add repeated search to history + poppedState = true; + } + + // do this after (in case of error) + document.getElementById(mpcp.browser.tbodyid).innerHTML = ''; + mpcp.browser.searching = true; + mpcp.browser.searchTerm = name; + mpcp.browser.localFolders = []; + mpcp.browser.localFiles = []; + var html; + + if (!files.length) { + // note: when the search is nothing, it does not save to + // history + html = '' + + 'No songs found'; + document.getElementById(mpcp.browser.tbodyid).innerHTML = html; + mpcp.pages.update('browser'); + console.log('No songs found'); + window.dispatchEvent(new CustomEvent("MPCperowserChanged")); + if (callback) callback(0); + return; + } - anyFiles = mpcp.utils.toArray(anyFiles); - files = mpcp.utils.toArray(files); + html = ''; - //console.log(anyFiles); - //console.log(files); + for (var i = 0; i < files.length; ++i) { + html = mpcp.browser.getHtmlFiles(files[i]); - var unique = mpcp.utils.concatDedupe(anyFiles, files); + if (html !== '') + mpcp.browser.localFiles.push(html); + } - //console.log(unique); - callbackSearch(unique); - }); - }); + if (!poppedState) { + console.log('pushing history search'); + window.history.pushState('', name + ' - MPCParty', + '/search/' + encodeURIComponent(name)); + } - function callbackSearch(files) { - if (mpcp.browser.searchTerm == name) { - // just don't add repeated search to history - poppedState = true; - } + mpcp.browser.updateLocal(callback); + } + }, - // do this after (in case of error) - document.getElementById(mpcp.browser.tbodyid).innerHTML = ''; - mpcp.browser.searching = true; - mpcp.browser.searchTerm = name; - mpcp.browser.localFolders = []; - mpcp.browser.localFiles = []; - var html; - - if (!files.length) { - // note: when the search is nothing, it does not save to - // history - html = '' + - 'No songs found'; - document.getElementById(mpcp.browser.tbodyid).innerHTML = html; - mpcp.pages.update('browser'); - console.log('No songs found'); - window.dispatchEvent(new CustomEvent("MPCperowserChanged")); - if (callback) callback(0); - return; - } + // used for unit tests atm + resetSearch: function (callback) { + mpcp.browser.update(null, null, callback); + }, - html = ''; + getHtmlFolders: function (value) { + var tableStart = '
    ', + tableEnd = '
    ', + strippedDir = '', + html = ''; - for (var i = 0; i < files.length; ++i) { - html = mpcp.browser.getHtmlFiles(files[i]); + if (value.directory) { + //console.log('dir'); - if (html !== '') - mpcp.browser.localFiles.push(html); - } + strippedDir = mpcp.utils.stripSlash(value.directory); - if (!poppedState) { - console.log('pushing history search'); - window.history.pushState('', name + ' - MPCParty', - '/search/' + encodeURIComponent(name)); - } + html = ' ' + tableStart + strippedDir + tableEnd + ''; + } - mpcp.browser.updateLocal(callback); - } - }, + return html; + }, - // used for unit tests atm - resetSearch: function (callback) { - mpcp.browser.update(null, null, callback); - }, + getHtmlFiles: function (value) { + var tableStart = '
    ', + tableEnd = '
    ', + stripFile = '', + html = ''; - getHtmlFolders: function (value) { - var tableStart = '
    ', - tableEnd = '
    ', - strippedDir = '', - html = ''; + if (value.file) { + //console.log('file'); - if (value.directory) { - //console.log('dir'); + value.Album = (!value.Album ? mpcp.settings.unknown : + value.Album); + value.Artist = (!value.Artist ? mpcp.settings.unknown : + value.Artist); + stripFile = mpcp.utils.stripSlash(value.file); + value.Title = (!value.Title ? stripFile : value.Title); - strippedDir = mpcp.utils.stripSlash(value.directory); + html = '' + tableStart + value.Title + tableEnd + '' + tableStart + value.Artist + tableEnd + '' + tableStart + value.Album + tableEnd + '' + mpcp.utils.toMMSS(value.Time) + ''; + } - html = ' ' + tableStart + strippedDir + tableEnd + ''; - } + return html; + }, - return html; - }, + // update the song positions, instead of reloading the whole browser + updatePosition: function (callback) { + console.log('updatePosition'); + var tr = $('.song-list tbody').children('.file'); - getHtmlFiles: function (value) { - var tableStart = '
    ', - tableEnd = '
    ', - stripFile = '', - html = ''; + komponist.currentsong(function (err, song) { + if (err) { + window.dispatchEvent(new CustomEvent("MPCperowserChanged")); - if (value.file) { - //console.log('file'); + if (callback) callback(); - value.Album = (!value.Album ? mpcp.settings.unknown : - value.Album); - value.Artist = (!value.Artist ? mpcp.settings.unknown : - value.Artist); - stripFile = mpcp.utils.stripSlash(value.file); - value.Title = (!value.Title ? stripFile : value.Title); + return console.log(err); + } + + if ($.isEmptyObject(song)) + mpcp.player.setCurrent(null); + else + mpcp.player.setCurrent(song); + + if (mpcp.player.current) { + document.getElementById('title-pos').innerHTML = + (mpcp.player.current.Pos + 1) + '. '; + } + }); + + var element, fileid, icon, index; + + for (var i = 0; i < tr.length; ++i) { + element = tr[i]; + + fileid = $(element).data().fileid; + icon = ''; + index = mpcp.playlist.list.files.indexOf(fileid); + + if (index != -1) { + icon = (parseInt(mpcp.playlist.list.positions[index]) + 1) + + '.'; + element.firstChild.innerHTML = icon; + } else { + icon = '' + + ''; + element.firstChild.innerHTML = icon; + } + } - html = '' + tableStart + value.Title + tableEnd + '' + tableStart + value.Artist + tableEnd + '' + tableStart + value.Album + tableEnd + '' + mpcp.utils.toMMSS(value.Time) + ''; - } + window.dispatchEvent(new CustomEvent("MPCperowserChanged")); - return html; - }, + if (callback) callback(); + }, - // update the song positions, instead of reloading the whole browser - updatePosition: function (callback) { - console.log('updatePosition'); - var tr = $('.song-list tbody').children('.file'); + updateLocal: function (callback) { + console.log('update local browser'); - komponist.currentsong(function (err, song) { - if (err) { - window.dispatchEvent(new CustomEvent("MPCperowserChanged")); + // this.local* always has a length of 1, but may have an empty + // object. This fixes removeal of "Empty directory" + if (this.localFolders.length <= 1 && + this.localFiles.length <= 1) { + if ((this.localFolders[0] && Object.getOwnPropertyNames( + this.localFolders[0]).length <= 0) && + (this.localFiles[0] && Object.getOwnPropertyNames( + this.localFiles[0]).length <= 0)) { + if (callback) callback(); + return; + } else if (this.localFolders.length <= 0 && + this.localFiles.length <= 0) { + if (callback) callback(); + return; + } + } - if (callback) callback(); + document.getElementById(this.tbodyid).innerHTML = ''; - return console.log(err); - } + var start = 0, + end = this.localFolders.length + this.localFiles.length, + html = ''; - if ($.isEmptyObject(song)) - mpcp.player.setCurrent(null); - else - mpcp.player.setCurrent(song); + if (mpcp.pages.enabledBrowser) { + start = (mpcp.pages.currentBrowser - 1) * mpcp.pages.maxBrowser; + end = (((mpcp.pages.currentBrowser - 1) * mpcp.pages.maxBrowser) + + mpcp.pages.maxBrowser) - 1; + } - if (mpcp.player.current) { - document.getElementById('title-pos').innerHTML = - (mpcp.player.current.Pos + 1) + '. '; - } - }); + var current = start, + i; + //console.log(start); + //console.log(end); - var element, fileid, icon, index; + for (i = current; i < this.localFolders.length; ++i) { + if (current > end || current > mpcp.browser.localFolders.length) + break; - for (var i = 0; i < tr.length; ++i) { - element = tr[i]; + html += mpcp.browser.localFolders[i]; + current++; + } - fileid = $(element).data().fileid; - icon = ''; - index = mpcp.playlist.list.files.indexOf(fileid); + i = current - this.localFolders.length; + //console.log(current); + //console.log(i); - if (index != -1) { - icon = (parseInt(mpcp.playlist.list.positions[index]) + 1) + - '.'; - element.firstChild.innerHTML = icon; - } else { - icon = '' + - ''; - element.firstChild.innerHTML = icon; - } - } + for (; i < this.localFiles.length; ++i) { + if (current > end || current - mpcp.browser.localFolders.length > + mpcp.browser.localFiles.length) + break; - window.dispatchEvent(new CustomEvent("MPCperowserChanged")); + html += mpcp.browser.localFiles[i]; + current++; + } - if (callback) callback(); - }, - - updateLocal: function (callback) { - console.log('update local browser'); - - // this.local* always has a length of 1, but may have an empty - // object. This fixes removeal of "Empty directory" - if (this.localFolders.length <= 1 && - this.localFiles.length <= 1) { - if ((this.localFolders[0] && Object.getOwnPropertyNames( - this.localFolders[0]).length <= 0) && - (this.localFiles[0] && Object.getOwnPropertyNames( - this.localFiles[0]).length <= 0)) { - if (callback) callback(); - return; - } else if (this.localFolders.length <= 0 && - this.localFiles.length <= 0) { - if (callback) callback(); - return; - } + document.getElementById(this.tbodyid).innerHTML = html; + //console.log(current); + mpcp.pages.update('browser'); + this.updatePosition(callback); + }, + + // show song information to the user + getSongInfo: function (file, callback) { + komponist.find('file', file, function (err, value) { + mpcp.utils.parseSongInfo(err, value[0], callback); + }); + }, + + // add all songs in this.current to playlist + addAll: function (callback) { + console.log('add all songs from ' + this.current); + + if (this.searching) { + komponist.search('any', this.searchTerm, + function (err, files) { + if (err) { + console.log(err); + if (callback) callback(); + return; } - document.getElementById(this.tbodyid).innerHTML = ''; - - var start = 0, - end = this.localFolders.length + this.localFiles.length, - html = ''; - - if (mpcp.pages.enabledBrowser) { - start = (mpcp.pages.currentBrowser - 1) * mpcp.pages.maxBrowser; - end = (((mpcp.pages.currentBrowser - 1) * mpcp.pages.maxBrowser) + - mpcp.pages.maxBrowser) - 1; + if ($.isEmptyObject(files[0])) { + console.log('No songs found'); + if (callback) callback(); + return; } - var current = start, - i; - //console.log(start); - //console.log(end); - - for (i = current; i < this.localFolders.length; ++i) { - if (current > end || current > mpcp.browser.localFolders.length) - break; + if (mpcp.pe.current !== null) + mpcp.pe.addSong(files, null, callback); + else { + mpcp.playlist.addCallbackUpdate(callback); - html += mpcp.browser.localFolders[i]; - current++; + $(files).each(function (item, value) { + komponist.add(value.file, function (err) { + if (err) console.log(err); + }); + }); } + }); + } else { + if (mpcp.pe.current) { + mpcp.utils.getAllInfo(this.current, function (files) { + mpcp.pe.addSong(files, null, callback); + }); + } else { + komponist.lsinfo(this.current, function (err, files) { + //console.log(files); - i = current - this.localFolders.length; - //console.log(current); - //console.log(i); - - for (; i < this.localFiles.length; ++i) { - if (current > end || current - mpcp.browser.localFolders.length > - mpcp.browser.localFiles.length) - break; - - html += mpcp.browser.localFiles[i]; - current++; - } + if (err) { + console.log(err); + if (callback) callback(); + return; + } - document.getElementById(this.tbodyid).innerHTML = html; - //console.log(current); - mpcp.pages.update('browser'); - this.updatePosition(callback); - }, + files = mpcp.utils.toArray(files); - // show song information to the user - getSongInfo: function (file, callback) { - komponist.find('file', file, function (err, value) { - mpcp.utils.parseSongInfo(err, value[0], callback); - }); - }, - - // add all songs in this.current to playlist - addAll: function (callback) { - console.log('add all songs from ' + this.current); - - if (this.searching) { - komponist.search('any', this.searchTerm, - function (err, files) { - if (err) { - console.log(err); - if (callback) callback(); - return; - } - - if ($.isEmptyObject(files[0])) { - console.log('No songs found'); - if (callback) callback(); - return; - } - - if (mpcp.pe.current !== null) - mpcp.pe.addSong(files, null, callback); - else { - mpcp.playlist.addCallbackUpdate(callback); - - $(files).each(function (item, value) { - komponist.add(value.file, function (err) { - if (err) console.log(err); - }); - }); - } - }); - } else { - if (mpcp.pe.current) { - mpcp.utils.getAllInfo(this.current, function (files) { - mpcp.pe.addSong(files, null, callback); - }); - } else { - komponist.lsinfo(this.current, function (err, files) { - //console.log(files); - - if (err) { - console.log(err); - if (callback) callback(); - return; - } - - files = mpcp.utils.toArray(files); - - if (!files.length) { - console.log('Empty directory'); - if (callback) callback(); - return; - } - - mpcp.playlist.addCallbackUpdate(callback); - - $(files).each(function (item, value) { - if (value.directory) { - komponist.add(value.directory, function (err) { - if (err) console.log(err); - }); - } - - if (value.file) { - komponist.add(value.file, function (err) { - if (err) console.log(err); - }); - } - }); - }); - } - } - }, - - // used when the user manually opens the browser - open: function (callback) { - mpcp.settings.saveBrowser('browser'); - this.addToHistory(); - this.update(null, false, callback); - }, - - show: function () { - if (!this.hidden) return; - this.hidden = false; - mpcp.utils.buttonSelect("#open-file-browser", "#browser-selection"); - document.getElementById('browser').style.display = 'flex'; - }, - - hide: function () { - document.getElementById('browser').style.display = 'none'; - this.hidden = true; - mpcp.utils.clearSelected(mpcp.browser); - }, - - addMulti: function (to, callback, dragging) { - console.log('browser add multi'); - mpcp.utils.toArraySelected(mpcp.browser); - var i, tr, dir, file, j = 0; - - function addFile(file) { - mpcp.playlist.addSong(file, to, dontScroll, function () { - if (++j == mpcp.browser.selected.length && callback) - callback(); - }); - } + if (!files.length) { + console.log('Empty directory'); + if (callback) callback(); + return; + } - function addDir(dir) { - mpcp.playlist.addDir(dir, to, dontScroll, function () { - if (++j == mpcp.browser.selected.length && callback) - callback(); - }); - } + mpcp.playlist.addCallbackUpdate(callback); - // used for the context menu - if (mpcp.pe.current && !dragging) { - var arr = []; - - for (i = 0; i < this.selected.length; ++i) { - tr = this.selected[i]; - if (tr.classList.contains('file')) { - file = tr.dataset.fileid; - //console.log('adding ----- ' + file); - arr.push(['id', file]); - } else if (tr.classList.contains('directory')) { - dir = tr.dataset.dirid; - arr.push(['dir', dir]); - } + $(files).each(function (item, value) { + if (value.directory) { + komponist.add(value.directory, function (err) { + if (err) console.log(err); + }); } - mpcp.pe.addArr(arr, to, callback); - } else { - var dontScroll = false; - // dont scroll if drag and drop ("to" would not be null) - if (to && mpcp.player.current && to != mpcp.player.current.Pos + 1) - dontScroll = true; - - // reverse because not incrementing to variable because - // scrolling to center will be overridden in addSong - this.selected.reverse(); - for (i = 0; i < this.selected.length; ++i) { - tr = this.selected[i]; - if (tr.classList.contains('file')) { - file = tr.dataset.fileid; - addFile(file); - } else if (tr.classList.contains('directory')) { - dir = tr.dataset.dirid; - addDir(dir); - } else { - if (++j == this.selected.length && callback) - callback(); - } + if (value.file) { + komponist.add(value.file, function (err) { + if (err) console.log(err); + }); } - } - - mpcp.utils.clearSelected(mpcp.browser); - }, - - addExternal: function (file, to, callback) { - if (this.selected.length) - this.addMulti(to, callback); - else if (mpcp.pe.current) - mpcp.pe.addid(file, to, callback); - else - mpcp.playlist.addSong(file, to, false, callback); - }, - - addExternalDir: function (dir, to, callback) { - if (this.selected.length) - this.addMulti(to, callback); - else if (mpcp.pe.current) - mpcp.pe.add(dir, null, callback); - else - mpcp.playlist.add(dir, to, callback); - }, - - initEvents: function () { - $('#update').click(function () { - console.log('update database'); - // set to false until broadcast updates everyone - // for now, the other clients will still receive multiple updates - mpcp.browser.doUpdate = false; - - $('#update .glyphicon').addClass('spinning'); - - komponist.update(function (err) { - // check if this is satus.updating_db is undefined - // if so, it is done updating (hopefully) - if (err) return console.log(err); - - var updateInterval = setInterval(function () { - console.log('checking if update db is done...'); - - komponist.status(function (err, status) { - if (err) { - $('#update .glyphicon')[0].classList.remove('spinning'); - clearInterval(updateInterval); - mpcp.lazyToast.error( - 'Error getting the status from MPD!'); - - return console.log(err); - } - - // incase job id is 0/1, just check if undefined - if (status.updating_db === undefined) { - // stop interval and send update-browser - // to everyone - clearInterval(updateInterval); - $('#update .glyphicon')[0].classList.remove('spinning'); - mpcp.lazyToast.info( - 'Music library updated!', 'Library'); - - socket.send(JSON.stringify( - {'type': 'update-browser'}), - function (err) { - if (err) console.log(err); - }); - } - }); - }, 500); - }); - }); - - $(document).on('click', '#home', function () { - console.log('home'); - mpcp.browser.update('/'); - }); - - $(document).on('click', '.song-add', function () { - var file = $(this).parent().parent().data().fileid; - mpcp.browser.addExternal(file); - }); - - $(document).on('dblclick', 'tr.file', function () { - var file = $(this).data().fileid; - mpcp.browser.addExternal(file); - }); - - $(document).on('dblclick', '.song-list tr.directory', function () { - var dir = $(this).data().dirid; - mpcp.browser.update(dir); - }); - - $(document).on('click', '.song-list .folder-open', function () { - var dir = $(this).parent().parent().data().dirid; - mpcp.browser.update(dir); + }); }); + } + } + }, + + // used when the user manually opens the browser + open: function (callback) { + mpcp.settings.saveBrowser('browser'); + this.addToHistory(); + this.update(null, false, callback); + }, + + show: function () { + if (!this.hidden) return; + this.hidden = false; + mpcp.utils.buttonSelect("#open-file-browser", "#browser-selection"); + document.getElementById('browser').style.display = 'flex'; + }, + + hide: function () { + document.getElementById('browser').style.display = 'none'; + this.hidden = true; + mpcp.utils.clearSelected(mpcp.browser); + }, + + addMulti: function (to, callback, dragging) { + console.log('browser add multi'); + mpcp.utils.toArraySelected(mpcp.browser); + var i, tr, dir, file, j = 0; + + function addFile(file) { + mpcp.playlist.addSong(file, to, dontScroll, function () { + if (++j == mpcp.browser.selected.length && callback) + callback(); + }); + } - $(document).on('click', '.dir-add', function () { - var dir = $(this).parent().parent().data().dirid; - mpcp.browser.addExternalDir(dir); - }); + function addDir(dir) { + mpcp.playlist.addDir(dir, to, dontScroll, function () { + if (++j == mpcp.browser.selected.length && callback) + callback(); + }); + } - $(document).on('click', '.loc-dir', function () { - var file = $(this).data().dirid; - //console.log(file); - mpcp.browser.update(file); - }); + // used for the context menu + if (mpcp.pe.current && !dragging) { + var arr = []; + + for (i = 0; i < this.selected.length; ++i) { + tr = this.selected[i]; + if (tr.classList.contains('file')) { + file = tr.dataset.fileid; + //console.log('adding ----- ' + file); + arr.push(['id', file]); + } else if (tr.classList.contains('directory')) { + dir = tr.dataset.dirid; + arr.push(['dir', dir]); + } + } + + mpcp.pe.addArr(arr, to, callback); + } else { + var dontScroll = false; + // dont scroll if drag and drop ("to" would not be null) + if (to && mpcp.player.current && to != mpcp.player.current.Pos + 1) + dontScroll = true; + + // reverse because not incrementing to variable because + // scrolling to center will be overridden in addSong + this.selected.reverse(); + for (i = 0; i < this.selected.length; ++i) { + tr = this.selected[i]; + if (tr.classList.contains('file')) { + file = tr.dataset.fileid; + addFile(file); + } else if (tr.classList.contains('directory')) { + dir = tr.dataset.dirid; + addDir(dir); + } else { + if (++j == this.selected.length && callback) + callback(); + } + } + } - // add all songs from mpcp.browser.current - $('#add-all').click(function () { - mpcp.browser.addAll(); - }); + mpcp.utils.clearSelected(mpcp.browser); + }, + + addExternal: function (file, to, callback) { + if (this.selected.length) + this.addMulti(to, callback); + else if (mpcp.pe.current) + mpcp.pe.addid(file, to, callback); + else + mpcp.playlist.addSong(file, to, false, callback); + }, + + addExternalDir: function (dir, to, callback) { + if (this.selected.length) + this.addMulti(to, callback); + else if (mpcp.pe.current) + mpcp.pe.add(dir, null, callback); + else + mpcp.playlist.add(dir, to, callback); + }, + + initEvents: function () { + $('#update').click(function () { + console.log('update database'); + // set to false until broadcast updates everyone + // for now, the other clients will still receive multiple updates + mpcp.browser.doUpdate = false; + + $('#update .glyphicon').addClass('spinning'); + + komponist.update(function (err) { + // check if this is satus.updating_db is undefined + // if so, it is done updating (hopefully) + if (err) return console.log(err); + + var updateInterval = setInterval(function () { + console.log('checking if update db is done...'); + + komponist.status(function (err, status) { + if (err) { + $('#update .glyphicon')[0].classList.remove('spinning'); + clearInterval(updateInterval); + mpcp.lazyToast.error( + 'Error getting the status from MPD!'); - $('#open-file-browser').click(function () { - mpcp.browser.open(); - }); + return console.log(err); + } - mpcp.tableHeader(this.tableid, 'MPCperowserChanged'); - - // this cannot be part of .song-list because of a bug with sortColumn - // (overwrites contens from one tabe to other tables). - mpcp.utils.tableSort(this.table, this.table + '-col-number', - 1, 'number'); - mpcp.utils.tableSort(this.table, this.table + '-col-title', - 2, 'string'); - mpcp.utils.tableSort(this.table, this.table + '-col-artist', - 3, 'string'); - mpcp.utils.tableSort(this.table, this.table + '-col-album', - 4, 'string'); - mpcp.utils.tableSort(this.table, this.table + '-col-time', - 5, '00:00'); - - mpcp.utils.multiSelect(mpcp.browser, ['song-add', 'dir-add']); - } + // incase job id is 0/1, just check if undefined + if (status.updating_db === undefined) { + // stop interval and send update-browser + // to everyone + clearInterval(updateInterval); + $('#update .glyphicon')[0].classList.remove('spinning'); + mpcp.lazyToast.info( + 'Music library updated!', 'Library'); + + socket.send(JSON.stringify( + {'type': 'update-browser'}), + function (err) { + if (err) console.log(err); + }); + } + }); + }, 500); + }); + }); + + $(document).on('click', '#home', function () { + console.log('home'); + mpcp.browser.update('/'); + }); + + $(document).on('click', '.song-add', function () { + var file = $(this).parent().parent().data().fileid; + mpcp.browser.addExternal(file); + }); + + $(document).on('dblclick', 'tr.file', function () { + var file = $(this).data().fileid; + mpcp.browser.addExternal(file); + }); + + $(document).on('dblclick', '.song-list tr.directory', function () { + var dir = $(this).data().dirid; + mpcp.browser.update(dir); + }); + + $(document).on('click', '.song-list .folder-open', function () { + var dir = $(this).parent().parent().data().dirid; + mpcp.browser.update(dir); + }); + + $(document).on('click', '.dir-add', function () { + var dir = $(this).parent().parent().data().dirid; + mpcp.browser.addExternalDir(dir); + }); + + $(document).on('click', '.loc-dir', function () { + var file = $(this).data().dirid; + //console.log(file); + mpcp.browser.update(file); + }); + + // add all songs from mpcp.browser.current + $('#add-all').click(function () { + mpcp.browser.addAll(); + }); + + $('#open-file-browser').click(function () { + mpcp.browser.open(); + }); + + mpcp.tableHeader(this.tableid, 'MPCperowserChanged'); + + // this cannot be part of .song-list because of a bug with sortColumn + // (overwrites contens from one tabe to other tables). + mpcp.utils.tableSort(this.table, this.table + '-col-number', + 1, 'number'); + mpcp.utils.tableSort(this.table, this.table + '-col-title', + 2, 'string'); + mpcp.utils.tableSort(this.table, this.table + '-col-artist', + 3, 'string'); + mpcp.utils.tableSort(this.table, this.table + '-col-album', + 4, 'string'); + mpcp.utils.tableSort(this.table, this.table + '-col-time', + 5, '00:00'); + + mpcp.utils.multiSelect(mpcp.browser, ['song-add', 'dir-add']); + } }; }; diff --git a/src/contextmenu.js b/src/contextmenu.js index 6fb373b..1d49d74 100644 --- a/src/contextmenu.js +++ b/src/contextmenu.js @@ -2,310 +2,310 @@ module.exports = function (mpcp) { // context menu function contextResponse(key, table, tr) { - console.log(key); + console.log(key); - switch(key) { - // playlist - case 'mttPlaylist': - mpcp.playlist.moveToTop(tr.data().fileid); - break; - case 'mtbPlaylist': - mpcp.playlist.moveToBottom(tr.data().fileid); - break; - case 'mtc': - mpcp.playlist.moveToCurrent(tr); - break; - case 'remPlaylist': - mpcp.playlist.remove(tr); - break; - case 'play': - mpcp.playlist.play(tr); - break; - case 'setPriority': - document.getElementById('priority-form').dataset.fileid = tr.data().fileid; - document.getElementById('priority').value = tr.data().prio; - $('#set-priority-modal').modal('show'); - break; - // playlist editor - case 'mttpe': - mpcp.pe.moveToTop(tr); - break; - case 'mtbpe': - mpcp.pe.moveToBottom(tr); - break; - case 'rempe': - mpcp.pe.removeSong(tr); - break; - case 'infoBrowser': - mpcp.browser.getSongInfo(tr.data().fileid); - break; - case 'infoPlaylist': - mpcp.playlist.getSongInfo(tr.data().file); - break; - } + switch(key) { + // playlist + case 'mttPlaylist': + mpcp.playlist.moveToTop(tr.data().fileid); + break; + case 'mtbPlaylist': + mpcp.playlist.moveToBottom(tr.data().fileid); + break; + case 'mtc': + mpcp.playlist.moveToCurrent(tr); + break; + case 'remPlaylist': + mpcp.playlist.remove(tr); + break; + case 'play': + mpcp.playlist.play(tr); + break; + case 'setPriority': + document.getElementById('priority-form').dataset.fileid = tr.data().fileid; + document.getElementById('priority').value = tr.data().prio; + $('#set-priority-modal').modal('show'); + break; + // playlist editor + case 'mttpe': + mpcp.pe.moveToTop(tr); + break; + case 'mtbpe': + mpcp.pe.moveToBottom(tr); + break; + case 'rempe': + mpcp.pe.removeSong(tr); + break; + case 'infoBrowser': + mpcp.browser.getSongInfo(tr.data().fileid); + break; + case 'infoPlaylist': + mpcp.playlist.getSongInfo(tr.data().file); + break; + } - // browser - // directory - if ($(tr)[0].classList.contains('directory')) { - var dirid = tr.data().dirid; + // browser + // directory + if ($(tr)[0].classList.contains('directory')) { + var dirid = tr.data().dirid; - switch(key) { - case 'attPlaylist': - if (mpcp.browser.selected.length) { - mpcp.browser.addMulti(0); - } else { - mpcp.playlist.addDir(dirid, 0); - } - break; - case 'attpe': - if (mpcp.browser.selected.length) { - mpcp.browser.addMulti(0); - } else { - mpcp.pe.add(dirid, 0); - } - break; - case 'atbPlaylist': - if (mpcp.browser.selected.length) { - mpcp.browser.addMulti(); - } else { - mpcp.playlist.addDir(dirid); - } - break; - case 'atbpe': - if (mpcp.browser.selected.length) { - mpcp.browser.addMulti(); - } else { - mpcp.pe.add(dirid); - } - break; - case 'atc': - mpcp.playlist.addToCurrent(dirid, 'dir'); - break; + switch(key) { + case 'attPlaylist': + if (mpcp.browser.selected.length) { + mpcp.browser.addMulti(0); + } else { + mpcp.playlist.addDir(dirid, 0); + } + break; + case 'attpe': + if (mpcp.browser.selected.length) { + mpcp.browser.addMulti(0); + } else { + mpcp.pe.add(dirid, 0); + } + break; + case 'atbPlaylist': + if (mpcp.browser.selected.length) { + mpcp.browser.addMulti(); + } else { + mpcp.playlist.addDir(dirid); + } + break; + case 'atbpe': + if (mpcp.browser.selected.length) { + mpcp.browser.addMulti(); + } else { + mpcp.pe.add(dirid); } + break; + case 'atc': + mpcp.playlist.addToCurrent(dirid, 'dir'); + break; } + } - // file - if ($(tr)[0].classList.contains('file')) { - var fileid = tr.data().fileid; + // file + if ($(tr)[0].classList.contains('file')) { + var fileid = tr.data().fileid; - switch(key) { - case 'attPlaylist': - case 'attpe': - mpcp.browser.addExternal(fileid, 0); - break; - case 'atbPlaylist': - case 'atbpe': - mpcp.browser.addExternal(fileid); - break; - case 'atc': - mpcp.browser.addExternal(fileid, mpcp.player.current.Pos + 1); - break; - } + switch(key) { + case 'attPlaylist': + case 'attpe': + mpcp.browser.addExternal(fileid, 0); + break; + case 'atbPlaylist': + case 'atbpe': + mpcp.browser.addExternal(fileid); + break; + case 'atc': + mpcp.browser.addExternal(fileid, mpcp.player.current.Pos + 1); + break; } + } - // library - if ($(tr)[0].classList.contains('artist') || $(tr)[0].classList.contains('album')) { - var artist = $(tr).data().artist, - album = $(tr).data().album; + // library + if ($(tr)[0].classList.contains('artist') || $(tr)[0].classList.contains('album')) { + var artist = $(tr).data().artist, + album = $(tr).data().album; - switch(key) { - case 'attPlaylist': - case 'attpe': - if ($(tr)[0].classList.contains('artist')) - mpcp.library.addExternal( - mpcp.libraryArtists, artist, album, 0, false); - else if ($(tr)[0].classList.contains('album')) - mpcp.library.addExternal( - mpcp.libraryAlbums, artist, album, 0, false); - break; - case 'atbPlaylist': - case 'atbpe': - if ($(tr)[0].classList.contains('artist')) - mpcp.library.addExternal( - mpcp.libraryArtists, artist, album, undefined, false); - else if ($(tr)[0].classList.contains('album')) - mpcp.library.addExternal( - mpcp.libraryAlbums, artist, album, undefined, false); - break; - case 'atc': - if ($(tr)[0].classList.contains('artist') && - mpcp.libraryArtists.selected.length) { - mpcp.library.addExternal(mpcp.libraryArtists, artist, - album, mpcp.player.current.Pos + 1, false); - } else if ($(tr)[0].classList.contains('album') && - mpcp.libraryAlbums.selected.length) { - mpcp.library.addExternal(mpcp.libraryAlbums, artist, album, - mpcp.player.current.Pos + 1, false); - } else { - mpcp.library.getSongsFromAlbum(artist, album, - function (files) { - for (var i = 0; i < files.length; ++i) { - mpcp.playlist.addToCurrent(files[i].file, 'file'); - } - }); - } - break; + switch(key) { + case 'attPlaylist': + case 'attpe': + if ($(tr)[0].classList.contains('artist')) + mpcp.library.addExternal( + mpcp.libraryArtists, artist, album, 0, false); + else if ($(tr)[0].classList.contains('album')) + mpcp.library.addExternal( + mpcp.libraryAlbums, artist, album, 0, false); + break; + case 'atbPlaylist': + case 'atbpe': + if ($(tr)[0].classList.contains('artist')) + mpcp.library.addExternal( + mpcp.libraryArtists, artist, album, undefined, false); + else if ($(tr)[0].classList.contains('album')) + mpcp.library.addExternal( + mpcp.libraryAlbums, artist, album, undefined, false); + break; + case 'atc': + if ($(tr)[0].classList.contains('artist') && + mpcp.libraryArtists.selected.length) { + mpcp.library.addExternal(mpcp.libraryArtists, artist, + album, mpcp.player.current.Pos + 1, false); + } else if ($(tr)[0].classList.contains('album') && + mpcp.libraryAlbums.selected.length) { + mpcp.library.addExternal(mpcp.libraryAlbums, artist, album, + mpcp.player.current.Pos + 1, false); + } else { + mpcp.library.getSongsFromAlbum(artist, album, + function (files) { + for (var i = 0; i < files.length; ++i) { + mpcp.playlist.addToCurrent(files[i].file, 'file'); + } + }); } + break; } + } } // enable the custom context menu $.contextMenu({ - selector: '.context-menu', - // above pe - zIndex: 1003, - build: function ($trigger, e) { - var table = $trigger.parent().parent(), - items = {}, - // can't get the title of contextmenu to work, so I'm using a menu - // item as a title. - title = $trigger.attr('title'); + selector: '.context-menu', + // above pe + zIndex: 1003, + build: function ($trigger, e) { + var table = $trigger.parent().parent(), + items = {}, + // can't get the title of contextmenu to work, so I'm using a menu + // item as a title. + title = $trigger.attr('title'); - switch(table.attr('id')) { - case mpcp.playlist.tableid: - mpcp.utils.checkSelected(e.currentTarget, mpcp.playlist); - break; - case mpcp.browser.tableid: - mpcp.utils.checkSelected(e.currentTarget, mpcp.browser); - break; - case mpcp.pe.tableid: - mpcp.utils.checkSelected(e.currentTarget, mpcp.pe); - break; - case mpcp.libraryArtists.tableid: - mpcp.utils.checkSelected(e.currentTarget, mpcp.libraryArtists); - break; - case mpcp.libraryAlbums.tableid: - mpcp.utils.checkSelected(e.currentTarget, mpcp.libraryAlbums); - break; - case mpcp.librarySongs.tableid: - mpcp.utils.checkSelected(e.currentTarget, mpcp.librarySongs); - break; - } - - if (table[0].classList.contains('song-list')) - title = $trigger.children('td:nth-child(2)').attr('title'); + switch(table.attr('id')) { + case mpcp.playlist.tableid: + mpcp.utils.checkSelected(e.currentTarget, mpcp.playlist); + break; + case mpcp.browser.tableid: + mpcp.utils.checkSelected(e.currentTarget, mpcp.browser); + break; + case mpcp.pe.tableid: + mpcp.utils.checkSelected(e.currentTarget, mpcp.pe); + break; + case mpcp.libraryArtists.tableid: + mpcp.utils.checkSelected(e.currentTarget, mpcp.libraryArtists); + break; + case mpcp.libraryAlbums.tableid: + mpcp.utils.checkSelected(e.currentTarget, mpcp.libraryAlbums); + break; + case mpcp.librarySongs.tableid: + mpcp.utils.checkSelected(e.currentTarget, mpcp.librarySongs); + break; + } - //console.log($trigger); - //console.log(title); + if (table[0].classList.contains('song-list')) + title = $trigger.children('td:nth-child(2)').attr('title'); - // only apply when playlist editor is active and not right clicking - // the playlist - if (mpcp.pe.current && table.attr('id') != 'playlist-song-list') { - // browser - if (table[0].classList.contains('song-list')) { - items = { - 'title': {name: title}, - 'attpe': {name: 'Add to top of playlist editor'}, - 'atbpe': {name: 'Add to bottom of playlist editor'} - }; + //console.log($trigger); + //console.log(title); - if (!$($trigger)[0].classList.contains('directory')) - items.infoBrowser = {name: 'Song information'}; - // only on pe - } else if (table.attr('id') == 'pe-song-list') { - items = { - 'title': {name: title}, - 'mttpe': {name: 'Move to top of playlist editor'}, - 'mtbpe': {name: 'Move to bottom of playlist editor'}, - 'rempe': {name: 'Remove'}, - 'infoBrowser': {name: 'Song information'} - }; - } else if (table[0].classList.contains('library-list-context')) { - items = { - 'title': {name: title}, - 'attpe': {name: 'Add to top of playlist editor'}, - 'atbpe': {name: 'Add to bottom of playlist editor'} - }; - } else { - items = { - 'temp': {name: 'Context menu not implemented yet for pe'} - }; - } - } - // only on playlist - else if (table.attr('id') == 'playlist-song-list') { - // song is playing - if (mpcp.player.current) - items = { - 'title': {name: title}, - 'play': {name: 'Play song'}, - 'mttPlaylist': {name: 'Move to top of playlist'}, - 'mtc': {name: 'Move after current playing song'}, - 'mtbPlaylist': {name: 'Move to bottom of playlist'}, - 'setPriority': {name: 'Set song priority'}, - 'remPlaylist': {name: 'Remove'}, - 'infoPlaylist': {name: 'Song information'} - }; - // song is not playing - else if (!mpcp.player.current) - items = { - 'title': {name: title}, - 'play': {name: 'Play song'}, - 'mttPlaylist': {name: 'Move to top of playlist'}, - 'mtbPlaylist': {name: 'Move to bottom of playlist'}, - 'setPriority': {name: 'Set song priority'}, - 'remPlaylist': {name: 'Remove'}, - 'infoPlaylist': {name: 'Song information'} - }; - } - // only on browser - else if (table[0].classList.contains('song-list')) { - // song is playing - if (mpcp.player.current) - items = { - 'title': {name: title}, - 'attPlaylist': {name: 'Add to top of playlist'}, - 'atc': {name: 'Add after current playing song'}, - 'atbPlaylist': {name: 'Add to bottom of playlist'} - }; - // song is not playing - else if (!mpcp.player.current) - items = { - 'title': {name: title}, - 'attPlaylist': {name: 'Add to top of playlist'}, - 'atbPlaylist': {name: 'Add to bottom of playlist'} - }; - - if (!$($trigger)[0].classList.contains('directory')) - items.infoBrowser = {name: 'Song information'}; - // only on browser - } else if (table[0].classList.contains('library-list-context')) { - // song is playing - if (mpcp.player.current) - items = { - 'title': {name: title}, - 'attPlaylist': {name: 'Add to top of playlist'}, - 'atc': {name: 'Add after current playing song'}, - 'atbPlaylist': {name: 'Add to bottom of playlist'} - }; - // song is not playing - else if (!mpcp.player.current) - items = { - 'title': {name: title}, - 'attPlaylist': {name: 'Add to top of playlist'}, - 'atbPlaylist': {name: 'Add to bottom of playlist'} - }; + // only apply when playlist editor is active and not right clicking + // the playlist + if (mpcp.pe.current && table.attr('id') != 'playlist-song-list') { + // browser + if (table[0].classList.contains('song-list')) { + items = { + 'title': {name: title}, + 'attpe': {name: 'Add to top of playlist editor'}, + 'atbpe': {name: 'Add to bottom of playlist editor'} + }; - if (!($($trigger)[0].classList.contains('directory') || - $($trigger)[0].classList.contains('artist') || - $($trigger)[0].classList.contains('album'))) - items.infoBrowser = {name: 'Song information'}; - } else { - items = { - 'temp': {name: 'Context menu not implemented yet'} - }; - } + if (!$($trigger)[0].classList.contains('directory')) + items.infoBrowser = {name: 'Song information'}; + // only on pe + } else if (table.attr('id') == 'pe-song-list') { + items = { + 'title': {name: title}, + 'mttpe': {name: 'Move to top of playlist editor'}, + 'mtbpe': {name: 'Move to bottom of playlist editor'}, + 'rempe': {name: 'Remove'}, + 'infoBrowser': {name: 'Song information'} + }; + } else if (table[0].classList.contains('library-list-context')) { + items = { + 'title': {name: title}, + 'attpe': {name: 'Add to top of playlist editor'}, + 'atbpe': {name: 'Add to bottom of playlist editor'} + }; + } else { + items = { + 'temp': {name: 'Context menu not implemented yet for pe'} + }; + } + } + // only on playlist + else if (table.attr('id') == 'playlist-song-list') { + // song is playing + if (mpcp.player.current) + items = { + 'title': {name: title}, + 'play': {name: 'Play song'}, + 'mttPlaylist': {name: 'Move to top of playlist'}, + 'mtc': {name: 'Move after current playing song'}, + 'mtbPlaylist': {name: 'Move to bottom of playlist'}, + 'setPriority': {name: 'Set song priority'}, + 'remPlaylist': {name: 'Remove'}, + 'infoPlaylist': {name: 'Song information'} + }; + // song is not playing + else if (!mpcp.player.current) + items = { + 'title': {name: title}, + 'play': {name: 'Play song'}, + 'mttPlaylist': {name: 'Move to top of playlist'}, + 'mtbPlaylist': {name: 'Move to bottom of playlist'}, + 'setPriority': {name: 'Set song priority'}, + 'remPlaylist': {name: 'Remove'}, + 'infoPlaylist': {name: 'Song information'} + }; + } + // only on browser + else if (table[0].classList.contains('song-list')) { + // song is playing + if (mpcp.player.current) + items = { + 'title': {name: title}, + 'attPlaylist': {name: 'Add to top of playlist'}, + 'atc': {name: 'Add after current playing song'}, + 'atbPlaylist': {name: 'Add to bottom of playlist'} + }; + // song is not playing + else if (!mpcp.player.current) + items = { + 'title': {name: title}, + 'attPlaylist': {name: 'Add to top of playlist'}, + 'atbPlaylist': {name: 'Add to bottom of playlist'} + }; - return { - callback: function (key, options) { - //console.log("clicked: " + key); - //console.log(options); - var table = $(options.$trigger).parent().parent(); - contextResponse(key, table, options.$trigger); - }, - items: items + if (!$($trigger)[0].classList.contains('directory')) + items.infoBrowser = {name: 'Song information'}; + // only on browser + } else if (table[0].classList.contains('library-list-context')) { + // song is playing + if (mpcp.player.current) + items = { + 'title': {name: title}, + 'attPlaylist': {name: 'Add to top of playlist'}, + 'atc': {name: 'Add after current playing song'}, + 'atbPlaylist': {name: 'Add to bottom of playlist'} + }; + // song is not playing + else if (!mpcp.player.current) + items = { + 'title': {name: title}, + 'attPlaylist': {name: 'Add to top of playlist'}, + 'atbPlaylist': {name: 'Add to bottom of playlist'} }; + + if (!($($trigger)[0].classList.contains('directory') || + $($trigger)[0].classList.contains('artist') || + $($trigger)[0].classList.contains('album'))) + items.infoBrowser = {name: 'Song information'}; + } else { + items = { + 'temp': {name: 'Context menu not implemented yet'} + }; } + + return { + callback: function (key, options) { + //console.log("clicked: " + key); + //console.log(options); + var table = $(options.$trigger).parent().parent(); + contextResponse(key, table, options.$trigger); + }, + items: items + }; + } }); }; diff --git a/src/disconnect.js b/src/disconnect.js index eeb0d12..475cbe7 100644 --- a/src/disconnect.js +++ b/src/disconnect.js @@ -4,58 +4,58 @@ var host = window.document.location.host; // server disconnect handling return { - secInterval: null, - retryTimeout: null, - - callSocketClose: function () { - clearInterval(this.secInterval); - clearTimeout(this.retryTimeout); - - console.log('WebSocket disconnected'); - // timeout to not show if refreshing the page - setTimeout(function () { - var msg = 'The page will refresh when it comes back online.'; - mpcp.lazyToast.error(msg + '
    Retrying in 1 second(s)... ', 'Server Disconnected!', 5000, false); - }, 200); - - this.retryWebSocket(1); - }, - - retryWebSocket: function (attempts) { - mpcp.socket = new WebSocket('ws://' + host); - - mpcp.socket.onclose = function () { - var seconds = attempts; - - document.getElementById('count').innerHTML = seconds--; - - mpcp.disconnect.secInterval = setInterval(function () { - document.getElementById('count').innerHTML = seconds--; - }, 1000); - - mpcp.disconnect.retryTimeout = setTimeout(function () { - clearInterval(mpcp.disconnect.secInterval); - console.log('WebSocket closed, retrying... ' + attempts); - // Connection has closed so try to reconnect every few seconds - // max is 5 seconds - if (attempts < 5) ++attempts; - mpcp.disconnect.retryWebSocket(attempts); - }, attempts * 1000); - }; - - mpcp.socket.onopen = function (event) { - // refresh browser on restart, maybe there is a better way - // (komponist needs to reconnect to its socket as well) - console.log('WebSocket connected'); - window.location.reload(); - }; - }, - - initEvents: function () { - $(document).on('click', '.retry-server', function () { - mpcp.disconnect.callSocketClose(); - }); - } + secInterval: null, + retryTimeout: null, + + callSocketClose: function () { + clearInterval(this.secInterval); + clearTimeout(this.retryTimeout); + + console.log('WebSocket disconnected'); + // timeout to not show if refreshing the page + setTimeout(function () { + var msg = 'The page will refresh when it comes back online.'; + mpcp.lazyToast.error(msg + '
    Retrying in 1 second(s)... ', 'Server Disconnected!', 5000, false); + }, 200); + + this.retryWebSocket(1); + }, + + retryWebSocket: function (attempts) { + mpcp.socket = new WebSocket('ws://' + host); + + mpcp.socket.onclose = function () { + var seconds = attempts; + + document.getElementById('count').innerHTML = seconds--; + + mpcp.disconnect.secInterval = setInterval(function () { + document.getElementById('count').innerHTML = seconds--; + }, 1000); + + mpcp.disconnect.retryTimeout = setTimeout(function () { + clearInterval(mpcp.disconnect.secInterval); + console.log('WebSocket closed, retrying... ' + attempts); + // Connection has closed so try to reconnect every few seconds + // max is 5 seconds + if (attempts < 5) ++attempts; + mpcp.disconnect.retryWebSocket(attempts); + }, attempts * 1000); + }; + + mpcp.socket.onopen = function (event) { + // refresh browser on restart, maybe there is a better way + // (komponist needs to reconnect to its socket as well) + console.log('WebSocket connected'); + window.location.reload(); + }; + }, + + initEvents: function () { + $(document).on('click', '.retry-server', function () { + mpcp.disconnect.callSocketClose(); + }); + } }; }; diff --git a/src/downloader.js b/src/downloader.js index d07d586..f3fd116 100644 --- a/src/downloader.js +++ b/src/downloader.js @@ -2,253 +2,253 @@ module.exports = function (mpcp) { // the downloader return { - table: '#downloader-folder-list', - tbody: '#downloader-folder-list .append', - current: '/', - previous: '/', - localFolders: [], - rowSelect: null, - - init: function (location) { - // set the location to the last saved location, else set the server's - // default - var curLocation = localStorage.getItem('mpcp-downloader-location'); - - if (curLocation) location = curLocation; - - document.getElementById('downloader-location').value = location; - }, - - // download the video - download: function (url, location) { - if (url !== '') { - mpcp.socket.send(JSON.stringify({ - 'type': 'downloader-download', - 'url': url, - 'location': location - }), function (err) { - if (err) console.log(err); - }); - } - }, - - // check which dir the user is in. - // only update that dir - update: function (dir, callback) { - if (dir) mpcp.downloader.current = dir; - - //console.log('previous directory: ' + mpcp.browser.previous); - console.log('reloading directory: ' + mpcp.downloader.current); - - mpcp.downloader.updateBrowser(mpcp.downloader.current, callback); - - if (dir) mpcp.downloader.previous = dir; - }, - - // grab url from downloader element - downloadFromDownloader: function () { - var url = document.getElementById('downloader-url').value; - var location = document.getElementById('downloader-location').value; - this.download(url, location); - }, - - setStatus: function (str) { - document.getElementById('downloader-status').innerHTML = str; - }, - - updateLocal: function (callback) { - console.log('update downloader browser'); - - // this.local* always has a length of 1, but may have an empty - // object. This fixes removeal of "Empty directory" - if (this.localFolders.length <= 1) { - if (this.localFolders[0] && Object.getOwnPropertyNames( - this.localFolders[0]).length <= 0) { - if (callback) callback(); - return; - } - } - - document.querySelectorAll(this.table + ' .gen') - .forEach(e => e.parentNode.removeChild(e)); - - var start = 0, - end = this.localFolders.length, - html = ''; - - var current = start, - i; - //console.log(start); - //console.log(end); - - for (i = current; i < this.localFolders.length; ++i) { - if (current > end || current > mpcp.downloader.localFolders.length) - break; - - html += mpcp.downloader.localFolders[i]; - ++current; - } - - $(this.tbody)[0].innerHTML = html; - - //console.log(current); + table: '#downloader-folder-list', + tbody: '#downloader-folder-list .append', + current: '/', + previous: '/', + localFolders: [], + rowSelect: null, + + init: function (location) { + // set the location to the last saved location, else set the server's + // default + var curLocation = localStorage.getItem('mpcp-downloader-location'); + + if (curLocation) location = curLocation; + + document.getElementById('downloader-location').value = location; + }, + + // download the video + download: function (url, location) { + if (url !== '') { + mpcp.socket.send(JSON.stringify({ + 'type': 'downloader-download', + 'url': url, + 'location': location + }), function (err) { + if (err) console.log(err); + }); + } + }, + + // check which dir the user is in. + // only update that dir + update: function (dir, callback) { + if (dir) mpcp.downloader.current = dir; + + //console.log('previous directory: ' + mpcp.browser.previous); + console.log('reloading directory: ' + mpcp.downloader.current); + + mpcp.downloader.updateBrowser(mpcp.downloader.current, callback); + + if (dir) mpcp.downloader.previous = dir; + }, + + // grab url from downloader element + downloadFromDownloader: function () { + var url = document.getElementById('downloader-url').value; + var location = document.getElementById('downloader-location').value; + this.download(url, location); + }, + + setStatus: function (str) { + document.getElementById('downloader-status').innerHTML = str; + }, + + updateLocal: function (callback) { + console.log('update downloader browser'); + + // this.local* always has a length of 1, but may have an empty + // object. This fixes removeal of "Empty directory" + if (this.localFolders.length <= 1) { + if (this.localFolders[0] && Object.getOwnPropertyNames( + this.localFolders[0]).length <= 0) { + if (callback) callback(); + return; + } + } + + document.querySelectorAll(this.table + ' .gen') + .forEach(e => e.parentNode.removeChild(e)); + + var start = 0, + end = this.localFolders.length, + html = ''; + + var current = start, + i; + //console.log(start); + //console.log(end); + + for (i = current; i < this.localFolders.length; ++i) { + if (current > end || current > mpcp.downloader.localFolders.length) + break; + html += mpcp.downloader.localFolders[i]; + ++current; + } + + $(this.tbody)[0].innerHTML = html; + + //console.log(current); + + if (callback) callback(); + }, + + // shows directories. use '/' for root + updateBrowser: function (directory, callback) { + // location bar: + // split directory based on /'s + // create a list item for each dir split + document.querySelectorAll('#downloader-location-crumb .downloader-loc-dir') + .forEach(e => e.parentNode.removeChild(e)); + // toString incase of number only directories + var dirs = directory.toString().split('/'), + dirId = dirs[0], + html = '', + i; + + if (this.current != '/') + for (i = 0; i < dirs.length; ++i) { + html += '
  • ' + + dirs[i] + '
  • '; + dirId += '/' + dirs[i+1]; + } + + document.querySelector('#downloader-location-crumb ol') + .insertAdjacentHTML('beforeend', html); + + komponist.lsinfo(directory, function (err, files) { + //console.log(files); + if (err) return console.log(err); + + document.querySelectorAll(mpcp.downloader.table + ' .gen') + .forEach(e => e.parentNode.removeChild(e)); + mpcp.downloader.localFolders = []; + files = mpcp.utils.toArray(files); + + if (!files.length) { + html = '' + + 'Empty directory'; + $(mpcp.downloader.tbody)[0].innerHTML = html; + console.log('Empty directory'); if (callback) callback(); - }, - - // shows directories. use '/' for root - updateBrowser: function (directory, callback) { - // location bar: - // split directory based on /'s - // create a list item for each dir split - document.querySelectorAll('#downloader-location-crumb .downloader-loc-dir') - .forEach(e => e.parentNode.removeChild(e)); - // toString incase of number only directories - var dirs = directory.toString().split('/'), - dirId = dirs[0], - html = '', - i; - - if (this.current != '/') - for (i = 0; i < dirs.length; ++i) { - html += '
  • ' + - dirs[i] + '
  • '; - dirId += '/' + dirs[i+1]; - } - - document.querySelector('#downloader-location-crumb ol') - .insertAdjacentHTML('beforeend', html); - - komponist.lsinfo(directory, function (err, files) { - //console.log(files); - if (err) return console.log(err); - - document.querySelectorAll(mpcp.downloader.table + ' .gen') - .forEach(e => e.parentNode.removeChild(e)); - mpcp.downloader.localFolders = []; - files = mpcp.utils.toArray(files); - - if (!files.length) { - html = '' + - 'Empty directory'; - $(mpcp.downloader.tbody)[0].innerHTML = html; - console.log('Empty directory'); - if (callback) callback(); - return; - } - - var html = ''; - - // initialize html for browser - for (i = 0; i < files.length; ++i) { - html = mpcp.downloader.getHtmlFolders(files[i]); - - if (html !== '') mpcp.downloader.localFolders.push(html); - } - - mpcp.downloader.updateLocal(callback); - }); - }, - - getHtmlFolders: function (value) { - var tableStart = '
    ', - tableEnd = '
    ', - strippedDir = '', - html = ''; - - if (value.directory) { - //console.log('dir'); - - strippedDir = mpcp.utils.stripSlash(value.directory); - - html = ' ' + tableStart + strippedDir + tableEnd + ''; - } - - return html; - }, - - doneSelection: function () { - // "root" folder - var dir = ''; - - if (mpcp.downloader.rowSelect.getSelected()) { - dir = $(mpcp.downloader.rowSelect.getSelected()).data().dirid; - mpcp.downloader.rowSelect.deselect(); - } - - document.getElementById('downloader-location').value = dir; - mpcp.downloader.saveLocation(); - }, - - saveLocation: function () { - var location = document.getElementById('downloader-location').value; - localStorage.setItem('mpcp-downloader-location', location); - }, - - initEvents: function () { - $('#downloader-download').click(function () { - mpcp.downloader.downloadFromDownloader(); - }); - - // detect enter key - $('#downloader-url').keyup(function (e) { - if (e.keyCode == 13) - mpcp.downloader.downloadFromDownloader(); - }); - - // save location - $('#downloader-location').change(function () { - mpcp.downloader.setStatus(''); - mpcp.downloader.saveLocation(); - }); - - $('#downloader-url').change(function () { - mpcp.downloader.setStatus(''); - }); - - $(document).on('dblclick', mpcp.downloader.table + ' tr.downloader-directory', function () { - var dir = $(this).data().dirid; - mpcp.downloader.update(dir); - }); - - $(document).on('click', mpcp.downloader.table + ' .folder-open', function () { - var dir = $(this).parent().parent().data().dirid; - mpcp.downloader.update(dir); - }); - - $(document).on('click', '#downloader-browse', function () { - mpcp.downloader.setStatus(''); - document.getElementById('downloader-btn').parentElement.classList.add('keep-open'); - mpcp.downloader.update('/'); - }); - - $('#downloader-home').click(function () { - console.log('home'); - mpcp.downloader.update('/'); - }); - - $(document).on('click', '.downloader-loc-dir', function () { - var dir = $(this).data().dirid; - //console.log(dir); - mpcp.downloader.update(dir); - }); - - $(document).on('click', '#downloader-location-confirm', function () { - mpcp.downloader.doneSelection(); - }); - - mpcp.downloader.rowSelect = - mpcp.utils.rowSelect('.downloader-directory', 'bg-primary', '#downloader-wrap'); - - mpcp.downloader.rowSelect.on('enter', function (ele) { - var dir = $(ele).data().dirid; - mpcp.downloader.update(dir); - }); - - $('#downloader-location-modal').on('hidden.bs.modal', function (e) { - document.getElementById('downloader-btn').parentElement.classList.remove('keep-open'); - }); + return; + } + + var html = ''; + + // initialize html for browser + for (i = 0; i < files.length; ++i) { + html = mpcp.downloader.getHtmlFolders(files[i]); + + if (html !== '') mpcp.downloader.localFolders.push(html); + } + + mpcp.downloader.updateLocal(callback); + }); + }, + + getHtmlFolders: function (value) { + var tableStart = '
    ', + tableEnd = '
    ', + strippedDir = '', + html = ''; + + if (value.directory) { + //console.log('dir'); + + strippedDir = mpcp.utils.stripSlash(value.directory); + + html = ' ' + tableStart + strippedDir + tableEnd + ''; } + + return html; + }, + + doneSelection: function () { + // "root" folder + var dir = ''; + + if (mpcp.downloader.rowSelect.getSelected()) { + dir = $(mpcp.downloader.rowSelect.getSelected()).data().dirid; + mpcp.downloader.rowSelect.deselect(); + } + + document.getElementById('downloader-location').value = dir; + mpcp.downloader.saveLocation(); + }, + + saveLocation: function () { + var location = document.getElementById('downloader-location').value; + localStorage.setItem('mpcp-downloader-location', location); + }, + + initEvents: function () { + $('#downloader-download').click(function () { + mpcp.downloader.downloadFromDownloader(); + }); + + // detect enter key + $('#downloader-url').keyup(function (e) { + if (e.keyCode == 13) + mpcp.downloader.downloadFromDownloader(); + }); + + // save location + $('#downloader-location').change(function () { + mpcp.downloader.setStatus(''); + mpcp.downloader.saveLocation(); + }); + + $('#downloader-url').change(function () { + mpcp.downloader.setStatus(''); + }); + + $(document).on('dblclick', mpcp.downloader.table + ' tr.downloader-directory', function () { + var dir = $(this).data().dirid; + mpcp.downloader.update(dir); + }); + + $(document).on('click', mpcp.downloader.table + ' .folder-open', function () { + var dir = $(this).parent().parent().data().dirid; + mpcp.downloader.update(dir); + }); + + $(document).on('click', '#downloader-browse', function () { + mpcp.downloader.setStatus(''); + document.getElementById('downloader-btn').parentElement.classList.add('keep-open'); + mpcp.downloader.update('/'); + }); + + $('#downloader-home').click(function () { + console.log('home'); + mpcp.downloader.update('/'); + }); + + $(document).on('click', '.downloader-loc-dir', function () { + var dir = $(this).data().dirid; + //console.log(dir); + mpcp.downloader.update(dir); + }); + + $(document).on('click', '#downloader-location-confirm', function () { + mpcp.downloader.doneSelection(); + }); + + mpcp.downloader.rowSelect = + mpcp.utils.rowSelect('.downloader-directory', 'bg-primary', '#downloader-wrap'); + + mpcp.downloader.rowSelect.on('enter', function (ele) { + var dir = $(ele).data().dirid; + mpcp.downloader.update(dir); + }); + + $('#downloader-location-modal').on('hidden.bs.modal', function (e) { + document.getElementById('downloader-btn').parentElement.classList.remove('keep-open'); + }); + } }; }; diff --git a/src/draganddrop.js b/src/draganddrop.js index f1a0d73..1f10b0c 100644 --- a/src/draganddrop.js +++ b/src/draganddrop.js @@ -2,108 +2,108 @@ module.exports = function (mpcp) { // drag and drop operations var drake = dragula([ - document.getElementById(mpcp.browser.tbodyid), - document.getElementById(mpcp.playlist.tbodyid), - document.getElementById(mpcp.pe.tbodyid), - document.getElementById(mpcp.libraryAlbums.tbodyid), - document.getElementById(mpcp.libraryArtists.tbodyid), - document.getElementById(mpcp.librarySongs.tbodyid), + document.getElementById(mpcp.browser.tbodyid), + document.getElementById(mpcp.playlist.tbodyid), + document.getElementById(mpcp.pe.tbodyid), + document.getElementById(mpcp.libraryAlbums.tbodyid), + document.getElementById(mpcp.libraryArtists.tbodyid), + document.getElementById(mpcp.librarySongs.tbodyid), ], { - copy: function (el, source) { - // clone everything but the playlist and playlist editor - return ( - source.parentElement.id != mpcp.playlist.tableid && - source.parentElement.id != mpcp.pe.tableid - ); - }, - accepts: function (el, target, source, sibling) { - var tp = target.parentElement.id, - sp = source.parentElement.id; + copy: function (el, source) { + // clone everything but the playlist and playlist editor + return ( + source.parentElement.id != mpcp.playlist.tableid && + source.parentElement.id != mpcp.pe.tableid + ); + }, + accepts: function (el, target, source, sibling) { + var tp = target.parentElement.id, + sp = source.parentElement.id; - // only accept the playlist and playlist editor but - // make sure pl and pe don't accept eachother - return ( - ( - (tp != mpcp.pe.tableid || sp != mpcp.playlist.tableid) && - (tp != mpcp.playlist.tableid || sp != mpcp.pe.tableid) - ) && - ( - tp == mpcp.playlist.tableid || - tp == mpcp.pe.tableid - ) - ); - } + // only accept the playlist and playlist editor but + // make sure pl and pe don't accept eachother + return ( + ( + (tp != mpcp.pe.tableid || sp != mpcp.playlist.tableid) && + (tp != mpcp.playlist.tableid || sp != mpcp.pe.tableid) + ) && + ( + tp == mpcp.playlist.tableid || + tp == mpcp.pe.tableid + ) + ); + } }); function getIndex(el, target) { - // added at the end of the target - if (!el) return target.children.length - 1; - return Array.prototype.indexOf.call(target.children, el) - 1; + // added at the end of the target + if (!el) return target.children.length - 1; + return Array.prototype.indexOf.call(target.children, el) - 1; } drake.on("drag", function (el, source) { - switch (source.parentElement.id) { - case mpcp.browser.tableid: - mpcp.utils.checkSelected(el, mpcp.browser); - break; - case mpcp.libraryArtists.tableid: - mpcp.utils.checkSelected(el, mpcp.libraryArtists); - break; - case mpcp.libraryAlbums.tableid: - mpcp.utils.checkSelected(el, mpcp.libraryAlbums); - break; - case mpcp.librarySongs.tableid: - mpcp.utils.checkSelected(el, mpcp.librarySongs); - break; - case mpcp.playlist.tableid: - mpcp.utils.checkSelected(el, mpcp.playlist); - break; - case mpcp.pe.tableid: - mpcp.utils.checkSelected(el, mpcp.pe); - } + switch (source.parentElement.id) { + case mpcp.browser.tableid: + mpcp.utils.checkSelected(el, mpcp.browser); + break; + case mpcp.libraryArtists.tableid: + mpcp.utils.checkSelected(el, mpcp.libraryArtists); + break; + case mpcp.libraryAlbums.tableid: + mpcp.utils.checkSelected(el, mpcp.libraryAlbums); + break; + case mpcp.librarySongs.tableid: + mpcp.utils.checkSelected(el, mpcp.librarySongs); + break; + case mpcp.playlist.tableid: + mpcp.utils.checkSelected(el, mpcp.playlist); + break; + case mpcp.pe.tableid: + mpcp.utils.checkSelected(el, mpcp.pe); + } }); drake.on("drop", function (el, target, source, sibling) { - if (!source || !target) return console.log("null dnd"); + if (!source || !target) return console.log("null dnd"); - var tp = target.parentElement.id, - sp = source.parentElement.id, - index = getIndex(sibling, target), - pageIndex = 0; + var tp = target.parentElement.id, + sp = source.parentElement.id, + index = getIndex(sibling, target), + pageIndex = 0; - // TODO figure out how to make the pe and pl scroll? + // TODO figure out how to make the pe and pl scroll? - if ( - sp == mpcp.browser.tableid && tp == mpcp.playlist.tableid || - sp == mpcp.libraryArtists.tableid && tp == mpcp.playlist.tableid || - sp == mpcp.libraryAlbums.tableid && tp == mpcp.playlist.tableid || - sp == mpcp.librarySongs.tableid && tp == mpcp.playlist.tableid - ) { - // browser -> playlist - // library artists -> playlist - // library albums -> playlist - // library songs -> playlist - pageIndex = (mpcp.pages.currentPlaylist - 1) * mpcp.pages.maxPlaylist; - mpcp.playlist.fromSortableSender(el, index + pageIndex); - } else if (sp == mpcp.playlist.tableid && tp == mpcp.playlist.tableid) { - // playlist -> playlist - pageIndex = (mpcp.pages.currentPlaylist - 1) * mpcp.pages.maxPlaylist; - mpcp.playlist.fromSortableSelf(el, index + pageIndex); - } else if ( - sp == mpcp.browser.tableid && tp == mpcp.pe.tableid || - sp == mpcp.libraryArtists.tableid && tp == mpcp.pe.tableid || - sp == mpcp.libraryAlbums.tableid && tp == mpcp.pe.tableid || - sp == mpcp.librarySongs.tableid && tp == mpcp.pe.tableid - ) { - // browser -> playlist editor - // library artists -> playlist editor - // library albums -> playlist editor - // library songs -> playlist editor - mpcp.pe.fromSortableSender(el, index); - } else if (sp == mpcp.pe.tableid && tp == mpcp.pe.tableid) { - // playlist editor -> playlist editor - mpcp.pe.fromSortableSelf(el); - } + if ( + sp == mpcp.browser.tableid && tp == mpcp.playlist.tableid || + sp == mpcp.libraryArtists.tableid && tp == mpcp.playlist.tableid || + sp == mpcp.libraryAlbums.tableid && tp == mpcp.playlist.tableid || + sp == mpcp.librarySongs.tableid && tp == mpcp.playlist.tableid + ) { + // browser -> playlist + // library artists -> playlist + // library albums -> playlist + // library songs -> playlist + pageIndex = (mpcp.pages.currentPlaylist - 1) * mpcp.pages.maxPlaylist; + mpcp.playlist.fromSortableSender(el, index + pageIndex); + } else if (sp == mpcp.playlist.tableid && tp == mpcp.playlist.tableid) { + // playlist -> playlist + pageIndex = (mpcp.pages.currentPlaylist - 1) * mpcp.pages.maxPlaylist; + mpcp.playlist.fromSortableSelf(el, index + pageIndex); + } else if ( + sp == mpcp.browser.tableid && tp == mpcp.pe.tableid || + sp == mpcp.libraryArtists.tableid && tp == mpcp.pe.tableid || + sp == mpcp.libraryAlbums.tableid && tp == mpcp.pe.tableid || + sp == mpcp.librarySongs.tableid && tp == mpcp.pe.tableid + ) { + // browser -> playlist editor + // library artists -> playlist editor + // library albums -> playlist editor + // library songs -> playlist editor + mpcp.pe.fromSortableSender(el, index); + } else if (sp == mpcp.pe.tableid && tp == mpcp.pe.tableid) { + // playlist editor -> playlist editor + mpcp.pe.fromSortableSelf(el); + } }); }; diff --git a/src/history.js b/src/history.js index 4a691a6..406c5c4 100644 --- a/src/history.js +++ b/src/history.js @@ -2,27 +2,27 @@ module.exports = function (mpcp) { // notification history return { - // max items in history - max: 20, - // type: bootstrap color type - add: function (title, type) { - if (!type) - type = ''; - else - type = 'bg-' + type; + // max items in history + max: 20, + // type: bootstrap color type + add: function (title, type) { + if (!type) + type = ''; + else + type = 'bg-' + type; - var html = '' + mpcp.utils.getTime() + '' + title + ''; - $('#history').prepend(html); + var html = '' + mpcp.utils.getTime() + '' + title + ''; + $('#history').prepend(html); - if ($('#history').children().length > this.max) - $('#history tr:last-child').remove(); - }, + if ($('#history').children().length > this.max) + $('#history tr:last-child').remove(); + }, - initEvents: function () { - $(document).on('click', '.history-remove', function () { - $(this)[0].parentNode.parentNode.remove(); - }); - } + initEvents: function () { + $(document).on('click', '.history-remove', function () { + $(this)[0].parentNode.parentNode.remove(); + }); + } }; }; diff --git a/src/lazytoast.js b/src/lazytoast.js index 6466e69..e935eb0 100644 --- a/src/lazytoast.js +++ b/src/lazytoast.js @@ -1,47 +1,47 @@ module.exports = function (mpcp) { return { - info: function (msg, title, timeout, addHistory) { - if (!title) title = 'Info'; - if (!timeout) timeout = 5000; - - if (addHistory) mpcp.history.add(msg, 'info'); - - toastr.info(msg, title, { - 'closeButton': true, - 'positionClass': 'toast-bottom-left', - 'preventDuplicates': true, - 'timeOut': timeout - }); - }, - - error: function (msg, title, timeout, addHistory) { - if (!title) title = 'Error'; - - if (addHistory) mpcp.history.add(msg, 'danger'); - - toastr.error(msg, title, { - 'closeButton': true, - 'positionClass': 'toast-bottom-left', - 'preventDuplicates': true, - 'timeOut': '-1', - 'extendedTimeOut': '-1' - }); - }, - - warning: function (msg, title, timeout, addHistory) { - if (!title) title = 'Warning'; - if (!timeout) timeout = 5000; - - if (addHistory) mpcp.history.add(msg, 'warning'); - - toastr.warning(msg, title, { - 'closeButton': true, - 'positionClass': 'toast-bottom-left', - 'preventDuplicates': false, - 'timeOut': timeout - }); - } + info: function (msg, title, timeout, addHistory) { + if (!title) title = 'Info'; + if (!timeout) timeout = 5000; + + if (addHistory) mpcp.history.add(msg, 'info'); + + toastr.info(msg, title, { + 'closeButton': true, + 'positionClass': 'toast-bottom-left', + 'preventDuplicates': true, + 'timeOut': timeout + }); + }, + + error: function (msg, title, timeout, addHistory) { + if (!title) title = 'Error'; + + if (addHistory) mpcp.history.add(msg, 'danger'); + + toastr.error(msg, title, { + 'closeButton': true, + 'positionClass': 'toast-bottom-left', + 'preventDuplicates': true, + 'timeOut': '-1', + 'extendedTimeOut': '-1' + }); + }, + + warning: function (msg, title, timeout, addHistory) { + if (!title) title = 'Warning'; + if (!timeout) timeout = 5000; + + if (addHistory) mpcp.history.add(msg, 'warning'); + + toastr.warning(msg, title, { + 'closeButton': true, + 'positionClass': 'toast-bottom-left', + 'preventDuplicates': false, + 'timeOut': timeout + }); + } }; }; diff --git a/src/library.js b/src/library.js index 10f9816..9403193 100644 --- a/src/library.js +++ b/src/library.js @@ -3,192 +3,192 @@ module.exports = function (mpcp) { // the library (alternative to file browser) // controls the main #library return { - hidden: true, - // used when searching globally and other things - bringBack: false, - // save these when updating the library externally - artist: null, - album: null, - - addToHistory: function () { - if (!mpcp.library.artist) { - console.log('adding /library/ to history'); - window.history.pushState('','MPCParty', '/library/'); - return; - } + hidden: true, + // used when searching globally and other things + bringBack: false, + // save these when updating the library externally + artist: null, + album: null, + + addToHistory: function () { + if (!mpcp.library.artist) { + console.log('adding /library/ to history'); + window.history.pushState('','MPCParty', '/library/'); + return; + } - var albumHistory = '', - artistHistory = encodeURIComponent(mpcp.library.artist); + var albumHistory = '', + artistHistory = encodeURIComponent(mpcp.library.artist); - if (mpcp.library.album) - albumHistory = '/' + encodeURIComponent(mpcp.library.album); + if (mpcp.library.album) + albumHistory = '/' + encodeURIComponent(mpcp.library.album); - var url = artistHistory + albumHistory; - console.log('adding /library/' + url + ' to history'); - window.history.pushState('', url + ' - MPCParty', '/library/' + url); - }, + var url = artistHistory + albumHistory; + console.log('adding /library/' + url + ' to history'); + window.history.pushState('', url + ' - MPCParty', '/library/' + url); + }, - // manually open from the user - open: function (callback) { - mpcp.settings.saveBrowser('library'); - this.addToHistory(); - mpcp.libraryArtists.update(mpcp.library.artist, callback); + // manually open from the user + open: function (callback) { + mpcp.settings.saveBrowser('library'); + this.addToHistory(); + mpcp.libraryArtists.update(mpcp.library.artist, callback); - if (!mpcp.library.artist) { - // force an update so header is not squashed together - mpcp.librarySongs.fixedThead.update(); - } - }, - - show: function () { - if (!this.hidden) return; - - this.hidden = false; - document.getElementById('library').style.display = 'flex'; - mpcp.utils.buttonSelect("#open-library", "#browser-selection"); - //mpcp.utils.restoreSelected(mpcp.libraryArtists); - //mpcp.utils.restoreSelected(mpcp.libraryAlbums); - }, - - hide: function () { - this.hidden = true; - document.getElementById('library').style.display = 'none'; - mpcp.utils.saveSelected(mpcp.libraryArtists); - mpcp.utils.clearSelected(mpcp.libraryArtists); - mpcp.utils.saveSelected(mpcp.libraryAlbums); - mpcp.utils.clearSelected(mpcp.libraryAlbums); - }, - - // obj, libraryArtist or libraryAlbum - addMulti: function (obj, to, dontScroll, dragging) { - mpcp.utils.toArraySelected(obj); - var i, tr, artist, album; - - function sendTope(art, alb) { - mpcp.library.getSongsFromAlbum(art, alb, function (files) { - mpcp.pe.addSong(files, to); - }); - } + if (!mpcp.library.artist) { + // force an update so header is not squashed together + mpcp.librarySongs.fixedThead.update(); + } + }, + + show: function () { + if (!this.hidden) return; + + this.hidden = false; + document.getElementById('library').style.display = 'flex'; + mpcp.utils.buttonSelect("#open-library", "#browser-selection"); + //mpcp.utils.restoreSelected(mpcp.libraryArtists); + //mpcp.utils.restoreSelected(mpcp.libraryAlbums); + }, + + hide: function () { + this.hidden = true; + document.getElementById('library').style.display = 'none'; + mpcp.utils.saveSelected(mpcp.libraryArtists); + mpcp.utils.clearSelected(mpcp.libraryArtists); + mpcp.utils.saveSelected(mpcp.libraryAlbums); + mpcp.utils.clearSelected(mpcp.libraryAlbums); + }, + + // obj, libraryArtist or libraryAlbum + addMulti: function (obj, to, dontScroll, dragging) { + mpcp.utils.toArraySelected(obj); + var i, tr, artist, album; + + function sendTope(art, alb) { + mpcp.library.getSongsFromAlbum(art, alb, function (files) { + mpcp.pe.addSong(files, to); + }); + } - function sendToPl(art, alb) { - mpcp.library.getSongsFromAlbum(artist, album, function (files) { - // reverse because not incrementing to variable because - // scrolling to center will be overridden in addSong - files = files.reverse(); - for (var i = 0; i < files.length; ++i) { - mpcp.playlist.addSong(files[i].file, to, dontScroll); - } - }); + function sendToPl(art, alb) { + mpcp.library.getSongsFromAlbum(artist, album, function (files) { + // reverse because not incrementing to variable because + // scrolling to center will be overridden in addSong + files = files.reverse(); + for (var i = 0; i < files.length; ++i) { + mpcp.playlist.addSong(files[i].file, to, dontScroll); } + }); + } - obj.selected.reverse(); - - // context menu check - if (mpcp.pe.current && !dragging) { - for (i = 0; i < obj.selected.length; ++i) { - tr = obj.selected[i]; - artist = $(tr).data().artist; - album = $(tr).data().album; - sendTope(artist, album); - } - } else { - for (i = 0; i < obj.selected.length; ++i) { - tr = obj.selected[i]; - artist = $(tr).data().artist; - album = $(tr).data().album; - sendToPl(artist, album); - } - } + obj.selected.reverse(); + + // context menu check + if (mpcp.pe.current && !dragging) { + for (i = 0; i < obj.selected.length; ++i) { + tr = obj.selected[i]; + artist = $(tr).data().artist; + album = $(tr).data().album; + sendTope(artist, album); + } + } else { + for (i = 0; i < obj.selected.length; ++i) { + tr = obj.selected[i]; + artist = $(tr).data().artist; + album = $(tr).data().album; + sendToPl(artist, album); + } + } - mpcp.utils.clearSelected(obj); - }, - - // return a file list from an album - getSongsFromAlbum: function (artist, album, callback) { - // TODO fix special characters - if (!album) { - komponist.find('artist', artist, function (err, files) { - setSongs(err, files); - }); - } else { - komponist.find('artist', artist, 'album', album, - function (err, files) { - setSongs(err, files); - }); - } + mpcp.utils.clearSelected(obj); + }, + + // return a file list from an album + getSongsFromAlbum: function (artist, album, callback) { + // TODO fix special characters + if (!album) { + komponist.find('artist', artist, function (err, files) { + setSongs(err, files); + }); + } else { + komponist.find('artist', artist, 'album', album, + function (err, files) { + setSongs(err, files); + }); + } - function setSongs (err, files) { - //console.log(files); - files = mpcp.utils.toArray(files); + function setSongs (err, files) { + //console.log(files); + files = mpcp.utils.toArray(files); - if (!files.length || (files.length == 1 && !files[0].Album && - !files[0].Artist)) { - console.log('No songs found'); - mpcp.lazyToast.warning('This may be an issue with special characters: ' + album, 'No songs found'); - return; - } + if (!files.length || (files.length == 1 && !files[0].Album && + !files[0].Artist)) { + console.log('No songs found'); + mpcp.lazyToast.warning('This may be an issue with special characters: ' + album, 'No songs found'); + return; + } - if (callback) callback(files); - } - }, - - addExternal: function (obj, artist, album, to, dontScroll) { - if (obj.selected.length) - this.addMulti(obj, to, dontScroll); - else if (mpcp.pe.current) - this.getSongsFromAlbum(artist, album, function (files) { - mpcp.pe.addSong(files, to, dontScroll); - }); - else - this.getSongsFromAlbum(artist, album, function (files) { - for (var i = 0; i < files.length; ++i) - mpcp.playlist.addSong(files[i].file, to); - }); - }, - - // decode the url when loading the page - decodeRequest: function (request) { - var artist = request, - album = null; - - if (!request) request = ''; - //console.log(request); - - if (request.indexOf('/') != -1) { - artist = request.slice(0, request.indexOf('/')); - album = request.slice(request.indexOf('/') + 1, request.length); - } + if (callback) callback(files); + } + }, + + addExternal: function (obj, artist, album, to, dontScroll) { + if (obj.selected.length) + this.addMulti(obj, to, dontScroll); + else if (mpcp.pe.current) + this.getSongsFromAlbum(artist, album, function (files) { + mpcp.pe.addSong(files, to, dontScroll); + }); + else + this.getSongsFromAlbum(artist, album, function (files) { + for (var i = 0; i < files.length; ++i) + mpcp.playlist.addSong(files[i].file, to); + }); + }, + + // decode the url when loading the page + decodeRequest: function (request) { + var artist = request, + album = null; + + if (!request) request = ''; + //console.log(request); + + if (request.indexOf('/') != -1) { + artist = request.slice(0, request.indexOf('/')); + album = request.slice(request.indexOf('/') + 1, request.length); + } - artist = decodeURIComponent(artist); + artist = decodeURIComponent(artist); - if (album) album = decodeURIComponent(album); + if (album) album = decodeURIComponent(album); - //console.log(artist); - //console.log(mpcp.library.artist); - //console.log(album); - //console.log(mpcp.library.album); + //console.log(artist); + //console.log(mpcp.library.artist); + //console.log(album); + //console.log(mpcp.library.album); - mpcp.browser.hide(); - this.show(); + mpcp.browser.hide(); + this.show(); - // we don't need to update everything if everything is the same - // (like coming from the browser) - if (mpcp.library.album !== null && mpcp.library.album === album) return; + // we don't need to update everything if everything is the same + // (like coming from the browser) + if (mpcp.library.album !== null && mpcp.library.album === album) return; - if (mpcp.library.artist != artist) mpcp.libraryArtists.update(artist); + if (mpcp.library.artist != artist) mpcp.libraryArtists.update(artist); - mpcp.libraryAlbums.update(artist, album, true); - }, + mpcp.libraryAlbums.update(artist, album, true); + }, - initEvents: function () { - $('#open-library').click(function () { - mpcp.library.open(); - }); + initEvents: function () { + $('#open-library').click(function () { + mpcp.library.open(); + }); - mpcp.libraryAlbums.initEvents(); - mpcp.libraryArtists.initEvents(); - mpcp.librarySongs.initEvents(); - } + mpcp.libraryAlbums.initEvents(); + mpcp.libraryArtists.initEvents(); + mpcp.librarySongs.initEvents(); + } }; }; diff --git a/src/libraryalbums.js b/src/libraryalbums.js index cc983ba..ba23d51 100644 --- a/src/libraryalbums.js +++ b/src/libraryalbums.js @@ -2,104 +2,104 @@ module.exports = function (mpcp) { // separate mutliselect for album return { - selected: [], - saved: [], - tableid: 'library-albums-list', - table: '#library-albums-list', - tbody: '#library-albums-list .append', - tbodyid: 'library-albums-list-tbody', - - // put albums in table - // albumUse: highlight in table - update: function (artist, albumUse, poppedState, callback) { - // if still null, return (user updates library without clicking an - // artist) - if (!artist) { - if (callback) callback(); - return; - } - - console.log('update albums'); - mpcp.library.artist = artist; - - komponist.list('album', artist, function (err, files) { - if (err) { - console.log(err); - mpcp.librarySongs.update( - artist, albumUse, poppedState, callback); - return; - } - - $(mpcp.libraryAlbums.table + ' .gen').remove(); - files = mpcp.utils.toArray(files); - - var html = '', - tableStart = '
    ', - tableEnd = '
    ', - addClass = ''; - - if (!albumUse) - addClass = 'info'; - - // All row - html += '' + tableStart + 'All' + tableEnd + ''; - addClass = ''; - - //console.log(files); - - if (!files.length || files[0].Album === '') { - html = '' + - 'No albums'; - document.getElementById(mpcp.libraryAlbums.tbodyid).innerHTML = html; - console.log('No albums found'); - mpcp.librarySongs.update( - artist, albumUse, poppedState, callback); - return; - } - - for (var i = 0; i < files.length; ++i) { - var album = files[i].Album; - - if (album == albumUse) - addClass = 'info'; - - html += '' + tableStart + album + tableEnd + ''; - addClass = ''; - } - - document.getElementById(mpcp.libraryAlbums.tbodyid).innerHTML = html; - - // show all songs initially - mpcp.librarySongs.update(artist, albumUse, poppedState, callback); - }); - }, - - initEvents: function () { - mpcp.utils.lazySearch('#search-albums', this.table, 'album', - '#search-albums-clear'); - - $(document).on('click', this.table + ' .gen', function () { - var artist = $(this).data().artist, - album = $(this).data().album; - mpcp.librarySongs.update(artist, album); - }); - - $(document).on('click', '.album-add', function () { - var artist = $(this).parent().parent().data().artist, - album = $(this).parent().parent().data().album; - mpcp.library.addExternal(mpcp.libraryAlbums, artist, album); - }); - - $(document).on('dblclick', '.album', function () { - var artist = $(this).parent().parent().data().artist, - album = $(this).parent().parent().data().album; - mpcp.library.addExternal(mpcp.libraryAlbums, artist, album); - }); - - mpcp.utils.tableSort(this.table, '#library-col-albums', 1, 'string'); - - mpcp.utils.multiSelect(this, ['album-add'], ['body'], false); + selected: [], + saved: [], + tableid: 'library-albums-list', + table: '#library-albums-list', + tbody: '#library-albums-list .append', + tbodyid: 'library-albums-list-tbody', + + // put albums in table + // albumUse: highlight in table + update: function (artist, albumUse, poppedState, callback) { + // if still null, return (user updates library without clicking an + // artist) + if (!artist) { + if (callback) callback(); + return; } + + console.log('update albums'); + mpcp.library.artist = artist; + + komponist.list('album', artist, function (err, files) { + if (err) { + console.log(err); + mpcp.librarySongs.update( + artist, albumUse, poppedState, callback); + return; + } + + $(mpcp.libraryAlbums.table + ' .gen').remove(); + files = mpcp.utils.toArray(files); + + var html = '', + tableStart = '
    ', + tableEnd = '
    ', + addClass = ''; + + if (!albumUse) + addClass = 'info'; + + // All row + html += '' + tableStart + 'All' + tableEnd + ''; + addClass = ''; + + //console.log(files); + + if (!files.length || files[0].Album === '') { + html = '' + + 'No albums'; + document.getElementById(mpcp.libraryAlbums.tbodyid).innerHTML = html; + console.log('No albums found'); + mpcp.librarySongs.update( + artist, albumUse, poppedState, callback); + return; + } + + for (var i = 0; i < files.length; ++i) { + var album = files[i].Album; + + if (album == albumUse) + addClass = 'info'; + + html += '' + tableStart + album + tableEnd + ''; + addClass = ''; + } + + document.getElementById(mpcp.libraryAlbums.tbodyid).innerHTML = html; + + // show all songs initially + mpcp.librarySongs.update(artist, albumUse, poppedState, callback); + }); + }, + + initEvents: function () { + mpcp.utils.lazySearch('#search-albums', this.table, 'album', + '#search-albums-clear'); + + $(document).on('click', this.table + ' .gen', function () { + var artist = $(this).data().artist, + album = $(this).data().album; + mpcp.librarySongs.update(artist, album); + }); + + $(document).on('click', '.album-add', function () { + var artist = $(this).parent().parent().data().artist, + album = $(this).parent().parent().data().album; + mpcp.library.addExternal(mpcp.libraryAlbums, artist, album); + }); + + $(document).on('dblclick', '.album', function () { + var artist = $(this).parent().parent().data().artist, + album = $(this).parent().parent().data().album; + mpcp.library.addExternal(mpcp.libraryAlbums, artist, album); + }); + + mpcp.utils.tableSort(this.table, '#library-col-albums', 1, 'string'); + + mpcp.utils.multiSelect(this, ['album-add'], ['body'], false); + } }; }; diff --git a/src/libraryartists.js b/src/libraryartists.js index ee62ab4..ffdef1d 100644 --- a/src/libraryartists.js +++ b/src/libraryartists.js @@ -2,88 +2,88 @@ module.exports = function (mpcp) { // separate mutliselect for artist return { - selected: [], - saved: [], - tableid: 'library-artists-list', - table: '#library-artists-list', - tbody: '#library-artists-list .append', - tbodyid: 'library-artists-list-tbody', - - // put artists in table - // artistUse: highlight in table - update: function (artistUse, callback) { - if (this.hidden) { - if (callback) callback(); - return; - } - - console.log('update artists'); - - komponist.list('artist', function (err, files) { - if (err) { - console.log(err); - if (callback) callback(); - return; - } - - //console.log(files); - - $(mpcp.libraryArtists.table + ' .gen').remove(); - files = mpcp.utils.toArray(files); - - var html = ''; - - if (!files.length || files[0].Artist === '') { - html = '' + - 'No artists'; - document.getElementById(mpcp.libraryArtists.tbodyid).innerHTML = html; - console.log('No artists found'); - if (callback) callback(); - return; - } - - var tableStart = '
    ', - tableEnd = '
    ', - addClass = ''; - - for (var i = 0; i < files.length; ++i) { - var artist = files[i].Artist; - - if (artist == artistUse) addClass = 'info'; - - html += '' + tableStart + artist + tableEnd + ''; - addClass = ''; - } - - document.getElementById(mpcp.libraryArtists.tbodyid).innerHTML = html; - - if (callback) callback(); - }); - }, - - initEvents: function () { - mpcp.utils.lazySearch('#search-artists', this.table, 'artist', - '#search-artists-clear'); - - $(document).on('click', this.table + ' .gen', function () { - var artist = $(this).data().artist; - mpcp.libraryAlbums.update(artist); - }); - - $(document).on('click', '.artist-add', function () { - var artist = $(this).parent().parent().data().artist; - mpcp.library.addExternal(mpcp.libraryArtists, artist); - }); - - $(document).on('dblclick', '.artist', function () { - var artist = $(this).parent().parent().data().artist; - mpcp.library.addExternal(mpcp.libraryArtists, artist); - }); - - mpcp.utils.tableSort(this.table, '#library-col-artists', 1, 'string'); - - mpcp.utils.multiSelect(this, ['artist-add'], ['body'], false); + selected: [], + saved: [], + tableid: 'library-artists-list', + table: '#library-artists-list', + tbody: '#library-artists-list .append', + tbodyid: 'library-artists-list-tbody', + + // put artists in table + // artistUse: highlight in table + update: function (artistUse, callback) { + if (this.hidden) { + if (callback) callback(); + return; } + + console.log('update artists'); + + komponist.list('artist', function (err, files) { + if (err) { + console.log(err); + if (callback) callback(); + return; + } + + //console.log(files); + + $(mpcp.libraryArtists.table + ' .gen').remove(); + files = mpcp.utils.toArray(files); + + var html = ''; + + if (!files.length || files[0].Artist === '') { + html = '' + + 'No artists'; + document.getElementById(mpcp.libraryArtists.tbodyid).innerHTML = html; + console.log('No artists found'); + if (callback) callback(); + return; + } + + var tableStart = '
    ', + tableEnd = '
    ', + addClass = ''; + + for (var i = 0; i < files.length; ++i) { + var artist = files[i].Artist; + + if (artist == artistUse) addClass = 'info'; + + html += '' + tableStart + artist + tableEnd + ''; + addClass = ''; + } + + document.getElementById(mpcp.libraryArtists.tbodyid).innerHTML = html; + + if (callback) callback(); + }); + }, + + initEvents: function () { + mpcp.utils.lazySearch('#search-artists', this.table, 'artist', + '#search-artists-clear'); + + $(document).on('click', this.table + ' .gen', function () { + var artist = $(this).data().artist; + mpcp.libraryAlbums.update(artist); + }); + + $(document).on('click', '.artist-add', function () { + var artist = $(this).parent().parent().data().artist; + mpcp.library.addExternal(mpcp.libraryArtists, artist); + }); + + $(document).on('dblclick', '.artist', function () { + var artist = $(this).parent().parent().data().artist; + mpcp.library.addExternal(mpcp.libraryArtists, artist); + }); + + mpcp.utils.tableSort(this.table, '#library-col-artists', 1, 'string'); + + mpcp.utils.multiSelect(this, ['artist-add'], ['body'], false); + } }; }; diff --git a/src/librarysongs.js b/src/librarysongs.js index 269bef6..fbcea0a 100644 --- a/src/librarysongs.js +++ b/src/librarysongs.js @@ -1,161 +1,158 @@ module.exports = function (mpcp) { return { - // used for song selection - selected: [], - // used for saving selected temporarily - saved: [], - tableid: 'library-songs-list', - table: '#library-songs-list', - tbody: '#library-songs-list .append', - tbodyid: 'library-songs-list-tbody', - // used for dragging while selected - clone: null, - fixedThead: null, - - // put songs in table - update: function (artist, album, poppedState, callback) { - // if still null, return (user updates library without clicking an - // artist) - if (!artist) { - if (callback) callback(); - return; - } - - console.log('update songs'); - mpcp.library.album = album; - - if (!album) { - komponist.find('artist', artist, function (err, files) { - setSongs(err, files); - }); - } else { - komponist.find('artist', artist, 'album', album, - function (err, files) { - setSongs(err, files); - }); - } - - if (!poppedState) mpcp.library.addToHistory(); - - function setSongs(err, files) { - if (err) { - console.log(err); - if (callback) callback(); - return; - } - - //console.log(files); - - $(mpcp.librarySongs.table + ' .gen').remove(); - files = mpcp.utils.toArray(files); - - var html = ''; - - if (!files.length || (files.length == 1 && !files[0].Album && - !files[0].Artist)) { - html = '' + - 'No songs found'; - document.getElementById(mpcp.librarySongs.tbodyid).innerHTML = html; - window.dispatchEvent(new CustomEvent('MPCPLibrarySongsChanged')); - console.log('No songs found'); - if (callback) callback(); - } - - var tableStart = '
    ', - tableEnd = '
    '; - - for (var i = 0; i < files.length; ++i) { - html += mpcp.browser.getHtmlFiles(files[i]); - } - - document.getElementById(mpcp.librarySongs.tbodyid).innerHTML = html; - - mpcp.browser.updatePosition(); - window.dispatchEvent(new CustomEvent('MPCPLibrarySongsChanged')); - - if (callback) callback(); - } - }, - - addMulti: function (to, dragging) { - mpcp.browser.selected = this.selected; - mpcp.browser.addMulti(to, null, dragging); - mpcp.utils.clearSelected(mpcp.librarySongs); + // used for song selection + selected: [], + // used for saving selected temporarily + saved: [], + tableid: 'library-songs-list', + table: '#library-songs-list', + tbody: '#library-songs-list .append', + tbodyid: 'library-songs-list-tbody', + // used for dragging while selected + clone: null, + fixedThead: null, + + // put songs in table + update: function (artist, album, poppedState, callback) { + // if still null, return (user updates library without clicking an + // artist) + if (!artist) { + if (callback) callback(); + return; + } + + console.log('update songs'); + mpcp.library.album = album; + + if (!album) { + komponist.find('artist', artist, function (err, files) { + setSongs(err, files); + }); + } else { + komponist.find('artist', artist, 'album', album, + function (err, files) { + setSongs(err, files); + }); + } + + if (!poppedState) mpcp.library.addToHistory(); + + function setSongs(err, files) { + if (err) { + console.log(err); + if (callback) callback(); return; - }, - - // assume library.artist and library.album is already set - // this only searches for the title atm - search: function (title, callback) { - console.log('library search: ' + title); - - function compare(files) { - $(files).each(function (item, file) { - $(mpcp.librarySongs.tbody + ' .gen').each(function (item, val) { - if ($(val).data().fileid == file.file) { - //$(this)[0].style.display = 'block'; - $(this).show(); - } else { - //$(this)[0].style.display = 'none'; - $(this).hide(); - } - }); - }); - - if (callback) callback(); - } - - if (mpcp.library.artist && mpcp.library.album) { - //console.log('search artist and album'); - komponist.search('artist', mpcp.library.artist, 'album', - mpcp.library.album, 'title', title, function (err, files) { - if (err) return console.log(err); - compare(files); - }); - } else if (mpcp.library.artist) { - //console.log('search artist'); - komponist.search('artist', mpcp.library.artist, - 'title', title, function (err, files) { - if (err) return console.log(err); - compare(files); - }); - } else { - console.log('no artist or album selected?'); - if (callback) callback(); - } - }, - - initEvents: function () { - // we only really care about the title (hopefully, only exception is - // when in the 'all' album) - mpcp.utils.createSearch( - '#search-songs', - this.search, - function () { - $(mpcp.librarySongs.table + ' .gen').show(); - }, - '#search-songs-clear', - 1000); - - this.fixedThead = mpcp.tableHeader(this.tableid, 'MPCPLibrarySongsChanged'); - mpcp.librarySongs.fixedThead.update(); - - // this cannot be part of .song-list because of a bug with sortColumn - // (overwrites contens from one tabe to other tables). - mpcp.utils.tableSort(this.table, this.table + '-col-number', - 1, 'number'); - mpcp.utils.tableSort(this.table, this.table + '-col-title', - 2, 'string'); - mpcp.utils.tableSort(this.table, this.table + '-col-artist', - 3, 'string'); - mpcp.utils.tableSort(this.table, this.table + '-col-album', - 4, 'string'); - mpcp.utils.tableSort(this.table, this.table + '-col-time', - 5, '00:00'); - - mpcp.utils.multiSelect(this, ['song-add']); + } + + //console.log(files); + + $(mpcp.librarySongs.table + ' .gen').remove(); + files = mpcp.utils.toArray(files); + + var html = ''; + + if (!files.length || (files.length == 1 && !files[0].Album && + !files[0].Artist)) { + html = '' + + 'No songs found'; + document.getElementById(mpcp.librarySongs.tbodyid).innerHTML = html; + window.dispatchEvent(new CustomEvent('MPCPLibrarySongsChanged')); + console.log('No songs found'); + if (callback) callback(); + } + + for (var i = 0; i < files.length; ++i) { + html += mpcp.browser.getHtmlFiles(files[i]); + } + + document.getElementById(mpcp.librarySongs.tbodyid).innerHTML = html; + + mpcp.browser.updatePosition(); + window.dispatchEvent(new CustomEvent('MPCPLibrarySongsChanged')); + + if (callback) callback(); + } + }, + + addMulti: function (to, dragging) { + mpcp.browser.selected = this.selected; + mpcp.browser.addMulti(to, null, dragging); + mpcp.utils.clearSelected(mpcp.librarySongs); + return; + }, + + // assume library.artist and library.album is already set + // this only searches for the title atm + search: function (title, callback) { + console.log('library search: ' + title); + + function compare(files) { + $(files).each(function (item, file) { + $(mpcp.librarySongs.tbody + ' .gen').each(function (item, val) { + if ($(val).data().fileid == file.file) { + //$(this)[0].style.display = 'block'; + $(this).show(); + } else { + //$(this)[0].style.display = 'none'; + $(this).hide(); + } + }); + }); + + if (callback) callback(); + } + + if (mpcp.library.artist && mpcp.library.album) { + //console.log('search artist and album'); + komponist.search('artist', mpcp.library.artist, 'album', + mpcp.library.album, 'title', title, function (err, files) { + if (err) return console.log(err); + compare(files); + }); + } else if (mpcp.library.artist) { + //console.log('search artist'); + komponist.search('artist', mpcp.library.artist, + 'title', title, function (err, files) { + if (err) return console.log(err); + compare(files); + }); + } else { + console.log('no artist or album selected?'); + if (callback) callback(); } + }, + + initEvents: function () { + // we only really care about the title (hopefully, only exception is + // when in the 'all' album) + mpcp.utils.createSearch( + '#search-songs', + this.search, + function () { + $(mpcp.librarySongs.table + ' .gen').show(); + }, + '#search-songs-clear', + 1000); + + this.fixedThead = mpcp.tableHeader(this.tableid, 'MPCPLibrarySongsChanged'); + mpcp.librarySongs.fixedThead.update(); + + // this cannot be part of .song-list because of a bug with sortColumn + // (overwrites contens from one tabe to other tables). + mpcp.utils.tableSort(this.table, this.table + '-col-number', + 1, 'number'); + mpcp.utils.tableSort(this.table, this.table + '-col-title', + 2, 'string'); + mpcp.utils.tableSort(this.table, this.table + '-col-artist', + 3, 'string'); + mpcp.utils.tableSort(this.table, this.table + '-col-album', + 4, 'string'); + mpcp.utils.tableSort(this.table, this.table + '-col-time', + 5, '00:00'); + + mpcp.utils.multiSelect(this, ['song-add']); + } }; }; diff --git a/src/main.js b/src/main.js index ff6bdab..38cfdd2 100644 --- a/src/main.js +++ b/src/main.js @@ -5,173 +5,173 @@ $(function () { "use strict"; -mpcp.socket = require('./socket.js')(mpcp); -mpcp.disconnect = require('./disconnect.js')(mpcp); -mpcp.utils = require('./utils.js')(mpcp); -mpcp.player = require('./player.js')(mpcp); -mpcp.history = require('./history.js')(mpcp); -mpcp.playlist = require('./playlist.js')(mpcp); -mpcp.browser = require('./browser.js')(mpcp); +mpcp.socket = require('./socket.js')(mpcp); +mpcp.disconnect = require('./disconnect.js')(mpcp); +mpcp.utils = require('./utils.js')(mpcp); +mpcp.player = require('./player.js')(mpcp); +mpcp.history = require('./history.js')(mpcp); +mpcp.playlist = require('./playlist.js')(mpcp); +mpcp.browser = require('./browser.js')(mpcp); mpcp.libraryArtists = require('./libraryartists.js')(mpcp); -mpcp.libraryAlbums = require('./libraryalbums.js')(mpcp); -mpcp.librarySongs = require('./librarysongs.js')(mpcp); -mpcp.library = require('./library.js')(mpcp); -mpcp.stored = require('./stored.js')(mpcp); -mpcp.progressbar = require('./progressbar.js')(mpcp); -mpcp.pe = require('./playlisteditor.js')(mpcp); -mpcp.vote = require('./vote.js')(mpcp); -mpcp.users = require('./users.js')(mpcp); -mpcp.pages = require('./pages.js')(mpcp); -mpcp.settings = require('./settings.js')(mpcp); -mpcp.downloader = require('./downloader.js')(mpcp); -mpcp.lazyToast = require('./lazytoast.js')(mpcp); -mpcp.tableHeader = require('./tableheader.js')(mpcp); +mpcp.libraryAlbums = require('./libraryalbums.js')(mpcp); +mpcp.librarySongs = require('./librarysongs.js')(mpcp); +mpcp.library = require('./library.js')(mpcp); +mpcp.stored = require('./stored.js')(mpcp); +mpcp.progressbar = require('./progressbar.js')(mpcp); +mpcp.pe = require('./playlisteditor.js')(mpcp); +mpcp.vote = require('./vote.js')(mpcp); +mpcp.users = require('./users.js')(mpcp); +mpcp.pages = require('./pages.js')(mpcp); +mpcp.settings = require('./settings.js')(mpcp); +mpcp.downloader = require('./downloader.js')(mpcp); +mpcp.lazyToast = require('./lazytoast.js')(mpcp); +mpcp.tableHeader = require('./tableheader.js')(mpcp); require('./contextmenu.js')(mpcp); require('./draganddrop.js')(mpcp); komponist.on('changed', function (system) { - console.log('changed: ' + system); - - switch (system) { - case 'player': - mpcp.player.updateAll(); - break; - - case 'playlist': - mpcp.playlist.updateAll(); - break; - - case 'mixer': - mpcp.player.updateMixer(); - break; - - case 'options': - mpcp.player.updateControls(); - break; - - case 'update': - mpcp.browser.update(); - mpcp.utils.updateStats(); - break; - - case 'stored_playlist': - mpcp.stored.updatePlaylists('#playlist-open-modal'); - mpcp.stored.updatePlaylists('#playlist-save-modal'); - break; - } + console.log('changed: ' + system); + + switch (system) { + case 'player': + mpcp.player.updateAll(); + break; + + case 'playlist': + mpcp.playlist.updateAll(); + break; + + case 'mixer': + mpcp.player.updateMixer(); + break; + + case 'options': + mpcp.player.updateControls(); + break; + + case 'update': + mpcp.browser.update(); + mpcp.utils.updateStats(); + break; + + case 'stored_playlist': + mpcp.stored.updatePlaylists('#playlist-open-modal'); + mpcp.stored.updatePlaylists('#playlist-save-modal'); + break; + } }); // misc events $(document).on('click', '.stop-click-event', function (event) { - // stop bootstrap from closing the dropdown - event.stopPropagation(); + // stop bootstrap from closing the dropdown + event.stopPropagation(); }); $('.dropdown').on('hide.bs.dropdown', function (e) { - // if keep-shown, don't close dropwdown - return !$(e.target)[0].classList.contains('keep-open'); + // if keep-shown, don't close dropwdown + return !$(e.target)[0].classList.contains('keep-open'); }); $(document).on('click', '.stop-server', function () { - console.log('you stopped the server'); - mpcp.socket.send(JSON.stringify({'type': 'stop-server'}), function (err) { - if (err) console.log(err); - }); + console.log('you stopped the server'); + mpcp.socket.send(JSON.stringify({'type': 'stop-server'}), function (err) { + if (err) console.log(err); + }); }); $(document).on('click', '.playlist-reload', function () { - mpcp.socket.send(JSON.stringify({ - 'type': 'playlist-reload', 'info': mpcp.playlist.current - }), function (err) { - if (err) console.log(err); - }); + mpcp.socket.send(JSON.stringify({ + 'type': 'playlist-reload', 'info': mpcp.playlist.current + }), function (err) { + if (err) console.log(err); + }); }); // handle back and forwards window.onpopstate = function (event) { - var url = decodeURIComponent(document.location.pathname), - option = url.substr(1, mpcp.utils.nthOccurrence(url, '/', 2) - 1), - folder; - - console.log('poppedState: ' + option); - if (option == 'browser') { - folder = url.replace('/browser/', ''); - mpcp.library.hide(); - mpcp.browser.show(); - if (folder === '') folder = '/'; - mpcp.browser.update(folder, true); - } else if (option == 'search') { - var search = url.replace('/search/', ''); - mpcp.browser.search(search, true); - } else if (option == 'library') { - var request = document.location.pathname.replace('/library/', ''); - mpcp.library.decodeRequest(request); + var url = decodeURIComponent(document.location.pathname), + option = url.substr(1, mpcp.utils.nthOccurrence(url, '/', 2) - 1), + folder; + + console.log('poppedState: ' + option); + if (option == 'browser') { + folder = url.replace('/browser/', ''); + mpcp.library.hide(); + mpcp.browser.show(); + if (folder === '') folder = '/'; + mpcp.browser.update(folder, true); + } else if (option == 'search') { + var search = url.replace('/search/', ''); + mpcp.browser.search(search, true); + } else if (option == 'library') { + var request = document.location.pathname.replace('/library/', ''); + mpcp.library.decodeRequest(request); + } else { + // unknown, use mpcp.settings.browser + if (mpcp.settings.lastBrowser == 'library') { + mpcp.browser.hide(); + mpcp.library.show(); + mpcp.library.decodeRequest(); } else { - // unknown, use mpcp.settings.browser - if (mpcp.settings.lastBrowser == 'library') { - mpcp.browser.hide(); - mpcp.library.show(); - mpcp.library.decodeRequest(); - } else { - mpcp.library.hide(); - mpcp.browser.show(); - mpcp.browser.update('/', true); - } + mpcp.library.hide(); + mpcp.browser.show(); + mpcp.browser.update('/', true); } + } }; // disable form submissions because everything runs on a single page $('form').submit(function (e) { - e.preventDefault(); - return false; + e.preventDefault(); + return false; }); // enables resizable playlist editor $('#pe').resizable({ - handles: 'n, w, s, e, nw, ne, sw, se', - minHeight: 140, - minWidth: 160 + handles: 'n, w, s, e, nw, ne, sw, se', + minHeight: 140, + minWidth: 160 }); // make pe draggable because why not $('#pe').draggable({ - containment: 'body', - handle: '#pe-header' + containment: 'body', + handle: '#pe-header' }); $('#pe-tab').draggable({ - axis: 'x', - containment: 'body' + axis: 'x', + containment: 'body' }); // enables resizable testing $('#testing').resizable({ - handles: 'n, w, s, e, nw, ne, sw, se', - minHeight: 140, - minWidth: 140 + handles: 'n, w, s, e, nw, ne, sw, se', + minHeight: 140, + minWidth: 140 }); // make testing draggable because why not $('#testing').draggable({ - containment: 'body', - handle: '#testing-header' + containment: 'body', + handle: '#testing-header' }); mpcp.settings.loadAll(); mpcp.progressbar.initEvents(); -mpcp.player .initEvents(); -mpcp.playlist .initEvents(); +mpcp.player.initEvents(); +mpcp.playlist.initEvents(); // gets called in initAfterConnection(). For some reason it was setting the // current page to /browser/ when sharing a link -//mpcp.browser .initEvents(); -mpcp.settings .initEvents(); -mpcp.pages .initEvents(); -mpcp.pe .initEvents(); -mpcp.history .initEvents(); -mpcp.stored .initEvents(); -mpcp.downloader .initEvents(); -mpcp.disconnect .initEvents(); -mpcp.library .initEvents(); +//mpcp.browser.initEvents(); +mpcp.settings.initEvents(); +mpcp.pages.initEvents(); +mpcp.pe.initEvents(); +mpcp.history.initEvents(); +mpcp.stored.initEvents(); +mpcp.downloader.initEvents(); +mpcp.disconnect.initEvents(); +mpcp.library.initEvents(); }); diff --git a/src/pages.js b/src/pages.js index 34f252a..cd27bad 100644 --- a/src/pages.js +++ b/src/pages.js @@ -2,155 +2,155 @@ module.exports = function (mpcp) { // page support for browser, playlist, etc return { - // enable page support - enabledPlaylist: true, - enabledBrowser: false, - // maximum items per page (playlist or browser) - maxPlaylist: 200, - maxBrowser: 200, - // total pages - totalPlaylist: 1, - totalBrowser: 1, - // current page - currentPlaylist: 1, - currentBrowser: 1, - - // updates the max pages - update: function (type) { - console.log('pages update for ' + type); - - if (type == 'playlist') { - this.totalPlaylist = - Math.ceil(mpcp.playlist.local.length / this.maxPlaylist); - - // just in case - if (this.totalPlaylist <= 0) this.totalPlaylist = 1; - - $('#playlist-pages .total-pages')[0].innerHTML = this.totalPlaylist; - $('#playlist-pages input').prop('max', this.totalPlaylist); - - // checks while user updating maximum item values - if($('#playlist-pages input')[0].value > this.totalPlaylist) { - console.log('changing to max page'); - $('#playlist-pages input')[0].value = this.totalPlaylist; - this.go('playlist', this.totalPlaylist); - } - } else if (type == 'browser') { - this.totalBrowser = Math.ceil((mpcp.browser.localFolders.length + - mpcp.browser.localFiles.length) / this.maxBrowser); - - // just in case - if (this.totalBrowser <= 0) this.totalBrowser = 1; - - $('#browser-pages .total-pages')[0].innerHTML = this.totalBrowser; - $('#browser-pages input').prop('max', this.totalBrowser); - - // checks while user updating maximum item values - if($('#browser-pages input')[0].value > this.totalBrowser) { - console.log('changing to max page'); - $('#browser-pages input')[0].value = this.totalBrowser; - this.go('browser', this.totalBrowser); - } - } - }, - - // type: browser or playlist, page: the page to go - go: function (type, page, scrollDown) { - page = Math.floor(page); - console.log('go to page ' + page + ' on ' + type); - - if (type == 'playlist' && this.enabledPlaylist) { - if (page < 1 || page > this.totalPlaylist) return; - - this.currentPlaylist = parseInt(page); - $('#playlist-pages input')[0].value = this.currentPlaylist; - mpcp.playlist.updateLocal(); - - if (!scrollDown) - $('#pslwrap').scrollTop($(mpcp.playlist.table)); - else - $('#pslwrap').scrollTop($(mpcp.playlist.table)[0].scrollHeight); - } else if (type == 'browser' && this.enabledBrowser) { - if (page < 1 || page > this.totalBrowser) return; - - this.currentBrowser = parseInt(page); - $('#browser-pages input')[0].value = this.currentBrowser; - mpcp.browser.updateLocal(); - - if (!scrollDown) - $('#slwrap').scrollTop($(mpcp.browser.table)); - else - $('#slwrap').scrollTop($(mpcp.browser.table)[0].scrollHeight); - } - }, - - // show page footer - show: function (type) { - console.log('show pages: ' + type); - - if (type == 'playlist') - document.getElementById('playlist-pages').style.display = 'flex'; - else if (type == 'browser') - document.getElementById('browser-pages').style.display = 'flex'; - else - $('.pages').show(); - }, - - // hide page footer - hide: function (type) { - console.log('hide pages: ' + type); - - if (type == 'playlist') - document.getElementById('playlist-pages').style.display = 'none'; - else if (type == 'browser') - document.getElementById('browser-pages').style.display = 'none'; - else - $('.pages').hide(); - }, - - initEvents: function () { - // playlist pages event handling - $('#playlist-pages input').change(function () { - mpcp.pages.go('playlist', this.value); - }); - - $('#playlist-pages .first').click(function () { - mpcp.pages.go('playlist', 1); - }); - - $('#playlist-pages .previous').click(function () { - mpcp.pages.go('playlist', mpcp.pages.currentPlaylist - 1); - }); - - $('#playlist-pages .next').click(function () { - mpcp.pages.go('playlist', mpcp.pages.currentPlaylist + 1); - }); - - $('#playlist-pages .last').click(function () { - mpcp.pages.go('playlist', mpcp.pages.totalPlaylist); - }); - - // browser pages event handling - $('#browser-pages input').change(function () { - mpcp.pages.go('browser', this.value); - }); - - $('#browser-pages .first').click(function () { - mpcp.pages.go('browser', 1); - }); - - $('#browser-pages .previous').click(function () { - mpcp.pages.go('browser', mpcp.pages.currentBrowser - 1); - }); - - $('#browser-pages .next').click(function () { - mpcp.pages.go('browser', mpcp.pages.currentBrowser + 1); - }); - - $('#browser-pages .last').click(function () { - mpcp.pages.go('browser', mpcp.pages.totalBrowser); - }); + // enable page support + enabledPlaylist: true, + enabledBrowser: false, + // maximum items per page (playlist or browser) + maxPlaylist: 200, + maxBrowser: 200, + // total pages + totalPlaylist: 1, + totalBrowser: 1, + // current page + currentPlaylist: 1, + currentBrowser: 1, + + // updates the max pages + update: function (type) { + console.log('pages update for ' + type); + + if (type == 'playlist') { + this.totalPlaylist = + Math.ceil(mpcp.playlist.local.length / this.maxPlaylist); + + // just in case + if (this.totalPlaylist <= 0) this.totalPlaylist = 1; + + $('#playlist-pages .total-pages')[0].innerHTML = this.totalPlaylist; + $('#playlist-pages input').prop('max', this.totalPlaylist); + + // checks while user updating maximum item values + if($('#playlist-pages input')[0].value > this.totalPlaylist) { + console.log('changing to max page'); + $('#playlist-pages input')[0].value = this.totalPlaylist; + this.go('playlist', this.totalPlaylist); + } + } else if (type == 'browser') { + this.totalBrowser = Math.ceil((mpcp.browser.localFolders.length + + mpcp.browser.localFiles.length) / this.maxBrowser); + + // just in case + if (this.totalBrowser <= 0) this.totalBrowser = 1; + + $('#browser-pages .total-pages')[0].innerHTML = this.totalBrowser; + $('#browser-pages input').prop('max', this.totalBrowser); + + // checks while user updating maximum item values + if($('#browser-pages input')[0].value > this.totalBrowser) { + console.log('changing to max page'); + $('#browser-pages input')[0].value = this.totalBrowser; + this.go('browser', this.totalBrowser); + } } + }, + + // type: browser or playlist, page: the page to go + go: function (type, page, scrollDown) { + page = Math.floor(page); + console.log('go to page ' + page + ' on ' + type); + + if (type == 'playlist' && this.enabledPlaylist) { + if (page < 1 || page > this.totalPlaylist) return; + + this.currentPlaylist = parseInt(page); + $('#playlist-pages input')[0].value = this.currentPlaylist; + mpcp.playlist.updateLocal(); + + if (!scrollDown) + $('#pslwrap').scrollTop($(mpcp.playlist.table)); + else + $('#pslwrap').scrollTop($(mpcp.playlist.table)[0].scrollHeight); + } else if (type == 'browser' && this.enabledBrowser) { + if (page < 1 || page > this.totalBrowser) return; + + this.currentBrowser = parseInt(page); + $('#browser-pages input')[0].value = this.currentBrowser; + mpcp.browser.updateLocal(); + + if (!scrollDown) + $('#slwrap').scrollTop($(mpcp.browser.table)); + else + $('#slwrap').scrollTop($(mpcp.browser.table)[0].scrollHeight); + } + }, + + // show page footer + show: function (type) { + console.log('show pages: ' + type); + + if (type == 'playlist') + document.getElementById('playlist-pages').style.display = 'flex'; + else if (type == 'browser') + document.getElementById('browser-pages').style.display = 'flex'; + else + $('.pages').show(); + }, + + // hide page footer + hide: function (type) { + console.log('hide pages: ' + type); + + if (type == 'playlist') + document.getElementById('playlist-pages').style.display = 'none'; + else if (type == 'browser') + document.getElementById('browser-pages').style.display = 'none'; + else + $('.pages').hide(); + }, + + initEvents: function () { + // playlist pages event handling + $('#playlist-pages input').change(function () { + mpcp.pages.go('playlist', this.value); + }); + + $('#playlist-pages .first').click(function () { + mpcp.pages.go('playlist', 1); + }); + + $('#playlist-pages .previous').click(function () { + mpcp.pages.go('playlist', mpcp.pages.currentPlaylist - 1); + }); + + $('#playlist-pages .next').click(function () { + mpcp.pages.go('playlist', mpcp.pages.currentPlaylist + 1); + }); + + $('#playlist-pages .last').click(function () { + mpcp.pages.go('playlist', mpcp.pages.totalPlaylist); + }); + + // browser pages event handling + $('#browser-pages input').change(function () { + mpcp.pages.go('browser', this.value); + }); + + $('#browser-pages .first').click(function () { + mpcp.pages.go('browser', 1); + }); + + $('#browser-pages .previous').click(function () { + mpcp.pages.go('browser', mpcp.pages.currentBrowser - 1); + }); + + $('#browser-pages .next').click(function () { + mpcp.pages.go('browser', mpcp.pages.currentBrowser + 1); + }); + + $('#browser-pages .last').click(function () { + mpcp.pages.go('browser', mpcp.pages.totalBrowser); + }); + } }; }; diff --git a/src/player.js b/src/player.js index ea6eac2..e46d6b7 100644 --- a/src/player.js +++ b/src/player.js @@ -2,391 +2,391 @@ module.exports = function (mpcp) { // the header with music controls return { - // current song to highlight the playlist - current: null, - // current song title - title: '', - // current state of the player - state: '', - // callback after dom updates - callbackUpdates: [], - - updateAll: function (callback) { - // set song title - komponist.currentsong(function (err, song) { - if (err) { - console.log(err); - mpcp.player.updateControls(callback); - return; - } - - //console.log(song); - - if ($.isEmptyObject(song)) { - document.getElementById('title-text').innerHTML = - 'No song selected'; - document.title = 'MPCParty'; - document.getElementById('title-pos').innerHTML = ''; - document.getElementById('time-total').innerHTML = - '-- / --'; - mpcp.player.setCurrent(null); - console.log('No song selected'); - mpcp.player.updateControls(callback); - return; - } - - mpcp.player.title = mpcp.utils.getSimpleTitle( - song.Title, song.Artist, song.file); - - if (mpcp.player.current && - mpcp.player.current.file != song.file) { - mpcp.history.add('Playing: ' + mpcp.player.title); - } - - mpcp.player.setCurrent(song); - document.getElementById('title-text').innerHTML = mpcp.player.title; - $('#title-text').attr('title', mpcp.player.title); - document.title = mpcp.player.title + ' - MPCParty'; - document.getElementById('title-pos').innerHTML = - (parseInt(song.Pos) + 1) + '. '; - $('#music-time').attr('max', song.Time); - document.getElementById('time-total').innerHTML = - ' / ' + mpcp.utils.toMMSS(song.Time); - - // highlight song in playlist with song.Id and data-fileid - // this happens with only a player update - $(mpcp.playlist.table + ' .gen').each(function () { - var id = $(this).data().fileid; - if (parseInt(id) == mpcp.player.current.Id) { - $(this)[0].classList.add('bg-success'); - } else { - $(this)[0].classList.remove('bg-success'); - } - }); - - mpcp.player.updateControls(callback); - }); - }, - - // set player properties - updateControls: function (callback) { - komponist.status(function (err, status) { - //console.log(status); - if (err) { - console.log(err); - mpcp.player.callbackUpdate(); - if (callback) callback(); - return; - } - - document.getElementById('music-time').value = status.elapsed; - if (mpcp.player.current !== null && status.state == 'stop') { - document.getElementById('time-current').innerHTML = '00:00'; - } else if (!status.elapsed) { - document.getElementById('time-current').innerHTML = ''; - } else { - document.getElementById('time-current').innerHTML = mpcp.utils.toMMSS(status.elapsed); - } - - mpcp.progressbar.progress = parseInt(status.elapsed); - - console.log('state: ' + status.state); - mpcp.player.state = status.state; - - switch (status.state) { - case 'stop': - document.getElementById('stop').style.display = 'none'; - document.getElementById('pause').style.display = 'none'; - document.getElementById('play').style.display = 'block'; - mpcp.progressbar.stopProgress(); - break; - - case 'play': - document.getElementById('stop').style.display = 'block'; - document.getElementById('pause').style.display = 'block'; - document.getElementById('play').style.display = 'none'; - document.getElementById('pause').classList.remove('active'); - mpcp.progressbar.startProgress(); - break; - - case 'pause': - document.getElementById('play').style.display = 'none'; - document.getElementById('pause').classList.add('active'); - mpcp.progressbar.stopProgress(); - break; - } - - // random - if (parseInt(status.random) === 0) { - document.getElementById('random').classList.remove('active'); - } else if (parseInt(status.random) == 1) { - document.getElementById('random').classList.add('active'); - } - - // repeat - if (parseInt(status.repeat) === 0) { - document.getElementById('repeat').classList.remove('active'); - } else if (parseInt(status.repeat) == 1) { - document.getElementById('repeat').classList.add('active'); - } - - // consume - if (parseInt(status.consume) === 0) { - $('#consume').prop('checked', false); - document.getElementById('warning-consume').style.display = 'none'; - } else if (parseInt(status.consume) == 1) { - $('#consume').prop('checked', true); - if (mpcp.settings.consumeWarning) - document.getElementById('warning-consume').style.display = - 'block'; - } - - if (!status.xfade) status.xfade = 0; - document.getElementById('crossfade').value = status.xfade; - - mpcp.player.callbackUpdate(); - if (callback) callback(); - }); - }, - - // set interface properties - updateMixer: function () { - komponist.status(function (err, status) { - //console.log(status); - if (err) return console.log(err); - - document.getElementById('volume').value = status.volume; - - if (status.error) { - mpcp.lazyToast.error(status.error); - } - }); - }, - - // set int's so there is less parsing when accessing mpcp.player.current - setCurrent: function (song) { - if (song) { - song.Id = parseInt(song.Id); - song.Pos = parseInt(song.Pos); + // current song to highlight the playlist + current: null, + // current song title + title: '', + // current state of the player + state: '', + // callback after dom updates + callbackUpdates: [], + + updateAll: function (callback) { + // set song title + komponist.currentsong(function (err, song) { + if (err) { + console.log(err); + mpcp.player.updateControls(callback); + return; + } + + //console.log(song); + + if ($.isEmptyObject(song)) { + document.getElementById('title-text').innerHTML = + 'No song selected'; + document.title = 'MPCParty'; + document.getElementById('title-pos').innerHTML = ''; + document.getElementById('time-total').innerHTML = + '-- / --'; + mpcp.player.setCurrent(null); + console.log('No song selected'); + mpcp.player.updateControls(callback); + return; + } + + mpcp.player.title = mpcp.utils.getSimpleTitle( + song.Title, song.Artist, song.file); + + if (mpcp.player.current && + mpcp.player.current.file != song.file) { + mpcp.history.add('Playing: ' + mpcp.player.title); + } + + mpcp.player.setCurrent(song); + document.getElementById('title-text').innerHTML = mpcp.player.title; + $('#title-text').attr('title', mpcp.player.title); + document.title = mpcp.player.title + ' - MPCParty'; + document.getElementById('title-pos').innerHTML = + (parseInt(song.Pos) + 1) + '. '; + $('#music-time').attr('max', song.Time); + document.getElementById('time-total').innerHTML = + ' / ' + mpcp.utils.toMMSS(song.Time); + + // highlight song in playlist with song.Id and data-fileid + // this happens with only a player update + $(mpcp.playlist.table + ' .gen').each(function () { + var id = $(this).data().fileid; + if (parseInt(id) == mpcp.player.current.Id) { + $(this)[0].classList.add('bg-success'); + } else { + $(this)[0].classList.remove('bg-success'); } + }); + + mpcp.player.updateControls(callback); + }); + }, + + // set player properties + updateControls: function (callback) { + komponist.status(function (err, status) { + //console.log(status); + if (err) { + console.log(err); + mpcp.player.callbackUpdate(); + if (callback) callback(); + return; + } + + document.getElementById('music-time').value = status.elapsed; + if (mpcp.player.current !== null && status.state == 'stop') { + document.getElementById('time-current').innerHTML = '00:00'; + } else if (!status.elapsed) { + document.getElementById('time-current').innerHTML = ''; + } else { + document.getElementById('time-current').innerHTML = mpcp.utils.toMMSS(status.elapsed); + } + + mpcp.progressbar.progress = parseInt(status.elapsed); + + console.log('state: ' + status.state); + mpcp.player.state = status.state; + + switch (status.state) { + case 'stop': + document.getElementById('stop').style.display = 'none'; + document.getElementById('pause').style.display = 'none'; + document.getElementById('play').style.display = 'block'; + mpcp.progressbar.stopProgress(); + break; + + case 'play': + document.getElementById('stop').style.display = 'block'; + document.getElementById('pause').style.display = 'block'; + document.getElementById('play').style.display = 'none'; + document.getElementById('pause').classList.remove('active'); + mpcp.progressbar.startProgress(); + break; + + case 'pause': + document.getElementById('play').style.display = 'none'; + document.getElementById('pause').classList.add('active'); + mpcp.progressbar.stopProgress(); + break; + } + + // random + if (parseInt(status.random) === 0) { + document.getElementById('random').classList.remove('active'); + } else if (parseInt(status.random) == 1) { + document.getElementById('random').classList.add('active'); + } + + // repeat + if (parseInt(status.repeat) === 0) { + document.getElementById('repeat').classList.remove('active'); + } else if (parseInt(status.repeat) == 1) { + document.getElementById('repeat').classList.add('active'); + } + + // consume + if (parseInt(status.consume) === 0) { + $('#consume').prop('checked', false); + document.getElementById('warning-consume').style.display = 'none'; + } else if (parseInt(status.consume) == 1) { + $('#consume').prop('checked', true); + if (mpcp.settings.consumeWarning) + document.getElementById('warning-consume').style.display = + 'block'; + } + + if (!status.xfade) status.xfade = 0; + document.getElementById('crossfade').value = status.xfade; + + mpcp.player.callbackUpdate(); + if (callback) callback(); + }); + }, + + // set interface properties + updateMixer: function () { + komponist.status(function (err, status) { + //console.log(status); + if (err) return console.log(err); + + document.getElementById('volume').value = status.volume; + + if (status.error) { + mpcp.lazyToast.error(status.error); + } + }); + }, + + // set int's so there is less parsing when accessing mpcp.player.current + setCurrent: function (song) { + if (song) { + song.Id = parseInt(song.Id); + song.Pos = parseInt(song.Pos); + } - this.current = song; - }, + this.current = song; + }, - // create a queue because the server responds once for a dom update - addCallbackUpdate: function (callback) { - if (typeof callback === 'function') { - this.callbackUpdates.push(callback); - } else if (callback) { - console.log('!!! This is not a function, fix it:'); - console.log(callback); - } - }, - - callbackUpdate: function () { - // remove callback from array instead of just resetting the array - // afterward to fix an issue where callbackUpdates was not reset - // because a callback was executing a NEW callback stack. - while (this.callbackUpdates.length) { - var callback = mpcp.player.callbackUpdates[0]; - mpcp.player.callbackUpdates.splice(0, 1); - - if (!mpcp.player.callbackUpdates.length) - return callback(); - else - callback(); - } - }, - - // wrapper for komponist.play - play: function (callback) { - console.log('play'); - komponist.play(function (err) { - if (err) console.log(err); - mpcp.player.addCallbackUpdate(callback); + // create a queue because the server responds once for a dom update + addCallbackUpdate: function (callback) { + if (typeof callback === 'function') { + this.callbackUpdates.push(callback); + } else if (callback) { + console.log('!!! This is not a function, fix it:'); + console.log(callback); + } + }, + + callbackUpdate: function () { + // remove callback from array instead of just resetting the array + // afterward to fix an issue where callbackUpdates was not reset + // because a callback was executing a NEW callback stack. + while (this.callbackUpdates.length) { + var callback = mpcp.player.callbackUpdates[0]; + mpcp.player.callbackUpdates.splice(0, 1); + + if (!mpcp.player.callbackUpdates.length) + return callback(); + else + callback(); + } + }, + + // wrapper for komponist.play + play: function (callback) { + console.log('play'); + komponist.play(function (err) { + if (err) console.log(err); + mpcp.player.addCallbackUpdate(callback); + }); + }, + + // wrapper for komponist.toggle + toggle: function (callback) { + console.log('toggle'); + + // 1. (playing) -> mousedown adds active -> mouseup -> active goes away -> + // wait for mpd -> active comes back (to indicate pause). + // This removes the "active goes away" part so it is more consistent. + document.getElementById('pause').classList.toggle('active'); + + komponist.toggle(function (err) { + if (err) console.log(err); + mpcp.player.addCallbackUpdate(callback); + }); + }, + + // wrapper for komponist.stop + stop: function () { + console.log('stop'); + komponist.stop(function (err) { + if (err) console.log(err); + }); + }, + + // wrapper for komponist.next + next: function (callback) { + if (mpcp.vote.enabled) { + mpcp.player.addCallbackUpdate(callback); + + if (!document.getElementById('next').classList.contains('active')) { + document.getElementById('next').classList.add('active'); + + socket.send(JSON.stringify({ + 'type': 'song-vote-next', 'info': 'yes' + }), function (err) { + if (err) console.log(err); }); - }, - // wrapper for komponist.toggle - toggle: function (callback) { - console.log('toggle'); + } else { + document.getElementById('next').classList.remove('active'); - // 1. (playing) -> mousedown adds active -> mouseup -> active goes away -> - // wait for mpd -> active comes back (to indicate pause). - // This removes the "active goes away" part so it is more consistent. - document.getElementById('pause').classList.toggle('active'); - - komponist.toggle(function (err) { - if (err) console.log(err); - mpcp.player.addCallbackUpdate(callback); - }); - }, - - // wrapper for komponist.stop - stop: function () { - console.log('stop'); - komponist.stop(function (err) { - if (err) console.log(err); + socket.send(JSON.stringify({ + 'type': 'song-vote-next', 'info': 'no' + }), function (err) { + if (err) console.log(err); }); - }, - - // wrapper for komponist.next - next: function (callback) { - if (mpcp.vote.enabled) { - mpcp.player.addCallbackUpdate(callback); - - if (!document.getElementById('next').classList.contains('active')) { - document.getElementById('next').classList.add('active'); - - socket.send(JSON.stringify({ - 'type': 'song-vote-next', 'info': 'yes' - }), function (err) { - if (err) console.log(err); - }); - - } else { - document.getElementById('next').classList.remove('active'); - - socket.send(JSON.stringify({ - 'type': 'song-vote-next', 'info': 'no' - }), function (err) { - if (err) console.log(err); - }); - } - } else { - console.log('next'); - socket.send(JSON.stringify({ - 'type': 'song-next' - }), function (err) { - if (err) console.log(err); - }); - - // if skipping too fast, race conditions happen - var current = mpcp.player.current; - komponist.next(function (err) { - if (err) console.log(err); - - if (mpcp.playlist.skipToRemove) { - mpcp.playlist.removeSong(current.Id); - } - - mpcp.player.addCallbackUpdate(callback); - }); + } + } else { + console.log('next'); + socket.send(JSON.stringify({ + 'type': 'song-next' + }), function (err) { + if (err) console.log(err); + }); + + // if skipping too fast, race conditions happen + var current = mpcp.player.current; + komponist.next(function (err) { + if (err) console.log(err); + + if (mpcp.playlist.skipToRemove) { + mpcp.playlist.removeSong(current.Id); } - }, - - // wrapper for komponist.previous - previous: function (callback) { - if (mpcp.vote.enabled) { - mpcp.player.addCallbackUpdate(callback); - - if (!document.getElementById('previous').classList.contains('active')) { - socket.send(JSON.stringify({ - 'type': 'song-vote-previous', 'info': 'yes' - }), function (err) { - if (err) console.log(err); - }); - document.getElementById('previous').classList.add('active'); - } else { - document.getElementById('previous').classList.remove('active'); - socket.send(JSON.stringify({ - 'type': 'song-vote-previous', 'info': 'no' - }), function (err) { - if (err) console.log(err); - }); - } - } else { - console.log('previous'); - socket.send(JSON.stringify({ - 'type': 'song-previous' - }), function (err) { - if (err) console.log(err); - }); - - // if skipping too fast, race conditions happen - var current = mpcp.player.current; - komponist.previous(function (err) { - if (err) console.log(err); - - if (mpcp.playlist.skipToRemove) { - mpcp.playlist.removeSong(current.Id); - } - - mpcp.player.addCallbackUpdate(callback); - }); - } - }, - - initEvents: function () { - $('#pause').click(function () { - mpcp.player.toggle(); - }); - - $('#play').click(function () { - mpcp.player.play(); - }); - - $('#stop').click(function () { - mpcp.player.stop(); - }); - $('#next').click(function () { - mpcp.player.next(); + mpcp.player.addCallbackUpdate(callback); + }); + } + }, + + // wrapper for komponist.previous + previous: function (callback) { + if (mpcp.vote.enabled) { + mpcp.player.addCallbackUpdate(callback); + + if (!document.getElementById('previous').classList.contains('active')) { + socket.send(JSON.stringify({ + 'type': 'song-vote-previous', 'info': 'yes' + }), function (err) { + if (err) console.log(err); }); - - $('#previous').click(function () { - mpcp.player.previous(); + document.getElementById('previous').classList.add('active'); + } else { + document.getElementById('previous').classList.remove('active'); + socket.send(JSON.stringify({ + 'type': 'song-vote-previous', 'info': 'no' + }), function (err) { + if (err) console.log(err); }); + } + } else { + console.log('previous'); + socket.send(JSON.stringify({ + 'type': 'song-previous' + }), function (err) { + if (err) console.log(err); + }); + + // if skipping too fast, race conditions happen + var current = mpcp.player.current; + komponist.previous(function (err) { + if (err) console.log(err); + + if (mpcp.playlist.skipToRemove) { + mpcp.playlist.removeSong(current.Id); + } - $('#go-current').click(function () { - mpcp.playlist.goToCurrent(); + mpcp.player.addCallbackUpdate(callback); + }); + } + }, + + initEvents: function () { + $('#pause').click(function () { + mpcp.player.toggle(); + }); + + $('#play').click(function () { + mpcp.player.play(); + }); + + $('#stop').click(function () { + mpcp.player.stop(); + }); + + $('#next').click(function () { + mpcp.player.next(); + }); + + $('#previous').click(function () { + mpcp.player.previous(); + }); + + $('#go-current').click(function () { + mpcp.playlist.goToCurrent(); + }); + + // 'input change' allows arrow keys to work + $('#volume').on('input change', function () { + komponist.setvol(this.value, function (err) { + if (err) console.log(err); + }); + }); + + $('#random').click(function () { + console.log('random'); + if (document.getElementById('random').classList.contains('active')) { + // read 1. + document.getElementById('random').classList.toggle('active'); + komponist.random(0, function (err) { + if (err) console.log(err); }); - - // 'input change' allows arrow keys to work - $('#volume').on('input change', function () { - komponist.setvol(this.value, function (err) { - if (err) console.log(err); - }); + } else { + // read 1. + document.getElementById('random').classList.toggle('active'); + komponist.random(1, function (err) { + if (err) console.log(err); }); - - $('#random').click(function () { - console.log('random'); - if (document.getElementById('random').classList.contains('active')) { - // read 1. - document.getElementById('random').classList.toggle('active'); - komponist.random(0, function (err) { - if (err) console.log(err); - }); - } else { - // read 1. - document.getElementById('random').classList.toggle('active'); - komponist.random(1, function (err) { - if (err) console.log(err); - }); - } + } + }); + + $('#repeat').click(function () { + console.log('repeat'); + if (document.getElementById('repeat').classList.contains('active')) { + // read 1. + document.getElementById('repeat').classList.toggle('active'); + komponist.repeat(0, function (err) { + if (err) console.log(err); }); - - $('#repeat').click(function () { - console.log('repeat'); - if (document.getElementById('repeat').classList.contains('active')) { - // read 1. - document.getElementById('repeat').classList.toggle('active'); - komponist.repeat(0, function (err) { - if (err) console.log(err); - }); - } else { - // read 1. - document.getElementById('repeat').classList.toggle('active'); - komponist.repeat(1, function (err) { - if (err) console.log(err); - }); - } + } else { + // read 1. + document.getElementById('repeat').classList.toggle('active'); + komponist.repeat(1, function (err) { + if (err) console.log(err); }); + } + }); - mpcp.utils.createSearch( - '#search-browser', mpcp.browser.search, - mpcp.browser.resetSearch, '#search-clear'); - } + mpcp.utils.createSearch( + '#search-browser', mpcp.browser.search, + mpcp.browser.resetSearch, '#search-clear'); + } }; }; diff --git a/src/playlist.js b/src/playlist.js index 5794d0c..6f92b23 100644 --- a/src/playlist.js +++ b/src/playlist.js @@ -2,957 +2,956 @@ module.exports = function (mpcp) { // the playlist return { - table: '#playlist-song-list', - tableid: 'playlist-song-list', - tbody: '#playlist-song-list .append', - tbodyid: 'playlist-song-list-tbody', - // current playlist title - current: '', - // set to false when we don't want an update to happen - doUpdate: true, - // list used for other functions - list: {files: [], positions: []}, - // local copy of mpd json response - local: {}, - // selected items from multiSelect - selected: [], - // add pulse effect on next update - toPulse: [], - // check if searching as to keep search while updating - isSearching: false, - // current search term - searchTerm: '', - // scroll to position after playlist update - goAfterUpdate: false, - // remove after skipping songs - skipToRemove: false, - // callback after dom updates - callbackUpdates: [], - - // used to update the current playlist - updateAll: function () { - // do not use 'this'. Can be used in a callback. - if (!mpcp.playlist.doUpdate) { - mpcp.playlist.doUpdate = true; + table: '#playlist-song-list', + tableid: 'playlist-song-list', + tbody: '#playlist-song-list .append', + tbodyid: 'playlist-song-list-tbody', + // current playlist title + current: '', + // set to false when we don't want an update to happen + doUpdate: true, + // list used for other functions + list: {files: [], positions: []}, + // local copy of mpd json response + local: {}, + // selected items from multiSelect + selected: [], + // add pulse effect on next update + toPulse: [], + // check if searching as to keep search while updating + isSearching: false, + // current search term + searchTerm: '', + // scroll to position after playlist update + goAfterUpdate: false, + // remove after skipping songs + skipToRemove: false, + // callback after dom updates + callbackUpdates: [], + + // used to update the current playlist + updateAll: function () { + // do not use 'this'. Can be used in a callback. + if (!mpcp.playlist.doUpdate) { + mpcp.playlist.doUpdate = true; + return; + } + + console.log('updating playlist'); + + // reset list + mpcp.playlist.list = {files: [], positions: []}; + + if (mpcp.playlist.isSearching) { + mpcp.playlist.search(); + } else { + // we update the player first to update the current song positison + // which is used for 'movetocurrent', the 'remove last song' bug + // and other operations + mpcp.player.updateAll(function () { + komponist.playlistinfo(function (err, playlistLoad) { + $('#playlist-title strong')[0].innerHTML = + mpcp.playlist.current; + $('#playlist-title strong').attr('title', + mpcp.playlist.current); + + if (err) { + console.log(err); + mpcp.playlist.callbackUpdate(); return; - } + } - console.log('updating playlist'); + document.getElementById(mpcp.playlist.tbodyid).innerHTML = ''; + mpcp.playlist.local = playlistLoad; - // reset list - mpcp.playlist.list = {files: [], positions: []}; + if ($.isEmptyObject(playlistLoad[0])) { + var html = 'The playlist is empty! Songs can be added from the browser or by opening a playlist.'; + document.getElementById(mpcp.playlist.tbodyid).innerHTML = html; + // fix for removing the last song that's + // playling from the playlist + mpcp.playlist.doUpdate = true; + mpcp.player.updateAll(); + mpcp.pages.update('playlist'); + mpcp.browser.updatePosition(); + console.log('Empty playlist'); + mpcp.playlist.callbackUpdate(); + return; + } + + // TODO figure out a way to use mpcp.playlist.local + // efficiently with mpcp.browser.updatePlaylist instead of + // utilizing mpcp.playlist.list + for (var i = 0; i < mpcp.playlist.local.length; ++i) { + mpcp.playlist.list.files.push( + mpcp.playlist.local[i].file); + mpcp.playlist.list.positions.push( + mpcp.playlist.local[i].Pos); + } + + // since goToCurrent runs updateLocal, skip the regular + // updateLocal + if (mpcp.playlist.goAfterUpdate) { + mpcp.playlist.goAfterUpdate = false; + mpcp.playlist.goToCurrent(); + mpcp.playlist.callbackUpdate(); + } else { + mpcp.playlist.updateLocal(); + } + }); + }); + } + }, - if (mpcp.playlist.isSearching) { - mpcp.playlist.search(); - } else { - // we update the player first to update the current song positison - // which is used for 'movetocurrent', the 'remove last song' bug - // and other operations - mpcp.player.updateAll(function () { - komponist.playlistinfo(function (err, playlistLoad) { - $('#playlist-title strong')[0].innerHTML = - mpcp.playlist.current; - $('#playlist-title strong').attr('title', - mpcp.playlist.current); - - if (err) { - console.log(err); - mpcp.playlist.callbackUpdate(); - return; - } - - document.getElementById(mpcp.playlist.tbodyid).innerHTML = ''; - mpcp.playlist.local = playlistLoad; - - if ($.isEmptyObject(playlistLoad[0])) { - var html = 'The playlist is empty! Songs can be added from the browser or by opening a playlist.'; - document.getElementById(mpcp.playlist.tbodyid).innerHTML = html; - // fix for removing the last song that's - // playling from the playlist - mpcp.playlist.doUpdate = true; - mpcp.player.updateAll(); - mpcp.pages.update('playlist'); - mpcp.browser.updatePosition(); - console.log('Empty playlist'); - mpcp.playlist.callbackUpdate(); - return; - } - - // TODO figure out a way to use mpcp.playlist.local - // efficiently with mpcp.browser.updatePlaylist instead of - // utilizing mpcp.playlist.list - for (var i = 0; i < mpcp.playlist.local.length; ++i) { - mpcp.playlist.list.files.push( - mpcp.playlist.local[i].file); - mpcp.playlist.list.positions.push( - mpcp.playlist.local[i].Pos); - } - - // since goToCurrent runs updateLocal, skip the regular - // updateLocal - if (mpcp.playlist.goAfterUpdate) { - mpcp.playlist.goAfterUpdate = false; - mpcp.playlist.goToCurrent(); - mpcp.playlist.callbackUpdate(); - } else { - mpcp.playlist.updateLocal(); - } - }); - }); - } - }, + updateLocal: function (callback) { + if (!this.doUpdate) { + this.doUpdate = true; + return; + } - updateLocal: function (callback) { - if (!this.doUpdate) { - this.doUpdate = true; - return; - } + console.log('update playlist locally'); - console.log('update playlist locally'); + // length is always 1 for this.local, this fixes the empty + // object + if (this.local.length <= 1) { + // remove album-art if playlist was cleared + mpcp.utils.setCurrentAlbumArt(); - // length is always 1 for this.local, this fixes the empty - // object - if (this.local.length <= 1) { - // remove album-art if playlist was cleared - mpcp.utils.setCurrentAlbumArt(); + if ((this.local[0] && Object.getOwnPropertyNames( + this.local[0]).length <= 0) || + this.local.length <= 0) { + this.callbackUpdate(); + return; + } + } - if ((this.local[0] && Object.getOwnPropertyNames( - this.local[0]).length <= 0) || - this.local.length <= 0) { - this.callbackUpdate(); - return; - } - } + $(this.table + ' .gen').remove(); - $(this.table + ' .gen').remove(); + var html = '', + // item start and end from current page + start = (mpcp.pages.currentPlaylist - 1) * + mpcp.pages.maxPlaylist, + end = ((mpcp.pages.currentPlaylist - 1) * + mpcp.pages.maxPlaylist) + mpcp.pages.maxPlaylist, + i; - var html = '', - // item start and end from current page - start = (mpcp.pages.currentPlaylist - 1) * - mpcp.pages.maxPlaylist, - end = ((mpcp.pages.currentPlaylist - 1) * - mpcp.pages.maxPlaylist) + mpcp.pages.maxPlaylist, - i; + //console.log(end); + // make all toPulse to ints + for (i = 0; i < this.toPulse.length; ++i) { + this.toPulse[i] = parseInt(this.toPulse[i]); + } - //console.log(end); - // make all toPulse to ints - for (i = 0; i < this.toPulse.length; ++i) { - this.toPulse[i] = parseInt(this.toPulse[i]); - } + for (i = 0; i < this.local.length; ++i) { + var value = this.local[i]; - for (i = 0; i < this.local.length; ++i) { - var value = this.local[i]; + value.Pos = parseInt(value.Pos); + value.Id = parseInt(value.Id); + //console.log(value.file); - value.Pos = parseInt(value.Pos); - value.Id = parseInt(value.Id); - //console.log(value.file); + // show only necessary files + if (mpcp.pages.enabledPlaylist) { + if (i < start) + continue; + else if (i >= end) + break; + } - // show only necessary files - if (mpcp.pages.enabledPlaylist) { - if (i < start) - continue; - else if (i >= end) - break; - } + var title = mpcp.utils.getSimpleTitle( + value.Title, value.Artist, value.file), + current = 'gen'; - var title = mpcp.utils.getSimpleTitle( - value.Title, value.Artist, value.file), - current = 'gen'; + // highlight current song on first load + if (mpcp.player.current && value.Id == mpcp.player.current.Id) + current += ' bg-success'; - // highlight current song on first load - if (mpcp.player.current && value.Id == mpcp.player.current.Id) - current += ' bg-success'; + if (mpcp.settings.pulse && + ~this.toPulse.indexOf(value.Id)) { + // pulses twice because of lag + current += ' pulse2'; + } - if (mpcp.settings.pulse && - ~this.toPulse.indexOf(value.Id)) { - // pulses twice because of lag - current += ' pulse2'; - } + //console.log(i + ': start'); - //console.log(i + ': start'); + let prio = ''; - let prio = ''; + if (value.Prio) { + prio = ` [${value.Prio}]`; + } - if (value.Prio) { - prio = ` [${value.Prio}]`; - } + html += `${value.Pos + 1}.${prio}
    ${title}
    `; + } - html += `${value.Pos + 1}.${prio}
    ${title}
    `; - } + this.toPulse = []; + + document.getElementById(this.tbodyid).innerHTML = html; + + mpcp.browser.updatePosition(); + mpcp.pages.update('playlist'); + this.callbackUpdate(); + if (callback) callback(); + }, + + // drag and drop came from a different table + fromSortableSender: function (el, newIndex) { + console.log('from sortable sender (playlist)'); + + // check if "playlist is empty" is showing + if (newIndex == 1 && document.getElementById(this.tbodyid).children[0].classList.contains('rem')) + newIndex = 0; + + if (mpcp.browser.selected.length) { + mpcp.browser.addMulti(newIndex, null, true); + return; + } else if (mpcp.librarySongs.selected.length) { + mpcp.librarySongs.addMulti(newIndex, true); + return; + } else if (el.classList.contains('artist') && + mpcp.libraryArtists.selected.length) { + mpcp.library.addMulti(mpcp.libraryArtists, newIndex, true, true); + return; + } else if (el.classList.contains('album') && + mpcp.libraryAlbums.selected.length) { + mpcp.library.addMulti(mpcp.libraryAlbums, newIndex, true, true); + return; + } - this.toPulse = []; + var artist, album, i; + + // file + // note: multiselect is checked in addDir and addSong! + if (el.classList.contains('file')) { + var file = el.dataset.fileid; + // check if nothing in playlist + if (this.local.length <= 1) { + this.addid(file, undefined, true); + } else { + this.addSong(file, newIndex, true); + } + } else if (el.classList.contains('directory')) { + // directory + var dir = el.dataset.dirid; + console.log('add dir ' + dir); + + if (this.local.length <= 1) { + this.addDir(dir, undefined, true); + } else { + this.addDir(dir, newIndex, true); + } + } else if (el.classList.contains('album')) { + artist = el.dataset.artist; + album = el.dataset.album; + + mpcp.library.getSongsFromAlbum(artist, album, function (files) { + for (i = 0; i < files.length; ++i) { + mpcp.playlist.addSong(files[i].file, newIndex, true); + } + }); + } else if (el.classList.contains('artist')) { + artist = el.dataset.artist; + + mpcp.library.getSongsFromAlbum( + artist, undefined, function (files) { + for (i = 0; i < files.length; ++i) { + mpcp.playlist.addSong(files[i].file, newIndex, true); + } + }); + } else { + console.log('not supported drag for: ' + el.classList); + mpcp.sortHelper.removeItem(e); + } + }, - document.getElementById(this.tbodyid).innerHTML = html; + // drag and drop came from the same table + fromSortableSelf: function (el, newIndex) { + console.log('from sortable self (playlist)'); - mpcp.browser.updatePosition(); - mpcp.pages.update('playlist'); - this.callbackUpdate(); - if (callback) callback(); - }, + var file; - // drag and drop came from a different table - fromSortableSender: function (el, newIndex) { - console.log('from sortable sender (playlist)'); + if (this.selected.length) { + $(this.selected).each(function (item, tr) { + file = $(tr).data().fileid; - // check if "playlist is empty" is showing - if (newIndex == 1 && document.getElementById(this.tbodyid).children[0].classList.contains('rem')) - newIndex = 0; + mpcp.playlist.toPulse.push(file); - if (mpcp.browser.selected.length) { - mpcp.browser.addMulti(newIndex, null, true); - return; - } else if (mpcp.librarySongs.selected.length) { - mpcp.librarySongs.addMulti(newIndex, true); - return; - } else if (el.classList.contains('artist') && - mpcp.libraryArtists.selected.length) { - mpcp.library.addMulti(mpcp.libraryArtists, newIndex, true, true); - return; - } else if (el.classList.contains('album') && - mpcp.libraryAlbums.selected.length) { - mpcp.library.addMulti(mpcp.libraryAlbums, newIndex, true, true); - return; - } + var pos = $(tr).data().pos, + index; - var artist, album, i; - - // file - // note: multiselect is checked in addDir and addSong! - if (el.classList.contains('file')) { - var file = el.dataset.fileid; - // check if nothing in playlist - if (this.local.length <= 1) { - this.addid(file, undefined, true); - } else { - this.addSong(file, newIndex, true); - } - } else if (el.classList.contains('directory')) { - // directory - var dir = el.dataset.dirid; - console.log('add dir ' + dir); - - if (this.local.length <= 1) { - this.addDir(dir, undefined, true); - } else { - this.addDir(dir, newIndex, true); - } - } else if (el.classList.contains('album')) { - artist = el.dataset.artist; - album = el.dataset.album; - - mpcp.library.getSongsFromAlbum(artist, album, function (files) { - for (i = 0; i < files.length; ++i) { - mpcp.playlist.addSong(files[i].file, newIndex, true); - } - }); - } else if (el.classList.contains('artist')) { - artist = el.dataset.artist; - - mpcp.library.getSongsFromAlbum( - artist, undefined, function (files) { - for (i = 0; i < files.length; ++i) { - mpcp.playlist.addSong(files[i].file, newIndex, true); - } - }); + // if original location is below the "move to" + // location + if (pos > newIndex) { + index = newIndex + item; + //console.log('dragged playlist: ' + file + ' to ' + + //index); + komponist.moveid(file, index, function (err) { + if (err) console.log(err); + }); } else { - console.log('not supported drag for: ' + el.classList); - mpcp.sortHelper.removeItem(e); + // else if original location is about the + // "move to" location + index = newIndex; + //console.log('dragged playlist: ' + file + ' to ' + + //index); + komponist.moveid(file, index, function (err) { + if (err) console.log(err); + }); } - }, - - // drag and drop came from the same table - fromSortableSelf: function (el, newIndex) { - console.log('from sortable self (playlist)'); - - var file; - - if (this.selected.length) { - $(this.selected).each(function (item, tr) { - file = $(tr).data().fileid; - - mpcp.playlist.toPulse.push(file); - - var pos = $(tr).data().pos, - index; - - // if original location is below the "move to" - // location - if (pos > newIndex) { - index = newIndex + item; - //console.log('dragged playlist: ' + file + ' to ' + - //index); - komponist.moveid(file, index, function (err) { - if (err) console.log(err); - }); - } else { - // else if original location is about the - // "move to" location - index = newIndex; - //console.log('dragged playlist: ' + file + ' to ' + - //index); - komponist.moveid(file, index, function (err) { - if (err) console.log(err); - }); - } - }); + }); - mpcp.utils.clearSelected(this); - } else { - file = el.dataset.fileid; - this.toPulse.push(file); - //console.log('dragged playlist: ' + file + ' to ' + newIndex); + mpcp.utils.clearSelected(this); + } else { + file = el.dataset.fileid; + this.toPulse.push(file); + //console.log('dragged playlist: ' + file + ' to ' + newIndex); - komponist.moveid(file, newIndex, function (err) { - if (err) console.log(err); - }); - } - }, - - // wrapper for komponist.addid - addid: function (file, to, callback) { + komponist.moveid(file, newIndex, function (err) { + if (err) console.log(err); + }); + } + }, - if (mpcp.utils.isNumber(to)) - komponist.addid(file, to, function (err, val) { - if (err) - console.log(err); - else - mpcp.playlist.toPulse.push(val.Id); + // wrapper for komponist.addid + addid: function (file, to, callback) { - mpcp.playlist.addCallbackUpdate(callback); - }); + if (mpcp.utils.isNumber(to)) + komponist.addid(file, to, function (err, val) { + if (err) + console.log(err); else - komponist.addid(file, function (err, val) { - if (err) - console.log(err); - else - mpcp.playlist.toPulse.push(val.Id); - - mpcp.playlist.addCallbackUpdate(callback); - }); - }, - - // wrapper for komponist.add - add: function (dir, to, callback) { - if (mpcp.utils.isNumber(to)) { - mpcp.utils.getAllInfo(dir, function (files) { - if (!files.length) { - console.log('Nothing in db'); - if (callback) callback(); - return; - } - - //console.log(files); - var j = 0; - - $(files).each(function (item, value) { - //console.log(value); - if (value.file) { - mpcp.playlist.addid(value.file, to++, function () { - if (++j == Object.keys(files).length) - mpcp.playlist.addCallbackUpdate(callback); - }); - } else { - if (++j == Object.keys(files).length) - mpcp.playlist.addCallbackUpdate(callback); - } - }); - }); - } else { - komponist.add(dir, function (err, val) { - //val returns an empty object (I was hoping for an Id list) - //console.log(val); - if (err) - console.log(err); - //else - //mpcp.playlist.toPulse.push(val.Id); + mpcp.playlist.toPulse.push(val.Id); - mpcp.playlist.addCallbackUpdate(callback); - }); - } - }, - - // addSong - addSong: function (file, to, dontScroll, callback) { - console.log('adding song to playlist'); - - if (!dontScroll) { - if (to === undefined || isNaN(to)) { - this.addCallbackUpdate(function () { - mpcp.pages.go('playlist', mpcp.pages.totalPlaylist, true); - }); - } else if (mpcp.player.current && - to == mpcp.player.current.Pos + 1) { - this.addCallbackUpdate(function () { - mpcp.playlist.goToCurrent(); - }); - } else { - this.addCallbackUpdate(function () { - mpcp.playlist.goToPos(to); - }); - } - } + mpcp.playlist.addCallbackUpdate(callback); + }); + else + komponist.addid(file, function (err, val) { + if (err) + console.log(err); + else + mpcp.playlist.toPulse.push(val.Id); - this.addid(file, to, callback); - }, - - // addDir - addDir: function (dir, to, dontScroll, callback) { - console.log('adding dir to playlist'); - - if (!dontScroll) { - if (to === undefined || isNaN(to)) { - this.addCallbackUpdate(function () { - mpcp.pages.go('playlist', mpcp.pages.totalPlaylist, true); - }); - } else { - this.addCallbackUpdate(function () { - mpcp.playlist.goToPos(to); - }); - } + mpcp.playlist.addCallbackUpdate(callback); + }); + }, + + // wrapper for komponist.add + add: function (dir, to, callback) { + if (mpcp.utils.isNumber(to)) { + mpcp.utils.getAllInfo(dir, function (files) { + if (!files.length) { + console.log('Nothing in db'); + if (callback) callback(); + return; } - this.add(dir, to, callback); - }, + //console.log(files); + var j = 0; - // wrapper for komponist.findadd - findAdd: function (artist, album) { - if (!album) { - komponist.findadd('artist', artist, function (err, files) { - setSongs(err, files); - }); - } else { - komponist.findadd( - 'artist', artist, 'album', album, function (err, files) { - setSongs(err, files); + $(files).each(function (item, value) { + //console.log(value); + if (value.file) { + mpcp.playlist.addid(value.file, to++, function () { + if (++j == Object.keys(files).length) + mpcp.playlist.addCallbackUpdate(callback); }); - } + } else { + if (++j == Object.keys(files).length) + mpcp.playlist.addCallbackUpdate(callback); + } + }); + }); + } else { + komponist.add(dir, function (err, val) { + //val returns an empty object (I was hoping for an Id list) + //console.log(val); + if (err) + console.log(err); + //else + //mpcp.playlist.toPulse.push(val.Id); - function setSongs (err, files) { - if (err) console.log(err); - // no files in response + mpcp.playlist.addCallbackUpdate(callback); + }); + } + }, - mpcp.playlist.addCallbackUpdate(function () { - mpcp.pages.go('playlist', mpcp.pages.totalPlaylist, true); - }); - } - }, - - // plays the song in the playlist - playSong: function (file) { - console.log('play song from playlist: ' + file); - komponist.playid(file, function (err, val) { - // false positive error - if (err && err.message != - 'Integer expected: undefined [2@0] {playid}') { - console.log('Error: No song found to play'); - console.log(err); - } + // addSong + addSong: function (file, to, dontScroll, callback) { + console.log('adding song to playlist'); + + if (!dontScroll) { + if (to === undefined || isNaN(to)) { + this.addCallbackUpdate(function () { + mpcp.pages.go('playlist', mpcp.pages.totalPlaylist, true); }); - }, - - // wrapper for playSong, given an element - play: function (ele) { - var file = $(ele).data().fileid; - this.playSong(file); - }, - - // update the current playlist title - updateTitle: function (title) { - console.log('got pl title from another user: ' + title); - - mpcp.history.add('Loaded playlist: ' + title); - this.current = title.replace(/ /g, '\u00a0'); - - // fixes a bug where the playlist doesn't get updated - // after a new one is loadded - this.updateAll(); - }, - - // remove duplicate files from the playlist - removeDuplicates: function (callback) { - console.log('remove duplicates'); - var duplicate = {}, - j = 0; - - $(this.local).each(function (item, file) { - if (duplicate[file.file] && (mpcp.player.current === null || - file.Pos != mpcp.player.current.Pos)) { - komponist.deleteid(file.Id, function (err, val) { - if (err) { - console.log('Error: Cannot remove duplicate file, this might be a bug'); - console.log(err); - } - - if (++j == Object.keys(mpcp.playlist.local).length && - callback) - mpcp.playlist.addCallbackUpdate(callback); - }); - } else { - duplicate[file.file] = true; - - if (++j == Object.keys(mpcp.playlist.local).length && callback) - mpcp.playlist.addCallbackUpdate(callback); - } + } else if (mpcp.player.current && + to == mpcp.player.current.Pos + 1) { + this.addCallbackUpdate(function () { + mpcp.playlist.goToCurrent(); }); - }, - - // TODO Goal: remove song from playlist without mpd updating the playlist - // locally. Have to update this.local .Pos to work, or somehow - // use numbered table rows instead of using Pos. - // remove song from the playlist. The element must be removed - // manually before or after calling! - removeSong: function (fileid) { - komponist.deleteid(fileid, function (err, val) { - if (err) console.log('No song with id ' + fileid + ' to delete!'); + } else { + this.addCallbackUpdate(function () { + mpcp.playlist.goToPos(to); }); + } + } - // playlist doesn't get updated when the same song being removed is - // playling (future me: this happens with not only pause, but other - // times as well, so dont check for a pause flag!) - if (mpcp.player.current && fileid == mpcp.player.current.Id) - socket.send(JSON.stringify( - {'type': 'update-playlist'}), function (err) { - if (err) console.log(err); - }); - }, - - // wrapper for removeSong, given an element - remove: function (ele) { - //console.log(this.selected); - var file; - - // multiselect check (any left clicks) - if (this.selected.length) { - $(this.selected).each(function (item, tr) { - //console.log(tr); - file = $(tr).data().fileid; - mpcp.playlist.removeSong(file); - }); + this.addid(file, to, callback); + }, - // clear this.selected just in case. - mpcp.utils.clearSelected(this); - } else { - // single file (fallback) - file = $(ele).data().fileid; - this.removeSong(file); - } - }, + // addDir + addDir: function (dir, to, dontScroll, callback) { + console.log('adding dir to playlist'); - // adds the song after the currently playing song - addToCurrent: function (file, type) { - var newPos = mpcp.player.current.Pos + 1; - this.goAfterUpdate = true; + if (!dontScroll) { + if (to === undefined || isNaN(to)) { + this.addCallbackUpdate(function () { + mpcp.pages.go('playlist', mpcp.pages.totalPlaylist, true); + }); + } else { + this.addCallbackUpdate(function () { + mpcp.playlist.goToPos(to); + }); + } + } - if (mpcp.browser.selected.length) { - mpcp.browser.addMulti(newPos); - return; - } + this.add(dir, to, callback); + }, + + // wrapper for komponist.findadd + findAdd: function (artist, album) { + if (!album) { + komponist.findadd('artist', artist, function (err, files) { + setSongs(err, files); + }); + } else { + komponist.findadd( + 'artist', artist, 'album', album, function (err, files) { + setSongs(err, files); + }); + } - if (type == 'file') { - this.addSong(file, newPos, true); - } else if (type == 'dir') { - this.addDir(file, newPos, true); - } - }, - - // move song to the top of the playlist - moveToTop: function (file, type) { - //console.log(file); - // goes to page first because of pulse being cleared - $('#pslwrap').scrollTop($(mpcp.playlist.table)); - mpcp.pages.go('playlist', 1); - - // multiselect check - if (mpcp.playlist.selected.length) { - $(mpcp.playlist.selected).each(function (item, tr) { - //console.log(tr); - file = $(tr).data().fileid; - mpcp.playlist.toPulse.push(file); - - komponist.moveid(file, item, function (err) { - if (err) console.log(err); - }); - }); + function setSongs (err, files) { + if (err) console.log(err); + // no files in response - // clear selected just in case. - mpcp.utils.clearSelected(mpcp.playlist); - } else { - mpcp.playlist.toPulse.push(file); - console.log(file); + mpcp.playlist.addCallbackUpdate(function () { + mpcp.pages.go('playlist', mpcp.pages.totalPlaylist, true); + }); + } + }, + + // plays the song in the playlist + playSong: function (file) { + console.log('play song from playlist: ' + file); + komponist.playid(file, function (err, val) { + // false positive error + if (err && err.message != + 'Integer expected: undefined [2@0] {playid}') { + console.log('Error: No song found to play'); + console.log(err); + } + }); + }, + + // wrapper for playSong, given an element + play: function (ele) { + var file = $(ele).data().fileid; + this.playSong(file); + }, + + // update the current playlist title + updateTitle: function (title) { + console.log('got pl title from another user: ' + title); + + mpcp.history.add('Loaded playlist: ' + title); + this.current = title.replace(/ /g, '\u00a0'); + + // fixes a bug where the playlist doesn't get updated + // after a new one is loadded + this.updateAll(); + }, + + // remove duplicate files from the playlist + removeDuplicates: function (callback) { + console.log('remove duplicates'); + var duplicate = {}, + j = 0; + + $(this.local).each(function (item, file) { + if (duplicate[file.file] && (mpcp.player.current === null || + file.Pos != mpcp.player.current.Pos)) { + komponist.deleteid(file.Id, function (err, val) { + if (err) { + console.log('Error: Cannot remove duplicate file, this might be a bug'); + console.log(err); + } + + if (++j == Object.keys(mpcp.playlist.local).length && + callback) + mpcp.playlist.addCallbackUpdate(callback); + }); + } else { + duplicate[file.file] = true; + + if (++j == Object.keys(mpcp.playlist.local).length && callback) + mpcp.playlist.addCallbackUpdate(callback); + } + }); + }, + + // TODO Goal: remove song from playlist without mpd updating the playlist + // locally. Have to update this.local .Pos to work, or somehow + // use numbered table rows instead of using Pos. + // remove song from the playlist. The element must be removed + // manually before or after calling! + removeSong: function (fileid) { + komponist.deleteid(fileid, function (err, val) { + if (err) console.log('No song with id ' + fileid + ' to delete!'); + }); + + // playlist doesn't get updated when the same song being removed is + // playling (future me: this happens with not only pause, but other + // times as well, so dont check for a pause flag!) + if (mpcp.player.current && fileid == mpcp.player.current.Id) + socket.send(JSON.stringify( + {'type': 'update-playlist'}), function (err) { + if (err) console.log(err); + }); + }, + + // wrapper for removeSong, given an element + remove: function (ele) { + //console.log(this.selected); + var file; + + // multiselect check (any left clicks) + if (this.selected.length) { + $(this.selected).each(function (item, tr) { + //console.log(tr); + file = $(tr).data().fileid; + mpcp.playlist.removeSong(file); + }); + + // clear this.selected just in case. + mpcp.utils.clearSelected(this); + } else { + // single file (fallback) + file = $(ele).data().fileid; + this.removeSong(file); + } + }, - komponist.moveid(file, 0, function (err) { - if (err) console.log(err); - }); - } - }, - - // move song to the currently playing song in the playlist - moveToCurrent: function (file) { - //console.log(mpcp.player.current.Pos); - //console.log(file); - - var newPos = mpcp.player.current.Pos + 1; - - // multiselect check - if (mpcp.playlist.selected.length) { - // only do this if the the moved songs are above the current song - var lastPos = $(mpcp.playlist.selected[ - mpcp.playlist.selected.length-1]). - data().pos; - - mpcp.playlist.goAfterUpdate = true; - - $(mpcp.playlist.selected).each(function (item, tr) { - var fileid = $(tr).data().fileid, - pos = $(file).data().pos; - - mpcp.playlist.toPulse.push(fileid); - - // currently playing song is above file to be moved - if (mpcp.player.current && mpcp.player.current.Pos < pos) - newPos = mpcp.player.current.Pos + 1 + item; - // currently playing song is below file to be moved - else if ((mpcp.player.current && - mpcp.player.current.Pos > pos) || - mpcp.player.current) - newPos = mpcp.player.current.Pos; - // currently playing song is the same file to be moved - else - newPos = 0 + item; - - //console.log(newPos); - komponist.moveid(fileid, newPos, function (err) { - if (err) console.log(err); - }); - }); + // adds the song after the currently playing song + addToCurrent: function (file, type) { + var newPos = mpcp.player.current.Pos + 1; + this.goAfterUpdate = true; - // clear selected just in case. - mpcp.utils.clearSelected(mpcp.playlist); - } else { - mpcp.playlist.goAfterUpdate = true; - var fileid = $(file).data().fileid, - pos = $(file).data().pos; - - mpcp.playlist.toPulse.push(fileid); - - // currently playing song is above file to be moved - if (mpcp.player.current && mpcp.player.current.Pos < pos) - newPos = mpcp.player.current.Pos + 1; - // currently playing song is below file to be moved - else if ((mpcp.player.current && mpcp.player.current.Pos > pos) || - mpcp.player.current) - newPos = mpcp.player.current.Pos; - // currently playing song is the same file to be moved - else - newPos = 0; - - //console.log(newPos); - komponist.moveid(fileid, newPos, function (err) { - if (err) console.log(err); - }); - } - }, - - // move song to the bottom of the playlist - moveToBottom: function (file) { - var index = mpcp.playlist.local.length; - //console.log(index); - - // goes to page first because of pulse being cleared - mpcp.pages.go('playlist', mpcp.pages.totalPlaylist); - $('#pslwrap').scrollTop($(mpcp.playlist.table)[0].scrollHeight); - - // multiselect check - if (mpcp.playlist.selected.length) { - $(mpcp.playlist.selected).each(function (item, tr) { - file = $(tr).data().fileid; - mpcp.playlist.toPulse.push(file); - - komponist.moveid(file, (index - 1), function (err) { - if (err) console.log(err); - }); - }); + if (mpcp.browser.selected.length) { + mpcp.browser.addMulti(newPos); + return; + } - // clear selected just in case. - mpcp.utils.clearSelected(mpcp.playlist); - } else { - //console.log(file); - mpcp.playlist.toPulse.push(file); + if (type == 'file') { + this.addSong(file, newPos, true); + } else if (type == 'dir') { + this.addDir(file, newPos, true); + } + }, + + // move song to the top of the playlist + moveToTop: function (file, type) { + //console.log(file); + // goes to page first because of pulse being cleared + $('#pslwrap').scrollTop($(mpcp.playlist.table)); + mpcp.pages.go('playlist', 1); + + // multiselect check + if (mpcp.playlist.selected.length) { + $(mpcp.playlist.selected).each(function (item, tr) { + //console.log(tr); + file = $(tr).data().fileid; + mpcp.playlist.toPulse.push(file); + + komponist.moveid(file, item, function (err) { + if (err) console.log(err); + }); + }); - komponist.moveid(file, (index - 1), function (err) { - if (err) console.log(err); - }); - } - }, + // clear selected just in case. + mpcp.utils.clearSelected(mpcp.playlist); + } else { + mpcp.playlist.toPulse.push(file); + console.log(file); - // go to the page that the position of the song is located in - goToPos: function (pos) { - mpcp.pages.go('playlist', pos / mpcp.pages.maxPlaylist + 1); - }, + komponist.moveid(file, 0, function (err) { + if (err) console.log(err); + }); + } + }, + + // move song to the currently playing song in the playlist + moveToCurrent: function (file) { + //console.log(mpcp.player.current.Pos); + //console.log(file); + var newPos = mpcp.player.current.Pos + 1; + + // multiselect check + if (mpcp.playlist.selected.length) { + // only do this if the the moved songs are above the current song + var lastPos = $(mpcp.playlist.selected[ + mpcp.playlist.selected.length-1]). + data().pos; + + mpcp.playlist.goAfterUpdate = true; + + $(mpcp.playlist.selected).each(function (item, tr) { + var fileid = $(tr).data().fileid, + pos = $(file).data().pos; + + mpcp.playlist.toPulse.push(fileid); + + // currently playing song is above file to be moved + if (mpcp.player.current && mpcp.player.current.Pos < pos) + newPos = mpcp.player.current.Pos + 1 + item; + // currently playing song is below file to be moved + else if ((mpcp.player.current && + mpcp.player.current.Pos > pos) || + mpcp.player.current) + newPos = mpcp.player.current.Pos; + // currently playing song is the same file to be moved + else + newPos = 0 + item; - // goes to the current song in the playlist. - goToCurrent: function (callback) { - console.log('go to current'); + //console.log(newPos); + komponist.moveid(fileid, newPos, function (err) { + if (err) console.log(err); + }); + }); + + // clear selected just in case. + mpcp.utils.clearSelected(mpcp.playlist); + } else { + mpcp.playlist.goAfterUpdate = true; + var fileid = $(file).data().fileid, + pos = $(file).data().pos; + + mpcp.playlist.toPulse.push(fileid); + + // currently playing song is above file to be moved + if (mpcp.player.current && mpcp.player.current.Pos < pos) + newPos = mpcp.player.current.Pos + 1; + // currently playing song is below file to be moved + else if ((mpcp.player.current && mpcp.player.current.Pos > pos) || + mpcp.player.current) + newPos = mpcp.player.current.Pos; + // currently playing song is the same file to be moved + else + newPos = 0; + + //console.log(newPos); + komponist.moveid(fileid, newPos, function (err) { + if (err) console.log(err); + }); + } + }, - if (!mpcp.player.current) { - console.log('no song selected'); - if (callback) callback(); - return; - } + // move song to the bottom of the playlist + moveToBottom: function (file) { + var index = mpcp.playlist.local.length; + //console.log(index); - // scroll to top to avoid scrolling bugs - $('#pslwrap').scrollTop($(this.table)); + // goes to page first because of pulse being cleared + mpcp.pages.go('playlist', mpcp.pages.totalPlaylist); + $('#pslwrap').scrollTop($(mpcp.playlist.table)[0].scrollHeight); - // go to the page the song is playing on - this.goToPos(mpcp.player.current.Pos); - // try to go above the element, so the item is semi-centered - var to = (mpcp.player.current.Pos % mpcp.pages.maxPlaylist) - 5; + // multiselect check + if (mpcp.playlist.selected.length) { + $(mpcp.playlist.selected).each(function (item, tr) { + file = $(tr).data().fileid; + mpcp.playlist.toPulse.push(file); - if (to < 1) to = 1; + komponist.moveid(file, (index - 1), function (err) { + if (err) console.log(err); + }); + }); - console.log('scroll to ' + to); + // clear selected just in case. + mpcp.utils.clearSelected(mpcp.playlist); + } else { + //console.log(file); + mpcp.playlist.toPulse.push(file); - var toOffset = $(this.tbody + ' .gen:nth-child(' + to + ')'); + komponist.moveid(file, (index - 1), function (err) { + if (err) console.log(err); + }); + } + }, - if (toOffset.length) { - var offset = toOffset.offset().top; - $('#pslwrap').scrollTop(offset); - } + // go to the page that the position of the song is located in + goToPos: function (pos) { + mpcp.pages.go('playlist', pos / mpcp.pages.maxPlaylist + 1); + }, - if (callback) callback(); - }, + // goes to the current song in the playlist. + goToCurrent: function (callback) { + console.log('go to current'); - // show song information to the user - getSongInfo: function (file, callback) { - komponist.playlistfind('file', file, function (err, value) { - mpcp.utils.parseSongInfo(err, value[0], callback); - }); - }, + if (!mpcp.player.current) { + console.log('no song selected'); + if (callback) callback(); + return; + } - search: function (val, callback) { - if (!val) - val = mpcp.playlist.searchTerm; - else - mpcp.playlist.searchTerm = val; + // scroll to top to avoid scrolling bugs + $('#pslwrap').scrollTop($(this.table)); - // set to true (in case of after clicking clear) - mpcp.playlist.isSearching = true; + // go to the page the song is playing on + this.goToPos(mpcp.player.current.Pos); + // try to go above the element, so the item is semi-centered + var to = (mpcp.player.current.Pos % mpcp.pages.maxPlaylist) - 5; - // search for tags - komponist.playlistsearch('any', val, function(err, anyFiles) { - - // search for file names - komponist.playlistsearch('file', val, function(err, files) { - if (err) { - if (callback) callback(); - return console.log(err); - } - - //console.log(files); - - anyFiles = mpcp.utils.toArray(anyFiles); - files = mpcp.utils.toArray(files); - - var unique = mpcp.utils.concatDedupe(anyFiles, files); - - //console.log(unique); - - $(mpcp.playlist.table + ' .gen').remove(); - mpcp.playlist.local = unique; - - if ($.isEmptyObject(unique[0])) { - var html = 'No songs found'; - document.getElementById(mpcp.playlist.tbodyid).innerHTML = html; - // fix for removing the last song that's - // playling from the playlist - mpcp.playlist.doUpdate = true; - mpcp.player.updateAll(); - mpcp.pages.update('playlist'); - mpcp.browser.updatePosition(); - if (callback) callback(); - return console.log('No songs found in playlist search'); - } - - // TODO figure out a way to use this.local - // efficiently with mpcp.browser.updatePlaylist instead of - // utilizing this.list - $(mpcp.playlist.local).each(function (item, value) { - mpcp.playlist.list.files.push(value.file); - mpcp.playlist.list.positions.push(value.Pos); - }); - - mpcp.playlist.updateLocal(function () { - // fixes issues such as the last song not updating the player; - mpcp.player.updateAll(callback); - }); - }); - }); - }, + if (to < 1) to = 1; - resetSearch: function (callback) { - mpcp.playlist.addCallbackUpdate(callback); - mpcp.playlist.isSearching = false; - mpcp.playlist.updateAll(); - }, + console.log('scroll to ' + to); - // open playlist from stored element - openFromStored: function (callback) { - if ($('#playlist-open-modal .selected').length) { - var file = $('#playlist-open-modal .selected').data().fileid; - mpcp.stored.open(file, callback); - } else { - mpcp.lazyToast.warning('No playlist was selected', 'Playlist'); + var toOffset = $(this.tbody + ' .gen:nth-child(' + to + ')'); - if (callback) callback(); - } - }, - - // save playlist from stored element - saveFromStored: function (callback) { - console.log('confirm save playlist'); - var file = document.getElementById('playlist-save-input').value; - mpcp.stored.save(file, callback); - }, - - // create a queue because the server responds once for a dom update - addCallbackUpdate: function (callback) { - if (typeof callback === 'function') { - this.callbackUpdates.push(callback); - } else if (callback) { - console.log('!!! This is not a function, fix it:'); - console.log(callback); - } - }, - - callbackUpdate: function () { - // remove callback from array instead of just resetting the array - // afterward to fix an issue where callbackUpdates was not reset - // because a callback was executing a NEW callback stack. - while (this.callbackUpdates.length) { - var callback = this.callbackUpdates[0]; - this.callbackUpdates.splice(0, 1); - - if (!this.callbackUpdates.length) - return callback(); - else - callback(); + if (toOffset.length) { + var offset = toOffset.offset().top; + $('#pslwrap').scrollTop(offset); + } + + if (callback) callback(); + }, + + // show song information to the user + getSongInfo: function (file, callback) { + komponist.playlistfind('file', file, function (err, value) { + mpcp.utils.parseSongInfo(err, value[0], callback); + }); + }, + + search: function (val, callback) { + if (!val) + val = mpcp.playlist.searchTerm; + else + mpcp.playlist.searchTerm = val; + + // set to true (in case of after clicking clear) + mpcp.playlist.isSearching = true; + + // search for tags + komponist.playlistsearch('any', val, function(err, anyFiles) { + + // search for file names + komponist.playlistsearch('file', val, function(err, files) { + if (err) { + if (callback) callback(); + return console.log(err); } - }, - clear: function (callback) { - console.log('clear playlist'); - this.addCallbackUpdate(callback); + //console.log(files); - // this is done server-side to fix a bug: - // refresh -> add -> play -> clear does not work - socket.send(JSON.stringify({'type': 'clear-playlist'}), - function (err) { - if (err) console.log(err); - }); - }, + anyFiles = mpcp.utils.toArray(anyFiles); + files = mpcp.utils.toArray(files); - scramble: function (callback) { - console.log('scramble playlist'); - komponist.shuffle(function (err) { - if (err) console.log(err); - mpcp.playlist.addCallbackUpdate(callback); - }); - }, + var unique = mpcp.utils.concatDedupe(anyFiles, files); - setPriority: (priority, fileid) => { - komponist.prioid(priority, fileid, (err) => { - if (err) console.error(err); - }); - }, + //console.log(unique); - setPriorityFromForm: () => { - if (!document.getElementById('priority-form').checkValidity()) { - document.getElementById('priority-submit').click(); - } else { - $('#set-priority-modal').modal('hide'); - let priority = document.getElementById('priority').value; - let fileid = document.getElementById('priority-form').dataset.fileid; - mpcp.playlist.setPriority(priority, fileid); - } - }, + $(mpcp.playlist.table + ' .gen').remove(); + mpcp.playlist.local = unique; - initEvents: function () { - $('#new-playlist').click(function () { mpcp.pe.newLocal(); }); + if ($.isEmptyObject(unique[0])) { + var html = 'No songs found'; + document.getElementById(mpcp.playlist.tbodyid).innerHTML = html; + // fix for removing the last song that's + // playling from the playlist + mpcp.playlist.doUpdate = true; + mpcp.player.updateAll(); + mpcp.pages.update('playlist'); + mpcp.browser.updatePosition(); + if (callback) callback(); + return console.log('No songs found in playlist search'); + } - $('#scramble').click(function () { - mpcp.playlist.scramble(); + // TODO figure out a way to use this.local + // efficiently with mpcp.browser.updatePlaylist instead of + // utilizing this.list + $(mpcp.playlist.local).each(function (item, value) { + mpcp.playlist.list.files.push(value.file); + mpcp.playlist.list.positions.push(value.Pos); }); - $('#remove-duplicates').click(function () { - mpcp.playlist.removeDuplicates(); + mpcp.playlist.updateLocal(function () { + // fixes issues such as the last song not updating the player; + mpcp.player.updateAll(callback); }); + }); + }); + }, + + resetSearch: function (callback) { + mpcp.playlist.addCallbackUpdate(callback); + mpcp.playlist.isSearching = false; + mpcp.playlist.updateAll(); + }, + + // open playlist from stored element + openFromStored: function (callback) { + if ($('#playlist-open-modal .selected').length) { + var file = $('#playlist-open-modal .selected').data().fileid; + mpcp.stored.open(file, callback); + } else { + mpcp.lazyToast.warning('No playlist was selected', 'Playlist'); + + if (callback) callback(); + } + }, + + // save playlist from stored element + saveFromStored: function (callback) { + console.log('confirm save playlist'); + var file = document.getElementById('playlist-save-input').value; + mpcp.stored.save(file, callback); + }, + + // create a queue because the server responds once for a dom update + addCallbackUpdate: function (callback) { + if (typeof callback === 'function') { + this.callbackUpdates.push(callback); + } else if (callback) { + console.log('!!! This is not a function, fix it:'); + console.log(callback); + } + }, + + callbackUpdate: function () { + // remove callback from array instead of just resetting the array + // afterward to fix an issue where callbackUpdates was not reset + // because a callback was executing a NEW callback stack. + while (this.callbackUpdates.length) { + var callback = this.callbackUpdates[0]; + this.callbackUpdates.splice(0, 1); + + if (!this.callbackUpdates.length) + return callback(); + else + callback(); + } + }, + + clear: function (callback) { + console.log('clear playlist'); + this.addCallbackUpdate(callback); + + // this is done server-side to fix a bug: + // refresh -> add -> play -> clear does not work + socket.send(JSON.stringify({'type': 'clear-playlist'}), + function (err) { + if (err) console.log(err); + }); + }, + + scramble: function (callback) { + console.log('scramble playlist'); + komponist.shuffle(function (err) { + if (err) console.log(err); + mpcp.playlist.addCallbackUpdate(callback); + }); + }, + + setPriority: (priority, fileid) => { + komponist.prioid(priority, fileid, (err) => { + if (err) console.error(err); + }); + }, + + setPriorityFromForm: () => { + if (!document.getElementById('priority-form').checkValidity()) { + document.getElementById('priority-submit').click(); + } else { + $('#set-priority-modal').modal('hide'); + let priority = document.getElementById('priority').value; + let fileid = document.getElementById('priority-form').dataset.fileid; + mpcp.playlist.setPriority(priority, fileid); + } + }, - $('#open-playlist').click(function () { - console.log('open playlists'); - mpcp.stored.updatePlaylists('#playlist-open-modal'); - }); + initEvents: function () { + $('#new-playlist').click(function () { mpcp.pe.newLocal(); }); - $('#save-playlist').click(function () { - console.log('save playlist'); - mpcp.stored.updatePlaylists('#playlist-save-modal'); - }); + $('#scramble').click(function () { + mpcp.playlist.scramble(); + }); - $('#playlist-save-confirm').click(function () { - mpcp.playlist.saveFromStored(); - }); + $('#remove-duplicates').click(function () { + mpcp.playlist.removeDuplicates(); + }); - $('#playlist-open-confirm').click(function () { - mpcp.playlist.openFromStored(); - }); + $('#open-playlist').click(function () { + console.log('open playlists'); + mpcp.stored.updatePlaylists('#playlist-open-modal'); + }); - $('#clear-playlist').click(function () { - mpcp.playlist.clear(); - }); + $('#save-playlist').click(function () { + console.log('save playlist'); + mpcp.stored.updatePlaylists('#playlist-save-modal'); + }); - $('#playlist-search-toggle').click(function () { - if (document.getElementById('playlist-search-toggle').classList.contains('active')) { - document.getElementById('playlist-search-toggle').classList.remove('active'); - document.getElementById('playlist-search').style.display = 'none'; - mpcp.playlist.isSearching = false; - document.getElementById('search-playlist').value = ''; - mpcp.playlist.searchTerm = ''; - mpcp.playlist.updateAll(); - } else { - document.getElementById('playlist-search-toggle').classList.add('active'); - document.getElementById('playlist-search').style.display = 'block'; - $('#search-playlist').focus(); - mpcp.playlist.isSearching = true; - } - }); + $('#playlist-save-confirm').click(function () { + mpcp.playlist.saveFromStored(); + }); - mpcp.utils.createSearch('#search-playlist', this.search, this.resetSearch, '#search-playlist-clear'); + $('#playlist-open-confirm').click(function () { + mpcp.playlist.openFromStored(); + }); - $(document).on('click', '.song-remove', function () { - var ele = $(this).parent().parent(); - //console.log(ele); - mpcp.playlist.remove(ele); - }); + $('#clear-playlist').click(function () { + mpcp.playlist.clear(); + }); - $(document).on('dblclick', this.tbody + ' tr', function () { - var file = $(this).data().fileid; - mpcp.playlist.playSong(file); - }); + $('#playlist-search-toggle').click(function () { + if (document.getElementById('playlist-search-toggle').classList.contains('active')) { + document.getElementById('playlist-search-toggle').classList.remove('active'); + document.getElementById('playlist-search').style.display = 'none'; + mpcp.playlist.isSearching = false; + document.getElementById('search-playlist').value = ''; + mpcp.playlist.searchTerm = ''; + mpcp.playlist.updateAll(); + } else { + document.getElementById('playlist-search-toggle').classList.add('active'); + document.getElementById('playlist-search').style.display = 'block'; + $('#search-playlist').focus(); + mpcp.playlist.isSearching = true; + } + }); - $(document).on('click', '.song-play', function () { - var file = $(this).parent().parent().data().fileid; - mpcp.playlist.playSong(file); - }); + mpcp.utils.createSearch('#search-playlist', this.search, this.resetSearch, '#search-playlist-clear'); - $('#set-priority-confirm').click(() => { - mpcp.playlist.setPriorityFromForm(); - }); + $(document).on('click', '.song-remove', function () { + var ele = $(this).parent().parent(); + //console.log(ele); + mpcp.playlist.remove(ele); + }); - document.getElementById('priority-form').addEventListener('submit', () => { - mpcp.playlist.setPriorityFromForm(); - }); + $(document).on('dblclick', this.tbody + ' tr', function () { + var file = $(this).data().fileid; + mpcp.playlist.playSong(file); + }); - mpcp.utils.multiSelect(this, ['song-remove']); - } + $(document).on('click', '.song-play', function () { + var file = $(this).parent().parent().data().fileid; + mpcp.playlist.playSong(file); + }); + + $('#set-priority-confirm').click(() => { + mpcp.playlist.setPriorityFromForm(); + }); + + document.getElementById('priority-form').addEventListener('submit', () => { + mpcp.playlist.setPriorityFromForm(); + }); + + mpcp.utils.multiSelect(this, ['song-remove']); + } }; }; diff --git a/src/playlisteditor.js b/src/playlisteditor.js index bc97531..c56af59 100644 --- a/src/playlisteditor.js +++ b/src/playlisteditor.js @@ -2,443 +2,443 @@ module.exports = function (mpcp) { // the playlist editor return { - current: null, - selector: '#pe', - table: '#pe-song-list', - tableid: 'pe-song-list', - tbody: '#pe-song-list .append', - tbodyid: 'pe-song-list-tbody', - minimized: false, - // selected items from multiSelect - selected: [], - - // future: share playlists to edit? - newLocal: function () { - if (this.minimized) { - this.minimized = false; - this.resume(); - } else if (!this.current) { - this.current = 'local'; - document.getElementById('pe').style.display = 'flex'; - this.clear(); - } - }, - - getHtml: function (title, file) { - var extra = ''; - if (mpcp.settings.pulse) extra += 'pulse'; - - return '
    ' + title + '
    '; - }, - - // file object, position to put song - // file can be an array of 'file' objects - addSong: function (file, pos, callback) { - //console.log('adding song to pe: ' + this.current); - //console.log(file); - this.removeNothingMessage(); - - var title, html = ''; - - if (Array.isArray(file)) { - for (var i = 0; i < file.length; ++i) { - title = mpcp.utils.getSimpleTitle(file[i].Title, file[i].Artist, - file[i].file); - html += this.getHtml(title, file[i].file); - } - } else { - title = mpcp.utils.getSimpleTitle(file.Title, file.Artist, file.file); - html = this.getHtml(title, file.file); - } + current: null, + selector: '#pe', + table: '#pe-song-list', + tableid: 'pe-song-list', + tbody: '#pe-song-list .append', + tbodyid: 'pe-song-list-tbody', + minimized: false, + // selected items from multiSelect + selected: [], + + // future: share playlists to edit? + newLocal: function () { + if (this.minimized) { + this.minimized = false; + this.resume(); + } else if (!this.current) { + this.current = 'local'; + document.getElementById('pe').style.display = 'flex'; + this.clear(); + } + }, + + getHtml: function (title, file) { + var extra = ''; + if (mpcp.settings.pulse) extra += 'pulse'; + + return '
    ' + title + '
    '; + }, + + // file object, position to put song + // file can be an array of 'file' objects + addSong: function (file, pos, callback) { + //console.log('adding song to pe: ' + this.current); + //console.log(file); + this.removeNothingMessage(); + + var title, html = ''; + + if (Array.isArray(file)) { + for (var i = 0; i < file.length; ++i) { + title = mpcp.utils.getSimpleTitle(file[i].Title, file[i].Artist, + file[i].file); + html += this.getHtml(title, file[i].file); + } + } else { + title = mpcp.utils.getSimpleTitle(file.Title, file.Artist, file.file); + html = this.getHtml(title, file.file); + } - if (pos >= 0) { - if (!pos) { - $(this.tbody).prepend(html); - $('#pe-main').scrollTop($(this.table)); - } else { - //console.log('add to ' + pos); - $(this.tbody + ' > .gen:nth-child(' + (pos) + ')').after(html); - } - } else { - //console.log('add to bottom'); - document.getElementById(this.tbodyid).innerHTML += html; - $('#pe-main').scrollTop($(this.table)[0].scrollHeight); - } + if (pos >= 0) { + if (!pos) { + $(this.tbody).prepend(html); + $('#pe-main').scrollTop($(this.table)); + } else { + //console.log('add to ' + pos); + $(this.tbody + ' > .gen:nth-child(' + (pos) + ')').after(html); + } + } else { + //console.log('add to bottom'); + document.getElementById(this.tbodyid).innerHTML += html; + $('#pe-main').scrollTop($(this.table)[0].scrollHeight); + } - this.move(); + this.move(); - if (callback) callback(); - }, - - fromSortableSelf: function (el) { - if (mpcp.pe.selected.length) { - var last, - index = mpcp.pe.selected.index(el); - - $(mpcp.pe.selected).each(function (item, tr) { - //var file = $(tr).data().fileid; - - // if dropped item is further down than the current tr - if (index > item) { - //console.log(file + ': insert before'); - $(tr).insertBefore(el); - } else if (index < item) { - //console.log(file + ': insert after'); - // put tr after the last placed element - if (!last) - $(tr).insertAfter(el); - else - $(tr).insertAfter(last); - - last = tr; - } - // else dont do anything - // (the item that was dropped) - }); - - mpcp.utils.clearSelected(mpcp.pe); - } + if (callback) callback(); + }, - this.move(); - }, + fromSortableSelf: function (el) { + if (mpcp.pe.selected.length) { + var last, + index = mpcp.pe.selected.index(el); - // dragging from another drag table - // mutliselect is handled in addDir and addfile - fromSortableSender: function (el, index) { - var rem = 1; + $(mpcp.pe.selected).each(function (item, tr) { + //var file = $(tr).data().fileid; - // check if "playlist editor is empty" is showing - if (index == 1 && document.getElementById(this.tbodyid).children[0].classList.contains('rem')) { - index = 0; - ++rem; - } + // if dropped item is further down than the current tr + if (index > item) { + //console.log(file + ': insert before'); + $(tr).insertBefore(el); + } else if (index < item) { + //console.log(file + ': insert after'); + // put tr after the last placed element + if (!last) + $(tr).insertAfter(el); + else + $(tr).insertAfter(last); - // remove the dragged element - $(this.tbody + ' .gen:nth-child(' + (rem + index) + ')').remove(); - - // mutli select check - if (mpcp.browser.selected.length) { - mpcp.browser.addMulti(index); - return; - } else if (mpcp.librarySongs.selected.length) { - mpcp.librarySongs.addMulti(index); - return; - } else if (el.classList.contains('artist') && - mpcp.libraryArtists.selected.length) { - mpcp.library.addMulti(mpcp.libraryArtists, index, true); - return; - } else if (el.classList.contains('album') && - mpcp.libraryAlbums.selected.length) { - mpcp.library.addMulti(mpcp.libraryAlbums, index, true); - return; + last = tr; } + // else dont do anything + // (the item that was dropped) + }); - var artist, album; - - if (el.classList.contains('file')) { - var fileName = el.dataset.fileid; - this.addid(fileName, index); - } else if (el.classList.contains('directory')) { - // directory - var dir = el.dataset.dirid; - this.add(dir, index); - } else if (el.classList.contains('album')) { - artist = el.dataset.artist; - album = el.dataset.album; - - mpcp.library.getSongsFromAlbum(artist, album, function (files) { - mpcp.pe.addSong(files, index); - }); - } else if (el.classList.contains('artist')) { - artist = el.dataset.artist; - - mpcp.library.getSongsFromAlbum(artist, null, function (files) { - mpcp.pe.addSong(files, index); - }); - } else { - console.log('not supported drag for: ' + $(el).attr('class')); - } - }, - - // wrapper (similar to komponist.addid) - addid: function (fileid, pos, callback) { - //console.log(fileid); - komponist.find('file', fileid, function (err, value) { - if (err) { - console.log(err); - if (callback) callback(); - return; - } - - //console.log(value); - value = value[0]; - - if (value.file && !value.directory) { - mpcp.pe.addSong(value, pos, callback); - } - }); - }, - - // add an array so the DOM is only updated once - // arr: [type][value] - addArr: function (arr, pos, rootCallback) { - // converts to a files array for addSong - var newArr = [], - j = 0; - - function callback() { - mpcp.pe.addSong(newArr, pos, rootCallback); - } + mpcp.utils.clearSelected(mpcp.pe); + } - function setFile(fileid) { - komponist.find('file', content, function (err, value) { - if (err) return console.log(err); + this.move(); + }, - value = value[0]; + // dragging from another drag table + // mutliselect is handled in addDir and addfile + fromSortableSender: function (el, index) { + var rem = 1; - if (value.file && !value.directory) { - //console.log(value); - newArr.push(value); - if (++j == arr.length) callback(); - } - }); - } + // check if "playlist editor is empty" is showing + if (index == 1 && document.getElementById(this.tbodyid).children[0].classList.contains('rem')) { + index = 0; + ++rem; + } - function setDir(dir) { - mpcp.utils.getAllInfo(dir, function (files) { - newArr = newArr.concat(files); - if (++j == arr.length) callback(); - }); - } + // remove the dragged element + $(this.tbody + ' .gen:nth-child(' + (rem + index) + ')').remove(); + + // mutli select check + if (mpcp.browser.selected.length) { + mpcp.browser.addMulti(index); + return; + } else if (mpcp.librarySongs.selected.length) { + mpcp.librarySongs.addMulti(index); + return; + } else if (el.classList.contains('artist') && + mpcp.libraryArtists.selected.length) { + mpcp.library.addMulti(mpcp.libraryArtists, index, true); + return; + } else if (el.classList.contains('album') && + mpcp.libraryAlbums.selected.length) { + mpcp.library.addMulti(mpcp.libraryAlbums, index, true); + return; + } - //console.log(arr); - for (var i = 0; i < arr.length; i++) { - var val = arr[i][0], - content = arr[i][1]; - - // fileid - if (val == 'id') { - setFile(content); - } - // dir - else if (val == 'dir') { - setDir(content); - } - // file - else if (val == 'file') { - newArr.push(content); - j += 1; - } else { - console.log(val + ' is not supported?'); - j += 1; - } - - if (j == arr.length) callback(); - } - }, - - // wrapper (similar to komponist.add) - add: function (dir, pos, callback) { - // FUTURE SELF: DO NOT USE LISTALLINFO, IT WILL HAVE THE "OFF BY ONE" - // BUG, KEEP THIS 'getAllInfo'. - // Option 1: Loop through all directories recursively running lsinfo - // Option 2: Run listall, then loop through each item to get metadata - // Option 1 sends less requests to the server, so we'll implement that. - // Add All and Multiselect directories is still based on how fast the - // server can respond per directory (so it can look random) - mpcp.utils.getAllInfo(dir, function (files) { - //console.log(files); - mpcp.pe.addSong(files, pos, callback); - }); - }, - - // remove song from the pe - removeSong: function (element) { - // multiselect check (any left clicks) - if (this.selected.length) { - $(this.selected).each(function (item, tr) { - //console.log(tr); - $(tr)[0].remove(); - }); - - // clear this.selected just in case. - mpcp.utils.clearSelected(this); - } else { - $(element)[0].remove(); - } + var artist, album; + + if (el.classList.contains('file')) { + var fileName = el.dataset.fileid; + this.addid(fileName, index); + } else if (el.classList.contains('directory')) { + // directory + var dir = el.dataset.dirid; + this.add(dir, index); + } else if (el.classList.contains('album')) { + artist = el.dataset.artist; + album = el.dataset.album; + + mpcp.library.getSongsFromAlbum(artist, album, function (files) { + mpcp.pe.addSong(files, index); + }); + } else if (el.classList.contains('artist')) { + artist = el.dataset.artist; + + mpcp.library.getSongsFromAlbum(artist, null, function (files) { + mpcp.pe.addSong(files, index); + }); + } else { + console.log('not supported drag for: ' + $(el).attr('class')); + } + }, + + // wrapper (similar to komponist.addid) + addid: function (fileid, pos, callback) { + //console.log(fileid); + komponist.find('file', fileid, function (err, value) { + if (err) { + console.log(err); + if (callback) callback(); + return; + } + + //console.log(value); + value = value[0]; + + if (value.file && !value.directory) { + mpcp.pe.addSong(value, pos, callback); + } + }); + }, + + // add an array so the DOM is only updated once + // arr: [type][value] + addArr: function (arr, pos, rootCallback) { + // converts to a files array for addSong + var newArr = [], + j = 0; + + function callback() { + mpcp.pe.addSong(newArr, pos, rootCallback); + } - this.move(); - }, + function setFile(fileid) { + komponist.find('file', content, function (err, value) { + if (err) return console.log(err); - // just updates the numbers column in the table - move: function () { - var pos = 0, - tr = document.getElementById(mpcp.pe.tbodyid).children; + value = value[0]; - for(var i = 0; i < tr.length; ++i) { - tr[i].firstChild.innerHTML = ++pos + '.'; + if (value.file && !value.directory) { + //console.log(value); + newArr.push(value); + if (++j == arr.length) callback(); } + }); + } - if (!pos) mpcp.pe.showNothingMessage(); - }, - - // open the playlist to the pe - open: function (file, callback) { - komponist.listplaylistinfo(file, function (err, val) { - if (err) { - console.log(err); - if (callback) callback(); - return; - } - - mpcp.pe.clear(); - mpcp.pe.addSong(val, null, callback); - }); - }, - - // clear the pe - clear: function () { - $(this.table + ' .gen').remove(); - this.showNothingMessage(); - }, - - // close the pe (and clear) - close: function () { - this.current = null; - $(this.selector)[0].style.display = 'none'; - this.clear(); - }, - - // minimize the pe (dont clear) - minimize: function () { - this.current = null; - this.minimized = true; - $(this.selector)[0].style.display = 'none'; - document.getElementById('pe-tab').style.display = 'block'; - }, - - // resume after minimize - resume: function () { - this.minimized = false; - this.current = 'local'; - document.getElementById('pe-tab').style.display = 'none'; - $(this.selector)[0].style.display = 'flex'; - }, - - // scramble the pe - scramble: function (callback) { - $(mpcp.pe.tbody).randomize('.gen'); - mpcp.pe.move(); - if (callback) callback(); - }, + function setDir(dir) { + mpcp.utils.getAllInfo(dir, function (files) { + newArr = newArr.concat(files); + if (++j == arr.length) callback(); + }); + } - // remove duplicate songs in the pe - removeDuplicates: function (callback) { - console.log('remove duplicates'); - var duplicate = {}; + //console.log(arr); + for (var i = 0; i < arr.length; i++) { + var val = arr[i][0], + content = arr[i][1]; + + // fileid + if (val == 'id') { + setFile(content); + } + // dir + else if (val == 'dir') { + setDir(content); + } + // file + else if (val == 'file') { + newArr.push(content); + j += 1; + } else { + console.log(val + ' is not supported?'); + j += 1; + } + + if (j == arr.length) callback(); + } + }, + + // wrapper (similar to komponist.add) + add: function (dir, pos, callback) { + // FUTURE SELF: DO NOT USE LISTALLINFO, IT WILL HAVE THE "OFF BY ONE" + // BUG, KEEP THIS 'getAllInfo'. + // Option 1: Loop through all directories recursively running lsinfo + // Option 2: Run listall, then loop through each item to get metadata + // Option 1 sends less requests to the server, so we'll implement that. + // Add All and Multiselect directories is still based on how fast the + // server can respond per directory (so it can look random) + mpcp.utils.getAllInfo(dir, function (files) { + //console.log(files); + mpcp.pe.addSong(files, pos, callback); + }); + }, + + // remove song from the pe + removeSong: function (element) { + // multiselect check (any left clicks) + if (this.selected.length) { + $(this.selected).each(function (item, tr) { + //console.log(tr); + $(tr)[0].remove(); + }); + + // clear this.selected just in case. + mpcp.utils.clearSelected(this); + } else { + $(element)[0].remove(); + } - $(this.table + ' .gen').each(function () { - var title = $(this).attr('title'); + this.move(); + }, - if (duplicate[title]) - mpcp.pe.removeSong(this); - else - duplicate[title] = true; - }); + // just updates the numbers column in the table + move: function () { + var pos = 0, + tr = document.getElementById(mpcp.pe.tbodyid).children; + + for(var i = 0; i < tr.length; ++i) { + tr[i].firstChild.innerHTML = ++pos + '.'; + } + if (!pos) mpcp.pe.showNothingMessage(); + }, + + // open the playlist to the pe + open: function (file, callback) { + komponist.listplaylistinfo(file, function (err, val) { + if (err) { + console.log(err); if (callback) callback(); - }, - - // move rows to top of pe - moveToTop: function (tr) { - if (this.selected.length) { - $(this.selected).each(function (item, tr) { - $(tr).prependTo(mpcp.pe.tbody); - }); - - mpcp.utils.clearSelected(this); - } else { - $(tr).prependTo(this.tbody); - } + return; + } + + mpcp.pe.clear(); + mpcp.pe.addSong(val, null, callback); + }); + }, + + // clear the pe + clear: function () { + $(this.table + ' .gen').remove(); + this.showNothingMessage(); + }, + + // close the pe (and clear) + close: function () { + this.current = null; + $(this.selector)[0].style.display = 'none'; + this.clear(); + }, + + // minimize the pe (dont clear) + minimize: function () { + this.current = null; + this.minimized = true; + $(this.selector)[0].style.display = 'none'; + document.getElementById('pe-tab').style.display = 'block'; + }, + + // resume after minimize + resume: function () { + this.minimized = false; + this.current = 'local'; + document.getElementById('pe-tab').style.display = 'none'; + $(this.selector)[0].style.display = 'flex'; + }, + + // scramble the pe + scramble: function (callback) { + $(mpcp.pe.tbody).randomize('.gen'); + mpcp.pe.move(); + if (callback) callback(); + }, + + // remove duplicate songs in the pe + removeDuplicates: function (callback) { + console.log('remove duplicates'); + var duplicate = {}; + + $(this.table + ' .gen').each(function () { + var title = $(this).attr('title'); + + if (duplicate[title]) + mpcp.pe.removeSong(this); + else + duplicate[title] = true; + }); + + if (callback) callback(); + }, + + // move rows to top of pe + moveToTop: function (tr) { + if (this.selected.length) { + $(this.selected).each(function (item, tr) { + $(tr).prependTo(mpcp.pe.tbody); + }); + + mpcp.utils.clearSelected(this); + } else { + $(tr).prependTo(this.tbody); + } - $('#pe-main').scrollTop($(this.table)); - this.move(); - }, - - // move rows to bottom of pe - moveToBottom: function (tr) { - if (this.selected.length) { - $(this.selected).each(function (item, tr) { - $(tr).appendTo(this.tbody); - }); - - mpcp.utils.clearSelected(this); - } else { - $(tr).appendTo(this.tbody); - } + $('#pe-main').scrollTop($(this.table)); + this.move(); + }, - $('#pe-main').scrollTop(document.getElementById(this.tableid).scrollHeight); - this.move(); - }, + // move rows to bottom of pe + moveToBottom: function (tr) { + if (this.selected.length) { + $(this.selected).each(function (item, tr) { + $(tr).appendTo(this.tbody); + }); - showNothingMessage: function () { - var html = 'The playlist editor is empty! Songs can be added from the browser or by opening a playlist.'; - document.getElementById(this.tbodyid).innerHTML = html; - }, + mpcp.utils.clearSelected(this); + } else { + $(tr).appendTo(this.tbody); + } - removeNothingMessage: function () { - $(this.tbody + ' .rem').remove(); - }, + $('#pe-main').scrollTop(document.getElementById(this.tableid).scrollHeight); + this.move(); + }, - initEvents: function () { - $(document).on('click', '.pe-song-remove', function () { - var file = $(this).parent().parent(); - mpcp.pe.removeSong(file); - }); + showNothingMessage: function () { + var html = 'The playlist editor is empty! Songs can be added from the browser or by opening a playlist.'; + document.getElementById(this.tbodyid).innerHTML = html; + }, - $(document).on('click', '#pe-clear', function () { mpcp.pe.clear(); }); + removeNothingMessage: function () { + $(this.tbody + ' .rem').remove(); + }, - $(document).on('click', '#pe-close', function () { mpcp.pe.close(); }); + initEvents: function () { + $(document).on('click', '.pe-song-remove', function () { + var file = $(this).parent().parent(); + mpcp.pe.removeSong(file); + }); - $(document).on('click', '#pe-save', function () { - mpcp.stored.externalSave(); - }); + $(document).on('click', '#pe-clear', function () { mpcp.pe.clear(); }); - $(document).on('click', '#pe-minimize', function () { - mpcp.pe.minimize(); - }); + $(document).on('click', '#pe-close', function () { mpcp.pe.close(); }); - $(document).on('click', '#pe-tab', function () { mpcp.pe.resume(); }); + $(document).on('click', '#pe-save', function () { + mpcp.stored.externalSave(); + }); - $(document).on('click', '#pe-open', function () { - mpcp.stored.updatePlaylists('#playlist-open-modal', mpcp.pe.open); - }); + $(document).on('click', '#pe-minimize', function () { + mpcp.pe.minimize(); + }); - $(document).on('click', '#pe-scramble', function () { - mpcp.pe.scramble(); - }); + $(document).on('click', '#pe-tab', function () { mpcp.pe.resume(); }); - $(document).on('click', '#pe-remove-duplicates', function () { - mpcp.pe.removeDuplicates(); - }); + $(document).on('click', '#pe-open', function () { + mpcp.stored.updatePlaylists('#playlist-open-modal', mpcp.pe.open); + }); - mpcp.utils.multiSelect(this, ['pe-song-remove']); + $(document).on('click', '#pe-scramble', function () { + mpcp.pe.scramble(); + }); - $('#pe-search-toggle').click(function () { - if (document.getElementById('pe-search-toggle').classList.contains('active')) { - document.getElementById('pe-search-toggle').classList.remove('active'); - document.getElementById('pe-search').style.display = 'none'; - document.getElementById('search-pe').value = ''; - $(mpcp.pe.tbody).children().show(); - } else { - document.getElementById('pe-search-toggle').classList.add('active'); - document.getElementById('pe-search').style.display = 'block'; - $('#search-pe').focus(); - } - }); + $(document).on('click', '#pe-remove-duplicates', function () { + mpcp.pe.removeDuplicates(); + }); - mpcp.utils.lazySearch('#search-pe', this.table, 'title', '#search-pe-clear'); - } + mpcp.utils.multiSelect(this, ['pe-song-remove']); + + $('#pe-search-toggle').click(function () { + if (document.getElementById('pe-search-toggle').classList.contains('active')) { + document.getElementById('pe-search-toggle').classList.remove('active'); + document.getElementById('pe-search').style.display = 'none'; + document.getElementById('search-pe').value = ''; + $(mpcp.pe.tbody).children().show(); + } else { + document.getElementById('pe-search-toggle').classList.add('active'); + document.getElementById('pe-search').style.display = 'block'; + $('#search-pe').focus(); + } + }); + + mpcp.utils.lazySearch('#search-pe', this.table, 'title', '#search-pe-clear'); + } }; }; diff --git a/src/progressbar.js b/src/progressbar.js index fc9ed92..103735d 100644 --- a/src/progressbar.js +++ b/src/progressbar.js @@ -2,46 +2,46 @@ module.exports = function (mpcp) { // progress bar simulation for the player return { - // current time in seconds - progress: 0, - // interval - musicprogress: null, - - progressfn: function () { - // avoid 'this' as it's in a new scope of setInterval - ++mpcp.progressbar.progress; - - if (mpcp.progressbar.progress > mpcp.player.current.Time) { - mpcp.progressbar.stopProgress(); - return; - } - - document.getElementById('music-time').value = mpcp.progressbar.progress; - document.getElementById('time-current').innerHTML = mpcp.utils.toMMSS(mpcp.progressbar.progress); - }, - - stopProgress: function () { - //console.log('stop progressbar'); - clearInterval(this.musicprogress); - }, - - startProgress: function () { - this.stopProgress(); // stops in case of duplicates; - //console.log('start progressbar'); - this.musicprogress = setInterval(this.progressfn, 1000); - }, - - initEvents: function () { - $('#music-time').on('change', function () { - // DO NOT USE seekcur, it introduces the SKIPPING BUG - // on SOME systems. - komponist.seek(mpcp.player.current.Pos, this.value, - function (err) { - console.log('Seeking...'); - if (err) console.log('no song playing to seek'); - }); - }); + // current time in seconds + progress: 0, + // interval + musicprogress: null, + + progressfn: function () { + // avoid 'this' as it's in a new scope of setInterval + ++mpcp.progressbar.progress; + + if (mpcp.progressbar.progress > mpcp.player.current.Time) { + mpcp.progressbar.stopProgress(); + return; } + + document.getElementById('music-time').value = mpcp.progressbar.progress; + document.getElementById('time-current').innerHTML = mpcp.utils.toMMSS(mpcp.progressbar.progress); + }, + + stopProgress: function () { + //console.log('stop progressbar'); + clearInterval(this.musicprogress); + }, + + startProgress: function () { + this.stopProgress(); // stops in case of duplicates; + //console.log('start progressbar'); + this.musicprogress = setInterval(this.progressfn, 1000); + }, + + initEvents: function () { + $('#music-time').on('change', function () { + // DO NOT USE seekcur, it introduces the SKIPPING BUG + // on SOME systems. + komponist.seek(mpcp.player.current.Pos, this.value, + function (err) { + console.log('Seeking...'); + if (err) console.log('no song playing to seek'); + }); + }); + } }; }; diff --git a/src/settings.js b/src/settings.js index 1c41632..a3d96c9 100644 --- a/src/settings.js +++ b/src/settings.js @@ -3,352 +3,352 @@ module.exports = function (mpcp) { // saved user settings // since all storage is text, the if statements check if their undefined. return { - // default theme to use - theme: 'default-thin', - // show pulsing effect - pulse: true, - // 'unknown' text - unknown: 'unknown', - // which browser to use (library or browser) - browser: 'browser', - // used for unknown pop states - lastBrowser: 'browser', - // show consume warning - consumeWarning: true, - - // initially load all the settings - loadAll: function () { - this.loadTheme(); - this.loadHistoryMax(); - this.loadItemsMax(); - this.loadPagination(); - this.loadShowAllErrors(); - this.loadPulse(); - this.loadUnknown(); - this.loadSkipToRemove(); - this.loadBrowser(); - this.loadConsumeWarning(); - }, - - loadTheme: function () { - var theme = localStorage.getItem('mpcp-theme'); - - if (theme) this.theme = theme; - - document.getElementById('themes').value = this.theme; - - var url = '/css/themes/' + this.theme + '/main.css'; - - $.get(url, function () { - $('#theme').attr('href', url); - // I'm hoping it will only take Xms to load all the css... - setTimeout(function () { - window.dispatchEvent(new CustomEvent("MPCPReflow")); - }, 500); - }); - }, + // default theme to use + theme: 'default-thin', + // show pulsing effect + pulse: true, + // 'unknown' text + unknown: 'unknown', + // which browser to use (library or browser) + browser: 'browser', + // used for unknown pop states + lastBrowser: 'browser', + // show consume warning + consumeWarning: true, + + // initially load all the settings + loadAll: function () { + this.loadTheme(); + this.loadHistoryMax(); + this.loadItemsMax(); + this.loadPagination(); + this.loadShowAllErrors(); + this.loadPulse(); + this.loadUnknown(); + this.loadSkipToRemove(); + this.loadBrowser(); + this.loadConsumeWarning(); + }, + + loadTheme: function () { + var theme = localStorage.getItem('mpcp-theme'); + + if (theme) this.theme = theme; + + document.getElementById('themes').value = this.theme; + + var url = '/css/themes/' + this.theme + '/main.css'; + + $.get(url, function () { + $('#theme').attr('href', url); + // I'm hoping it will only take Xms to load all the css... + setTimeout(function () { + window.dispatchEvent(new CustomEvent("MPCPReflow")); + }, 500); + }); + }, + + saveTheme: function (theme) { + console.log('changed theme'); + localStorage.setItem('mpcp-theme', theme); + this.loadTheme(); + }, + + loadHistoryMax: function () { + var max = localStorage.getItem('mpcp-history-max'); + + if (max) mpcp.history.max = max; + + document.getElementById('history-max').value = mpcp.history.max; + }, + + saveHistoryMax: function (max) { + console.log('changed max history items'); + localStorage.setItem('mpcp-history-max', max); + this.loadHistoryMax(); + }, + + loadItemsMax: function (type, force) { + var max; + + if (type == 'playlist' || type === undefined) { + max = localStorage.getItem('mpcp-items-max-playlist'); + + if (max) mpcp.pages.maxPlaylist = parseInt(max); + + document.getElementById('items-max-playlist').value = + mpcp.pages.maxPlaylist; + } - saveTheme: function (theme) { - console.log('changed theme'); - localStorage.setItem('mpcp-theme', theme); - this.loadTheme(); - }, + if (type == 'browser' || type === undefined) { + max = localStorage.getItem('mpcp-items-max-browser'); - loadHistoryMax: function () { - var max = localStorage.getItem('mpcp-history-max'); + if (max) mpcp.pages.maxBrowser = parseInt(max); - if (max) mpcp.history.max = max; + document.getElementById('items-max-browser').value = + mpcp.pages.maxBrowser; + } - document.getElementById('history-max').value = mpcp.history.max; - }, + if (type !== undefined) mpcp.pages.update(type); - saveHistoryMax: function (max) { - console.log('changed max history items'); - localStorage.setItem('mpcp-history-max', max); - this.loadHistoryMax(); - }, + if (force && type == 'playlist') mpcp.playlist.updateLocal(); + if (force && type == 'browser') mpcp.browser.updateLocal(); + }, - loadItemsMax: function (type, force) { - var max; + saveItemsMax: function (type, max) { + console.log('changed max items for ' + type); - if (type == 'playlist' || type === undefined) { - max = localStorage.getItem('mpcp-items-max-playlist'); + if (type == 'playlist') + localStorage.setItem('mpcp-items-max-playlist', max); + else if (type == 'browser') + localStorage.setItem('mpcp-items-max-browser', max); - if (max) mpcp.pages.maxPlaylist = parseInt(max); + this.loadItemsMax(type, true); + }, - document.getElementById('items-max-playlist').value = - mpcp.pages.maxPlaylist; - } + loadPagination: function (type, force) { + var use; - if (type == 'browser' || type === undefined) { - max = localStorage.getItem('mpcp-items-max-browser'); + if (type == 'playlist' || type === undefined) { + use = localStorage.getItem('mpcp-use-pages-playlist'); - if (max) mpcp.pages.maxBrowser = parseInt(max); + if (use) mpcp.pages.enabledPlaylist = (use === 'true'); - document.getElementById('items-max-browser').value = - mpcp.pages.maxBrowser; - } + $('#use-pages-playlist').prop( + 'checked', mpcp.pages.enabledPlaylist); - if (type !== undefined) mpcp.pages.update(type); + if (mpcp.pages.enabledPlaylist) + mpcp.pages.show('playlist'); + else + mpcp.pages.hide('playlist'); + } - if (force && type == 'playlist') mpcp.playlist.updateLocal(); - if (force && type == 'browser') mpcp.browser.updateLocal(); - }, + if (type == 'browser' || type === undefined) { + use = localStorage.getItem('mpcp-use-pages-browser'); - saveItemsMax: function (type, max) { - console.log('changed max items for ' + type); + if (use) mpcp.pages.enabledBrowser = (use === 'true'); - if (type == 'playlist') - localStorage.setItem('mpcp-items-max-playlist', max); - else if (type == 'browser') - localStorage.setItem('mpcp-items-max-browser', max); + $('#use-pages-browser').prop('checked', mpcp.pages.enabledBrowser); - this.loadItemsMax(type, true); - }, + if (mpcp.pages.enabledBrowser) + mpcp.pages.show('browser'); + else + mpcp.pages.hide('browser'); + } - loadPagination: function (type, force) { - var use; + if (force && type == 'playlist') mpcp.playlist.updateLocal(); + if (force && type == 'browser') mpcp.browser.updateLocal(); + }, - if (type == 'playlist' || type === undefined) { - use = localStorage.getItem('mpcp-use-pages-playlist'); + savePagination: function (type, use) { + console.log('changed use pagination for ' + type); - if (use) mpcp.pages.enabledPlaylist = (use === 'true'); + if (type == 'playlist') + localStorage.setItem('mpcp-use-pages-playlist', use); + else if (type == 'browser') + localStorage.setItem('mpcp-use-pages-browser', use); - $('#use-pages-playlist').prop( - 'checked', mpcp.pages.enabledPlaylist); + this.loadPagination(type, true); + }, - if (mpcp.pages.enabledPlaylist) - mpcp.pages.show('playlist'); - else - mpcp.pages.hide('playlist'); - } + loadShowAllErrors: function () { + var use = localStorage.getItem('mpcp-show-all-errors'), + show = false; - if (type == 'browser' || type === undefined) { - use = localStorage.getItem('mpcp-use-pages-browser'); + if (use && use === 'true') show = true; - if (use) mpcp.pages.enabledBrowser = (use === 'true'); + $('#show-all-errors').prop('checked', show); + }, - $('#use-pages-browser').prop('checked', mpcp.pages.enabledBrowser); + saveShowAllErrors: function (use) { + console.log('changed show all errors'); + localStorage.setItem('mpcp-show-all-errors', use); + }, - if (mpcp.pages.enabledBrowser) - mpcp.pages.show('browser'); - else - mpcp.pages.hide('browser'); - } + loadPulse: function () { + var use = localStorage.getItem('mpcp-use-pulse'); - if (force && type == 'playlist') mpcp.playlist.updateLocal(); - if (force && type == 'browser') mpcp.browser.updateLocal(); - }, + if (use && use === 'false') this.pulse = false; - savePagination: function (type, use) { - console.log('changed use pagination for ' + type); + $('#use-pulse').prop('checked', this.pulse); + }, - if (type == 'playlist') - localStorage.setItem('mpcp-use-pages-playlist', use); - else if (type == 'browser') - localStorage.setItem('mpcp-use-pages-browser', use); + savePulse: function (use) { + console.log('changed pulse'); + this.pulse = use; - this.loadPagination(type, true); - }, + if (mpcp.pe.current) { + if (use) + $(mpcp.pe.tbody).children().addClass('pulse'); + else + $(mpcp.pe.tbody).children().removeClass('pulse'); + } - loadShowAllErrors: function () { - var use = localStorage.getItem('mpcp-show-all-errors'), - show = false; + localStorage.setItem('mpcp-use-pulse', use); + }, - if (use && use === 'true') show = true; + loadUnknown: function () { + var use = localStorage.getItem('mpcp-use-unknown'); - $('#show-all-errors').prop('checked', show); - }, + if (use !== undefined && use === '') { + this.unknown = ''; + $('#use-unknown').prop('checked', false); + } else { + this.unknown = 'unknown'; + $('#use-unknown').prop('checked', true); + } + }, - saveShowAllErrors: function (use) { - console.log('changed show all errors'); - localStorage.setItem('mpcp-show-all-errors', use); - }, + saveUnknown: function (use) { + console.log('changed unknown'); - loadPulse: function () { - var use = localStorage.getItem('mpcp-use-pulse'); + if (use) { + localStorage.setItem('mpcp-use-unknown', 'unknown'); + this.loadUnknown(); + } else { + localStorage.setItem('mpcp-use-unknown', ''); + this.loadUnknown(); + } - if (use && use === 'false') this.pulse = false; + mpcp.browser.update(); + }, - $('#use-pulse').prop('checked', this.pulse); - }, + loadSkipToRemove: function () { + var use = localStorage.getItem('mpcp-use-skip-to-remove'); - savePulse: function (use) { - console.log('changed pulse'); - this.pulse = use; + if (use && use == 'true') { + mpcp.playlist.skipToRemove = true; + } else { + mpcp.playlist.skipToRemove = false; + } - if (mpcp.pe.current) { - if (use) - $(mpcp.pe.tbody).children().addClass('pulse'); - else - $(mpcp.pe.tbody).children().removeClass('pulse'); - } - - localStorage.setItem('mpcp-use-pulse', use); - }, - - loadUnknown: function () { - var use = localStorage.getItem('mpcp-use-unknown'); - - if (use !== undefined && use === '') { - this.unknown = ''; - $('#use-unknown').prop('checked', false); - } else { - this.unknown = 'unknown'; - $('#use-unknown').prop('checked', true); - } - }, - - saveUnknown: function (use) { - console.log('changed unknown'); - - if (use) { - localStorage.setItem('mpcp-use-unknown', 'unknown'); - this.loadUnknown(); - } else { - localStorage.setItem('mpcp-use-unknown', ''); - this.loadUnknown(); - } - - mpcp.browser.update(); - }, - - loadSkipToRemove: function () { - var use = localStorage.getItem('mpcp-use-skip-to-remove'); - - if (use && use == 'true') { - mpcp.playlist.skipToRemove = true; - } else { - mpcp.playlist.skipToRemove = false; - } - - $('#use-skip-to-remove').prop('checked', mpcp.playlist.skipToRemove); - }, - - saveSkipToRemove: function (use) { - console.log('changed skip-to-remove'); - localStorage.setItem('mpcp-use-skip-to-remove', use); - this.loadSkipToRemove(); - }, - - loadBrowser: function (activate) { - var use = localStorage.getItem('mpcp-browser'); - - if (use) this.browser = use; - - if (activate) { - if (use && use == 'library') { - mpcp.library.show(); - mpcp.browser.hide(); - } else { - mpcp.library.hide(); - mpcp.browser.show(); - } - } else { - this.lastBrowser = this.browser; - mpcp.library.hide(); - mpcp.browser.hide(); - } - }, - - saveBrowser: function (use) { - console.log('changed browser'); - this.lastBrowser = this.browser; - localStorage.setItem('mpcp-browser', use); - this.loadBrowser(true); - }, - - loadConsumeWarning: function () { - var use = localStorage.getItem('mpcp-use-consume-warning'); - - if (use && use === 'false') this.consumeWarning = false; - - $('#use-consume-warning').prop( - 'checked', this.consumeWarning); - }, - - saveConsumeWarning: function (use) { - console.log('changed consume warning'); - this.consumeWarning = use; - - if (use && $('#consume').is(':checked')) - document.getElementById('warning-consume').style.display = 'block'; - else - document.getElementById('warning-consume').style.display = 'none'; - - localStorage.setItem('mpcp-use-consume-warning', use); - }, - - initEvents: function () { - // settings event handling - // client config - $('#themes').change(function () { - mpcp.settings.saveTheme(this.value); - }); - - $('#history-max').change(function () { - mpcp.settings.saveHistoryMax(this.value); - }); - - $('#items-max-playlist').change(function () { - mpcp.settings.saveItemsMax('playlist', this.value); - }); - - $('#items-max-browser').change(function () { - mpcp.settings.saveItemsMax('browser', this.value); - }); - - $('#use-pages-playlist').change(function () { - var use = $(this).prop('checked'); - mpcp.settings.savePagination('playlist', use); - }); - - $('#use-pages-browser').change(function () { - var use = $(this).prop('checked'); - mpcp.settings.savePagination('browser', use); - }); - - $('#use-pulse').change(function () { - var use = $(this).prop('checked'); - mpcp.settings.savePulse(use); - }); - - $('#use-unknown').change(function () { - var use = $(this).prop('checked'); - mpcp.settings.saveUnknown(use); - }); - - $('#use-skip-to-remove').change(function () { - var use = $(this).prop('checked'); - mpcp.settings.saveSkipToRemove(use); - }); - - $('#show-all-errors').change(function () { - var use = $(this).prop('checked'); - mpcp.settings.saveShowAllErrors(use); - }); - - // server config - $('#crossfade').on('input change', function () { - komponist.crossfade(this.value, function (err) { - if (err) console.log(err); - }); - }); - - $('#consume').change(function () { - var use = $(this).prop('checked'); - use = use ? 1 : 0; - - komponist.consume(use, function (err) { - if (err) console.log(err); - }); - }); - - $('#use-consume-warning').change(function () { - var use = $(this).prop('checked'); - mpcp.settings.saveConsumeWarning(use); - }); + $('#use-skip-to-remove').prop('checked', mpcp.playlist.skipToRemove); + }, + + saveSkipToRemove: function (use) { + console.log('changed skip-to-remove'); + localStorage.setItem('mpcp-use-skip-to-remove', use); + this.loadSkipToRemove(); + }, + + loadBrowser: function (activate) { + var use = localStorage.getItem('mpcp-browser'); + + if (use) this.browser = use; + + if (activate) { + if (use && use == 'library') { + mpcp.library.show(); + mpcp.browser.hide(); + } else { + mpcp.library.hide(); + mpcp.browser.show(); + } + } else { + this.lastBrowser = this.browser; + mpcp.library.hide(); + mpcp.browser.hide(); } + }, + + saveBrowser: function (use) { + console.log('changed browser'); + this.lastBrowser = this.browser; + localStorage.setItem('mpcp-browser', use); + this.loadBrowser(true); + }, + + loadConsumeWarning: function () { + var use = localStorage.getItem('mpcp-use-consume-warning'); + + if (use && use === 'false') this.consumeWarning = false; + + $('#use-consume-warning').prop( + 'checked', this.consumeWarning); + }, + + saveConsumeWarning: function (use) { + console.log('changed consume warning'); + this.consumeWarning = use; + + if (use && $('#consume').is(':checked')) + document.getElementById('warning-consume').style.display = 'block'; + else + document.getElementById('warning-consume').style.display = 'none'; + + localStorage.setItem('mpcp-use-consume-warning', use); + }, + + initEvents: function () { + // settings event handling + // client config + $('#themes').change(function () { + mpcp.settings.saveTheme(this.value); + }); + + $('#history-max').change(function () { + mpcp.settings.saveHistoryMax(this.value); + }); + + $('#items-max-playlist').change(function () { + mpcp.settings.saveItemsMax('playlist', this.value); + }); + + $('#items-max-browser').change(function () { + mpcp.settings.saveItemsMax('browser', this.value); + }); + + $('#use-pages-playlist').change(function () { + var use = $(this).prop('checked'); + mpcp.settings.savePagination('playlist', use); + }); + + $('#use-pages-browser').change(function () { + var use = $(this).prop('checked'); + mpcp.settings.savePagination('browser', use); + }); + + $('#use-pulse').change(function () { + var use = $(this).prop('checked'); + mpcp.settings.savePulse(use); + }); + + $('#use-unknown').change(function () { + var use = $(this).prop('checked'); + mpcp.settings.saveUnknown(use); + }); + + $('#use-skip-to-remove').change(function () { + var use = $(this).prop('checked'); + mpcp.settings.saveSkipToRemove(use); + }); + + $('#show-all-errors').change(function () { + var use = $(this).prop('checked'); + mpcp.settings.saveShowAllErrors(use); + }); + + // server config + $('#crossfade').on('input change', function () { + komponist.crossfade(this.value, function (err) { + if (err) console.log(err); + }); + }); + + $('#consume').change(function () { + var use = $(this).prop('checked'); + use = use ? 1 : 0; + + komponist.consume(use, function (err) { + if (err) console.log(err); + }); + }); + + $('#use-consume-warning').change(function () { + var use = $(this).prop('checked'); + mpcp.settings.saveConsumeWarning(use); + }); + } }; }; diff --git a/src/socket.js b/src/socket.js index 862277f..ddb09ce 100644 --- a/src/socket.js +++ b/src/socket.js @@ -6,214 +6,214 @@ socket = new WebSocket('ws://' + host); // called in socket init function initAfterConnection() { - console.log('initAfterConnection'); - //mpcp.player.updateAll(); // inside of mpcp.playlist.updateTitle - mpcp.player.updateMixer(); - //mpcp.playlist.updateAll(); // inside of mpcp.playlist.updateTitle - //mpcp.browser.update(); // done lower in the function - mpcp.utils.updateStats(); - - var path = window.location.pathname. - slice(1, window.location.pathname.length). - replace(/%20/g, ' '), - action = path.slice(0, path.indexOf('/')), - request = path.slice(path.indexOf('/') + 1, path.length); - - //console.log(path); - //console.log(action); - //console.log(request); - - // check action - if (action == 'search') { - mpcp.browser.show(); - request = decodeURIComponent(request); - mpcp.browser.search(request); - document.getElementById('search-browser').value = request; - } else if (action == 'library') { - mpcp.library.decodeRequest(request); - } else if (action == 'browser') { - mpcp.browser.show(); - mpcp.browser.current = request; - mpcp.browser.previous = request; - mpcp.browser.update(); + console.log('initAfterConnection'); + //mpcp.player.updateAll(); // inside of mpcp.playlist.updateTitle + mpcp.player.updateMixer(); + //mpcp.playlist.updateAll(); // inside of mpcp.playlist.updateTitle + //mpcp.browser.update(); // done lower in the function + mpcp.utils.updateStats(); + + var path = window.location.pathname. + slice(1, window.location.pathname.length). + replace(/%20/g, ' '), + action = path.slice(0, path.indexOf('/')), + request = path.slice(path.indexOf('/') + 1, path.length); + + //console.log(path); + //console.log(action); + //console.log(request); + + // check action + if (action == 'search') { + mpcp.browser.show(); + request = decodeURIComponent(request); + mpcp.browser.search(request); + document.getElementById('search-browser').value = request; + } else if (action == 'library') { + mpcp.library.decodeRequest(request); + } else if (action == 'browser') { + mpcp.browser.show(); + mpcp.browser.current = request; + mpcp.browser.previous = request; + mpcp.browser.update(); + } else { + // else check settings + if (mpcp.settings.browser == 'library') { + mpcp.library.show(); + mpcp.libraryArtists.update(); } else { - // else check settings - if (mpcp.settings.browser == 'library') { - mpcp.library.show(); - mpcp.libraryArtists.update(); - } else { - mpcp.browser.show(); - mpcp.browser.update(); - } + mpcp.browser.show(); + mpcp.browser.update(); } + } + + mpcp.browser.initEvents(); + + // sometimes the socket doesn't send the vote updates, + // this is used for that + setTimeout(function () { + if (!mpcp.vote.received) { + console.log('server didnt send votes, asking for votes'); + socket.send(JSON.stringify( + {'type': 'get-votes'}), function (err) { + if (err) console.log(err); + }); + } + }, 200); - mpcp.browser.initEvents(); - - // sometimes the socket doesn't send the vote updates, - // this is used for that - setTimeout(function () { - if (!mpcp.vote.received) { - console.log('server didnt send votes, asking for votes'); - socket.send(JSON.stringify( - {'type': 'get-votes'}), function (err) { - if (err) console.log(err); - }); - } - }, 200); - - socket.onclose = function (event) { - mpcp.disconnect.callSocketClose(); - }; + socket.onclose = function (event) { + mpcp.disconnect.callSocketClose(); + }; } // Web socket configuration socket.onmessage = function (event) { - if (!event.data) return; - - //console.log(event.data); - - var msg = JSON.parse(event.data); - - switch(msg.type) { - case 'current-info': - mpcp.vote.received = true; - mpcp.users.total = msg['total-clients']; - mpcp.vote.needed = msg['song-skip-total']; - mpcp.vote.setTitles( msg['song-skip-previous'], 'previous'); - mpcp.vote.setTitles( msg['song-skip-next'], 'next'); - break; - - case 'init': - mpcp.playlist.updateTitle(msg['playlist-title']); - mpcp.vote.enabled = msg['song-vote']; - if (msg['downloader-enabled']) - mpcp.downloader.init(msg['downloader-location']); - mpcp.utils.setCurrentAlbumArt(msg['album-art']); - initAfterConnection(); - break; - - // playlist - // when song is playing, the playlist doesn't get updated, - // this is used to force the update - case 'clear-playlist': - console.log('user clear-playlist called'); - mpcp.playlist.updateTitle(''); - //mpcp.player.updateAll(); // inside mpcp.playlist.updateAll - mpcp.playlist.updateAll(); - break; - - case 'update-playlist': - console.log('user update-playlist called'); - mpcp.playlist.updateAll(); - break; - - case 'update-browser': - console.log('user update-browser called'); - mpcp.browser.doUpdate = true; - mpcp.browser.update(); - mpcp.utils.updateStats(); - break; - - case 'playlist-title': - mpcp.playlist.updateTitle(msg.info); - break; - - // player - case 'song-next': - msg.info += ' skipped to the next song.'; - mpcp.history.add(msg.info, 'info'); - - // don't show notification if only 1 person is using the client - if (mpcp.users.total <= 1) return; - - mpcp.lazyToast.info(msg.info, 'Song Skipped', 10000); - break; - - case 'song-previous': - msg.info += ' skipped to the previous song.'; - mpcp.history.add(msg.info, 'info'); - - // don't show notification if only 1 person is using the client - if (mpcp.users.total <= 1) return; - - mpcp.lazyToast.info(msg.info, 'Song Skipped', 10000); - break; - - // stored - case 'playlist-reload': - mpcp.stored.open(msg.info); - break; - - // vote - case 'song-vote-next': - console.log('received skip'); - mpcp.vote.message(msg.info, 'next'); - break; - - case 'song-vote-previous': - console.log('received skip'); - mpcp.vote.message(msg.info, 'previous'); - break; - - case 'request-vote-update-from-server': - // assums a vote reset - document.getElementById('next').classList.remove('active'); - document.getElementById('previous').classList.remove('active'); - break; - - case 'skipped': - console.log('skip successful received'); - document.getElementById('next').classList.remove('active'); - document.getElementById('previous').classList.remove('active'); - var str = ''; - - for (var i in msg.info) { - str += mpcp.users.get(msg.info[i]) + ', '; - } - - if (mpcp.users.total > 1) { - str += 'skipped: ' + mpcp.player.title + '.'; - mpcp.lazyToast.info(str, 'Song Skip'); - mpcp.history.add(str, 'info'); - } else - mpcp.history.add('Skipped: ' + mpcp.player.title, 'info'); - - mpcp.vote.setTitles(0, 'previous'); - mpcp.vote.setTitles(0, 'next'); - break; - - case 'user-skip-next': - document.getElementById('next').classList.add('active'); - break; - - case 'user-skip-previous': - document.getElementById('previous').classList.add('active'); - break; - - case 'hostnames': - console.log('received hostnames update'); - mpcp.users.populate(msg.info); - break; - - // downloader - case 'downloader-download': - mpcp.downloader.setStatus('Downloading and converting video...'); - break; - - case 'downloader-status': - document.getElementById('downloader-status').innerHTML = msg.info; - break; - - // album art - case 'album-art': - mpcp.utils.setCurrentAlbumArt(msg.url); - break; - } + if (!event.data) return; + + //console.log(event.data); + + var msg = JSON.parse(event.data); + + switch(msg.type) { + case 'current-info': + mpcp.vote.received = true; + mpcp.users.total = msg['total-clients']; + mpcp.vote.needed = msg['song-skip-total']; + mpcp.vote.setTitles( msg['song-skip-previous'], 'previous'); + mpcp.vote.setTitles( msg['song-skip-next'], 'next'); + break; + + case 'init': + mpcp.playlist.updateTitle(msg['playlist-title']); + mpcp.vote.enabled = msg['song-vote']; + if (msg['downloader-enabled']) + mpcp.downloader.init(msg['downloader-location']); + mpcp.utils.setCurrentAlbumArt(msg['album-art']); + initAfterConnection(); + break; + + // playlist + // when song is playing, the playlist doesn't get updated, + // this is used to force the update + case 'clear-playlist': + console.log('user clear-playlist called'); + mpcp.playlist.updateTitle(''); + //mpcp.player.updateAll(); // inside mpcp.playlist.updateAll + mpcp.playlist.updateAll(); + break; + + case 'update-playlist': + console.log('user update-playlist called'); + mpcp.playlist.updateAll(); + break; + + case 'update-browser': + console.log('user update-browser called'); + mpcp.browser.doUpdate = true; + mpcp.browser.update(); + mpcp.utils.updateStats(); + break; + + case 'playlist-title': + mpcp.playlist.updateTitle(msg.info); + break; + + // player + case 'song-next': + msg.info += ' skipped to the next song.'; + mpcp.history.add(msg.info, 'info'); + + // don't show notification if only 1 person is using the client + if (mpcp.users.total <= 1) return; + + mpcp.lazyToast.info(msg.info, 'Song Skipped', 10000); + break; + + case 'song-previous': + msg.info += ' skipped to the previous song.'; + mpcp.history.add(msg.info, 'info'); + + // don't show notification if only 1 person is using the client + if (mpcp.users.total <= 1) return; + + mpcp.lazyToast.info(msg.info, 'Song Skipped', 10000); + break; + + // stored + case 'playlist-reload': + mpcp.stored.open(msg.info); + break; + + // vote + case 'song-vote-next': + console.log('received skip'); + mpcp.vote.message(msg.info, 'next'); + break; + + case 'song-vote-previous': + console.log('received skip'); + mpcp.vote.message(msg.info, 'previous'); + break; + + case 'request-vote-update-from-server': + // assums a vote reset + document.getElementById('next').classList.remove('active'); + document.getElementById('previous').classList.remove('active'); + break; + + case 'skipped': + console.log('skip successful received'); + document.getElementById('next').classList.remove('active'); + document.getElementById('previous').classList.remove('active'); + var str = ''; + + for (var i in msg.info) { + str += mpcp.users.get(msg.info[i]) + ', '; + } + + if (mpcp.users.total > 1) { + str += 'skipped: ' + mpcp.player.title + '.'; + mpcp.lazyToast.info(str, 'Song Skip'); + mpcp.history.add(str, 'info'); + } else + mpcp.history.add('Skipped: ' + mpcp.player.title, 'info'); + + mpcp.vote.setTitles(0, 'previous'); + mpcp.vote.setTitles(0, 'next'); + break; + + case 'user-skip-next': + document.getElementById('next').classList.add('active'); + break; + + case 'user-skip-previous': + document.getElementById('previous').classList.add('active'); + break; + + case 'hostnames': + console.log('received hostnames update'); + mpcp.users.populate(msg.info); + break; + + // downloader + case 'downloader-download': + mpcp.downloader.setStatus('Downloading and converting video...'); + break; + + case 'downloader-status': + document.getElementById('downloader-status').innerHTML = msg.info; + break; + + // album art + case 'album-art': + mpcp.utils.setCurrentAlbumArt(msg.url); + break; + } }; // gracefully close the socket $(window).on('beforeunload', function () { - socket.close(); + socket.close(); }); return socket; diff --git a/src/stored.js b/src/stored.js index a13f496..2620ee6 100644 --- a/src/stored.js +++ b/src/stored.js @@ -2,456 +2,456 @@ module.exports = function (mpcp) { // the stored playlists return { - // used for saving external playlists - fileArr: [], - // used for returning values for opening playlists - call: null, - // current is used to tell what the save operation is (native being mpd) - current: 'native', - // whether to update the list - doUpdate: true, - // total number of places that update(id) is used (for doUpdate) - // may be changed in the future to be more dynamic - totalIds: 2, - currentId: 0, - // currently opened modal (open or save) - active: '', - // get row selected - rowSelect: null, - - // used show all playlists - // fileArr is used when saving the playlist externally after clicking save - updatePlaylists: function (id, type, callback) { - if (type) { - if (typeof type == 'function') { - this.call = type; - this.current = 'native'; - } else if (Array.isArray(type)) { - this.fileArr = type; - this.current = 'fileids'; - } - } else if (!this.doUpdate) { - if (++this.currentId >= this.totalIds) { - this.doUpdate = true; - this.currentId = 0; - - if ($('.playlists .append').children('.gen') < 1) { - var html = 'No playlists found'; - $(id +' .playlists tbody')[0].innerHTML = html; - console.log('no playlists found (dont update)'); - } - } - - if (callback) callback(); - return console.log('no update'); - } else { - this.current = 'native'; + // used for saving external playlists + fileArr: [], + // used for returning values for opening playlists + call: null, + // current is used to tell what the save operation is (native being mpd) + current: 'native', + // whether to update the list + doUpdate: true, + // total number of places that update(id) is used (for doUpdate) + // may be changed in the future to be more dynamic + totalIds: 2, + currentId: 0, + // currently opened modal (open or save) + active: '', + // get row selected + rowSelect: null, + + // used show all playlists + // fileArr is used when saving the playlist externally after clicking save + updatePlaylists: function (id, type, callback) { + if (type) { + if (typeof type == 'function') { + this.call = type; + this.current = 'native'; + } else if (Array.isArray(type)) { + this.fileArr = type; + this.current = 'fileids'; + } + } else if (!this.doUpdate) { + if (++this.currentId >= this.totalIds) { + this.doUpdate = true; + this.currentId = 0; + + if ($('.playlists .append').children('.gen') < 1) { + var html = 'No playlists found'; + $(id +' .playlists tbody')[0].innerHTML = html; + console.log('no playlists found (dont update)'); } + } - console.log('update stored playlists table'); - - komponist.listplaylists(function (err, playlists) { - //console.log(id + ':'); - //console.log(playlists); - if (err) { - if (err.message == 'No such file or directory [52@0] {listplaylists}') { - mpcp.lazyToast.error('Is there a playlist directory and correct write permissions?', 'Cannot read playlist directory!'); - } - - html = 'No saved playlists'; - $(id +' .modal-body')[0].innerHTML = html; - console.log(err); - if (callback) callback(); - return; - } - - if (id == '#playlist-open-modal') - mpcp.stored.active = 'open'; - else if (id == '#playlist-save-modal') - mpcp.stored.active = 'save'; - - $(id +' .modal-body .gen').remove(); - - var html = ''; + if (callback) callback(); + return console.log('no update'); + } else { + this.current = 'native'; + } - if (!Array.isArray(playlists)) - playlists = [playlists]; + console.log('update stored playlists table'); - if (!playlists.length) { - html = 'No saved playlists'; - $(id +' .modal-body')[0].innerHTML = html; - return console.log('No playlists'); - } + komponist.listplaylists(function (err, playlists) { + //console.log(id + ':'); + //console.log(playlists); + if (err) { + if (err.message == 'No such file or directory [52@0] {listplaylists}') { + mpcp.lazyToast.error('Is there a playlist directory and correct write permissions?', 'Cannot read playlist directory!'); + } - var i = 0; - - $(playlists).each(function (item, value) { - komponist.listplaylist(value.playlist, function (err, songs) { - if (err && err.message == - 'No such playlist [50@0] {listplaylist}') { - console.log('no playlist found: "' + - value.playlist + '"'); - } else if (err) { - console.log(err); - } - - if (!Array.isArray(songs)) - songs = [songs]; - - value.playlist = value.playlist.replace(/ /g, '\u00a0'); - html += '' + value.playlist + '' + songs.length + ''; - if (++i == playlists.length) { - $(id +' .playlists tbody')[0].innerHTML = html; - if (callback) callback(); - } - }); - }); + html = 'No saved playlists'; + $(id +' .modal-body')[0].innerHTML = html; + console.log(err); + if (callback) callback(); + return; + } + + if (id == '#playlist-open-modal') + mpcp.stored.active = 'open'; + else if (id == '#playlist-save-modal') + mpcp.stored.active = 'save'; + + $(id +' .modal-body .gen').remove(); + + var html = ''; + + if (!Array.isArray(playlists)) + playlists = [playlists]; + + if (!playlists.length) { + html = 'No saved playlists'; + $(id +' .modal-body')[0].innerHTML = html; + return console.log('No playlists'); + } + + var i = 0; + + $(playlists).each(function (item, value) { + komponist.listplaylist(value.playlist, function (err, songs) { + if (err && err.message == + 'No such playlist [50@0] {listplaylist}') { + console.log('no playlist found: "' + + value.playlist + '"'); + } else if (err) { + console.log(err); + } + + if (!Array.isArray(songs)) + songs = [songs]; + + value.playlist = value.playlist.replace(/ /g, '\u00a0'); + html += '' + value.playlist + '' + songs.length + ''; + if (++i == playlists.length) { + $(id +' .playlists tbody')[0].innerHTML = html; + if (callback) callback(); + } }); - }, - - // save the playlist. Wrapper for komponist.save() - save: function (file, callback) { - // When titles are "", it updates the current playlist, kind of. - // It works via playlist editor, but not the playlist. - // So I'd rather disable the feature in case people get confused. - if (!file || file === "") { - mpcp.lazyToast.warning('You must provide a title!', 'Playlist'); - $('#playlist-save-modal').modal('hide'); + }); + }); + }, + + // save the playlist. Wrapper for komponist.save() + save: function (file, callback) { + // When titles are "", it updates the current playlist, kind of. + // It works via playlist editor, but not the playlist. + // So I'd rather disable the feature in case people get confused. + if (!file || file === "") { + mpcp.lazyToast.warning('You must provide a title!', 'Playlist'); + $('#playlist-save-modal').modal('hide'); + + if (callback) callback(); + return console.log('invalid title'); + } - if (callback) callback(); - return console.log('invalid title'); - } + file = file.trim().replace(/\u00a0/g, " "); + console.log(file); - file = file.trim().replace(/\u00a0/g, " "); - console.log(file); + if (this.current == 'fileids') { + if (!this.fileArr.length) { + mpcp.lazyToast.warning('Playlist empty!', 'Playlist'); + $('#playlist-save-modal').modal('hide'); - if (this.current == 'fileids') { - if (!this.fileArr.length) { - mpcp.lazyToast.warning('Playlist empty!', 'Playlist'); - $('#playlist-save-modal').modal('hide'); + if (callback) callback(); + return console.log('empty playlist'); + } + + // overwrite any existing playlist + komponist.rm(file, function (err, val) { + if (err) { + if (err.message == 'No such playlist [50@0] {rm}') + console.log('No playlist to overwrite, continue...'); + else + console.log(err); + } - if (callback) callback(); - return console.log('empty playlist'); + //console.log(mpcp.stored.fileArr); + + // continue saving... + var saved = true, + updatedCurrentPlaylist = false, + invalid = false, + noFile = false, + notFound = false, + unknown = false, + err2, + // since everything is async, we have to use a deferred object. + // i is counting the elements being added, which resolves the + // deferred. + i = 0, + def = $.Deferred(); + + function addSongToPlaylist(file, song) { + komponist.playlistadd(file, song, function (err2, val) { + // I would like to break from the each loop when an + // error occurs, but getting that set up is hackish. + // For now, it will run the each loop every time an + // error is caught + ++i; + + if (err2) { + if (err2.message == 'playlist name is invalid: playlist names may not contain slashes, newlines or carriage returns [2@0] {playlistadd}') { + invalid = true; + } else if (err2.message == 'No such file or directory [52@0] {playlistadd}') { + noFile = true; + } else if (err2.message == 'Not found [50@0] {playlistadd}') { + // read MESSAGE1 + err2 = song; + notFound = true; + } else { + unknown = true; + } + + saved = false; + // resolves earlier because output would be the + // same anyways + console.log(err2); + def.resolve(); + return; } - // overwrite any existing playlist - komponist.rm(file, function (err, val) { - if (err) { - if (err.message == 'No such playlist [50@0] {rm}') - console.log('No playlist to overwrite, continue...'); - else - console.log(err); - } - - //console.log(mpcp.stored.fileArr); - - // continue saving... - var saved = true, - updatedCurrentPlaylist = false, - invalid = false, - noFile = false, - notFound = false, - unknown = false, - err2, - // since everything is async, we have to use a deferred object. - // i is counting the elements being added, which resolves the - // deferred. - i = 0, - def = $.Deferred(); - - function addSongToPlaylist(file, song) { - komponist.playlistadd(file, song, function (err2, val) { - // I would like to break from the each loop when an - // error occurs, but getting that set up is hackish. - // For now, it will run the each loop every time an - // error is caught - ++i; - - if (err2) { - if (err2.message == 'playlist name is invalid: playlist names may not contain slashes, newlines or carriage returns [2@0] {playlistadd}') { - invalid = true; - } else if (err2.message == 'No such file or directory [52@0] {playlistadd}') { - noFile = true; - } else if (err2.message == 'Not found [50@0] {playlistadd}') { - // read MESSAGE1 - err2 = song; - notFound = true; - } else { - unknown = true; - } - - saved = false; - // resolves earlier because output would be the - // same anyways - console.log(err2); - def.resolve(); - return; - } - - if (i == mpcp.stored.fileArr.length) { - if (mpcp.playlist.current == file) - updatedCurrentPlaylist = true; - def.resolve(); - } - }); - } - - for (var j = 0; j < mpcp.stored.fileArr.length; ++j) { - // this if statement doesn't actually work, async makes - // this loop happen too quickly - if (!saved) return false; - addSongToPlaylist(file, mpcp.stored.fileArr[j]); - } - - def.done(function () { - // in deferred because the loop can execute the - // playlistadd multiple times - if (invalid) { - mpcp.lazyToast.warning('Playlist may not contain slashes, newlines, or carriage returns.', 'Invalid Characters', 10000); - } else if (noFile) { - mpcp.lazyToast.error('Is there a playlist directory and correct write permissions?', 'Cannot read playlist directory!'); - } else if (notFound) { - // read MESSAGE1 - mpcp.lazyToast.error('File not found: ' + err2, 'Playlist'); - } else if (unknown) { - mpcp.lazyToast.error(err2, 'Unhandled Error'); - } - - if (saved) { - file = file.replace(/ /g, '\u00a0'); - mpcp.lazyToast.info( - file + ' playlist saved!', 'Playlist update'); - - if (updatedCurrentPlaylist) { - var msg = 'You must open the updated playlist for it to update the current playlist.'; - mpcp.history.add(msg, 'info'); - toastr.info(msg + '', 'Playlist update', { - 'closeButton': true, - 'positionClass': 'toast-bottom-left', - 'preventDuplicates': false, - 'timeOut': '-1', - 'extendedTimeOut': '-1' - }); - } - - // clear fileArr after saving - mpcp.stored.fileArr = []; - } - - if (callback) callback(); - }); - }); - } else { - var trs = $(mpcp.playlist.tbody).children('.gen').not('.rem'); - - // horrible check, whats the better way to check for - // 'Empty playlist'? - if (trs.length < 1 || ( - trs[0].childNodes[0].childNodes[0].childNodes[0] && - $(trs[0].childNodes[0].childNodes[0].childNodes[0].data). - selector == 'Empty playlist')) { - mpcp.lazyToast.warning('Playlist empty!', 'Playlist'); - $('#playlist-save-modal').modal('hide'); - - if (callback) callback(); - return console.log('playlist empty'); + if (i == mpcp.stored.fileArr.length) { + if (mpcp.playlist.current == file) + updatedCurrentPlaylist = true; + def.resolve(); } + }); + } - komponist.rm(file, function (err, val) { - if (err) { - if (err.message == 'No such playlist [50@0] {rm}') - console.log('No playlist to overwrite, continue...'); - else - console.log(err); - } - - // continue saving... - komponist.save(file, function (err, val) { - file = file.replace(/ /g, '\u00a0'); - - if (err) { - console.log(err.message); - - if (err.message == 'No such file or directory [52@0] {save}') { - mpcp.lazyToast.error('Is there a playlist directory and correct write permissions?', 'Cannot read playlist directory!'); - } else if (err.message == 'playlist name is invalid: playlist names may not contain slashes, newlines or carriage returns [2@0] {save}') { - mpcp.lazyToast.warning('Playlist may not contain slashes, newlines, or carriage returns.', 'Invalid Characters', 10000); - } - - if (callback) callback(); - return console.log(err); - } - - mpcp.lazyToast.info( - file + ' playlist saved!', 'Playlist update'); - - // set title locally before sending to clients - $('#playlist-title strong')[0].innerHTML = file; - $('#playlist-title strong').attr('title', file); - - socket.send(JSON.stringify({ - 'type': 'playlist-title', 'info': file - }), function (err) { - if (err) console.log(err); - }); - - if (callback) callback(); - }); - }); + for (var j = 0; j < mpcp.stored.fileArr.length; ++j) { + // this if statement doesn't actually work, async makes + // this loop happen too quickly + if (!saved) return false; + addSongToPlaylist(file, mpcp.stored.fileArr[j]); } - $('#playlist-save-modal').modal('hide'); - }, - - externalSave: function (callback) { - var trs = $(mpcp.pe.tbody).children('.gen').not('.rem'), - fileIds = []; - - for (var i = 0; i < trs.length; ++i) - fileIds[i] = $(trs[i]).data().fileid; - - mpcp.stored.updatePlaylists('#playlist-save-modal', fileIds, callback); - }, - - removePlaylist: function (file, tr, callback) { - file = String(file).replace(/\u00a0/g, " "); - // client side deletion, less jaring (stops flashing the list) - mpcp.stored.doUpdate = false; - console.log('delete playlist ' + file); - - komponist.rm(file, function (err) { - if (err) { - mpcp.lazyToast.error(err, 'Error removing playlist!'); - console.log(err); - } else { - $(tr)[0].remove(); + def.done(function () { + // in deferred because the loop can execute the + // playlistadd multiple times + if (invalid) { + mpcp.lazyToast.warning('Playlist may not contain slashes, newlines, or carriage returns.', 'Invalid Characters', 10000); + } else if (noFile) { + mpcp.lazyToast.error('Is there a playlist directory and correct write permissions?', 'Cannot read playlist directory!'); + } else if (notFound) { + // read MESSAGE1 + mpcp.lazyToast.error('File not found: ' + err2, 'Playlist'); + } else if (unknown) { + mpcp.lazyToast.error(err2, 'Unhandled Error'); + } + + if (saved) { + file = file.replace(/ /g, '\u00a0'); + mpcp.lazyToast.info( + file + ' playlist saved!', 'Playlist update'); + + if (updatedCurrentPlaylist) { + var msg = 'You must open the updated playlist for it to update the current playlist.'; + mpcp.history.add(msg, 'info'); + toastr.info(msg + '', 'Playlist update', { + 'closeButton': true, + 'positionClass': 'toast-bottom-left', + 'preventDuplicates': false, + 'timeOut': '-1', + 'extendedTimeOut': '-1' + }); } - if (callback) callback(); + // clear fileArr after saving + mpcp.stored.fileArr = []; + } + + if (callback) callback(); }); - }, - - // open a playlist. Wrapper for komponist.open() - open: function (file, callback) { - file = file.toString().replace(/\u00a0/g, " "); - - if (this.call !== null) { - console.log('calling fn..'); - this.call(file, callback); - this.call = null; - } else { - console.log('confirm open playlist'); - console.log(file); - // stops duplicate updating because of socket sending - mpcp.playlist.doUpdate = false; - - komponist.clear(function (err) { - if (err) console.log(err); - - komponist.load(file, function (err) { - if (err) { - mpcp.lazyToast.error('Error loading the playlist! ' + - err.message); - console.log(err); - if (callback) callback(); - return; - } - - // set title locally before sending to clients - $('#playlist-title strong')[0].innerHTML = file; - $('#playlist-title strong').attr('title', file); - - socket.send(JSON.stringify({ - 'type': 'playlist-title', 'info': file - }), function (err) { - if (err) console.log(err); - }); - - mpcp.playlist.addCallbackUpdate(callback); - }); - }); - } - $('#playlist-open-modal').modal('hide'); - }, + }); + } else { + var trs = $(mpcp.playlist.tbody).children('.gen').not('.rem'); + + // horrible check, whats the better way to check for + // 'Empty playlist'? + if (trs.length < 1 || ( + trs[0].childNodes[0].childNodes[0].childNodes[0] && + $(trs[0].childNodes[0].childNodes[0].childNodes[0].data). + selector == 'Empty playlist')) { + mpcp.lazyToast.warning('Playlist empty!', 'Playlist'); + $('#playlist-save-modal').modal('hide'); - initEvents: function () { - var rowSelect = mpcp.utils.rowSelect('.playlists-row', 'bg-primary'); + if (callback) callback(); + return console.log('playlist empty'); + } - rowSelect.on('down', function (ele) { - var file = $(ele).data().fileid; - document.getElementById('playlist-save-input').value = file; - }); + komponist.rm(file, function (err, val) { + if (err) { + if (err.message == 'No such playlist [50@0] {rm}') + console.log('No playlist to overwrite, continue...'); + else + console.log(err); + } - rowSelect.on('up', function (ele) { - var file = $(ele).data().fileid; - document.getElementById('playlist-save-input').value = file; - }); + // continue saving... + komponist.save(file, function (err, val) { + file = file.replace(/ /g, '\u00a0'); - rowSelect.on('click', function (ele) { - var file = $(ele).data().fileid; - document.getElementById('playlist-save-input').value = file; - }); + if (err) { + console.log(err.message); - rowSelect.on('enter', function (ele) { - if (mpcp.stored.active == 'open') - mpcp.playlist.openFromStored(); - else if (mpcp.stored.active == 'save') - mpcp.playlist.saveFromStored(); - }); + if (err.message == 'No such file or directory [52@0] {save}') { + mpcp.lazyToast.error('Is there a playlist directory and correct write permissions?', 'Cannot read playlist directory!'); + } else if (err.message == 'playlist name is invalid: playlist names may not contain slashes, newlines or carriage returns [2@0] {save}') { + mpcp.lazyToast.warning('Playlist may not contain slashes, newlines, or carriage returns.', 'Invalid Characters', 10000); + } - rowSelect.on('delete', function (ele) { - var file = $(ele).data().fileid; - mpcp.stored.removePlaylist(file, ele); - }); + if (callback) callback(); + return console.log(err); + } - $(document).on('click', '.playlist-remove', function () { - var file = $(this).data().fileid, - tr = $(this).parent().parent(); + mpcp.lazyToast.info( + file + ' playlist saved!', 'Playlist update'); - mpcp.stored.removePlaylist(file, tr); - }); + // set title locally before sending to clients + $('#playlist-title strong')[0].innerHTML = file; + $('#playlist-title strong').attr('title', file); - $(document).on('dblclick', '#playlist-open-modal .gen', function () { - mpcp.playlist.openFromStored(); - }); + socket.send(JSON.stringify({ + 'type': 'playlist-title', 'info': file + }), function (err) { + if (err) console.log(err); + }); - $(document).on('dblclick', '#playlist-save-modal .gen', function () { - mpcp.playlist.saveFromStored(); + if (callback) callback(); }); + }); + } - // reset vars - $('#playlist-open-modal').on('hidden.bs.modal', function () { - mpcp.stored.call = null; - mpcp.stored.active = ''; - }); + $('#playlist-save-modal').modal('hide'); + }, + + externalSave: function (callback) { + var trs = $(mpcp.pe.tbody).children('.gen').not('.rem'), + fileIds = []; + + for (var i = 0; i < trs.length; ++i) + fileIds[i] = $(trs[i]).data().fileid; + + mpcp.stored.updatePlaylists('#playlist-save-modal', fileIds, callback); + }, + + removePlaylist: function (file, tr, callback) { + file = String(file).replace(/\u00a0/g, " "); + // client side deletion, less jaring (stops flashing the list) + mpcp.stored.doUpdate = false; + console.log('delete playlist ' + file); + + komponist.rm(file, function (err) { + if (err) { + mpcp.lazyToast.error(err, 'Error removing playlist!'); + console.log(err); + } else { + $(tr)[0].remove(); + } + + if (callback) callback(); + }); + }, + + // open a playlist. Wrapper for komponist.open() + open: function (file, callback) { + file = file.toString().replace(/\u00a0/g, " "); + + if (this.call !== null) { + console.log('calling fn..'); + this.call(file, callback); + this.call = null; + } else { + console.log('confirm open playlist'); + console.log(file); + // stops duplicate updating because of socket sending + mpcp.playlist.doUpdate = false; + + komponist.clear(function (err) { + if (err) console.log(err); + + komponist.load(file, function (err) { + if (err) { + mpcp.lazyToast.error('Error loading the playlist! ' + + err.message); + console.log(err); + if (callback) callback(); + return; + } - $('#playlist-save-modal').on('hidden.bs.modal', function () { - mpcp.stored.fileArr = []; - mpcp.stored.active = ''; - }); + // set title locally before sending to clients + $('#playlist-title strong')[0].innerHTML = file; + $('#playlist-title strong').attr('title', file); - $('#playlist-save-clear').click(function () { - document.getElementById('playlist-save-input').value = ''; - $('#playlist-save-input').focus(); - rowSelect.deselect(); - }); + socket.send(JSON.stringify({ + 'type': 'playlist-title', 'info': file + }), function (err) { + if (err) console.log(err); + }); - $('#playlist-save-input').focus(function () { - $(document).keydown(function (e) { - if (e.keyCode == 13) mpcp.playlist.saveFromStored(); - }); + mpcp.playlist.addCallbackUpdate(callback); }); - - // separate for open and save because of duplication issues - mpcp.utils.tableSort('#playlist-open-modal table', - '#playlist-open-modal .col-playlists-title', 1, 'string'); - mpcp.utils.tableSort('#playlist-open-modal table', - '#playlist-open-modal .col-playlists-songs', 2, 'number'); - mpcp.utils.tableSort('#playlist-save-modal table', - '#playlist-save-modal .col-playlists-title', 1, 'string'); - mpcp.utils.tableSort('#playlist-save-modal table', - '#playlist-save-modal .col-playlists-songs', 2, 'number'); + }); } + $('#playlist-open-modal').modal('hide'); + }, + + initEvents: function () { + var rowSelect = mpcp.utils.rowSelect('.playlists-row', 'bg-primary'); + + rowSelect.on('down', function (ele) { + var file = $(ele).data().fileid; + document.getElementById('playlist-save-input').value = file; + }); + + rowSelect.on('up', function (ele) { + var file = $(ele).data().fileid; + document.getElementById('playlist-save-input').value = file; + }); + + rowSelect.on('click', function (ele) { + var file = $(ele).data().fileid; + document.getElementById('playlist-save-input').value = file; + }); + + rowSelect.on('enter', function (ele) { + if (mpcp.stored.active == 'open') + mpcp.playlist.openFromStored(); + else if (mpcp.stored.active == 'save') + mpcp.playlist.saveFromStored(); + }); + + rowSelect.on('delete', function (ele) { + var file = $(ele).data().fileid; + mpcp.stored.removePlaylist(file, ele); + }); + + $(document).on('click', '.playlist-remove', function () { + var file = $(this).data().fileid, + tr = $(this).parent().parent(); + + mpcp.stored.removePlaylist(file, tr); + }); + + $(document).on('dblclick', '#playlist-open-modal .gen', function () { + mpcp.playlist.openFromStored(); + }); + + $(document).on('dblclick', '#playlist-save-modal .gen', function () { + mpcp.playlist.saveFromStored(); + }); + + // reset vars + $('#playlist-open-modal').on('hidden.bs.modal', function () { + mpcp.stored.call = null; + mpcp.stored.active = ''; + }); + + $('#playlist-save-modal').on('hidden.bs.modal', function () { + mpcp.stored.fileArr = []; + mpcp.stored.active = ''; + }); + + $('#playlist-save-clear').click(function () { + document.getElementById('playlist-save-input').value = ''; + $('#playlist-save-input').focus(); + rowSelect.deselect(); + }); + + $('#playlist-save-input').focus(function () { + $(document).keydown(function (e) { + if (e.keyCode == 13) mpcp.playlist.saveFromStored(); + }); + }); + + // separate for open and save because of duplication issues + mpcp.utils.tableSort('#playlist-open-modal table', + '#playlist-open-modal .col-playlists-title', 1, 'string'); + mpcp.utils.tableSort('#playlist-open-modal table', + '#playlist-open-modal .col-playlists-songs', 2, 'number'); + mpcp.utils.tableSort('#playlist-save-modal table', + '#playlist-save-modal .col-playlists-title', 1, 'string'); + mpcp.utils.tableSort('#playlist-save-modal table', + '#playlist-save-modal .col-playlists-songs', 2, 'number'); + } }; }; diff --git a/src/tableheader.js b/src/tableheader.js index d530195..2b0a5ee 100644 --- a/src/tableheader.js +++ b/src/tableheader.js @@ -8,68 +8,68 @@ module.exports = function (mpcp) { // I don't care that much for portability of this functionality, especially since // so many plugins like this exist already. return (function (tableid, event) { - var table = document.getElementById(tableid), - thead = document.getElementById(tableid + '-header').tHead; + var table = document.getElementById(tableid), + thead = document.getElementById(tableid + '-header').tHead; - updateWidth(); - - if (event) { - window.addEventListener(event, function () { - updateWidth(); - }); - } + updateWidth(); - window.addEventListener('MPCPReflow', function () { - updateWidth(); + if (event) { + window.addEventListener(event, function () { + updateWidth(); }); + } - window.addEventListener('resize', function () { - updateWidth(); - }); + window.addEventListener('MPCPReflow', function () { + updateWidth(); + }); - function updateWidth() { - // update cell width - var row = table.tBodies[0].rows[0], - i = 0, - width = 0, - tHeadCell; + window.addEventListener('resize', function () { + updateWidth(); + }); + + function updateWidth() { + // update cell width + var row = table.tBodies[0].rows[0], + i = 0, + width = 0, + tHeadCell; - if (!row) { - //console.log('test ' + tableid); - width = thead.clientWidth; + if (!row) { + //console.log('test ' + tableid); + width = thead.clientWidth; - for (i = 0; i < thead.rows[0].cells.length; ++i) { - tHeadCell = thead.rows[0].cells[i]; - tHeadCell.style.width = width + 'px'; - } + for (i = 0; i < thead.rows[0].cells.length; ++i) { + tHeadCell = thead.rows[0].cells[i]; + tHeadCell.style.width = width + 'px'; + } - return; - } + return; + } - var cells = row.cells, - // check for colSpan - column = 0; + var cells = row.cells, + // check for colSpan + column = 0; - for (i = 0; i < cells.length; ++i) { - width = cells[i].clientWidth; - var colSpan = cells[i].colSpan; + for (i = 0; i < cells.length; ++i) { + width = cells[i].clientWidth; + var colSpan = cells[i].colSpan; - if (colSpan != 1) - width = width / colSpan; + if (colSpan != 1) + width = width / colSpan; - for (var j = 0; j < colSpan; ++j) { - tHeadCell = thead.rows[0].cells[column]; - tHeadCell.style.width = width + 'px'; - ++column; - } - } + for (var j = 0; j < colSpan; ++j) { + tHeadCell = thead.rows[0].cells[column]; + tHeadCell.style.width = width + 'px'; + ++column; + } } + } - return { - update: function() { - updateWidth(); - } - }; + return { + update: function() { + updateWidth(); + } + }; }); }; diff --git a/src/users.js b/src/users.js index 9b72325..12d546f 100644 --- a/src/users.js +++ b/src/users.js @@ -3,31 +3,31 @@ module.exports = function (mpcp) { // may utilize in the future for logging users who has voted and for // sharing pe's with other users return { - //ip:hostname - hostnames: {}, - // total number of users - total: 1, + //ip:hostname + hostnames: {}, + // total number of users + total: 1, - // populate user list - populate: function (hostnames) { - this.hostnames = hostnames; - var html = ''; - $('#user-list .gen').remove(); + // populate user list + populate: function (hostnames) { + this.hostnames = hostnames; + var html = ''; + $('#user-list .gen').remove(); - for (var ip in hostnames) { - html += '
  • ' + hostnames[ip] + - '
  • '; - } + for (var ip in hostnames) { + html += '
  • ' + hostnames[ip] + + '
  • '; + } - document.getElementById('user-list').innerHTML = html; - }, + document.getElementById('user-list').innerHTML = html; + }, - // returns the hostname (or ip) - get: function (ip) { - if (this.hostnames[ip] === undefined) return ip; + // returns the hostname (or ip) + get: function (ip) { + if (this.hostnames[ip] === undefined) return ip; - return this.hostnames[ip]; - } + return this.hostnames[ip]; + } }; }; diff --git a/src/utils.js b/src/utils.js index 248bf8b..8e2d0dd 100644 --- a/src/utils.js +++ b/src/utils.js @@ -2,659 +2,659 @@ module.exports = function (mpcp) { // copied from http://stackoverflow.com/a/1533945 $.fn.randomize = function (childElem) { - return this.each(function () { - var $this = $(this), - elems = $this.children(childElem); + return this.each(function () { + var $this = $(this), + elems = $this.children(childElem); - elems.sort(function () { return (Math.round(Math.random()) - 0.5); }); + elems.sort(function () { return (Math.round(Math.random()) - 0.5); }); - $this.detach(childElem); + $this.detach(childElem); - for (var i = 0; i < elems.length; ++i) $this.append(elems[i]); - }); + for (var i = 0; i < elems.length; ++i) $this.append(elems[i]); + }); }; return { - // convert int to mm:ss - toMMSS: function (str) { - str = (!str ? '0' : str); + // convert int to mm:ss + toMMSS: function (str) { + str = (!str ? '0' : str); + + var secNum = parseInt(str, 10), + minutes = Math.floor(secNum / 60), + seconds = secNum - (minutes * 60); + + if (minutes < 10) minutes = '0' + minutes; + if (seconds < 10) seconds = '0' + seconds; + + var time = minutes + ':' + seconds; + return time; + }, + + // convert int to dd days, hh hours, mm minutes + toFriendlyDDHHMM: function (str) { + str = (!str ? '0' : str); + + var secNum = parseInt(str, 10), + days = Math.floor(secNum / 86400), + hours = Math.floor(secNum / 3600) - (days * 24), + minutes = Math.floor(secNum / 60) - (days * 24 + hours) * 60; + + var time = days + ' days, ' + hours + ' hours, ' + minutes + ' minutes'; + return time; + }, + + // copied from http://stackoverflow.com/a/12745196 + nthOccurrence: function (string, char, nth) { + var firstIndex = string.indexOf(char); + var lengthUpToFirstIndex = firstIndex + 1; + + if (nth == 1) { + return firstIndex; + } else { + var stringAfterFirstOccurrence = + string.slice(lengthUpToFirstIndex), + nextOccurrence = + mpcp.utils.nthOccurrence( + stringAfterFirstOccurrence, char, nth - 1); + + if (nextOccurrence === -1) { + return -1; + } else { + return lengthUpToFirstIndex + nextOccurrence; + } + } + }, + + // return hours:minutes:seconds + getTime: function () { + var date = new Date(), + hours = date.getHours(), + minutes = date.getMinutes(), + seconds = date.getSeconds(); + + if (hours. toString().length == 1) hours = '0' + hours; + if (minutes.toString().length == 1) minutes = '0' + minutes; + if (seconds.toString().length == 1) seconds = '0' + seconds; + + return hours + ':' + minutes + ':' + seconds; + }, + + // generate a title for playlist and player + getSimpleTitle: function (title, artist, file, playlistVal) { + artist = (!artist ? 'unknown' : artist); + var song = artist + ' - ' + title, + fileStripped = mpcp.utils.stripSlash(file); + + // if the title doesn't exist, just return the file name + if (!playlistVal) { + return (!title ? fileStripped : song); + } else { + playlistVal = parseInt(playlistVal) + 1; + var songPlaylist = playlistVal + '. ' + song; + var fileStrippedPlaylist = playlistVal + '. ' + fileStripped; + + return (!title ? fileStrippedPlaylist : songPlaylist); + } + }, + + // strip to the last slash + stripSlash: function (str) { + if (!str) return; + + var index = str.lastIndexOf('/'); + return str.substring(index+1); + }, + + // http://stackoverflow.com/a/9645447 + ignoreCase: function (a, b) { + return a.toLowerCase().localeCompare(b.toLowerCase()); + }, + + // http://stackoverflow.com/a/9716488 + isNumber: function (n) { + return !isNaN(parseFloat(n)) && isFinite(n); + }, + + // create the popup window for song information + parseSongInfo: function (err, values, callback) { + if (err || !values || Object.keys(values).length < 1) { + mpcp.lazyToast.warning('This is most likely a bug with MPCParty or the song is not in the live database.', 'Error getting song information.', 10000); + if (callback) callback(); + return console.log(err); + } - var secNum = parseInt(str, 10), - minutes = Math.floor(secNum / 60), - seconds = secNum - (minutes * 60); + //console.log(values); + $('#song-info .gen').remove(); + $('#song-info-modal h4')[0].innerHTML = ''; + $('#song-info-modal').modal('show'); - if (minutes < 10) minutes = '0' + minutes; - if (seconds < 10) seconds = '0' + seconds; + var title = mpcp.utils.getSimpleTitle(values.Title, values.Artist, values.file); + $('#song-info-modal h4')[0].innerHTML = title; - var time = minutes + ':' + seconds; - return time; - }, + if (values.Time) values.Time = mpcp.utils.toMMSS(values.Time); - // convert int to dd days, hh hours, mm minutes - toFriendlyDDHHMM: function (str) { - str = (!str ? '0' : str); + // http://stackoverflow.com/a/31102605 + var html = ''; + Object.keys(values).sort(mpcp.utils.ignoreCase).forEach(function (key) { + html += '' + key + '' + values[key] + ''; + }); - var secNum = parseInt(str, 10), - days = Math.floor(secNum / 86400), - hours = Math.floor(secNum / 3600) - (days * 24), - minutes = Math.floor(secNum / 60) - (days * 24 + hours) * 60; + $('#song-info tbody')[0].innerHTML = html; - var time = days + ' days, ' + hours + ' hours, ' + minutes + ' minutes'; - return time; - }, + if (callback) callback(); + }, - // copied from http://stackoverflow.com/a/12745196 - nthOccurrence: function (string, char, nth) { - var firstIndex = string.indexOf(char); - var lengthUpToFirstIndex = firstIndex + 1; + // replace listAllInfo because of issues with it + // loop through each directory, add each file to array, return array + getAllInfo: function (dir, callback) { + var arr = []; - if (nth == 1) { - return firstIndex; - } else { - var stringAfterFirstOccurrence = - string.slice(lengthUpToFirstIndex), - nextOccurrence = - mpcp.utils.nthOccurrence( - stringAfterFirstOccurrence, char, nth - 1); - - if (nextOccurrence === -1) { - return -1; - } else { - return lengthUpToFirstIndex + nextOccurrence; - } - } - }, - - // return hours:minutes:seconds - getTime: function () { - var date = new Date(), - hours = date.getHours(), - minutes = date.getMinutes(), - seconds = date.getSeconds(); - - if (hours. toString().length == 1) hours = '0' + hours; - if (minutes.toString().length == 1) minutes = '0' + minutes; - if (seconds.toString().length == 1) seconds = '0' + seconds; - - return hours + ':' + minutes + ':' + seconds; - }, - - // generate a title for playlist and player - getSimpleTitle: function (title, artist, file, playlistVal) { - artist = (!artist ? 'unknown' : artist); - var song = artist + ' - ' + title, - fileStripped = mpcp.utils.stripSlash(file); - - // if the title doesn't exist, just return the file name - if (!playlistVal) { - return (!title ? fileStripped : song); - } else { - playlistVal = parseInt(playlistVal) + 1; - var songPlaylist = playlistVal + '. ' + song; - var fileStrippedPlaylist = playlistVal + '. ' + fileStripped; + // MESSAGE1: so there is a bug with lsinfo. + // sometimes the json is is oddly placed. http://i.imgur.com/Iankhha.png, + // notice the second json object, where part of the file is a key, and + // the key is undefined. So... A way to work around this bug is to check + // for an undefined value (because there should not be any), then run a + // find('file') because that works. + komponist.lsinfo(dir, function (err, files) { + if (err) { + console.log(err); + callback(arr); + } - return (!title ? fileStrippedPlaylist : songPlaylist); - } - }, - - // strip to the last slash - stripSlash: function (str) { - if (!str) return; - - var index = str.lastIndexOf('/'); - return str.substring(index+1); - }, - - // http://stackoverflow.com/a/9645447 - ignoreCase: function (a, b) { - return a.toLowerCase().localeCompare(b.toLowerCase()); - }, - - // http://stackoverflow.com/a/9716488 - isNumber: function (n) { - return !isNaN(parseFloat(n)) && isFinite(n); - }, - - // create the popup window for song information - parseSongInfo: function (err, values, callback) { - if (err || !values || Object.keys(values).length < 1) { - mpcp.lazyToast.warning('This is most likely a bug with MPCParty or the song is not in the live database.', 'Error getting song information.', 10000); - if (callback) callback(); - return console.log(err); - } + // make object to array (single items usually ... hopefully) + if (!Array.isArray(files)) + files = [files]; - //console.log(values); - $('#song-info .gen').remove(); - $('#song-info-modal h4')[0].innerHTML = ''; - $('#song-info-modal').modal('show'); + if (!files.length) callback(arr); - var title = mpcp.utils.getSimpleTitle(values.Title, values.Artist, values.file); - $('#song-info-modal h4')[0].innerHTML = title; + var j = 0; - if (values.Time) values.Time = mpcp.utils.toMMSS(values.Time); + function recurse(dir) { + mpcp.utils.getAllInfo(dir, function (newArr) { + arr = arr.concat(newArr); - // http://stackoverflow.com/a/31102605 - var html = ''; - Object.keys(values).sort(mpcp.utils.ignoreCase).forEach(function (key) { - html += '' + key + '' + values[key] + ''; + if (++j == files.length) { + callback(arr); + } }); - - $('#song-info tbody')[0].innerHTML = html; - - if (callback) callback(); - }, - - // replace listAllInfo because of issues with it - // loop through each directory, add each file to array, return array - getAllInfo: function (dir, callback) { - var arr = []; - - // MESSAGE1: so there is a bug with lsinfo. - // sometimes the json is is oddly placed. http://i.imgur.com/Iankhha.png, - // notice the second json object, where part of the file is a key, and - // the key is undefined. So... A way to work around this bug is to check - // for an undefined value (because there should not be any), then run a - // find('file') because that works. - komponist.lsinfo(dir, function (err, files) { - if (err) { - console.log(err); - callback(arr); - } - - // make object to array (single items usually ... hopefully) - if (!Array.isArray(files)) - files = [files]; - - if (!files.length) callback(arr); - - var j = 0; - - function recurse(dir) { - mpcp.utils.getAllInfo(dir, function (newArr) { - arr = arr.concat(newArr); + } + + // and this is the function to work around said bug... + function findFile(file, key) { + //console.log(files[i]); + // append a space + var file1 = file.file + ' ' + key; + // do not append a space + var file2 = file.file + key; + //console.log(file.file); + //console.log(file1); + //console.log(file2); + + // 1. find file as it normally would + komponist.find('file', file.file, function (findErr, value) { + //console.log(value); + + if (findErr || $.isEmptyObject(value[0])) { + // 2. find file with file1 + komponist.find('file', file1, function (findErr, value) { + //console.log(value); + + if (findErr || $.isEmptyObject(value[0])) { + // 3. find file with file2 + komponist.find('file', file2, + function (findErr, value) { + //console.log(value); + + if (findErr || $.isEmptyObject(value[0])) { + console.log( + 'no matches found, falling back'); + arr.push(file); if (++j == files.length) { - callback(arr); - } - }); - } - - // and this is the function to work around said bug... - function findFile(file, key) { - //console.log(files[i]); - // append a space - var file1 = file.file + ' ' + key; - // do not append a space - var file2 = file.file + key; - //console.log(file.file); - //console.log(file1); - //console.log(file2); - - // 1. find file as it normally would - komponist.find('file', file.file, function (findErr, value) { - //console.log(value); - - if (findErr || $.isEmptyObject(value[0])) { - // 2. find file with file1 - komponist.find('file', file1, function (findErr, value) { - //console.log(value); - - if (findErr || $.isEmptyObject(value[0])) { - // 3. find file with file2 - komponist.find('file', file2, - function (findErr, value) { - //console.log(value); - - if (findErr || $.isEmptyObject(value[0])) { - console.log( - 'no matches found, falling back'); - arr.push(file); - - if (++j == files.length) { - callback(arr); - } - } else { - console.log('found match 2!'); - //console.log(value[0]); - arr.push(value[0]); - - if (++j == files.length) { - callback(arr); - } - } - }); - } else { - console.log('found match 1!'); - //console.log(value[0]); - arr.push(value[0]); - - if (++j == files.length) { - callback(arr); - } - } - }); - } else { - console.log('found match 0!'); - arr.push(file); - - if (++j == files.length) { - callback(arr); - } + callback(arr); } - }); - } + } else { + console.log('found match 2!'); + //console.log(value[0]); + arr.push(value[0]); - //console.log(files); - for (var i = 0; i < files.length; ++i) { - if (files[i].directory && files[i].file) { - // ignore? if (++j == files.length) { - callback(arr); - } - } else if (files[i].directory) { - recurse(files[i].directory); - } else if (files[i].file) { - // check if the file contains an undefined value - var add = true; - for (var key in files[i]) { - if (!files[i].hasOwnProperty(key)) { - continue; - } - - if (files[i][key] === undefined) { - add = false; - findFile(files[i], key); - } + callback(arr); } + } + }); + } else { + console.log('found match 1!'); + //console.log(value[0]); + arr.push(value[0]); - // add file - if (add) { - arr.push(files[i]); - - if (++j == files.length) { - callback(arr); - } - } - } else { - // fallback (such as empty directories) - if (++j == files.length) { - callback(arr); - } + if (++j == files.length) { + callback(arr); } + } + }); + } else { + console.log('found match 0!'); + arr.push(file); + + if (++j == files.length) { + callback(arr); } + } }); - }, - - // update database statistics to the client - updateStats: function () { - komponist.stats(function (err, stats) { - if (err) return console.log(err); - - //console.log(stats); - var html = '' + stats.artists + ' artists, ' + stats.albums + ' albums, ' + stats.songs + ' songs (' + mpcp.utils.toFriendlyDDHHMM(stats.db_playtime) + '). Last database update: ' + new Date(stats.db_update * 1000).toLocaleString() + ''; - //console.log(html); - document.getElementById('stats').innerHTML = html; - }); - }, - - // sometimes when komponist returns a length 0 or 1 item, it returns an object - // instead of any array. This is used to fix that. (less need for jquery.each) - // also used to avoid issues when converting an array which is already an - // array) - toArray: function (obj) { - if (!Array.isArray(obj)) { - obj = [obj]; + } + + //console.log(files); + for (var i = 0; i < files.length; ++i) { + if (files[i].directory && files[i].file) { + // ignore? + if (++j == files.length) { + callback(arr); + } + } else if (files[i].directory) { + recurse(files[i].directory); + } else if (files[i].file) { + // check if the file contains an undefined value + var add = true; + for (var key in files[i]) { + if (!files[i].hasOwnProperty(key)) { + continue; + } - if (obj.length == 1 && $.isEmptyObject(obj[0])) - return []; + if (files[i][key] === undefined) { + add = false; + findFile(files[i], key); + } + } - return obj; - } else { - if (obj.length == 1 && $.isEmptyObject(obj[0])) - return []; + // add file + if (add) { + arr.push(files[i]); - return obj; - } - }, - - // select a table row and apply a custom style - // ele: the table to click the rows with - // scrollEle: element to scroll if out of view - rowSelect: (function (ele, style, scrollEle) { - var selected, - downCallback, - upCallback, - enterCallback, - clickCallback, - deleteCallback; - - function deselect() { - if (selected) { - $(selected).children().removeClass(style); - $(selected)[0].classList.remove('selected'); + if (++j == files.length) { + callback(arr); } + } + } else { + // fallback (such as empty directories) + if (++j == files.length) { + callback(arr); + } } + } + }); + }, - function select(obj) { - // fix hover issues - $(obj).children().addClass(style); - $(obj)[0].classList.add('selected'); - selected = obj; - } + // update database statistics to the client + updateStats: function () { + komponist.stats(function (err, stats) { + if (err) return console.log(err); - function checkScroll(top) { - if (!scrollEle) return; + //console.log(stats); + var html = '' + stats.artists + ' artists, ' + stats.albums + ' albums, ' + stats.songs + ' songs (' + mpcp.utils.toFriendlyDDHHMM(stats.db_playtime) + '). Last database update: ' + new Date(stats.db_update * 1000).toLocaleString() + ''; + //console.log(html); + document.getElementById('stats').innerHTML = html; + }); + }, - var middle = $(selected)[0].offsetTop - ($(scrollEle).height() / 2); - $(scrollEle)[0].scrollTo(0, middle); - } + // sometimes when komponist returns a length 0 or 1 item, it returns an object + // instead of any array. This is used to fix that. (less need for jquery.each) + // also used to avoid issues when converting an array which is already an + // array) + toArray: function (obj) { + if (!Array.isArray(obj)) { + obj = [obj]; - $(document).on('click', ele, function () { - deselect(); - select(this); + if (obj.length == 1 && $.isEmptyObject(obj[0])) + return []; - if (clickCallback) clickCallback(selected); - }); + return obj; + } else { + if (obj.length == 1 && $.isEmptyObject(obj[0])) + return []; + + return obj; + } + }, + + // select a table row and apply a custom style + // ele: the table to click the rows with + // scrollEle: element to scroll if out of view + rowSelect: (function (ele, style, scrollEle) { + var selected, + downCallback, + upCallback, + enterCallback, + clickCallback, + deleteCallback; + + function deselect() { + if (selected) { + $(selected).children().removeClass(style); + $(selected)[0].classList.remove('selected'); + } + } + + function select(obj) { + // fix hover issues + $(obj).children().addClass(style); + $(obj)[0].classList.add('selected'); + selected = obj; + } - $(document).on('keydown', selected, function (e) { - // we don't want to fire an event if it's not visible (these are - // so far only in modals) - // pretty sure there is a better way, but idk - if (!$(ele).is(':visible')) return; + function checkScroll(top) { + if (!scrollEle) return; - switch (e.keyCode) { - // enter key - case 13: - if (enterCallback) enterCallback(selected); - break; + var middle = $(selected)[0].offsetTop - ($(scrollEle).height() / 2); + $(scrollEle)[0].scrollTo(0, middle); + } - // up arrow - case 38: - if (!$(selected).prev().length) break; + $(document).on('click', ele, function () { + deselect(); + select(this); - deselect(); - select($(selected).prev()); - checkScroll(true); + if (clickCallback) clickCallback(selected); + }); - if (upCallback) upCallback(selected); + $(document).on('keydown', selected, function (e) { + // we don't want to fire an event if it's not visible (these are + // so far only in modals) + // pretty sure there is a better way, but idk + if (!$(ele).is(':visible')) return; - break; + switch (e.keyCode) { + // enter key + case 13: + if (enterCallback) enterCallback(selected); + break; - // down arrow - case 40: - if (!$(selected).next().length) break; + // up arrow + case 38: + if (!$(selected).prev().length) break; - deselect(); - select($(selected).next()); - checkScroll(false); + deselect(); + select($(selected).prev()); + checkScroll(true); - if (downCallback) downCallback(selected); + if (upCallback) upCallback(selected); - break; + break; - // delete - case 46: - if (deleteCallback) deleteCallback(selected); + // down arrow + case 40: + if (!$(selected).next().length) break; - break; - } - }); + deselect(); + select($(selected).next()); + checkScroll(false); - return { - getSelected: function () { - return selected; - }, - - deselect: function () { - deselect(); - selected = null; - }, - - on: function (event, callback) { - switch(event) { - case 'down': - downCallback = callback; - break; - case 'up': - upCallback = callback; - break; - case 'enter': - enterCallback = callback; - break; - case 'click': - clickCallback = callback; - break; - case 'delete': - deleteCallback = callback; - break; - } - } - }; - }), - - buttonSelect: function (ele, par) { - $(par).children().removeClass('active'); - $(ele)[0].classList.add('active'); - }, - - // creates a search input setup (input, search callback [returns searchVal], - // reset callback, input clear button, time it takes to search - createSearch: function (input, callSearch, callReset, inputClear, time) { - if (!time) time = 3000; - - function getSearchVal() { return $(input)[0].value.toLowerCase(); } - - // searching the database, instant searching is "slowed" - // for better client and server performance - var searchInterval; - var lastVal = ''; - $(input).focus(function () { - // makes the instant search not as instant (instead of - // relying on every keyUp) - searchInterval = setInterval(function () { - var searchVal = getSearchVal(); - //console.log('attempting searching for ' + searchVal); - if (searchVal && searchVal != lastVal) { - callSearch(searchVal); - lastVal = searchVal; - } else if (searchVal === '' && lastVal !== '') { - callReset(); - } - }, time); - }); + if (downCallback) downCallback(selected); - $(input).focusout(function () { - clearInterval(searchInterval); - var searchVal = getSearchVal(); - if (searchVal === '' && lastVal !== '') { - lastVal = ''; - callReset(); - } - }); + break; - $(inputClear).click(function () { - //console.log('clearing search'); - $(input)[0].value = ''; - $(input).focus(); - lastVal = ''; - callReset(); - - // not the best way of doing things... - if (mpcp.library.bringBack) { - mpcp.browser.hide(); - mpcp.library.show(); - mpcp.library.bringBack = false; - } - }); + // delete + case 46: + if (deleteCallback) deleteCallback(selected); - // detect enter key - $(input).keyup(function (e) { - if (e.keyCode == 13) { - var searchVal = getSearchVal(); - //console.log('attempting searching for ' + searchVal); - if (searchVal === '') { - callReset(); - } else { - callSearch(searchVal); - lastVal = searchVal; - } - } - }); - }, + break; + } + }); - // hides table rows as the user searches (input text box, table, data-*, - // clear input button) - lazySearch: function (input, table, data, inputClear, time) { - if (!time) time = 1000; + return { + getSelected: function () { + return selected; + }, + + deselect: function () { + deselect(); + selected = null; + }, + + on: function (event, callback) { + switch(event) { + case 'down': + downCallback = callback; + break; + case 'up': + upCallback = callback; + break; + case 'enter': + enterCallback = callback; + break; + case 'click': + clickCallback = callback; + break; + case 'delete': + deleteCallback = callback; + break; + } + } + }; + }), + + buttonSelect: function (ele, par) { + $(par).children().removeClass('active'); + $(ele)[0].classList.add('active'); + }, + + // creates a search input setup (input, search callback [returns searchVal], + // reset callback, input clear button, time it takes to search + createSearch: function (input, callSearch, callReset, inputClear, time) { + if (!time) time = 3000; + + function getSearchVal() { return $(input)[0].value.toLowerCase(); } + + // searching the database, instant searching is "slowed" + // for better client and server performance + var searchInterval; + var lastVal = ''; + $(input).focus(function () { + // makes the instant search not as instant (instead of + // relying on every keyUp) + searchInterval = setInterval(function () { + var searchVal = getSearchVal(); + //console.log('attempting searching for ' + searchVal); + if (searchVal && searchVal != lastVal) { + callSearch(searchVal); + lastVal = searchVal; + } else if (searchVal === '' && lastVal !== '') { + callReset(); + } + }, time); + }); - mpcp.utils.createSearch( - input, - function (search) { - var tr = $(table + ' .gen'); + $(input).focusout(function () { + clearInterval(searchInterval); + var searchVal = getSearchVal(); + if (searchVal === '' && lastVal !== '') { + lastVal = ''; + callReset(); + } + }); - for (var i = 0; i < tr.length; ++i) { - var str = String($(tr[i]).data()[data]).toLowerCase(); + $(inputClear).click(function () { + //console.log('clearing search'); + $(input)[0].value = ''; + $(input).focus(); + lastVal = ''; + callReset(); + + // not the best way of doing things... + if (mpcp.library.bringBack) { + mpcp.browser.hide(); + mpcp.library.show(); + mpcp.library.bringBack = false; + } + }); - if (~str.indexOf(search)) { - $(tr[i])[0].style.display = 'block'; - } else { - $(tr[i])[0].style.display = 'none'; - } - } - }, - function () { - $(table + ' .gen').show(); - }, - inputClear, - time - ); - }, - - // a specialized function to convert the multiselect object to an array - toArraySelected: function (obj) { - var arr = []; - for(var i = 0; i < obj.selected.length; ++i) { - arr.push(obj.selected[i]); + // detect enter key + $(input).keyup(function (e) { + if (e.keyCode == 13) { + var searchVal = getSearchVal(); + //console.log('attempting searching for ' + searchVal); + if (searchVal === '') { + callReset(); + } else { + callSearch(searchVal); + lastVal = searchVal; } - obj.selected = arr; - }, - - // table sorting - tableSort: function (table, thead, index, format) { - var sortOrder = 'asc'; - $(document).on('click', thead, function () { - if (sortOrder == 'asc') { - $(table).sortColumn({ - index: index, order: 'desc', format: format - }); - sortOrder = 'desc'; - } else { - $(table).sortColumn({ - index: index, order: 'asc', format: format - }); - sortOrder = 'asc'; - } + } + }); + }, + + // hides table rows as the user searches (input text box, table, data-*, + // clear input button) + lazySearch: function (input, table, data, inputClear, time) { + if (!time) time = 1000; + + mpcp.utils.createSearch( + input, + function (search) { + var tr = $(table + ' .gen'); + + for (var i = 0; i < tr.length; ++i) { + var str = String($(tr[i]).data()[data]).toLowerCase(); + + if (~str.indexOf(search)) { + $(tr[i])[0].style.display = 'block'; + } else { + $(tr[i])[0].style.display = 'none'; + } + } + }, + function () { + $(table + ' .gen').show(); + }, + inputClear, + time + ); + }, + + // a specialized function to convert the multiselect object to an array + toArraySelected: function (obj) { + var arr = []; + for(var i = 0; i < obj.selected.length; ++i) { + arr.push(obj.selected[i]); + } + obj.selected = arr; + }, + + // table sorting + tableSort: function (table, thead, index, format) { + var sortOrder = 'asc'; + $(document).on('click', thead, function () { + if (sortOrder == 'asc') { + $(table).sortColumn({ + index: index, order: 'desc', format: format }); - }, - - // MultiSelect. Multiselections handled by respective object. - // Events handled in callback and contextMenu. - // table for multislection, obj used for multiselection (must contain selected - // variable. Because it needs to pass by reference to selected. - multiSelect: function (obj, cancel, exclude, deselect) { - var disable = ['tbody']; - - if (exclude) disable = disable.concat(exclude); - - if (deselect === undefined) deselect = true; - - $(obj.table).multiSelect({ - actcls: 'info', - selector: 'tr.gen', - except: disable, - deselect: deselect, - cancel: cancel, - statics: [':hidden'], - callback: function (items, e) { - if (!items.length) return; - obj.selected = items; - } + sortOrder = 'desc'; + } else { + $(table).sortColumn({ + index: index, order: 'asc', format: format }); - }, - - // save the list temporarily - saveSelected: function (obj) { - //console.log('saving list temp: ' + this.selected.length); - obj.saved = obj.selected; - }, - - // restore the list - restoreSelected: function (obj) { - //console.log('restore list temp: ' + this.saved.length); - obj.selected = obj.saved; - obj.saved = []; - }, - - clearSelected: function (obj) { - console.log('clearing selected: ' + obj.table); - - for (var i = 0; i < obj.selected.length; ++i) { - //console.log(obj.selected[i]); - $(obj.selected[i])[0].classList.remove('info'); - } + sortOrder = 'asc'; + } + }); + }, + + // MultiSelect. Multiselections handled by respective object. + // Events handled in callback and contextMenu. + // table for multislection, obj used for multiselection (must contain selected + // variable. Because it needs to pass by reference to selected. + multiSelect: function (obj, cancel, exclude, deselect) { + var disable = ['tbody']; + + if (exclude) disable = disable.concat(exclude); + + if (deselect === undefined) deselect = true; + + $(obj.table).multiSelect({ + actcls: 'info', + selector: 'tr.gen', + except: disable, + deselect: deselect, + cancel: cancel, + statics: [':hidden'], + callback: function (items, e) { + if (!items.length) return; + obj.selected = items; + } + }); + }, + + // save the list temporarily + saveSelected: function (obj) { + //console.log('saving list temp: ' + this.selected.length); + obj.saved = obj.selected; + }, + + // restore the list + restoreSelected: function (obj) { + //console.log('restore list temp: ' + this.saved.length); + obj.selected = obj.saved; + obj.saved = []; + }, + + clearSelected: function (obj) { + console.log('clearing selected: ' + obj.table); + + for (var i = 0; i < obj.selected.length; ++i) { + //console.log(obj.selected[i]); + $(obj.selected[i])[0].classList.remove('info'); + } - obj.selected = []; - }, - - // check if selection is actually selected (right click non selected item - // will clear the selection) - checkSelected: function(el, obj, save) { - var inside = false; - for (var i = 0; i < obj.selected.length; ++i) { - if (obj.selected[i].isEqualNode(el)) { - inside = true; - break; - } - } + obj.selected = []; + }, + + // check if selection is actually selected (right click non selected item + // will clear the selection) + checkSelected: function(el, obj, save) { + var inside = false; + for (var i = 0; i < obj.selected.length; ++i) { + if (obj.selected[i].isEqualNode(el)) { + inside = true; + break; + } + } - //console.log(el); - //console.log(obj.tableid); - //console.log(inside); + //console.log(el); + //console.log(obj.tableid); + //console.log(inside); - // if el is not in obj.selected, clear it - if (!inside) { - if (save) { - mpcp.utils.saveSelected(obj); - } + // if el is not in obj.selected, clear it + if (!inside) { + if (save) { + mpcp.utils.saveSelected(obj); + } - mpcp.utils.clearSelected(obj); - } - }, + mpcp.utils.clearSelected(obj); + } + }, - setCurrentAlbumArt: function (url) { - //console.log(url); + setCurrentAlbumArt: function (url) { + //console.log(url); - if (!url) { - $('#album-art').hide().attr('src', ''); - } else { - $('#album-art').show().attr('src', url); - } - }, - - // remove duplicates for song object arrays - concatDedupe: function (arr1, arr2) { - var all = arr1.concat(arr2); - - // remove duplicates from the array by utilizing an object - // this should be roughly O(n); - var duplicates = {}; - return all.filter(function (ele) { - if (!duplicates[ele.file]) { - duplicates[ele.file] = true; - return true; - } - }); + if (!url) { + $('#album-art').hide().attr('src', ''); + } else { + $('#album-art').show().attr('src', url); } + }, + + // remove duplicates for song object arrays + concatDedupe: function (arr1, arr2) { + var all = arr1.concat(arr2); + + // remove duplicates from the array by utilizing an object + // this should be roughly O(n); + var duplicates = {}; + return all.filter(function (ele) { + if (!duplicates[ele.file]) { + duplicates[ele.file] = true; + return true; + } + }); + } }; }; diff --git a/src/vote.js b/src/vote.js index 1551e6e..27e0dd9 100644 --- a/src/vote.js +++ b/src/vote.js @@ -2,23 +2,23 @@ module.exports = function (mpcp) { // vote to skip (most of it is server side) return { - // gets from socket connection - received: false, - enabled: false, - needed: 1, + // gets from socket connection + received: false, + enabled: false, + needed: 1, - // send a message to the client (using setTitle as the message) - message: function (current, id) { - mpcp.lazyToast.info(setTitles(current, id), 'Song Skip'); - }, + // send a message to the client (using setTitle as the message) + message: function (current, id) { + mpcp.lazyToast.info(setTitles(current, id), 'Song Skip'); + }, - // create a title for the next and previous buttons - setTitles: function (current, id) { - var msg = 'Skip to ' + id + ' song: ' + current + ' / ' + mpcp.vote.needed + - ' from ' + mpcp.users.total + ' clients.'; - $('#' + id).attr('title', msg); - return msg; - } + // create a title for the next and previous buttons + setTitles: function (current, id) { + var msg = 'Skip to ' + id + ' song: ' + current + ' / ' + mpcp.vote.needed + + ' from ' + mpcp.users.total + ' clients.'; + $('#' + id).attr('title', msg); + return msg; + } }; }; diff --git a/tests/browser.js b/tests/browser.js index 4a767de..1684222 100644 --- a/tests/browser.js +++ b/tests/browser.js @@ -1,72 +1,72 @@ module.exports = function (utils) { QUnit.test('browser', function (assert) { - var children = $(utils.fb).children('.gen'); - assert.ok(children.length > 1, 'check if anything is in the file browser'); - // use contains because of my history additions where clicking back and forth between library and browser will bring you back to the original hit - assert.ok(~document.location.pathname.indexOf('/browser/'), 'check if url contains /browser/'); + var children = $(utils.fb).children('.gen'); + assert.ok(children.length > 1, 'check if anything is in the file browser'); + // use contains because of my history additions where clicking back and forth between library and browser will bring you back to the original hit + assert.ok(~document.location.pathname.indexOf('/browser/'), 'check if url contains /browser/'); }); QUnit.test('search from browser', function (assert) { - var done = assert.async(); - var children = $(utils.fb).children('.gen'); - var keyword = 'love'; + var done = assert.async(); + var children = $(utils.fb).children('.gen'); + var keyword = 'love'; - mpcp.browser.search(keyword, null, function () { - var childrenSearch = $(utils.fb).children('.gen'); - assert.ok(childrenSearch.length > 1, 'check if anything is in the file browser'); - assert.ok(children.length != childrenSearch.length, 'check if different than root'); - assert.equal(document.location.pathname, '/search/' + encodeURIComponent(keyword), 'check if url is encoded the same as the search name'); + mpcp.browser.search(keyword, null, function () { + var childrenSearch = $(utils.fb).children('.gen'); + assert.ok(childrenSearch.length > 1, 'check if anything is in the file browser'); + assert.ok(children.length != childrenSearch.length, 'check if different than root'); + assert.equal(document.location.pathname, '/search/' + encodeURIComponent(keyword), 'check if url is encoded the same as the search name'); - mpcp.browser.resetSearch(function () { - var childrenNow = $(utils.fb).children('.gen'); - utils.offWorkaround(assert, children, childrenNow); - done(); - }); + mpcp.browser.resetSearch(function () { + var childrenNow = $(utils.fb).children('.gen'); + utils.offWorkaround(assert, children, childrenNow); + done(); }); + }); }); QUnit.test('search from browser nothing', function (assert) { - var done = assert.async(); - var children = $(utils.fb).children('.gen'); - $('#search-browser').focus(); - var keyword = 'thisshouldnotbeasongnamelikesrsly'; - $('#search-browser').val(keyword); + var done = assert.async(); + var children = $(utils.fb).children('.gen'); + $('#search-browser').focus(); + var keyword = 'thisshouldnotbeasongnamelikesrsly'; + $('#search-browser').val(keyword); - // can't get enter to work, so waiting several seconds for auto search to kick in - mpcp.browser.search(keyword, null, function () { - var childrenSearch = $(utils.fb).children('.gen'); - assert.equal(childrenSearch.length, 1, 'check if one item is in the file browser'); - assert.notOk(~document.location.pathname.indexOf(encodeURIComponent(keyword)), 'check if url is not the search name'); - $('#search-clear').click(); - assert.ok($('#search-browser').val() != keyword, 'check if clear works'); + // can't get enter to work, so waiting several seconds for auto search to kick in + mpcp.browser.search(keyword, null, function () { + var childrenSearch = $(utils.fb).children('.gen'); + assert.equal(childrenSearch.length, 1, 'check if one item is in the file browser'); + assert.notOk(~document.location.pathname.indexOf(encodeURIComponent(keyword)), 'check if url is not the search name'); + $('#search-clear').click(); + assert.ok($('#search-browser').val() != keyword, 'check if clear works'); - mpcp.browser.resetSearch(function () { - var childrenNow = $(utils.fb).children('.gen'); - utils.offWorkaround(assert, children, childrenNow); - done(); - }); + mpcp.browser.resetSearch(function () { + var childrenNow = $(utils.fb).children('.gen'); + utils.offWorkaround(assert, children, childrenNow); + done(); }); + }); }); QUnit.test('browser folder open + back', function (assert) { - var done = assert.async(); + var done = assert.async(); - utils.openFolder(assert, null, function () { - done(); - }); + utils.openFolder(assert, null, function () { + done(); + }); }); QUnit.test('browser song info', function (assert) { - var done = assert.async(); - var file = $($(utils.fb).find('.file')[0]).data().fileid; + var done = assert.async(); + var file = $($(utils.fb).find('.file')[0]).data().fileid; - mpcp.browser.getSongInfo(file, function () { - var children = $('#song-info tbody').children('.gen'); - assert.ok($(children).length > 1, 'check if at least 2 items are in song info'); - $('#song-info-modal').modal('hide'); - done(); - }); + mpcp.browser.getSongInfo(file, function () { + var children = $('#song-info tbody').children('.gen'); + assert.ok($(children).length > 1, 'check if at least 2 items are in song info'); + $('#song-info-modal').modal('hide'); + done(); + }); }); }; diff --git a/tests/library.js b/tests/library.js index a3103db..2f398a8 100644 --- a/tests/library.js +++ b/tests/library.js @@ -1,59 +1,59 @@ module.exports = function (utils) { QUnit.test('library', function (assert) { - var done = assert.async(); - utils.openLibrary(assert, function () { - utils.closeLibrary(assert, function () { - done(); - }); + var done = assert.async(); + utils.openLibrary(assert, function () { + utils.closeLibrary(assert, function () { + done(); }); + }); }); QUnit.test('library albums + all', function (assert) { - var done = assert.async(); - utils.openLibrary(assert, function () { - var childrenArtists = $(utils.libArt).children(); - $(childrenArtists[0]).click(); - var artist = $(childrenArtists[0]).data().artist; - - mpcp.libraryAlbums.update(artist, null, false, function () { - assert.ok(~document.location.pathname.indexOf('/library/' + encodeURIComponent(artist)), 'check if url contains /library/artist'); - var children = $(utils.libAlb).children(); - assert.ok(children.length > 1, 'more than All is shown'); - assert.ok($(children[0]).hasClass('library-artist-all'), 'check if All is shown as an album'); - var childrenSongs = $(utils.libAlb).children(); - assert.ok(childrenSongs.length > 1, 'check if all songs shown'); - - utils.closeLibrary(assert, function () { - done(); - }); - }); + var done = assert.async(); + utils.openLibrary(assert, function () { + var childrenArtists = $(utils.libArt).children(); + $(childrenArtists[0]).click(); + var artist = $(childrenArtists[0]).data().artist; + + mpcp.libraryAlbums.update(artist, null, false, function () { + assert.ok(~document.location.pathname.indexOf('/library/' + encodeURIComponent(artist)), 'check if url contains /library/artist'); + var children = $(utils.libAlb).children(); + assert.ok(children.length > 1, 'more than All is shown'); + assert.ok($(children[0]).hasClass('library-artist-all'), 'check if All is shown as an album'); + var childrenSongs = $(utils.libAlb).children(); + assert.ok(childrenSongs.length > 1, 'check if all songs shown'); + + utils.closeLibrary(assert, function () { + done(); + }); }); + }); }); QUnit.test('library artist search', function (assert) { - var done = assert.async(); - utils.openLibrary(assert, function (children) { - $('#search-artists').focus(); - var keyword = 'the'; - $('#search-artists').val(keyword); - - setTimeout(function () { - var childrenSearch = $(utils.libArt).children('.gen:visible'); - assert.ok(childrenSearch.length > 1, 'check if anything is in the artists list'); - assert.ok(children.length != childrenSearch.length, 'check if different than root'); - - $('#search-artists-clear').click(); - assert.ok($('#search-artists').val() != 'the', 'check if clear works'); - - var childrenNow = $(utils.libArt).children('.gen:visible'); - assert.equal(childrenNow.length, children.length, 'check if same as original'); - - utils.closeLibrary(assert, function () { - done(); - }); - }, 3000); - }); + var done = assert.async(); + utils.openLibrary(assert, function (children) { + $('#search-artists').focus(); + var keyword = 'the'; + $('#search-artists').val(keyword); + + setTimeout(function () { + var childrenSearch = $(utils.libArt).children('.gen:visible'); + assert.ok(childrenSearch.length > 1, 'check if anything is in the artists list'); + assert.ok(children.length != childrenSearch.length, 'check if different than root'); + + $('#search-artists-clear').click(); + assert.ok($('#search-artists').val() != 'the', 'check if clear works'); + + var childrenNow = $(utils.libArt).children('.gen:visible'); + assert.equal(childrenNow.length, children.length, 'check if same as original'); + + utils.closeLibrary(assert, function () { + done(); + }); + }, 3000); + }); }); // TODO test albums search diff --git a/tests/main.js b/tests/main.js index 32eb45a..1479f28 100644 --- a/tests/main.js +++ b/tests/main.js @@ -9,44 +9,44 @@ QUnit.config.autostart = false; $(function () { - $('#start-testing').click(function() { - // set a default state before starting - $('#open-file-browser').click(); - $('#home').click(); - $('#clear-playlist').click(); + $('#start-testing').click(function() { + // set a default state before starting + $('#open-file-browser').click(); + $('#home').click(); + $('#clear-playlist').click(); - if ($('#use-skip-to-remove').is(':checked')) { - $('#use-skip-to-remove').click(); - } + if ($('#use-skip-to-remove').is(':checked')) { + $('#use-skip-to-remove').click(); + } - if (!$('#use-unknown').is(':checked')) { - $('#use-unknown').click(); - } + if (!$('#use-unknown').is(':checked')) { + $('#use-unknown').click(); + } - if ($('#use-pages-browser').is(':checked')) { - $('#use-pages-browser').click(); - } + if ($('#use-pages-browser').is(':checked')) { + $('#use-pages-browser').click(); + } - if ($('#items-max-playlist').val() != 200) { - mpcp.settings.saveItemsMax('browser', 200); - } + if ($('#items-max-playlist').val() != 200) { + mpcp.settings.saveItemsMax('browser', 200); + } - if (!$('#use-pages-playlist').is(':checked')) { - $('#use-pages-playlist').click(); - } + if (!$('#use-pages-playlist').is(':checked')) { + $('#use-pages-playlist').click(); + } - if ($('#items-max-playlist').val() != 200) { - mpcp.settings.saveItemsMax('playlist', 200); - } + if ($('#items-max-playlist').val() != 200) { + mpcp.settings.saveItemsMax('playlist', 200); + } - if ($('#random').hasClass('active')) { - $('#random').click(); - } + if ($('#random').hasClass('active')) { + $('#random').click(); + } - setTimeout(function () { - QUnit.start(); - }, 1000); - }); + setTimeout(function () { + QUnit.start(); + }, 1000); + }); }); var utils = {}; diff --git a/tests/playlist.js b/tests/playlist.js index c0673cc..8aed0d4 100644 --- a/tests/playlist.js +++ b/tests/playlist.js @@ -2,213 +2,213 @@ module.exports = function (utils) { // player / playlist QUnit.test('pl + player: add dir + play + go to current + clear', function (assert) { - var done = assert.async(); + var done = assert.async(); - utils.addDirToPl(assert, function () { - utils.play(assert, function () { - currentPos = parseInt($('#title-pos').text()); + utils.addDirToPl(assert, function () { + utils.play(assert, function () { + currentPos = parseInt($('#title-pos').text()); - // we go to current in case it's on a different page - mpcp.playlist.goToCurrent(function () { - var songPos = parseInt($(utils.pl + ' .bg-success').text()); - assert.equal(songPos, currentPos, 'go to current check'); + // we go to current in case it's on a different page + mpcp.playlist.goToCurrent(function () { + var songPos = parseInt($(utils.pl + ' .bg-success').text()); + assert.equal(songPos, currentPos, 'go to current check'); - utils.clearPlaylist(assert, function () { - done(); - }); - }); + utils.clearPlaylist(assert, function () { + done(); }); + }); }); + }); }); QUnit.test('pl + player: add songs + play + pause + pause + pause + clear ', function (assert) { - var done = assert.async(); - - utils.addSong(2, true, function () { - var children = $(utils.pl).children('.gen'); - assert.equal($(children).length, 2, 'check if 2 songs added'); - - utils.play(assert, function () { - utils.pause(assert, function () { - utils.pause(assert, function () { - utils.pause(assert, function () { - var childrenNow = $(utils.pl).children('.gen'); - assert.equal($(childrenNow).length, 2, 'check if 2 songs left'); - - utils.clearPlaylist(assert, function () { - done(); - }); - }); - }); + var done = assert.async(); + + utils.addSong(2, true, function () { + var children = $(utils.pl).children('.gen'); + assert.equal($(children).length, 2, 'check if 2 songs added'); + + utils.play(assert, function () { + utils.pause(assert, function () { + utils.pause(assert, function () { + utils.pause(assert, function () { + var childrenNow = $(utils.pl).children('.gen'); + assert.equal($(childrenNow).length, 2, 'check if 2 songs left'); + + utils.clearPlaylist(assert, function () { + done(); }); + }); }); + }); }); + }); }); QUnit.test('pl + player: add songs + play + next + previous + clear ', function (assert) { - var done = assert.async(); - utils.addSong(2, true, function () { - var children = $(utils.pl).children('.gen'); - assert.equal($(children).length, 2, 'check if 2 songs added'); - - utils.play(assert, function () { - utils.next(assert, function () { - // .status is getting called during this timeout which calls the previous()'s callback to early - setTimeout(function () { - - utils.previous(assert, function () { - var childrenNow = $(utils.pl).children('.gen'); - assert.equal($(childrenNow).length, 2, 'check if 2 songs left'); - - utils.clearPlaylist(assert, function () { - done(); - }); - }); + var done = assert.async(); + utils.addSong(2, true, function () { + var children = $(utils.pl).children('.gen'); + assert.equal($(children).length, 2, 'check if 2 songs added'); - }, 500); - }); + utils.play(assert, function () { + utils.next(assert, function () { + // .status is getting called during this timeout which calls the previous()'s callback to early + setTimeout(function () { + + utils.previous(assert, function () { + var childrenNow = $(utils.pl).children('.gen'); + assert.equal($(childrenNow).length, 2, 'check if 2 songs left'); + + utils.clearPlaylist(assert, function () { + done(); + }); }); + + }, 500); + }); }); + }); }); // playlist QUnit.test('add dir to playlist + clear', function (assert) { - var done = assert.async(); + var done = assert.async(); - utils.addDirToPl(assert, function () { - utils.clearPlaylist(assert, function () { - done(); - }); + utils.addDirToPl(assert, function () { + utils.clearPlaylist(assert, function () { + done(); }); + }); }); QUnit.test('pl scramble check', function (assert) { - var done = assert.async(); - utils.addSong(5, true, function () { - var children = $(utils.pl).children('.gen'); - assert.equal($(children).length, 5, 'check if 5 songs added'); - - console.log('pl test before scramble'); - mpcp.playlist.scramble(function () { - console.log('pl test after scramble'); - var childrenNow = $(utils.pl).children('.gen'); - assert.ok($(children[0]).text() != $(childrenNow[0]).text() || $(children[1]).text() != $(childrenNow[1]).text() || $(children[2]).text() != $(childrenNow[2]).text(), 'check if scramble works'); - - console.log('pl test before clear'); - utils.clearPlaylist(assert, function () { - console.log('pl test after clear'); - done(); - }); - }); + var done = assert.async(); + utils.addSong(5, true, function () { + var children = $(utils.pl).children('.gen'); + assert.equal($(children).length, 5, 'check if 5 songs added'); + + console.log('pl test before scramble'); + mpcp.playlist.scramble(function () { + console.log('pl test after scramble'); + var childrenNow = $(utils.pl).children('.gen'); + assert.ok($(children[0]).text() != $(childrenNow[0]).text() || $(children[1]).text() != $(childrenNow[1]).text() || $(children[2]).text() != $(childrenNow[2]).text(), 'check if scramble works'); + + console.log('pl test before clear'); + utils.clearPlaylist(assert, function () { + console.log('pl test after clear'); + done(); + }); }); + }); }); QUnit.test('pl duplicate check', function (assert) { - var done = assert.async(); - utils.addSong(2, true, function () { - utils.addSong(5, false, function () { - var children = $(utils.pl).children('.gen'); - assert.equal($(children).length, 7, 'check if 8 songs added'); + var done = assert.async(); + utils.addSong(2, true, function () { + utils.addSong(5, false, function () { + var children = $(utils.pl).children('.gen'); + assert.equal($(children).length, 7, 'check if 8 songs added'); - mpcp.playlist.removeDuplicates(function () { - var childrenNow = $(utils.pl).children('.gen'); - assert.equal($(childrenNow).length, 2, 'check if 2 songs left'); + mpcp.playlist.removeDuplicates(function () { + var childrenNow = $(utils.pl).children('.gen'); + assert.equal($(childrenNow).length, 2, 'check if 2 songs left'); - utils.clearPlaylist(assert, function () { - done(); - }); - }); + utils.clearPlaylist(assert, function () { + done(); }); + }); }); + }); }); QUnit.test('browser to playlist: folder + save + clear + open + delete + clear', function (assert) { - var done = assert.async(); - - utils.addDirToPl(assert, function (childrenBefore) { - // save the playlist - mpcp.stored.updatePlaylists(utils.plSaveModal, null, function () { - $('#playlist-save-input').val(utils.plSave); - - mpcp.playlist.saveFromStored(function () { - assert.equal($('#playlist-title').text(), utils.plSave, 'check if title is utils.plSave'); - - // clear the playlist - utils.clearPlaylist(assert, function () { - // open the playlist - mpcp.stored.updatePlaylists(utils.plOpenModal, null, function () { - var ele = utils.plOpenModal + ' tr:contains("' + utils.plSave + '")'; - assert.ok($(ele).length, 'check if pl is visible'); - $(ele).click(); - - mpcp.playlist.openFromStored(function () { - children = $(utils.pl).children('.gen'); - assert.equal(children.length, childrenBefore.length, 'check if same playlist'); - - // open the playlist - mpcp.stored.updatePlaylists(utils.plOpenModal, null, function () { - var ele = utils.plOpenModal + ' tr:contains("' + utils.plSave + '")'; - assert.ok($(ele).length, 'check if pl is visible'); - var file = $(ele).data().fileid; - - // delete the playlist - mpcp.stored.removePlaylist(file, ele, function () { - assert.notOk($(ele).is(':visible'), 'check if pl is not visible'); - - // clear the playlist - utils.clearPlaylist(assert, function () { - done(); - }); - }); - }); - }); - }); + var done = assert.async(); + + utils.addDirToPl(assert, function (childrenBefore) { + // save the playlist + mpcp.stored.updatePlaylists(utils.plSaveModal, null, function () { + $('#playlist-save-input').val(utils.plSave); + + mpcp.playlist.saveFromStored(function () { + assert.equal($('#playlist-title').text(), utils.plSave, 'check if title is utils.plSave'); + + // clear the playlist + utils.clearPlaylist(assert, function () { + // open the playlist + mpcp.stored.updatePlaylists(utils.plOpenModal, null, function () { + var ele = utils.plOpenModal + ' tr:contains("' + utils.plSave + '")'; + assert.ok($(ele).length, 'check if pl is visible'); + $(ele).click(); + + mpcp.playlist.openFromStored(function () { + children = $(utils.pl).children('.gen'); + assert.equal(children.length, childrenBefore.length, 'check if same playlist'); + + // open the playlist + mpcp.stored.updatePlaylists(utils.plOpenModal, null, function () { + var ele = utils.plOpenModal + ' tr:contains("' + utils.plSave + '")'; + assert.ok($(ele).length, 'check if pl is visible'); + var file = $(ele).data().fileid; + + // delete the playlist + mpcp.stored.removePlaylist(file, ele, function () { + assert.notOk($(ele).is(':visible'), 'check if pl is not visible'); + + // clear the playlist + utils.clearPlaylist(assert, function () { + done(); + }); }); + }); }); + }); }); + }); }); + }); }); QUnit.test('add all + search', function (assert) { - var done = assert.async(); + var done = assert.async(); - mpcp.browser.addAll(function () { - var pages = $('#playlist-pages .total-pages').text(); + mpcp.browser.addAll(function () { + var pages = $('#playlist-pages .total-pages').text(); - mpcp.playlist.search('love', function () { - var pagesNow = $('#playlist-pages .total-pages').text(); - assert.ok(pages != pagesNow, 'check if pages are different'); - var childrenNow = $(utils.pl).children('.gen'); - assert.ok($(childrenNow).length > 1, 'check if at least 2 songs are in pl'); + mpcp.playlist.search('love', function () { + var pagesNow = $('#playlist-pages .total-pages').text(); + assert.ok(pages != pagesNow, 'check if pages are different'); + var childrenNow = $(utils.pl).children('.gen'); + assert.ok($(childrenNow).length > 1, 'check if at least 2 songs are in pl'); - mpcp.playlist.resetSearch(function () { - var pagesNow = $('#playlist-pages .total-pages').text(); - assert.ok(pages == pagesNow, 'check if pages are the same again'); + mpcp.playlist.resetSearch(function () { + var pagesNow = $('#playlist-pages .total-pages').text(); + assert.ok(pages == pagesNow, 'check if pages are the same again'); - utils.clearPlaylist(assert, function () { - done(); - }); - }); + utils.clearPlaylist(assert, function () { + done(); }); + }); }); + }); }); QUnit.test('pl song info', function (assert) { - var done = assert.async(); + var done = assert.async(); - utils.addDirToPl(assert, function () { - var file = $($(utils.pl).find('.gen')[0]).data().file; + utils.addDirToPl(assert, function () { + var file = $($(utils.pl).find('.gen')[0]).data().file; - mpcp.playlist.getSongInfo(file, function () { - var children = $('#song-info tbody').children('.gen'); - assert.ok($(children).length > 1, 'check if at least 2 items are in song info'); - $('#song-info-modal').modal('hide'); + mpcp.playlist.getSongInfo(file, function () { + var children = $('#song-info tbody').children('.gen'); + assert.ok($(children).length > 1, 'check if at least 2 items are in song info'); + $('#song-info-modal').modal('hide'); - utils.clearPlaylist(assert, function () { - done(); - }); - }); + utils.clearPlaylist(assert, function () { + done(); + }); }); + }); }); }; diff --git a/tests/playlisteditor.js b/tests/playlisteditor.js index 84d4ab0..4eed3f7 100644 --- a/tests/playlisteditor.js +++ b/tests/playlisteditor.js @@ -4,301 +4,301 @@ module.exports = function (utils) { var keyword = 'e'; QUnit.test('open and close pe', function (assert) { - utils.openpe(assert); - utils.closepe(assert); + utils.openpe(assert); + utils.closepe(assert); }); // contextmenu plugin triggers dont work (tried mouseenter, then enter key also) //QUnit.test('song-information', function (assert) { -// var fileOpen = $(utils.fb).find('.file')[0]; -// $(fileOpen).trigger('contextmenu'); -// var infoOpen = $('.context-menu-item:contains("Song information")'); -// $(infoOpen).click(); +// var fileOpen = $(utils.fb).find('.file')[0]; +// $(fileOpen).trigger('contextmenu'); +// var infoOpen = $('.context-menu-item:contains("Song information")'); +// $(infoOpen).click(); //}); QUnit.test('open and minimize pe', function (assert) { - utils.openpe(assert); - utils.minimizepe(assert); - utils.resumepe(assert); - utils.closepe(assert); + utils.openpe(assert); + utils.minimizepe(assert); + utils.resumepe(assert); + utils.closepe(assert); }); QUnit.test('browser to pe: folder', function (assert) { - utils.openpe(assert); - var done = assert.async(); + utils.openpe(assert); + var done = assert.async(); - utils.addDirTope(assert, function () { - utils.closepe(assert); - done(); - }); + utils.addDirTope(assert, function () { + utils.closepe(assert); + done(); + }); }); QUnit.test('browser to pe: folder + close', function (assert) { - utils.openpe(assert); - var done = assert.async(); + utils.openpe(assert); + var done = assert.async(); - utils.addDirTope(assert, function () { - utils.closepe(assert); - utils.openpe(assert); - var children = $(utils.pe).children('.gen').not('.rem'); - assert.equal(children.length, 0, 'check if pe is empty'); - utils.closepe(assert); - done(); - }); + utils.addDirTope(assert, function () { + utils.closepe(assert); + utils.openpe(assert); + var children = $(utils.pe).children('.gen').not('.rem'); + assert.equal(children.length, 0, 'check if pe is empty'); + utils.closepe(assert); + done(); + }); }); QUnit.test('browser to pe: folder + minimze', function (assert) { - utils.openpe(assert); - var done = assert.async(); + utils.openpe(assert); + var done = assert.async(); - utils.addDirTope(assert, function (childrenBefore) { - utils.minimizepe(assert); - utils.resumepe(assert); - var children = $(utils.pe).children('.gen'); - assert.equal(children.length, childrenBefore.length, 'check if pe is the same'); - utils.closepe(assert); - done(); - }); + utils.addDirTope(assert, function (childrenBefore) { + utils.minimizepe(assert); + utils.resumepe(assert); + var children = $(utils.pe).children('.gen'); + assert.equal(children.length, childrenBefore.length, 'check if pe is the same'); + utils.closepe(assert); + done(); + }); }); QUnit.test('browser to pe: folder + remove', function (assert) { - utils.openpe(assert); - var done = assert.async(); + utils.openpe(assert); + var done = assert.async(); - utils.addDirTope(assert, function (children) { - var songRemove = $(utils.pe).find('.pe-song-remove')[0]; - $(songRemove).click(); + utils.addDirTope(assert, function (children) { + var songRemove = $(utils.pe).find('.pe-song-remove')[0]; + $(songRemove).click(); - var childrenRem = $(utils.pe).children('.gen'); - assert.ok(childrenRem.length < children.length, 'check if removed a song'); + var childrenRem = $(utils.pe).children('.gen'); + assert.ok(childrenRem.length < children.length, 'check if removed a song'); - utils.closepe(assert); - done(); - }); + utils.closepe(assert); + done(); + }); }); QUnit.test('browser to pe: folder + clear', function (assert) { - utils.openpe(assert); - var done = assert.async(); + utils.openpe(assert); + var done = assert.async(); - utils.addDirTope(assert, function (children) { - utils.clearpe(assert); - utils.closepe(assert); - done(); - }); + utils.addDirTope(assert, function (children) { + utils.clearpe(assert); + utils.closepe(assert); + done(); + }); }); QUnit.test('browser to pe: file + remove', function (assert) { - utils.openpe(assert); - var done = assert.async(); + utils.openpe(assert); + var done = assert.async(); - utils.addSongTope(assert, function (children) { - var songRemove = $(utils.pe).find('.pe-song-remove')[0]; - $(songRemove).click(); + utils.addSongTope(assert, function (children) { + var songRemove = $(utils.pe).find('.pe-song-remove')[0]; + $(songRemove).click(); - var childrenRem = $(utils.pe).children('.gen'); - assert.ok(childrenRem.length < children.length, 'check if removed a song'); + var childrenRem = $(utils.pe).children('.gen'); + assert.ok(childrenRem.length < children.length, 'check if removed a song'); - utils.closepe(assert); - done(); - }); + utils.closepe(assert); + done(); + }); }); QUnit.test('browser to pe: add all', function (assert) { - utils.openpe(assert); - var done = assert.async(); - - utils.openFolder(assert, function (finish, childrenFb) { - // execute - mpcp.browser.addAll(function() { - var childrenpe = $(utils.pe).children('.gen'); - assert.equal(childrenpe.length, childrenFb.length, 'check if fb is same as pe'); - finish(); - }); - }, - function () { - // callback - utils.closepe(assert); - done(); + utils.openpe(assert); + var done = assert.async(); + + utils.openFolder(assert, function (finish, childrenFb) { + // execute + mpcp.browser.addAll(function() { + var childrenpe = $(utils.pe).children('.gen'); + assert.equal(childrenpe.length, childrenFb.length, 'check if fb is same as pe'); + finish(); }); + }, + function () { + // callback + utils.closepe(assert); + done(); + }); }); QUnit.test('browser to pe: folder + save + open + delete', function (assert) { - utils.openpe(assert); - var done = assert.async(); - - utils.addDirTope(assert, function (childrenBefore) { - // save the playlist - mpcp.stored.externalSave(function () { - $('#playlist-save-input').val(utils.plSave); - - mpcp.playlist.saveFromStored(function () { - utils.clearpe(assert); - - // open the playlists - mpcp.stored.updatePlaylists(utils.plOpenModal, mpcp.pe.open, function () { - var ele = utils.plOpenModal + ' tr:contains("' + utils.plSave + '")'; - var num = parseInt($(ele + ' td:nth-child(2)').html()); - assert.ok($(ele).length, 'check if pl is visible'); - utils.closeEnough(assert, num, childrenBefore.length, 'check saved playlist has same number of songs'); - $(ele).click(); - - // open the playlist - mpcp.playlist.openFromStored(function () { - children = $(utils.pe).children('.gen'); - assert.equal(children.length, childrenBefore.length, 'check if same playlist'); - utils.closepe(assert); - - // open the playlists - mpcp.stored.updatePlaylists(utils.plOpenModal, null, function () { - var ele = utils.plOpenModal + ' tr:contains("' + utils.plSave + '")'; - assert.ok($(ele).length, 'check if pl is visible'); - var file = $(ele).data().fileid; - - // delete the playlist - mpcp.stored.removePlaylist(file, ele, function () { - assert.notOk($(ele).length, 'check if pl is not visible'); - done(); - }); - }); - }); - }); + utils.openpe(assert); + var done = assert.async(); + + utils.addDirTope(assert, function (childrenBefore) { + // save the playlist + mpcp.stored.externalSave(function () { + $('#playlist-save-input').val(utils.plSave); + + mpcp.playlist.saveFromStored(function () { + utils.clearpe(assert); + + // open the playlists + mpcp.stored.updatePlaylists(utils.plOpenModal, mpcp.pe.open, function () { + var ele = utils.plOpenModal + ' tr:contains("' + utils.plSave + '")'; + var num = parseInt($(ele + ' td:nth-child(2)').html()); + assert.ok($(ele).length, 'check if pl is visible'); + utils.closeEnough(assert, num, childrenBefore.length, 'check saved playlist has same number of songs'); + $(ele).click(); + + // open the playlist + mpcp.playlist.openFromStored(function () { + children = $(utils.pe).children('.gen'); + assert.equal(children.length, childrenBefore.length, 'check if same playlist'); + utils.closepe(assert); + + // open the playlists + mpcp.stored.updatePlaylists(utils.plOpenModal, null, function () { + var ele = utils.plOpenModal + ' tr:contains("' + utils.plSave + '")'; + assert.ok($(ele).length, 'check if pl is visible'); + var file = $(ele).data().fileid; + + // delete the playlist + mpcp.stored.removePlaylist(file, ele, function () { + assert.notOk($(ele).length, 'check if pl is not visible'); + done(); + }); }); + }); }); + }); }); + }); }); // this test seems to no longer work..? //QUnit.test('browser to pe: folder + save odd title + open + delete', function (assert) { -// utils.openpe(assert); -// var done = assert.async(); +// utils.openpe(assert); +// var done = assert.async(); // -// utils.addDirTope(assert, function (childrenBefore) { -// // save the playlist -// mpcp.stored.externalSave(function () { -// $('#playlist-save-input').val(utils.plSaveOdd); +// utils.addDirTope(assert, function (childrenBefore) { +// // save the playlist +// mpcp.stored.externalSave(function () { +// $('#playlist-save-input').val(utils.plSaveOdd); // -// mpcp.playlist.saveFromStored(function () { -// utils.clearpe(assert); +// mpcp.playlist.saveFromStored(function () { +// utils.clearpe(assert); // -// // open the playlist -// mpcp.playlist.openFromStored(function () { -// var ele = utils.plOpenModal + ' tr:contains("' + utils.plSaveOddFix + '")'; -// var num = parseInt($(ele + ' td:nth-child(2)').html()); -// assert.ok($(ele).length, 'check if pl is visible'); -// utils.closeEnough(assert, num, childrenBefore.length, 'check saved playlist has same number of songs'); -// $(ele).click(); +// // open the playlist +// mpcp.playlist.openFromStored(function () { +// var ele = utils.plOpenModal + ' tr:contains("' + utils.plSaveOddFix + '")'; +// var num = parseInt($(ele + ' td:nth-child(2)').html()); +// assert.ok($(ele).length, 'check if pl is visible'); +// utils.closeEnough(assert, num, childrenBefore.length, 'check saved playlist has same number of songs'); +// $(ele).click(); // -// // open the playlist -// mpcp.playlist.openFromStored(function () { -// children = $(utils.pe).children('.gen'); -// assert.equal(children.length, childrenBefore.length, 'check if same playlist'); -// utils.closepe(assert); +// // open the playlist +// mpcp.playlist.openFromStored(function () { +// children = $(utils.pe).children('.gen'); +// assert.equal(children.length, childrenBefore.length, 'check if same playlist'); +// utils.closepe(assert); // -// // open the playlists -// mpcp.stored.updatePlaylists(utils.plOpenModal, null, function () { -// console.log($(utils.plOpenModal + ' tbody').children()); -// var ele = utils.plOpenModal + ' tr:contains("' + utils.plSaveOddFix + '")'; -// assert.ok($(ele).is(':visible'), 'check if pl is visible'); -// var file = $(ele).data().fileid; +// // open the playlists +// mpcp.stored.updatePlaylists(utils.plOpenModal, null, function () { +// console.log($(utils.plOpenModal + ' tbody').children()); +// var ele = utils.plOpenModal + ' tr:contains("' + utils.plSaveOddFix + '")'; +// assert.ok($(ele).is(':visible'), 'check if pl is visible'); +// var file = $(ele).data().fileid; // -// // delete the playlist -// mpcp.stored.removePlaylist(file, ele, function () { -// assert.notOk($(ele).length, 'check if pl is not visible'); -// done(); -// }); -// }); -// }); -// }); +// // delete the playlist +// mpcp.stored.removePlaylist(file, ele, function () { +// assert.notOk($(ele).length, 'check if pl is not visible'); +// done(); +// }); // }); +// }); // }); +// }); // }); +// }); //}); QUnit.test('pe scramble check', function (assert) { - var done = assert.async(); - utils.openpe(assert); - utils.addSong(5, true, function () { - var children = $(utils.pe).children('.gen'); - assert.equal($(children).length, 5, 'check if 5 songs added'); - - mpcp.pe.scramble(function () { - var childrenNow = $(utils.pe).children('.gen'); - assert.ok($(children[0]).text() != $(childrenNow[0]).text() || $(children[1]).text() != $(childrenNow[1]).text() || $(children[2]).text() != $(childrenNow[2]).text(), 'check if scramble works'); - - utils.closepe(assert); - done(); - }); + var done = assert.async(); + utils.openpe(assert); + utils.addSong(5, true, function () { + var children = $(utils.pe).children('.gen'); + assert.equal($(children).length, 5, 'check if 5 songs added'); + + mpcp.pe.scramble(function () { + var childrenNow = $(utils.pe).children('.gen'); + assert.ok($(children[0]).text() != $(childrenNow[0]).text() || $(children[1]).text() != $(childrenNow[1]).text() || $(children[2]).text() != $(childrenNow[2]).text(), 'check if scramble works'); + + utils.closepe(assert); + done(); }); + }); }); QUnit.test('pe duplicate check', function (assert) { - var done = assert.async(); - utils.openpe(assert); - utils.addSong(2, true, function() { - utils.addSong(5, false, function () { - var children = $(utils.pe).children('.gen'); - assert.equal($(children).length, 7, 'check if 8 songs added'); + var done = assert.async(); + utils.openpe(assert); + utils.addSong(2, true, function() { + utils.addSong(5, false, function () { + var children = $(utils.pe).children('.gen'); + assert.equal($(children).length, 7, 'check if 8 songs added'); - mpcp.pe.removeDuplicates(function () { - var childrenNow = $(utils.pe).children('.gen'); - assert.equal($(childrenNow).length, 2, 'check if 2 songs left'); + mpcp.pe.removeDuplicates(function () { + var childrenNow = $(utils.pe).children('.gen'); + assert.equal($(childrenNow).length, 2, 'check if 2 songs left'); - utils.closepe(assert); - done(); - }); - }); + utils.closepe(assert); + done(); + }); }); + }); }); QUnit.test('pe search check with clear', function (assert) { - var done = assert.async(); - utils.openpe(assert); - utils.addSong(5, true, function () { - var children = $(utils.pe).children('.gen'); - assert.equal($(children).length, 5, 'check if 5 songs added'); - assert.ok(!$('#search-pe').is(':visible'), 'check if search is not visible'); - $('#pe-search-toggle').click(); - assert.ok($('#search-pe').is(':visible'), 'check if search is visible'); - $('#search-pe').val(keyword); - - setTimeout(function () { - var childrenNow = $(utils.pe).children('.gen:visible'); - assert.notEqual($(childrenNow).length, $(children).length, 'check if less songs'); - $('#search-pe-clear').click(); - childrenNow = $(utils.pe).children('.gen:visible'); - assert.equal($(childrenNow).length, $(children).length, 'check if same songs'); - $('#pe-search-toggle').click(); - assert.ok(!$('#search-pe').is(':visible'), 'check if search is not visible'); - utils.closepe(assert); - done(); - }, 2000); - }); + var done = assert.async(); + utils.openpe(assert); + utils.addSong(5, true, function () { + var children = $(utils.pe).children('.gen'); + assert.equal($(children).length, 5, 'check if 5 songs added'); + assert.ok(!$('#search-pe').is(':visible'), 'check if search is not visible'); + $('#pe-search-toggle').click(); + assert.ok($('#search-pe').is(':visible'), 'check if search is visible'); + $('#search-pe').val(keyword); + + setTimeout(function () { + var childrenNow = $(utils.pe).children('.gen:visible'); + assert.notEqual($(childrenNow).length, $(children).length, 'check if less songs'); + $('#search-pe-clear').click(); + childrenNow = $(utils.pe).children('.gen:visible'); + assert.equal($(childrenNow).length, $(children).length, 'check if same songs'); + $('#pe-search-toggle').click(); + assert.ok(!$('#search-pe').is(':visible'), 'check if search is not visible'); + utils.closepe(assert); + done(); + }, 2000); + }); }); QUnit.test('pe search check with cancel', function (assert) { - var done = assert.async(); - utils.openpe(assert); - utils.addSong(5, true, function () { - var children = $(utils.pe).children('.gen'); - assert.equal($(children).length, 5, 'check if 5 songs added'); - assert.ok(!$('#search-pe').is(':visible'), 'check if search is not visible'); - $('#pe-search-toggle').click(); - assert.ok($('#search-pe').is(':visible'), 'check if search is visible'); - $('#search-pe').val(keyword); - - setTimeout(function () { - var childrenNow = $(utils.pe).children('.gen:visible'); - console.log($(childrenNow).length); - assert.notEqual($(childrenNow).length, $(children).length, 'check if less songs'); - $('#pe-search-toggle').click(); - assert.ok(!$('#search-pe').is(':visible'), 'check if search is not visible'); - childrenNow = $(utils.pe).children('.gen:visible'); - assert.equal($(childrenNow).length, $(children).length, 'check if same songs'); - utils.closepe(assert); - done(); - }, 2000); - }); + var done = assert.async(); + utils.openpe(assert); + utils.addSong(5, true, function () { + var children = $(utils.pe).children('.gen'); + assert.equal($(children).length, 5, 'check if 5 songs added'); + assert.ok(!$('#search-pe').is(':visible'), 'check if search is not visible'); + $('#pe-search-toggle').click(); + assert.ok($('#search-pe').is(':visible'), 'check if search is visible'); + $('#search-pe').val(keyword); + + setTimeout(function () { + var childrenNow = $(utils.pe).children('.gen:visible'); + console.log($(childrenNow).length); + assert.notEqual($(childrenNow).length, $(children).length, 'check if less songs'); + $('#pe-search-toggle').click(); + assert.ok(!$('#search-pe').is(':visible'), 'check if search is not visible'); + childrenNow = $(utils.pe).children('.gen:visible'); + assert.equal($(childrenNow).length, $(children).length, 'check if same songs'); + utils.closepe(assert); + done(); + }, 2000); + }); }); }; diff --git a/tests/settings.js b/tests/settings.js index fcbdfbf..82d4f98 100644 --- a/tests/settings.js +++ b/tests/settings.js @@ -1,78 +1,78 @@ module.exports = function (utils) { QUnit.test('settings use pl pagination', function (assert) { - var done = assert.async(); + var done = assert.async(); - utils.addSong(210, false, function () { - var children = $(utils.pl).children('.gen'); + utils.addSong(210, false, function () { + var children = $(utils.pl).children('.gen'); - mpcp.settings.savePagination('playlist', false); - assert.ok(!$('#playlist-pages').is(':visible'), 'check if pl pages is not visible'); - var childrenNow = $(utils.pl).children('.gen'); - assert.ok(children.length != childrenNow.length, 'check if pl children is not the same'); + mpcp.settings.savePagination('playlist', false); + assert.ok(!$('#playlist-pages').is(':visible'), 'check if pl pages is not visible'); + var childrenNow = $(utils.pl).children('.gen'); + assert.ok(children.length != childrenNow.length, 'check if pl children is not the same'); - mpcp.settings.savePagination('playlist', true); - assert.ok($('#playlist-pages').is(':visible'), 'check if pl pages is visible'); + mpcp.settings.savePagination('playlist', true); + assert.ok($('#playlist-pages').is(':visible'), 'check if pl pages is visible'); - utils.clearPlaylist(assert, function () { - done(); - }); + utils.clearPlaylist(assert, function () { + done(); }); + }); }); QUnit.test('settings set max pl pagination', function (assert) { - var done = assert.async(); + var done = assert.async(); - utils.addSong(210, false, function () { - var children = $(utils.pl).children('.gen'); + utils.addSong(210, false, function () { + var children = $(utils.pl).children('.gen'); - mpcp.settings.saveItemsMax('playlist', 10); - var childrenNow = $(utils.pl).children('.gen'); - assert.ok(children.length != childrenNow.length, 'check if pl children is not the same'); + mpcp.settings.saveItemsMax('playlist', 10); + var childrenNow = $(utils.pl).children('.gen'); + assert.ok(children.length != childrenNow.length, 'check if pl children is not the same'); - mpcp.settings.saveItemsMax('playlist', 200); + mpcp.settings.saveItemsMax('playlist', 200); - utils.clearPlaylist(assert, function () { - done(); - }); + utils.clearPlaylist(assert, function () { + done(); }); + }); }); QUnit.test('settings use browser pagination', function (assert) { - var children = $(utils.fb).children('.gen'); + var children = $(utils.fb).children('.gen'); - // set it to 10, just in case - mpcp.settings.saveItemsMax('browser', 10); + // set it to 10, just in case + mpcp.settings.saveItemsMax('browser', 10); - mpcp.settings.savePagination('browser', true); - assert.ok($('#browser-pages').is(':visible'), 'check if fb pages is visible'); - var childrenNow = $(utils.fb).children('.gen'); - assert.ok(children.length != childrenNow.length, 'check if fb children is not the same'); + mpcp.settings.savePagination('browser', true); + assert.ok($('#browser-pages').is(':visible'), 'check if fb pages is visible'); + var childrenNow = $(utils.fb).children('.gen'); + assert.ok(children.length != childrenNow.length, 'check if fb children is not the same'); - mpcp.settings.savePagination('browser', false); - assert.ok(!$('#browser-pages').is(':visible'), 'check if fb pages is not visible'); + mpcp.settings.savePagination('browser', false); + assert.ok(!$('#browser-pages').is(':visible'), 'check if fb pages is not visible'); - mpcp.settings.saveItemsMax('browser', 200); + mpcp.settings.saveItemsMax('browser', 200); }); QUnit.test('settings use browser pagination', function (assert) { - // set it to 10, just in case - mpcp.settings.saveItemsMax('browser', 10); + // set it to 10, just in case + mpcp.settings.saveItemsMax('browser', 10); - mpcp.settings.savePagination('browser', true); - assert.ok($('#browser-pages').is(':visible'), 'check if fb pages is visible'); + mpcp.settings.savePagination('browser', true); + assert.ok($('#browser-pages').is(':visible'), 'check if fb pages is visible'); - var children = $(utils.fb).children('.gen'); + var children = $(utils.fb).children('.gen'); - mpcp.settings.saveItemsMax('browser', 5); - var childrenNow = $(utils.fb).children('.gen'); + mpcp.settings.saveItemsMax('browser', 5); + var childrenNow = $(utils.fb).children('.gen'); - assert.ok(children.length != childrenNow.length, 'check if fb children is not the same'); + assert.ok(children.length != childrenNow.length, 'check if fb children is not the same'); - mpcp.settings.savePagination('browser', false); - assert.ok(!$('#browser-pages').is(':visible'), 'check if fb pages is not visible'); + mpcp.settings.savePagination('browser', false); + assert.ok(!$('#browser-pages').is(':visible'), 'check if fb pages is not visible'); - mpcp.settings.saveItemsMax('browser', 200); + mpcp.settings.saveItemsMax('browser', 200); }); // TODO check unknown diff --git a/tests/stored.js b/tests/stored.js index 26659af..a6cf14b 100644 --- a/tests/stored.js +++ b/tests/stored.js @@ -1,45 +1,45 @@ module.exports = function (utils) { QUnit.test('stored: no songs test', function (assert) { - var done = assert.async(); + var done = assert.async(); - // save the playlist - mpcp.stored.updatePlaylists(utils.plSaveModal, null, function () { - $('#playlist-save-input').val(utils.plSave); + // save the playlist + mpcp.stored.updatePlaylists(utils.plSaveModal, null, function () { + $('#playlist-save-input').val(utils.plSave); - mpcp.playlist.saveFromStored(function () { - assert.ok(~$('.toast-message').text().indexOf('empty'), 'check if empty toast'); - toastr.clear(); + mpcp.playlist.saveFromStored(function () { + assert.ok(~$('.toast-message').text().indexOf('empty'), 'check if empty toast'); + toastr.clear(); - // clear the playlist - utils.clearPlaylist(assert, function () { - done(); - }); - }); + // clear the playlist + utils.clearPlaylist(assert, function () { + done(); + }); }); + }); }); QUnit.test('stored: no title test', function (assert) { - var done = assert.async(); - utils.addSong(2, false, function () { - var children = $(utils.pl).children('.gen'); - assert.equal($(children).length, 2, 'check if 2 songs added'); - - // save the playlist - mpcp.stored.updatePlaylists(utils.plSaveModal, null, function () { - $('#playlist-save-input').val(''); - - mpcp.playlist.saveFromStored(function () { - assert.ok(~$('.toast-message').text().indexOf('title'), 'check if no title toast'); - toastr.clear(); - - // clear the playlist - utils.clearPlaylist(assert, function () { - done(); - }); - }); + var done = assert.async(); + utils.addSong(2, false, function () { + var children = $(utils.pl).children('.gen'); + assert.equal($(children).length, 2, 'check if 2 songs added'); + + // save the playlist + mpcp.stored.updatePlaylists(utils.plSaveModal, null, function () { + $('#playlist-save-input').val(''); + + mpcp.playlist.saveFromStored(function () { + assert.ok(~$('.toast-message').text().indexOf('title'), 'check if no title toast'); + toastr.clear(); + + // clear the playlist + utils.clearPlaylist(assert, function () { + done(); }); + }); }); + }); }); }; diff --git a/tests/utils.js b/tests/utils.js index 166267b..9f4f6cb 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -1,280 +1,280 @@ module.exports = function () { var utils = { - fb: '#file-browser-song-list tbody', - pe: '#pe-song-list tbody', - libArt: '#library-artists-list tbody', - libAlb: '#library-albums-list tbody', - libSon: '#library-songs-list tbody', - plSaveModal: '#playlist-save-modal', - plOpenModal: '#playlist-open-modal', - plSave: 'unitTest', - // do not put ?<>|:* etc for windows... I don't check for that because I don't expect server owners to be using windows for actually running mpcparty... I hope. May implement later. - plSaveOdd: ' \t unitTest~!@#$%^&()_+,. \n ', - plSaveOddFix: 'unitTest~!@#$%^&()_+,.', - pl: '#playlist-song-list tbody', - - // I think I found a bug jquery, sometimes the children function doesn't get everything. I keep getting an off by one failed assertion at random times. The elements that don't show in the jquery object IS in the DOM. So I'm not sure how to handle this... - offWorkaround: function (assert, children, childrenNow) { - if (children.length == childrenNow.length + 1 || children.length + 1 == childrenNow.length) { - console.log('Possible jquery bug detected, off by one issue?'); - - console.log(children.length); - console.log(childrenNow.length); - - //for(var i = 0; i < children.length; ++i) { - // if (children[i].attributes["data-dirid"]) - // if (children[i].attributes["data-dirid"].value != childrenNow[i].attributes["data-dirid"].value) { - // console.log(children.get(i-1)); - // console.log(childrenNow.get(i-1)); - // console.log(children[i]); - // console.log(childrenNow[i]); - // break; - // } - // else if (children[i].attributes["data-fileid"]) - // if (children[i].attributes["data-fileid"].value != childrenNow[i].attributes["data-fileid"].value) { - // console.log(children[i]); - // console.log(childrenNow[i]); - // } - // else { - // console.log(children[i]); - // console.log(childrenNow[i]); - // } - //} - - closeEnough(assert, children.length, childrenNow.length, 'check if same as original'); + fb: '#file-browser-song-list tbody', + pe: '#pe-song-list tbody', + libArt: '#library-artists-list tbody', + libAlb: '#library-albums-list tbody', + libSon: '#library-songs-list tbody', + plSaveModal: '#playlist-save-modal', + plOpenModal: '#playlist-open-modal', + plSave: 'unitTest', + // do not put ?<>|:* etc for windows... I don't check for that because I don't expect server owners to be using windows for actually running mpcparty... I hope. May implement later. + plSaveOdd: ' \t unitTest~!@#$%^&()_+,. \n ', + plSaveOddFix: 'unitTest~!@#$%^&()_+,.', + pl: '#playlist-song-list tbody', + + // I think I found a bug jquery, sometimes the children function doesn't get everything. I keep getting an off by one failed assertion at random times. The elements that don't show in the jquery object IS in the DOM. So I'm not sure how to handle this... + offWorkaround: function (assert, children, childrenNow) { + if (children.length == childrenNow.length + 1 || children.length + 1 == childrenNow.length) { + console.log('Possible jquery bug detected, off by one issue?'); + + console.log(children.length); + console.log(childrenNow.length); + + //for(var i = 0; i < children.length; ++i) { + // if (children[i].attributes["data-dirid"]) + // if (children[i].attributes["data-dirid"].value != childrenNow[i].attributes["data-dirid"].value) { + // console.log(children.get(i-1)); + // console.log(childrenNow.get(i-1)); + // console.log(children[i]); + // console.log(childrenNow[i]); + // break; + // } + // else if (children[i].attributes["data-fileid"]) + // if (children[i].attributes["data-fileid"].value != childrenNow[i].attributes["data-fileid"].value) { + // console.log(children[i]); + // console.log(childrenNow[i]); + // } + // else { + // console.log(children[i]); + // console.log(childrenNow[i]); + // } + //} + + closeEnough(assert, children.length, childrenNow.length, 'check if same as original'); + } else { + assert.equal(childrenNow.length, children.length, 'check if same as original'); + } + }, + + // used for issues out of my scope + closeEnough: function (assert, val1, val2, msg) { + if (val1 == val2) + assert.ok(true, msg); + else if (val1 + 1 == val2) + assert.ok(true, msg + ', not equal'); + else if (val1 == val2 + 1) + assert.ok(true, msg + ', not equal'); + else + assert.ok(false, msg); + }, + + // browser functions + // execute: call function while in folder + openFolder: function (assert, execute, callback) { + var title = $($(utils.fb).find('tr')[0]).data().dirid; + + mpcp.browser.update(title, false, function() { + var titleCrumb = $($('#location').find('.loc-dir')[0]).data().dirid; + assert.equal(titleCrumb, title, 'check if loc-dir is the same as the original folder name'); + + var children = $(utils.fb).children('.gen'); + assert.ok(children.length > 1, 'check if anything is in the file browser'); + + assert.equal(document.location.pathname, '/browser/' + encodeURIComponent(title), 'check if url is encoded the same as the folder name'); + + function finish() { + window.history.back(); + + setTimeout(function () { + var childrenNow = $(utils.fb).children('.gen'); + assert.ok(children.length != childrenNow.length, 'check if file browser is different'); + assert.equal(document.location.pathname, '/browser/', 'BACK: check if url is /browser/'); + callback(); + }, 1000); + } + + if (execute) { + execute(finish, children); + } else { + finish(); + } + }); + }, + + // pe functions + openpe: function (assert) { + assert.notOk($('#pe').is(':visible'), 'check if pe is not visible'); + $('#new-playlist').click(); + assert.ok($('#pe').is(':visible'), 'check if pe is visible'); + }, + + closepe: function (assert) { + $('#pe-close').click(); + assert.notOk($('#pe').is(':visible'), 'check if pe is not visible'); + }, + + minimizepe: function (assert) { + $('#pe-minimize').click(); + assert.notOk($('#pe').is(':visible'), 'check if pe is not visible'); + assert.ok($('#pe-tab').is(':visible'), 'check if pe tab is visible'); + }, + + resumepe: function (assert) { + $('#pe-tab').click(); + assert.notOk($('#pe-tab').is(':visible'), 'check if pe tab is not visible'); + assert.ok($('#pe').is(':visible'), 'check if pe is visible'); + }, + + addDirTope: function (assert, callback) { + var title = $($(utils.fb).find('tr')[0]).data().dirid; + mpcp.browser.addExternalDir(title, null, function() { + var children = $(utils.pe).children('.gen'); + assert.ok(children.length > 1, 'check if anything is in the pe'); + callback(children); + }); + }, + + addSong: function (times, incremental, callback) { + var songs = $(utils.fb).find('.song-add'), + i, file, j = 0; + + function mpcpAdd(file) { + mpcp.browser.addExternal(file, null, function () { + if (++j == times && callback) callback(); + }); + } + + if (incremental) { + for (i = 0; i < times; ++i) { + file = $(songs[i]).parent().parent().data().fileid; + mpcpAdd(file); + } + } else { + for (i = 0; i < times; ++i) { + file = $(songs[0]).parent().parent().data().fileid; + mpcpAdd(file); + } + } + + }, + + // add two songs for other tests + addSongTope: function (assert, callback) { + utils.addSong(2, true, function() { + var children = $(utils.pe).children('.gen').not('.rem'); + assert.ok(children.length > 1, 'check if anything is in the pe'); + callback(children); + }); + }, + + clearpe: function (assert) { + $('#pe-clear').click(); + childrenClear = $(utils.pe).children('.gen').not('.rem'); + assert.equal(childrenClear.length, 0, 'check if cleared pe'); + }, + + // library functions + openLibrary: function (assert, callback) { + assert.notOk($('#library').is(':visible'), 'check if library is not visible'); + mpcp.library.open(function () { + assert.ok($('#library').is(':visible'), 'check if library is visible'); + // use contains because of my history additions where clicking back and forth between library and browser will bring you back to the original hit + assert.ok(~document.location.pathname.indexOf('/library/'), 'check if url contains /library/'); + var children = $(utils.libArt).children('.gen:visible'); + assert.ok(children.length > 1, 'check if anything is in the library'); + callback(children); + }); + }, + + closeLibrary: function (assert, callback) { + $('#open-file-browser').click(); + mpcp.browser.open(function () { + assert.notOk($('#library').is(':visible'), 'check if library is not visible'); + assert.ok($('#browser').is(':visible'), 'check if browser is visible'); + callback(); + }); + }, + + // playlist functions + addDirToPl: function (assert, callback) { + var title = $($(utils.fb).find('tr')[0]).data().dirid; + mpcp.browser.addExternalDir(title, null, function() { + var children = $(utils.pl).children('.gen'); + assert.ok(children.length > 1, 'check if anything is in the pl'); + callback(children); + }); + }, + + clearPlaylist: function (assert, callback) { + console.log('before clear'); + mpcp.playlist.clear(function () { + var children = $(utils.pl).children('.gen'); + console.log('after clear'); + assert.equal(children.length, 1, 'check if nothing except 1 item'); + assert.notOk($('#stop').is(':visible'), 'check if stop is not visible'); + assert.equal($('#playlist-title').text(), "", 'check if title is nothing'); + callback(); + }); + }, + + play: function (assert, callback) { + assert.equal($('#title-text').text(), 'No song selected', 'check if title is No song selected'); + assert.notOk($('#stop').is(':visible'), 'check if stop is not visible'); + + mpcp.player.play(function () { + // we wait for progress bar checks + setTimeout(function () { + assert.ok($('#title-text').text() != 'No song selected', 'check if title is not No song selected'); + assert.ok($('#stop').is(':visible'), 'check if stop is visible'); + callback(); + }, 3000); + }); + }, + + pause: function (assert, callback) { + var time = $('#time-current').text(); + var active = $('#pause').hasClass('active'); + + mpcp.player.toggle(function () { + // we wait for progress bar checks + setTimeout(function () { + var timeNow = $('#time-current').text(); + // + 1 incase of time difference before pause + var newTime = parseInt(time.slice(-1)) + 1; + var timeEdit = time.substr(0, 4) + newTime; + if (active) { + assert.notOk($('#pause').hasClass('active'), 'check if not toggled (after previous toggle)'); + assert.ok(timeEdit < timeNow, 'check if time is different'); } else { - assert.equal(childrenNow.length, children.length, 'check if same as original'); + assert.ok($('#pause').hasClass('active'), 'check if toggled (after previous toggle)'); + assert.ok(time == timeNow || timeEdit == timeNow, 'check if time is the same'); } - }, - - // used for issues out of my scope - closeEnough: function (assert, val1, val2, msg) { - if (val1 == val2) - assert.ok(true, msg); - else if (val1 + 1 == val2) - assert.ok(true, msg + ', not equal'); - else if (val1 == val2 + 1) - assert.ok(true, msg + ', not equal'); - else - assert.ok(false, msg); - }, - - // browser functions - // execute: call function while in folder - openFolder: function (assert, execute, callback) { - var title = $($(utils.fb).find('tr')[0]).data().dirid; - - mpcp.browser.update(title, false, function() { - var titleCrumb = $($('#location').find('.loc-dir')[0]).data().dirid; - assert.equal(titleCrumb, title, 'check if loc-dir is the same as the original folder name'); - - var children = $(utils.fb).children('.gen'); - assert.ok(children.length > 1, 'check if anything is in the file browser'); - - assert.equal(document.location.pathname, '/browser/' + encodeURIComponent(title), 'check if url is encoded the same as the folder name'); - - function finish() { - window.history.back(); - - setTimeout(function () { - var childrenNow = $(utils.fb).children('.gen'); - assert.ok(children.length != childrenNow.length, 'check if file browser is different'); - assert.equal(document.location.pathname, '/browser/', 'BACK: check if url is /browser/'); - callback(); - }, 1000); - } - - if (execute) { - execute(finish, children); - } else { - finish(); - } - }); - }, - - // pe functions - openpe: function (assert) { - assert.notOk($('#pe').is(':visible'), 'check if pe is not visible'); - $('#new-playlist').click(); - assert.ok($('#pe').is(':visible'), 'check if pe is visible'); - }, - - closepe: function (assert) { - $('#pe-close').click(); - assert.notOk($('#pe').is(':visible'), 'check if pe is not visible'); - }, - - minimizepe: function (assert) { - $('#pe-minimize').click(); - assert.notOk($('#pe').is(':visible'), 'check if pe is not visible'); - assert.ok($('#pe-tab').is(':visible'), 'check if pe tab is visible'); - }, - - resumepe: function (assert) { - $('#pe-tab').click(); - assert.notOk($('#pe-tab').is(':visible'), 'check if pe tab is not visible'); - assert.ok($('#pe').is(':visible'), 'check if pe is visible'); - }, - - addDirTope: function (assert, callback) { - var title = $($(utils.fb).find('tr')[0]).data().dirid; - mpcp.browser.addExternalDir(title, null, function() { - var children = $(utils.pe).children('.gen'); - assert.ok(children.length > 1, 'check if anything is in the pe'); - callback(children); - }); - }, - - addSong: function (times, incremental, callback) { - var songs = $(utils.fb).find('.song-add'), - i, file, j = 0; - - function mpcpAdd(file) { - mpcp.browser.addExternal(file, null, function () { - if (++j == times && callback) callback(); - }); - } - - if (incremental) { - for (i = 0; i < times; ++i) { - file = $(songs[i]).parent().parent().data().fileid; - mpcpAdd(file); - } - } else { - for (i = 0; i < times; ++i) { - file = $(songs[0]).parent().parent().data().fileid; - mpcpAdd(file); - } - } - - }, - - // add two songs for other tests - addSongTope: function (assert, callback) { - utils.addSong(2, true, function() { - var children = $(utils.pe).children('.gen').not('.rem'); - assert.ok(children.length > 1, 'check if anything is in the pe'); - callback(children); - }); - }, - - clearpe: function (assert) { - $('#pe-clear').click(); - childrenClear = $(utils.pe).children('.gen').not('.rem'); - assert.equal(childrenClear.length, 0, 'check if cleared pe'); - }, - - // library functions - openLibrary: function (assert, callback) { - assert.notOk($('#library').is(':visible'), 'check if library is not visible'); - mpcp.library.open(function () { - assert.ok($('#library').is(':visible'), 'check if library is visible'); - // use contains because of my history additions where clicking back and forth between library and browser will bring you back to the original hit - assert.ok(~document.location.pathname.indexOf('/library/'), 'check if url contains /library/'); - var children = $(utils.libArt).children('.gen:visible'); - assert.ok(children.length > 1, 'check if anything is in the library'); - callback(children); - }); - }, - - closeLibrary: function (assert, callback) { - $('#open-file-browser').click(); - mpcp.browser.open(function () { - assert.notOk($('#library').is(':visible'), 'check if library is not visible'); - assert.ok($('#browser').is(':visible'), 'check if browser is visible'); - callback(); - }); - }, - - // playlist functions - addDirToPl: function (assert, callback) { - var title = $($(utils.fb).find('tr')[0]).data().dirid; - mpcp.browser.addExternalDir(title, null, function() { - var children = $(utils.pl).children('.gen'); - assert.ok(children.length > 1, 'check if anything is in the pl'); - callback(children); - }); - }, - - clearPlaylist: function (assert, callback) { - console.log('before clear'); - mpcp.playlist.clear(function () { - var children = $(utils.pl).children('.gen'); - console.log('after clear'); - assert.equal(children.length, 1, 'check if nothing except 1 item'); - assert.notOk($('#stop').is(':visible'), 'check if stop is not visible'); - assert.equal($('#playlist-title').text(), "", 'check if title is nothing'); - callback(); - }); - }, - - play: function (assert, callback) { - assert.equal($('#title-text').text(), 'No song selected', 'check if title is No song selected'); - assert.notOk($('#stop').is(':visible'), 'check if stop is not visible'); - - mpcp.player.play(function () { - // we wait for progress bar checks - setTimeout(function () { - assert.ok($('#title-text').text() != 'No song selected', 'check if title is not No song selected'); - assert.ok($('#stop').is(':visible'), 'check if stop is visible'); - callback(); - }, 3000); - }); - }, - - pause: function (assert, callback) { - var time = $('#time-current').text(); - var active = $('#pause').hasClass('active'); - - mpcp.player.toggle(function () { - // we wait for progress bar checks - setTimeout(function () { - var timeNow = $('#time-current').text(); - // + 1 incase of time difference before pause - var newTime = parseInt(time.slice(-1)) + 1; - var timeEdit = time.substr(0, 4) + newTime; - if (active) { - assert.notOk($('#pause').hasClass('active'), 'check if not toggled (after previous toggle)'); - assert.ok(timeEdit < timeNow, 'check if time is different'); - } else { - assert.ok($('#pause').hasClass('active'), 'check if toggled (after previous toggle)'); - assert.ok(time == timeNow || timeEdit == timeNow, 'check if time is the same'); - } - callback(); - }, 3000); - }); - }, - - // needs a song to be playing first - next: function (assert, callback) { - var title = $('#title-text').text(); - var pos = parseInt($('#title-pos').text()); - assert.ok(title != 'No song selected', 'check if title is not No song selected'); - - mpcp.player.next(function () { - var titleNew = $('#title-text').text(); - var posNew = parseInt($('#title-pos').text()); - assert.ok(title != titleNew, 'check if title is not the same'); - assert.ok(pos < posNew, 'check if pos < posNew'); - callback(); - }); - }, - - // needs a song to be playing first - previous: function (assert, callback) { - var title = $('#title-text').text(); - var pos = parseInt($('#title-pos').text()); - assert.ok(title != 'No song selected', 'check if title is not No song selected'); - - mpcp.player.previous(function () { - var titleNew = $('#title-text').text(); - var posNew = parseInt($('#title-pos').text()); - assert.ok(title != titleNew, 'check if title is not the same'); - assert.ok(pos > posNew, 'check if pos > posNew'); - callback(); - }); - }, + callback(); + }, 3000); + }); + }, + + // needs a song to be playing first + next: function (assert, callback) { + var title = $('#title-text').text(); + var pos = parseInt($('#title-pos').text()); + assert.ok(title != 'No song selected', 'check if title is not No song selected'); + + mpcp.player.next(function () { + var titleNew = $('#title-text').text(); + var posNew = parseInt($('#title-pos').text()); + assert.ok(title != titleNew, 'check if title is not the same'); + assert.ok(pos < posNew, 'check if pos < posNew'); + callback(); + }); + }, + + // needs a song to be playing first + previous: function (assert, callback) { + var title = $('#title-text').text(); + var pos = parseInt($('#title-pos').text()); + assert.ok(title != 'No song selected', 'check if title is not No song selected'); + + mpcp.player.previous(function () { + var titleNew = $('#title-text').text(); + var posNew = parseInt($('#title-pos').text()); + assert.ok(title != titleNew, 'check if title is not the same'); + assert.ok(pos > posNew, 'check if pos > posNew'); + callback(); + }); + }, }; return utils; diff --git a/views/404.pug b/views/404.pug index 2786335..d38a4ed 100644 --- a/views/404.pug +++ b/views/404.pug @@ -1,5 +1,5 @@ doctype html html - body - p If you expected #{url} to go somewhere, please message the developers, otherwise.. - a(href='/') Go here. + body + p If you expected #{url} to go somewhere, please message the developers, otherwise.. + a(href='/') Go here. diff --git a/views/index.pug b/views/index.pug index c4466dd..51b3219 100644 --- a/views/index.pug +++ b/views/index.pug @@ -1,514 +1,514 @@ doctype html html - head - title #{pack.name} - meta(http-equiv='Content-Type', content='text/html; charset=utf-8') - meta(name='viewport', content='width=device-width, initial-scale=1') - link(rel='icon', href='/img/favicon.png', type='image/x-icon') - link(rel='shortcut icon', href='/img/favicon.png', type='image/x-icon') - link(rel='stylesheet', href='/bootstrap/css/bootstrap.min.css') - link(rel='stylesheet', href='/toastr/toastr.css') - link(rel='stylesheet', href='/jquery-contextmenu/jquery.contextMenu.min.css') - link(rel='stylesheet', href='/dragula/dragula.min.css') - link(rel='stylesheet', href='/css/jquery-ui.min.css') - link(rel='stylesheet', href='/css/overwrite.css') - link#theme(rel='stylesheet') - script(src='/jquery/jquery.min.js') - script(src='/js/jquery-ui.min.js') - script(src='/bootstrap/js/bootstrap.min.js') - script(src='/js/komponist.build.js') - script(src='/js/jQuery_sortColumn.js') - script(src='/js/jquery-multiselect.js') - script(src='/toastr/toastr.min.js') - script(src='/dragula/dragula.min.js') - script(src='/jquery-contextmenu/jquery.contextMenu.min.js') - script(src='/mpcparty.js') - if (config.testing) - link(rel='stylesheet', href='https://code.jquery.com/qunit/qunit-1.23.1.css') - script(src='https://code.jquery.com/qunit/qunit-1.23.1.js') - script(src='/testing.js') + head + title #{pack.name} + meta(http-equiv='Content-Type', content='text/html; charset=utf-8') + meta(name='viewport', content='width=device-width, initial-scale=1') + link(rel='icon', href='/img/favicon.png', type='image/x-icon') + link(rel='shortcut icon', href='/img/favicon.png', type='image/x-icon') + link(rel='stylesheet', href='/bootstrap/css/bootstrap.min.css') + link(rel='stylesheet', href='/toastr/toastr.css') + link(rel='stylesheet', href='/jquery-contextmenu/jquery.contextMenu.min.css') + link(rel='stylesheet', href='/dragula/dragula.min.css') + link(rel='stylesheet', href='/css/jquery-ui.min.css') + link(rel='stylesheet', href='/css/overwrite.css') + link#theme(rel='stylesheet') + script(src='/jquery/jquery.min.js') + script(src='/js/jquery-ui.min.js') + script(src='/bootstrap/js/bootstrap.min.js') + script(src='/js/komponist.build.js') + script(src='/js/jQuery_sortColumn.js') + script(src='/js/jquery-multiselect.js') + script(src='/toastr/toastr.min.js') + script(src='/dragula/dragula.min.js') + script(src='/jquery-contextmenu/jquery.contextMenu.min.js') + script(src='/mpcparty.js') + if (config.testing) + link(rel='stylesheet', href='https://code.jquery.com/qunit/qunit-1.23.1.css') + script(src='https://code.jquery.com/qunit/qunit-1.23.1.js') + script(src='/testing.js') - body - #info-modal.modal.fade(tabindex='-1') - .modal-dialog.modal-lg - .modal-content - .modal-header - button.close(data-dismiss='modal') - span × - h4.modal-title #{pack.name} - v#{pack.version} - .modal-body - p - = pack.description - hr - h4 Database Statistics - p#stats - hr - h4 Libraries used in #{pack.name} - p - a(href='https://github.com/hughsk/komponist', target='_blank') Komponist - | - MPD controller - p - a(href='https://jquery.com/', target='_blank') jQuery - | and  - a(href='https://jqueryui.com/', target='_blank') jQuery UI Interaction Modules - | - Javascript framework - p - a(href='http://getbootstrap.com/', target='_blank') Bootstrap - | - Interface framework - p - a(href='https://github.com/bighoho123/jQuery-sortColumn', target='_blank') jQuery-sortColumn - | (modified) - Table column sorting - p - a(href='https://github.com/CodeSeven/toastr', target='_blank') toastr - | - Notification system - p - a(href='https://github.com/bevacqua/dragula', target='_blank') Dragula - | - Drag and drop framework - p - a(href='https://github.com/swisnl/jQuery-contextMenu', target='_blank') jquery-contextmenu - | - Context menus on right click - p - a(href='https://github.com/gkShine/jquery-multiselect', target='_blank') jquery-multiselect - | (modified) - Allows multiselection in tables - p - a(href='https://www.npmjs.com/pack/youtube-dl', target='_blank') youtube-dl - | - Download videos from a wide variety of websites (This is a 'frontend' to  - a(href='https://rg3.github.io/youtube-dl/') youtube-dl - | ) - hr - h4 Themes - p: a(href='http://getbootstrap.com/', target='_blank') Default Theme - hr - h4 License of #{pack.name} - p #{pack.license} - .modal-footer.space-between - button.stop-server.btn.btn-danger(title='Stops the server. Depending on the server admin configuration, the server will restart ASAP.') - span.glyphicon.glyphicon-off - p.main-link: a(href='https://github.com/jplsek/MPCParty', target='_blank') https://github.com/jplsek/MPCParty - button.btn.btn-default(data-dismiss='modal') Close + body + #info-modal.modal.fade(tabindex='-1') + .modal-dialog.modal-lg + .modal-content + .modal-header + button.close(data-dismiss='modal') + span × + h4.modal-title #{pack.name} - v#{pack.version} + .modal-body + p + = pack.description + hr + h4 Database Statistics + p#stats + hr + h4 Libraries used in #{pack.name} + p + a(href='https://github.com/hughsk/komponist', target='_blank') Komponist + | - MPD controller + p + a(href='https://jquery.com/', target='_blank') jQuery + | and  + a(href='https://jqueryui.com/', target='_blank') jQuery UI Interaction Modules + | - Javascript framework + p + a(href='http://getbootstrap.com/', target='_blank') Bootstrap + | - Interface framework + p + a(href='https://github.com/bighoho123/jQuery-sortColumn', target='_blank') jQuery-sortColumn + | (modified) - Table column sorting + p + a(href='https://github.com/CodeSeven/toastr', target='_blank') toastr + | - Notification system + p + a(href='https://github.com/bevacqua/dragula', target='_blank') Dragula + | - Drag and drop framework + p + a(href='https://github.com/swisnl/jQuery-contextMenu', target='_blank') jquery-contextmenu + | - Context menus on right click + p + a(href='https://github.com/gkShine/jquery-multiselect', target='_blank') jquery-multiselect + | (modified) - Allows multiselection in tables + p + a(href='https://www.npmjs.com/pack/youtube-dl', target='_blank') youtube-dl + | - Download videos from a wide variety of websites (This is a 'frontend' to  + a(href='https://rg3.github.io/youtube-dl/') youtube-dl + | ) + hr + h4 Themes + p: a(href='http://getbootstrap.com/', target='_blank') Default Theme + hr + h4 License of #{pack.name} + p #{pack.license} + .modal-footer.space-between + button.stop-server.btn.btn-danger(title='Stops the server. Depending on the server admin configuration, the server will restart ASAP.') + span.glyphicon.glyphicon-off + p.main-link: a(href='https://github.com/jplsek/MPCParty', target='_blank') https://github.com/jplsek/MPCParty + button.btn.btn-default(data-dismiss='modal') Close - #settings-modal.modal.fade(tabindex='-1') - .modal-dialog - .modal-content - .modal-header - button.close(type='button', data-dismiss='modal') - span × - h4.modal-title Settings - .modal-body - form.form-group(autocomplete='off') - h3 Client Settings - .form-group - label Theme - select#themes.form-control - option(value='default') Default - option(value='default-thin') Default (Thin) - option(value='materialish') Materialish - option(value='materialish-thin') Materialish (Thin) - .form-group - .checkbox - label(title='Note: This client will be slow with playlists larger than 2000 songs if pagination is disabled.') - input#use-pages-playlist(type='checkbox') - | Use playlist pagination - label Maximum playlist table rows per page - input#items-max-playlist.form-control(type='number', min='5', max='1000') - .form-group - .checkbox - label(title='Note: This client will be slow with libraries larger than 2000 songs if pagination is disabled.') - input#use-pages-browser(type='checkbox') - | Use browser pagination - label Maximum browser table rows per page - input#items-max-browser.form-control(type='number', min='5', max='1000') - .form-group - label Maximum notification history table rows - input#history-max.form-control(type='number', min='0', max='1000') - .form-group - .checkbox - label(title='Disabling pulse animations may improve performace') - input#use-pulse(type='checkbox') - | Use pulse animation - .checkbox - label(title='Show or hide "unknown" text in the browser') - input#use-unknown(type='checkbox') - | Use 'unknown' text in the browser (if false, it will show nothing) - .checkbox - label(title='When enabled, whenever you skip a song, it will be removed from the playlist') - input#use-skip-to-remove(type='checkbox') - | Use skip to remove feature (only works when voting is disabled) - .checkbox - label(title='Stops showing the warning icon when consume mode is enabled') - input#use-consume-warning(type='checkbox') - | Show MPD's consume warning - .checkbox - label(title='Note: some errors you may see are false-positives.') - input#show-all-errors(type='checkbox') - | Debug: Show all errors - h3 Server Settings - .form-group - label Track crossfade (0 - 10 seconds) - input#crossfade(type='range', min='0', max='10') - .form-group - .checkbox - label(title='Removes songs when skipping and when the song finishes') - input#consume(type='checkbox') - | Use consume mode - .modal-footer - button.btn.btn-default(type='button', data-dismiss='modal') Close + #settings-modal.modal.fade(tabindex='-1') + .modal-dialog + .modal-content + .modal-header + button.close(type='button', data-dismiss='modal') + span × + h4.modal-title Settings + .modal-body + form.form-group(autocomplete='off') + h3 Client Settings + .form-group + label Theme + select#themes.form-control + option(value='default') Default + option(value='default-thin') Default (Thin) + option(value='materialish') Materialish + option(value='materialish-thin') Materialish (Thin) + .form-group + .checkbox + label(title='Note: This client will be slow with playlists larger than 2000 songs if pagination is disabled.') + input#use-pages-playlist(type='checkbox') + | Use playlist pagination + label Maximum playlist table rows per page + input#items-max-playlist.form-control(type='number', min='5', max='1000') + .form-group + .checkbox + label(title='Note: This client will be slow with libraries larger than 2000 songs if pagination is disabled.') + input#use-pages-browser(type='checkbox') + | Use browser pagination + label Maximum browser table rows per page + input#items-max-browser.form-control(type='number', min='5', max='1000') + .form-group + label Maximum notification history table rows + input#history-max.form-control(type='number', min='0', max='1000') + .form-group + .checkbox + label(title='Disabling pulse animations may improve performace') + input#use-pulse(type='checkbox') + | Use pulse animation + .checkbox + label(title='Show or hide "unknown" text in the browser') + input#use-unknown(type='checkbox') + | Use 'unknown' text in the browser (if false, it will show nothing) + .checkbox + label(title='When enabled, whenever you skip a song, it will be removed from the playlist') + input#use-skip-to-remove(type='checkbox') + | Use skip to remove feature (only works when voting is disabled) + .checkbox + label(title='Stops showing the warning icon when consume mode is enabled') + input#use-consume-warning(type='checkbox') + | Show MPD's consume warning + .checkbox + label(title='Note: some errors you may see are false-positives.') + input#show-all-errors(type='checkbox') + | Debug: Show all errors + h3 Server Settings + .form-group + label Track crossfade (0 - 10 seconds) + input#crossfade(type='range', min='0', max='10') + .form-group + .checkbox + label(title='Removes songs when skipping and when the song finishes') + input#consume(type='checkbox') + | Use consume mode + .modal-footer + button.btn.btn-default(type='button', data-dismiss='modal') Close - mixin playlists - table.playlists.table.table-hover - thead: tr - th.col-playlists-title Title - th.col-playlists-songs Songs - th - tbody.append + mixin playlists + table.playlists.table.table-hover + thead: tr + th.col-playlists-title Title + th.col-playlists-songs Songs + th + tbody.append - #playlist-open-modal.modal.fade(tabindex='-1') - .modal-dialog - .modal-content - .modal-header - button.close(type='button', data-dismiss='modal') - span × - h4.modal-title Open playlist - .modal-body - +playlists - .modal-footer - button.btn.btn-default(type='button', data-dismiss='modal') Cancel - button#playlist-open-confirm.btn.btn-primary(type='button', data-dismiss='modal') Open + #playlist-open-modal.modal.fade(tabindex='-1') + .modal-dialog + .modal-content + .modal-header + button.close(type='button', data-dismiss='modal') + span × + h4.modal-title Open playlist + .modal-body + +playlists + .modal-footer + button.btn.btn-default(type='button', data-dismiss='modal') Cancel + button#playlist-open-confirm.btn.btn-primary(type='button', data-dismiss='modal') Open - #playlist-save-modal.modal.fade(tabindex='-1') - .modal-dialog - .modal-content - .modal-header - button.close(type='button', data-dismiss='modal') - span × - h4.modal-title Save playlist - .modal-body - .input-group - input#playlist-save-input.form-control(type='text', placeholder='Enter new playlist name') - span.input-group-btn - button#playlist-save-clear.btn.btn-default(type='button') - span.glyphicon.glyphicon-remove - br - +playlists - .modal-footer - button.btn.btn-default(type='button', data-dismiss='modal') Cancel - button#playlist-save-confirm.btn.btn-primary(type='button', data-dismiss='modal') Save + #playlist-save-modal.modal.fade(tabindex='-1') + .modal-dialog + .modal-content + .modal-header + button.close(type='button', data-dismiss='modal') + span × + h4.modal-title Save playlist + .modal-body + .input-group + input#playlist-save-input.form-control(type='text', placeholder='Enter new playlist name') + span.input-group-btn + button#playlist-save-clear.btn.btn-default(type='button') + span.glyphicon.glyphicon-remove + br + +playlists + .modal-footer + button.btn.btn-default(type='button', data-dismiss='modal') Cancel + button#playlist-save-confirm.btn.btn-primary(type='button', data-dismiss='modal') Save - #song-info-modal.modal.fade(tabindex='-1') - .modal-dialog - .modal-content - .modal-header - button.close(type='button', data-dismiss='modal') - span × - h4.modal-title - .modal-body - table#song-info.table: tbody - .modal-footer - button.btn.btn-default(type='button', data-dismiss='modal') Close + #song-info-modal.modal.fade(tabindex='-1') + .modal-dialog + .modal-content + .modal-header + button.close(type='button', data-dismiss='modal') + span × + h4.modal-title + .modal-body + table#song-info.table: tbody + .modal-footer + button.btn.btn-default(type='button', data-dismiss='modal') Close - #set-priority-modal.modal.fade(tabindex='-1') - .modal-dialog - .modal-content - .modal-header - button.close(type='button', data-dismiss='modal') - span × - h4.modal-title Set Priority - .modal-body - p Set the priority of the specified song. A higher priority means that it will be played first when in random mode. The default priority of new songs is 0. - p: a(href='https://www.musicpd.org/doc/protocol/queue.html', target='_blank') Source - form#priority-form - input#priority.form-control(type='number', min='0', max='255') - // form validation, and press enter to submit - input#priority-submit.hidden(type='submit') - .modal-footer - button.btn.btn-default(type='button', data-dismiss='modal') Close - button#set-priority-confirm.btn.btn-primary(type='button') Ok + #set-priority-modal.modal.fade(tabindex='-1') + .modal-dialog + .modal-content + .modal-header + button.close(type='button', data-dismiss='modal') + span × + h4.modal-title Set Priority + .modal-body + p Set the priority of the specified song. A higher priority means that it will be played first when in random mode. The default priority of new songs is 0. + p: a(href='https://www.musicpd.org/doc/protocol/queue.html', target='_blank') Source + form#priority-form + input#priority.form-control(type='number', min='0', max='255') + // form validation, and press enter to submit + input#priority-submit.hidden(type='submit') + .modal-footer + button.btn.btn-default(type='button', data-dismiss='modal') Close + button#set-priority-confirm.btn.btn-primary(type='button') Ok - if (config.downloader) - #downloader-location-modal.modal.fade(tabindex='-1') - .modal-dialog - .modal-content - .modal-header - button.close(type='button', data-dismiss='modal') - span × - h4.modal-title Select Location - .modal-body - #downloader-location-crumb.flex-full - ol.breadcrumb - li: span#downloader-home.glyphicon.glyphicon-home(title='Go to the root of the music database.') - #downloader-wrap - table#downloader-folder-list.table.table-hover - tbody.append - .modal-footer - button.btn.btn-default(type='button', data-dismiss='modal') Close - button#downloader-location-confirm.btn.btn-primary(type='button', data-dismiss='modal') Select + if (config.downloader) + #downloader-location-modal.modal.fade(tabindex='-1') + .modal-dialog + .modal-content + .modal-header + button.close(type='button', data-dismiss='modal') + span × + h4.modal-title Select Location + .modal-body + #downloader-location-crumb.flex-full + ol.breadcrumb + li: span#downloader-home.glyphicon.glyphicon-home(title='Go to the root of the music database.') + #downloader-wrap + table#downloader-folder-list.table.table-hover + tbody.append + .modal-footer + button.btn.btn-default(type='button', data-dismiss='modal') Close + button#downloader-location-confirm.btn.btn-primary(type='button', data-dismiss='modal') Select - #page-wrapper - header.navbar.navbar-default - #header-left.navbar.navbar-default - #slider - input#volume(type='range', min='0', max='100', orient='vertical', title='Change the volume') - #media-buttons - .btn-group - button#previous.btn.btn-default(title='Play previous song in playlist') - span.glyphicon.glyphicon-backward - button#play.btn.btn-default(title='Play song in playlist') - span.glyphicon.glyphicon-play - button#pause.btn.btn-default(title='Pause song in playlist') - span.glyphicon.glyphicon-pause - button#stop.btn.btn-default(title='Stop song in playlist') - span.glyphicon.glyphicon-stop - button#next.btn.btn-default(title='Play next song in playlist') - span.glyphicon.glyphicon-forward - .space-between - button#go-current.btn.btn-default(title='Go to the current song in the playlist') - span.glyphicon.glyphicon-screenshot - .btn-group - button#repeat.btn.btn-default(title='Repeat playlist') - span.glyphicon.glyphicon-repeat - button#random.btn.btn-default(title='Select random songs in playlist') - span.glyphicon.glyphicon-random - #header-middle - img#album-art - #time-bar.flex-full.text-center - table#title.fixed-table - tr - td - span#title-pos - span#title-text - #time-range - input#music-time(type='range', min='0') - span#time-current - span#time-total - #header-right - #header-right-top.space-between - if (config.downloader) - span.dropdown - button#downloader-btn.btn.btn-default.dropdown-toggle(title='Downloader' data-toggle='dropdown') - span.glyphicon.glyphicon-facetime-video - ul#downloader.dropdown-menu.dropdown-menu-right.stop-click-event - li.dropdown-header Audio Downloader - li: a.no-hover - form(autocomplete='off') - label(for='downloader-url') URL - .input-group - input#downloader-url.form-control(type='text', title='Enter a URL, such as from youtube, and this will download the audio on to the server.', placeholder='Enter a url (Ex: a video from youtube)') - span.input-group-btn - button#downloader-download.btn.btn-default(type='button', title='Download') - span.glyphicon.glyphicon-download-alt - label(for='downloader-location') Location - .input-group - input#downloader-location.form-control(type='text', title='Location to download the file') - span.input-group-btn - button#downloader-browse.btn.btn-default(type='button', title='Browse to location', data-toggle='modal', data-target='#downloader-location-modal') - span.glyphicon.glyphicon-folder-open - #downloader-title - .space-between - #downloader-status - span.dropdown - button.btn.btn-default.dropdown-toggle(title='History' data-toggle='dropdown') - span.glyphicon.glyphicon-time - ul.dropdown-menu.dropdown-menu-right.stop-click-event - li.dropdown-header Notification History - li: a.no-hover: table.table: tbody#history - if (config.showUsers) - span.dropdown - button.btn.btn-default.dropdown-toggle(title='Users using mpcparty' data-toggle='dropdown') - span.glyphicon.glyphicon-user - ul#user-list.dropdown-menu.dropdown-menu-right.stop-click-event - li.dropdown-header Users Connected - .btn-group - button.btn.btn-default(title='Settings of mpcparty' data-toggle='modal', data-target='#settings-modal') - span.glyphicon.glyphicon-wrench - button.btn.btn-default(title='Information about mpcparty' data-toggle='modal', data-target='#info-modal') - span.glyphicon.glyphicon-info-sign + #page-wrapper + header.navbar.navbar-default + #header-left.navbar.navbar-default + #slider + input#volume(type='range', min='0', max='100', orient='vertical', title='Change the volume') + #media-buttons + .btn-group + button#previous.btn.btn-default(title='Play previous song in playlist') + span.glyphicon.glyphicon-backward + button#play.btn.btn-default(title='Play song in playlist') + span.glyphicon.glyphicon-play + button#pause.btn.btn-default(title='Pause song in playlist') + span.glyphicon.glyphicon-pause + button#stop.btn.btn-default(title='Stop song in playlist') + span.glyphicon.glyphicon-stop + button#next.btn.btn-default(title='Play next song in playlist') + span.glyphicon.glyphicon-forward + .space-between + button#go-current.btn.btn-default(title='Go to the current song in the playlist') + span.glyphicon.glyphicon-screenshot + .btn-group + button#repeat.btn.btn-default(title='Repeat playlist') + span.glyphicon.glyphicon-repeat + button#random.btn.btn-default(title='Select random songs in playlist') + span.glyphicon.glyphicon-random + #header-middle + img#album-art + #time-bar.flex-full.text-center + table#title.fixed-table + tr + td + span#title-pos + span#title-text + #time-range + input#music-time(type='range', min='0') + span#time-current + span#time-total + #header-right + #header-right-top.space-between + if (config.downloader) + span.dropdown + button#downloader-btn.btn.btn-default.dropdown-toggle(title='Downloader' data-toggle='dropdown') + span.glyphicon.glyphicon-facetime-video + ul#downloader.dropdown-menu.dropdown-menu-right.stop-click-event + li.dropdown-header Audio Downloader + li: a.no-hover form(autocomplete='off') - .input-group - input#search-browser.form-control(type='text', placeholder='Search songs...') - span.input-group-btn - button#search-clear.btn.btn-default(type='button') - span.glyphicon.glyphicon-remove + label(for='downloader-url') URL + .input-group + input#downloader-url.form-control(type='text', title='Enter a URL, such as from youtube, and this will download the audio on to the server.', placeholder='Enter a url (Ex: a video from youtube)') + span.input-group-btn + button#downloader-download.btn.btn-default(type='button', title='Download') + span.glyphicon.glyphicon-download-alt + label(for='downloader-location') Location + .input-group + input#downloader-location.form-control(type='text', title='Location to download the file') + span.input-group-btn + button#downloader-browse.btn.btn-default(type='button', title='Browse to location', data-toggle='modal', data-target='#downloader-location-modal') + span.glyphicon.glyphicon-folder-open + #downloader-title + .space-between + #downloader-status + span.dropdown + button.btn.btn-default.dropdown-toggle(title='History' data-toggle='dropdown') + span.glyphicon.glyphicon-time + ul.dropdown-menu.dropdown-menu-right.stop-click-event + li.dropdown-header Notification History + li: a.no-hover: table.table: tbody#history + if (config.showUsers) + span.dropdown + button.btn.btn-default.dropdown-toggle(title='Users using mpcparty' data-toggle='dropdown') + span.glyphicon.glyphicon-user + ul#user-list.dropdown-menu.dropdown-menu-right.stop-click-event + li.dropdown-header Users Connected + .btn-group + button.btn.btn-default(title='Settings of mpcparty' data-toggle='modal', data-target='#settings-modal') + span.glyphicon.glyphicon-wrench + button.btn.btn-default(title='Information about mpcparty' data-toggle='modal', data-target='#info-modal') + span.glyphicon.glyphicon-info-sign + form(autocomplete='off') + .input-group + input#search-browser.form-control(type='text', placeholder='Search songs...') + span.input-group-btn + button#search-clear.btn.btn-default(type='button') + span.glyphicon.glyphicon-remove - mixin songListHeader(id) - table.table-header.table(id=id+'-header') - thead: tr - th(id=id+'-col-number') # - th(id=id+'-col-title') Title - th(id=id+'-col-artist') Artist - th(id=id+'-col-album') Album - th(id=id+'-col-time') Time - th + mixin songListHeader(id) + table.table-header.table(id=id+'-header') + thead: tr + th(id=id+'-col-number') # + th(id=id+'-col-title') Title + th(id=id+'-col-artist') Artist + th(id=id+'-col-album') Album + th(id=id+'-col-time') Time + th - mixin songList(id) - table.song-list.table.table-hover(id=id) - tbody.append.connected(id=id+'-tbody') + mixin songList(id) + table.song-list.table.table-hover(id=id) + tbody.append.connected(id=id+'-tbody') - mixin libraryAA(id, title, search) - div(id='library-'+id) - div(id='library-'+id+'-header') - form(autocomplete='off') - .input-group - input(id='search-'+id, class='form-control', type='text', placeholder='Search '+search+'...') - span.input-group-btn - button(id='search-'+id+'-clear', class='btn btn-default', type='button') - span.glyphicon.glyphicon-remove - table.table-header-fixed.table(id='library-'+id+'-list-header') - thead: tr - th(id='library-col-'+id) #{title} - th - div(id='library-'+id+'-wrap') - table(id='library-'+id+'-list', class='table table-hover library-list-context') - tbody.append.connected(id='library-'+id+'-list-tbody') + mixin libraryAA(id, title, search) + div(id='library-'+id) + div(id='library-'+id+'-header') + form(autocomplete='off') + .input-group + input(id='search-'+id, class='form-control', type='text', placeholder='Search '+search+'...') + span.input-group-btn + button(id='search-'+id+'-clear', class='btn btn-default', type='button') + span.glyphicon.glyphicon-remove + table.table-header-fixed.table(id='library-'+id+'-list-header') + thead: tr + th(id='library-col-'+id) #{title} + th + div(id='library-'+id+'-wrap') + table(id='library-'+id+'-list', class='table table-hover library-list-context') + tbody.append.connected(id='library-'+id+'-list-tbody') - main - #browser-selection - button#update.btn.btn-default(title='Update the music database (after uploading new music to the folder)') - span.glyphicon.glyphicon-refresh - hr - button#open-file-browser.btn.btn-default(title='Show the file browser') - span.glyphicon.glyphicon-folder-open - button#open-library.btn.btn-default(title='Show the library') - span.glyphicon.glyphicon-align-justify - span#warning-consume.text-warning.glyphicon.glyphicon-warning-sign.none(title='MPD\'s consume mode is enabled!') + main + #browser-selection + button#update.btn.btn-default(title='Update the music database (after uploading new music to the folder)') + span.glyphicon.glyphicon-refresh + hr + button#open-file-browser.btn.btn-default(title='Show the file browser') + span.glyphicon.glyphicon-folder-open + button#open-library.btn.btn-default(title='Show the library') + span.glyphicon.glyphicon-align-justify + span#warning-consume.text-warning.glyphicon.glyphicon-warning-sign.none(title='MPD\'s consume mode is enabled!') - #library.col-sm-8 - #library-top - +libraryAA('artists', 'Artist', 'artists') - +libraryAA('albums', 'Album', 'albums from artist') - #library-songs - #library-songs-header - form(autocomplete='off') - .input-group - input#search-songs.form-control(type='text', placeholder='Search songs from album...') - span.input-group-btn - button#search-songs-clear.btn.btn-default(type='button') - span.glyphicon.glyphicon-remove - +songListHeader('library-songs-list') - #library-songs-wrap - +songList('library-songs-list') + #library.col-sm-8 + #library-top + +libraryAA('artists', 'Artist', 'artists') + +libraryAA('albums', 'Album', 'albums from artist') + #library-songs + #library-songs-header + form(autocomplete='off') + .input-group + input#search-songs.form-control(type='text', placeholder='Search songs from album...') + span.input-group-btn + button#search-songs-clear.btn.btn-default(type='button') + span.glyphicon.glyphicon-remove + +songListHeader('library-songs-list') + #library-songs-wrap + +songList('library-songs-list') - #browser.col-sm-8 - noscript: h1 You have Javascript disabled. This does not work without Javascript. - // having the block wrapper fixes an issue with webkit not getting the childs height - #browser-header-wrap - #browser-header - #location.flex-full - ol.breadcrumb - li: span#home.glyphicon.glyphicon-home(title='Go to the root of the music database.') - #browser-bottons - .btn-group - button#add-all.btn.btn-default(title='Add all songs from the current page') - | Add All - +songListHeader('file-browser-song-list') - #slwrap - +songList('file-browser-song-list') - #browser-pages.pages.navbar.navbar-default - button.btn.btn-default.first(title='Go to first page') - span.glyphicon.glyphicon-step-backward - button.btn.btn-default.previous(title='Go to previous page') - span.glyphicon.glyphicon-chevron-left - .page - span Page - form(autocomplete='off') - input.form-control(type='number', min='1', value='1') - span of - span.total-pages 1 - button.btn.btn-default.next(title='Go to next page') - span.glyphicon.glyphicon-chevron-right - button.btn.btn-default.last(title='Go to last page') - span.glyphicon.glyphicon-step-forward + #browser.col-sm-8 + noscript: h1 You have Javascript disabled. This does not work without Javascript. + // having the block wrapper fixes an issue with webkit not getting the childs height + #browser-header-wrap + #browser-header + #location.flex-full + ol.breadcrumb + li: span#home.glyphicon.glyphicon-home(title='Go to the root of the music database.') + #browser-bottons + .btn-group + button#add-all.btn.btn-default(title='Add all songs from the current page') + | Add All + +songListHeader('file-browser-song-list') + #slwrap + +songList('file-browser-song-list') + #browser-pages.pages.navbar.navbar-default + button.btn.btn-default.first(title='Go to first page') + span.glyphicon.glyphicon-step-backward + button.btn.btn-default.previous(title='Go to previous page') + span.glyphicon.glyphicon-chevron-left + .page + span Page + form(autocomplete='off') + input.form-control(type='number', min='1', value='1') + span of + span.total-pages 1 + button.btn.btn-default.next(title='Go to next page') + span.glyphicon.glyphicon-chevron-right + button.btn.btn-default.last(title='Go to last page') + span.glyphicon.glyphicon-step-forward - #playlist.col-sm-4 - // having the block wrapper fixes an issue with webkit not getting the childs height - #playlist-header-wrap - #playlist-header - div.flex-full - table.fixed-table - tr - td#playlist-title - strong - #playlist-buttons.btn-group - button#new-playlist.btn.btn-default(title='Create a new playlist') - span.glyphicon.glyphicon-plus - button#clear-playlist.btn.btn-default(title='Clear the playlist') - span.glyphicon.glyphicon-remove - button#open-playlist.btn.btn-default(title='Open a playlist' data-toggle='modal', data-target='#playlist-open-modal') - span.glyphicon.glyphicon-folder-open - button#save-playlist.btn.btn-default(title='Save the playlist' data-toggle='modal', data-target='#playlist-save-modal') - span.glyphicon.glyphicon-floppy-save - button#scramble.btn.btn-default(title='Scramble the playlist') - span.glyphicon.glyphicon-random - button#remove-duplicates.btn.btn-default(title='Remove duplicate songs from the playlist based on the title') - span.glyphicon.glyphicon-fire - button#playlist-search-toggle.btn.btn-default(type='button') - span.glyphicon.glyphicon-search - #playlist-search.none - form(autocomplete='off') - .input-group - input#search-playlist.form-control(type='text', placeholder='Search playlist...') - span.input-group-btn - button#search-playlist-clear.btn.btn-default(type='button') - span.glyphicon.glyphicon-remove - #pslwrap - table#playlist-song-list.table.table-hover - tbody#playlist-song-list-tbody.append.connected - #playlist-pages.pages.navbar.navbar-default - button.btn.btn-default.first(title='Go to first page') - span.glyphicon.glyphicon-step-backward - button.btn.btn-default.previous(title='Go to previous page') - span.glyphicon.glyphicon-chevron-left - .page - span Page - form(autocomplete='off') - input.form-control(type='number', min='1', value='1') - span of - span.total-pages 1 - button.btn.btn-default.next(title='Go to next page') - span.glyphicon.glyphicon-chevron-right - button.btn.btn-default.last(title='Go to last page') - span.glyphicon.glyphicon-step-forward + #playlist.col-sm-4 + // having the block wrapper fixes an issue with webkit not getting the childs height + #playlist-header-wrap + #playlist-header + div.flex-full + table.fixed-table + tr + td#playlist-title + strong + #playlist-buttons.btn-group + button#new-playlist.btn.btn-default(title='Create a new playlist') + span.glyphicon.glyphicon-plus + button#clear-playlist.btn.btn-default(title='Clear the playlist') + span.glyphicon.glyphicon-remove + button#open-playlist.btn.btn-default(title='Open a playlist' data-toggle='modal', data-target='#playlist-open-modal') + span.glyphicon.glyphicon-folder-open + button#save-playlist.btn.btn-default(title='Save the playlist' data-toggle='modal', data-target='#playlist-save-modal') + span.glyphicon.glyphicon-floppy-save + button#scramble.btn.btn-default(title='Scramble the playlist') + span.glyphicon.glyphicon-random + button#remove-duplicates.btn.btn-default(title='Remove duplicate songs from the playlist based on the title') + span.glyphicon.glyphicon-fire + button#playlist-search-toggle.btn.btn-default(type='button') + span.glyphicon.glyphicon-search + #playlist-search.none + form(autocomplete='off') + .input-group + input#search-playlist.form-control(type='text', placeholder='Search playlist...') + span.input-group-btn + button#search-playlist-clear.btn.btn-default(type='button') + span.glyphicon.glyphicon-remove + #pslwrap + table#playlist-song-list.table.table-hover + tbody#playlist-song-list-tbody.append.connected + #playlist-pages.pages.navbar.navbar-default + button.btn.btn-default.first(title='Go to first page') + span.glyphicon.glyphicon-step-backward + button.btn.btn-default.previous(title='Go to previous page') + span.glyphicon.glyphicon-chevron-left + .page + span Page + form(autocomplete='off') + input.form-control(type='number', min='1', value='1') + span of + span.total-pages 1 + button.btn.btn-default.next(title='Go to next page') + span.glyphicon.glyphicon-chevron-right + button.btn.btn-default.last(title='Go to last page') + span.glyphicon.glyphicon-step-forward - footer - #pe.panel.panel-default.none - #pe-header.panel-heading - .panel-default-shown - .btn-group - button#pe-clear.btn.btn-default(title='Clear the playlist') - span.glyphicon.glyphicon-remove - button#pe-open.btn.btn-default(title='Open a playlist' data-toggle='modal', data-target='#playlist-open-modal') - span.glyphicon.glyphicon-folder-open - button#pe-save.btn.btn-default(title='Save the playlist', data-toggle='modal', data-target='#playlist-save-modal') - span.glyphicon.glyphicon-floppy-save - button#pe-scramble.btn.btn-default(title='Scramble the playlist') - span.glyphicon.glyphicon-random - button#pe-remove-duplicates.btn.btn-default(title='Remove duplicate songs from the playlist based on the title') - span.glyphicon.glyphicon-fire - button#pe-search-toggle.btn.btn-default(type='button') - span.glyphicon.glyphicon-search - span.panel-title Playlist Editor - .btn-group.pull-right - button#pe-minimize.btn.btn-default - span.glyphicon.glyphicon-minus - button#pe-close.btn.btn-default - span.glyphicon.glyphicon-remove - #pe-search.none - form(autocomplete='off') - .input-group - input#search-pe.form-control(type='text', placeholder='Search playlist editor...') - span.input-group-btn - button#search-pe-clear.btn.btn-default(type='button') - span.glyphicon.glyphicon-remove - #pe-main.panel-body - #pewrap - table#pe-song-list.table.table-hover - tbody#pe-song-list-tbody.append.connected - #pe-tab.none Resume playlist editor + footer + #pe.panel.panel-default.none + #pe-header.panel-heading + .panel-default-shown + .btn-group + button#pe-clear.btn.btn-default(title='Clear the playlist') + span.glyphicon.glyphicon-remove + button#pe-open.btn.btn-default(title='Open a playlist' data-toggle='modal', data-target='#playlist-open-modal') + span.glyphicon.glyphicon-folder-open + button#pe-save.btn.btn-default(title='Save the playlist', data-toggle='modal', data-target='#playlist-save-modal') + span.glyphicon.glyphicon-floppy-save + button#pe-scramble.btn.btn-default(title='Scramble the playlist') + span.glyphicon.glyphicon-random + button#pe-remove-duplicates.btn.btn-default(title='Remove duplicate songs from the playlist based on the title') + span.glyphicon.glyphicon-fire + button#pe-search-toggle.btn.btn-default(type='button') + span.glyphicon.glyphicon-search + span.panel-title Playlist Editor + .btn-group.pull-right + button#pe-minimize.btn.btn-default + span.glyphicon.glyphicon-minus + button#pe-close.btn.btn-default + span.glyphicon.glyphicon-remove + #pe-search.none + form(autocomplete='off') + .input-group + input#search-pe.form-control(type='text', placeholder='Search playlist editor...') + span.input-group-btn + button#search-pe-clear.btn.btn-default(type='button') + span.glyphicon.glyphicon-remove + #pe-main.panel-body + #pewrap + table#pe-song-list.table.table-hover + tbody#pe-song-list-tbody.append.connected + #pe-tab.none Resume playlist editor - if (config.testing) - #testing.panel.panel-default - #testing-header.panel-heading - span.panel-title Unit Tests - button#start-testing.pull-right.btn.btn-default(type='button') Start tests - #testing-main.panel-body - #qunit + if (config.testing) + #testing.panel.panel-default + #testing-header.panel-heading + span.panel-title Unit Tests + button#start-testing.pull-right.btn.btn-default(type='button') Start tests + #testing-main.panel-body + #qunit