Merging local changes

This commit is contained in:
Trevor Squillario 2014-01-12 16:06:54 -05:00
parent ea8409575c
commit 661053aee2
34 changed files with 1691 additions and 814 deletions

View file

@ -27,6 +27,7 @@ https://github.com/tsquillario/Jamstash/issues
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
* Getting Started https://github.com/tsquillario/Jamstash/wiki/Getting-Started
* Twitter (Release Announcements): https://twitter.com/JamstashApp * Twitter (Release Announcements): https://twitter.com/JamstashApp
* Live Beta: http://jamstash.com/beta * Live Beta: http://jamstash.com/beta
* Github Repo: https://github.com/tsquillario/Jamstash * Github Repo: https://github.com/tsquillario/Jamstash

View file

@ -11,20 +11,29 @@
<link rel="icon" href="images/favicon_32x32.png" sizes="32x32"/> <link rel="icon" href="images/favicon_32x32.png" sizes="32x32"/>
<link href="style/Style.css" rel="stylesheet" type="text/css" data-name="main" /> <link href="style/Style.css" rel="stylesheet" type="text/css" data-name="main" />
<link href="" rel="stylesheet" type="text/css" data-name="theme" /> <link href="" rel="stylesheet" type="text/css" data-name="theme" />
<link href="js/plugins/jquery.layout-default.css" rel="stylesheet" type="text/css" />
<link href="js/plugins/fancybox/jquery.fancybox.css" rel="stylesheet" type="text/css" /> <link href="js/plugins/fancybox/jquery.fancybox.css" rel="stylesheet" type="text/css" />
<script src="js/plugins/jquery-1.8.3.js" type="text/javascript"></script> <link href="js/plugins/jquery-split-pane.css" rel="stylesheet" />
<script src="js/plugins/jquery-2.0.3.min.js" type="text/javascript"></script>
<script src="js/plugins/angular.min.js" type="text/javascript"></script> <script src="js/plugins/angular.min.js" type="text/javascript"></script>
<script src="js/plugins/angular-cookies.js" type="text/javascript"></script> <script src="js/plugins/angular-sanitize.min.js" type="text/javascript"></script>
<script src="js/plugins/jquery-ui-1.9.2.custom.min.js" type="text/javascript"></script> <script src="js/plugins/angular-cookies.min.js" type="text/javascript"></script>
<script src="js/plugins/angular-resource.min.js" type="text/javascript"></script>
<!--
<script src="../js/plugins/angular.min.js"></script>
<script src="../js/plugins/angular-cookies.min.js"></script>
<script src="../js/plugins/angular-resource.min.js"></script>
<script src="../js/plugins/angular-route.min.js"></script>
-->
<script src="js/plugins/jquery-ui-1.10.3.custom.min.js" type="text/javascript"></script>
<script src="js/plugins/jquery-split-pane.js"></script>
<script src="js/plugins/fancybox/jquery.fancybox.js" type="text/javascript"></script> <script src="js/plugins/fancybox/jquery.fancybox.js" type="text/javascript"></script>
<script src="js/plugins/jquery.base64.js" type="text/javascript"></script> <script src="js/plugins/jquery.base64.js" type="text/javascript"></script>
<script src="js/plugins/jquery.dateFormat-1.0.js" type="text/javascript"></script> <script src="js/plugins/jquery.dateFormat-1.0.js" type="text/javascript"></script>
<script src="js/plugins/jquery.layout-latest.min.js" type="text/javascript"></script>
<script src="js/plugins/jquery.scrollTo.min.js" type="text/javascript"></script> <script src="js/plugins/jquery.scrollTo.min.js" type="text/javascript"></script>
<script src="js/plugins/UnityShim.js" type="text/javascript"></script> <script src="js/plugins/UnityShim.js" type="text/javascript"></script>
<script src="js/plugins/jplayer/jquery.jplayer.min.js" type="text/javascript"></script> <script src="js/plugins/jplayer/jquery.jplayer.min.js" type="text/javascript"></script>
<script src="js/app.js" type="text/javascript"></script> <script src="js/app.js" type="text/javascript"></script>
<script src="js/service.js" type="text/javascript"></script>
<script src="js/utils.js" type="text/javascript"></script> <script src="js/utils.js" type="text/javascript"></script>
<script src="js/controllers/main.js" type="text/javascript"></script> <script src="js/controllers/main.js" type="text/javascript"></script>
<script src="js/controllers/settings.js" type="text/javascript"></script> <script src="js/controllers/settings.js" type="text/javascript"></script>
@ -34,121 +43,121 @@
<script src="js/controllers/archive.js" type="text/javascript"></script> <script src="js/controllers/archive.js" type="text/javascript"></script>
<script src="js/player.js" type="text/javascript"></script> <script src="js/player.js" type="text/javascript"></script>
</head> </head>
<!--<body ng-controller="AppCtrl" layout state="bodyState" ng-init="bodyState = 1">-->
<body ng-controller="AppCtrl"> <body ng-controller="AppCtrl">
<div id="container"> <div id="container">
<div id="header"> <div id="header">
<div id="messages"></div> <div id="messages"></div>
<div id="loading"></div> <div id="loading"></div>
<a id="jslogo" title="Jamstash"></a> <a id="jslogo" title="Jamstash"></a>
<a id="sslogo" target="_blank" ng-show="settings.Server" ng-href="{{settings.Server}}" title="{{settings.Server}}"></a> <a id="sslogo" target="_blank" ng-show="settings.Server" ng-href="{{settings.Server}}" title="{{settings.Server}}"></a>
<div id="nav"> <div id="globalactions">
<ul class="tabs"> <a href="" class="button" title="Shuffle Queue" ng-click="queueShuffle()"><img src="images/fork_gd_11x12.png" /></a>
<li><a href="#/library" class="first" title="Library" ng-class="{'active': isActive('/library')}"><img src="images/headphones_gd_16x14.png" /></a></li> <a href="" class="button" id="action_Empty" title="Delete Queue" ng-click="queueEmpty()"><img src="images/trash_fill_gd_12x12.png" /></a>
<li><a href="#/playlists" title="Playlists" ng-class="{'active': isActive('/playlists')}"><img src="images/list_gd_16x14.png" /></a></li> <a href="" class="button" id="action_DeleteSelected" title="Delete Song(s) From Queue" ng-click="queueRemoveSelected()"><img src="images/minus_8x2.png" /></a>
<li><a href="#/podcasts" title="Podcasts" ng-class="{'active': isActive('/podcasts')}"><img src="images/rss_16x16.png" /></a></li> <!--<a href="" class="button buttonvertical" id="action_QueueToPlaylist" title="Create Playlist From Queue"><img src="images/list_gd_12x11.png" /></a>-->
<li><a href="#/archive" class="" title="Archive.org - Live Music Archive" ng-class="{'active': isActive('/archive')}"><img src="images/archive_gd_16x16.png" /></a></li>
<li><a href="#/settings" class="last" title="Settings" ng-class="{'active': isActive('/settings')}"><img src="images/cog_16x16.png" /></a></li>
</ul>
</div>
</div>
<div id="content">
<!-- Main -->
<div ng-view></div>
<div id="SideBar">
<div id="NowPlaying">
<div class="header"><img src="images/rss_12x12.png" /> Now Playing</div>
<div id="NowPlayingList"><span class="user">Loading...</span></div>
</div> </div>
<div id="Chat"> <div id="nav">
<div class="header"><img src="images/chat_alt_stroke_12x12.png" /> Chat</div> <ul class="tabs">
<div id="ChatMsgs"></div> <li><a href="#/library" class="first" title="Library" ng-class="{'active': isActive('/library')}"><img src="images/headphones_gd_16x14.png" /></a></li>
</div> <li><a href="#/playlists" title="Playlists" ng-class="{'active': isActive('/playlists')}"><img src="images/list_gd_16x14.png" /></a></li>
<div class="submit"><img src="images/comment_stroke_gl_12x11.png" /><input type="text" id="ChatMsg" class="chat" title="Hit [Enter] to Post" /></div> <li><a href="#/podcasts" title="Podcasts" ng-class="{'active': isActive('/podcasts')}"><img src="images/rss_16x16.png" /></a></li>
</div> <li><a href="#/archive" class="" title="Archive.org - Live Music Archive" ng-class="{'active': isActive('/archive')}"><img src="images/archive_gd_16x16.png" /></a></li>
<!-- Audio Player --> <li><a href="#/settings" class="last" title="Settings" ng-class="{'active': isActive('/settings')}"><img src="images/cog_16x16.png" /></a></li>
<div class="clear"></div>
<div class="clear"></div>
<div id="player">
<div id="playerleft" class="floatleft">
<div class="playeractions floatleft">
<a class="button" id="PreviousTrack" title="Previous Track" ng-click="previousTrack()"><img src="images/first_24x24.png" /></a>
<a class="button" id="PlayTrack" title="Play/Pause" ng-click="defaultPlay()"><img src="images/play_24x32.png" /></a>
<a class="button" id="PauseTrack" title="Play/Pause" style="display: none;"><img src="images/pause_24x32.png" /></a>
<a class="button" id="NextTrack" title="Next Track" ng-click="nextTrack()"><img src="images/last_24x24.png" /></a>
</div>
<div id="songdetails">
<div id="coverart"><a class="coverartfancy" href="{{playingSong.coverartfull}}"><img ng-src="{{playingSong.coverartthumb}}" src="images/albumdefault_60.jpg" alt=""/></a></div>
<ul>
<li class="song" id="{{playingSong.id}}" ng-bind-html-unsafe="playingSong.name"></li>
<li class="album" ng-bind-html-unsafe="playingSong.album"></li>
<li class="specs" ng-bind-html-unsafe="playingSong.specs"></li>
<li id="songdetails_controls">
<a href="" id="action_Mute" class="mute first" title="Mute"></a>
<a href="" id="action_UnMute" class="unmute first" title="Unmute" style="display: none;"></a>
<div class="jp-volume-bar"><div class="jp-volume-bar-value"></div></div><a href="" id="action_VolumeMax" class="volume" title="Max Volume"></a>
<a href="" class="loop" title="Repeat" ng-click="toggleSetting('Repeat')" ng-class="{'hoverSelected': !settings.Repeat }"></a>
<a href="" id="action_SaveProgress" class="lock" title="Save Track Position: On" ng-show="settings.SaveTrackPosition"></a>
<a title="Favorite" href="" ng-class="{'favorite': playingSong.starred, 'rate': !playingSong.starred}" ng-click="updateFavorite(playingSong)" stop-event="click"></a>
</li>
</ul> </ul>
<div class="vertshade"></div>
</div>
<div id="playdeck_1"></div>
<div id="playdeck_2"></div>
<div id="submenu_CurrentPlaylist" class="submenu shadow" style="display: none;">
<table id="CurrentPlaylistPreviewContainer" class="simplelist songlist">
<thead></thead>
<tbody></tbody>
</table>
</div> </div>
</div> </div>
<div class="playeractionssmall"><!--<a href="" class="button" id="action_ToggleSideBar" title="Toggle Side Bar"><img src="images/arrow_right_gl_12x12.png" /></a>--></div> <div id="content">
<div id="playermiddle"> <!-- Main -->
<div id="audiocontainer"> <div ng-view></div>
<div class="audiojs" id="audio_wrapper0">
<div class="scrubber"><div class="progress"></div><div class="loaded"></div></div> <div id="SideBar">
<div class="time"><em id="played">00:00</em>/<strong id="duration">00:00</strong></div> <div id="NowPlaying">
<div class="error-message"></div> <div class="header"><img src="images/rss_12x12.png" /> Now Playing</div>
<div id="NowPlayingList"><span class="user">Loading...</span></div>
</div> </div>
<div id="Chat">
<div class="header"><img src="images/chat_alt_stroke_12x12.png" /> Chat</div>
<div id="ChatMsgs"></div>
</div>
<div class="submit"><img src="images/comment_stroke_gl_12x11.png" /><input type="text" id="ChatMsg" class="chat" title="Hit [Enter] to Post" /></div>
</div> </div>
<div id="preview"></div> <!-- Audio Player -->
</div> <div class="clear"></div>
<div class="clear"></div>
</div> <div class="clear"></div>
</div><!-- end #content --> <div id="player">
</div> <!-- End container --> <div id="playerleft" class="floatleft">
<div id="footer"> <div class="playeractions floatleft">
<div id="QueuePreview"> <a class="button" id="PreviousTrack" title="Previous Track" ng-click="previousTrack()"><img src="images/first_24x24.png" /></a>
<div class="queueactions"> <a class="button" id="PlayTrack" title="Play/Pause" ng-click="defaultPlay()"><img src="images/play_24x32.png" /></a>
<a href="" class="button buttonvertical" title="Shuffle Queue" ng-click="queueShuffle()"><img src="images/fork_gd_11x12.png" /></a><br /> <a class="button" id="PauseTrack" title="Play/Pause" style="display: none;"><img src="images/pause_24x32.png" /></a>
<a href="" class="button buttonvertical" id="action_Empty" title="Delete Queue" ng-click="queueEmpty()"><img src="images/trash_fill_gd_12x12.png" /></a><br /> <a class="button" id="NextTrack" title="Next Track" ng-click="nextTrack()"><img src="images/last_24x24.png" /></a>
<a href="" class="button buttonvertical" id="action_DeleteSelected" title="Delete Song(s) From Queue" ng-click="queueRemoveSelected()"><img src="images/minus_8x2.png" /></a><br /> </div>
<!--<a href="" class="button buttonvertical" id="action_QueueToPlaylist" title="Create Playlist From Queue"><img src="images/list_gd_12x11.png" /></a>--> <div id="songdetails">
</div> <div id="coverart"><a class="coverartfancy" href="{{playingSong.coverartfull}}"><img ng-src="{{playingSong.coverartthumb}}" src="images/albumdefault_60.jpg" alt="" /></a></div>
<ul id="QueuePreviewList" class="songlist noselect"> <ul>
<li class="row song" ng-repeat="o in queue track by $index" ng-class="{'playing': o.playing, 'selected': o.selected}" ng-click="selectSong(o)" ng-dblclick="playSong(false, o)" id="{{o.id}}" parentid="{{o.parentid}}"> <li class="song" id="{{playingSong.id}}" ng-bind-html="playingSong.name"></li>
<div class="albumart"><a class="coverartfancy" href="{{o.coverartfull}}"><img class="" ng-src="{{o.coverartthumb}}" src="images/albumdefault_25.jpg" /></a></div> <li class="album" ng-bind-html="playingSong.album"></li>
<li class="specs" ng-bind-html="playingSong.specs"></li>
<li id="songdetails_controls">
<a href="" id="action_Mute" class="mute first" title="Mute"></a>
<a href="" id="action_UnMute" class="unmute first" title="Unmute" style="display: none;"></a>
<div class="jp-volume-bar"><div class="jp-volume-bar-value"></div></div><a href="" id="action_VolumeMax" class="volume" title="Max Volume"></a>
<a href="" class="loop" title="Repeat" ng-click="toggleSetting('Repeat')" ng-class="{'hoverSelected': !settings.Repeat }"></a>
<a href="" id="action_SaveProgress" class="lock" title="Save Track Position: On" ng-show="settings.SaveTrackPosition"></a>
<a title="Favorite" href="" ng-class="{'favorite': playingSong.starred, 'rate': !playingSong.starred}" ng-click="updateFavorite(playingSong)" stop-event="click"></a>
</li>
</ul>
<div class="vertshade"></div>
</div>
<div id="playdeck_1"></div>
<div id="playdeck_2"></div>
<div id="submenu_CurrentPlaylist" class="submenu shadow" style="display: none;">
<table id="CurrentPlaylistPreviewContainer" class="simplelist songlist">
<thead></thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="playeractionssmall"><!--<a href="" class="button" id="action_ToggleSideBar" title="Toggle Side Bar"><img src="images/arrow_right_gl_12x12.png" /></a>--></div>
<div id="playermiddle">
<div id="audiocontainer">
<div class="audiojs" id="audio_wrapper0">
<div class="scrubber"><div class="progress"></div><div class="loaded"></div></div>
<div class="time"><em id="played">00:00</em>/<strong id="duration">00:00</strong></div>
<div class="error-message"></div>
</div>
</div>
<div id="preview"></div>
</div>
<div class="clear"></div> <div class="clear"></div>
<div class="title" title="{{ o.track + ' - ' + o.name + ' - ' + o.time }}" ng-bind-html-unsafe="o.name"></div> </div>
<div class="albumtext" title="{{o.album}}" ng-bind-html-unsafe="o.album"></div> </div><!-- end #content -->
<div class="albumtext" title="{{o.artist}}" ng-bind-html-unsafe="o.artist"></div> </div> <!-- End container -->
<div id="footer">
<ul id="QueuePreview" class="songlist noselect" sortable>
<li class="row song" ng-repeat="o in queue track by $index" ng-class="{'playing': o.playing, 'selected': o.selected}" ng-click="selectSong(o)" ng-dblclick="playSong(false, o)" id="{{o.id}}" parentid="{{o.parentid}}">
<div class="albumart"><a class="coverartfancy" href="{{o.coverartfull}}"><img class="" ng-src="{{o.coverartthumb}}" src="images/albumdefault_60.jpg" /></a></div>
<div class="clear"></div>
<div class="title" title="{{ o.track + ' - ' + o.name + ' - ' + o.time }}" ng-bind-html="o.name"></div>
<div class="albumtext" title="{{o.album}}" ng-bind-html="o.album"></div>
<div class="albumtext" title="{{o.artist}}" ng-bind-html="o.artist"></div>
</li> </li>
</ul> </ul>
<a id="action_OpenQueue" class="pagetabcenter shadow" title="Open Queue" ng-click="pinQueue()" ng-mouseover="showQueue()"></a>
</div> </div>
</div> <script>
<script> (function (i, s, o, g, r, a, m) {
(function (i, s, o, g, r, a, m) { i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () { (i[r].q = i[r].q || []).push(arguments)
(i[r].q = i[r].q || []).push(arguments) }, i[r].l = 1 * new Date(); a = s.createElement(o),
}, i[r].l = 1 * new Date(); a = s.createElement(o), m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m) })(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
ga('create', 'UA-40174100-1', 'jamstash.com'); ga('create', 'UA-40174100-1', 'jamstash.com');
ga('send', 'pageview'); ga('send', 'pageview');
</script> </script>
</body> </body>
</html> </html>

View file

@ -155,7 +155,7 @@
<script type="text/html" id="album-template"> <script type="text/html" id="album-template">
<!-- ko if: flag != 'archive' --> <!-- ko if: flag != 'archive' -->
<ul class="actionlist"> <ul class="actionlist">
<li><select data-bind="options: $root.SubsonicAlbumSort, value: $root.selectedSubsonicAlbumSort"></select></li> <li><select data-bind="options: $root.SubsonicSort, value: $root.selectedSubsonicAlbumSort"></select></li>
</ul> </ul>
<!-- /ko --> <!-- /ko -->
<ul class="simplelist songlist noselect"> <ul class="simplelist songlist noselect">

View file

@ -172,7 +172,7 @@
rescanLibrary: self.rescanLibrary, rescanLibrary: self.rescanLibrary,
toggleAZ: self.toggleAZ, toggleAZ: self.toggleAZ,
selectedSubsonicAlbumSort: self.selectedSubsonicAlbumSort, selectedSubsonicAlbumSort: self.selectedSubsonicAlbumSort,
SubsonicAlbumSort: self.SubsonicAlbumSort, SubsonicSort: self.SubsonicSort,
getPodcasts: self.getPodcasts, getPodcasts: self.getPodcasts,
getPodcast: self.getPodcast, getPodcast: self.getPodcast,
getPlaylists: self.getPlaylists, getPlaylists: self.getPlaylists,

268
js/app.js
View file

@ -1,5 +1,18 @@
/* Declare app level module */ /* Declare app level module */
var JamStash = angular.module('JamStash', ['ngCookies']); var JamStash = angular.module('JamStash', ['ngCookies', 'ngSanitize']);
//var JamStash = angular.module('JamStash', ['ngCookies', 'ngRoute']);
/*
JamStash.config(function ($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist(['/^\s*(https?|file|ms-appx):/', 'self']);
});
// Given:
// URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
// Route: /Chapter/:chapterId/Section/:sectionId
//
// Then
$routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
*/
JamStash.config(function ($routeProvider) { JamStash.config(function ($routeProvider) {
$routeProvider $routeProvider
.when('/index', { redirectTo: '/library' }) .when('/index', { redirectTo: '/library' })
@ -25,252 +38,9 @@ JamStash.config(function ($routeProvider) {
$location.path('/settings'); $location.path('/settings');
} }
}); });
} ]); }]);
/* /*
JamStash.config(function ($httpProvider) { JamStash.config(function ($httpProvider, globals) {
$httpProvider.interceptors.push(function ($rootScope, $location, $q, globals) { $httpProvider.defaults.timeout = globals.settings.Timeout;
return { })
'request': function (request) { */
// if we're not logged-in to the AngularJS app, redirect to login page
//$rootScope.loggedIn = $rootScope.loggedIn || globals.settings.Username;
$rootScope.loggedIn = false;
if (globals.settings.Username != "" && globals.settings.Password != "" && globals.settings.Server != "") {
$rootScope.loggedIn = true;
}
if (!$rootScope.loggedIn && $location.path() != '/settings' && $location.path() != '/archive') {
$location.path('/settings');
}
return request;
},
'responseError': function (rejection) {
// if we're not logged-in to the web service, redirect to login page
if (rejection.status === 401 && $location.path() != '/settings') {
$rootScope.loggedIn = false;
$location.path('/settings');
}
return $q.reject(rejection);
}
};
});
});
*/
JamStash.service('model', function (utils) {
this.Index = function (name, artist) {
this.name = name;
this.artist = artist;
}
this.Artist = function (id, name) {
this.id = id;
this.name = name;
}
this.Album = function (id, parentid, name, artist, artistId, coverartthumb, coverartfull, date, starred, description, url, type) {
this.id = id;
this.parentid = parentid;
this.name = name;
this.artist = artist;
this.artistId = artistId;
this.coverartthumb = coverartthumb;
this.coverartfull = coverartfull;
this.date = date;
this.starred = starred;
this.description = description;
this.url = url;
this.type = type;
}
this.Song = function (id, parentid, track, name, artist, artistId, album, albumId, coverartthumb, coverartfull, duration, rating, starred, suffix, specs, url, position, description) {
this.id = id;
this.parentid = parentid;
this.track = track;
this.name = name;
this.artist = artist;
this.artistId = artistId;
this.album = album;
this.albumId = albumId;
this.coverartthumb = coverartthumb;
this.coverartfull = coverartfull;
this.duration = duration;
this.time = duration == '' ? '00:00' : utils.secondsToTime(duration);
this.rating = rating;
this.starred = starred;
this.suffix = suffix;
this.specs = specs;
this.url = url;
this.position = position;
this.selected = false;
this.playing = false;
this.description = description;
this.displayName = this.name + " - " + this.album + " - " + this.artist;
}
});
JamStash.service('globals', function (utils) {
this.settings = {
// Subsonic
/* Demo Server
Username: "android-guest"),
Password: "guest"),
Server: "http://subsonic.org/demo"),
*/
Url: "http://Jamstash.com/beta/#/archive/",
Username: "",
Password: "",
Server: "",
Timeout: 10000,
NotificationTimeout: 20000,
Protocol: "jsonp",
ApplicationName: "Jamstash",
ApiVersion: "1.6.0",
AutoPlaylists: "",
AutoPlaylistSize: 25,
AutoAlbumSize: 15,
// General
HideAZ: false,
ScrollTitle: true,
NotificationSong: true,
NotificationNowPlaying: false,
SaveTrackPosition: false,
ForceFlash: false,
Theme: "Default",
DefaultLibraryLayout: "grid",
AutoPlay: false,
LoopQueue: false,
Repeat: false,
Debug: false
};
this.SavedCollections = [];
this.SavedGenres = [];
this.BaseURL = function () { return this.settings.Server + '/rest'; };
this.BaseParams = function () { return 'u=' + this.settings.Username + '&p=' + this.settings.Password + '&f=' + this.settings.Protocol + '&v=' + this.settings.ApiVersion + '&c=' + this.settings.ApplicationName; };
});
// Directives
JamStash.directive('stopEvent', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
element.bind(attr.stopEvent, function (e) {
e.stopPropagation();
});
}
};
});
JamStash.directive('ngEnter', function () {
return function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if (event.which === 13) {
scope.$apply(function () {
scope.$eval(attrs.ngEnter);
});
event.preventDefault();
}
});
};
});
JamStash.directive('ngDownload', function ($compile) {
return {
restrict: 'E',
scope: { data: '=' },
link: function (scope, elm, attrs) {
function getUrl() {
return URL.createObjectURL(new Blob([JSON.stringify(scope.data)], { type: "application/json" }));
}
elm.append($compile(
'<a class="button" download="backup.json"' +
'href="' + getUrl() + '">' +
'Download' +
'</a>'
)(scope));
scope.$watch(scope.data, function () {
elm.children()[0].href = getUrl();
});
}
};
});
/* Factory */
JamStash.factory('json', function ($http) { // Deferred loading
return {
getCollections: function (callback) {
$http.get('js/json_collections.js').success(callback);
},
getChangeLog: function (callback) {
$http.get('js/json_changelog.js').success(callback);
}
}
});
/* Filters */
JamStash.filter('capitalize', function () {
return function (input, scope) {
return input.substring(0, 1).toUpperCase() + input.substring(1);
}
});
JamStash.service('notifications', function ($rootScope, globals) {
var msgIndex = 1;
this.updateMessage = function (msg, autohide) {
if (msg != '') {
var id = msgIndex;
$('#messages').append('<span id=\"msg_' + id + '\" class="message">' + msg + '</span>');
$('#messages').fadeIn();
$("#messages").scrollTo('100%');
var el = '#msg_' + id;
if (autohide) {
setTimeout(function () {
$(el).fadeOut(function () { $(this).remove(); });
}, globals.settings.NotificationTimeout);
} else {
$(el).click(function () {
$(el).fadeOut(function () { $(this).remove(); });
return false;
});
}
msgIndex++;
}
}
this.requestPermissionIfRequired = function () {
if (!this.hasNotificationPermission() && (window.webkitNotifications)) {
window.webkitNotifications.requestPermission();
}
}
this.hasNotificationPermission = function () {
return !!(window.webkitNotifications) && (window.webkitNotifications.checkPermission() == 0);
}
var notifications = new Array();
this.showNotification = function (pic, title, text, type, bind) {
if (this.hasNotificationPermission()) {
//closeAllNotifications()
var popup;
if (type == 'text') {
popup = window.webkitNotifications.createNotification(pic, title, text);
} else if (type == 'html') {
popup = window.webkitNotifications.createHTMLNotification(text);
}
if (bind = '#NextTrack') {
popup.addEventListener('click', function (bind) {
//$(bind).click();
$rootScope.nextTrack();
this.cancel();
})
}
notifications.push(popup);
setTimeout(function (notWin) {
notWin.cancel();
}, globals.settings.NotificationTimeout, popup);
popup.show();
} else {
console.log("showNotification: No Permission");
}
}
this.closeAllNotifications = function () {
for (notification in notifications) {
notifications[notification].cancel();
}
}
});

View file

@ -1,6 +1,6 @@
JamStash.controller('ArchiveCtrl', JamStash.controller('ArchiveCtrl',
function ArchiveCtrl($scope, $rootScope, $location, $routeParams, $http, utils, globals, model, notifications, player, json) { function ArchiveCtrl($scope, $rootScope, $location, $routeParams, $http, utils, globals, model, notifications, player, json) {
$("#LayoutContainer").layout($scope.layoutThreeCol); //$("#left-component").layout($scope.layoutThreeCol);
$scope.settings = globals.settings; $scope.settings = globals.settings;
$scope.itemType = 'archive'; $scope.itemType = 'archive';
@ -166,6 +166,7 @@ function ArchiveCtrl($scope, $rootScope, $location, $routeParams, $http, utils,
items = data["response"].docs; items = data["response"].docs;
//alert(JSON.stringify(data["response"])); //alert(JSON.stringify(data["response"]));
$scope.album = []; $scope.album = [];
$rootScope.song = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$scope.album.push(map(item)); $scope.album.push(map(item));
}); });
@ -180,7 +181,7 @@ function ArchiveCtrl($scope, $rootScope, $location, $routeParams, $http, utils,
} }
}); });
}; };
$scope.mapSong = function (key, song, server, dir, identifier, coverart) { utils.mapSong = function (key, song, server, dir, identifier, coverart) {
var url, time, track, title, rating, starred, contenttype, suffix; var url, time, track, title, rating, starred, contenttype, suffix;
var specs = '' var specs = ''
if (song.format == 'VBR MP3') { if (song.format == 'VBR MP3') {
@ -211,18 +212,18 @@ function ArchiveCtrl($scope, $rootScope, $location, $routeParams, $http, utils,
var items = data.files; var items = data.files;
if (action == 'add') { if (action == 'add') {
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
var song = $scope.mapSong(key, item, server, dir, identifier, coverart); var song = utils.mapSong(key, item, server, dir, identifier, coverart);
if (song) { if (song) {
$rootScope.queue.push(song); $rootScope.queue.push(song);
} }
}); });
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(Object.keys(items).length + ' Song(s) Added to Queue', true); notifications.updateMessage(Object.keys(items).length + ' Song(s) Added to Queue', true);
$scope.$apply(); $scope.$apply();
} else if (action == 'play') { } else if (action == 'play') {
$rootScope.queue = []; $rootScope.queue = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
var song = $scope.mapSong(key, item, server, dir, identifier, coverart); var song = utils.mapSong(key, item, server, dir, identifier, coverart);
if (song) { if (song) {
$rootScope.queue.push(song); $rootScope.queue.push(song);
} }
@ -231,12 +232,13 @@ function ArchiveCtrl($scope, $rootScope, $location, $routeParams, $http, utils,
$scope.$apply(function () { $scope.$apply(function () {
$rootScope.playSong(false, next); $rootScope.playSong(false, next);
}); });
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(Object.keys(items).length + ' Song(s) Added to Queue', true); notifications.updateMessage(Object.keys(items).length + ' Song(s) Added to Queue', true);
} else { } else {
$scope.album = [];
$rootScope.song = []; $rootScope.song = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
var song = $scope.mapSong(key, item, server, dir, identifier, coverart); var song = utils.mapSong(key, item, server, dir, identifier, coverart);
if (song) { if (song) {
$rootScope.song.push(song); $rootScope.song.push(song);
} }
@ -252,7 +254,7 @@ function ArchiveCtrl($scope, $rootScope, $location, $routeParams, $http, utils,
$scope.queue.push(item); $scope.queue.push(item);
item.selected = false; item.selected = false;
}); });
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage($scope.selectedSongs.length + ' Song(s) Added to Queue', true); notifications.updateMessage($scope.selectedSongs.length + ' Song(s) Added to Queue', true);
} }
} }

View file

@ -1,6 +1,6 @@
JamStash.controller('SubsonicCtrl', JamStash.controller('SubsonicCtrl',
function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, utils, globals, model, notifications, player) { function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, utils, globals, model, notifications, player) {
$("#SubsonicAlbums").layout($scope.layoutThreeCol); //$("#SubsonicAlbums").layout($scope.layoutThreeCol);
$rootScope.song = []; $rootScope.song = [];
//$scope.artistId = $routeParams.artistId; //$scope.artistId = $routeParams.artistId;
@ -20,27 +20,40 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
{ id: "frequent", name: "Most Played" }, { id: "frequent", name: "Most Played" },
{ id: "recent", name: "Recently Played" } { id: "recent", name: "Recently Played" }
]; ];
$scope.DefaultSearchType = globals.settings.DefaultSearchType;
$scope.selectedAutoAlbum; $scope.selectedAutoAlbum;
$scope.selectedArtist; $scope.selectedArtist;
$scope.selectedAlbum; $scope.selectedAlbum;
$scope.selectedSubsonicAlbumSort = globals.settings.DefaultSubsonicAlbumSort; $scope.selectedSubsonicAlbumSort = 'default';
$scope.SubsonicSort = [];
$scope.SubsonicAlbumSort = [ $scope.SubsonicAlbumSort = [
{ id: "default", name: "Default Sort" }, { id: "default", name: "Default Sort" },
{ id: "artist", name: "Artist" }, { id: "artist", name: "Artist" },
{ id: "album", name: "Album" }, { id: "album", name: "Album" },
{ id: "createdate desc", name: "Created Date - Desc" }, { id: "createdate desc", name: "Date Added" },
]; ];
$scope.SubsonicSongSort = [
{ id: "default", name: "Default Sort" },
{ id: "track", name: "Track" },
{ id: "artist", name: "Artist" },
{ id: "album", name: "Album" },
{ id: "createdate desc", name: "Date Added" },
];
$scope.BreadCrumbs = [];
$scope.$watch("selectedSubsonicAlbumSort", function (newValue, oldValue) { $scope.$watch("selectedSubsonicAlbumSort", function (newValue, oldValue) {
if (newValue !== oldValue) { if (newValue !== oldValue) {
$scope.sortSubsonicAlbums(newValue); if ($rootScope.song.length > 0) {
$scope.sortSubsonicSongs(newValue);
} else if ($scope.album.length > 0) {
$scope.sortSubsonicAlbums(newValue);
}
}
});
$rootScope.$watch("selectedMusicFolder", function (newValue, oldValue) {
if (newValue !== oldValue) {
utils.setValue('MusicFolders', angular.toJson(newValue), true);
$scope.getArtists(newValue.id);
} }
}); });
$scope.SearchType = globals.settings.DefaultSearchType;
$scope.SearchTypeLayout = [
{ id: "song", name: "Song" },
{ id: "album", name: "Album" },
];
$scope.selectedLayout = globals.settings.DefaultLibraryLayout; $scope.selectedLayout = globals.settings.DefaultLibraryLayout;
//not sure how to just grab the layouts hash from the settings controller //not sure how to just grab the layouts hash from the settings controller
$scope.Layouts = [ $scope.Layouts = [
@ -123,6 +136,11 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
} }
}); });
}; };
$scope.refreshArtists = function (id) {
utils.setValue('MusicFolders', null, true);
$scope.getArtists();
};
$scope.mapAlbum = function (data) { $scope.mapAlbum = function (data) {
var album = data; var album = data;
var title, coverartthumb, coverartfull, starred; var title, coverartthumb, coverartfull, starred;
@ -140,10 +158,11 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
} }
return new model.Album(album.id, album.parent, title, album.artist, album.artistId, coverartthumb, coverartfull, $.format.date(new Date(album.created), "yyyy-MM-dd h:mm a"), starred, '', '', type); return new model.Album(album.id, album.parent, title, album.artist, album.artistId, coverartthumb, coverartfull, $.format.date(new Date(album.created), "yyyy-MM-dd h:mm a"), starred, '', '', type);
} }
$scope.getAlbums = function (id) { $scope.getAlbums = function (id, name) {
$scope.selectedAutoAlbum = null; $scope.selectedAutoAlbum = null;
$scope.selectedArtist = id; $scope.selectedArtist = id;
$scope.artistId = id; $scope.BreadCrumbs = [];
$scope.BreadCrumbs.push({ 'type': 'artist', 'id': id, 'name': name });
var url = globals.BaseURL() + '/getMusicDirectory.view?' + globals.BaseParams() + '&id=' + id; var url = globals.BaseURL() + '/getMusicDirectory.view?' + globals.BaseParams() + '&id=' + id;
$.ajax({ $.ajax({
url: url, url: url,
@ -165,13 +184,13 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
if (item.isDir) { if (item.isDir) {
$scope.album.push($scope.mapAlbum(item)); $scope.album.push($scope.mapAlbum(item));
} else { } else {
$rootScope.song.push($scope.mapSong(item)); $rootScope.song.push(utils.mapSong(item));
} }
}); });
if ($scope.selectedSubsonicAlbumSort != "default") { if ($scope.selectedSubsonicAlbumSort != "default") {
$scope.sortSubsonicAlbums($scope.selectedSubsonicAlbumSort); $scope.sortSubsonicAlbums($scope.selectedSubsonicAlbumSort);
} }
//$location.path('/library/' + id); $scope.SubsonicSort = $scope.SubsonicAlbumSort;
$scope.$apply(); $scope.$apply();
} else { } else {
notifications.updateMessage('No Albums Returned :(', true); notifications.updateMessage('No Albums Returned :(', true);
@ -243,7 +262,7 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
items[0] = data["subsonic-response"].album.song; items[0] = data["subsonic-response"].album.song;
} }
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.song.push($scope.mapSong(item)); $rootScope.song.push(utils.mapSong(item));
}); });
$scope.$apply(); $scope.$apply();
} }
@ -255,6 +274,7 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
var size, url; var size, url;
$scope.selectedArtist = null; $scope.selectedArtist = null;
$scope.selectedAutoAlbum = id; $scope.selectedAutoAlbum = id;
$scope.BreadCrumbs = [];
if (offset == 'next') { if (offset == 'next') {
$scope.offset = $scope.offset + globals.settings.AutoAlbumSize; $scope.offset = $scope.offset + globals.settings.AutoAlbumSize;
} else if (offset == 'prev') { } else if (offset == 'prev') {
@ -279,6 +299,7 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
items[0] = data["subsonic-response"].albumList.album; items[0] = data["subsonic-response"].albumList.album;
} }
$scope.album = []; $scope.album = [];
$rootScope.song = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
if (item.isDir) { if (item.isDir) {
$scope.album.push($scope.mapAlbum(item)); $scope.album.push($scope.mapAlbum(item));
@ -286,6 +307,7 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
$rootScope.song.push($scope.mapAlbum(item)); $rootScope.song.push($scope.mapAlbum(item));
} }
}); });
$scope.SubsonicSort = $scope.SubsonicAlbumSort;
$scope.$apply(); $scope.$apply();
} else { } else {
notifications.updateMessage('No Albums Returned :(', true); notifications.updateMessage('No Albums Returned :(', true);
@ -309,43 +331,53 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
} else { } else {
items[0] = data["subsonic-response"].directory.child; items[0] = data["subsonic-response"].directory.child;
} }
if (typeof data["subsonic-response"].directory.id != 'undefined') {
//alert(data["subsonic-response"].directory.id);
// Look at bringing back the breadcrumb
}
//alert(JSON.stringify(getMusicDirectory["subsonic-response"].directory.child));
if (action == 'add') { if (action == 'add') {
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.queue.push($scope.mapSong(item)); $rootScope.queue.push(utils.mapSong(item));
}); });
$scope.$apply(); $scope.$apply();
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else if (action == 'play') { } else if (action == 'play') {
$rootScope.queue = []; $rootScope.queue = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.queue.push($scope.mapSong(item)); $rootScope.queue.push(utils.mapSong(item));
}); });
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$scope.$apply(function () { $scope.$apply(function () {
$rootScope.playSong(false, next); $rootScope.playSong(false, next);
}); });
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else if (action == 'preview') {
$scope.songpreview = [];
angular.forEach(items, function (item, key) {
if (!item.isDir) {
$rootScope.songpreview.push(utils.mapSong(item));
}
});
$scope.$apply();
} else { } else {
if (typeof data["subsonic-response"].directory.id != 'undefined') {
var albumId = data["subsonic-response"].directory.id;
var albumName = data["subsonic-response"].directory.name;
if ($scope.BreadCrumbs.length > 0) { $scope.BreadCrumbs.splice(1, ($scope.BreadCrumbs.length - 1)) };
$scope.BreadCrumbs.push({ 'type': 'album', 'id': albumId, 'name': albumName });
}
$rootScope.song = []; $rootScope.song = [];
$scope.album = [];
var albums = []; var albums = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
if (item.isDir) { if (item.isDir) {
albums.push($scope.mapAlbum(item)); albums.push($scope.mapAlbum(item));
} else { } else {
$rootScope.song.push($scope.mapSong(item)); $rootScope.song.push(utils.mapSong(item));
} }
}); });
if (albums.length > 0) { if (albums.length > 0) {
$scope.album = albums; $scope.album = albums;
} }
//$location.path('/library/0/' + id); $scope.SubsonicSort = $scope.SubsonicSongSort;
$scope.$apply(); $scope.$apply();
} }
} else { } else {
@ -367,8 +399,7 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
if (data["subsonic-response"].searchResult2 !== "") { if (data["subsonic-response"].searchResult2 !== "") {
var header; var header;
var items = []; var items = [];
if (type === '0') { if (type === 'song') {
type = 'song'; //this could be done better if a way to share data between controllers was implemented
if (data["subsonic-response"].searchResult2.song !== undefined) { if (data["subsonic-response"].searchResult2.song !== undefined) {
if (data["subsonic-response"].searchResult2.song.length > 0) { if (data["subsonic-response"].searchResult2.song.length > 0) {
items = data["subsonic-response"].searchResult2.song; items = data["subsonic-response"].searchResult2.song;
@ -377,13 +408,12 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
} }
$rootScope.song = []; $rootScope.song = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.song.push($scope.mapSong(item)); $rootScope.song.push(utils.mapSong(item));
}); });
$scope.$apply(); $scope.$apply();
} }
} }
if (type === '1') { if (type === 'album') {
type = 'album';
if (data["subsonic-response"].searchResult2.album !== undefined) { if (data["subsonic-response"].searchResult2.album !== undefined) {
if (data["subsonic-response"].searchResult2.album.length > 0) { if (data["subsonic-response"].searchResult2.album.length > 0) {
items = data["subsonic-response"].searchResult2.album; items = data["subsonic-response"].searchResult2.album;
@ -474,18 +504,14 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
return a.date < b.date ? 1 : -1; return a.date < b.date ? 1 : -1;
}; };
$scope.sortArtistFunction = function (a, b) { $scope.sortArtistFunction = function (a, b) {
return a.artist.toLowerCase() > b.artist.toLowerCase() ? -1 : 1; return a.artist.toLowerCase() < b.artist.toLowerCase() ? -1 : 1;
}; };
$scope.sortAlbumFunction = function (a, b) { $scope.sortAlbumFunction = function (a, b) {
/*
if (a.name < b.name) //sort string ascending
return -1
if (a.name > b.name)
return 1
return 0
*/
return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1; return a.name.toLowerCase() < b.name.toLowerCase() ? -1 : 1;
}; };
$scope.sortTrackFunction = function (a, b) {
return parseInt(a.track) > parseInt(b.track) ? -1 : 1;
};
$scope.sortSubsonicAlbums = function (newValue) { $scope.sortSubsonicAlbums = function (newValue) {
if (typeof newValue != 'undefined') { if (typeof newValue != 'undefined') {
//alert(newValue); //alert(newValue);
@ -502,6 +528,25 @@ function SubsonicCtrl($scope, $rootScope, $location, $window, $routeParams, util
} }
} }
}; };
$scope.sortSubsonicSongs = function (newValue) {
if (typeof newValue != 'undefined') {
//alert(newValue);
switch (newValue) {
case 'createdate desc':
$rootScope.song.sort($scope.sortDateFunction);
break;
case 'artist':
$rootScope.song.sort($scope.sortArtistFunction);
break;
case 'album':
$rootScope.song.sort($scope.sortAlbumFunction);
break;
case 'track':
$rootScope.song.sort($scope.sortTrackFunction);
break;
}
}
};
/* Launch on Startup */ /* Launch on Startup */
$scope.getArtists(); $scope.getArtists();

View file

@ -10,18 +10,6 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
$rootScope.selectedAutoPlaylist = ""; $rootScope.selectedAutoPlaylist = "";
$rootScope.selectedMusicFolder = ""; $rootScope.selectedMusicFolder = "";
$rootScope.unity; $rootScope.unity;
$rootScope.$watch("selectedMusicFolder", function (newValue, oldValue) {
if (newValue !== oldValue) {
if (typeof newValue != 'undefined' && newValue != null) {
utils.setValue('MusicFolders', angular.toJson(newValue), true);
//$scope.getArtists(newValue.id);
} else {
utils.setValue('MusicFolders', null, true);
//$scope.getArtists();
}
}
});
$rootScope.loggedIn = function () { $rootScope.loggedIn = function () {
if (globals.settings.Server != '' && globals.settings.Username != '' && globals.settings.Password != '') { if (globals.settings.Server != '' && globals.settings.Username != '' && globals.settings.Password != '') {
return true; return true;
@ -70,7 +58,7 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
}); });
$("a.coverartfancy").live("click", function () { $(".coverartfancy").on("click", "a", function () {
$("a.coverartfancy").fancybox({ $("a.coverartfancy").fancybox({
beforeShow: function () { beforeShow: function () {
//this.title = $('#songdetails_artist').html(); //this.title = $('#songdetails_artist').html();
@ -117,7 +105,30 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
setTimeout(function () { if (submenu_active == false) $('div.submenu').stop().fadeOut(); }, 10000); setTimeout(function () { if (submenu_active == false) $('div.submenu').stop().fadeOut(); }, 10000);
} }
} }
$rootScope.showQueue = function (show) {
var submenu = $(QueuePreview);
submenu.fadeIn(400);
setTimeout(function () { submenu.fadeOut(); }, 20000);
}
$rootScope.hideQueue = function (show) {
submenu.fadeOut();
}
$scope.toggleQueue = function (show) {
var submenu = $(QueuePreview);
if (submenu.css('display') !== 'none') {
$rootScope.showQueue();
} else {
$rootScope.hideQueue();
}
}
$scope.pinQueue = function () {
var submenu = $(QueuePreview);
if (submenu.css('display') !== 'none') {
submenu.fadeOut();
} else {
submenu.fadeIn(400);
}
}
$("a.coverartfancy").fancybox({ $("a.coverartfancy").fancybox({
beforeShow: function () { beforeShow: function () {
//this.title = $('#songdetails_artist').html(); //this.title = $('#songdetails_artist').html();
@ -144,18 +155,7 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
$('.audiojs .scrubber').stop().animate({ height: '4px' }); $('.audiojs .scrubber').stop().animate({ height: '4px' });
}); });
$('.message').live('click', function () { $(this).remove(); }); $('.message').on('click', function () { $(this).remove(); });
// JQuery UI Sortable - Drag and drop sorting
var fixHelper = function (e, ui) {
ui.children().each(function () {
$(this).width($(this).width());
});
return ui;
};
$("#QueuePreview ul.songlist").sortable({
helper: fixHelper
});
// Sway.fm Unity Plugin // Sway.fm Unity Plugin
$rootScope.unity = UnityMusicShim(); $rootScope.unity = UnityMusicShim();
@ -179,76 +179,45 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
} }
}); });
// JQuery Layout Plugin
// JQuery UI Sortable - Drag and drop sorting
/*
var fixHelper = function (e, ui) {
ui.children().each(function () {
$(this).width($(this).width());
});
return ui;
};
$("#QueuePreview ul.songlist").sortable({
helper: fixHelper
});
*/
/* JQuery Layout Plugin - I don't think this is used anywhere
function resizePageLayout() { function resizePageLayout() {
var pageLayout = $("body").data("layout"); var pageLayout = $("body").data("layout");
if (pageLayout) pageLayout.resizeAll(); if (pageLayout) pageLayout.resizeAll();
}; };
*/
//$( "#nav" ).tabs();
var pageLayoutOptions = {
name: 'pageLayout', // only for debugging
resizeWithWindowDelay: 250, // delay calling resizeAll when window is *still* resizing
//, resizeWithWindowMaxDelay: 2000 // force resize every XX ms while window is being resized
//center__children: {},
//north__paneSelector: "#container",
center__paneSelector: "#container",
south__paneSelector: "#QueuePreview",
south__resizable: false, // No resize
//south__closable: false, // No close handle
//south__spacing_open: 0, // No resize bar
south__size: 145,
south__initClosed: true,
south__minWidth: 145,
south__maxWidth: 145
};
// create the page-layout, which will ALSO create the tabs-wrapper child-layout
var pageLayout = $("body").layout(pageLayoutOptions);
$scope.layoutThreeCol = {
east__size: .45,
east__minSize: 400,
east__maxSize: .5, // 50% of layout width
east__initClosed: false,
east__initHidden: false,
//center__size: 'auto',
center__minWidth: .35,
center__initClosed: false,
center__initHidden: false,
west__size: .2,
west__minSize: 200,
west__initClosed: false,
west__initHidden: false,
//stateManagement__enabled: true, // automatic cookie load & save enabled by default
showDebugMessages: true // log and/or display messages from debugging & testing code
//applyDefaultStyles: true
};
$scope.layoutTwoCol = {
center__size: .8,
center__minSize: 400,
center__maxSize: .5, // 50% of layout width
center__initClosed: false,
center__initHidden: false,
west__size: .2,
west__minSize: 200,
west__initClosed: false,
west__initHidden: false,
//stateManagement__enabled: true, // automatic cookie load & save enabled by default
showDebugMessages: true // log and/or display messages from debugging & testing code
//applyDefaultStyles: true
};
// Global Functions // Global Functions
window.onbeforeunload = function () { window.onbeforeunload = function () {
if (!self.settings.Debug()) { if (!globals.settings.Debug) {
if (self.queue().length > 0) { if ($rootScope.queue.length > 0) {
return "You're about to end your session, are you sure?"; return "You're about to end your session, are you sure?";
} }
} }
} }
$scope.dragStart = function (e, ui) {
ui.item.data('start', ui.item.index());
}
$scope.dragEnd = function (e, ui) {
var start = ui.item.data('start'),
end = ui.item.index();
$rootScope.queue.splice(end, 0,
$rootScope.queue.splice(start, 1)[0]);
$scope.$apply();
}
$document.keydown(function (e) { $document.keydown(function (e) {
$scope.scrollToIndex(e); $scope.scrollToIndex(e);
}); });
@ -279,17 +248,17 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
} }
var el = '#' + key.toUpperCase(); var el = '#' + key.toUpperCase();
if ($(el).length > 0) { if ($(el).length > 0) {
$('#SubsonicArtists').stop().scrollTo(el, 400); $('#left-component').stop().scrollTo(el, 400);
} }
} else if (unicode == 39 || unicode == 176) { // right arrow } else if (unicode == 39 || unicode == 176) { // right arrow
$rootScope.nextTrack(); $rootScope.nextTrack();
} else if (unicode == 37 || unicode == 177) { // back arrow } else if (unicode == 37 || unicode == 177) { // back arrow
$rootScope.previousTrack(); $rootScope.previousTrack();
} else if (unicode == 32 || unicode == 179 || unicode == 0179) { // spacebar } else if (unicode == 32 || unicode == 179 || unicode.toString() == '0179') { // spacebar
player.playPauseSong(); player.playPauseSong();
return false; return false;
} else if (unicode == 36 && $('#tabLibrary').is(':visible')) { // home } else if (unicode == 36 && $('#tabLibrary').is(':visible')) { // home
$('#SubsonicArtists').stop().scrollTo('#MusicFolders', 400); $('#left-component').stop().scrollTo('#MusicFolders', 400);
} }
if (unicode == 189) { // dash - volume down if (unicode == 189) { // dash - volume down
var volume = utils.getValue('Volume') ? parseFloat(utils.getValue('Volume')) : 1; var volume = utils.getValue('Volume') ? parseFloat(utils.getValue('Volume')) : 1;
@ -319,7 +288,7 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
$scope.scrollToIndexName = function (index) { $scope.scrollToIndexName = function (index) {
var el = '#' + index; var el = '#' + index;
if ($(el).length > 0) { if ($(el).length > 0) {
$('#SubsonicArtists').stop().scrollTo(el, 400); $('#left-component').stop().scrollTo(el, 400);
} }
}; };
$scope.scrollToTop = function () { $scope.scrollToTop = function () {
@ -331,6 +300,12 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
item.selected = true; item.selected = true;
}); });
} }
$scope.playAll = function () {
$scope.selectAll();
$scope.addSongsToQueue();
var next = $rootScope.queue[0];
$rootScope.playSong(false, next);
}
$scope.selectNone = function () { $scope.selectNone = function () {
angular.forEach($rootScope.song, function (item, key) { angular.forEach($rootScope.song, function (item, key) {
$scope.selectedSongs = []; $scope.selectedSongs = [];
@ -343,7 +318,7 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
$scope.queue.push(item); $scope.queue.push(item);
item.selected = false; item.selected = false;
}); });
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage($scope.selectedSongs.length + ' Song(s) Added to Queue', true); notifications.updateMessage($scope.selectedSongs.length + ' Song(s) Added to Queue', true);
$scope.selectedSongs.length = 0; $scope.selectedSongs.length = 0;
} }
@ -438,34 +413,15 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
globals.settings.ApiVersion = data["subsonic-response"].version; globals.settings.ApiVersion = data["subsonic-response"].version;
} else { } else {
if (typeof data["subsonic-response"].error != 'undefined') { if (typeof data["subsonic-response"].error != 'undefined') {
alert(data["subsonic-response"].error.message); notifications.updateMessage(data["subsonic-response"].error.message);
} }
} }
}, },
error: function () { error: function () {
alert('Unable to connect to Subsonic server'); notifications.updateMessage('Unable to connect to Subsonic server');
} }
}); });
} }
$scope.mapSong = function (data) {
var song = data;
var url, track, rating, starred, contenttype, suffix, description;
var specs = '', coverartthumb = '', coverartfull = '';
if (typeof song.coverArt != 'undefined') {
coverartthumb = globals.BaseURL() + '/getCoverArt.view?' + globals.BaseParams() + '&size=60&id=' + song.coverArt;
coverartfull = globals.BaseURL() + '/getCoverArt.view?' + globals.BaseParams() + '&id=' + song.coverArt;
}
if (typeof song.description == 'undefined') { description = ''; } else { description = song.description; }
if (typeof song.track == 'undefined') { track = '&nbsp;'; } else { track = song.track; }
if (typeof song.starred !== 'undefined') { starred = true; } else { starred = false; }
if (song.bitRate !== undefined) { specs += song.bitRate + ' Kbps'; }
if (song.transcodedSuffix !== undefined) { specs += ', transcoding:' + song.suffix + ' > ' + song.transcodedSuffix; } else { specs += ', ' + song.suffix; }
if (song.transcodedSuffix !== undefined) { suffix = song.transcodedSuffix; } else { suffix = song.suffix; }
if (suffix == 'ogg') { suffix = 'oga'; }
var salt = Math.floor(Math.random() * 100000);
url = globals.BaseURL() + '/stream.view?' + globals.BaseParams() + '&id=' + song.id + '&salt=' + salt;
return new model.Song(song.id, song.parent, track, song.title, song.artist, song.artistId, song.album, song.albumId, coverartthumb, coverartfull, song.duration, song.userRating, starred, suffix, specs, url, 0, description);
}
$scope.addSongToQueue = function (data) { $scope.addSongToQueue = function (data) {
$rootScope.queue.push(data); $rootScope.queue.push(data);
} }
@ -545,26 +501,26 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
} }
if (action == 'add') { if (action == 'add') {
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.queue.push($scope.mapSong(item)); $rootScope.queue.push(utils.mapSong(item));
}); });
$scope.$apply(); $scope.$apply();
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else if (action == 'play') { } else if (action == 'play') {
$rootScope.queue = []; $rootScope.queue = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.queue.push($scope.mapSong(item)); $rootScope.queue.push(utils.mapSong(item));
}); });
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$scope.$apply(function () { $scope.$apply(function () {
$rootScope.playSong(false, next); $rootScope.playSong(false, next);
}); });
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else { } else {
$rootScope.song = []; $rootScope.song = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.song.push($scope.mapSong(item)); $rootScope.song.push(utils.mapSong(item));
}); });
$scope.$apply(); $scope.$apply();
} }
@ -593,13 +549,14 @@ function AppCtrl($scope, $rootScope, $document, $location, utils, globals, model
} }
}); });
} }
$scope.toTrusted = function (html) {
return $sce.trustAsHtml(html);
}
/* Launch on Startup */ /* Launch on Startup */
$scope.loadSettings(); $scope.loadSettings();
utils.switchTheme(globals.settings.Theme); utils.switchTheme(globals.settings.Theme);
if ($scope.loggedIn) { if ($scope.loggedIn()) {
$scope.ping(); $scope.ping();
$scope.getMusicFolders(); $scope.getMusicFolders();
if (globals.settings.SaveTrackPosition) { if (globals.settings.SaveTrackPosition) {

52
js/controllers/partial.js Normal file
View file

@ -0,0 +1,52 @@
JamStash.controller('PartialCtrl',
function PartialCtrl($scope, $rootScope, $location, $window, $routeParams, utils, globals) {
//$("#SubsonicAlbums").layout($scope.layoutThreeCol);
$scope.song = [];
$scope.itemType = 'ss';
$scope.index = [];
$scope.shortcut = [];
$scope.album = [];
$scope.Server = globals.settings.Server;
$scope.getSongs = function (id) {
var url = globals.BaseURL() + '/getMusicDirectory.view?' + globals.BaseParams() + '&id=' + id;
$.ajax({
url: url,
method: 'GET',
dataType: globals.settings.Protocol,
timeout: globals.settings.Timeout,
success: function (data) {
var items = [];
if (typeof data["subsonic-response"].directory.child != 'undefined') {
if (data["subsonic-response"].directory.child.length > 0) {
items = data["subsonic-response"].directory.child;
} else {
items[0] = data["subsonic-response"].directory.child;
}
$scope.song = [];
var albums = [];
angular.forEach(items, function (item, key) {
if (item.isDir) {
//albums.push($scope.mapAlbum(item));
} else {
$rootScope.song.push(utils.mapSong(item));
}
});
//$location.path('/library/0/' + id);
$scope.$apply();
} else {
notifications.updateMessage('No Songs Returned :(', true);
}
}
});
};
/* Launch on Startup */
if ($routeParams.albumId) {
$scope.getSongs($routeParams.albumId);
}
/* End Startup */
});

View file

@ -1,6 +1,6 @@
JamStash.controller('PlaylistCtrl', JamStash.controller('PlaylistCtrl',
function PlaylistCtrl($scope, $rootScope, $location, utils, globals, model, notifications) { function PlaylistCtrl($scope, $rootScope, $location, utils, globals, model, notifications) {
$("#LayoutContainer").layout($scope.layoutTwoCol); //$("#left-component").layout($scope.layoutTwoCol);
$rootScope.song = []; $rootScope.song = [];
$scope.itemType = 'pl'; $scope.itemType = 'pl';
@ -61,26 +61,27 @@ function PlaylistCtrl($scope, $rootScope, $location, utils, globals, model, noti
} }
if (action == 'add') { if (action == 'add') {
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.queue.push($scope.mapSong(item)); $rootScope.queue.push(utils.mapSong(item));
}); });
$scope.$apply(); $scope.$apply();
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else if (action == 'play') { } else if (action == 'play') {
$rootScope.queue = []; $rootScope.queue = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.queue.push($scope.mapSong(item)); $rootScope.queue.push(utils.mapSong(item));
}); });
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$scope.$apply(function () { $scope.$apply(function () {
$rootScope.playSong(false, next); $rootScope.playSong(false, next);
}); });
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else { } else {
$scope.album = [];
$rootScope.song = []; $rootScope.song = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.song.push($scope.mapSong(item)); $rootScope.song.push(utils.mapSong(item));
}); });
$scope.$apply(); $scope.$apply();
} }
@ -130,26 +131,26 @@ function PlaylistCtrl($scope, $rootScope, $location, utils, globals, model, noti
} }
if (action == 'add') { if (action == 'add') {
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.queue.push($scope.mapSong(item)); $rootScope.queue.push(utils.mapSong(item));
}); });
$scope.$apply(); $scope.$apply();
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else if (action == 'play') { } else if (action == 'play') {
$rootScope.queue = []; $rootScope.queue = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.queue.push($scope.mapSong(item)); $rootScope.queue.push(utils.mapSong(item));
}); });
var next = $rootScope.queue[0]; var next = $rootScope.queue[0];
$scope.$apply(function () { $scope.$apply(function () {
$rootScope.playSong(false, next); $rootScope.playSong(false, next);
}); });
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else { } else {
$rootScope.song = []; $rootScope.song = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
$rootScope.song.push($scope.mapSong(item)); $rootScope.song.push(utils.mapSong(item));
}); });
$scope.$apply(); $scope.$apply();
} }

View file

@ -1,6 +1,6 @@
JamStash.controller('PodcastCtrl', JamStash.controller('PodcastCtrl',
function PodcastCtrl($scope, $rootScope, $location, utils, globals, model, notifications) { function PodcastCtrl($scope, $rootScope, $location, utils, globals, model, notifications) {
$("#LayoutContainer").layout($scope.layoutTwoCol); //$("#left-component").layout($scope.layoutTwoCol);
$rootScope.song = []; $rootScope.song = [];
$scope.podcasts = []; $scope.podcasts = [];
@ -75,7 +75,7 @@ function PodcastCtrl($scope, $rootScope, $location, utils, globals, model, notif
} }
}); });
$scope.$apply(); $scope.$apply();
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else if (action == 'play') { } else if (action == 'play') {
$rootScope.queue = []; $rootScope.queue = [];
@ -88,9 +88,10 @@ function PodcastCtrl($scope, $rootScope, $location, utils, globals, model, notif
$scope.$apply(function () { $scope.$apply(function () {
$rootScope.playSong(false, next); $rootScope.playSong(false, next);
}); });
$('body').layout().open('south'); $rootScope.showQueue();
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true); notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else { } else {
$scope.album = [];
$rootScope.song = []; $rootScope.song = [];
angular.forEach(items, function (item, key) { angular.forEach(items, function (item, key) {
if (item.status != "skipped") { if (item.status != "skipped") {

View file

@ -13,23 +13,11 @@ function SettingsCtrl($scope, $routeParams, $location, utils, globals, json, not
]; ];
$scope.Protocols = ["json", "jsonp"]; $scope.Protocols = ["json", "jsonp"];
$scope.Themes = ["Default", "Dark"]; $scope.Themes = ["Default", "Dark"];
$scope.SearchTypeLayout = [
{ id: "song", name: "Song" },
{ id: "album", name: "Album" },
];
$scope.DefaultSearchType = 'song';
$scope.LibraryLayouts = [ $scope.LibraryLayouts = [
{ id: "list", name: "List" }, { id: "list", name: "List" },
{ id: "grid", name: "Grid" }, { id: "grid", name: "Grid" },
]; ];
$scope.DefaultLibraryLayout = 'grid'; $scope.DefaultLibraryLayout = 'grid';
$scope.DefaultSubsonicAlbumSort = 'list';
$scope.SubsonicAlbumSort = [
{ id: "default", name: "Default Sort" },
{ id: "artist", name: "Artist" },
{ id: "album", name: "Album" },
{ id: "createdate desc", name: "Created Date - Desc" },
];
$scope.$watch('settings.HideAZ', function () { $scope.$watch('settings.HideAZ', function () {
if (globals.settings.HideAZ) { if (globals.settings.HideAZ) {
$('#AZIndex').hide(); $('#AZIndex').hide();

View file

@ -1,7 +1,15 @@
[ [
{
"date": "1/12/2014", "version": "3.2",
"changes": [
{ "text": "- Redesigned Queue, moved buttons" },
{ "text": "- Added Play button to play current song list" },
{ "text": "- Fixed drag & drop sorting, Artist list will refresh on Folder change" }
]
},
{ "date": "12/5/2013", "version": "3.1.2", { "date": "12/5/2013", "version": "3.1.2",
"changes": [ "changes": [
{ "text": "- Improved linking between tabs, + Playlist fixed, " } { "text": "- Improved linking between tabs, + Playlist fixed" }
] ]
}, },
{ "date": "10/31/2013", "version": "3.1.1", { "date": "10/31/2013", "version": "3.1.1",

View file

@ -7,8 +7,8 @@
<a href="" class="button" id="action_AddToQueue" title="Add To Queue" ng-click="addSongsToQueue()">+ Queue</a> <a href="" class="button" id="action_AddToQueue" title="Add To Queue" ng-click="addSongsToQueue()">+ Queue</a>
</div> </div>
<div class="clear"></div> <div class="clear"></div>
<div id="LayoutContainer" class="section lgsection"> <div id="LayoutContainer" class="section lgsection split-pane fixed-left" split>
<div class="ui-layout-west noselect hide" tabindex="0"> <div id="left-component" class="split-pane-component smcolumn noselect" tabindex="0">
<!-- Artist --> <!-- Artist -->
<select class="large" id="Collections" ng-disabled="!loadedCollection" ng-model="selectedCollection" ng-options="o for o in AllCollections"> <select class="large" id="Collections" ng-disabled="!loadedCollection" ng-model="selectedCollection" ng-options="o for o in AllCollections">
<option value="">Select Collection</option> <option value="">Select Collection</option>
@ -27,48 +27,50 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="split-pane-divider" id="my-divider"></div>
<!-- Album --> <!-- Album -->
<div class="ui-layout-center"> <div id="right-component" class="split-pane-component lgcolumn">
<ul class="actionlist"> <ul class="actionlist">
<li> <li>
<form class="form"> <form class="form">
<select ng-model="selectedArchiveAlbumSort" ng-options="o for o in ArchiveAlbumSort"></select> <select ng-model="selectedArchiveAlbumSort" ng-options="o for o in ArchiveAlbumSort"></select>
<select id="Years" ng-model="filter.Year" ng-options="o for o in Years"> <select id="Years" ng-model="filter.Year" ng-options="o for o in Years">
<option value="">[Year]</option> <option value="">[Year]</option>
</select> </select>
<input type="text" id="Source" name="Source" class="sm" ng-model="filter.Source" placeholder="Source" title="Source"/> <input type="text" id="Source" name="Source" class="sm" ng-model="filter.Source" placeholder="Source" title="Source" />
<input type="text" id="Description" name="Description" class="m" ng-model="filter.Description" placeholder="Description" title="Description"/> <input type="text" id="Description" name="Description" class="m" ng-model="filter.Description" placeholder="Description" title="Description" />
<a href="" class="button" ng-click="filterSave()">Go</a>&nbsp; <a href="" class="button" ng-click="filterSave()">Go</a>&nbsp;
</form> </form>
</li> </li>
</ul> </ul>
<div class="clear"></div> <div class="clear"></div>
<ul class="simplelist songlist noselect"> <ul class="simplelist songlist noselect">
<li class="album" ng-repeat="o in album" id="{{o.id}}" parentid="{{o.parentid}}" ng-class="{'selected': selectedAlbum == o.id}" ng-click="getSongs(o.id, '')"> <div class="" ng-repeat="o in album" ng-switch on="o.type">
<div id="{{'AlbumInfo' + $index}}" class="infolink"> <li class="album" id="{{o.id}}" parentid="{{o.parentid}}" ng-class="{'selected': selectedAlbum == o.id}" ng-click="getSongs(o.id, '')">
<a href="" class="hover" title="More Info..." ng-click="toggleSubmenu('#submenu_AlbumInfo' + $index, '#AlbumInfo' + $index, 'right', 10)" stop-event="click"><img src="images/info_gl_6x12.png" /></a> <div id="{{'AlbumInfo' + $index}}" class="infolink">
</div> <a href="" class="hover" title="More Info..." ng-click="toggleSubmenu('#submenu_AlbumInfo' + $index, '#AlbumInfo' + $index, 'right', 10)" stop-event="click"><img src="images/info_gl_6x12.png" /></a>
<div id="{{'submenu_AlbumInfo' + $index}}" class="submenu_AlbumInfo submenu shadow" style="display: none;"> </div>
<a href="" ng-href="{{o.url}}" title="{{o.url}}" target="_blank" stop-event="click">Source</a><br /> <div id="{{'submenu_AlbumInfo' + $index}}" class="submenu_AlbumInfo submenu shadow" style="display: none;">
<a href="" ng-href="{{settings.Url + o.artist + '/' + o.id}}" title="{{settings.Url + o.artist + '/' + o.id}}" target="_blank" stop-event="click">PermaStash</a> <a href="" ng-href="{{o.url}}" title="{{o.url}}" target="_blank" stop-event="click">Source</a><br />
</div> <a href="" ng-href="{{settings.Url + o.artist + '/' + o.id}}" title="{{settings.Url + o.artist + '/' + o.id}}" target="_blank" stop-event="click">PermaStash</a>
<div class="itemactions"> </div>
<a class="add" href="" title="Add To Play Queue" ng-click="getSongs(o.id, 'add')" stop-event="click"></a> <div class="itemactions">
<a class="play" href="" title="Play" ng-click="getSongs(o.id, 'play')" stop-event="click"></a> <a class="add" href="" title="Add To Play Queue" ng-click="getSongs(o.id, 'add')" stop-event="click"></a>
<a class="download" href="" title="Download"></a> <a class="play" href="" title="Play" ng-click="getSongs(o.id, 'play')" stop-event="click"></a>
<a href="" title="Favorite" ng-class="{'favorite': o.starred, 'rate': !o.starred}" ng-click="updateFavorite(o)" stop-event="click"></a> <a class="download" href="" title="Download"></a>
</div> <a href="" title="Favorite" ng-class="{'favorite': o.starred, 'rate': !o.starred}" ng-click="updateFavorite(o)" stop-event="click"></a>
<div class="albumart"><img ng-src="{{o.coverart}}" src="images/albumdefault_50.jpg"></div> </div>
<div class="title">{{o.name}}</div> <div class="albumart"><img ng-src="{{o.coverart}}" src="images/albumdefault_50.jpg"></div>
<div class="artist"><a href="" id="{{o.parentid}}" ng-click="getAlbums(o.artist, o.id)" stop-event="click">{{o.artist}}</a></div> <div class="title">{{o.name}}</div>
<!--<div class="details">Created: {{o.date}}</div>--> <div class="artist"><a href="" id="{{o.parentid}}" ng-click="getAlbums(o.artist, o.id)" stop-event="click">{{o.artist}}</a></div>
<div class="description shadow" ng-show="selectedAlbum == o.id" ng-bind-html-unsafe="o.description"></div> <!--<div class="details">Created: {{o.date}}</div>-->
<div class="clear"></div> <div class="description shadow" ng-show="selectedAlbum == o.id" ng-bind-html="o.description"></div>
</li> <div class="clear"></div>
</li>
</div>
<div ng-include src="'js/partials/songs.html'"></div>
</ul> </ul>
</div> </div>
<!-- Song -->
<div class="ui-layout-east noselect hide" ng-include src="'js/partials/songs.html'"></div>
</div> </div>
</div> </div>
<div class="clear"></div> <div class="clear"></div>

View file

@ -2,15 +2,19 @@
<div id="tab1" class="tabcontent"> <div id="tab1" class="tabcontent">
<div id="tabLibrary"> <div id="tabLibrary">
<div class="actions floatleft"> <div class="actions floatleft">
<a href="" class="button" id="action_RefreshArtists" title="Refresh Artists" ng-click="getArtists()"><img class="pad" src="images/reload_9x11.png" /></a> <a href="" class="button" id="action_RefreshArtists" title="Refresh Artists" ng-click="refreshArtists()"><img class="pad" src="images/reload_9x11.png" /></a>
<a href="" class="button" id="action_RescanLibrary" title="Rescan Library" ng-click="rescanLibrary()"><img class="pad" src="images/loop_alt1_gd_12x9.png" /></a> <a href="" class="button" id="action_RescanLibrary" title="Rescan Library" ng-click="rescanLibrary()"><img class="pad" src="images/loop_alt1_gd_12x9.png" /></a>
</div> </div>
<div id="search"> <div id="search">
<input type="text" id="Search" class="medium" title="Wildcards (*) supported" placeholder="Search..." ng-enter="search()"/> <input type="text" id="Search" class="medium" title="Wildcards (*) supported" placeholder="Search..." ng-enter="search()"/>
<select id="SearchType" name="SearchType" ng-model="SearchType" ng-options="o.id as o.name for o in SearchTypeLayout"></select> <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" ng-click="search()"><img class="pad" src="images/magnifying_glass_alt_12x12.png" /></a> <a href="" class="button" id="action_Search" title="Search" ng-click="search()"><img class="pad" src="images/magnifying_glass_alt_12x12.png" /></a>
</div> </div>
<div class="subactions"> <div class="subactions">
<a href="" class="button" id="action_PlayAlbum" title="Play Album" ng-click="playAll()"><img src="images/play_gl_6x8.png"></a>
<a href="" class="button" id="action_SelectAll" title="Select All" ng-click="selectAll()">All</a> <a href="" class="button" id="action_SelectAll" title="Select All" ng-click="selectAll()">All</a>
<a href="" class="button" id="action_SelectNone" title="Select None" ng-click="selectNone()">None</a> <a href="" class="button" id="action_SelectNone" title="Select None" ng-click="selectNone()">None</a>
<a href="" class="button" id="action_AddToQueue" title="Add To Queue" ng-click="addSongsToQueue()">+ Queue</a> <a href="" class="button" id="action_AddToQueue" title="Add To Queue" ng-click="addSongsToQueue()">+ Queue</a>
@ -19,96 +23,99 @@
<a href="" ng-repeat="o in playlistMenu" ng-click="addToPlaylist(o.id)">{{o.name}}</a> <a href="" ng-repeat="o in playlistMenu" ng-click="addToPlaylist(o.id)">{{o.name}}</a>
</div> </div>
</div> </div>
<div class="clear"></div> <div id="SubsonicAlbums" class="section lgsection split-pane fixed-left" split>
<div id="SubsonicAlbums" class="section lgsection"> <!--<div id="SubsonicAlbums" class="section lgsection" layout state="bodyState" ng-init="bodyState = 3">-->
<div id="SubsonicArtists" class="ui-layout-west noselect hide" tabindex="0"> <!--<div id="SubsonicAlbums" class="section lgsection">-->
<div id="left-component" class="split-pane-component smcolumn noselect" tabindex="0">
<div id="AZIndex" ng-show="!settings.HideAZ" class="subactionsfixed"> <div id="AZIndex" ng-show="!settings.HideAZ" class="subactionsfixed">
<a href="" ng-click="toggleAZ()" stop-event="click">A-Z</a> <a href="" ng-click="toggleAZ()" stop-event="click">A-Z</a>
</div> </div>
<div id="submenu_AZIndex" class="submenu shadow" style="display: none;">
<ul>
<li ng-repeat="o in index"><a href="" ng-click="scrollToIndexName(o.name)">{{o.name}}</a></li>
<li><a href="" class="close" ng-click="scrollToIndexName('AZIndex')">[Top]</a></li>
<li><a href="" class="close" ng-click="toggleAZ()">[Close]</a></li>
</ul>
</div>
<select id="MusicFolders" class="folders" ng-model="$root.selectedMusicFolder" ng-options="o as o.name for o in MusicFolders"> <select id="MusicFolders" class="folders" ng-model="$root.selectedMusicFolder" ng-options="o as o.name for o in MusicFolders">
<option value="">All Folders</option> <option value="">All Folders</option>
</select> </select>
<ul id="AutoAlbumContainer" class="simplelist mainlist noselect"> <ul id="AutoAlbumContainer" class="simplelist mainlist noselect">
<li class="index" id="auto">Auto Albums</li> <li class="index" id="auto">Auto Albums</li>
<li class="item" ng-repeat="o in AutoAlbums" id="{{o.id}}" ng-click="getAlbumListBy(o.id)" ng-class="{'selected': selectedAutoAlbum == o.id }"><span>{{o.name}}</span> <li class="item" ng-repeat="o in AutoAlbums" id="{{o.id}}" ng-click="getAlbumListBy(o.id)" ng-class="{'selected': selectedAutoAlbum == o.id }">
<div class="floatright"> <span>{{o.name}}</span>
<a href="" class="nextprev hover" id="random" title="Previous" ng-click="getAlbumListBy(o.id, 'prev')" stop-event="click">&lsaquo;</a> <div class="floatright">
<a href="" class="nextprev hover" id="random" title="Next" ng-click="getAlbumListBy(o.id, 'next')" stop-event="click">&rsaquo;</a> <a href="" class="nextprev hover" id="random" title="Previous" ng-click="getAlbumListBy(o.id, 'prev')" stop-event="click">&lsaquo;</a>
</div> <a href="" class="nextprev hover" id="random" title="Next" ng-click="getAlbumListBy(o.id, 'next')" stop-event="click">&rsaquo;</a>
</li> </div>
</li>
</ul> </ul>
<!-- Shortcut --> <!-- Shortcut -->
<ul class="simplelist mainlist noselect" ng-show="shortcut.length"> <ul class="simplelist mainlist noselect" ng-show="shortcut.length">
<li class="index" title="Scroll to Top" data-bind="click: $root.scrollToTop"><a>Shortcuts</a></li> <li class="index" title="Scroll to Top" data-bind="click: $root.scrollToTop"><a>Shortcuts</a></li>
<ul class="simplelist mainlist noselect" ng-repeat="o in shortcut"> <ul class="simplelist mainlist noselect" ng-repeat="o in shortcut">
<li class="item" id="{{o.id}}" ng-click="getAlbums(o.id)" ng-class="{'selected': selectedArtist == o.id}"><span ng-bind-html-unsafe="o.name"></span></li> <li class="item" id="{{o.id}}" ng-click="getAlbums(o.id, o.name)" ng-class="{'selected': selectedArtist == o.id}"><span ng-bind-html-unsafe="o.name"></span></li>
</ul> </ul>
</ul> </ul>
<!-- Artist --> <!-- Artist -->
<ul class="simplelist mainlist noselect" ng-repeat="o in index"> <ul class="simplelist mainlist noselect" ng-repeat="o in index">
<li class="index" title="Scroll to Top" id="{{o.name}}" ng-click="scrollToTop()"><a>{{o.name}}</a><span class="floatright">?</span></li> <li class="index" title="Scroll to Top" id="{{o.name}}" ng-click="scrollToTop()"><a>{{o.name}}</a><span class="floatright">?</span></li>
<ul class="simplelist mainlist noselect"> <ul class="simplelist mainlist noselect">
<li class="item" id="{{a.id}}" ng-repeat="a in o.artist" ng-class="{'selected': selectedArtist == a.id}" ng-click="getAlbums(a.id)"><a ng-href="" ng-bind-html-unsafe="a.name"></a></li> <li class="item" id="{{a.id}}" ng-repeat="a in o.artist" ng-class="{'selected': selectedArtist == a.id}" ng-click="getAlbums(a.id, a.name)"><a ng-href="" ng-bind-html-unsafe="a.name"></a></li>
</ul> </ul>
</ul> </ul>
</div> </div>
<div class="split-pane-divider" id="my-divider"></div>
<!-- Album --> <!-- Album -->
<div class="ui-layout-center"> <div id="right-component" class="split-pane-component lgcolumn">
<ul class="actionlist"> <ul class="actionlist">
<li> <li>
<form class="form"> <form class="form">
<select id="selectedSubsonicAlbumSort" ng-model="selectedSubsonicAlbumSort" ng-options="o.id as o.name for o in SubsonicAlbumSort"></select> <select id="selectedSubsonicAlbumSort" ng-model="selectedSubsonicAlbumSort" ng-show="SubsonicSort.length" ng-options="o.id as o.name for o in SubsonicSort"></select>
<select id="selectedLayout" ng-model="selectedLayout" ng-options="o.id as o.name for o in Layouts"></select> <!--<select id="selectedLayout" ng-model="selectedLayout" ng-options="o.id as o.name for o in Layouts"></select>-->
</form> </form>
<!--
<div id="BreadCrumb"> <div id="BreadCrumb">
<a href="#" id="BreadHome"><img src="images/home_gl_12x12.png"></a> <a href="#" id="BreadHome"><img src="images/home_gl_12x12.png"></a>
<div id="BreadCrumbs" class="floatleft"><a ng-show="artistId" ng-href="#/library/{{artistId}}">AC/DC</a> &gt;<a ng-show="albumId" ng-href="#/library/{{albumId}}">Back In Black</a></div> <div id="BreadCrumbs" class="floatleft">
<div class="crumb" ng-repeat="o in BreadCrumbs | filter:{type:'artist'}"><a ng-click="getAlbums(o.id, o.name)">{{o.name}}</a> &gt;</div>
<div class="crumb" ng-repeat="o in BreadCrumbs | filter:{type:'album'}"><a ng-click="getSongs(o.id, '')">{{o.name}}</a> &gt;</div>
</div>
</div> </div>
-->
</li> </li>
</ul> </ul>
<div class="clear"></div> <div class="clear"></div>
<ul class="simplelist songlist noselect"> <ul class="simplelist songlist noselect">
<div class="animate-switch-container" ng-repeat="o in album" ng-switch on="o.type"> <div class="" ng-repeat="o in album" ng-switch on="o.type">
<!--<div class="animate-switch" ng-switch-default>default</div>--> <li class="album" ng-switch-when="byfolder" id="{{o.id}}" ng-class="{'selected': selectedAlbum == o.id, 'albumgrid': selectedLayout == 'grid'}" ng-click="getSongs(o.id, '')" parentid="{{o.parentid}}">
<li class="album" ng-switch-when="byfolder" id="{{o.id}}" ng-class="{'selected': selectedAlbum == o.id, 'albumgrid': selectedLayout == 'grid'}" ng-click="getSongs(o.id, '')" parentid="{{o.parentid}}"> <div class="itemactions">
<div class="itemactions"> <a class="add" href="" title="Add To Play Queue" ng-click="getSongs(o.id, 'add')" stop-event="click"></a>
<a class="add" href="" title="Add To Play Queue" ng-click="getSongs(o.id, 'add')" stop-event="click"></a> <a class="play" href="" title="Play" ng-click="getSongs(o.id, 'play')" stop-event="click"></a>
<a class="play" href="" title="Play" ng-click="getSongs(o.id, 'play')" stop-event="click"></a> <a class="download" href="" ng-click="download(o.id)" title="Download" stop-event="click"></a>
<a class="download" href="" ng-click="download(o.id)" title="Download" stop-event="click"></a> <a title="Favorite" href="" ng-class="{'favorite': o.starred, 'rate': !o.starred}" ng-click="updateFavorite(o)" stop-event="click"></a>
<a title="Favorite" href="" ng-class="{'favorite': o.starred, 'rate': !o.starred}" ng-click="updateFavorite(o)" stop-event="click"></a> <a class="info hover" href="" title="{{'Created: ' + o.date}}"></a>
<a class="info hover" href="" title="{{'Created: ' + o.date}}"></a> </div>
</div> <div class="albumart"><img ng-src="{{o.coverartthumb}}" src="images/albumdefault_160.jpg"></div>
<div class="albumart"><img ng-src="{{o.coverartthumb}}" src="images/albumdefault_160.jpg"></div> <div class="albuminfo">
<div class="albuminfo"> <div class="title" title="{{o.name}}" ng-bind-html-unsafe="o.name"></div>
<div class="title" title="{{o.name}}" ng-bind-html-unsafe="o.name"></div> <div class="artist" title="{{o.artist}}"><a href="" id="{{o.parentid}}" ng-click="getAlbums(o.parentid, o.artist)" stop-event="click" ng-bind-html-unsafe="o.artist"></a></div>
<div class="artist" title="{{o.artist}}"><a href="" id="{{o.parentid}}" ng-click="getAlbums(o.parentid)" stop-event="click" ng-bind-html-unsafe="o.artist"></a></div> </div>
</div> <div class="clear"></div>
<div class="clear"></div> </li>
</li> <li class="album" ng-switch-when="bytag" id="{{o.id}}" ng-class="{'selected': selectedAlbum == o.id, 'albumgrid': selectedLayout == 'grid'}" ng-click="getAlbumByTag(o.id)">
<li class="album" ng-switch-when="bytag" id="{{o.id}}" ng-class="{'selected': selectedAlbum == o.id, 'albumgrid': selectedLayout == 'grid'}" ng-click="getAlbumByTag(o.id)"> <div class="albumart"><img ng-src="{{o.coverartthumb}}" src="images/albumdefault_160.jpg"></div>
<div class="albumart"><img ng-src="{{o.coverartthumb}}" src="images/albumdefault_160.jpg"></div> <div class="albuminfo">
<div class="albuminfo"> <div class="title" title="{{o.name}}" ng-bind-html-unsafe="o.name"></div>
<div class="title" title="{{o.name}}" ng-bind-html-unsafe="o.name"></div> <div class="artist" title="{{o.artist}}"><a href="" ng-click="getArtistByTag(o.artistId)" stop-event="click" ng-bind-html-unsafe="o.artist"></a></div>
<div class="artist" title="{{o.artist}}"><a href="" ng-click="getArtistByTag(o.artistId)" stop-event="click" ng-bind-html-unsafe="o.artist"></a></div> </div>
</div> <div class="clear"></div>
<div class="clear"></div> </li>
</li> </div>
</div> <div ng-include src="'js/partials/songs.html'"></div>
</ul> </ul>
</div> </div>
<!-- Song -->
<div class="ui-layout-east noselect hide" ng-include src="'js/partials/songs.html'"></div>
</div> </div>
<div id="submenu_AZIndex" class="submenu shadow" style="display: none;">
<ul>
<li ng-repeat="o in index"><a href="" ng-click="scrollToIndexName(o.name)">{{o.name}}</a></li>
<li><a href="" class="close" ng-click="scrollToIndexName('AZIndex')">[Top]</a></li>
<li><a href="" class="close" ng-click="toggleAZ()">[Close]</a></li>
</ul>
</div>
<!--<songpreview class="songpreview"></songpreview>-->
</div> </div>
<div class="clear"></div> <div class="clear"></div>
</div> </div>
<!-- End: Library Tab --> <!-- End: Library Tab -->

View file

@ -0,0 +1,119 @@
<!-- Start: Library Tab -->
<div id="tab1" class="tabcontent">
<div id="tabLibrary">
<div class="actions floatleft">
<a href="" class="button" id="action_RefreshArtists" title="Refresh Artists" ng-click="getArtists()"><img class="pad" src="images/reload_9x11.png" /></a>
<a href="" class="button" id="action_RescanLibrary" title="Rescan Library" ng-click="rescanLibrary()"><img class="pad" src="images/loop_alt1_gd_12x9.png" /></a>
</div>
<div id="search">
<input type="text" id="Search" class="medium" title="Wildcards (*) supported" placeholder="Search..." ng-enter="search()"/>
<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" ng-click="search()"><img class="pad" src="images/magnifying_glass_alt_12x12.png" /></a>
</div>
<div class="subactions">
<a href="" class="button" id="action_SelectAll" title="Select All" ng-click="selectAll()">All</a>
<a href="" class="button" id="action_SelectNone" title="Select None" ng-click="selectNone()">None</a>
<a href="" class="button" id="action_AddToQueue" title="Add To Queue" ng-click="addSongsToQueue()">+ Queue</a>
<a href="" class="button" id="action_AddToPlaylist" title="Add Selected To Playlist" ng-click="loadPlaylistsForMenu()">+ Playlist</a>
<div id="submenu_AddToPlaylist" class="submenu shadow" style="display: none;">
<a href="" ng-repeat="o in playlistMenu" ng-click="addToPlaylist(o.id)">{{o.name}}</a>
</div>
</div>
<div class="clear"></div>
<!--<div id="SubsonicAlbums" class="section lgsection" layout state="bodyState" ng-init="bodyState = 3">-->
<div id="SubsonicAlbums" class="section lgsection">
<div id="SubsonicArtists" class="ui-layout-west noselect hide" tabindex="0">
<div id="AZIndex" ng-show="!settings.HideAZ" class="subactionsfixed">
<a href="" ng-click="toggleAZ()" stop-event="click">A-Z</a>
</div>
<div id="submenu_AZIndex" class="submenu shadow" style="display: none;">
<ul>
<li ng-repeat="o in index"><a href="" ng-click="scrollToIndexName(o.name)">{{o.name}}</a></li>
<li><a href="" class="close" ng-click="scrollToIndexName('AZIndex')">[Top]</a></li>
<li><a href="" class="close" ng-click="toggleAZ()">[Close]</a></li>
</ul>
</div>
<select id="MusicFolders" class="folders" ng-model="$root.selectedMusicFolder" ng-options="o as o.name for o in MusicFolders">
<option value="">All Folders</option>
</select>
<ul id="AutoAlbumContainer" class="simplelist mainlist noselect">
<li class="index" id="auto">Auto Albums</li>
<li class="item" ng-repeat="o in AutoAlbums" id="{{o.id}}" ng-click="getAlbumListBy(o.id)" ng-class="{'selected': selectedAutoAlbum == o.id }">
<span>{{o.name}}</span>
<div class="floatright">
<a href="" class="nextprev hover" id="random" title="Previous" ng-click="getAlbumListBy(o.id, 'prev')" stop-event="click">&lsaquo;</a>
<a href="" class="nextprev hover" id="random" title="Next" ng-click="getAlbumListBy(o.id, 'next')" stop-event="click">&rsaquo;</a>
</div>
</li>
</ul>
<!-- Shortcut -->
<ul class="simplelist mainlist noselect" ng-show="shortcut.length">
<li class="index" title="Scroll to Top" data-bind="click: $root.scrollToTop"><a>Shortcuts</a></li>
<ul class="simplelist mainlist noselect" ng-repeat="o in shortcut">
<li class="item" id="{{o.id}}" ng-click="getAlbums(o.id)" ng-class="{'selected': selectedArtist == o.id}"><span ng-bind-html-unsafe="o.name"></span></li>
</ul>
</ul>
<!-- Artist -->
<ul class="simplelist mainlist noselect" ng-repeat="o in index">
<li class="index" title="Scroll to Top" id="{{o.name}}" ng-click="scrollToTop()"><a>{{o.name}}</a><span class="floatright">?</span></li>
<ul class="simplelist mainlist noselect">
<li class="item" id="{{a.id}}" ng-repeat="a in o.artist" ng-class="{'selected': selectedArtist == a.id}" ng-click="getAlbums(a.id)"><a ng-href="" ng-bind-html-unsafe="a.name"></a></li>
</ul>
</ul>
</div>
<!-- Album -->
<div class="ui-layout-center">
<ul class="actionlist">
<li>
<form class="form">
<select id="selectedSubsonicAlbumSort" ng-model="selectedSubsonicAlbumSort" ng-options="o.id as o.name for o in SubsonicSort"></select>
<select id="selectedLayout" ng-model="selectedLayout" ng-options="o.id as o.name for o in Layouts"></select>
</form>
<!--
<div id="BreadCrumb">
<a href="#" id="BreadHome"><img src="images/home_gl_12x12.png"></a>
<div id="BreadCrumbs" class="floatleft"><a ng-show="artistId" ng-href="#/library/{{artistId}}">AC/DC</a> &gt;<a ng-show="albumId" ng-href="#/library/{{albumId}}">Back In Black</a></div>
</div>
-->
</li>
</ul>
<div class="clear"></div>
<ul class="simplelist songlist noselect">
<div class="animate-switch-container" ng-repeat="o in album" ng-switch on="o.type">
<!--<div class="animate-switch" ng-switch-default>default</div>-->
<li class="album" ng-switch-when="byfolder" id="{{o.id}}" ng-class="{'selected': selectedAlbum == o.id, 'albumgrid': selectedLayout == 'grid'}" ng-click="getSongs(o.id, '')" parentid="{{o.parentid}}">
<div class="itemactions">
<a class="add" href="" title="Add To Play Queue" ng-click="getSongs(o.id, 'add')" stop-event="click"></a>
<a class="play" href="" title="Play" ng-click="getSongs(o.id, 'play')" stop-event="click"></a>
<a class="download" href="" ng-click="download(o.id)" title="Download" stop-event="click"></a>
<a title="Favorite" href="" ng-class="{'favorite': o.starred, 'rate': !o.starred}" ng-click="updateFavorite(o)" stop-event="click"></a>
<a class="info hover" href="" title="{{'Created: ' + o.date}}"></a>
</div>
<div class="albumart"><img ng-src="{{o.coverartthumb}}" src="images/albumdefault_160.jpg"></div>
<div class="albuminfo">
<div class="title" title="{{o.name}}" ng-bind-html-unsafe="o.name"></div>
<div class="artist" title="{{o.artist}}"><a href="" id="{{o.parentid}}" ng-click="getAlbums(o.parentid)" stop-event="click" ng-bind-html-unsafe="o.artist"></a></div>
</div>
<div class="clear"></div>
</li>
<li class="album" ng-switch-when="bytag" id="{{o.id}}" ng-class="{'selected': selectedAlbum == o.id, 'albumgrid': selectedLayout == 'grid'}" ng-click="getAlbumByTag(o.id)">
<div class="albumart"><img ng-src="{{o.coverartthumb}}" src="images/albumdefault_160.jpg"></div>
<div class="albuminfo">
<div class="title" title="{{o.name}}" ng-bind-html-unsafe="o.name"></div>
<div class="artist" title="{{o.artist}}"><a href="" ng-click="getArtistByTag(o.artistId)" stop-event="click" ng-bind-html-unsafe="o.artist"></a></div>
</div>
<div class="clear"></div>
</li>
</div>
</ul>
</div>
<!-- Song -->
<div class="ui-layout-east noselect hide" ng-include src="'js/partials/songs.html'"></div>
</div>
</div>
<div class="clear"></div>
</div>
<!-- End: Library Tab -->

View file

@ -14,9 +14,9 @@
<a href="" class="button" id="action_RemoveSongs" title="Remove selected song(s) from playlist" ng-click="removeSelectedSongs()">Remove Song(s)</a> <a href="" class="button" id="action_RemoveSongs" title="Remove selected song(s) from playlist" ng-click="removeSelectedSongs()">Remove Song(s)</a>
</div> </div>
<div class="clear"></div> <div class="clear"></div>
<div id="LayoutContainer" class="section lgsection"> <div id="LayoutContainer" class="section lgsection split-pane fixed-left" split>
<!-- Playlists --> <!-- Playlists -->
<div class="ui-layout-west noselect hide" tabindex="0"> <div id="left-component" class="split-pane-component smcolumn noselect" tabindex="0">
<ul class="simplelist mainlist noselect"> <ul class="simplelist mainlist noselect">
<li class="index" id="auto">Auto Playlists</li> <li class="index" id="auto">Auto Playlists</li>
<li class="item" ng-click="getStarred('', 'song')" ng-class="{'selected': selectedAutoPlaylist == 'starred'}"> <li class="item" ng-click="getStarred('', 'song')" ng-class="{'selected': selectedAutoPlaylist == 'starred'}">
@ -51,7 +51,7 @@
<div class="title">{{o.name}}</div> <div class="title">{{o.name}}</div>
</li> </li>
</ul> </ul>
<ul class="simplelist mainlist noselect" > <ul class="simplelist mainlist noselect">
<li class="index" id="auto">My Playlists</li> <li class="index" id="auto">My Playlists</li>
<li class="item" ng-repeat="o in playlists" ng-click="getPlaylist(o.id, '')" ng-class="{'selected': o.id == selectedPlaylist}"> <li class="item" ng-repeat="o in playlists" ng-click="getPlaylist(o.id, '')" ng-class="{'selected': o.id == selectedPlaylist}">
<div class="itemactions"> <div class="itemactions">
@ -60,7 +60,7 @@
<div class="title" title="{{'Songs: ' + o.songCount + ', Public: ' + o.public}}">{{o.name}}</div> <div class="title" title="{{'Songs: ' + o.songCount + ', Public: ' + o.public}}">{{o.name}}</div>
</li> </li>
</ul> </ul>
<ul class="simplelist mainlist noselect" > <ul class="simplelist mainlist noselect">
<li class="index" id="auto">Shared Playlists</li> <li class="index" id="auto">Shared Playlists</li>
<li class="item" ng-repeat="o in playlistsPublic" ng-click="getPlaylist(o.id, '')" ng-class="{'selected': o.id == selectedPlaylist}"> <li class="item" ng-repeat="o in playlistsPublic" ng-click="getPlaylist(o.id, '')" ng-class="{'selected': o.id == selectedPlaylist}">
<div class="itemactions"> <div class="itemactions">
@ -70,8 +70,11 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="split-pane-divider" id="my-divider"></div>
<!-- Songs --> <!-- Songs -->
<div class="ui-layout-center noselect hide" ng-include src="'js/partials/songs.html'"></div> <div id="right-component" class="split-pane-component lgcolumn noselect">
<ul class="songlist simplelist" ng-include src="'js/partials/songs.html'"></ul>
</div>
</div> </div>
</div> </div>
<div class="clear"></div> <div class="clear"></div>

View file

@ -10,9 +10,9 @@
<a href="" class="button" id="action_AddToQueue" title="Add To Queue" ng-click="addSongsToQueue()">+ Queue</a> <a href="" class="button" id="action_AddToQueue" title="Add To Queue" ng-click="addSongsToQueue()">+ Queue</a>
</div> </div>
<div class="clear"></div> <div class="clear"></div>
<div id="LayoutContainer" class="section lgsection"> <div id="LayoutContainer" class="section lgsection split-pane fixed-left" split>
<!-- Artist --> <!-- Artist -->
<div class="ui-layout-west noselect hide" tabindex="0"> <div id="left-component" class="split-pane-component smcolumn noselect" tabindex="0">
<ul class="simplelist mainlist noselect"> <ul class="simplelist mainlist noselect">
<li class="index" id="podcasts">Podcasts</li> <li class="index" id="podcasts">Podcasts</li>
<li class="item" ng-repeat="o in podcasts" ng-click="getPodcast(o.id, '')" ng-class="{ 'selected': o.id == selectedPodcast }"> <li class="item" ng-repeat="o in podcasts" ng-click="getPodcast(o.id, '')" ng-class="{ 'selected': o.id == selectedPodcast }">
@ -23,8 +23,11 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="split-pane-divider" id="my-divider"></div>
<!-- Song --> <!-- Song -->
<div class="ui-layout-center noselect hide" ng-include src="'js/partials/songs.html'"></div> <div id="right-component" class="split-pane-component lgcolumn noselect">
<ul class="songlist simplelist" ng-include src="'js/partials/songs.html'"></ul>
</div>
</div> </div>
</div> </div>
<div class="clear"></div> <div class="clear"></div>

View file

@ -1,6 +1,108 @@
<div id="settings" class="tabcontent"> <div id="settings" class="tabcontent">
<div class="section fullsection floatleft"> <div class="section fullsection floatleft">
<form class="form" id="Settings" ng-include="'js/partials/settingsForm.html'"></form>
<form class="form" id="Settings">
<div class="subsection floatleft">
<h3 class="title">Login</h3>
<label for="Username">Username <span class="red">*</span></label><br />
<input type="text" id="Username" name="Username" class="large" ng-model="settings.Username" /><br />
<label for="Password">Password <span class="red">*</span></label><br />
<input type="password" id="Password" name="Password" class="large" ng-model="settings.Password" /><br />
<label for="Server">Server <span class="red">*</span> (<a href="" title="Connect to Demo Subsonic Server" ng-click="setupDemo()">Demo</a>)</label><br />
<input type="text" id="Server" name="Server" class="xlarge" title="Subsonic Server URL Ex: http://host:port/subsonic" ng-model="settings.Server" /><br />
<!--<a href="#" class="button" id="action_RequestURL" title="Request Permission for Server URL">Enable URL</a><br />-->
<label for="SubsonicVersion">Subsonic API: <span class="apiversion" id="SubsonicVersion">{{settings.ApiVersion}}</span></label><br />
<label for="SMStats">Audio State: <span id="SMStats"></span></label><br /><br />
<br />
<a id="action_Welcome" href="#welcome">Launch Welcome</a>
<div id="welcome"><h2>Welcome</h2></div>
</div>
<div class="subsection floatleft">
<h3 class="title">Options</h3>
<label for="Theme">Theme</label><br />
<select id="Theme" name="Theme" class="large" ng-model="settings.Theme" ng-options="o for o in Themes"></select><br />
<!--<label for="AutoFilter">Filter</label><br />
<input type="text" id="AutoFilter" name="AutoFilter" class="large" title="Comma separated list of albums for the AutoPilot Filter"/><br />-->
<label for="AutoAlbumSize">Auto Album Size</label><br />
<input type="text" id="AutoAlbumSize" name="AutoAlbumSize" class="large" title="Number of Albums to Get on the Music Library tab" ng-model="settings.AutoAlbumSize" /><br />
<label for="AutoPlaylistSize">Auto Playlist Size</label><br />
<input type="text" id="AutoPlaylistSize" name="AutoPlaylistSize" class="large" title="Number of Songs to Get on the Playlist tab" ng-model="settings.AutoPlaylistSize" /><br />
<label for="ApplicationName">Application Name</label><br />
<input type="text" id="ApplicationName" name="ApplicationName" class="large" title="Custom Player Name" ng-model="settings.ApplicationName" /><br />
</div>
<div class="subsection floatleft checkboxes">
<div class="checkboxes">
<fieldset id="General">
<legend class="aligncenter">General</legend>
<div class="inputwrap"><input type="checkbox" id="AutoPlay" name="AutoPlay" value="1" title="When the Queue has ended, load random songs" ng-model="settings.AutoPlay" /></div>
<label for="AutoPlay">Auto Play</label>
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="LoopQueue" name="LoopQueue" value="1" title="When the Queue has ended, start from beginning" ng-model="settings.LoopQueue" /></div>
<label for="LoopQueue">Loop Queue</label>
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="HideAZ" name="HideAZ" value="1" title="Hide A-Z Artist Picker (Tablet/Touch friendly feature)" ng-model="settings.HideAZ" /></div>
<label for="HideAZ">Hide A-Z</label>
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="ScrollTitle" name="ScrollTitle" value="1" title="Scroll the Title Once" ng-model="settings.ScrollTitle" /></div>
<label for="ScrollTitle">Scroll Title</label>
<div class="clear"></div>
<label for="DefaultLibraryLayout">Default Library Layout</label>
<div class="clear"></div>
<select id="DefaultLibraryLayout" name="DefaultLibraryLayout" class="" ng-model="settings.DefaultLibraryLayout" ng-options="o.id as o.name for o in LibraryLayouts" title="AJAX Request Library Layout"></select>
<div class="clear"></div>
</fieldset>
<div class="clear"></div>
<fieldset id="HTML5">
<legend class="aligncenter">HTML5 [Beta]</legend>
<span>Notifications</span><br />
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="NotificationSong" name="NotificationSong" value="1" title="Enable Notifications When Tracks Change" ng-model="settings.NotificationSong" /></div>
<label for="NotificationSong">Song Change</label>
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="NotificationNowPlaying" name="NotificationNowPlaying" value="1" title="Enable Notifications When Other Users Play Songs" ng-model="settings.NotificationNowPlaying" /></div>
<label for="NotificationNowPlaying">Now Playing</label>
<div class="clear"></div>
<span>Local Storage</span><br />
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="SaveTrackPosition" name="SaveTrackPosition" value="1" title="Saves Play Queue & Track Position Periodically (Uses HTML5: localStorage)" ng-model="settings.SaveTrackPosition" /></div>
<label for="SaveTrackPosition">Save Progress</label>
</fieldset>
</div>
</div>
<div class="subsection floatleft">
<div class="checkboxes">
<fieldset id="Advanced">
<legend class="aligncenter">Advanced</legend>
<div class="inputwrap"><input type="checkbox" id="Debug" name="Debug" value="1" title="Enable Debug Mode (Events will be logged to the Javascript Console)" ng-model="settings.Debug" /></div>
<label for="Debug">Debug Mode</label>
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="ForceFlash" name="ForceFlash" value="1" title="Force Flash Plugin for Audio (Option doesn't work with Chrome App)" ng-model="settings.ForceFlash" /></div>
<label for="ForceFlash">Force Flash</label>
<div class="clear"></div>
<label for="Timeout">Timeout</label>
<div class="clear"></div>
<select id="Timeout" name="Timeout" class="" ng-model="settings.Timeout" ng-options="o.id as o.name for o in Timeouts" title="AJAX Request Timeout (Seconds)"></select>
<div class="clear"></div>
<label for="NotificationTimeout">Notification Timeout</label>
<div class="clear"></div>
<select id="NotificationTimeout" name="NotificationTimeout" class="" ng-model="settings.NotificationTimeout" ng-options="o.id as o.name for o in Timeouts" title="Notification Timeout (Seconds)"></select>
<div class="clear"></div>
<label for="Protocol">Protocol</label><br />
<select id="Protocol" name="Protocol" class="" ng-model="settings.Protocol" ng-options="o for o in Protocols" title="Enable Cross-Domain AJAX Requests (Use if hosted in a different domain than Subsonic)"></select>
<!--
<div class="clear"></div>
<label title="Export these settings to a file">Export Settings</label><br />
<ng-download data="settings" />
-->
</fieldset>
</div>
</div>
<div class="clear"></div>
<div class="submit">
<a href="" class="button" ng-click="save()" title="Save Settings">Save</a>
</div>
</form>
<div class="subsection floatleft"> <div class="subsection floatleft">
<h3 class="title">Tips</h3> <h3 class="title">Tips</h3>
<ul class="preferences"> <ul class="preferences">
@ -35,7 +137,7 @@
<h3 class="title">Change Log</h3> <h3 class="title">Change Log</h3>
<ul id="ChangeLog" class="preferences"> <ul id="ChangeLog" class="preferences">
<li class="log" ng-repeat="o in changeLog"><span class="version">{{o.date + ' - ' + o.version}}</span> <li class="log" ng-repeat="o in changeLog"><span class="version">{{o.date + ' - ' + o.version}}</span>
<span class="changes" ng-repeat="a in o.changes" ng-bind-html-unsafe="a.text"></span> <span class="changes" ng-repeat="a in o.changes" ng-bind-html="a.text"></span>
</li> </li>
</ul> </ul>
<a href="" ng-click="changeLogShowMore()">Show More</a> <a href="" ng-click="changeLogShowMore()">Show More</a>
@ -44,8 +146,9 @@
<div class="subsection floatleft"> <div class="subsection floatleft">
<h3 class="title">Links</h3> <h3 class="title">Links</h3>
<ul class="preferences"> <ul class="preferences">
<li>GitHub Repo - <a href="https://github.com/tsquillario/Jamstash" target="_blank">https://github.com/tsquillario/Jamstash</a></li> <li>Report Issues - <a href="https://github.com/tsquillario/Jamstash" target="_blank">https://github.com/tsquillario/Jamstash</a></li>
<li>Jamstash Chrome App - <a href="https://chrome.google.com/webstore/detail/jccdpflnecheidefpofmlblgebobbloc" target="_blank">Chrome Web Store</a></li> <li>Jamstash Chrome App - <a href="https://chrome.google.com/webstore/detail/jccdpflnecheidefpofmlblgebobbloc" target="_blank">Chrome Web Store</a></li>
<li>Beta Site - <a href="http://jamstash.com/beta" target="_blank">http://jamstash.com/beta</a></li>
<li><a href="https://twitter.com/JamstashApp" target="_blank">Follow @JamstashApp</a> <li><a href="https://twitter.com/JamstashApp" target="_blank">Follow @JamstashApp</a>
</ul> </ul>
<h3 class="title">Thanks</h3> <h3 class="title">Thanks</h3>

View file

@ -1,107 +0,0 @@
<div class="subsection floatleft">
<h3 class="title">Login</h3>
<label for="Username">Username <span class="red">*</span></label><br />
<input type="text" id="Username" name="Username" class="large" ng-model="settings.Username"/><br />
<label for="Password">Password <span class="red">*</span></label><br />
<input type="password" id="Password" name="Password" class="large" ng-model="settings.Password"/><br />
<label for="Server">Server <span class="red">*</span> (<a href="" title="Connect to Demo Subsonic Server" ng-click="setupDemo()">Demo</a>)</label><br />
<input type="text" id="Server" name="Server" class="xlarge" title="Subsonic Server URL Ex: http://host:port/subsonic" ng-model="settings.Server"/><br />
<!--<a href="#" class="button" id="action_RequestURL" title="Request Permission for Server URL">Enable URL</a><br />-->
<label for="SubsonicVersion">Subsonic API: <span class="apiversion" id="SubsonicVersion">{{settings.ApiVersion}}</span></label><br />
<label for="SMStats">Audio State: <span id="SMStats"></span></label><br /><br />
<br />
<a id="action_Welcome" href="#welcome">Launch Welcome</a>
<div id="welcome"><h2>Welcome</h2></div>
</div>
<div class="subsection floatleft">
<h3 class="title">Options</h3>
<label for="Theme">Theme</label><br />
<select id="Theme" name="Theme" class="large" ng-model="settings.Theme" ng-options="o for o in Themes"></select><br />
<!--<label for="AutoFilter">Filter</label><br />
<input type="text" id="AutoFilter" name="AutoFilter" class="large" title="Comma separated list of albums for the AutoPilot Filter"/><br />-->
<label for="AutoAlbumSize">Auto Album Size</label><br />
<input type="text" id="AutoAlbumSize" name="AutoAlbumSize" class="large" title="Number of Albums to Get on the Music Library tab" ng-model="settings.AutoAlbumSize"/><br />
<label for="AutoPlaylistSize">Auto Playlist Size</label><br />
<input type="text" id="AutoPlaylistSize" name="AutoPlaylistSize" class="large" title="Number of Songs to Get on the Playlist tab" ng-model="settings.AutoPlaylistSize"/><br />
<label for="ApplicationName">Application Name</label><br />
<input type="text" id="ApplicationName" name="ApplicationName" class="large" title="Custom Player Name" ng-model="settings.ApplicationName"/><br />
</div>
<div class="subsection floatleft checkboxes">
<div class="checkboxes">
<fieldset id="General">
<legend class="aligncenter">General</legend>
<div class="inputwrap"><input type="checkbox" id="AutoPlay" name="AutoPlay" value="1" title="When the Queue has ended, load random songs" ng-model="settings.AutoPlay"/></div>
<label for="AutoPlay">Auto Play</label>
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="LoopQueue" name="LoopQueue" value="1" title="When the Queue has ended, start from beginning" ng-model="settings.LoopQueue"/></div>
<label for="LoopQueue">Loop Queue</label>
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="HideAZ" name="HideAZ" value="1" title="Hide A-Z Artist Picker (Tablet/Touch friendly feature)" ng-model="settings.HideAZ"/></div>
<label for="HideAZ">Hide A-Z</label>
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="ScrollTitle" name="ScrollTitle" value="1" title="Scroll the Title Once" ng-model="settings.ScrollTitle"/></div>
<label for="ScrollTitle">Scroll Title</label>
<div class="clear"></div>
<label for="DefaultLibraryLayout">Default Library Layout</label>
<div class="clear"></div>
<select id="DefaultLibraryLayout" name="DefaultLibraryLayout" class="" ng-model="settings.DefaultLibraryLayout" ng-options="o.id as o.name for o in LibraryLayouts" title="AJAX Request Library Layout"></select>
<div class="clear"></div>
<label for="DefaultSubsonicAlbumSort">Default Album Sort Order</label>
<div class="clear"></div>
<select id="DefaultSubsonicAlbumSort" name="DefaultSubsonicAlbumSort" class="" ng-model="settings.DefaultSubsonicAlbumSort" ng-options="o.id as o.name for o in SubsonicAlbumSort" title="Subsonic Album Layout"></select>
<div class="clear"></div>
<label for="DefaultSearchType">Default Search Type</label>
<div class="clear"></div>
<select id="DefaultSearchType" name="DefaultSearchType" class="" ng-model="settings.DefaultSearchType" ng-options="o.id as o.name for o in SearchTypeLayout" title="Subsonic Album Layout"></select>
<div class="clear"></div>
</fieldset>
<div class="clear"></div>
<fieldset id="HTML5">
<legend class="aligncenter">HTML5 [Beta]</legend>
<span>Notifications</span><br />
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="NotificationSong" name="NotificationSong" value="1" title="Enable Notifications When Tracks Change" ng-model="settings.NotificationSong"/></div>
<label for="NotificationSong">Song Change</label>
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="NotificationNowPlaying" name="NotificationNowPlaying" value="1" title="Enable Notifications When Other Users Play Songs" ng-model="settings.NotificationNowPlaying"/></div>
<label for="NotificationNowPlaying">Now Playing</label>
<div class="clear"></div>
<span>Local Storage</span><br />
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="SaveTrackPosition" name="SaveTrackPosition" value="1" title="Saves Play Queue & Track Position Periodically (Uses HTML5: localStorage)" ng-model="settings.SaveTrackPosition"/></div>
<label for="SaveTrackPosition">Save Progress</label>
</fieldset>
</div>
</div>
<div class="subsection floatleft">
<div class="checkboxes">
<fieldset id="Advanced">
<legend class="aligncenter">Advanced</legend>
<div class="inputwrap"><input type="checkbox" id="Debug" name="Debug" value="1" title="Enable Debug Mode (Events will be logged to the Javascript Console)" ng-model="settings.Debug"/></div>
<label for="Debug">Debug Mode</label>
<div class="clear"></div>
<div class="inputwrap"><input type="checkbox" id="ForceFlash" name="ForceFlash" value="1" title="Force Flash Plugin for Audio (Option doesn't work with Chrome App)" ng-model="settings.ForceFlash"/></div>
<label for="ForceFlash">Force Flash</label>
<div class="clear"></div>
<label for="Timeout">Timeout</label>
<div class="clear"></div>
<select id="Timeout" name="Timeout" class="" ng-model="settings.Timeout" ng-options="o.id as o.name for o in Timeouts" title="AJAX Request Timeout (Seconds)"></select>
<div class="clear"></div>
<label for="NotificationTimeout">Notification Timeout</label>
<div class="clear"></div>
<select id="NotificationTimeout" name="NotificationTimeout" class="" ng-model="settings.NotificationTimeout" ng-options="o.id as o.name for o in Timeouts" title="Notification Timeout (Seconds)"></select>
<div class="clear"></div>
<label for="Protocol">Protocol</label><br />
<select id="Protocol" name="Protocol" class="" ng-model="settings.Protocol" ng-options="o for o in Protocols" title="Enable Cross-Domain AJAX Requests (Use if hosted in a different domain than Subsonic)"></select>
<!--
<div class="clear"></div>
<label title="Export these settings to a file">Export Settings</label><br />
<ng-download data="settings" />
-->
</fieldset>
</div>
</div>
<div class="clear"></div>
<div class="submit">
<a href="" class="button" ng-click="save()" title="Save Settings">Save</a>
</div>

View file

@ -1,19 +1,17 @@
<ul class="simplelist songlist"> <li class="row song" ng-repeat="o in song" ng-click="selectSong(o)" ng-dblclick="playSong(false, o)" ng-class="{'selected': o.selected, 'playing': o.playing}" data-bind="attr: { id: id, parentid: parentid, title: description }">
<li class="row song" ng-repeat="o in song" ng-click="selectSong(o)" ng-dblclick="playSong(false, o)" ng-class="{'selected': o.selected, 'playing': o.playing}" data-bind="attr: { id: id, parentid: parentid, title: description }"> <div class="itemactions">
<div class="itemactions"> <a class="add" href="" title="Add To Play Queue" ng-click="addSongToQueue(o)" stop-event="click"></a>
<a class="add" href="" title="Add To Play Queue" ng-click="addSongToQueue(o)" stop-event="click"></a> <a class="remove" href="" title="Remove"></a>
<a class="remove" href="" title="Remove"></a> <a class="play" href="" title="Play" ng-click="playSong(false, o)" stop-event="click"></a><a class="download" href="" title="Download"></a>
<a class="play" href="" title="Play" ng-click="playSong(false, o)" stop-event="click"></a><a class="download" href="" title="Download"></a> <a href="" title="Favorite" ng-class="{'favorite': o.starred, 'rate': !o.starred}" ng-click="updateFavorite(o)" stop-event="click"></a>
<a href="" title="Favorite" ng-class="{'favorite': o.starred, 'rate': !o.starred}" ng-click="updateFavorite(o)" stop-event="click"></a>
<div class="clear"></div>
</div>
<div class="track floatleft" ng-bind-html-unsafe="o.track"></div>
<div class="title floatleft" title="{{o.description}}" ng-bind-html-unsafe="o.name"></div>
<span class="albumblock floatleft" ng-show="o.album && itemType === 'ss'" ng-bind-html-unsafe="o.album" title="{{o.album}}"></span>
<a ng-href="#/library/0/{{o.albumId}}" class="albumblock floatleft" ng-show="o.album && itemType === 'pl'" stop-event="click" ng-bind-html-unsafe="o.album" title="{{o.album}}"></a>
<a ng-href="#/archive/{{o.artist}}/{{o.parentid}}" class="albumblock floatleft" ng-show="o.album && itemType === 'archive'" stop-event="click" ng-bind-html-unsafe="o.album" title="{{o.album}}"></a>
<div class="albumblock floatleft" ng-show="!o.album">&nbsp;</div>
<div class="time floatleft">{{o.time}}</div>
<div class="clear"></div> <div class="clear"></div>
</li> </div>
</ul> <div class="track floatleft" ng-bind-html="o.track"></div>
<div class="title floatleft" title="{{o.description}}" ng-bind-html="o.name"></div>
<span class="albumblock floatleft" ng-show="o.album && itemType === 'ss'" ng-bind-html="o.album" title="{{o.album}}"></span>
<a ng-href="#/library/0/{{o.albumId}}" class="albumblock floatleft" ng-show="o.album && itemType === 'pl'" stop-event="click" ng-bind-html="o.album" title="{{o.album}}"></a>
<a ng-href="#/archive/{{o.artist}}/{{o.parentid}}" class="albumblock floatleft" ng-show="o.album && itemType === 'archive'" stop-event="click" ng-bind-html="o.album" title="{{o.album}}"></a>
<div class="albumblock floatleft" ng-show="!o.album">&nbsp;</div>
<div class="time floatleft">{{o.time}}</div>
<div class="clear"></div>
</li>

View file

@ -8,7 +8,7 @@
<a href="" class="button" id="action_SelectAll" title="Select All" ng-click="selectAll()">All</a> <a href="" class="button" id="action_SelectAll" title="Select All" ng-click="selectAll()">All</a>
</div> </div>
<div class="clear"></div> <div class="clear"></div>
<div id="LayoutContainer" class="section lgsection"> <div id="left-component" class="section lgsection">
<!-- Artist --> <!-- Artist -->
<div class="ui-layout-west noselect hide" tabindex="0"> <div class="ui-layout-west noselect hide" tabindex="0">
<ul class="simplelist mainlist noselect"> <ul class="simplelist mainlist noselect">

View file

@ -116,7 +116,6 @@
//$rootScope.queue = []; //$rootScope.queue = [];
$rootScope.queue = items; $rootScope.queue = items;
if ($rootScope.queue.length > 0) { if ($rootScope.queue.length > 0) {
//$('body').layout().open('south');
notifications.updateMessage($rootScope.queue.length + ' Saved Song(s)', true); notifications.updateMessage($rootScope.queue.length + ' Saved Song(s)', true);
} }
if (globals.settings.Debug) { console.log('Play Queue Loaded From localStorage: ' + $rootScope.queue.length + ' song(s)'); } if (globals.settings.Debug) { console.log('Play Queue Loaded From localStorage: ' + $rootScope.queue.length + ' song(s)'); }
@ -173,8 +172,8 @@
} }
// End UnityShim // End UnityShim
} }
$('#Queue').stop().scrollTo('#' + id, 400); $('#QueuePreview').stop().scrollTo('#' + id, 400);
$('#QueuePreviewList').stop().scrollTo('#' + id, 400); $rootScope.showQueue();
var spechtml = ''; var spechtml = '';
var data = $(player1).data().jPlayer; var data = $(player1).data().jPlayer;
for (i = 0; i < data.solutions.length; i++) { for (i = 0; i < data.solutions.length; i++) {

7
js/plugins/angular-cookies.min.js vendored Normal file
View file

@ -0,0 +1,7 @@
/*
AngularJS v1.1.4
(c) 2010-2012 Google, Inc. http://angularjs.org
License: MIT
*/
(function(m,f,l){'use strict';f.module("ngCookies",["ng"]).factory("$cookies",["$rootScope","$browser",function(d,c){var b={},g={},h,i=!1,j=f.copy,k=f.isUndefined;c.addPollFn(function(){var a=c.cookies();h!=a&&(h=a,j(a,g),j(a,b),i&&d.$apply())})();i=!0;d.$watch(function(){var a,e,d;for(a in g)k(b[a])&&c.cookies(a,l);for(a in b)e=b[a],f.isString(e)?e!==g[a]&&(c.cookies(a,e),d=!0):f.isDefined(g[a])?b[a]=g[a]:delete b[a];if(d)for(a in e=c.cookies(),b)b[a]!==e[a]&&(k(e[a])?delete b[a]:b[a]=e[a])});return b}]).factory("$cookieStore",
["$cookies",function(d){return{get:function(c){return f.fromJson(d[c])},put:function(c,b){d[c]=f.toJson(b)},remove:function(c){delete d[c]}}}])})(window,window.angular);

13
js/plugins/angular-sanitize.min.js vendored Normal file
View file

@ -0,0 +1,13 @@
/*
AngularJS v1.1.4
(c) 2010-2012 Google, Inc. http://angularjs.org
License: MIT
*/
(function(I,h){'use strict';function i(a){var d={},a=a.split(","),c;for(c=0;c<a.length;c++)d[a[c]]=!0;return d}function z(a,d){function c(a,b,c,f){b=h.lowercase(b);if(m[b])for(;e.last()&&n[e.last()];)g("",e.last());o[b]&&e.last()==b&&g("",b);(f=p[b]||!!f)||e.push(b);var j={};c.replace(A,function(a,b,d,c,g){j[b]=k(d||c||g||"")});d.start&&d.start(b,j,f)}function g(a,b){var c=0,g;if(b=h.lowercase(b))for(c=e.length-1;c>=0;c--)if(e[c]==b)break;if(c>=0){for(g=e.length-1;g>=c;g--)d.end&&d.end(e[g]);e.length=
c}}var b,f,e=[],j=a;for(e.last=function(){return e[e.length-1]};a;){f=!0;if(!e.last()||!q[e.last()]){if(a.indexOf("<\!--")===0)b=a.indexOf("--\>"),b>=0&&(d.comment&&d.comment(a.substring(4,b)),a=a.substring(b+3),f=!1);else if(B.test(a)){if(b=a.match(r))a=a.substring(b[0].length),b[0].replace(r,g),f=!1}else if(C.test(a)&&(b=a.match(s)))a=a.substring(b[0].length),b[0].replace(s,c),f=!1;f&&(b=a.indexOf("<"),f=b<0?a:a.substring(0,b),a=b<0?"":a.substring(b),d.chars&&d.chars(k(f)))}else a=a.replace(RegExp("(.*)<\\s*\\/\\s*"+
e.last()+"[^>]*>","i"),function(a,b){b=b.replace(D,"$1").replace(E,"$1");d.chars&&d.chars(k(b));return""}),g("",e.last());if(a==j)throw"Parse Error: "+a;j=a}g()}function k(a){l.innerHTML=a.replace(/</g,"&lt;");return l.innerText||l.textContent||""}function t(a){return a.replace(/&/g,"&amp;").replace(F,function(a){return"&#"+a.charCodeAt(0)+";"}).replace(/</g,"&lt;").replace(/>/g,"&gt;")}function u(a){var d=!1,c=h.bind(a,a.push);return{start:function(a,b,f){a=h.lowercase(a);!d&&q[a]&&(d=a);!d&&v[a]==
!0&&(c("<"),c(a),h.forEach(b,function(a,b){var d=h.lowercase(b);if(G[d]==!0&&(w[d]!==!0||a.match(H)))c(" "),c(b),c('="'),c(t(a)),c('"')}),c(f?"/>":">"))},end:function(a){a=h.lowercase(a);!d&&v[a]==!0&&(c("</"),c(a),c(">"));a==d&&(d=!1)},chars:function(a){d||c(t(a))}}}var s=/^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*>/,r=/^<\s*\/\s*([\w:-]+)[^>]*>/,A=/([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,C=/^</,B=/^<\s*\//,D=/<\!--(.*?)--\>/g,
E=/<!\[CDATA\[(.*?)]]\>/g,H=/^((ftp|https?):\/\/|mailto:|tel:|#)/,F=/([^\#-~| |!])/g,p=i("area,br,col,hr,img,wbr"),x=i("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),y=i("rp,rt"),o=h.extend({},y,x),m=h.extend({},x,i("address,article,aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5,h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul")),n=h.extend({},y,i("a,abbr,acronym,b,bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s,samp,small,span,strike,strong,sub,sup,time,tt,u,var")),
q=i("script,style"),v=h.extend({},p,m,n,o),w=i("background,cite,href,longdesc,src,usemap"),G=h.extend({},w,i("abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,scope,scrolling,shape,span,start,summary,target,title,type,valign,value,vspace,width")),l=document.createElement("pre");h.module("ngSanitize",[]).value("$sanitize",function(a){var d=[];
z(a,u(d));return d.join("")});h.module("ngSanitize").directive("ngBindHtml",["$sanitize",function(a){return function(d,c,g){c.addClass("ng-binding").data("$binding",g.ngBindHtml);d.$watch(g.ngBindHtml,function(b){b=a(b);c.html(b||"")})}}]);h.module("ngSanitize").filter("linky",function(){var a=/((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,d=/^mailto:/;return function(c,g){if(!c)return c;var b,f=c,e=[],j=u(e),i,k,l={};if(h.isDefined(g))l.target=g;for(;b=f.match(a);)i=
b[0],b[2]==b[3]&&(i="mailto:"+i),k=b.index,j.chars(f.substr(0,k)),l.href=i,j.start("a",l),j.chars(b[0].replace(d,"")),j.end("a"),f=f.substring(k+b[0].length);j.chars(f);return e.join("")}})})(window,window.angular);

6
js/plugins/jquery-2.0.3.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,99 @@
/*!
Split Pane v0.3
Copyright (c) 2012 Simon Hagström
Released under the MIT license
https://raw.github.com/shagstrom/split-pane/master/LICENSE
*/
.split-pane {
position: absolute;
width: 100%;
overflow: hidden;
}
.split-pane.fixed-top > .split-pane-component,
.split-pane.fixed-bottom > .split-pane-component,
.split-pane.horizontal-percent > .split-pane-component {
position: absolute;
left: 0;
width: 100%;
overflow: auto;
top: auto;
bottom: 0;
z-index: 1;
}
.split-pane.fixed-top > .split-pane-component:first-child,
.split-pane.fixed-bottom > .split-pane-component:first-child,
.split-pane.horizontal-percent > .split-pane-component:first-child {
top: 0;
bottom: auto;
}
.split-pane.fixed-top > .split-pane-divider,
.split-pane.fixed-bottom > .split-pane-divider,
.split-pane.horizontal-percent > .split-pane-divider {
position: absolute;
width: 100%;
left: 0;
cursor: ns-resize;
cursor: n-resize\9;
z-index: 2;
}
.split-pane.fixed-left > .split-pane-component,
.split-pane.fixed-right > .split-pane-component,
.split-pane.vertical-percent > .split-pane-component {
position: absolute;
top: 0;
height: 100%;
overflow: auto;
left: auto;
right: 0;
z-index: 1;
}
.split-pane.fixed-left > .split-pane-component:first-child,
.split-pane.fixed-right > .split-pane-component:first-child,
.split-pane.vertical-percent > .split-pane-component:first-child {
left: 0;
right: auto;
}
.split-pane.fixed-left > .split-pane-divider,
.split-pane.fixed-right > .split-pane-divider,
.split-pane.vertical-percent > .split-pane-divider {
position: absolute;
height: 100%;
top: 0;
cursor: ew-resize;
cursor: w-resize\9;
z-index: 2;
}
.split-pane-resize-shim {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10000;
display: none;
}
.split-pane.fixed-left > .split-pane-resize-shim,
.split-pane.fixed-right > .split-pane-resize-shim,
.split-pane.vertical-percent > .split-pane-resize-shim {
cursor: ew-resize;
cursor: w-resize\9;
}
.split-pane.fixed-top > .split-pane-resize-shim,
.split-pane.fixed-bottom > .split-pane-resize-shim,
.split-pane.horizontal-percent > .split-pane-resize-shim {
cursor: ns-resize;
cursor: n-resize\9;
}

236
js/plugins/jquery-split-pane.js vendored Normal file
View file

@ -0,0 +1,236 @@
/*!
Split Pane v0.3
Copyright (c) 2012 Simon Hagström
Released under the MIT license
https://raw.github.com/shagstrom/split-pane/master/LICENSE
*/
(function($) {
$.fn.splitPane = function() {
var $splitPanes = this;
$splitPanes.each(setMinHeightAndMinWidth);
$splitPanes.append('<div class="split-pane-resize-shim">');
$splitPanes.children('.split-pane-divider').bind('mousedown', mousedownHandler);
setTimeout(function() {
// Doing this later because of an issue with Chrome (v23.0.1271.64) returning split-pane width = 0
// and triggering multiple resize events when page is being opened from an <a target="_blank"> .
$splitPanes.bind('_splitpaneparentresize', parentresizeHandler);
$(window).trigger('resize');
}, 100);
};
var SPLITPANERESIZE_HANDLER = '_splitpaneparentresizeHandler';
/**
* A special event that will "capture" a resize event from the parent split-pane or window.
* The event will NOT propagate to grandchildren.
*/
jQuery.event.special._splitpaneparentresize = {
setup: function(data, namespaces) {
var element = this,
parent = $(this).parent().closest('.split-pane')[0] || window;
$(this).data(SPLITPANERESIZE_HANDLER, function(event) {
var target = event.target === document ? window : event.target;
if (target === parent) {
event.type = "_splitpaneparentresize";
jQuery.event.dispatch.apply(element, arguments);
} else {
event.stopPropagation();
}
});
$(parent).bind('resize', $(this).data(SPLITPANERESIZE_HANDLER));
},
teardown: function(namespaces) {
var parent = $(this).parent().closest('.split-pane')[0] || window;
$(parent).unbind('resize', $(this).data(SPLITPANERESIZE_HANDLER));
$(this).removeData(SPLITPANERESIZE_HANDLER);
}
};
function setMinHeightAndMinWidth() {
var $splitPane = $(this),
$firstComponent = $splitPane.children('.split-pane-component:first'),
$divider = $splitPane.children('.split-pane-divider'),
$lastComponent = $splitPane.children('.split-pane-component:last');
if ($splitPane.is('.fixed-top, .fixed-bottom, .horizontal-percent')) {
$splitPane.css('min-height', (minHeight($firstComponent) + minHeight($lastComponent) + $divider.height()) + 'px');
} else {
$splitPane.css('min-width', (minWidth($firstComponent) + minWidth($lastComponent) + $divider.width()) + 'px');
}
}
function mousedownHandler(event) {
event.preventDefault();
var $resizeShim = $(this).siblings('.split-pane-resize-shim').show(),
mousemove = createMousemove($(this).parent(), event.pageX, event.pageY);
$(document).mousemove(mousemove);
$(document).one('mouseup', function(event) {
$(document).unbind('mousemove', mousemove);
$resizeShim.hide();
});
}
function parentresizeHandler() {
var $splitPane = $(this),
$firstComponent = $splitPane.children('.split-pane-component:first'),
$divider = $splitPane.children('.split-pane-divider'),
$lastComponent = $splitPane.children('.split-pane-component:last');
if ($splitPane.is('.fixed-top')) {
var maxfirstComponentHeight = $splitPane.height() - minHeight($lastComponent) - $divider.height();
if ($firstComponent.height() > maxfirstComponentHeight) {
setTop($splitPane, $firstComponent, $divider, $lastComponent, maxfirstComponentHeight + 'px');
} else {
$splitPane.resize();
}
} else if ($splitPane.is('.fixed-bottom')) {
var maxLastComponentHeight = $splitPane.height() - minHeight($firstComponent) - $divider.height();
if ($lastComponent.height() > maxLastComponentHeight) {
setBottom($splitPane, $firstComponent, $divider, $lastComponent, maxLastComponentHeight + 'px')
} else {
$splitPane.resize();
}
} else if ($splitPane.is('.horizontal-percent')) {
var maxLastComponentHeight = $splitPane.height() - minHeight($firstComponent) - $divider.height();
if ($lastComponent.height() > maxLastComponentHeight) {
setBottom($splitPane, $firstComponent, $divider, $lastComponent, (maxLastComponentHeight / $splitPane.height() * 100) + '%');
} else {
var lastComponentMinHeight = minHeight($lastComponent);
if ($splitPane.height() - $firstComponent.height() - $divider.height() < lastComponentMinHeight) {
setBottom($splitPane, $firstComponent, $divider, $lastComponent, (lastComponentMinHeight / $splitPane.height() * 100) + '%');
} else {
$splitPane.resize();
}
}
} else if ($splitPane.is('.fixed-left')) {
var maxFirstComponentWidth = $splitPane.width() - minWidth($lastComponent) - $divider.width();
if ($firstComponent.width() > maxFirstComponentWidth) {
setLeft($splitPane, $firstComponent, $divider, $lastComponent, maxFirstComponentWidth + 'px');
} else {
$splitPane.resize();
}
} else if ($splitPane.is('.fixed-right')) {
var maxLastComponentWidth = $splitPane.width() - minWidth($firstComponent) - $divider.width();
if ($lastComponent.width() > maxLastComponentWidth) {
setRight($splitPane, $firstComponent, $divider, $lastComponent, maxLastComponentWidth + 'px')
} else {
$splitPane.resize();
}
} else if ($splitPane.is('.vertical-percent')) {
var maxLastComponentWidth = $splitPane.width() - minWidth($firstComponent) - $divider.width();
if ($lastComponent.width() > maxLastComponentWidth) {
setRight($splitPane, $firstComponent, $divider, $lastComponent, (maxLastComponentWidth / $splitPane.width() * 100) + '%');
} else {
var lastComponentMinWidth = minWidth($lastComponent);
if ($splitPane.width() - $firstComponent.width() - $divider.width() < lastComponentMinWidth) {
setRight($splitPane, $firstComponent, $divider, $lastComponent, (lastComponentMinWidth / $splitPane.width() * 100) + '%');
} else {
$splitPane.resize();
}
}
}
}
function createMousemove($splitPane, pageX, pageY) {
var $firstComponent = $splitPane.children('.split-pane-component:first'),
$divider = $splitPane.children('.split-pane-divider'),
$lastComponent = $splitPane.children('.split-pane-component:last');
if ($splitPane.is('.fixed-top')) {
var firstComponentMinHeight = minHeight($firstComponent),
maxFirstComponentHeight = $splitPane.height() - minHeight($lastComponent) - $divider.height(),
topOffset = $divider.position().top - pageY;
return function(event) {
event.preventDefault();
var top = Math.min(Math.max(firstComponentMinHeight, topOffset + event.pageY), maxFirstComponentHeight);
setTop($splitPane, $firstComponent, $divider, $lastComponent, top + 'px')
};
} else if ($splitPane.is('.fixed-bottom')) {
var lastComponentMinHeight = minHeight($lastComponent),
maxLastComponentHeight = $splitPane.height() - minHeight($firstComponent) - $divider.height(),
bottomOffset = $lastComponent.height() + pageY;
return function(event) {
event.preventDefault();
var bottom = Math.min(Math.max(lastComponentMinHeight, bottomOffset - event.pageY), maxLastComponentHeight);
setBottom($splitPane, $firstComponent, $divider, $lastComponent, bottom + 'px');
};
} else if ($splitPane.is('.horizontal-percent')) {
var splitPaneHeight = $splitPane.height(),
lastComponentMinHeight = minHeight($lastComponent),
maxLastComponentHeight = splitPaneHeight - minHeight($firstComponent) - $divider.height(),
bottomOffset = $lastComponent.height() + pageY;
return function(event) {
event.preventDefault();
var bottom = Math.min(Math.max(lastComponentMinHeight, bottomOffset - event.pageY), maxLastComponentHeight);
setBottom($splitPane, $firstComponent, $divider, $lastComponent, (bottom / splitPaneHeight * 100) + '%');
};
} else if ($splitPane.is('.fixed-left')) {
var firstComponentMinWidth = minWidth($firstComponent),
maxFirstComponentWidth = $splitPane.width() - minWidth($lastComponent) - $divider.width(),
leftOffset = $divider.position().left - pageX;
return function(event) {
event.preventDefault();
var left = Math.min(Math.max(firstComponentMinWidth, leftOffset + event.pageX), maxFirstComponentWidth);
setLeft($splitPane, $firstComponent, $divider, $lastComponent, left + 'px')
};
} else if ($splitPane.is('.fixed-right')) {
var lastComponentMinWidth = minWidth($lastComponent),
maxLastComponentWidth = $splitPane.width() - minWidth($firstComponent) - $divider.width(),
rightOffset = $lastComponent.width() + pageX;
return function(event) {
event.preventDefault();
var right = Math.min(Math.max(lastComponentMinWidth, rightOffset - event.pageX), maxLastComponentWidth);
setRight($splitPane, $firstComponent, $divider, $lastComponent, right + 'px');
};
} else if ($splitPane.is('.vertical-percent')) {
var splitPaneWidth = $splitPane.width(),
lastComponentMinWidth = minWidth($lastComponent),
maxLastComponentWidth = splitPaneWidth - minWidth($firstComponent) - $divider.width(),
rightOffset = $lastComponent.width() + pageX;
return function(event) {
event.preventDefault();
var right = Math.min(Math.max(lastComponentMinWidth, rightOffset - event.pageX), maxLastComponentWidth);
setRight($splitPane, $firstComponent, $divider, $lastComponent, (right / splitPaneWidth * 100) + '%');
};
}
}
function minHeight($element) {
return parseInt($element.css('min-height')) || 0;
}
function minWidth($element) {
return parseInt($element.css('min-width')) || 0;
}
function setTop($splitPane, $firstComponent, $divider, $lastComponent, top) {
$firstComponent.css('height', top);
$divider.css('top', top);
$lastComponent.css('top', top);
$splitPane.resize();
}
function setBottom($splitPane, $firstComponent, $divider, $lastComponent, bottom) {
$firstComponent.css('bottom', bottom);
$divider.css('bottom', bottom);
$lastComponent.css('height', bottom);
$splitPane.resize();
}
function setLeft($splitPane, $firstComponent, $divider, $lastComponent, left) {
$firstComponent.css('width', left);
$divider.css('left', left);
$lastComponent.css('left', left);
$splitPane.resize();
}
function setRight($splitPane, $firstComponent, $divider, $lastComponent, right) {
$firstComponent.css('right', right);
$divider.css('right', right);
$lastComponent.css('width', right);
$splitPane.resize();
}
})(jQuery);

File diff suppressed because one or more lines are too long

View file

@ -106,18 +106,18 @@ l=r[e],u=q[e],v=u.content;if(!m||!p||!m.is(":visible"))return!0;if(!p.length&&(S
visibility:"visible"});0<x&&0<l.innerWidth()?l.data("autoHidden")&&(l.show().data("autoHidden",!1),G.mozilla||l.css(k.hidden).css(k.visible)):l.data("autoHidden")||l.hide().data("autoHidden",!0);v.height=m}q.initialized&&C("onsizecontent_end",e)}})}},qa=function(a){a=(a=A.call(this,a))?a.split(","):k.borderPanes;b.each(a,function(a,d){var e=r[d],g=q[d],h=y[d],j=F[d],p=P[d],u;if(h&&j){var l=k[d].dir,x=g.isClosed?"_closed":"_open",B=e["spacing"+x],z=e["togglerAlign"+x],x=e["togglerLength"+x],A;if(0=== visibility:"visible"});0<x&&0<l.innerWidth()?l.data("autoHidden")&&(l.show().data("autoHidden",!1),G.mozilla||l.css(k.hidden).css(k.visible)):l.data("autoHidden")||l.hide().data("autoHidden",!0);v.height=m}q.initialized&&C("onsizecontent_end",e)}})}},qa=function(a){a=(a=A.call(this,a))?a.split(","):k.borderPanes;b.each(a,function(a,d){var e=r[d],g=q[d],h=y[d],j=F[d],p=P[d],u;if(h&&j){var l=k[d].dir,x=g.isClosed?"_closed":"_open",B=e["spacing"+x],z=e["togglerAlign"+x],x=e["togglerLength"+x],A;if(0===
B)j.hide();else{!g.noRoom&&!g.isHidden&&j.show();"horz"===l?(A=v.innerWidth,g.resizerLength=A,h=b.layout.cssNum(h,"left"),j.css({width:Q(j,A),height:O(j,B),left:-9999<h?h:v.inset.left})):(A=h.outerHeight(),g.resizerLength=A,j.css({height:O(j,A),width:Q(j,B),top:v.inset.top+ga("north",!0)}));da(e,j);if(p){if(0===x||g.isSliding&&e.hideTogglerOnSlide){p.hide();return}p.show();if(!(0<x)||"100%"===x||x>A)x=A,z=0;else if(f(z))switch(z){case "top":case "left":z=0;break;case "bottom":case "right":z=A-x;break; B)j.hide();else{!g.noRoom&&!g.isHidden&&j.show();"horz"===l?(A=v.innerWidth,g.resizerLength=A,h=b.layout.cssNum(h,"left"),j.css({width:Q(j,A),height:O(j,B),left:-9999<h?h:v.inset.left})):(A=h.outerHeight(),g.resizerLength=A,j.css({height:O(j,A),width:Q(j,B),top:v.inset.top+ga("north",!0)}));da(e,j);if(p){if(0===x||g.isSliding&&e.hideTogglerOnSlide){p.hide();return}p.show();if(!(0<x)||"100%"===x||x>A)x=A,z=0;else if(f(z))switch(z){case "top":case "left":z=0;break;case "bottom":case "right":z=A-x;break;
default:z=c((A-x)/2)}else h=parseInt(z,10),z=0<=z?h:A-x+h;if("horz"===l){var D=Q(p,x);p.css({width:D,height:O(p,B),left:z,top:0});p.children(".content").each(function(){u=b(this);u.css("marginLeft",c((D-u.outerWidth())/2))})}else{var C=O(p,x);p.css({height:C,width:Q(p,B),top:z,left:0});p.children(".content").each(function(){u=b(this);u.css("marginTop",c((C-u.outerHeight())/2))})}da(0,p)}if(!q.initialized&&(e.initHidden||g.isHidden))j.hide(),p&&p.hide()}}})},pb=function(a){if(H()){var b=A.call(this, default:z=c((A-x)/2)}else h=parseInt(z,10),z=0<=z?h:A-x+h;if("horz"===l){var D=Q(p,x);p.css({width:D,height:O(p,B),left:z,top:0});p.children(".content").each(function(){u=b(this);u.css("marginLeft",c((D-u.outerWidth())/2))})}else{var C=O(p,x);p.css({height:C,width:Q(p,B),top:z,left:0});p.children(".content").each(function(){u=b(this);u.css("marginTop",c((C-u.outerHeight())/2))})}da(0,p)}if(!q.initialized&&(e.initHidden||g.isHidden))j.hide(),p&&p.hide()}}})},pb=function(a){if(H()){var b=A.call(this,
a);a=P[b];var d=r[b];a&&(d.closable=!0,a.bind("click."+K,function(a){a.stopPropagation();na(b)}).css("visibility","visible").css("cursor","pointer").attr("title",q[b].isClosed?d.tips.Open:d.tips.Close).show())}},Va=function(a,d){b.layout.plugins.buttons&&b.each(q[a].pins,function(c,f){b.layout.buttons.setPinState(z,b(f),a,d)})},u=b(this).eq(0);if(!u.length)return ca(r.errors.containerMissing);if(u.data("layoutContainer")&&u.data("layout"))return u.data("layout");var y={},U={},F={},P={},ea=b([]),v= a);a=P[b];var d=r[b];a&&(d.closable=!0,a.bind("click."+K,function(a){a.stopPropagation();na(b)}).css("visibility","visible").css("cursor","pointer").attr("title",q[b].isClosed?d.tips.Open:d.tips.Close).show())}},Va=function(a,d){b.layout.plugins.buttons&&b.each(q[a].pins,function(c,f){b.layout.buttons.setPinState(z,b(f),a,d)})},u=b(this).eq(0);if(!u.length)return ca(r.errors.containerMissing);if(u.data("left-component")&&u.data("layout"))return u.data("layout");var y={},U={},F={},P={},ea=b([]),v=
q.container,K=q.id,z={options:r,state:q,container:u,panes:y,contents:U,resizers:F,togglers:P,hide:Ta,show:Fa,toggle:na,open:ra,close:ja,slideOpen:sb,slideClose:Wa,slideToggle:function(a){a=A.call(this,a);na(a,!0)},setSizeLimits:Y,_sizePane:ka,sizePane:Ca,sizeContent:pa,swapPanes:function(a,c){function f(a){var d=y[a],c=U[a];return!d?!1:{pane:a,P:d?d[0]:!1,C:c?c[0]:!1,state:b.extend(!0,{},q[a]),options:b.extend(!0,{},r[a])}}function h(a,c){if(a){var e=a.P,f=a.C,g=a.pane,j=k[c],m=b.extend(!0,{},q[c]), q.container,K=q.id,z={options:r,state:q,container:u,panes:y,contents:U,resizers:F,togglers:P,hide:Ta,show:Fa,toggle:na,open:ra,close:ja,slideOpen:sb,slideClose:Wa,slideToggle:function(a){a=A.call(this,a);na(a,!0)},setSizeLimits:Y,_sizePane:ka,sizePane:Ca,sizeContent:pa,swapPanes:function(a,c){function f(a){var d=y[a],c=U[a];return!d?!1:{pane:a,P:d?d[0]:!1,C:c?c[0]:!1,state:b.extend(!0,{},q[a]),options:b.extend(!0,{},r[a])}}function h(a,c){if(a){var e=a.P,f=a.C,g=a.pane,j=k[c],m=b.extend(!0,{},q[c]),
n=r[c],w={resizerCursor:n.resizerCursor};b.each(["fxName","fxSpeed","fxSettings"],function(a,b){w[b+"_open"]=n[b+"_open"];w[b+"_close"]=n[b+"_close"];w[b+"_size"]=n[b+"_size"]});y[c]=b(e).data({layoutPane:z[c],layoutEdge:c}).css(k.hidden).css(j.cssReq);U[c]=f?b(f):!1;r[c]=b.extend(!0,{},a.options,w);q[c]=b.extend(!0,{},a.state);e.className=e.className.replace(RegExp(n.paneClass+"-"+g,"g"),n.paneClass+"-"+c);Pa(c);j.dir!=k[g].dir?(e=p[c]||0,Y(c),e=d(e,q[c].minSize),Ca(c,e,!0,!0)):F[c].css(j.side,v.inset[j.side]+ n=r[c],w={resizerCursor:n.resizerCursor};b.each(["fxName","fxSpeed","fxSettings"],function(a,b){w[b+"_open"]=n[b+"_open"];w[b+"_close"]=n[b+"_close"];w[b+"_size"]=n[b+"_size"]});y[c]=b(e).data({layoutPane:z[c],layoutEdge:c}).css(k.hidden).css(j.cssReq);U[c]=f?b(f):!1;r[c]=b.extend(!0,{},a.options,w);q[c]=b.extend(!0,{},a.state);e.className=e.className.replace(RegExp(n.paneClass+"-"+g,"g"),n.paneClass+"-"+c);Pa(c);j.dir!=k[g].dir?(e=p[c]||0,Y(c),e=d(e,q[c].minSize),Ca(c,e,!0,!0)):F[c].css(j.side,v.inset[j.side]+
(q[c].isVisible?ga(c):0));a.state.isVisible&&!m.isVisible?Ua(c,!0):(Da(c),ma(c,!0));a=null}}if(H()){var g=A.call(this,a);q[g].edge=c;q[c].edge=g;if(!1===C("onswap_start",g)||!1===C("onswap_start",c))q[g].edge=g,q[c].edge=c;else{var j=f(g),n=f(c),p={};p[g]=j?j.state.size:0;p[c]=n?n.state.size:0;y[g]=!1;y[c]=!1;q[g]={};q[c]={};P[g]&&P[g].remove();P[c]&&P[c].remove();F[g]&&F[g].remove();F[c]&&F[c].remove();F[g]=F[c]=P[g]=P[c]=!1;h(j,c);h(n,g);j=n=p=null;y[g]&&y[g].css(k.visible);y[c]&&y[c].css(k.visible); (q[c].isVisible?ga(c):0));a.state.isVisible&&!m.isVisible?Ua(c,!0):(Da(c),ma(c,!0));a=null}}if(H()){var g=A.call(this,a);q[g].edge=c;q[c].edge=g;if(!1===C("onswap_start",g)||!1===C("onswap_start",c))q[g].edge=g,q[c].edge=c;else{var j=f(g),n=f(c),p={};p[g]=j?j.state.size:0;p[c]=n?n.state.size:0;y[g]=!1;y[c]=!1;q[g]={};q[c]={};P[g]&&P[g].remove();P[c]&&P[c].remove();F[g]&&F[g].remove();F[c]&&F[c].remove();F[g]=F[c]=P[g]=P[c]=!1;h(j,c);h(n,g);j=n=p=null;y[g]&&y[g].css(k.visible);y[c]&&y[c].css(k.visible);
oa();C("onswap_end",g);C("onswap_end",c)}}},showMasks:va,hideMasks:za,initContent:Sa,addPane:ib,removePane:Ra,createChildren:Qa,refreshChildren:Ba,enableClosable:pb,disableClosable:function(a,b){if(H()){var c=A.call(this,a),d=P[c];d&&(r[c].closable=!1,q[c].isClosed&&ra(c,!1,!0),d.unbind("."+K).css("visibility",b?"hidden":"visible").css("cursor","default").attr("title",""))}},enableSlidable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&b.data("draggable")&&(r[a].slidable=!0,q[a].isClosed&&ma(a, oa();C("onswap_end",g);C("onswap_end",c)}}},showMasks:va,hideMasks:za,initContent:Sa,addPane:ib,removePane:Ra,createChildren:Qa,refreshChildren:Ba,enableClosable:pb,disableClosable:function(a,b){if(H()){var c=A.call(this,a),d=P[c];d&&(r[c].closable=!1,q[c].isClosed&&ra(c,!1,!0),d.unbind("."+K).css("visibility",b?"hidden":"visible").css("cursor","default").attr("title",""))}},enableSlidable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&b.data("draggable")&&(r[a].slidable=!0,q[a].isClosed&&ma(a,
!0))}},disableSlidable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&(r[a].slidable=!1,q[a].isSliding?ja(a,!1,!0):(ma(a,!1),b.css("cursor","default").attr("title",""),da(null,b[0])))}},enableResizable:function(a){if(H()){a=A.call(this,a);var b=F[a],c=r[a];b&&b.data("draggable")&&(c.resizable=!0,b.draggable("enable"),q[a].isClosed||b.css("cursor",c.resizerCursor).attr("title",c.tips.Resize))}},disableResizable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&b.data("draggable")&&(r[a].resizable= !0))}},disableSlidable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&(r[a].slidable=!1,q[a].isSliding?ja(a,!1,!0):(ma(a,!1),b.css("cursor","default").attr("title",""),da(null,b[0])))}},enableResizable:function(a){if(H()){a=A.call(this,a);var b=F[a],c=r[a];b&&b.data("draggable")&&(c.resizable=!0,b.draggable("enable"),q[a].isClosed||b.css("cursor",c.resizerCursor).attr("title",c.tips.Resize))}},disableResizable:function(a){if(H()){a=A.call(this,a);var b=F[a];b&&b.data("draggable")&&(r[a].resizable=
!1,b.draggable("disable").css("cursor","default").attr("title",""),da(null,b[0]))}},allowOverflow:x,resetOverflow:X,destroy:function(a,c){b(window).unbind("."+K);b(document).unbind("."+K);"object"===typeof a?A(a):c=a;u.clearQueue().removeData("layout").removeData("layoutContainer").removeClass(r.containerClass).unbind("."+K);ea.remove();b.each(k.allPanes,function(a,b){Ra(b,!1,!0,c)});u.data("layoutCSS")&&!u.data("layoutRole")&&u.css(u.data("layoutCSS")).removeData("layoutCSS");"BODY"===v.tagName&& !1,b.draggable("disable").css("cursor","default").attr("title",""),da(null,b[0]))}},allowOverflow:x,resetOverflow:X,destroy:function(a,c){b(window).unbind("."+K);b(document).unbind("."+K);"object"===typeof a?A(a):c=a;u.clearQueue().removeData("layout").removeData("left-component").removeClass(r.containerClass).unbind("."+K);ea.remove();b.each(k.allPanes,function(a,b){Ra(b,!1,!0,c)});u.data("layoutCSS")&&!u.data("layoutRole")&&u.css(u.data("layoutCSS")).removeData("layoutCSS");"BODY"===v.tagName&&
(u=b("html")).data("layoutCSS")&&u.css(u.data("layoutCSS")).removeData("layoutCSS");j(z,b.layout.onDestroy);mb();for(var d in z)d.match(/^(container|options)$/)||delete z[d];z.destroyed=!0;return z},initPanes:H,resizeAll:oa,runCallbacks:C,hasParentLayout:!1,children:ba,north:!1,south:!1,west:!1,east:!1,center:!1},Xa;var V,Ya,N,Ha,la,sa,W;h=b.layout.transformData(h,!0);h=b.layout.backwardCompatibility.renameAllOptions(h);if(!b.isEmptyObject(h.panes)){V=b.layout.optionsMap.noDefault;la=0;for(sa=V.length;la< (u=b("html")).data("layoutCSS")&&u.css(u.data("layoutCSS")).removeData("layoutCSS");j(z,b.layout.onDestroy);mb();for(var d in z)d.match(/^(container|options)$/)||delete z[d];z.destroyed=!0;return z},initPanes:H,resizeAll:oa,runCallbacks:C,hasParentLayout:!1,children:ba,north:!1,south:!1,west:!1,east:!1,center:!1},Xa;var V,Ya,N,Ha,la,sa,W;h=b.layout.transformData(h,!0);h=b.layout.backwardCompatibility.renameAllOptions(h);if(!b.isEmptyObject(h.panes)){V=b.layout.optionsMap.noDefault;la=0;for(sa=V.length;la<
sa;la++)N=V[la],delete h.panes[N];V=b.layout.optionsMap.layout;la=0;for(sa=V.length;la<sa;la++)N=V[la],delete h.panes[N]}V=b.layout.optionsMap.layout;var Bb=b.layout.config.optionRootKeys;for(N in h)Ha=h[N],0>b.inArray(N,Bb)&&0>b.inArray(N,V)&&(h.panes[N]||(h.panes[N]=b.isPlainObject(Ha)?b.extend(!0,{},Ha):Ha),delete h[N]);b.extend(!0,r,h);b.each(k.allPanes,function(a,c){k[c]=b.extend(!0,{},k.panes,k[c]);Ya=r.panes;W=r[c];if("center"===c){V=b.layout.optionsMap.center;a=0;for(sa=V.length;a<sa;a++)if(N= sa;la++)N=V[la],delete h.panes[N];V=b.layout.optionsMap.layout;la=0;for(sa=V.length;la<sa;la++)N=V[la],delete h.panes[N]}V=b.layout.optionsMap.layout;var Bb=b.layout.config.optionRootKeys;for(N in h)Ha=h[N],0>b.inArray(N,Bb)&&0>b.inArray(N,V)&&(h.panes[N]||(h.panes[N]=b.isPlainObject(Ha)?b.extend(!0,{},Ha):Ha),delete h[N]);b.extend(!0,r,h);b.each(k.allPanes,function(a,c){k[c]=b.extend(!0,{},k.panes,k[c]);Ya=r.panes;W=r[c];if("center"===c){V=b.layout.optionsMap.center;a=0;for(sa=V.length;a<sa;a++)if(N=
V[a],!h.center[N]&&(h.panes[N]||!W[N]))W[N]=Ya[N]}else{W=r[c]=b.extend(!0,{},Ya,W);var d=r[c],f=r.panes;d.fxSettings||(d.fxSettings={});f.fxSettings||(f.fxSettings={});b.each(["_open","_close","_size"],function(a,e){var h="fxName"+e,j="fxSpeed"+e,k="fxSettings"+e,l=d[h]=d[h]||f[h]||d.fxName||f.fxName||"none",p=b.effects&&(b.effects[l]||b.effects.effect&&b.effects.effect[l]);if("none"===l||!r.effects[l]||!p)l=d[h]="none";l=r.effects[l]||{};h=l.all||null;l=l[c]||null;d[j]=d[j]||f[j]||d.fxSpeed||f.fxSpeed|| V[a],!h.center[N]&&(h.panes[N]||!W[N]))W[N]=Ya[N]}else{W=r[c]=b.extend(!0,{},Ya,W);var d=r[c],f=r.panes;d.fxSettings||(d.fxSettings={});f.fxSettings||(f.fxSettings={});b.each(["_open","_close","_size"],function(a,e){var h="fxName"+e,j="fxSpeed"+e,k="fxSettings"+e,l=d[h]=d[h]||f[h]||d.fxName||f.fxName||"none",p=b.effects&&(b.effects[l]||b.effects.effect&&b.effects.effect[l]);if("none"===l||!r.effects[l]||!p)l=d[h]="none";l=r.effects[l]||{};h=l.all||null;l=l[c]||null;d[j]=d[j]||f[j]||d.fxSpeed||f.fxSpeed||
null;d[k]=b.extend(!0,{},h,l,f.fxSettings,d.fxSettings,f[k],d[k])});delete d.fxName;delete d.fxSpeed;delete d.fxSettings;W.resizerClass||(W.resizerClass="ui-layout-resizer");W.togglerClass||(W.togglerClass="ui-layout-toggler")}W.paneClass||(W.paneClass="ui-layout-pane")});var Ia=h.zIndex,xa=r.zIndexes;0<Ia&&(xa.pane_normal=Ia,xa.content_mask=d(Ia+1,xa.content_mask),xa.resizer_normal=d(Ia+2,xa.resizer_normal));delete r.panes;var Cb=r,tb=q;tb.creatingLayout=!0;j(z,b.layout.onCreate);if(!1===C("onload_start"))Xa= null;d[k]=b.extend(!0,{},h,l,f.fxSettings,d.fxSettings,f[k],d[k])});delete d.fxName;delete d.fxSpeed;delete d.fxSettings;W.resizerClass||(W.resizerClass="ui-layout-resizer");W.togglerClass||(W.togglerClass="ui-layout-toggler")}W.paneClass||(W.paneClass="ui-layout-pane")});var Ia=h.zIndex,xa=r.zIndexes;0<Ia&&(xa.pane_normal=Ia,xa.content_mask=d(Ia+1,xa.content_mask),xa.resizer_normal=d(Ia+2,xa.resizer_normal));delete r.panes;var Cb=r,tb=q;tb.creatingLayout=!0;j(z,b.layout.onCreate);if(!1===C("onload_start"))Xa=
"cancel";else{var Za=u[0],$=b("html"),ub=v.tagName=Za.tagName,vb=v.id=Za.id,wb=v.className=Za.className,L=r,Ja=L.name,$a={},Ka=u.data("parentLayout"),La=u.data("layoutEdge"),ab=Ka&&La,ta=b.layout.cssNum,bb,aa;v.selector=u.selector.split(".slice")[0];v.ref=(L.name?L.name+" layout / ":"")+ub+(vb?"#"+vb:wb?".["+wb+"]":"");v.isBody="BODY"===ub;!ab&&!v.isBody&&(bb=u.closest("."+b.layout.defaults.panes.paneClass),Ka=bb.data("parentLayout"),La=bb.data("layoutEdge"),ab=Ka&&La);u.data({layout:z,layoutContainer:K}).addClass(L.containerClass); "cancel";else{var Za=u[0],$=b("html"),ub=v.tagName=Za.tagName,vb=v.id=Za.id,wb=v.className=Za.className,L=r,Ja=L.name,$a={},Ka=u.data("parentLayout"),La=u.data("layoutEdge"),ab=Ka&&La,ta=b.layout.cssNum,bb,aa;v.selector=u.selector.split(".slice")[0];v.ref=(L.name?L.name+" layout / ":"")+ub+(vb?"#"+vb:wb?".["+wb+"]":"");v.isBody="BODY"===ub;!ab&&!v.isBody&&(bb=u.closest("."+b.layout.defaults.panes.paneClass),Ka=bb.data("parentLayout"),La=bb.data("layoutEdge"),ab=Ka&&La);u.data({layout:z,left-component:K}).addClass(L.containerClass);
var xb={destroy:"",initPanes:"",resizeAll:"resizeAll",resize:"resizeAll"};for(Ja in xb)u.bind("layout"+Ja.toLowerCase()+"."+K,z[xb[Ja]||Ja]);ab&&(z.hasParentLayout=!0,Ka.refreshChildren(La,z));u.data("layoutCSS")||(v.isBody?(u.data("layoutCSS",b.extend(D(u,"position,margin,padding,border"),{height:u.css("height"),overflow:u.css("overflow"),overflowX:u.css("overflowX"),overflowY:u.css("overflowY")})),$.data("layoutCSS",b.extend(D($,"padding"),{height:"auto",overflow:$.css("overflow"),overflowX:$.css("overflowX"), var xb={destroy:"",initPanes:"",resizeAll:"resizeAll",resize:"resizeAll"};for(Ja in xb)u.bind("layout"+Ja.toLowerCase()+"."+K,z[xb[Ja]||Ja]);ab&&(z.hasParentLayout=!0,Ka.refreshChildren(La,z));u.data("layoutCSS")||(v.isBody?(u.data("layoutCSS",b.extend(D(u,"position,margin,padding,border"),{height:u.css("height"),overflow:u.css("overflow"),overflowX:u.css("overflowX"),overflowY:u.css("overflowY")})),$.data("layoutCSS",b.extend(D($,"padding"),{height:"auto",overflow:$.css("overflow"),overflowX:$.css("overflowX"),
overflowY:$.css("overflowY")}))):u.data("layoutCSS",D(u,"position,margin,padding,border,top,bottom,left,right,width,height,overflow,overflowX,overflowY")));try{$a={overflow:"hidden",overflowX:"hidden",overflowY:"hidden"};u.css($a);L.inset&&!b.isPlainObject(L.inset)&&(aa=parseInt(L.inset,10)||0,L.inset={top:aa,bottom:aa,left:aa,right:aa});if(v.isBody)L.outset?b.isPlainObject(L.outset)||(aa=parseInt(L.outset,10)||0,L.outset={top:aa,bottom:aa,left:aa,right:aa}):L.outset={top:ta($,"paddingTop"),bottom:ta($, overflowY:$.css("overflowY")}))):u.data("layoutCSS",D(u,"position,margin,padding,border,top,bottom,left,right,width,height,overflow,overflowX,overflowY")));try{$a={overflow:"hidden",overflowX:"hidden",overflowY:"hidden"};u.css($a);L.inset&&!b.isPlainObject(L.inset)&&(aa=parseInt(L.inset,10)||0,L.inset={top:aa,bottom:aa,left:aa,right:aa});if(v.isBody)L.outset?b.isPlainObject(L.outset)||(aa=parseInt(L.outset,10)||0,L.outset={top:aa,bottom:aa,left:aa,right:aa}):L.outset={top:ta($,"paddingTop"),bottom:ta($,
"paddingBottom"),left:ta($,"paddingLeft"),right:ta($,"paddingRight")},$.css($a).css({height:"100%",border:"none",padding:0,margin:0}),G.isIE6?(u.css({width:"100%",height:"100%",border:"none",padding:0,margin:0,position:"relative"}),L.inset||(L.inset=R(u).inset)):(u.css({width:"auto",height:"auto",margin:0,position:"absolute"}),u.css(L.outset)),b.extend(v,R(u,L.inset));else{var yb=u.css("position");(!yb||!yb.match(/(fixed|absolute|relative)/))&&u.css("position","relative");u.is(":visible")&&(b.extend(v, "paddingBottom"),left:ta($,"paddingLeft"),right:ta($,"paddingRight")},$.css($a).css({height:"100%",border:"none",padding:0,margin:0}),G.isIE6?(u.css({width:"100%",height:"100%",border:"none",padding:0,margin:0,position:"relative"}),L.inset||(L.inset=R(u).inset)):(u.css({width:"auto",height:"auto",margin:0,position:"absolute"}),u.css(L.outset)),b.extend(v,R(u,L.inset));else{var yb=u.css("position");(!yb||!yb.match(/(fixed|absolute|relative)/))&&u.css("position","relative");u.is(":visible")&&(b.extend(v,

463
js/service.js Normal file
View file

@ -0,0 +1,463 @@
JamStash.service('model', function () {
secondsToTime = function (secs) {
// secs = 4729
var times = new Array(3600, 60, 1);
var time = '';
var tmp;
for (var i = 0; i < times.length; i++) {
tmp = Math.floor(secs / times[i]);
// 0: 4729/3600 = 1
// 1: 1129/60 = 18
// 2: 49/1 = 49
if (tmp < 1) {
tmp = '00';
}
else if (tmp < 10) {
tmp = '0' + tmp;
}
if (i == 0 && tmp == '00') {
} else {
time += tmp;
if (i < 2) {
time += ':';
}
}
secs = secs % times[i];
}
return time;
}
this.Index = function (name, artist) {
this.name = name;
this.artist = artist;
}
this.Artist = function (id, name) {
this.id = id;
this.name = name;
}
this.Album = function (id, parentid, name, artist, artistId, coverartthumb, coverartfull, date, starred, description, url, type) {
this.id = id;
this.parentid = parentid;
this.name = name;
this.artist = artist;
this.artistId = artistId;
this.coverartthumb = coverartthumb;
this.coverartfull = coverartfull;
this.date = date;
this.starred = starred;
this.description = description;
this.url = url;
this.type = type;
}
this.Song = function (id, parentid, track, name, artist, artistId, album, albumId, coverartthumb, coverartfull, duration, rating, starred, suffix, specs, url, position, description) {
this.id = id;
this.parentid = parentid;
this.track = track;
this.name = name;
this.artist = artist;
this.artistId = artistId;
this.album = album;
this.albumId = albumId;
this.coverartthumb = coverartthumb;
this.coverartfull = coverartfull;
this.duration = duration;
this.time = duration == '' ? '00:00' : secondsToTime(duration);
this.rating = rating;
this.starred = starred;
this.suffix = suffix;
this.specs = specs;
this.url = url;
this.position = position;
this.selected = false;
this.playing = false;
this.description = description;
this.displayName = this.name + " - " + this.album + " - " + this.artist;
}
});
JamStash.service('globals', function () {
this.settings = {
// Subsonic
/* Demo Server
Username: "android-guest"),
Password: "guest"),
Server: "http://subsonic.org/demo"),
*/
Url: "http://Jamstash.com/beta/#/archive/",
Username: "tsquillario",
Password: "Elephant1!",
Server: "https://tsquillario.dyndns.org:8443/subsonic",
Timeout: 10000,
NotificationTimeout: 20000,
Protocol: "jsonp",
ApplicationName: "Jamstash",
ApiVersion: "1.6.0",
AutoPlaylists: "",
AutoPlaylistSize: 25,
AutoAlbumSize: 15,
// General
HideAZ: false,
ScrollTitle: true,
NotificationSong: true,
NotificationNowPlaying: false,
SaveTrackPosition: false,
ForceFlash: false,
Theme: "Default",
DefaultLibraryLayout: "grid",
AutoPlay: false,
LoopQueue: false,
Repeat: false,
Debug: false
};
this.SavedCollections = [];
this.SavedGenres = [];
this.BaseURL = function () { return this.settings.Server + '/rest'; };
this.BaseParams = function () { return 'u=' + this.settings.Username + '&p=' + this.settings.Password + '&f=' + this.settings.Protocol + '&v=' + this.settings.ApiVersion + '&c=' + this.settings.ApplicationName; };
});
// Directives
JamStash.directive('layout', function () {
return {
link: function (scope, elm, attrs) {
var pageLayoutOptions = {
name: 'pageLayout', // only for debugging
resizeWithWindowDelay: 250, // delay calling resizeAll when window is *still* resizing
//, resizeWithWindowMaxDelay: 2000 // force resize every XX ms while window is being resized
//center__children: {},
//north__paneSelector: "#container",
center__paneSelector: "#container",
south__paneSelector: "#QueuePreview",
south__resizable: false, // No resize
//south__closable: false, // No close handle
//south__spacing_open: 0, // No resize bar
south__size: 145,
south__initClosed: true,
south__minWidth: 145,
south__maxWidth: 145
};
var layoutThreeCol = {
east__size: .42,
east__minSize: 400,
east__maxSize: .5, // 50% of layout width
east__initClosed: false,
east__initHidden: false,
//center__size: 'auto',
center__minWidth: .38,
center__initClosed: false,
center__initHidden: false,
west__size: .2,
west__minSize: 200,
west__initClosed: false,
west__initHidden: false,
//stateManagement__enabled: true, // automatic cookie load & save enabled by default
showDebugMessages: true // log and/or display messages from debugging & testing code
//applyDefaultStyles: true
};
var layoutTwoCol = {
center__size: .8,
center__minSize: 400,
center__maxSize: .5, // 50% of layout width
center__initClosed: false,
center__initHidden: false,
west__size: .2,
west__minSize: 200,
west__initClosed: false,
west__initHidden: false,
//stateManagement__enabled: true, // automatic cookie load & save enabled by default
showDebugMessages: true // log and/or display messages from debugging & testing code
//applyDefaultStyles: true
};
scope.$watch(attrs.state, function (state) {
if (state == 1) {
var layout = elm.layout(pageLayoutOptions);
}
if (state == 2) {
var layout = elm.layout(layoutTwoCol);
//scope.layout.sizePane('east', 120);
//scope.layout.show('west');
//scope.layout.show('south');
} else if (state == 3) {
var layout = elm.layout(layoutThreeCol);
//scope.layout.sizePane('east', 60);
//scope.layout.hide('west');
//scope.layout.hide('south');
}
scope.layout = layout;
});
}
};
});
JamStash.directive('sortable', function () {
return {
link: function (scope, elm, attrs) {
elm.sortable({
start: scope.dragStart,
update: scope.dragEnd
});
elm.disableSelection();
}
};
});
JamStash.directive('split', function () {
return {
link: function (scope, elm, attrs) {
elm.splitPane();
/*
//elm.first().resizable({
$('#SubsonicAlbums > div:first').resizable({
handles: 'e',
minWidth: '100',
maxWidth: '400',
resize: function () {
alert('foo');
var remainingSpace = $(this).parent().width() - $(this).outerWidth();
var divTwo = $(this).next();
var divTwoWidth = remainingSpace - (divTwo.outerWidth() - divTwo.width());
divTwo.css('width', divTwoWidth + 'px');
}
});
*/
/*
scope.$watch(attrs.state, function (state) {
if (state == 1) {
var layout = elm.layout(pageLayoutOptions);
}
if (state == 2) {
var layout = elm.layout(layoutTwoCol);
//scope.layout.sizePane('east', 120);
//scope.layout.show('west');
//scope.layout.show('south');
} else if (state == 3) {
var layout = elm.layout(layoutThreeCol);
//scope.layout.sizePane('east', 60);
//scope.layout.hide('west');
//scope.layout.hide('south');
}
scope.layout = layout;
});
*/
}
};
});
JamStash.directive('fancybox', function ($compile) {
return {
restrict: 'A',
replace: false,
link: function($scope, element, attrs) {
$scope.fancyboxOpen = function() {
var el = angular.element(element.html()),
compiled = $compile(el);
$.fancybox.open(el);
compiled($scope);
};
$scope.fancyboxOpenUrl = function () {
var el = angular.element(element.html()),
compiled = $compile(el);
$.fancybox.open(el);
compiled($scope);
};
}
};
});
JamStash.directive('songpreview', function ($compile, subsonic) {
return {
restrict: 'E',
templateUrl: 'js/partials/songs.html',
replace: false,
// pass these two names from attrs into the template scope
scope: {
song: '@'
},
link: function (scope, element, attrs) {
subsonic.getSongTemplate(function (data) {
scope.song = data;
//var el = angular.element(element.html()),
//var el = element.html(),
//compiled = $compile(el);
$.fancybox.open(element);
//compiled($scope);
});
}
}
})
JamStash.directive('stopEvent', function () {
return {
restrict: 'A',
link: function (scope, element, attr) {
element.bind(attr.stopEvent, function (e) {
e.stopPropagation();
});
}
};
});
JamStash.directive('ngEnter', function () {
return function (scope, element, attrs) {
element.bind("keydown keypress", function (event) {
if (event.which === 13) {
scope.$apply(function () {
scope.$eval(attrs.ngEnter);
});
event.preventDefault();
}
});
};
});
JamStash.directive('ngDownload', function ($compile) {
return {
restrict: 'E',
scope: { data: '=' },
link: function (scope, elm, attrs) {
function getUrl() {
return URL.createObjectURL(new Blob([JSON.stringify(scope.data)], { type: "application/json" }));
}
elm.append($compile(
'<a class="button" download="backup.json"' +
'href="' + getUrl() + '">' +
'Download' +
'</a>'
)(scope));
scope.$watch(scope.data, function () {
elm.children()[0].href = getUrl();
});
}
};
});
/* Factory */
JamStash.factory('json', function ($http) { // Deferred loading
return {
getCollections: function (callback) {
$http.get('js/json_collections.js').success(callback);
},
getChangeLog: function (callback) {
$http.get('js/json_changelog.js').success(callback);
}
}
});
JamStash.factory('template', function ($http, $compile, $http, $templateCache) { // Deferred loading
return {
getCollections: function (callback) {
$http.get('js/json_collections.js', { cache: $templateCache }).success(callback);
},
getChangeLog: function (callback) {
$http.get('js/json_changelog.js', { cache: $templateCache }).success(callback);
},
getSongs: function (callback) {
templateUrl = 'js/partials/songs.html';
$http.get(templateUrl, { cache: $templateCache }).success(callback);
}
}
});
JamStash.factory('subsonic', function ($http, globals, utils) {
return {
getSongTemplate: function (callback) {
var id = '16608';
var url = globals.BaseURL() + '/getMusicDirectory.view?' + globals.BaseParams() + '&id=' + id;
/*
$.ajax({
url: url,
method: 'GET',
dataType: globals.settings.Protocol,
timeout: globals.settings.Timeout,
success: function (data) {
}
});
*/
$http.get(url).success(function (data) {
var items = [];
var song = [];
if (typeof data["subsonic-response"].directory.child != 'undefined') {
if (data["subsonic-response"].directory.child.length > 0) {
items = data["subsonic-response"].directory.child;
} else {
items[0] = data["subsonic-response"].directory.child;
}
angular.forEach(items, function (item, key) {
if (!item.isDir) {
song.push(utils.mapSong(item));
}
});
callback(song);
}
});
}
}
});
/* Filters */
JamStash.filter('capitalize', function () {
return function (input, scope) {
return input.substring(0, 1).toUpperCase() + input.substring(1);
}
});
JamStash.service('notifications', function ($rootScope, globals) {
var msgIndex = 1;
this.updateMessage = function (msg, autohide) {
if (msg != '') {
var id = msgIndex;
$('#messages').append('<span id=\"msg_' + id + '\" class="message">' + msg + '</span>');
$('#messages').fadeIn();
$("#messages").scrollTo('100%');
var el = '#msg_' + id;
if (autohide) {
setTimeout(function () {
$(el).fadeOut(function () { $(this).remove(); });
}, globals.settings.NotificationTimeout);
}
$(el).click(function () {
$(el).fadeOut(function () { $(this).remove(); });
return false;
});
msgIndex++;
}
}
this.requestPermissionIfRequired = function () {
if (!this.hasNotificationPermission() && (window.webkitNotifications)) {
window.webkitNotifications.requestPermission();
}
}
this.hasNotificationPermission = function () {
return !!(window.webkitNotifications) && (window.webkitNotifications.checkPermission() == 0);
}
var notifications = new Array();
this.showNotification = function (pic, title, text, type, bind) {
if (this.hasNotificationPermission()) {
//closeAllNotifications()
var popup;
if (type == 'text') {
popup = window.webkitNotifications.createNotification(pic, title, text);
} else if (type == 'html') {
popup = window.webkitNotifications.createHTMLNotification(text);
}
if (bind = '#NextTrack') {
popup.addEventListener('click', function (bind) {
//$(bind).click();
$rootScope.nextTrack();
this.cancel();
})
}
notifications.push(popup);
setTimeout(function (notWin) {
notWin.cancel();
}, globals.settings.NotificationTimeout, popup);
popup.show();
} else {
console.log("showNotification: No Permission");
}
}
this.closeAllNotifications = function () {
for (notification in notifications) {
notifications[notification].cancel();
}
}
});

View file

@ -1,4 +1,4 @@
JamStash.service('utils', function ($cookieStore) { JamStash.service('utils', function ($cookieStore, globals, model) {
this.safeApply = function (fn) { this.safeApply = function (fn) {
var phase = this.$root.$$phase; var phase = this.$root.$$phase;
if (phase == '$apply' || phase == '$digest') { if (phase == '$apply' || phase == '$digest') {
@ -46,6 +46,26 @@ JamStash.service('utils', function ($cookieStore) {
} }
*/ */
} }
this.mapSong = function (data) {
var song = data;
var url, title, track, rating, starred, contenttype, suffix, description;
var specs = '', coverartthumb = '', coverartfull = '';
if (typeof song.coverArt != 'undefined') {
coverartthumb = globals.BaseURL() + '/getCoverArt.view?' + globals.BaseParams() + '&size=60&id=' + song.coverArt;
coverartfull = globals.BaseURL() + '/getCoverArt.view?' + globals.BaseParams() + '&id=' + song.coverArt;
}
if (typeof song.description == 'undefined') { description = ''; } else { description = song.description; }
if (typeof song.title == 'undefined') { title = '&nbsp;'; } else { title = song.title.toString(); }
if (typeof song.track == 'undefined') { track = '&nbsp;'; } else { track = song.track.toString(); }
if (typeof song.starred !== 'undefined') { starred = true; } else { starred = false; }
if (song.bitRate !== undefined) { specs += song.bitRate + ' Kbps'; }
if (song.transcodedSuffix !== undefined) { specs += ', transcoding:' + song.suffix + ' > ' + song.transcodedSuffix; } else { specs += ', ' + song.suffix; }
if (song.transcodedSuffix !== undefined) { suffix = song.transcodedSuffix; } else { suffix = song.suffix; }
if (suffix == 'ogg') { suffix = 'oga'; }
var salt = Math.floor(Math.random() * 100000);
url = globals.BaseURL() + '/stream.view?' + globals.BaseParams() + '&id=' + song.id + '&salt=' + salt;
return new model.Song(song.id, song.parent, track, title, song.artist, song.artistId, song.album, song.albumId, coverartthumb, coverartfull, song.duration, song.userRating, starred, suffix, specs, url, 0, description);
}
this.confirmDelete = function (text) { this.confirmDelete = function (text) {
var question = confirm(text); var question = confirm(text);
if (question) { if (question) {
@ -132,6 +152,12 @@ JamStash.service('utils', function ($cookieStore) {
} }
return time; return time;
} }
this.arrayObjectIndexOf = function (myArray, searchTerm, property) {
for (var i = 0, len = myArray.length; i < len; i++) {
if (myArray[i][property] === searchTerm) return i;
}
return -1;
}
this.logObjectProperties = function (obj) { this.logObjectProperties = function (obj) {
$.each(obj, function (key, value) { $.each(obj, function (key, value) {
var parent = key; var parent = key;

View file

@ -174,7 +174,7 @@ ul.simplelist li
ul.simplelist li a ul.simplelist li a
{ {
text-decoration: none; text-decoration: none;
color: #545454; color: #d6d469;
} }
ul.simplelist li:hover ul.simplelist li:hover
{ {

View file

@ -168,6 +168,27 @@ span.apiversion
{ {
background: url('../images/favicon_32x32.png') no-repeat 0 1px; background: url('../images/favicon_32x32.png') no-repeat 0 1px;
} }
.pagetabcenter
{
position: absolute;
bottom: 0;
left: 0;
right: 0;
margin: 0 auto;
width: 66px;
height: 22px;
background: url('../images/favicon_32x32.png') no-repeat center center #f9f9f9;
border-top: 1px solid #C3C3C3;
border-right: 1px solid #C3C3C3;
border-left: 1px solid #C3C3C3;
cursor: pointer;
z-index: 200;
}
.pagetabcenter:hover
{
height: 20px;
opacity: .8;
}
#sslogo #sslogo
{ {
position: absolute; position: absolute;
@ -198,6 +219,20 @@ span.apiversion
{ {
margin: 0; margin: 0;
} }
.smcolumn
{
height: 100%;
overflow-y: scroll;
outline: none;
float: left;
}
.lgcolumn
{
height: 100%;
overflow-y: scroll;
outline: none;
float: left;
}
.lgsection .lgsection
{ {
background: #fff; background: #fff;
@ -472,8 +507,8 @@ ul.mainlist li.item a.add:hover
} }
#BreadCrumb #BreadCrumb
{ {
float: right; float: left;
margin: 5px 0 0 0; margin: 13px 0 0 0;
padding: 2px 8px; padding: 2px 8px;
color: #AEAEA7; color: #AEAEA7;
background: #f6f6f6; background: #f6f6f6;
@ -498,6 +533,10 @@ ul.mainlist li.item a.add:hover
{ {
text-decoration: underline; text-decoration: underline;
} }
#BreadCrumbs .crumb
{
float: left;
}
#BreadHome a #BreadHome a
{ {
padding: 0; padding: 0;
@ -637,7 +676,7 @@ ul.songlist .albumgrid
height: 230px; height: 230px;
float: left; float: left;
margin: 5px; margin: 5px;
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.25);
} }
ul.songlist .albumgrid .title ul.songlist .albumgrid .title
{ {
@ -825,6 +864,12 @@ ul.songlist li:hover
{ {
background-color: #EEF4FA; background-color: #EEF4FA;
} }
.songpreview li {
list-style-type: none;
font-size: 14px;
padding: 8px;
border-bottom: 1px solid #f2f2f2;
}
.fillheight { .fillheight {
position: absolute; position: absolute;
/*top: 41px;*/ /*top: 41px;*/
@ -910,10 +955,18 @@ ul.songlist li:hover
{ {
height: 29px; height: 29px;
/* width: 660px; */ /* width: 660px; */
margin: 5px 68px 0 0; margin: 5px 205px 0 0;
padding: 0 0 0 5px; padding: 0 0 0 5px;
float: right; float: right;
} }
#globalactions
{
position: absolute;
top: 0px;
right: 65px;
height: 29px;
margin: 5px 0 0 0;
}
#search #search
{ {
margin: 6px 0 0 0; margin: 6px 0 0 0;
@ -1071,33 +1124,23 @@ ul.songlist li:hover
width: 100%; /* As wide as it's allowed */ width: 100%; /* As wide as it's allowed */
} }
#QueuePreview { #QueuePreview {
height: 150px; height: 140px;
width: 100%; width: 100%;
padding: 0;
margin: 0;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
z-index: 999; z-index: 100;
display: none; /*background: #5b5b4e; */
background: #ffffff; background: rgba(91, 91, 78, 0.4) ;
}
#QueuePreview ul {
height: 135px;
padding: 4px;
margin: 0;
list-style-type: none; list-style-type: none;
overflow-x: auto; overflow-x: scroll;
overflow-y: hidden; overflow-y: hidden;
text-align: left; text-align: left;
white-space: nowrap; white-space: nowrap;
background: #5b5b4e; display: none;
} }
#QueuePreview li { #QueuePreview li {
height: 120px;
}
#QueuePreview .queueactions {
padding: 0 5px;
float: left;
border-right: 1px solid #cbcbcb;
height: 100%;
} }
#QueuePreview li.selected { #QueuePreview li.selected {
background-color: #D4E2F1; background-color: #D4E2F1;
@ -1110,8 +1153,9 @@ ul.songlist li:hover
} }
#QueuePreview .row { #QueuePreview .row {
width: 92px; width: 92px;
height: 119px;
display: inline-block; display: inline-block;
margin: 0 1px; margin: 1px 1px;
padding: 4px 5px; padding: 4px 5px;
font-size: 11px; font-size: 11px;
text-align: center; text-align: center;
@ -1755,3 +1799,19 @@ legend
.ui-layout-west { .ui-layout-west {
z-index: 900 !important; z-index: 900 !important;
} }
.split-pane-divider {
background: #aaa;
}
#left-component {
width: 20em;
}
#my-divider {
left: 20em; /* Same as left component width */
width: 5px;
}
#right-component {
left: 20em; /* Same as left component width */
margin-left: 5px; /* Same as divider width */
}