2.2.0 Pushing beta to prod, lots of changes

This commit is contained in:
Trevor Squillario 2012-10-18 20:29:55 -04:00
parent db8cb0865a
commit 58d46ec544
16 changed files with 752 additions and 399 deletions

32
README
View file

@ -2,17 +2,35 @@ MiniSub - HTML5 Mini Player for Subsonic
Imagine you can stream all your music from home, to any device, where ever you are. That is Subsonic! Now imagine having a Web App to stream your music that is as beautiful and well designed as it is functional, that is MiniSub!
MiniSub is an HTML5 Mini Player for the Subsonic streaming server.
MiniSub is an HTML5 Web Player for the Subsonic streaming server.
Features
- HTML5 Audio with Flash fallback (provided by the SoundManager2 library)
- Flexible Layout (will scale to whatever size your browser window is)
- Customizable (features can be turned on/off and settings are saved)
- Keyboard shortcuts (back, forward, play/pause, skip to artist)
- Playlists (create new, add to existing, delete)
- Artist/Album Ratings
- Chat and Now Playing
- Last.fm
- Keyboard shortcuts (back, forward, play/pause, skip to artist, volume control)
- Playlist support (create new, add to existing, delete)
- Podcast support (includes description field on hover)
- Favorite/Starred support for Albums & Songs
- Chat support (when the chat window is open it will auto-update in the background)
- Now Playing support (see what others are streaming at the moment)
- Easy installation (Chrome App or manual install)
- FF/Chome support (IE9 works but is a little rough around the edges)
- Sortable Columns
- Light/Dark Theme
- Last.fm support
- Autopilot Mode (click one button and songs continue to play)
- AutoSave Mode (saves position and current playlist if you close or refresh your browser)
You will need a Subsonic server to be able to play your own music. Subsonic is a free, web-based media streamer, providing ubiquitous access to your music. Use it to share your music with friends, or to listen to your own music while at work. Please see http://www.subsonic.org
Chrome App: https://chrome.google.com/webstore/detail/minisub/jccdpflnecheidefpofmlblgebobbloc
Forum: http://forum.subsonic.org/forum/viewforum.php?f=12
Donations: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VMTENRSJWQ234
Change Log inside...
**Permissions**
* You will have to allow MiniSub to "Access your data on all websites". This is required because your Subsonic server could be at any URL `http://*/*` or `https://*/*`
* I NEVER access, read, modify, store, or transmit your personal data.
* [https://github.com/tsquillario/MiniSub/blob/master/manifest.json](https://github.com/tsquillario/MiniSub/blob/master/manifest.json)
* [http://developer.chrome.com/extensions/xhr.html](http://developer.chrome.com/extensions/xhr.html)

View file

@ -1,51 +1,36 @@
=======
MiniSub
=======
**HTML5 Mini Player for Subsonic**
MiniSub - HTML5 Mini Player for Subsonic
Imagine you can stream all your music from home, to any device, where ever you
are. That is Subsonic! Now imagine having a Web App to stream your music that
is as beautiful and well designed as it is functional, that is MiniSub!
Imagine you can stream all your music from home, to any device, where ever you are. That is Subsonic! Now imagine having a Web App to stream your music that is as beautiful and well designed as it is functional, that is MiniSub!
MiniSub is an HTML5 Mini Player for the Subsonic streaming server.
MiniSub is an HTML5 Web Player for the Subsonic streaming server.
Features
````````
* Flexible Layout (will scale to whatever size your browser window is)
* Customizable (features can be turned on/off and settings are saved)
* Keyboard shortcuts (back, forward, play/pause, skip to artist)
* Playlists (create new, add to existing, delete)
* Artist/Album Ratings
* Chat and Now Playing
* Last.fm
* Supports Podcasts.
- HTML5 Audio with Flash fallback (provided by the SoundManager2 library)
- Flexible Layout (will scale to whatever size your browser window is)
- Keyboard shortcuts (back, forward, play/pause, skip to artist, volume control)
- Playlist support (create new, add to existing, delete)
- Podcast support (includes description field on hover)
- Favorite/Starred support for Albums & Songs
- Chat support (when the chat window is open it will auto-update in the background)
- Now Playing support (see what others are streaming at the moment)
- Easy installation (Chrome App or manual install)
- FF/Chome support (IE9 works but is a little rough around the edges)
- Sortable Columns
- Light/Dark Theme
- Last.fm support
- Autopilot Mode (click one button and songs continue to play)
- AutoSave Mode (saves position and current playlist if you close or refresh your browser)
You will need a Subsonic server to be able to play your own music. Subsonic is
a free, web-based media streamer, providing ubiquitous access to your music.
Use it to share your music with friends, or to listen to your own music while
at work. Please see http://www.subsonic.org
You will need a Subsonic server to be able to play your own music. Subsonic is a free, web-based media streamer, providing ubiquitous access to your music. Use it to share your music with friends, or to listen to your own music while at work. Please see http://www.subsonic.org
Chrome App: https://chrome.google.com/webstore/detail/minisub/jccdpflnecheidefpofmlblgebobbloc
Forum: http://forum.subsonic.org/forum/viewforum.php?f=12
Donations: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VMTENRSJWQ234
Change Log inside...
License
```````
Copyright (c) 2011-2012 Trevor Squillario
https://github.com/tsquillario/MiniSub
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
**Permissions**
* You will have to allow MiniSub to "Access your data on all websites". This is required because your Subsonic server could be at any URL `http://*/*` or `https://*/*`
* I NEVER access, read, modify, store, or transmit your personal data.
* [https://github.com/tsquillario/MiniSub/blob/master/manifest.json](https://github.com/tsquillario/MiniSub/blob/master/manifest.json)
* [http://developer.chrome.com/extensions/xhr.html](http://developer.chrome.com/extensions/xhr.html)

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

View file

@ -17,6 +17,7 @@
<script src="js/plugins/jquery.dateFormat-1.0.js" type="text/javascript"></script>
<script src="js/plugins/jquery.disable.text.select.pack.js" type="text/javascript"></script>
<script src="js/plugins/jquery.hotkeys.js" type="text/javascript"></script>
<script src="js/plugins/jquery.stupidtable.js" type="text/javascript"></script>
<script src="js/plugins/jquery.linkify-1.0-min.js" type="text/javascript"></script>
<script src="js/plugins/jquery.periodic.js" type="text/javascript"></script>
<script src="js/plugins/jquery.scrollTo-1.4.2-min.js" type="text/javascript"></script>
@ -54,23 +55,23 @@
<a href="#" class="button" id="action_IncreaseWidth" title="Increase Width"><img src="images/plus_8x8.png" /></a>
</div>
<div class="subactions floatleft">
<div id="songactions">
<a href="#" class="button" id="action_SelectAll" title="Select All">All</a>
<a href="#" class="button" id="action_SelectNone" title="Select None">None</a>
<a href="#" class="button" id="action_AddToPlaylist" title="Add Selected To Playlist">+ Playlist</a>
<div id="songActions">
<a href="#" class="button disabled" id="action_SelectAll" title="Select All">All</a>
<a href="#" class="button disabled" id="action_SelectNone" title="Select None">None</a>
<a href="#" class="button disabled" id="action_AddToPlaylist" title="Add Selected To Playlist">+ Playlist</a>
<div id="submenu_AddToPlaylist" class="submenu shadow" style="display: none;"></div>
<a href="#" class="button" id="action_AddToCurrent" title="Add Selected To Current Playlist">+ Current</a>
<a href="#" class="button" id="action_AddAllToCurrent" title="Add All to Current Playlist">+All</a>
<a href="#" class="button" id="action_PlayAlbum" title="Play Album"><img src="images/play_gl_6x8.png" /></a>
<a href="#" class="button disabled" id="action_AddToCurrent" title="Add Selected To Current Playlist">+ Current</a>
<a href="#" class="button disabled" id="action_AddAllToCurrent" title="Add All to Current Playlist">+All</a>
<a href="#" class="button disabled" id="action_PlayAlbum" title="Play Album"><img src="images/play_gl_6x8.png" /></a>
</div>
<input type="text" id="Search" class="medium" />
<input type="text" id="Search" class="medium" title="Wildcards (*) supported"/>
<select id="SearchType" name="SearchType">
<option value="song">Song</option>
<option value="album">Album</option>
</select>
<a href="#" class="button" id="action_Search" title="Search"><img class="pad" src="images/magnifying_glass_alt_12x12.png" /></a>
</div>
<div id="Albums" class="lgsection floatleft">
<div id="Albums" class="section lgsection floatleft">
<div class="loading"></div>
<div id="Artists" class="smsection floatleft" tabindex="0">
<div class="padder">
@ -91,11 +92,11 @@
<div id="BottomContainer"><ul id="BottomIndex"></ul></div>
</div>
<table id="AlbumContainer" class="simplelist songlist noselect" cellspacing="1">
<thead id="AlbumHeader"></thead>
<tbody id="AlbumRows"></tbody>
<thead></thead>
<tbody></tbody>
</table>
</div>
<div id="status_Library" class="status alignleft" data-type="" data-offset=""><img src="images/arrow_left_gl_8x8.png" /> <a id="action_PreviousAlbumList" class="previous" href="#">Previous</a> | <a id="action_NextAlbumList" class="next" href="#">Next</a> <img src="images/arrow_right_gl_8x8.png" /></div>
</div>
<div id="tabCurrent" class="tabcontent">
<div class="actions floatleft">
@ -103,19 +104,21 @@
<a href="#" class="button" id="action_Empty" title="Remove All">Empty</a>
</div>
<div class="subactions floatleft">
<a href="#" class="button" id="action_CurrentSelectAll" title="Select All">All</a>
<a href="#" class="button" id="action_CurrentSelectNone" title="Select None">None</a>
<a href="#" class="button" id="action_AddCurrentToPlaylist" title="Add Selected To Playlist">+ Playlist</a>
<div id="currentActions">
<a href="#" class="button disabled" id="action_CurrentSelectAll" title="Select All">All</a>
<a href="#" class="button disabled" id="action_CurrentSelectNone" title="Select None">None</a>
<a href="#" class="button disabled" id="action_AddCurrentToPlaylist" title="Add Selected To Playlist">+ Playlist</a>
<div id="submenu_AddCurrentToPlaylist" class="submenu shadow" style="display: none;"></div>
<a href="#" class="button" id="action_AutoPilot" title="Autopilot"><img class="pad" src="images/steering_wheel_gd_12x12.png" /></a>
</div>
<div id="CurrentPlaylist" class="fullsection floatleft noselect">
</div>
<div id="CurrentPlaylist" class="section fullsection floatleft noselect">
<table id="CurrentPlaylistContainer" class="simplelist songlist" cellspacing="1">
<thead></thead>
<tbody></tbody>
</table>
</div>
<div id="status"></div>
<div id="status_Current" class="status">0 song(s), 00:00:00 total time</div>
</div>
<div id="tabPlaylists" class="tabcontent">
<div class="actions floatleft">
@ -129,7 +132,7 @@
<a href="#" class="button" id="action_RemoveSongs" title="Remove selected song(s) from playlist">Remove Song(s)</a>
</div>
<div class="clear"></div>
<div id="Tracks" class="lgsection floatleft noselect">
<div id="Tracks" class="section lgsection floatleft noselect">
<div class="loading"></div>
<div id="Playlists" class="smsection floatleft noselect">
<div class="padder">
@ -146,6 +149,7 @@
<tbody></tbody>
</table>
</div>
<div id="status_Playlists" class="status">0 song(s), 00:00:00 total time</div>
</div>
<div id="tabPodcasts" class="tabcontent">
<div class="actions floatleft">
@ -154,7 +158,7 @@
<div class="subactions floatleft">
</div>
<div class="clear"></div>
<div id="Podcasts" class="lgsection floatleft noselect">
<div id="Podcasts" class="section lgsection floatleft noselect">
<div class="loading"></div>
<div id="PodcastList" class="smsection floatleft noselect">
<div class="padder">
@ -167,6 +171,7 @@
<tbody></tbody>
</table>
</div>
<div id="status_Podcasts" class="status">0 song(s), 00:00:00 total time</div>
</div>
<div id="tabPreferences" class="tabcontent">
<div class="actions floatleft">
@ -174,7 +179,7 @@
<a href="#" class="button" id="SavePreferences" title="Save Preferences">Save</a>
</div>
<div class="clear"></div>
<div class="lgsection floatleft">
<div class="section lgsection floatleft">
<div class="subsection floatleft">
<h3 class="title">Login</h3>
<label for="Username">Username <span class="red">*</span></label><br />
@ -272,10 +277,15 @@
<span class="changes">- </span>
</li>
-->
<li class="log"><span class="version">10/15/2012 - 2.2.0</span>
<span class="changes">- Column sorting for all headers in all tables! (Thanks to <a href="https://github.com/joequery/Stupid-Table-Plugin" target="_blank">joequery</a>)</span>
<span class="changes">- Fancy Webkit styled scrollbars for those of you with browsers that don't suck (Works with Dark theme)</span>
<span class="changes">- Auto Albums feature Next & Previous links to page through results</span>
<span class="changes">- Forced to include permissions ("http://*/*" & "https://*/*"). Your Subsonic server can be at any URL. Will be asked to accept "Access your data on all websites".</span>
</li>
<li class="log"><span class="version">10/8/2012 - 2.1.5</span>
<span class="changes">- Added song count and total time to Current Playlist</span>
<span class="changes">- Migrated to version 2 of Chrome App manifest.json</span>
<span class="changes">- Added to manifest.json > "content_security_policy": "script-src 'self' https://*/; object-src 'self'"</span>
</li>
<li class="log"><span class="version">10/7/2012 - 2.1.2</span>
<span class="changes">- Current Playlist will stay focused on the current track (Thanks <a href="https://github.com/tsquillario/MiniSub/issues/42" target="_blank">Concept211</a>)</span>

View file

@ -10,6 +10,7 @@ var passwordenc;
var server;
var smwidth;
var volume = 50;
var currentVersion = '2.2.0';
function getCookie(value) {
if ($.cookie(value)) {
@ -96,6 +97,9 @@ if (getCookie('password')) {
setCookie('passwordenc', 'enc:' + HexEncode(getCookie('password')));
setCookie('password', null);
}
if (getCookie('Volume')) {
volume = parseInt(getCookie('Volume'));
}
var version = '1.6.0';
function loadTabContent(tab) {
@ -113,19 +117,28 @@ function loadTabContent(tab) {
case '#tabCurrent':
if (debug) { console.log("TAG CURRENT"); }
var header = generateSongHeaderHTML();
$("#CurrentPlaylistContainer thead").html(header);
window.location.hash = '#tabCurrent';
updateStatus(countCurrentPlaylist('#CurrentPlaylistContainer'));
$('#CurrentPlaylistContainer thead').html(header);
var count = $('#CurrentPlaylistContainer tbody tr.song').size();
updateStatus('#status_Current', countCurrentPlaylist('#CurrentPlaylistContainer'));
if (count > 0) {
$('#currentActions a.button').removeClass('disabled');
}
var songid = $('#CurrentPlaylistContainer tbody tr.playing').attr('childid');
if (songid !== undefined) {
$('#CurrentPlaylist').scrollTo($('#' + songid), 400);
}
break;
case '#tabPlaylists':
if (debug) { console.log("TAG PLAYLIST"); }
loadPlaylists();
loadFolders();
loadAutoPlaylists();
updateStatus('#status_Playlists', countCurrentPlaylist('#TrackContainer'));
break;
case '#tabPodcasts':
if (debug) { console.log("TAG PODCAST"); }
loadPodcasts();
updateStatus('#status_Podcasts', countCurrentPlaylist('#PodcastContainer'));
break;
case '#tabVideos':
if (debug) { console.log("TAG VIDEOS"); }

View file

@ -81,7 +81,7 @@ function loadArtists(id, refresh) {
} else {
indexes[0] = data["subsonic-response"].indexes.child;
}
var appendto = '#AlbumRows';
var appendto = '#AlbumContainer tbody';
$(appendto).empty();
$.each(indexes, function (i, child) {
if (i % 2 === 0) {
@ -102,8 +102,6 @@ function loadArtists(id, refresh) {
var errorcode = data["subsonic-response"].error.code;
var errormsg = data["subsonic-response"].error.message;
alert('Status: ' + error + ', Code: ' + errorcode + ', Message: ' + errormsg);
//var errorhtml = '<li class=\"item\"><span>' + error + '</span></li>';
//$(errorhtml).appendTo("#IndexList");
}
}
});
@ -187,13 +185,13 @@ function getAlbums(id, action, appendto) {
timeout: 10000,
success: function (data) {
if (action == '') {
$('#AlbumRows').empty();
$('#AlbumContainer tbody').empty();
}
if (action === 'autoplay') {
$('#CurrentPlaylistContainer tbody').empty();
}
if (action == 'link') {
$('#AlbumRows').empty();
$('#AlbumContainer tbody').empty();
$('#action_tabLibrary').trigger('click');
}
if (data["subsonic-response"].directory.child !== undefined) {
@ -216,18 +214,21 @@ function getAlbums(id, action, appendto) {
}
if (child.isDir == true) { isDir = true; }
var html = generateRowHTML(child, appendto, rowcolor);
$(html).appendTo(appendto);
$(html).appendTo(appendto).hide().fadeIn('fast');
});
toggleAlbumListNextPrev('#status_Library', false, '', '');
if (appendto == '#CurrentPlaylistContainer') {
updateMessage(children.length + ' Song(s) Added');
}
if (appendto == '#AlbumRows' && isDir == true) {
if (appendto == '#AlbumContainer tbody' && isDir == true) {
header = generateAlbumHeaderHTML();
$('#songActions a.button').addClass('disabled');
}
if (appendto == '#AlbumRows' && isDir == false) {
if (appendto == '#AlbumContainer tbody' && isDir == false) {
header = generateSongHeaderHTML();
$('#songActions a.button').removeClass('disabled');
}
$("#AlbumHeader").html(header);
$("#AlbumContainer thead").html(header);
if (action == 'autoplay') {
autoPlay();
}
@ -235,23 +236,28 @@ function getAlbums(id, action, appendto) {
}
});
}
function getAlbumListBy(id) {
var size;
if (getCookie('AutoAlbumSize') === null) {
size = 15;
} else {
function getAlbumListBy(id, offset) {
var size, url;
if (getCookie('AutoAlbumSize')) {
size = getCookie('AutoAlbumSize');
} else {
size = 15;
}
if (offset > 0) {
url = baseURL + '/getAlbumList.view?u=' + username + '&p=' + password + '&v=' + version + '&c=' + applicationName + '&f=json&size=' + size + '&type=' + id + '&offset=' + offset
} else {
url = baseURL + '/getAlbumList.view?u=' + username + '&p=' + password + '&v=' + version + '&c=' + applicationName + '&f=json&size=' + size + '&type=' + id
}
$.ajax({
url: baseURL + '/getAlbumList.view?u=' + username + '&p=' + password + '&v=' + version + '&c=' + applicationName + '&f=json&size=' + size + '&type=' + id,
url: url,
method: 'GET',
dataType: 'json',
timeout: 10000,
success: function (data) {
if (data["subsonic-response"].albumList.album !== undefined) {
$("#AlbumRows").empty();
$("#AlbumContainer tbody").empty();
var header = generateAlbumHeaderHTML();
$("#AlbumHeader").html(header);
$("#AlbumContainer thead").html(header);
// There is a bug in the API that doesn't return a JSON array for one artist
var albums = [];
if (data["subsonic-response"].albumList.album.length > 0) {
@ -274,14 +280,34 @@ function getAlbumListBy(id) {
if (album.isDir === true) {
albumhtml = generateAlbumHTML(rowcolor, album.id, album.parent, album.coverArt, album.title, album.artist, album.userRating, starred);
}
$(albumhtml).appendTo("#AlbumRows");
$(albumhtml).appendTo("#AlbumContainer tbody").hide().fadeIn('fast');
});
$('#songActions a.button').addClass('disabled');
toggleAlbumListNextPrev('#status_Library', true, id, offset);
} else {
$('#AlbumRows').empty();
$('#AlbumContainer tbody').empty();
}
}
});
}
function toggleAlbumListNextPrev(el, on, type, offset) {
if (el != '') {
if (on) {
$(el).addClass('on');
$('#status_Library').data('type', type);
if (offset === undefined) {
$('#status_Library').data('offset', '0');
} else {
$('#status_Library').data('offset', offset);
}
} else {
$(el).removeClass('on');
$(el).stop().fadeOut();
$('#status_Library').data('type', '');
$('#status_Library').data('offset', '0');
}
}
}
function getRandomSongList(action, appendto, genre, folder) {
var size, gstring;
gstring = '';
@ -342,7 +368,7 @@ function getRandomSongList(action, appendto, genre, folder) {
$(html).appendTo(appendto);
});
if (appendto === '#TrackContainer tbody') {
updateMessage(countCurrentPlaylist('#TrackContainer'));
updateStatus('#status_Playlists', countCurrentPlaylist('#TrackContainer'));
}
if (appendto === '#CurrentPlaylistContainer tbody') {
updateMessage(items.length + ' Song(s) Added');
@ -443,7 +469,7 @@ function getStarred(action, appendto, type) {
$(html).appendTo(appendto);
});
if (appendto == '#TrackContainer tbody') {
updateMessage(countCurrentPlaylist('#TrackContainer'));
updateStatus('#status_Playlists', countCurrentPlaylist('#TrackContainer'));
}
if (appendto == '#CurrentPlaylistContainer tbody') {
updateMessage(items.length + ' Song(s) Added');
@ -526,31 +552,32 @@ function search(type, query) {
dataType: 'json',
timeout: 10000,
success: function (data) {
$("#AlbumContainer tbody").empty();
if (data["subsonic-response"].searchResult2 !== "") {
$("#AlbumRows").empty();
var header;
var children = [];
if (type === 'song') {
header = generateSongHeaderHTML();
if (data["subsonic-response"].searchResult2.song !== undefined) {
header = generateSongHeaderHTML();
if (data["subsonic-response"].searchResult2.song.length > 0) {
children = data["subsonic-response"].searchResult2.song;
} else {
children[0] = data["subsonic-response"].searchResult2.song;
}
$("#AlbumContainer thead").html(header);
}
}
if (type === 'album') {
header = generateAlbumHeaderHTML();
if (data["subsonic-response"].searchResult2.album !== undefined) {
header = generateAlbumHeaderHTML();
if (data["subsonic-response"].searchResult2.album.length > 0) {
children = data["subsonic-response"].searchResult2.album;
} else {
children[0] = data["subsonic-response"].searchResult2.album;
}
$("#AlbumContainer thead").html(header);
}
}
$("#AlbumHeader").html(header);
$.each(children, function (i, child) {
if (i % 2 === 0) {
rowcolor = 'even';
@ -569,7 +596,7 @@ function search(type, query) {
if (child.duration !== undefined) { duration = child.duration; } else { duration = ''; }
albumhtml = generateSongHTML(rowcolor, child.id, child.parent, track, child.title, '', child.artist, child.album, child.coverArt, child.userRating, starred, duration);
}
$(albumhtml).appendTo("#AlbumRows");
$(albumhtml).appendTo("#AlbumContainer tbody");
});
}
}
@ -648,6 +675,84 @@ function loadPlaylists(refresh) {
});
}
}
function savePlaylist(playlistid) {
var songs = [];
$('#TrackContainer tr.song').each(function (index) {
songs.push($(this).attr('childid'));
});
if (songs.length > 0) {
$.ajax({
type: 'GET',
url: baseURL + '/createPlaylist.view?u=' + username + '&p=' + password,
dataType: 'json',
timeout: 10000,
data: { v: version, c: applicationName, f: "json", playlistId: playlistid, songId: songs },
success: function () {
getPlaylist(playlistid);
updateMessage('Playlist Updated!');
},
traditional: true // Fixes POST with an array in JQuery 1.4
});
}
}
function getPlaylist(id, action, appendto) {
$.ajax({
url: baseURL + '/getPlaylist.view?u=' + username + '&p=' + password + '&v=' + version + '&c=' + applicationName + '&f=json&id=' + id,
method: 'GET',
dataType: 'json',
timeout: 10000,
success: function (data) {
if (data["subsonic-response"].playlist.entry !== undefined) {
if (appendto === '#TrackContainer tbody') {
$(appendto).empty();
var header = generateSongHeaderHTML();
$("#TrackContainer thead").html(header);
}
if (action === 'autoplay') {
$(appendto).empty();
}
// There is a bug in the API that doesn't return a JSON array for one artist
var children = [];
var playlist = data["subsonic-response"].playlist;
if (playlist.entry.length > 0) {
children = playlist.entry;
} else {
children[0] = playlist.entry;
}
var rowcolor;
var html;
var count = children.length;
$.each(children, function (i, child) {
if (i % 2 === 0) {
rowcolor = 'even';
} else {
rowcolor = 'odd';
}
var track, starred, duration;
if (child.starred !== undefined) { starred = true; } else { starred = false; }
if (child.track === undefined) { track = "&nbsp;"; } else { track = child.track; }
if (child.duration !== undefined) { duration = child.duration; } else { duration = ''; }
html = generateSongHTML(rowcolor, child.id, child.parent, track, child.title, '', child.artist, child.album, child.coverArt, child.userRating, starred, duration);
$(html).appendTo(appendto);
});
if (appendto === '#TrackContainer tbody') {
updateStatus('#status_Playlists', countCurrentPlaylist('#TrackContainer'));
}
if (appendto === '#CurrentPlaylistContainer tbody') {
updateMessage(children.length + ' Song(s) Added');
}
if (action === 'autoplay') {
autoPlay();
}
} else {
if (appendto === '#TrackContainer tbody') {
$(appendto).empty();
}
}
}
});
}
function loadPlaylistsForMenu(menu) {
$('#' + menu).empty();
$.ajax({
@ -814,6 +919,7 @@ function addToCurrent(addAll) {
$(this).clone().appendTo('#CurrentPlaylistContainer tbody');
});
}
$('#CurrentPlaylistContainer tbody tr.song').removeClass('selected');
updateMessage(count + ' Song(s) Added');
}
}
@ -870,6 +976,25 @@ function loadCurrentPlaylist() {
if (debug) { console.log('HTML5::loadStorage not supported on your browser' + html.length + ' characters'); }
}
}
function saveTrackPosition() {
var el = $('#songdetails_song');
var songid = el.attr('childid');
if (songid !== undefined) {
var albumid = el.attr('parentid');
var sm = soundManager.getSoundById('audio');
var position = sm.position;
if (position != null && position >= 5000) {
var currentSong = {
songid: songid,
albumid: albumid,
position: position
};
setCookie('CurrentSong', JSON.stringify(currentSong));
saveCurrentPlaylist();
}
}
if (debug) { console.log('Saving Track Position: songid:' + songid + ', albumid:' + albumid + ', position:' + position); }
}
function downloadItem(id, type) {
var url;
if (type == 'item' && id) {
@ -883,84 +1008,7 @@ function downloadItem(id, type) {
window.location = url;
}
}
function savePlaylist(playlistid) {
var songs = [];
$('#TrackContainer tr.song').each(function (index) {
songs.push($(this).attr('childid'));
});
if (songs.length > 0) {
$.ajax({
type: 'GET',
url: baseURL + '/createPlaylist.view?u=' + username + '&p=' + password,
dataType: 'json',
timeout: 10000,
data: { v: version, c: applicationName, f: "json", playlistId: playlistid, songId: songs },
success: function () {
getPlaylist(playlistid);
updateMessage('Playlist Updated!');
},
traditional: true // Fixes POST with an array in JQuery 1.4
});
}
}
function getPlaylist(id, action, appendto) {
$.ajax({
url: baseURL + '/getPlaylist.view?u=' + username + '&p=' + password + '&v=' + version + '&c=' + applicationName + '&f=json&id=' + id,
method: 'GET',
dataType: 'json',
timeout: 10000,
success: function (data) {
if (data["subsonic-response"].playlist.entry !== undefined) {
if (appendto === '#TrackContainer tbody') {
$(appendto).empty();
var header = generateSongHeaderHTML();
$("#TrackContainer thead").html(header);
}
if (action === 'autoplay') {
$(appendto).empty();
}
// There is a bug in the API that doesn't return a JSON array for one artist
var children = [];
var playlist = data["subsonic-response"].playlist;
if (playlist.entry.length > 0) {
children = playlist.entry;
} else {
children[0] = playlist.entry;
}
var rowcolor;
var html;
var count = children.length;
$.each(children, function (i, child) {
if (i % 2 === 0) {
rowcolor = 'even';
} else {
rowcolor = 'odd';
}
var track, starred, duration;
if (child.starred !== undefined) { starred = true; } else { starred = false; }
if (child.track === undefined) { track = "&nbsp;"; } else { track = child.track; }
if (child.duration !== undefined) { duration = child.duration; } else { duration = ''; }
html = generateSongHTML(rowcolor, child.id, child.parent, track, child.title, '', child.artist, child.album, child.coverArt, child.userRating, starred, duration);
$(html).appendTo(appendto);
});
if (appendto === '#TrackContainer tbody') {
updateMessage(countCurrentPlaylist('#TrackContainer'));
}
if (appendto === '#CurrentPlaylistContainer tbody') {
updateMessage(children.length + ' Song(s) Added');
}
if (action === 'autoplay') {
autoPlay();
}
} else {
if (appendto === '#TrackContainer tbody') {
$(appendto).empty();
}
}
}
});
}
function loadPodcasts(refresh) {
if (debug) { console.log("LOAD PODCASTS"); }
@ -1040,7 +1088,6 @@ function getPodcast(id, action, appendto) {
var count = 0;
$.each(children, function (i, child) {
if (child.status == "skipped") return; // Skip podcasts that are not yet downloaded
if (i % 2 === 0) {
rowcolor = 'even';
} else {
@ -1050,16 +1097,17 @@ function getPodcast(id, action, appendto) {
var description = 'Published: ' + date + '\n\n';
description += child.description;
var starred, duration;
var starred, duration, publishdate;
if (child.starred !== undefined) { starred = true; } else { starred = false; }
if (child.duration !== undefined) { duration = child.duration; } else { duration = ''; }
if (child.publishDate !== undefined) { publishdate = child.publishDate.substring(0, child.publishDate.indexOf(" ")); } else { publishdate = ''; }
var time = secondsToTime(child.duration);
html = generateSongHTML(rowcolor, child.streamId, child.parent, child.track, child.title, description, child.artist, child.album, child.coverArt, child.userRating, starred, duration);
html = generateSongHTML(rowcolor, child.streamId, child.parent, publishdate, child.title, description, child.artist, child.album, child.coverArt, child.userRating, starred, duration);
$(html).appendTo(appendto);
count++;
});
if (appendto === '#PodcastContainer tbody') {
updateMessage(count + ' Song(s)');
updateStatus('#status_Podcasts', countCurrentPlaylist('#PodcastContainer'));
}
if (appendto === '#CurrentPlaylistContainer tbody') {
updateMessage(count + ' Song(s) Added');

View file

@ -14,7 +14,7 @@ function generateRowHTML(child, appendto, rowcolor) {
}
function generateAlbumHeaderHTML() {
var html;
html = '<tr><th></th><th></th><th>Album</th><th>Artist</th></tr>';
html = '<tr><th></th><th></th><th class=\"type-string\">Album</th><th class=\"type-string\">Artist</th></tr>';
return html;
}
function generateAlbumHTML(rowcolor, childid, parentid, coverart, title, artist, rating, starred) {
@ -41,7 +41,7 @@ function generateAlbumHTML(rowcolor, childid, parentid, coverart, title, artist,
}
function generateSongHeaderHTML() {
var html;
html = '<tr><th></th><th>Track</th><th>Title</th><th>Artist</th><th>Album</th><th class=\"alignright\">Time</th></tr>';
html = '<tr><th></th><th class=\"type-int\">Track</th><th class=\"type-string\">Title</th><th class=\"type-string\">Artist</th><th class=\"type-string\">Album</th><th class=\"alignright\">Time</th></tr>';
return html;
}
function generateSongHTML(rowcolor, childid, parentid, track, title, description, artist, album, coverart, rating, starred, duration) {
@ -76,11 +76,12 @@ function generateSongHTML(rowcolor, childid, parentid, track, title, description
} else {
coverartSrc = baseURL + '/getCoverArt.view?v=' + version + '&c=' + applicationName + '&f=json&size=25&id=' + coverart;
}
html += '<td class=\"album\"><a href="#" class=\"albumlink\"><img src=\"' + coverartSrc + '\" />' + album + '</a></td>';
html += '<td class=\"album\" data-order-by=\"' + album + '\"><a href="#" class=\"albumlink\"><img src=\"' + coverartSrc + '\" />' + album + '</a></td>';
html += '<td class=\"time\">' + time + '</td>';
html += '</tr>';
return html;
}
// Depreciated: 10/17/2012
function refreshRowColor(el) {
$.each($(el + ' tr.song'), function (i) {
$(this).removeClass('even odd');

View file

@ -19,7 +19,7 @@ function playSong(el, songid, albumid, position, loadonly) {
}
$.each(children, function (i, child) {
if (child.id == songid) {
title = child.title;
title = child.title.toString();
artist = child.artist;
album = child.album;
coverart = child.coverArt;
@ -67,6 +67,7 @@ function playSong(el, songid, albumid, position, loadonly) {
url: baseURL + '/stream.view?u=' + username + '&p=' + password + '&v=' + version + '&c=' + applicationName + '&id=' + songid + '&salt=' + salt,
stream: true,
type: 'audio/mp3',
multiShot: false,
whileloading: function () {
//if (debug) { console.log('loaded:' + this.bytesLoaded + ' total:' + this.bytesTotal); }
var percent = this.bytesLoaded / this.bytesTotal;
@ -105,9 +106,8 @@ function playSong(el, songid, albumid, position, loadonly) {
scrubber.click(function (e) {
var x = (e.pageX - this.offsetLeft) / scrubber.width();
var position = Math.round(dp * 1000 * x);
soundManager.play('audio', {
position: position
});
var s = soundManager.getSoundById('audio');
s.setPosition(position);
});
scrubber.mouseover(function (e) {
$('.audiojs .scrubber').stop().animate({ height: '8px' });
@ -141,10 +141,11 @@ function playSong(el, songid, albumid, position, loadonly) {
});
if (position == 0) {
soundManager.play('audio');
soundManager.setVolume('audio', volume);
} else {
var p = position;
//soundManager.play('audio', { position: p });
seekAndPlay('audio', p);
soundManager.setVolume('audio', volume);
}
var submenu = $('div#submenu_CurrentPlaylist');
if (submenu.is(":visible")) {
@ -175,9 +176,8 @@ function playSong(el, songid, albumid, position, loadonly) {
}
function seekAndPlay(soundID, soundPosition) {
var s = soundManager.getSoundById(soundID);
if (!s) {
return false;
}
if (s) {
s.unload();
if (s.readyState === 0) { // hasn't started loading yet...
// load the whole sound, and play when it's done
s.load({
@ -193,6 +193,7 @@ function seekAndPlay(soundID, soundPosition) {
position: soundPosition
});
}
}
}
function scrobbleSong(submission) {
var songid = $('#songdetails_song').attr('childid');
@ -320,22 +321,3 @@ function nextPlay() {
//song = $('#CurrentPlaylistContainer tr.playing').next();
}
}
function saveTrackPosition() {
var el = $('#songdetails_song');
var songid = el.attr('childid');
if (songid !== undefined) {
var albumid = el.attr('parentid');
var sm = soundManager.getSoundById('audio');
var position = sm.position;
if (position != null && position >= 5000) {
var currentSong = {
songid: songid,
albumid: albumid,
position: position
};
setCookie('CurrentSong', JSON.stringify(currentSong));
saveCurrentPlaylist();
}
}
if (debug) { console.log('Saving Track Position: songid:' + songid + ', albumid:' + albumid + ', position:' + position); }
}

View file

@ -112,12 +112,15 @@ function updateMessage(msg) {
msgIndex++;
}
}
function updateStatus(msg) {
$('#status').html(msg);
if ($('#tabLibrary').not(':visible')) {
if ($('#status').html() != '') {
$('#status').fadeIn();
function updateStatus(el, msg) {
if (msg == '') {
$(el).html('0 song(s), 00:00:00 total time');
} else {
$(el).html(msg);
}
if ($(el).html() != '') {
$(el).addClass('on');
$(el).fadeIn();
}
}
// Convert to unicode support
@ -254,6 +257,21 @@ function checkVersion(runningVersion, minimumVersion) {
return false;
}
}
function checkVersionNewer(runningVersion, newVersion) {
if (runningVersion.major < newVersion.major) {
return true;
} else {
if (runningVersion.minor < newVersion.minor) {
return true;
} else {
if (runningVersion.patch < newVersion.patch) {
return true;
} else {
return false;
}
}
}
}
function switchTheme(theme) {
switch (theme) {
case 'dark':

View file

@ -0,0 +1,141 @@
// Stupid jQuery table plugin.
// Call on a table
// sortFns: Sort functions for your datatypes.
(function ($) {
$.fn.stupidtable = function (sortFns) {
var table = this; sortFns = sortFns || {};
// ==================================================== //
// Utility functions //
// ==================================================== //
// Merge sort functions with some default sort functions.
sortFns = $.extend({}, {
"int": function (a, b) { return parseInt(a, 10) - parseInt(b, 10); },
"float": function (a, b) { return parseFloat(a) - parseFloat(b); },
"string": function (a, b) { if (a < b) return -1; if (a > b) return +1; return 0; }
}, sortFns);
// Array comparison. See http://stackoverflow.com/a/8618383
var arrays_equal = function (a, b) { return !!a && !!b && !(a < b || b < a); }
// Return the resulting indexes of a sort so we can apply
// this result elsewhere. This returns an array of index numbers.
// return[0] = x means "arr's 0th element is now at x"
var sort_map = function (arr, sort_function) {
var sorted = arr.slice(0).sort(sort_function);
var map = [];
var index = 0;
for (var i = 0; i < arr.length; i++) {
index = $.inArray(arr[i], sorted);
// If this index is already in the map, look for the next index.
// This handles the case of duplicate entries.
while ($.inArray(index, map) != -1) {
index++;
}
map.push(index);
}
return map;
}
// Apply a sort map to the array.
var apply_sort_map = function (arr, map) {
var clone = arr.slice(0);
for (var i = 0; i < map.length; i++) {
newIndex = map[i];
clone[newIndex] = arr[i];
}
return clone;
}
// Returns true if array is sorted, false otherwise.
// Checks for both ascending and descending
var is_sorted_array = function (arr, sort_function) {
var clone = arr.slice(0);
var reversed = arr.slice(0).reverse();
var sorted = arr.slice(0).sort(sort_function);
// Check if the array is sorted in either direction.
return arrays_equal(clone, sorted) || arrays_equal(reversed, sorted);
}
// ==================================================== //
// Begin execution! //
// ==================================================== //
// Do sorting when THs are clicked
table.delegate("th", "click", function () {
var trs = table.find("tbody tr");
var i = $(this).index();
var classes = $(this).attr("class");
var type = null;
var sort = {
asc: 'ascending',
desc: 'descending'
};
var direction = sort.asc;
var total = trs.length;
if (total > 0) {
if (classes) {
classes = classes.split(/\s+/);
for (var j = 0; j < classes.length; j++) {
if (classes[j].search("type-") != -1) {
type = classes[j];
break;
}
}
if (type) {
type = type.split('-')[1];
}
}
if (type) {
table.find('th').removeClass('sorted');
var sortMethod = sortFns[type];
// Gather the elements for this column
column = [];
// Push either the value of the 'data-order-by' attribute if specified
// or just the text() value in this column to column[] for comparison.
trs.each(function (index, tr) {
var e = $(tr).children().eq(i);
var order_by = e.attr('data-order-by') || e.text();
column.push(order_by);
});
// If the column is already sorted, just reverse the order. The sort
// map is just reversing the indexes.
if (is_sorted_array(column, sortMethod)) {
if ($(this).hasClass(sort.desc)) {
direction = sort.asc;
} else {
direction = sort.desc
}
table.find('th').removeClass(sort.asc + ' ' + sort.desc);
column.reverse();
var theMap = [];
for (var i = column.length - 1; i >= 0; i--) {
theMap.push(i);
}
}
else {
direction = sort.asc;
// Get a sort map and apply to all rows
theMap = sort_map(column, sortMethod);
}
var sortedTRs = $(apply_sort_map(trs, theMap));
// Replace the content of tbody with the sortedTRs. Strangely (and
// conveniently!) enough, .append accomplishes this for us.
table.find("tbody").append(sortedTRs);
$(this).addClass('sorted');
$(this).addClass(direction);
}
}
});
}
})(jQuery);

View file

@ -63,7 +63,7 @@ function resizeContent() {
}
}
var tabwidth = $('.tabcontent').width();
$('#AlbumContainer, #TrackContainer, #PodcastContainer, #CurrentPlaylistContainer').css({ 'width': (tabwidth - smwidth - 40) + 'px' });
$('#AlbumContainer, #TrackContainer, #PodcastContainer, #CurrentPlaylistContainer').css({ 'width': (tabwidth - smwidth - 45) + 'px' });
$('#CurrentPlaylistContainer').css({ 'width': (tabwidth - 30) + 'px' });
$('#player').css({ 'width': tabwidth + 'px' });
}
@ -78,6 +78,6 @@ function resizeSMSection(x) {
$('#BottomContainer').css({ 'width': (newsmwidth - 23) + 'px' });
setCookie('defaultsmwidth', newwidth);
var ulwidth = newsmwidth + 6;
$('#AlbumContainer, #TrackContainer, #PodcastContainer').css({ 'margin-left': ulwidth + 'px' });
$('#AlbumContainer, #TrackContainer, #PodcastContainer').css({ 'margin-left': (ulwidth + 15) + 'px' });
}
}

View file

@ -54,6 +54,22 @@
if (getCookie('AutoPilot')) {
setCookie('AutoPilot', null)
}
// Version check
if (getCookie('CurrentVersion')) {
if (checkVersionNewer(parseVersionString(getCookie('CurrentVersion')), parseVersionString(currentVersion))) {
updateMessage('MiniSub updated to ' + currentVersion);
setCookie('CurrentVersion', currentVersion);
}
} else {
setCookie('CurrentVersion', currentVersion);
}
// Table Sorting
$('#CurrentPlaylistContainer').stupidtable();
$('#TrackContainer').stupidtable();
$('#PodcastContainer').stupidtable();
$('#AlbumContainer').stupidtable();
// Tabs
$('.tabcontent').hide(); //Hide all content
@ -91,12 +107,14 @@
// Tabs - Click Event
$("ul.tabs li a").click(function () {
var currentTab = window.location.hash;
var activeTab = $(this).attr("href"); //Find the href attribute value to identify the active tab + content
if (currentTab != activeTab) {
$("ul.tabs li a").removeClass("active"); //Remove any "active" class
$(this).addClass("active"); //Add "active" class to selected tab
$(".tabcontent").hide(); //Hide all tab content
var activeTab = $(this).attr("href"); //Find the href attribute value to identify the active tab + content
$(activeTab).fadeIn('fast'); //Fade in the active ID content
}
loadTabContent(activeTab);
});
@ -140,6 +158,7 @@
if (volume <= 100 && volume > 0 && source == '') {
volume += -10;
soundManager.setVolume('audio', volume);
setCookie('Volume', volume);
updateMessage('Volume: ' + volume + '%');
}
}
@ -147,6 +166,7 @@
if (volume < 100 && volume >= 0 && source == '') {
volume += 10;
soundManager.setVolume('audio', volume);
setCookie('Volume', volume);
updateMessage('Volume: ' + volume + '%');
}
}
@ -164,7 +184,7 @@
$('#AutoAlbumContainer li').removeClass('selected');
$('#ArtistContainer li').removeClass('selected');
$(this).addClass('selected');
getAlbums($(this).attr("id"), '', '#AlbumRows');
getAlbums($(this).attr("id"), '', '#AlbumContainer tbody');
});
$('#BottomIndex li a').live('click', function () {
var el = 'a[name = "index_' + $(this).text() + '"]';
@ -213,7 +233,14 @@
$('tr.album').live('click', function (e) {
var albumid = $(this).attr('childid');
var artistid = $(this).attr('parentid');
getAlbums(albumid, '', '#AlbumRows');
getAlbums(albumid, '', '#AlbumContainer tbody');
return false;
});
$('tr.album').live('click', function (e) {
var albumid = $(this).attr('childid');
var artistid = $(this).attr('parentid');
getAlbums(albumid, '', '#AlbumContainer tbody');
return false;
});
@ -292,7 +319,6 @@
$('table.songlist tr.song a.remove').live('click', function (event) {
var track = $(this).parent().parent();
$(track).remove();
refreshRowColor('table.songlist');
return false;
});
$('table.songlist tr.song a.rate').live('click', function (event) {
@ -314,7 +340,9 @@
$('table.songlist tr.song a.albumlink').live('click', function (event) {
var parentid = $(this).parent().parent().attr('parentid');
if (parentid != '' && parentid !== undefined) {
getAlbums(parentid, 'link', '#AlbumRows');
$('#AutoAlbumContainer li').removeClass('selected');
$('#ArtistContainer li').removeClass('selected');
getAlbums(parentid, 'link', '#AlbumContainer tbody');
}
return false;
});
@ -324,7 +352,8 @@
});
// Music Library Click Events
$('a#action_AddToPlaylist').click(function () {
$('#action_AddToPlaylist').click(function () {
if (!$(this).hasClass('disabled')) {
var submenu = $('div#submenu_AddToPlaylist');
if (submenu.is(":visible")) {
submenu.fadeOut();
@ -337,6 +366,7 @@
//show the menu directly over the placeholder
submenu.css({ "left": (pos.left) + "px", "top": (pos.top + height + 14) + "px" }).fadeIn(400);
}
}
return false;
});
$("#submenu_AddToPlaylist a").live("click", function (event) {
@ -358,19 +388,25 @@
//setTimeout(function () { if (submenu_active == false) $('div.submenu').stop().fadeOut(); }, 400);
});
$('a#action_AddToCurrent').click(function () {
if (!$(this).hasClass('disabled')) {
addToCurrent(false);
}
return false;
});
$('a#action_AddAllToCurrent').click(function () {
if (!$(this).hasClass('disabled')) {
addToCurrent(true);
}
return false;
});
$('a#action_PlayAlbum').click(function () {
if (!$(this).hasClass('disabled')) {
$('#CurrentPlaylistContainer tbody').empty();
addToCurrent(true);
// Start playing song
var first = $('#CurrentPlaylistContainer tr.song').first();
changeTrack(first);
}
return false;
});
$('#action_RefreshArtists').click(function () {
@ -391,15 +427,19 @@
return false;
});
$('#action_SelectAll').click(function () {
if (!$(this).hasClass('disabled')) {
$('#Albums tr.song').each(function () {
$(this).addClass('selected');
});
}
return false;
});
$('#action_SelectNone').click(function () {
if (!$(this).hasClass('disabled')) {
$('#Albums tr.song').each(function () {
$(this).removeClass('selected');
});
}
return false;
});
$('input#Search').keydown(function (e) {
@ -410,16 +450,55 @@
});
$('#action_Search').click(function () {
var query = $('#Search').val();
if (query != '') {
var type = $('#SearchType').val();
search(type, query);
$('#Search').val("");
}
return false;
});
$('#action_PreviousAlbumList').live('click', function () {
var type = $('#status_Library').data('type');
var offset = 0;
if ($('#status_Library').data('offset') != '') {
offset = $('#status_Library').data('offset');
}
var currOffset = 15;
if (getCookie('AutoAlbumSize')) {
currOffset = getCookie('AutoAlbumSize');
}
if (offset > 0) {
$('#status_Library').data('offset', parseInt(offset) - parseInt(currOffset));
getAlbumListBy(type, parseInt(offset) - parseInt(currOffset));
}
return false;
});
$('#action_NextAlbumList').live('click', function () {
var currOffset = 15;
if (getCookie('AutoAlbumSize')) {
currOffset = getCookie('AutoAlbumSize');
}
var count = $('#AlbumContainer tr.album').size();
if (count == currOffset) {
var type = $('#status_Library').data('type');
var offset = 0;
if ($('#status_Library').data('offset') != '') {
offset = $('#status_Library').data('offset');
}
$('#status_Library').data('offset', parseInt(offset) + parseInt(currOffset));
getAlbumListBy(type, parseInt(offset) + parseInt(currOffset));
}
return false;
});
// Current Playlist Click Events
$('#action_Shuffle').live('click', function () {
//$('#CurrentPlaylistContainer tbody tr.song').shuffle();
$('#CurrentPlaylistContainer tbody tr.song:not(#CurrentPlaylistContainer tbody tr.playing)').shuffle();
refreshRowColor('#CurrentPlaylistContainer tbody');
/* Sets currently playing song first in list after sort
$('#CurrentPlaylistContainer tbody tr.song').shuffle();
$('#CurrentPlaylistContainer tbody tr.playing').insertBefore($('#CurrentPlaylistContainer tbody tr:first'));
*/
$('#CurrentPlaylistContainer thead').find('th').removeClass('sorted ascending descending');
var songid = $('#CurrentPlaylistContainer tbody tr.playing').attr('childid');
if (songid !== undefined) {
$('#CurrentPlaylist').scrollTo($('#' + songid), 400);
@ -431,7 +510,8 @@
deleteCurrentPlaylist();
return false;
});
$('a#action_AddCurrentToPlaylist').click(function () {
$('#action_AddCurrentToPlaylist').click(function () {
if (!$(this).hasClass('disabled')) {
var submenu = $('div#submenu_AddCurrentToPlaylist');
if (submenu.is(":visible")) {
submenu.fadeOut();
@ -444,6 +524,7 @@
//show the menu directly over the placeholder
submenu.css({ "left": (pos.left) + "px", "top": (pos.top + height + 14) + "px" }).fadeIn(400);
}
}
});
$("#submenu_AddCurrentToPlaylist a").live("click", function (event) {
var id = $(this).attr('childid');
@ -506,31 +587,34 @@
});
$('#songdetails').mouseout(function () {
$(this).removeClass('hover')
//$('div.submenu').hide();
});
$('#CurrentPlaylist').mouseenter(function () {
var html = $('#status').html();
if (html != '') {
$('#status').fadeIn();
$('.tabcontent').mouseenter(function () {
$('.status').each(function (i) {
if ($(this).hasClass('on')) {
$(this).fadeIn();
}
});
$('#CurrentPlaylist').mouseleave(function () {
$('#status').stop().fadeOut();
});
$('.tabcontent').mouseleave(function () {
$('.status').stop().fadeOut();
});
$('#action_CurrentSelectAll').click(function () {
if (!$(this).hasClass('disabled')) {
var count = 0;
$('#CurrentPlaylist tr.song').each(function () {
$(this).addClass('selected');
count++;
});
updateMessage(count + ' Song(s) Selected');
}
return false;
});
$('#action_CurrentSelectNone').click(function () {
if (!$(this).hasClass('disabled')) {
$('#CurrentPlaylist tr.song').each(function () {
$(this).removeClass('selected');
});
}
return false;
});
$('#action_AutoPilot').click(function () {
@ -546,6 +630,7 @@
if ($('#CurrentPlaylistContainer tbody').html() == '') {
$('#CurrentPlaylistContainer tbody').empty();
getRandomSongList('', '#CurrentPlaylistContainer tbody', '', '');
$('#currentActions a.button').removeClass('disabled');
}
}
$(this).attr("title", msg);
@ -623,8 +708,8 @@
return false;
});
$('#action_ShufflePlaylist').live('click', function () {
$('#TrackContainer thead').find('th').removeClass('sorted ascending descending');
$('#TrackContainer tbody tr.song').shuffle();
refreshRowColor('#TrackContainer tbody');
return false;
});
@ -857,6 +942,7 @@
return false;
});
/*
// JQuery UI Sortable - Drag and drop sorting
var fixHelper = function (e, ui) {
ui.children().each(function () {
@ -865,17 +951,11 @@
return ui;
};
$("#CurrentPlaylistContainer tbody").sortable({
helper: fixHelper,
stop: function () {
refreshRowColor('#CurrentPlaylistContainer tbody');
}
helper: fixHelper
}).disableSelection();
$("#TrackContainer tbody").sortable({
helper: fixHelper,
stop: function () {
refreshRowColor('#TrackContainer tbody');
}
helper: fixHelper
}).disableSelection();
*/
}); // End document.ready

View file

@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "MiniSub (Beta)",
"description": "MiniSub - HTML5 Mini Player for Subsonic",
"version": "2.1.7",
"version": "2.2.0",
"app": {
"launch": {
"local_path": "index.html"

View file

@ -15,9 +15,6 @@ h3.title {
color: #f2f2f2;
}
.odd { background: none; }
.even { background: #272727; }
#content
{
background: #222222;
@ -98,14 +95,18 @@ h3.title {
{
color: #d6d469;
background: #2e2e2e;
border: 1px solid #5a5a5a;
border: 1px solid #1d1d1d;
}
#status
.status
{
background: #222;
border-top: 1px solid #1D1D1D;
border-left: 1px solid #1D1D1D;
}
.status a
{
color: #d6d469;
}
#player
{
background: #2E2E2E;
@ -146,7 +147,7 @@ a.button:hover {
-moz-box-shadow: 0 2px 0 rgba(242, 242, 242, 0.2)
-webkit-box-shadow:0 2px 5px rgba(242, 242, 242, 0.2);
box-shadow: 0 1px 2px rgba(242, 242, 242, 0.15);
border: solid 1px #5a5a5a;
border-color: #5a5a5a;
}
a.button:active {
color: #000;
@ -155,8 +156,22 @@ a.button:active {
a.selected {
color: #939393;
border-color: #5a5a5a;
-moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2) -webkit-box-shadow:0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
-moz-box-shadow: 0 2px 0 rgba(242, 242, 242, 0.2)
-webkit-box-shadow:0 2px 5px rgba(242, 242, 242, 0.2);
box-shadow: 0 1px 2px rgba(242, 242, 242, 0.15);
background: #161616;
background: -webkit-gradient(linear,0% 40%,0% 70%,from(#161616),to(#111111));
background: -moz-linear-gradient(linear,0% 40%,0% 70%,from(#161616),to(#111111));
}
a.disabled {
color: #3D3D3D;
background: #2E2E2E;
}
a.disabled:hover {
color: #3D3D3D;
border-color: #1d1d1d;
-moz-box-shadow: none;
box-shadow: none;
}
ul.simplelist li
{
@ -196,8 +211,13 @@ ul.simplelist li.selected
}
table.songlist th {
color: #F2F2F2;
border-bottom: 1px solid #2e2e2e;
background: #2E2E2E;
}
table.songlist tr:hover
table.songlist th:hover, table.songlist th.sorted {
background: #272727;
}
table.songlist tr.album:hover
{
background: #1d1d1d;
}
@ -212,6 +232,10 @@ table.songlist tr.album td.albumart img
table.songlist tr.song {
border-bottom: 1px solid #232323;
}
table.songlist tr.song:hover
{
background: #1d1d1d;
}
table.songlist tr.song td.album img
{
border: 1px solid #232323;
@ -225,12 +249,15 @@ table.songlist tr.song td.album a:hover
color: #d6d469;
text-decoration: underline;
}
table.songlist tr:nth-child(odd) { background-color: #272727; }
table.songlist tr:nth-child(even) { background-color: #2e2e2e; }
table.songlist tr.playing {
background: #161616;
}
table.songlist tr.selected
{
background: #3a3a3a;
}
table.songlist tr.playing {
background: #161616;
border-bottom: 1px solid #232323;
}
#submenu_CurrentPlaylist
{
@ -255,3 +282,9 @@ fieldset
background: #232323;
border: 1px solid #1D1D1D;
}
::-webkit-scrollbar-thumb {
background: rgba(34, 34, 34, 0.8);
}
::-webkit-scrollbar-thumb:window-inactive {
background: rgba(34, 34, 34, 0.2);
}

View file

@ -31,8 +31,10 @@ h3.title
.alignright { text-align: right; }
.aligncenter { text-align: center; }
.padright { padding: 0 10px 0 0; }
/* Old: Using CSS3
.odd { background: #fff; }
.even { background: #f4f4f4; }
*/
.red { color: #E64F4C; }
.italic { font-style: italic; }
.beer { font-size: 17px; }
@ -161,7 +163,7 @@ a#logo:hover
overflow-y: auto;
overflow-x: hidden;
background: #fff;
border-right: 1px solid #cbcbcb;
border-right: 1px solid #f2f2f2;
position: absolute;
top: 39px;
left: 60px;
@ -225,7 +227,7 @@ a#logo:hover
position: absolute;
z-index: 99;
top: 0;
left: 790px;
right: 10px;
}
#messages .message
{
@ -236,11 +238,13 @@ a#logo:hover
margin: 2px 0 2px 0;
background: #fff;
border: 1px solid #A6CBF3;
border-radius: 4px 4px 4px 4px;
box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.1);
opacity: 0.8;
}
#status
.status
{
display: none;
width: 180px;
overflow: hidden;
font-size: 12px;
text-align: center;
@ -249,10 +253,32 @@ a#logo:hover
bottom: 86px;
right: 4px;
background: #f2f2f2;
padding: 2px 5px;
padding: 2px 10px;
border-top: 1px solid #CBCBCB;
border-left: 1px solid #CBCBCB;
opacity: .7;
opacity: .9;
}
.status a
{
color: #545454;
text-decoration: none;
font-size: 12px;
}
.status a:hover
{
text-decoration: underline;
}
.status a.previous
{
margin: 0 5px 0 0;
}
.status a.next
{
margin: 0 0 0 5px;
}
.on
{
display: block;
}
/* Library Style */
ul.simplelist
@ -337,13 +363,9 @@ ul.mainlist li.item a.add:hover
{
background: url('../images/plus_8x8.png') no-repeat 6px center #DEECFB;
}
#AlbumContainer
#AlbumContainer, #TrackContainer, #PodcastContainer
{
margin: 5px 5px 5px 206px;
}
#TrackContainer, #PodcastContainer
{
margin: 5px 5px 5px 206px;
margin: 5px 5px 5px 221px;
}
#CurrentPlaylistContainer tr.song a.add
{
@ -381,9 +403,25 @@ table.songlist th
{
font-size: 14px;
font-variant: small-caps;
padding: 5px 4px;
padding: 5px 5px 5px 12px;
cursor: pointer;
border-bottom: 1px solid #ffffff;
background: #fff;
}
table.songlist tr:hover
table.songlist th:hover
{
background: #fafafa;
}
table.songlist th.sorted {
background: #F2F3F4;
}
table.songlist th.sorted.ascending:after {
content: " \2191";
}
table.songlist th.sorted.descending:after {
content: " \2193";
}
table.songlist tr.album:hover
{
background: #EEF5FD;
}
@ -480,6 +518,7 @@ table.songlist tr.album a.favorite
display: block;
background: url('../images/star_yo_12x12.png') 2px center no-repeat;
}
table.songlist tr.song
{
cursor: pointer;
@ -499,13 +538,9 @@ table.songlist tr.song td.itemactions
height: 18px;
}
table.songlist tr.song td.track
{
width: 40px;
text-align: right;
}
table.songlist tr.song td.published
{
width: 80px;
text-align: right;
}
table.songlist tr.song td.title
{
@ -604,6 +639,8 @@ table.songlist tr.song a.favorite
display: block;
background: url('../images/star_yo_12x12.png') center 3px no-repeat;
}
table.songlist tr:nth-child(odd) { background-color:#f4f4f4; }
table.songlist tr:nth-child(even) { background-color:#fff; }
table.songlist tr.playing
{
background-color: #DEEBFA;
@ -622,50 +659,6 @@ table.songlist tr.selected td:first-child
{
background: url('../images/check_8x7.png') 10px 16px no-repeat;
}
table.songlist tr.selected td.album a
{
color: #f2f2f2;
}
/* tablesorter style */
table.tablesorter {
font-family:arial;
background-color: #CDCDCD;
margin:10px 0pt 15px;
font-size: 8pt;
width: 100%;
text-align: left;
}
table.tablesorter thead tr th, table.tablesorter tfoot tr th {
background-color: #e6EEEE;
border: 1px solid #FFF;
font-size: 8pt;
padding: 4px;
}
table.tablesorter thead tr .header {
background-image: url(../images/bg.gif);
background-repeat: no-repeat;
background-position: center right;
cursor: pointer;
}
table.tablesorter tbody td {
color: #3D3D3D;
padding: 4px;
background-color: #FFF;
vertical-align: top;
}
table.tablesorter tbody tr.odd td {
background-color:#F0F0F6;
}
table.tablesorter thead tr .headerSortUp {
background-image: url(../images/asc.gif);
}
table.tablesorter thead tr .headerSortDown {
background-image: url(../images/desc.gif);
}
table.tablesorter thead tr .headerSortDown, table.tablesorter thead tr .headerSortUp {
background-color: #8dbdd8;
}
#ArtistContainer
{
min-height: 360px;
@ -714,7 +707,7 @@ background-color: #8dbdd8;
margin: 4px 0 0 0;
padding: 0 0 0 5px;
}
#songactions
#songActions
{
float: left;
}
@ -1122,14 +1115,45 @@ a.selected {
border-color: #999;
-moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2) -webkit-box-shadow:0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
background: #fdfdfd;
background: -webkit-gradient(linear,0% 40%,0% 70%,from(#ffffff),to(#fdfdfd));
background: -moz-linear-gradient(linear,0% 40%,0% 70%,from(#ffffff),to(#fdfdfd));
}
a.disabled {
color: #B7B7B7;
background: #f1f1f1;
}
a.disabled:hover {
color: #B7B7B7;
border-color: #dcdcdc;
-moz-box-shadow: none;
box-shadow: none;
}
a.button img.pad { margin: 1px 0 -1px 0; }
.shadow
{
box-shadow: 0 0 4px rgba(0, 0, 0, 0.2);
}
/* Scrollbar Style */
::-webkit-scrollbar {
width: 14px;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 2px rgba(0,0,0,0.2);
-webkit-border-radius: 12px;
border-radius: 12px;
}
::-webkit-scrollbar-thumb {
-webkit-border-radius: 12px;
border-radius: 12px;
background: rgba(237, 237, 237, 0.4);
-webkit-box-shadow: inset 0 0 2px rgba(0,0,0,0.2);
}
::-webkit-scrollbar-thumb:window-inactive {
background: rgba(237, 237, 237, 0.2);
}
/* Form Style */
input
{