1.8 display changes, folder picker

This commit is contained in:
Trevor Squillario 2012-03-27 13:51:16 -04:00
parent ed43a7875d
commit c79720b923
18 changed files with 1627 additions and 1165 deletions

BIN
images/download_gd_9x12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

BIN
images/download_gl_9x12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

BIN
images/hash_gd_14x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

View file

Before

Width:  |  Height:  |  Size: 276 B

After

Width:  |  Height:  |  Size: 276 B

Before After
Before After

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 B

BIN
images/list_gd_16x14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

BIN
images/star_lgo_12x12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

View file

@ -7,6 +7,7 @@
<link rel="icon" href="images/subsonic_32x32.png" sizes="32x32"/>
<link href="style/Style.css" rel="stylesheet" type="text/css" />
<link href="js/fancybox/jquery.fancybox-1.3.4.css" rel="stylesheet" type="text/css" />
<link href="js/contextMenu/jquery.contextMenu.css" rel="stylesheet" type="text/css" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script src="js/audiojs/audio.min.js" type="text/javascript"></script>
<script src="js/jquery.scrollTo-1.4.2-min.js" type="text/javascript"></script>
@ -19,542 +20,9 @@
<script src="js/fancybox/jquery.fancybox-1.3.4.pack.js" type="text/javascript"></script>
<script src="js/jquery.linkify-1.0-min.js" type="text/javascript"></script>
<script src="js/app.js" type="text/javascript"></script>
<script src="js/ui-load.js" type="text/javascript"></script>
<script src="js/ui-ready.js" type="text/javascript"></script>
<script type="text/javascript">
$(document).ready(function () {
// Tabs
$(".tabcontent").hide(); //Hide all content
if ($.cookie('username') == null) {
$('ul.tabs li a').each(function () {
if ($(this).attr("href") == '#tabPreferences') {
$(this).addClass("active"); //Add "active" class to selected tab
}
});
$("#tabPreferences").show(); //Show first tab content
loadTabContent('#tabPreferences');
} else {
if (window.location.hash) {
var hash = window.location.hash;
$('ul.tabs li a').each(function () {
if ($(this).attr("href") == hash) {
$(this).addClass("active"); //Add "active" class to selected tab
}
});
$(hash).show(); //Fade in the active ID content
loadTabContent(hash);
} else {
$("ul.tabs li:first a").addClass("active").show(); //Activate first tab
$(".tabcontent:first").show(); //Show first tab content
var firstTab = $("ul.tabs li:first a").attr("href");
loadTabContent(firstTab);
}
}
// Tabs - Click Event
$("ul.tabs li a").click(function () {
$("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).show(); //Fade in the active ID content
loadTabContent(activeTab);
});
// Ajax Loading Screen
$(".toploading").ajaxStart(function () {
$(this).show();
});
$(".toploading").ajaxStop(function () {
$(this).hide();
});
// Keyboard shortcuts
$(document).keydown(function (e) {
var source = e.target.id;
if (source != 'Search' && source != 'ChatMsg') {
var unicode = e.charCode ? e.charCode : e.keyCode;
// a-z
if (unicode >= 65 && unicode <= 90) {
var key = findKeyForCode(unicode);
var el = '#index_' + key.toUpperCase();
$('#Artists').stop().scrollTo(el, 400);
// right arrow
} else if (unicode == 39 || unicode == 176) {
var next = $('ul.songlist li.playing').next();
if (!next.length) next = $('ul.songlist li').first();
changeTrack(next);
// back arrow
} else if (unicode == 37 || unicode == 177) {
var prev = $('ul.songlist li.playing').prev();
if (!prev.length) prev = $('ul.songlist li').last();
changeTrack(prev);
// spacebar
} else if (unicode == 32 || unicode == 179 || unicode == 0179) {
playPauseSong();
} else if (unicode == 36) {
$('#Artists').stop().scrollTo('#auto', 400);
}
}
});
// Main Click Events
// Albums Click Event
$('#ArtistContainer li.item').live('click', function () {
$('#AutoAlbumContainer li').removeClass('selected');
$('#ArtistContainer li').removeClass('selected');
$(this).addClass('selected');
getAlbums($(this).attr("id"), '', '#AlbumRows');
});
$('#BottomIndex li a').live('click', function () {
var el = '#index_' + $(this).text();
$('#Artists').stop().scrollTo(el, 400);
return false;
});
$('#AutoAlbumContainer li.item').live('click', function () {
$('#AutoAlbumContainer li').removeClass('selected');
$('#ArtistContainer li').removeClass('selected');
$(this).addClass('selected');
getAlbumListBy($(this).attr("id"));
});
$('tr.album a.play').live('click', function (e) {
var albumid = $(this).parent().parent().attr('childid');
var artistid = $(this).parent().parent().attr('parentid');
getAlbums(albumid, 'autoplay', '#CurrentPlaylistContainer');
return false;
});
$('tr.album a.add').live('click', function (e) {
var albumid = $(this).parent().parent().attr('childid');
var artistid = $(this).parent().parent().attr('parentid');
getAlbums(albumid, 'add', '#CurrentPlaylistContainer');
return false;
});
$('tr.album a.rate').live('click', function (event) {
var itemid = $(this).parent().parent().attr('childid');
rateSong(itemid, 5);
return false;
});
$('tr.album a.favorite').live('click', function (event) {
var itemid = $(this).parent().parent().attr('childid');
rateSong(itemid, 0);
return false;
});
$('tr.album').live('click', function (e) {
var albumid = $(this).attr('childid');
var artistid = $(this).attr('parentid');
getAlbums(albumid, '', '#AlbumRows');
return false;
});
// Track - Click Events
// Multiple Select
$('.noselect').disableTextSelect();
var lastChecked = null;
$('table.songlist tr.song').live('click', function (event) {
var checkboxclass = 'table.songlist tr.song';
var songid = $(this).attr('childid');
var albumid = $(this).attr('parentid');
if (!event.ctrlKey) {
$(checkboxclass).removeClass('selected');
}
if ($(this).hasClass('selected')) {
$(this).removeClass('selected');
} else {
$(this).addClass('selected');
}
if (!lastChecked) {
lastChecked = this;
return;
}
if (event.shiftKey) {
var start = $(checkboxclass).index(this);
var end = $(checkboxclass).index(lastChecked);
for (i = Math.min(start, end); i <= Math.max(start, end); i++) {
$(checkboxclass).eq(i).addClass('selected');
}
}
lastChecked = this;
});
// Double Click
$('table.songlist tr.song').live('dblclick', function (e) {
e.preventDefault();
//$(this).addClass('playing').siblings().removeClass('playing');
var songid = $(this).attr('childid');
var albumid = $(this).attr('parentid');
playSong('', this, songid, albumid);
});
$('table.songlist tr.song a.play').live('click', function (event) {
var songid = $(this).parent().parent().attr('childid');
var albumid = $(this).parent().parent().attr('parentid');
playSong($(this).parent().parent(), songid, albumid);
return false;
});
$('table.songlist tr.song a.add').live('click', function (event) {
var track = $(this).parent().parent();
$(track).clone().appendTo('#CurrentPlaylistContainer');
return false;
});
$('table.songlist tr.song a.remove').live('click', function (event) {
var track = $(this).parent().parent();
$(track).remove();
refreshRowColor();
return false;
});
$('table.songlist tr.song a.rate').live('click', function (event) {
var songid = $(this).parent().parent().attr('childid');
rateSong(songid, 5);
return false;
});
$('table.songlist tr.song a.favorite').live('click', function (event) {
var songid = $(this).parent().parent().attr('childid');
rateSong(songid, 0);
return false;
});
$('li.index').live('click', function (e) {
$('#Artists').stop().scrollTo('#auto', 400);
return false;
});
// Music Library Click Events
$('a#action_AddToPlaylist').click(function () {
var submenu = $('div#submenu_AddToPlaylist');
if (submenu.is(":visible")) {
submenu.fadeOut();
} else {
loadPlaylistsForMenu('submenu_AddToPlaylist');
//get the position of the placeholder element
pos = $(this).offset();
width = $(this).width();
height = $(this).height();
//show the menu directly over the placeholder
submenu.css({ "left": (pos.left) + "px", "top": (pos.top + height + 14) + "px" }).fadeIn(400);
}
});
var submenu_active = false;
$('div.submenu').mouseenter(function () {
submenu_active = true;
});
$('div.submenu').mouseleave(function () {
submenu_active = false;
setTimeout(function () { if (submenu_active == false) $('div.submenu').fadeOut(); }, 400);
});
$('a#action_AddToCurrent').click(function () {
addToCurrent();
});
$('#action_RefreshArtists').click(function () {
loadArtists(true);
return false;
});
$('#action_IncreaseWidth').click(function () {
resizeSMSection(50);
return false;
});
$('#action_DecreaseWidth').click(function () {
resizeSMSection(-50);
return false;
});
$('#action_SelectAll').click(function () {
$('#Albums tr.song').each(function () {
$(this).addClass('selected');
});
return false;
});
$('#action_SelectNone').click(function () {
$('#Albums tr.song').each(function () {
$(this).removeClass('selected');
});
return false;
});
$('input#Search').keydown(function (e) {
var unicode = e.charCode ? e.charCode : e.keyCode;
if (unicode == 13) {
$('#action_Search').click();
}
});
$('#action_Search').click(function () {
var query = $('#Search').val();
search('song', query);
$('#Search').val("");
return false;
});
// Current Playlist Click Events
$('#action_Shuffle').live('click', function () {
$('#CurrentPlaylistContainer tr.song').shuffle();
refreshRowColor();
return false;
});
$('#action_Empty').live('click', function () {
$('#CurrentPlaylistContainer tbody').empty();
return false;
});
$('a#action_AddCurrentToPlaylist').click(function () {
var submenu = $('div#submenu_AddCurrentToPlaylist');
if (submenu.is(":visible")) {
submenu.fadeOut();
} else {
loadPlaylistsForMenu('submenu_AddCurrentToPlaylist');
//get the position of the placeholder element
pos = $(this).offset();
width = $(this).width();
height = $(this).height();
//show the menu directly over the placeholder
submenu.css({ "left": (pos.left) + "px", "top": (pos.top + height + 14) + "px" }).fadeIn(400);
}
});
$('#action_CurrentSelectAll').click(function () {
$('#CurrentPlaylist tr.song').each(function () {
$(this).addClass('selected');
});
return false;
});
$('#action_CurrentSelectNone').click(function () {
$('#CurrentPlaylist tr.song').each(function () {
$(this).removeClass('selected');
});
return false;
});
// Playlist Click Events
$('#AutoPlaylistContainer li.item').live('click', function () {
$('#AutoPlaylistContainer li').removeClass('selected');
$('#PlaylistContainer li').removeClass('selected');
$(this).addClass('selected');
getRandomSongList('', '#TrackContainer');
});
$('#AutoPlaylistContainer li.item a.play').live('click', function () {
getRandomSongList('autoplay', '#CurrentPlaylistContainer');
return false;
});
$('#AutoPlaylistContainer li.item a.add').live('click', function () {
getRandomSongList('', '#CurrentPlaylistContainer');
return false;
});
$('#PlaylistContainer li.item').live('click', function () {
$('#AutoPlaylistContainer li').removeClass('selected');
$('#PlaylistContainer li').removeClass('selected');
$(this).addClass('selected');
getPlaylist($(this).attr("id"), '', '#TrackContainer tbody');
});
$('#PlaylistContainer li.item a.play').live('click', function () {
getPlaylist($(this).parent().parent().attr("id"), 'autoplay', '#CurrentPlaylistContainer tbody');
return false;
});
$('#PlaylistContainer li.item a.add').live('click', function () {
getPlaylist($(this).parent().parent().attr("id"), '', '#CurrentPlaylistContainer tbody');
return false;
});
$('#action_DeletePlaylist').click(function () {
if ($('#PlaylistContainer li.selected').length > 0) {
if (confirmDelete()) {
$('#PlaylistContainer li.selected').each(function () {
deletePlaylist($(this).attr("id"));
});
}
}
return false;
});
$('#action_SavePlaylist').click(function () {
if ($('#PlaylistContainer li.selected').length > 0) {
$('#PlaylistContainer li.selected').each(function () {
savePlaylist($(this).attr("id"));
});
}
return false;
});
$('#action_RemoveSongs').click(function () {
if ($('#TrackContainer tr.selected').length > 0) {
$('#TrackContainer tr.selected').each(function () {
$(this).remove();
});
}
return false;
});
$('#action_ShufflePlaylist').live('click', function () {
$('#TrackContainer tr.song').shuffle();
refreshRowColor();
return false;
});
// Player Click Events
$('#PlayTrack').live('click', function () {
playPauseSong();
return false;
});
$('#NextTrack').live('click', function () {
var next = $('#CurrentPlaylistContainer tr.playing').next();
changeTrack(next);
return false;
});
$('#PreviousTrack').live('click', function () {
var prev = $('#CurrentPlaylistContainer tr.playing').prev();
changeTrack(prev);
return false;
});
$("a#coverartimage").fancybox({
'hideOnContentClick': true,
'type': 'image'
});
// Side Bar Click Events
$('#action_ToggleSideBar').live('click', function () {
if ($.cookie('sidebar')) {
$.cookie('sidebar', null);
$('#SideBar').hide();
stopUpdateChatMessages();
stopUpdateNowPlaying();
} else {
$.cookie('sidebar', true, { expires: 365 });
$('#SideBar').show();
updateChatMessages();
updateNowPlaying();
}
resizeContent();
return false;
});
$('input#ChatMsg').keydown(function (e) {
var unicode = e.charCode ? e.charCode : e.keyCode;
if (unicode == 13) {
var msg = $('#ChatMsg').val();
if (msg != '') {
addChatMessage(msg);
}
$('#ChatMsg').val("");
}
});
// Preferences Click Events
$('#SavePreferences').live('click', function () {
var username = $('#Username').val();
var password = $('#Password').val();
$.cookie('username', username, { expires: 365 });
$.cookie('password', password, { expires: 365 });
var AutoAlbumSize = $('#AutoAlbumSize').val();
var AutoPlaylistSize = $('#AutoPlaylistSize').val();
$.cookie('AutoAlbumSize', AutoAlbumSize, { expires: 365 });
$.cookie('AutoPlaylistSize', AutoPlaylistSize, { expires: 365 });
var server = $('#Server').val();
if (server != "") {
$.cookie('Server', server, { expires: 365 });
}
var applicationname = $('#ApplicationName').val();
if (applicationname != "") {
$.cookie('ApplicationName', applicationname, { expires: 365 });
}
location.reload(true);
});
$('#HideAZ').live('click', function () {
if ($('#HideAZ').is(':checked')) {
$.cookie('HideAZ', '1', { expires: 365 });
$('#BottomContainer').hide();
}
});
$('#EnableNotifications').live('click', function () {
if ($('#EnableNotifications').is(':checked')) {
requestPermissionIfRequired();
if (hasNotificationPermission()) {
$.cookie('EnableNotifications', '1', { expires: 365 });
}
} else {
$.cookie('EnableNotifications', null);
}
});
$('#ScrollTitle').live('click', function () {
if ($('#ScrollTitle').is(':checked')) {
$.cookie('ScrollTitle', '1', { expires: 365 });
}
});
$('input#Password').keydown(function (e) {
var unicode = e.charCode ? e.charCode : e.keyCode;
if (unicode == 13) {
$('#SavePreferences').click();
}
});
$('#ResetPreferences').live('click', function () {
$.cookie('username', null);
$.cookie('password', null);
$.cookie('AutoAlbumSize', null);
$.cookie('AutoPlaylistSize', null);
$.cookie('Server', null);
$.cookie('ApplicationName', null);
$.cookie('HideAZ', null);
location.reload(true);
});
$('#ChangeLogShowMore').live('click', function () {
$('ul#ChangeLog li.log').each(function (i, el) {
$(el).show();
});
return false;
});
}); // End document.ready
$(window).load(function () {
if ($.cookie('defaultsmwidth')) {
var width = $.cookie('defaultsmwidth');
$('.smsection').css({ 'width': width + 'px' });
$('#MainActions').css({ 'width': (width - 5) + 'px' });
$('#BottomContainer').css({ 'width': (width - 16) + 'px' });
var ulwidth = parseInt(width) + 6;
$('#AlbumContainer').css({ 'margin-left': ulwidth + 'px' });
$('#TrackContainer').css({ 'margin-left': ulwidth + 'px' });
}
if ($.cookie('sidebar') && $.cookie('username') && $.cookie('password')) {
$('#SideBar').show();
updateChatMessages();
updateNowPlaying();
}
if ($.cookie('HideAZ')) {
$('#BottomContainer').hide();
}
$('ul#ChangeLog li.log').each(function (i, el) {
if (i > 3) {
$(el).hide();
}
});
resizeContent();
});
window.onbeforeunload = function() {
closeAllNotifications();
};
$(window).resize(function () {
resizeContent();
});
function resizeContent() {
var screenwidth = $(window).width();
$('.tabcontent').css({ 'height': (($(window).height() - 152)) + 'px' });
$('.smsection').css({ 'height': (($(window).height() - 192)) + 'px' });
var smheight = $('.smsection').height();
var smwidth = $('.smsection').width();
$('#BottomContainer').css({ 'top': smheight + 75 + 'px' });
if ($.cookie('sidebar')) {
var tabwidth = $(window).width() - 218;
if (tabwidth >= 700) {
$('.tabcontent').css({ 'width': tabwidth + 'px' });
}
var sbheight = $(window).height() - 152;
$('#SideBar').css({ 'height': sbheight + 'px' });
$('#ChatMsgs').css({ 'height': (sbheight - 270) + 'px' });
} else {
var tabwidth = $(window).width() - 11;
if (tabwidth >= 700) {
$('.tabcontent').css({ 'width': tabwidth + 'px' });
}
}
var tabwidth = $('.tabcontent').width();
$('#AlbumContainer, #TrackContainer, #CurrentPlaylistContainer').css({ 'width': (tabwidth - smwidth - 30) + 'px' });
$('#CurrentPlaylistContainer').css({ 'width': (tabwidth - 30) + 'px' });
}
function resizeSMSection(x) {
var smwidth = $('.smsection').width();
var newsmwidth = smwidth + x;
if (newsmwidth > 150 && newsmwidth < 500) {
$('.smsection').css({ 'width': (newsmwidth) + 'px' });
$('#MainActions').css({ 'width': (newsmwidth - 5) + 'px' });
$('#BottomContainer').css({ 'width': (newsmwidth - 16) + 'px' });
$.cookie('defaultsmwidth', newsmwidth, { expires: 365, path: '/' });
var ulwidth = newsmwidth + 6;
$('#AlbumContainer').css({ 'margin-left': ulwidth + 'px' });
$('#TrackContainer').css({ 'margin-left': ulwidth + 'px' });
}
}
var a;
var audio;
audiojs.events.ready(function () {
@ -598,24 +66,24 @@
<body>
<div id="container">
<div id="messages">This is a test!</div>
<div id="nav">
<ul class="tabs">
<li><a href="#tabLibrary">Music Library</a></li>
<li><a href="#tabCurrent">Current Playlist</a></li>
<li><a href="#tabPlaylists">Playlists</a></li>
<li><a href="#tabLibrary" class="first" title="Library"><img src="images/headphones_gd_16x14.png" /></a></li>
<li><a href="#tabCurrent" title="Current Playlist"><img src="images/play_alt_gd_16x16.png" /></a></li>
<li><a href="#tabPlaylists" title="Playlists"><img src="images/list_gd_16x14.png" /></a></li>
<li><a href="#tabPreferences" class="last" title="Preferences"><img src="images/cog_16x16.png" /></a></li>
</ul>
<div class="toploading"></div>
<div id="messages"></div>
</div>
<div id="content">
<div id="tabLibrary" class="tabcontent">
<div id="MainActions" class="actions floatleft">
<div class="actions floatleft">
<a href="#" class="button" id="action_RefreshArtists" title="Refresh Artist List"><img src="images/reload_9x11.png" /></a>
<a href="#" class="button" id="action_DecreaseWidth" title="Decrease Width"><img src="images/minus_8x2.png" /></a>
<a href="#" class="button" id="action_IncreaseWidth" title="Increase Width"><img src="images/plus_8x8.png" /></a>
</div>
<div class="actions floatleft">
<div class="subactions floatleft">
<a href="#" class="button" 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>
@ -623,11 +91,14 @@
<a href="#" class="button" id="action_SelectNone" title="Select None">None</a>
<input type="text" id="Search" class="medium" /><a href="#" class="button" id="action_Search" title="Search"><img src="images/magnifying_glass_alt_12x12.png" /></a>
</div>
<div id="Albums" class="lgsection floatleft noselect">
<div id="Albums" class="lgsection floatleft">
<div class="loading"></div>
<div id="Artists" class="smsection floatleft noselect" tabindex="0">
<div id="Artists" class="smsection floatleft" tabindex="0">
<div class="padder">
<ul id="AutoAlbumContainer" class="simplelist mainlist">
<select id="MusicFolders">
<option value="All Folders">All Folders</option>
</select>
<ul id="AutoAlbumContainer" class="simplelist mainlist noselect">
<li class="index" id="auto">Auto Albums</li>
<li class="item" id="newest"><span>Recently Added</span></li>
<li class="item" id="random"><span>Random</span></li>
@ -635,20 +106,23 @@
<li class="item" id="recent"><span>Recently Played</span></li>
<li class="item" id="frequent"><span>Most Played</span></li>
</ul>
<ul id="ArtistContainer" class="simplelist mainlist"></ul>
<ul id="ArtistContainer" class="simplelist mainlist noselect"></ul>
</div>
<div id="BottomContainer"><ul id="BottomIndex"></ul></div>
</div>
<table id="AlbumContainer" class="simplelist songlist" cellspacing="1">
<table id="AlbumContainer" class="simplelist songlist noselect" cellspacing="1">
<thead id="AlbumHeader"></thead>
<tbody id="AlbumRows"></tbody>
</table>
</div>
</div>
<div id="tabCurrent" class="tabcontent">
<div class="actions floatleft">
<a href="#" class="button" id="action_Shuffle" title="Shuffle"><img src="images/fork_11x12.png" /></a>
<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>
@ -666,6 +140,8 @@
<a href="#" class="button" id="action_NewPlaylist" onclick="newPlaylist(); return false;" title="New Playlist">+ New</a>
<a href="#" class="button" id="action_DeletePlaylist" title="Delete Playlist">Delete</a>
<a href="#" class="button" id="action_SavePlaylist" title="Save Playlist">Save</a>
</div>
<div class="subactions floatleft">
<a href="#" class="button" id="action_ShufflePlaylist" title="Shuffle Playlist"><img src="images/fork_11x12.png" /></a>
<a href="#" class="button" id="action_RemoveSongs" title="Remove selected song(s) from playlist">Remove Song(s)</a>
</div>
@ -766,7 +242,17 @@
<div class="subsection floatleft">
<h3 class="title">Change Log</h3>
<ul id="ChangeLog" class="preferences">
<!--<li class="log">- </li>-->
<!--
<li class="log"><span class="version"></span>
<span class="changes"></span>
</li>
-->
<li class="log"><span class="version">Current - 1.8</span>
<span class="changes">Added Download link to Albums</span>
<span class="changes">Fixed bug with Next/Previous keyboard shortcuts</span>
<span class="changes">Moved navigation since most displays are widescreen</span>
<span class="changes">Added folder picker, choice will be saved in cookie</span>
</li>
<li class="log"><span class="version">3/9/2012 - 1.7</span>
<span class="changes">- Ability to hide A-Z bar on Artists list</span>
<span class="changes">- Desktop Notifications on browsers that support <span class="code">webkitNotifications</span></span>

114
js/app.js
View file

@ -19,7 +19,12 @@ var version = '1.7.0';
function loadTabContent(tab) {
switch (tab) {
case '#tabLibrary':
if ($.cookie('MusicFolders')) {
loadArtists($.cookie('MusicFolders'), false);
} else {
loadArtists();
}
getMusicFolders();
break;
case '#tabCurrent':
var header = generateSongHeaderHTML();
@ -35,22 +40,39 @@ function loadTabContent(tab) {
}
}
function loadArtists(refresh) {
function loadArtists(id, refresh) {
if (refresh) {
$('#ArtistContainer').empty();
}
var url;
if (id == "all") {
url = baseURL + '/getIndexes.view?u=' + username + '&p=' + passwordenc + '&v=' + version + '&c=' + applicationName + '&f=jsonp';
} else if (id) {
url = baseURL + '/getIndexes.view?u=' + username + '&p=' + passwordenc + '&v=' + version + '&c=' + applicationName + '&f=jsonp&musicFolderId=' + id;
} else {
url = baseURL + '/getIndexes.view?u=' + username + '&p=' + passwordenc + '&v=' + version + '&c=' + applicationName + '&f=jsonp';
}
var content = $('#ArtistContainer').html();
if (content === "") {
// Load Artist List
$.ajax({
url: baseURL + '/getIndexes.view?u=' + username + '&p=' + passwordenc + '&v=' + version + '&c=' + applicationName + '&f=jsonp',
url: url,
method: 'GET',
dataType: 'jsonp',
timeout: 10000,
success: function (data) {
if (data["subsonic-response"].status === 'ok') {
var indexlist, indexname;
$.each(data["subsonic-response"].indexes.index, function (i, index) {
// There is a bug in the API that doesn't return a JSON array for one artist
var indexes = [];
if (data["subsonic-response"].indexes.index.length > 0) {
indexes = data["subsonic-response"].indexes.index;
} else {
indexes[0] = data["subsonic-response"].indexes.index;
}
$.each(indexes, function (i, index) {
if (index.name === '#') {
indexname = '0-9';
} else {
@ -89,6 +111,41 @@ function loadArtists(refresh) {
});
}
}
function getMusicFolders() {
$.ajax({
url: baseURL + '/getMusicFolders.view?u=' + username + '&p=' + passwordenc + '&v=' + version + '&c=' + applicationName + '&f=jsonp',
method: 'GET',
dataType: 'jsonp',
timeout: 10000,
beforeSend: function (req) {
req.setRequestHeader('Authorization', auth);
},
success: function (data) {
if (data["subsonic-response"].musicFolders.musicFolder !== undefined) {
// There is a bug in the API that doesn't return a JSON array for one artist
var folders = [];
if (data["subsonic-response"].musicFolders.musicFolder.length > 0) {
folders = data["subsonic-response"].musicFolders.musicFolder;
} else {
folders[0] = data["subsonic-response"].musicFolders.musicFolder;
}
var savedMusicFolder = $.cookie('MusicFolders');
var options = [];
options.push('<option value="all">All Folders</option>');
$.each(folders, function (i, folder) {
if (savedMusicFolder == folder.id) {
options.push('<option value="' + folder.id + '" selected>' + folder.name + '</option>');
} else {
options.push('<option value="' + folder.id + '">' + folder.name + '</option>');
}
});
$('#MusicFolders').html(options.join(''));
} else {
}
}
});
}
function getAlbums(id, action, appendto) {
$.ajax({
url: baseURL + '/getMusicDirectory.view?u=' + username + '&p=' + passwordenc + '&v=' + version + '&c=' + applicationName + '&f=jsonp&id=' + id,
@ -135,6 +192,9 @@ function getAlbums(id, action, appendto) {
}
$(albumhtml).appendTo(appendto);
});
if (appendto === '#CurrentPlaylistContainer') {
updateMessage(children.length + ' Song(s) Added');
}
if (appendto === '#AlbumRows' && isDir === true) {
header = generateAlbumHeaderHTML();
}
@ -243,6 +303,9 @@ function getRandomSongList(action, appendto) {
html = generateSongHTML(rowcolor, item.id, item.parent, track, item.title, item.artist, item.album, item.coverArt, item.userRating, time['m'], time['s']);
$(html).appendTo(appendto);
});
if (appendto === '#CurrentPlaylistContainer') {
updateMessage(items.length + ' Song(s) Added');
}
if (action === 'autoplay') {
autoPlay();
}
@ -262,6 +325,7 @@ function generateAlbumHTML(rowcolor, childid, parentid, coverart, title, artist,
html = '<tr class=\"album ' + rowcolor + '\" childid=\"' + childid + '\" parentid=\"' + parentid + '\" userrating=\"' + rating + '\">';
html += '<td class=\"itemactions\"><a class=\"add\" href=\"\" title=\"Add To Current Playlist\"></a>';
html += '<a class=\"play\" href=\"\" title=\"Play\"></a>';
html += '<a class=\"download\" href=\"\" title=\"Download\"></a>';
if (rating === 5) {
html += '<a class=\"favorite\" href=\"\" title=\"Favorite\"></a>';
} else {
@ -358,9 +422,9 @@ function playSong(el, songid, albumid) {
}
if ($.cookie('ScrollTitle')) {
//clearTimeout(timer);
scrollTitle(artist + ' - ' + title);
scrollTitle(toHTML.un(artist) + ' - ' + toHTML.un(title));
} else {
setTitle(artist + ' - ' + title);
setTitle(toHTML.un(artist) + ' - ' + toHTML.un(title));
}
}
});
@ -781,11 +845,29 @@ function addToPlaylist(playlistid, from) {
}
}
function addToCurrent() {
var count = $('table.songlist tr.selected').length;
$('table.songlist tr.selected').each(function (index) {
var count = $('#AlbumContainer tr.selected').length;
if (count > 0) {
$('#AlbumContainer tr.selected').each(function (index) {
$(this).clone().appendTo('#CurrentPlaylistContainer tbody');
updateMessage(count + ' Song(s) Added');
});
}
}
function downloadItem(id) {
var url;
if (id) {
url = baseURL + '/download.view?u=' + username + '&p=' + passwordenc + '&v=' + version + '&c=' + applicationName + '&f=jsonp&id=' + id;
window.location = url;
}
/*
$('table.songlist tr.selected').each(function (index) {
id = $(this).attr('childid');
if (id) {
url = baseURL + '/download.view?u=' + username + '&p=' + passwordenc + '&v=' + version + '&c=' + applicationName + '&f=jsonp&id=' + id;
window.location = url;
}
});
*/
}
function savePlaylist(playlistid) {
var songs = [];
@ -851,6 +933,9 @@ function getPlaylist(id, action, appendto) {
html = generateSongHTML(rowcolor, child.id, child.parent, track, child.title, child.artist, child.album, child.coverArt, child.userRating, time['m'], time['s']);
$(html).appendTo(appendto);
});
if (appendto === '#CurrentPlaylistContainer tbody') {
updateMessage(children.length + ' Song(s) Added');
}
if (action === 'autoplay') {
autoPlay();
}
@ -951,6 +1036,21 @@ function updateMessage(msg) {
$('#messages').fadeIn();
setTimeout(function () { $('#messages').fadeOut(); }, 5000);
}
// Convert to unicode support
var toHTML = {
on: function(str) {
var a = [],
i = 0;
for (; i < str.length;) a[i] = str.charCodeAt(i++);
return "&#" + a.join(";&#") + ";"
},
un: function(str) {
return str.replace(/&#(x)?([^&]{1,5});?/g,
function(a, b, c) {
return String.fromCharCode(parseInt(c, b ? 16 : 10))
})
}
};
function setTitle(text) {
if (text != "") {
document.title = text;

View file

@ -1,13 +1,13 @@
// A cross-browser javascript shim for html5 audio
(function(audiojs, audiojsInstance, container) {
(function (audiojs, audiojsInstance, container) {
// Use the path to the audio.js file to create relative paths to the swf and player graphics
// Remember that some systems (e.g. ruby on rails) append strings like '?1301478336' to asset paths
var path = (function() {
var path = (function () {
var re = new RegExp('audio(\.min)?\.js.*'),
scripts = document.getElementsByTagName('script');
for (var i = 0, ii = scripts.length; i < ii; i++) {
var path = scripts[i].getAttribute('src');
if(re.test(path)) return path.replace(re, '');
if (re.test(path)) return path.replace(re, '');
}
})();
@ -23,9 +23,9 @@
// `$3` Cache invalidation
flashSource: '\
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" id="$1" width="1" height="1" name="$1" style="position: absolute; left: -1px;"> \
<param name="movie" value="$2?playerInstance='+audiojs+'.instances[\'$1\']&datetime=$3"> \
<param name="movie" value="$2?playerInstance=' + audiojs + '.instances[\'$1\']&datetime=$3"> \
<param name="allowscriptaccess" value="always"> \
<embed name="$1" src="$2?playerInstance='+audiojs+'.instances[\'$1\']&datetime=$3" width="1" height="1" allowscriptaccess="always"> \
<embed name="$1" src="$2?playerInstance=' + audiojs + '.instances[\'$1\']&datetime=$3" width="1" height="1" allowscriptaccess="always"> \
</object>',
// ### The main settings object
@ -36,11 +36,11 @@
preload: true,
imageLocation: path + 'player-graphics.gif',
swfLocation: path + 'audiojs.swf',
useFlash: (function() {
useFlash: (function () {
var a = document.createElement('audio');
return !(a.canPlayType && a.canPlayType('audio/mpeg;').replace(/no/, ''));
})(),
hasFlash: (function() {
hasFlash: (function () {
if (navigator.plugins && navigator.plugins.length && navigator.plugins['Shockwave Flash']) {
return true;
} else if (navigator.mimeTypes && navigator.mimeTypes.length) {
@ -50,7 +50,7 @@
try {
var ax = new ActiveXObject('ShockwaveFlash.ShockwaveFlash');
return true;
} catch (e) {}
} catch (e) { }
}
return false;
})(),
@ -124,54 +124,55 @@
.error .play-pause p { cursor: auto; } \
.error .error-message { display: block; }',
// The default event callbacks:
trackEnded: function(e) {},
flashError: function() {
trackEnded: function (e) { },
flashError: function () {
var player = this.settings.createPlayer,
errorMessage = getByClass(player.errorMessageClass, this.wrapper),
html = 'Missing <a href="http://get.adobe.com/flashplayer/">flash player</a> plugin.';
if (this.mp3) html += ' <a href="'+this.mp3+'">Download audio file</a>.';
if (this.mp3) html += ' <a href="' + this.mp3 + '">Download audio file</a>.';
container[audiojs].helpers.removeClass(this.wrapper, player.loadingClass);
container[audiojs].helpers.addClass(this.wrapper, player.errorClass);
errorMessage.innerHTML = html;
},
loadError: function(e) {
loadError: function (e) {
var player = this.settings.createPlayer,
errorMessage = getByClass(player.errorMessageClass, this.wrapper);
container[audiojs].helpers.removeClass(this.wrapper, player.loadingClass);
container[audiojs].helpers.addClass(this.wrapper, player.errorClass);
errorMessage.innerHTML = 'Error loading: "'+this.mp3+'"';
errorMessage.innerHTML = 'Error loading: "' + this.mp3 + '"';
},
init: function() {
init: function () {
var player = this.settings.createPlayer;
container[audiojs].helpers.addClass(this.wrapper, player.loadingClass);
},
loadStarted: function() {
loadStarted: function () {
var player = this.settings.createPlayer,
duration = getByClass(player.durationClass, this.wrapper),
m = Math.floor(this.duration / 60),
s = Math.floor(this.duration % 60);
container[audiojs].helpers.removeClass(this.wrapper, player.loadingClass);
duration.innerHTML = ((m<10?'0':'')+m+':'+(s<10?'0':'')+s);
duration.innerHTML = ((m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s);
},
loadProgress: function(percent) {
loadProgress: function (percent) {
var player = this.settings.createPlayer,
scrubber = getByClass(player.scrubberClass, this.wrapper),
loaded = getByClass(player.loaderClass, this.wrapper);
loaded.style.width = (scrubber.offsetWidth * percent) + 'px';
},
playPause: function() {
playPause: function () {
if (this.playing) this.settings.play();
else this.settings.pause();
},
play: function() {
play: function () {
var player = this.settings.createPlayer;
container[audiojs].helpers.addClass(this.wrapper, player.playingClass);
},
pause: function() {
pause: function () {
var player = this.settings.createPlayer;
container[audiojs].helpers.removeClass(this.wrapper, player.playingClass);
},
updatePlayhead: function(percent) {
updatePlayhead: function (percent) {
var player = this.settings.createPlayer,
scrubber = getByClass(player.scrubberClass, this.wrapper),
progress = getByClass(player.progressClass, this.wrapper);
@ -181,7 +182,7 @@
p = this.duration * percent,
m = Math.floor(p / 60),
s = Math.floor(p % 60);
played.innerHTML = ((m<10?'0':'')+m+':'+(s<10?'0':'')+s);
played.innerHTML = ((m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s);
}
},
@ -191,7 +192,7 @@
// Used to create a single `audiojs` instance.
// If an array is passed then it calls back to `createAll()`.
// Otherwise, it creates a single instance and returns it.
create: function(element, options) {
create: function (element, options) {
var options = options || {}
if (element.length) {
return this.createAll(options, element);
@ -203,7 +204,7 @@
// `createAll()`
// Creates multiple `audiojs` instances.
// If `elements` is `null`, then automatically find any `<audio>` tags on the page and create `audiojs` instances for them.
createAll: function(options, elements) {
createAll: function (options, elements) {
var audioElements = elements || document.getElementsByTagName('audio'),
instances = []
options = options || {};
@ -215,11 +216,11 @@
// ### Creating and returning a new instance
// This goes through all the steps required to build out a usable `audiojs` instance.
newInstance: function(element, options) {
newInstance: function (element, options) {
var element = element,
s = this.helpers.clone(this.settings),
id = 'audiojs'+this.instanceCount,
wrapperId = 'audiojs_wrapper'+this.instanceCount,
id = 'audiojs' + this.instanceCount,
wrapperId = 'audiojs_wrapper' + this.instanceCount,
instanceCount = this.instanceCount++;
// Check for `autoplay`, `loop` and `preload` attributes and write them into the settings.
@ -257,7 +258,7 @@
// ### Helper methods for constructing a working player
// Inject a wrapping div and the markup for the html player.
createPlayer: function(element, player, id) {
createPlayer: function (element, player, id) {
var wrapper = document.createElement('div'),
newElement = element.cloneNode(true);
wrapper.setAttribute('class', 'audiojs');
@ -280,12 +281,12 @@
},
// Attaches useful event callbacks to an `audiojs` instance.
attachEvents: function(wrapper, audio) {
attachEvents: function (wrapper, audio) {
if (!audio.settings.createPlayer) return;
var player = audio.settings.createPlayer,
playPause = getByClass(player.playPauseClass, wrapper),
scrubber = getByClass(player.scrubberClass, wrapper),
leftPos = function(elem) {
leftPos = function (elem) {
var curleft = 0;
if (elem.offsetParent) {
do { curleft += elem.offsetLeft; } while (elem = elem.offsetParent);
@ -293,11 +294,11 @@
return curleft;
};
container[audiojs].events.addListener(playPause, 'click', function(e) {
container[audiojs].events.addListener(playPause, 'click', function (e) {
audio.playPause.apply(audio);
});
container[audiojs].events.addListener(scrubber, 'click', function(e) {
container[audiojs].events.addListener(scrubber, 'click', function (e) {
var relativeLeft = e.clientX - leftPos(this);
audio.skipTo(relativeLeft / scrubber.offsetWidth);
});
@ -308,15 +309,15 @@
// Start tracking the load progress of the track.
container[audiojs].events.trackLoadProgress(audio);
container[audiojs].events.addListener(audio.element, 'timeupdate', function(e) {
container[audiojs].events.addListener(audio.element, 'timeupdate', function (e) {
audio.updatePlayhead.apply(audio);
});
container[audiojs].events.addListener(audio.element, 'ended', function(e) {
container[audiojs].events.addListener(audio.element, 'ended', function (e) {
audio.trackEnded.apply(audio);
});
container[audiojs].events.addListener(audio.source, 'error', function(e) {
container[audiojs].events.addListener(audio.source, 'error', function (e) {
// on error, cancel any load timers that are running.
clearInterval(audio.readyTimer);
clearInterval(audio.loadTimer);
@ -326,28 +327,28 @@
},
// Flash requires a slightly different API to the `<audio>` element, so this method is used to overwrite the standard event handlers.
attachFlashEvents: function(element, audio) {
attachFlashEvents: function (element, audio) {
audio['swfReady'] = false;
audio['load'] = function(mp3) {
audio['load'] = function (mp3) {
// If the swf isn't ready yet then just set `audio.mp3`. `init()` will load it in once the swf is ready.
audio.mp3 = mp3;
if (audio.swfReady) audio.element.load(mp3);
}
audio['loadProgress'] = function(percent, duration) {
audio['loadProgress'] = function (percent, duration) {
audio.loadedPercent = percent;
audio.duration = duration;
audio.settings.loadStarted.apply(audio);
audio.settings.loadProgress.apply(audio, [percent]);
}
audio['skipTo'] = function(percent) {
audio['skipTo'] = function (percent) {
if (percent > audio.loadedPercent) return;
audio.updatePlayhead.call(audio, [percent])
audio.element.skipTo(percent);
}
audio['updatePlayhead'] = function(percent) {
audio['updatePlayhead'] = function (percent) {
audio.settings.updatePlayhead.apply(audio, [percent]);
}
audio['play'] = function() {
audio['play'] = function () {
// If the audio hasn't started preloading, then start it now.
// Then set `preload` to `true`, so that any tracks loaded in subsequently are loaded straight away.
if (!audio.settings.preload) {
@ -360,16 +361,16 @@
audio.element.pplay();
audio.settings.play.apply(audio);
}
audio['pause'] = function() {
audio['pause'] = function () {
audio.playing = false;
// Use `ppause()` for consistency with `pplay()`, even though it isn't really required.
audio.element.ppause();
audio.settings.pause.apply(audio);
}
audio['setVolume'] = function(v) {
audio['setVolume'] = function (v) {
audio.element.setVolume(v);
}
audio['loadStarted'] = function() {
audio['loadStarted'] = function () {
// Load the mp3 specified by the audio element into the swf.
audio.swfReady = true;
if (audio.settings.preload) audio.element.init(audio.mp3);
@ -379,7 +380,7 @@
// ### Injecting an swf from a string
// Build up the swf source by replacing the `$keys` and then inject the markup into the page.
injectFlash: function(audio, id) {
injectFlash: function (audio, id) {
var flashSource = this.flashSource.replace(/\$1/g, id);
flashSource = flashSource.replace(/\$2/g, audio.settings.swfLocation);
// `(+new Date)` ensures the swf is not pulled out of cache. The fixes an issue with Firefox running multiple players on the same page.
@ -396,7 +397,7 @@
helpers: {
// **Merge two objects, with `obj2` overwriting `obj1`**
// The merge is shallow, but that's all that is required for our purposes.
merge: function(obj1, obj2) {
merge: function (obj1, obj2) {
for (attr in obj2) {
if (obj1.hasOwnProperty(attr) || obj2.hasOwnProperty(attr)) {
obj1[attr] = obj2[attr];
@ -404,25 +405,25 @@
}
},
// **Clone a javascript object (recursively)**
clone: function(obj){
if (obj == null || typeof(obj) !== 'object') return obj;
clone: function (obj) {
if (obj == null || typeof (obj) !== 'object') return obj;
var temp = new obj.constructor();
for (var key in obj) temp[key] = arguments.callee(obj[key]);
return temp;
},
// **Adding/removing classnames from elements**
addClass: function(element, className) {
var re = new RegExp('(\\s|^)'+className+'(\\s|$)');
addClass: function (element, className) {
var re = new RegExp('(\\s|^)' + className + '(\\s|$)');
if (re.test(element.className)) return;
element.className += ' ' + className;
},
removeClass: function(element, className) {
var re = new RegExp('(\\s|^)'+className+'(\\s|$)');
element.className = element.className.replace(re,' ');
removeClass: function (element, className) {
var re = new RegExp('(\\s|^)' + className + '(\\s|$)');
element.className = element.className.replace(re, ' ');
},
// **Dynamic CSS injection**
// Takes a string of css, inserts it into a `<style>`, then injects it in at the very top of the `<head>`. This ensures any user-defined styles will take precedence.
injectCss: function(audio, string) {
injectCss: function (audio, string) {
// If an `audiojs` `<style>` tag already exists, then append to it rather than creating a whole new `<style>`.
var prepend = '',
@ -456,7 +457,7 @@
},
// **Handle all the IE6+7 requirements for cloning `<audio>` nodes**
// Create a html5-safe document fragment by injecting an `<audio>` element into the document fragment.
cloneHtml5Node: function(audioTag) {
cloneHtml5Node: function (audioTag) {
var fragment = document.createDocumentFragment();
fragment.createElement('audio');
var div = fragment.createElement('div');
@ -465,7 +466,7 @@
return div.firstChild;
},
// **Cross-browser `<object>` / `<embed>` element selection**
getSwf: function(name) {
getSwf: function (name) {
var swf = document[name] || window[name];
return swf.length > 1 ? swf[swf.length - 1] : swf;
}
@ -475,7 +476,7 @@
memoryLeaking: false,
listeners: [],
// **A simple cross-browser event handler abstraction**
addListener: function(element, eventName, func) {
addListener: function (element, eventName, func) {
// For modern browsers use the standard DOM-compliant `addEventListener`.
if (element.addEventListener) {
element.addEventListener(eventName, func, false);
@ -484,8 +485,8 @@
} else if (element.attachEvent) {
this.listeners.push(element);
if (!this.memoryLeaking) {
window.attachEvent('onunload', function() {
if(this.listeners) {
window.attachEvent('onunload', function () {
if (this.listeners) {
for (var i = 0, ii = this.listeners.length; i < ii; i++) {
container[audiojs].events.purge(this.listeners[i]);
}
@ -493,13 +494,13 @@
});
this.memoryLeaking = true;
}
element.attachEvent('on' + eventName, function() {
element.attachEvent('on' + eventName, function () {
func.call(element, window.event);
});
}
},
trackLoadProgress: function(audio) {
trackLoadProgress: function (audio) {
// If `preload` has been set to `none`, then we don't want to start loading the track yet.
if (!audio.settings.preload) return;
@ -509,7 +510,7 @@
ios = (/(ipod|iphone|ipad)/i).test(navigator.userAgent);
// Use timers here rather than the official `progress` event, as Chrome has issues calling `progress` when loading mp3 files from cache.
readyTimer = setInterval(function() {
readyTimer = setInterval(function () {
if (audio.element.readyState > -1) {
// iOS doesn't start preloading the mp3 until the user interacts manually, so this stops the loader being displayed prematurely.
if (!ios) audio.init.apply(audio);
@ -518,7 +519,7 @@
if (audio.settings.autoplay) audio.play.apply(audio);
clearInterval(readyTimer);
// Once we have data, start tracking the load progress.
loadTimer = setInterval(function() {
loadTimer = setInterval(function () {
audio.loadProgress.apply(audio);
if (audio.loadedPercent >= 1) clearInterval(loadTimer);
});
@ -531,7 +532,7 @@
// **Douglas Crockford's IE6 memory leak fix**
// <http://javascript.crockford.com/memory/leak.html>
// This is used to release the memory leak created by the circular references created when fixing `this` scoping for IE. It is called on page unload.
purge: function(d) {
purge: function (d) {
var a = d.attributes, i;
if (a) {
for (i = 0; i < a.length; i += 1) {
@ -546,25 +547,26 @@
// **DOMready function**
// As seen here: <https://github.com/dperini/ContentLoaded/>.
ready: (function() { return function(fn) {
ready: (function () {
return function (fn) {
var win = window, done = false, top = true,
doc = win.document, root = doc.documentElement,
add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
pre = doc.addEventListener ? '' : 'on',
init = function(e) {
init = function (e) {
if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
(e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
if (!done && (done = true)) fn.call(win, e.type || e);
},
poll = function() {
try { root.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; }
poll = function () {
try { root.doScroll('left'); } catch (e) { setTimeout(poll, 50); return; }
init('poll');
};
if (doc.readyState == 'complete') fn.call(win, 'lazy');
else {
if (doc.createEventObject && root.doScroll) {
try { top = !win.frameElement; } catch(e) { }
try { top = !win.frameElement; } catch (e) { }
if (top) poll();
}
doc[add](pre + 'DOMContentLoaded', init, false);
@ -579,13 +581,13 @@
// ## The audiojs class
// We create one of these per `<audio>` and then push them into `audiojs['instances']`.
container[audiojsInstance] = function(element, settings) {
container[audiojsInstance] = function (element, settings) {
// Each audio instance returns an object which contains an API back into the `<audio>` element.
this.element = element;
this.wrapper = element.parentNode;
this.source = element.getElementsByTagName('source')[0] || element;
// First check the `<audio>` element directly for a src and if one is not found, look for a `<source>` element.
this.mp3 = (function(element) {
this.mp3 = (function (element) {
var source = element.getElementsByTagName('source')[0];
return element.getAttribute('src') || (source ? source.getAttribute('src') : null);
})(element);
@ -599,16 +601,16 @@
container[audiojsInstance].prototype = {
// API access events:
// Each of these do what they need do and then call the matching methods defined in the settings object.
updatePlayhead: function() {
updatePlayhead: function () {
var percent = this.element.currentTime / this.duration;
this.settings.updatePlayhead.apply(this, [percent]);
},
skipTo: function(percent) {
skipTo: function (percent) {
if (percent > this.loadedPercent) return;
this.element.currentTime = this.duration * percent;
this.updatePlayhead();
},
load: function(mp3) {
load: function (mp3) {
this.loadStartedCalled = false;
this.source.setAttribute('src', mp3);
// The now outdated `load()` method is required for Safari 4
@ -616,13 +618,13 @@
this.mp3 = mp3;
container[audiojs].events.trackLoadProgress(this);
},
loadError: function() {
loadError: function () {
this.settings.loadError.apply(this);
},
init: function() {
init: function () {
this.settings.init.apply(this);
},
loadStarted: function() {
loadStarted: function () {
// Wait until `element.duration` exists before setting up the audio player.
if (!this.element.duration) return false;
@ -630,7 +632,7 @@
this.updatePlayhead();
this.settings.loadStarted.apply(this);
},
loadProgress: function() {
loadProgress: function () {
if (this.element.buffered != null && this.element.buffered.length) {
// Ensure `loadStarted()` is only called once.
if (!this.loadStartedCalled) {
@ -642,11 +644,11 @@
this.settings.loadProgress.apply(this, [this.loadedPercent]);
}
},
playPause: function() {
playPause: function () {
if (this.playing) this.pause();
else this.play();
},
play: function() {
play: function () {
var ios = (/(ipod|iphone|ipad)/i).test(navigator.userAgent);
// On iOS this interaction will trigger loading the mp3, so run `init()`.
if (ios && this.element.readyState == 0) this.init.apply(this);
@ -661,15 +663,15 @@
this.element.play();
this.settings.play.apply(this);
},
pause: function() {
pause: function () {
this.playing = false;
this.element.pause();
this.settings.pause.apply(this);
},
setVolume: function(v) {
setVolume: function (v) {
this.element.volume = v;
},
trackEnded: function(e) {
trackEnded: function (e) {
this.skipTo.apply(this, [0]);
if (!this.settings.loop) this.pause.apply(this);
this.settings.trackEnded.apply(this);
@ -679,7 +681,7 @@
// **getElementsByClassName**
// Having to rely on `getElementsByTagName` is pretty inflexible internally, so a modified version of Dustin Diaz's `getElementsByClassName` has been included.
// This version cleans things up and prefers the native DOM method if it's available.
var getByClass = function(searchClass, node, tag) {
var getByClass = function (searchClass, node, tag) {
var matches = [];
if (document.getElementsByClassName) {
matches = node.getElementsByClassName(searchClass);
@ -687,7 +689,7 @@
var node = node || document,
tag = tag || '*',
els = node.getElementsByTagName(tag),
pattern = new RegExp("(^|\\s)"+searchClass+"(\\s|$)");
pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
for (i = 0, j = 0, l = els.length; i < l; i++) {
if (pattern.test(els[i].className)) {
@ -698,5 +700,5 @@
}
return matches.length > 1 ? matches : matches[0];
}
// The global variable names are passed in here and can be changed if they conflict with anything else.
// The global variable names are passed in here and can be changed if they conflict with anything else.
})('audiojs', 'audiojsInstance', this);

View file

@ -0,0 +1,59 @@
/* Generic context menu styles */
.contextMenu {
position: absolute;
width: 115px;
z-index: 99999;
border: solid 1px #DEDEDE;
background: #fff;
padding: 0px;
margin: 0px;
display: none;
}
.contextMenu LI {
list-style: none;
padding: 0px;
margin: 0px;
}
.contextMenu A {
color: #333;
text-decoration: none;
display: block;
line-height: 20px;
height: 20px;
background-position: 9px center;
background-repeat: no-repeat;
outline: none;
padding: 3px 5px;
padding-left: 32px;
font-size: 16px;
font-variant: small-caps;
}
.contextMenu LI.hover A {
color: #FFF;
background-color: #3399FF;
}
.contextMenu LI.disabled A {
color: #AAA;
cursor: default;
}
.contextMenu LI.hover.disabled A {
background-color: transparent;
}
.contextMenu LI.separator {
border-top: solid 1px #CCC;
}
/*
Adding Icons
You can add icons to the context menu by adding
classes to the respective LI element(s)
*/
.contextMenu LI.download A { background-image: url(../../images/download_gd_12x16.png); }

View file

@ -0,0 +1,209 @@
// jQuery Context Menu Plugin
//
// Version 1.01
//
// Cory S.N. LaViska
// A Beautiful Site (http://abeautifulsite.net/)
//
// More info: http://abeautifulsite.net/2008/09/jquery-context-menu-plugin/
//
// Terms of Use
//
// This plugin is dual-licensed under the GNU General Public License
// and the MIT License and is copyright A Beautiful Site, LLC.
//
if (jQuery) (function () {
$.extend($.fn, {
contextMenu: function (o, callback) {
// Defaults
if (o.menu == undefined) return false;
if (o.inSpeed == undefined) o.inSpeed = 150;
if (o.outSpeed == undefined) o.outSpeed = 75;
// 0 needs to be -1 for expected results (no fade)
if (o.inSpeed == 0) o.inSpeed = -1;
if (o.outSpeed == 0) o.outSpeed = -1;
// Loop each context menu
$(this).live("mousedown", function (e) {
var el = $(this);
var offset = $(el).offset();
// Add contextMenu class
$('#' + o.menu).addClass('contextMenu');
var evt = e;
evt.stopPropagation();
$(this).mouseup(function (e) {
e.stopPropagation();
var srcElement = $(this);
$(this).unbind('mouseup');
if (evt.button == 2) {
// Hide context menus that may be showing
$(".contextMenu").hide();
// Get this context menu
var menu = $('#' + o.menu);
if ($(el).hasClass('disabled')) return false;
// Detect mouse position
var d = {}, x, y;
if (self.innerHeight) {
d.pageYOffset = self.pageYOffset;
d.pageXOffset = self.pageXOffset;
d.innerHeight = self.innerHeight;
d.innerWidth = self.innerWidth;
} else if (document.documentElement &&
document.documentElement.clientHeight) {
d.pageYOffset = document.documentElement.scrollTop;
d.pageXOffset = document.documentElement.scrollLeft;
d.innerHeight = document.documentElement.clientHeight;
d.innerWidth = document.documentElement.clientWidth;
} else if (document.body) {
d.pageYOffset = document.body.scrollTop;
d.pageXOffset = document.body.scrollLeft;
d.innerHeight = document.body.clientHeight;
d.innerWidth = document.body.clientWidth;
}
(e.pageX) ? x = e.pageX : x = e.clientX + d.scrollLeft;
(e.pageY) ? y = e.pageY : y = e.clientY + d.scrollTop;
// Show the menu
//$(document).unbind('click');
$(menu).css({ top: y, left: x }).fadeIn(o.inSpeed);
// Hover events
$(menu).find('A').mouseover(function () {
$(menu).find('LI.hover').removeClass('hover');
$(this).parent().addClass('hover');
}).mouseout(function () {
$(menu).find('LI.hover').removeClass('hover');
});
// Keyboard
$(document).keypress(function (e) {
switch (e.keyCode) {
case 38: // up
if ($(menu).find('LI.hover').size() == 0) {
$(menu).find('LI:last').addClass('hover');
} else {
$(menu).find('LI.hover').removeClass('hover').prevAll('LI:not(.disabled)').eq(0).addClass('hover');
if ($(menu).find('LI.hover').size() == 0) $(menu).find('LI:last').addClass('hover');
}
break;
case 40: // down
if ($(menu).find('LI.hover').size() == 0) {
$(menu).find('LI:first').addClass('hover');
} else {
$(menu).find('LI.hover').removeClass('hover').nextAll('LI:not(.disabled)').eq(0).addClass('hover');
if ($(menu).find('LI.hover').size() == 0) $(menu).find('LI:first').addClass('hover');
}
break;
case 13: // enter
$(menu).find('LI.hover A').trigger('click');
break;
case 27: // esc
$(document).trigger('click');
break
}
});
// When items are selected
$('#' + o.menu).find('A').unbind('click');
$('#' + o.menu).find('LI:not(.disabled) A').click(function () {
//$(document).unbind('click').unbind('keypress');
$(".contextMenu").hide();
// Callback
if (callback) callback($(this).attr('href').substr(1), $(srcElement), { x: x - offset.left, y: y - offset.top, docX: x, docY: y });
return false;
});
// Hide bindings
setTimeout(function () { // Delay for Mozilla
$(document).click(function () {
//$(document).unbind('click').unbind('keypress');
$(menu).fadeOut(o.outSpeed);
return false;
});
}, 0);
}
});
// Disable text selection
if ($.browser.mozilla) {
$('#' + o.menu).each(function () { $(this).css({ 'MozUserSelect': 'none' }); });
} else if ($.browser.msie) {
$('#' + o.menu).each(function () { $(this).bind('selectstart.disableTextSelect', function () { return false; }); });
} else {
$('#' + o.menu).each(function () { $(this).bind('mousedown.disableTextSelect', function () { return false; }); });
}
// Disable browser context menu (requires both selectors to work in IE/Safari + FF/Chrome)
$(el).add($('UL.contextMenu')).bind('contextmenu', function () { return false; });
});
return $(this);
},
// Disable context menu items on the fly
disableContextMenuItems: function (o) {
if (o == undefined) {
// Disable all
$(this).find('LI').addClass('disabled');
return ($(this));
}
$(this).each(function () {
if (o != undefined) {
var d = o.split(',');
for (var i = 0; i < d.length; i++) {
$(this).find('A[href="' + d[i] + '"]').parent().addClass('disabled');
}
}
});
return ($(this));
},
// Enable context menu items on the fly
enableContextMenuItems: function (o) {
if (o == undefined) {
// Enable all
$(this).find('LI.disabled').removeClass('disabled');
return ($(this));
}
$(this).each(function () {
if (o != undefined) {
var d = o.split(',');
for (var i = 0; i < d.length; i++) {
$(this).find('A[href="' + d[i] + '"]').parent().removeClass('disabled');
}
}
});
return ($(this));
},
// Disable context menu(s)
disableContextMenu: function () {
$(this).each(function () {
$(this).addClass('disabled');
});
return ($(this));
},
// Enable context menu(s)
enableContextMenu: function () {
$(this).each(function () {
$(this).removeClass('disabled');
});
return ($(this));
},
// Destroy context menu(s)
destroyContextMenu: function () {
// Destroy specified context menus
$(this).each(function () {
// Disable action
$(this).unbind('mousedown').unbind('mouseup');
});
return ($(this));
}
});
})(jQuery);

67
js/ui-load.js Normal file
View file

@ -0,0 +1,67 @@
$(window).load(function () {
if ($.cookie('defaultsmwidth')) {
var width = $.cookie('defaultsmwidth');
resizeSMSection(width);
}
if ($.cookie('sidebar') && $.cookie('username') && $.cookie('password')) {
$('#SideBar').show();
updateChatMessages();
updateNowPlaying();
}
if ($.cookie('HideAZ')) {
$('#BottomContainer').hide();
}
$('ul#ChangeLog li.log').each(function (i, el) {
if (i > 3) {
$(el).hide();
}
});
resizeContent();
});
window.onbeforeunload = function () {
closeAllNotifications();
};
$(window).resize(function () {
resizeContent();
});
function resizeContent() {
var screenwidth = $(window).width();
$('.tabcontent').css({ 'height': (($(window).height() - 112)) + 'px' });
$('.smsection').css({ 'height': (($(window).height() - 152)) + 'px' });
var smheight = $('.smsection').height();
var smwidth = $('.smsection').width();
$('#BottomContainer').css({ 'top': smheight + 34 + 'px' });
if ($.cookie('sidebar')) {
var tabwidth = $(window).width() - 268;
if (tabwidth >= 700) {
$('.tabcontent').css({ 'width': tabwidth + 'px' });
}
var sbheight = $(window).height() - 152;
$('#SideBar').css({ 'height': (sbheight + 108) + 'px' });
$('#ChatMsgs').css({ 'height': (sbheight - 166) + 'px' });
} else {
var tabwidth = $(window).width() - 61;
if (tabwidth >= 700) {
$('.tabcontent').css({ 'width': tabwidth + 'px' });
}
}
var tabwidth = $('.tabcontent').width();
$('#AlbumContainer, #TrackContainer, #CurrentPlaylistContainer').css({ 'width': (tabwidth - smwidth - 30) + 'px' });
$('#CurrentPlaylistContainer').css({ 'width': (tabwidth - 30) + 'px' });
$('#player').css({ 'width': tabwidth + 'px' });
}
function resizeSMSection(x) {
var defwidth = 200;
var smwidth = $('.smsection').width();
var newsmwidth = smwidth + parseInt(x);
var newwidth = newsmwidth - defwidth;
if (smwidth != newsmwidth && newsmwidth > 150 && newsmwidth < 500) {
$('.smsection').css({ 'width': (newsmwidth) + 'px' });
$('.actions').css({ 'width': (newsmwidth - 5) + 'px' });
$('#BottomContainer').css({ 'width': (newsmwidth - 16) + 'px' });
$.cookie('defaultsmwidth', newwidth, { expires: 365, path: '/' });
var ulwidth = newsmwidth + 6;
$('#AlbumContainer').css({ 'margin-left': ulwidth + 'px' });
$('#TrackContainer').css({ 'margin-left': ulwidth + 'px' });
}
}

488
js/ui-ready.js Normal file
View file

@ -0,0 +1,488 @@
$(document).ready(function () {
// Tabs
$(".tabcontent").hide(); //Hide all content
if ($.cookie('username') == null) {
$('ul.tabs li a').each(function () {
if ($(this).attr("href") == '#tabPreferences') {
$(this).addClass("active"); //Add "active" class to selected tab
}
});
$("#tabPreferences").show(); //Show first tab content
loadTabContent('#tabPreferences');
} else {
if (window.location.hash) {
var hash = window.location.hash;
$('ul.tabs li a').each(function () {
if ($(this).attr("href") == hash) {
$(this).addClass("active"); //Add "active" class to selected tab
}
});
$(hash).show(); //Fade in the active ID content
loadTabContent(hash);
} else {
$("ul.tabs li:first a").addClass("active").show(); //Activate first tab
$(".tabcontent:first").show(); //Show first tab content
var firstTab = $("ul.tabs li:first a").attr("href");
loadTabContent(firstTab);
}
}
// Tabs - Click Event
$("ul.tabs li a").click(function () {
$("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).show(); //Fade in the active ID content
loadTabContent(activeTab);
});
// Ajax Loading Screen
$(".toploading").ajaxStart(function () {
$(this).show();
});
$(".toploading").ajaxStop(function () {
$(this).hide();
});
// Keyboard shortcuts
$(document).keydown(function (e) {
var source = e.target.id;
if (source != 'Search' && source != 'ChatMsg') {
var unicode = e.charCode ? e.charCode : e.keyCode;
// a-z
if (unicode >= 65 && unicode <= 90) {
var key = findKeyForCode(unicode);
var el = '#index_' + key.toUpperCase();
$('#Artists').stop().scrollTo(el, 400);
// right arrow
} else if (unicode == 39 || unicode == 176) {
var next = $('#CurrentPlaylistContainer tr.playing').next();
if (!next.length) next = $('#CurrentPlaylistContainer li').first();
changeTrack(next);
// back arrow
} else if (unicode == 37 || unicode == 177) {
var prev = $('#CurrentPlaylistContainer tr.playing').prev();
if (!prev.length) prev = $('#CurrentPlaylistContainer tr').last();
changeTrack(prev);
// spacebar
} else if (unicode == 32 || unicode == 179 || unicode == 0179) {
playPauseSong();
} else if (unicode == 36) {
$('#Artists').stop().scrollTo('#auto', 400);
}
}
});
// Main Click Events
// Albums Click Event
$('#MusicFolders').live('change', function () {
var folder = $(this).val();
loadArtists(folder, true);
$.cookie('MusicFolders', folder, { expires: 365 });
});
$('#ArtistContainer li.item').live('click', function () {
$('#AutoAlbumContainer li').removeClass('selected');
$('#ArtistContainer li').removeClass('selected');
$(this).addClass('selected');
getAlbums($(this).attr("id"), '', '#AlbumRows');
});
$('#BottomIndex li a').live('click', function () {
var el = '#index_' + $(this).text();
$('#Artists').stop().scrollTo(el, 400);
return false;
});
$('#AutoAlbumContainer li.item').live('click', function () {
$('#AutoAlbumContainer li').removeClass('selected');
$('#ArtistContainer li').removeClass('selected');
$(this).addClass('selected');
getAlbumListBy($(this).attr("id"));
});
$('tr.album a.play').live('click', function (e) {
var albumid = $(this).parent().parent().attr('childid');
var artistid = $(this).parent().parent().attr('parentid');
getAlbums(albumid, 'autoplay', '#CurrentPlaylistContainer');
return false;
});
$('tr.album a.add').live('click', function (e) {
var albumid = $(this).parent().parent().attr('childid');
var artistid = $(this).parent().parent().attr('parentid');
getAlbums(albumid, 'add', '#CurrentPlaylistContainer');
return false;
});
$('tr.album a.download').live('click', function (event) {
var itemid = $(this).parent().parent().attr('childid');
downloadItem(itemid);
return false;
});
$('tr.album a.rate').live('click', function (event) {
var itemid = $(this).parent().parent().attr('childid');
rateSong(itemid, 5);
$(this).removeClass('rate');
$(this).addClass('favorite');
return false;
});
$('tr.album a.favorite').live('click', function (event) {
var itemid = $(this).parent().parent().attr('childid');
rateSong(itemid, 0);
$(this).removeClass('favorite');
$(this).addClass('rate');
return false;
});
$('tr.album').live('click', function (e) {
var albumid = $(this).attr('childid');
var artistid = $(this).attr('parentid');
getAlbums(albumid, '', '#AlbumRows');
return false;
});
// Track - Click Events
// Multiple Select
$('.noselect').disableTextSelect();
var lastChecked = null;
$('table.songlist tr.song').live('click', function (event) {
var checkboxclass = 'table.songlist tr.song';
var songid = $(this).attr('childid');
var albumid = $(this).attr('parentid');
if (!event.ctrlKey) {
$(checkboxclass).removeClass('selected');
}
if ($(this).hasClass('selected')) {
$(this).removeClass('selected');
} else {
$(this).addClass('selected');
}
if (!lastChecked) {
lastChecked = this;
return;
}
if (event.shiftKey) {
var start = $(checkboxclass).index(this);
var end = $(checkboxclass).index(lastChecked);
for (i = Math.min(start, end); i <= Math.max(start, end); i++) {
$(checkboxclass).eq(i).addClass('selected');
}
}
lastChecked = this;
});
// Double Click
$('table.songlist tr.song').live('dblclick', function (e) {
e.preventDefault();
//$(this).addClass('playing').siblings().removeClass('playing');
var songid = $(this).attr('childid');
var albumid = $(this).attr('parentid');
playSong('', this, songid, albumid);
});
$('table.songlist tr.song a.play').live('click', function (event) {
var songid = $(this).parent().parent().attr('childid');
var albumid = $(this).parent().parent().attr('parentid');
playSong($(this).parent().parent(), songid, albumid);
return false;
});
$('table.songlist tr.song a.add').live('click', function (event) {
var track = $(this).parent().parent();
$(track).clone().appendTo('#CurrentPlaylistContainer');
return false;
});
$('table.songlist tr.song a.remove').live('click', function (event) {
var track = $(this).parent().parent();
$(track).remove();
refreshRowColor();
return false;
});
$('table.songlist tr.song a.rate').live('click', function (event) {
var songid = $(this).parent().parent().attr('childid');
rateSong(songid, 5);
$(this).removeClass('rate');
$(this).addClass('favorite');
return false;
});
$('table.songlist tr.song a.favorite').live('click', function (event) {
var songid = $(this).parent().parent().attr('childid');
rateSong(songid, 0);
$(this).removeClass('favorite');
$(this).addClass('rate');
return false;
});
$('li.index').live('click', function (e) {
$('#Artists').stop().scrollTo('#auto', 400);
return false;
});
// Music Library Click Events
$('a#action_AddToPlaylist').click(function () {
var submenu = $('div#submenu_AddToPlaylist');
if (submenu.is(":visible")) {
submenu.fadeOut();
} else {
loadPlaylistsForMenu('submenu_AddToPlaylist');
//get the position of the placeholder element
pos = $(this).offset();
width = $(this).width();
height = $(this).height();
//show the menu directly over the placeholder
submenu.css({ "left": (pos.left) + "px", "top": (pos.top + height + 14) + "px" }).fadeIn(400);
}
return false;
});
var submenu_active = false;
$('div.submenu').mouseenter(function () {
submenu_active = true;
});
$('div.submenu').mouseleave(function () {
submenu_active = false;
setTimeout(function () { if (submenu_active == false) $('div.submenu').fadeOut(); }, 400);
});
$('a#action_AddToCurrent').click(function () {
addToCurrent();
return false;
});
$('#action_RefreshArtists').click(function () {
loadArtists("", true);
return false;
});
$('#action_IncreaseWidth').click(function () {
resizeSMSection(50);
return false;
});
$('#action_DecreaseWidth').click(function () {
resizeSMSection(-50);
return false;
});
$('#action_SelectAll').click(function () {
$('#Albums tr.song').each(function () {
$(this).addClass('selected');
});
return false;
});
$('#action_SelectNone').click(function () {
$('#Albums tr.song').each(function () {
$(this).removeClass('selected');
});
return false;
});
$('input#Search').keydown(function (e) {
var unicode = e.charCode ? e.charCode : e.keyCode;
if (unicode == 13) {
$('#action_Search').click();
}
});
$('#action_Search').click(function () {
var query = $('#Search').val();
search('song', query);
$('#Search').val("");
return false;
});
// Current Playlist Click Events
$('#action_Shuffle').live('click', function () {
$('#CurrentPlaylistContainer tr.song').shuffle();
refreshRowColor();
return false;
});
$('#action_Empty').live('click', function () {
$('#CurrentPlaylistContainer tbody').empty();
return false;
});
$('a#action_AddCurrentToPlaylist').click(function () {
var submenu = $('div#submenu_AddCurrentToPlaylist');
if (submenu.is(":visible")) {
submenu.fadeOut();
} else {
loadPlaylistsForMenu('submenu_AddCurrentToPlaylist');
//get the position of the placeholder element
pos = $(this).offset();
width = $(this).width();
height = $(this).height();
//show the menu directly over the placeholder
submenu.css({ "left": (pos.left) + "px", "top": (pos.top + height + 14) + "px" }).fadeIn(400);
}
});
$('#action_CurrentSelectAll').click(function () {
$('#CurrentPlaylist tr.song').each(function () {
$(this).addClass('selected');
});
return false;
});
$('#action_CurrentSelectNone').click(function () {
$('#CurrentPlaylist tr.song').each(function () {
$(this).removeClass('selected');
});
return false;
});
// Playlist Click Events
$('#AutoPlaylistContainer li.item').live('click', function () {
$('#AutoPlaylistContainer li').removeClass('selected');
$('#PlaylistContainer li').removeClass('selected');
$(this).addClass('selected');
getRandomSongList('', '#TrackContainer');
});
$('#AutoPlaylistContainer li.item a.play').live('click', function () {
getRandomSongList('autoplay', '#CurrentPlaylistContainer');
return false;
});
$('#AutoPlaylistContainer li.item a.add').live('click', function () {
getRandomSongList('', '#CurrentPlaylistContainer');
return false;
});
$('#PlaylistContainer li.item').live('click', function () {
$('#AutoPlaylistContainer li').removeClass('selected');
$('#PlaylistContainer li').removeClass('selected');
$(this).addClass('selected');
getPlaylist($(this).attr("id"), '', '#TrackContainer tbody');
});
$('#PlaylistContainer li.item a.play').live('click', function () {
getPlaylist($(this).parent().parent().attr("id"), 'autoplay', '#CurrentPlaylistContainer tbody');
return false;
});
$('#PlaylistContainer li.item a.add').live('click', function () {
getPlaylist($(this).parent().parent().attr("id"), '', '#CurrentPlaylistContainer tbody');
return false;
});
$('#action_DeletePlaylist').click(function () {
if ($('#PlaylistContainer li.selected').length > 0) {
if (confirmDelete()) {
$('#PlaylistContainer li.selected').each(function () {
deletePlaylist($(this).attr("id"));
});
}
}
return false;
});
$('#action_SavePlaylist').click(function () {
if ($('#PlaylistContainer li.selected').length > 0) {
$('#PlaylistContainer li.selected').each(function () {
savePlaylist($(this).attr("id"));
});
}
return false;
});
$('#action_RemoveSongs').click(function () {
if ($('#TrackContainer tr.selected').length > 0) {
$('#TrackContainer tr.selected').each(function () {
$(this).remove();
});
}
return false;
});
$('#action_ShufflePlaylist').live('click', function () {
$('#TrackContainer tr.song').shuffle();
refreshRowColor();
return false;
});
// Player Click Events
$('#PlayTrack').live('click', function () {
playPauseSong();
return false;
});
$('#NextTrack').live('click', function () {
var next = $('#CurrentPlaylistContainer tr.playing').next();
changeTrack(next);
return false;
});
$('#PreviousTrack').live('click', function () {
var prev = $('#CurrentPlaylistContainer tr.playing').prev();
changeTrack(prev);
return false;
});
$("a#coverartimage").fancybox({
'hideOnContentClick': true,
'type': 'image'
});
// Side Bar Click Events
$('#action_ToggleSideBar').live('click', function () {
if ($.cookie('sidebar')) {
$.cookie('sidebar', null);
$('#SideBar').hide();
stopUpdateChatMessages();
stopUpdateNowPlaying();
} else {
$.cookie('sidebar', true, { expires: 365 });
$('#SideBar').show();
updateChatMessages();
updateNowPlaying();
}
resizeContent();
return false;
});
$('input#ChatMsg').keydown(function (e) {
var unicode = e.charCode ? e.charCode : e.keyCode;
if (unicode == 13) {
var msg = $('#ChatMsg').val();
if (msg != '') {
addChatMessage(msg);
}
$('#ChatMsg').val("");
}
});
// Preferences Click Events
$('#SavePreferences').live('click', function () {
var username = $('#Username').val();
var password = $('#Password').val();
$.cookie('username', username, { expires: 365 });
$.cookie('password', password, { expires: 365 });
var AutoAlbumSize = $('#AutoAlbumSize').val();
var AutoPlaylistSize = $('#AutoPlaylistSize').val();
$.cookie('AutoAlbumSize', AutoAlbumSize, { expires: 365 });
$.cookie('AutoPlaylistSize', AutoPlaylistSize, { expires: 365 });
var server = $('#Server').val();
if (server != "") {
$.cookie('Server', server, { expires: 365 });
}
var applicationname = $('#ApplicationName').val();
if (applicationname != "") {
$.cookie('ApplicationName', applicationname, { expires: 365 });
}
location.reload(true);
});
$('#HideAZ').live('click', function () {
if ($('#HideAZ').is(':checked')) {
$.cookie('HideAZ', '1', { expires: 365 });
$('#BottomContainer').hide();
} else {
$.cookie('HideAZ', null);
$('#BottomContainer').show();
}
});
$('#EnableNotifications').live('click', function () {
if ($('#EnableNotifications').is(':checked')) {
requestPermissionIfRequired();
if (hasNotificationPermission()) {
$.cookie('EnableNotifications', '1', { expires: 365 });
}
} else {
$.cookie('EnableNotifications', null);
}
});
$('#ScrollTitle').live('click', function () {
if ($('#ScrollTitle').is(':checked')) {
$.cookie('ScrollTitle', '1', { expires: 365 });
}
});
$('input#Password').keydown(function (e) {
var unicode = e.charCode ? e.charCode : e.keyCode;
if (unicode == 13) {
$('#SavePreferences').click();
}
});
$('#ResetPreferences').live('click', function () {
$.cookie('username', null);
$.cookie('password', null);
$.cookie('AutoAlbumSize', null);
$.cookie('AutoPlaylistSize', null);
$.cookie('Server', null);
$.cookie('ApplicationName', null);
$.cookie('HideAZ', null);
location.reload(true);
});
$('#ChangeLogShowMore').live('click', function () {
$('ul#ChangeLog li.log').each(function (i, el) {
$(el).show();
});
return false;
});
}); // End document.ready

View file

@ -43,69 +43,91 @@ h3.title
}
#nav
{
height: 36px;
padding: 4px 0 0 52px;
background: url('../images/subsonic-36.png') no-repeat 6px 2px;
height: 127px;
padding: 44px 0 0 0;
background: url('../images/subsonic-36.png') no-repeat 6px 4px;
position: absolute;
top: 0px;
left: 0px;
z-index: 99;
}
#nav ul
{
list-style-type: none;
padding: 0;
margin: 0 0 0 5px;
margin: 0 0 0 4px;
}
#nav li
{
width: 50px;
}
#nav a
{
display: block;
padding: 6px 15px 5px 15px;
margin: 0 2px -1px 0;
float: left;
padding: 13px 9px 13px 15px;
color: #7C7C7C;
letter-spacing: -1px;
font-weight: bold;
font-size: 17px;
text-decoration: none;
background: #f4f4f4;
border-top: 1px solid #E4E4E4;
border-left: 1px solid #E4E4E4;
border-right: 1px solid #E4E4E4;
}
#nav a.active
{
color: #545454;
background: #EDEDED;
padding: 6px 15px 6px 15px;
border-top: 1px solid #CBCBCB;
border-left: 1px solid #CBCBCB;
border-right: 1px solid #CBCBCB;
border: 1px solid #E4E4E4;
border-right: 1px solid #D5D5D5;
border-bottom: none;
}
#nav a:hover
{
color: #545454;
border-top: 1px solid #CBCBCB;
border-left: 1px solid #CBCBCB;
border-right: 1px solid #CBCBCB;
border: 1px solid #D5D5D5;
border-bottom: none;
}
#nav a.first
{
border-top: 1px solid #E4E4E4;
}
#nav a.first:hover
{
color: #545454;
border: 1px solid #D5D5D5;
border-bottom: none;
}
#nav a.last
{
border-bottom: 1px solid #E4E4E4;
}
#nav a.last:hover
{
color: #545454;
border: 1px solid #D5D5D5;
}
#nav a.active
{
color: #545454;
background: #ffffff;
border: 1px solid #CBCBCB;
border-right: none;
}
#nav a.active:hover
{
color: #545454;
border: 1px solid #CBCBCB;
border-right: none;
}
#nav a img
{
display: block;
margin: 3px 0 4px 0;
margin: 3px 0 4px 1px;
}
#nav a.active img
{
display: block;
margin: 3px 0 4px 0;
margin: 3px 0 4px 1px;
}
#content
{
border-top: 1px solid #cbcbcb;
background: #EDEDED;
overflow: hidden;
margin-top: -1px;
padding: 0 5px;
}
#content h2
{
@ -117,6 +139,7 @@ h3.title
{
min-height: 400px;
min-width: 700px;
margin-left: 53px;
}
.smsection
{
@ -129,8 +152,8 @@ h3.title
background: #fff;
border-right: 1px solid #cbcbcb;
position: absolute;
top: 79px;
left: 6px;
top: 39px;
left: 60px;
}
.lgsection
{
@ -178,17 +201,24 @@ h3.title
display: none;
height: 35px;
width: 40px;
margin: 0 0 0 10px;
margin: 0 0 0 7px;
background: url('../images/ajax-loader.gif') no-repeat center center;
float: left;
}
#messages
{
display: none;
color: #A6CBF3;
float: left;
color: #4B95E5;
float: right;
font-size: 12px;
padding: 9px 0 0 10px;
padding: 5px;
position: absolute;
z-index: 99;
top: 0;
right: 58px;
background: #fff;
border: 1px solid #A6CBF3;
border-top: none;
}
/* Library Style */
ul.simplelist
@ -313,7 +343,7 @@ table.songlist tr.album
}
table.songlist tr.album td
{
padding: 4px;
padding: 4px 0 0 2px;
}
table.songlist tr.album td.title
{
@ -323,7 +353,8 @@ table.songlist tr.album td.artist
}
table.songlist tr.album td.itemactions
{
width: 80px;
width: 85px;
padding-left: 10px;
}
table.songlist tr.album td.albumart
{
@ -342,12 +373,11 @@ table.songlist tr.album a.play
width: 20px;
height: 50px;
display: block;
margin: 0 5px 0 0;
background: url('../images/play_gl_6x8.png') no-repeat 6px center;
background: url('../images/play_gl_6x8.png') no-repeat 7px center;
}
table.songlist tr.album a.play:hover
{
background: url('../images/play_6x8.png') no-repeat 6px center #DEECFB;
background: url('../images/play_6x8.png') no-repeat 7px center #DEECFB;
}
table.songlist tr.album a.add
{
@ -355,13 +385,24 @@ table.songlist tr.album a.add
width: 20px;
height: 50px;
display: block;
margin: 0 5px 0 0;
background: url('../images/plus_gl_8x8.png') no-repeat 6px center;
}
table.songlist tr.album a.add:hover
{
background: url('../images/plus_8x8.png') no-repeat 6px center #DEECFB;
}
table.songlist tr.album a.download
{
float: left;
width: 20px;
height: 50px;
display: block;
background: url('../images/download_gl_9x12.png') no-repeat 5px center;
}
table.songlist tr.album a.download:hover
{
background: url('../images/download_gd_9x12.png') no-repeat 5px center #DEECFB;
}
table.songlist tr.album a.remove
{
display: none;
@ -371,13 +412,12 @@ table.songlist tr.album a.rate
float: left;
height: 50px;
width: 20px;
margin: 0 5px 0 0;
display: block;
background: url('../images/star_w_12x12.png') 2px center no-repeat;
background: url('../images/star_wo_12x12.png') 2px center no-repeat;
}
table.songlist tr.album a.rate:hover
{
background: url('../images/star_wo_12x12.png') 2px center no-repeat;
background: url('../images/star_lgo_12x12.png') 2px center no-repeat;
}
table.songlist tr.album a.favorite
{
@ -401,7 +441,7 @@ table.songlist tr.song td
table.songlist tr.song td.itemactions
{
width: 80px;
padding-left: 25px;
padding-left: 22px;
}
table.songlist tr.song td.track
{
@ -438,11 +478,11 @@ table.songlist tr.song a.play
height: 20px;
display: block;
margin: 0;
background: url('../images/play_gl_6x8.png') no-repeat 6px center;
background: url('../images/play_gl_6x8.png') no-repeat 7px center;
}
table.songlist tr.song a.play:hover
{
background: url('../images/play_6x8.png') no-repeat 6px center #DEECFB;
background: url('../images/play_6x8.png') no-repeat 7px center #DEECFB;
}
table.songlist tr.song a.add
{
@ -464,11 +504,11 @@ table.songlist tr.song a.rate
width: 20px;
margin: 0;
display: block;
background: url('../images/star_w_12x12.png') center 3px no-repeat;
background: url('../images/star_wo_12x12.png') center 3px no-repeat;
}
table.songlist tr.song a.rate:hover
{
background: url('../images/star_wo_12x12.png') center 3px no-repeat;
background: url('../images/star_lgo_12x12.png') center 3px no-repeat;
}
table.songlist tr.song a.favorite
{
@ -572,6 +612,13 @@ background-color: #8dbdd8;
text-decoration: underline;
}
.actions
{
width: 195px;
height: 29px;
margin: 4px 0 0 0;
padding: 0 0 0 5px;
}
.subactions
{
height: 29px;
margin: 4px 0 0 0;
@ -612,7 +659,7 @@ background-color: #8dbdd8;
display: none;
width: 200px;
position: fixed;
top: 78px;
top: 38px;
right: 4px;
background: none repeat scroll 0 0 #FFFFFF;
border: 1px solid #CBCBCB;
@ -700,10 +747,10 @@ background-color: #8dbdd8;
/* Player Style */
#player
{
width: 100%;
background: #fff;
border: 1px solid #cbcbcb;
margin: 5px 0;
margin-left: 55px;
}
#audiocontainer
{
@ -948,6 +995,10 @@ select
margin: 5px;
border: 1px solid #d6d6d6;
}
select#MusicFolders
{
width: 94%;
}
.submit
{
margin: 0 5px;