Remember selection of which backend to search.

Fixes #130.
This commit is contained in:
jcass 2016-03-18 22:03:27 +02:00
parent 7944acecb9
commit db006f6c54
11 changed files with 395 additions and 353 deletions

View File

@ -75,6 +75,8 @@ v2.3.0 (UNRELEASED)
- Enhance build workflow to include style checks and syntax validation for HTML, CSS, and Javascript. - Enhance build workflow to include style checks and syntax validation for HTML, CSS, and Javascript.
- Now displays album and artist info when browsing tracks. (Addresses: `#99 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/99>`_). - Now displays album and artist info when browsing tracks. (Addresses: `#99 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/99>`_).
- Now remembers which backend was searched previously, and automatically selects that backend as the default search target.
(Addresses: `#130 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/130>`_).
**Fixes** **Fixes**

View File

@ -74,7 +74,7 @@ module.exports = function (config) {
dir: 'coverage/', dir: 'coverage/',
reporters: [ reporters: [
{ type: 'lcov', subdir: '.' }, { type: 'lcov', subdir: '.' },
{ type: 'text'} { type: 'text' }
] ]
} }
}) })

View File

@ -395,14 +395,10 @@
<div class="ui-block"> <div class="ui-block">
<form> <form>
<p>Search for artists, albums, or specific tracks. <p>Search for artists, albums, or specific tracks.
<select id="selectSearchService"> <select id="selectSearchService"></select>
<!-- data-native-menu="false">
multiple="multiple" data-native-menu="false">
<option data-placeholder="true">Choose services</option> -->
</select>
<input id="searchinput" placeholder="Search term" class="span2" data-clear-btn="true" <input id="searchinput" placeholder="Search term" class="span2" data-clear-btn="true"
onkeypress="return searchPressed(event.keyCode);" type="text"/> onkeypress="return library.searchPressed(event.keyCode);" type="text"/>
<button class="btn" type="button" onclick="return initSearch(event.value);"> <button class="btn" type="button" onclick="return library.initSearch(event.value);">
Search! Search!
</button> </button>
</form> </form>

View File

@ -95,9 +95,9 @@ var uriClassList = [
var uriHumanList = [ var uriHumanList = [
['spotify', 'Spotify'], ['spotify', 'Spotify'],
['spotifytunigo', 'Spotify Browse'], ['spotifytunigo', 'Spotify browse'],
['local', 'Local Files'], ['local', 'Local files'],
['m3u', 'Local Playlists'], ['m3u', 'Local playlists'],
['podcast', 'Podcasts'], ['podcast', 'Podcasts'],
['dirble', 'Dirble'], ['dirble', 'Dirble'],
['tunein', 'TuneIn'], ['tunein', 'TuneIn'],
@ -249,7 +249,7 @@ function renderSongLiDivider (track, nextTrack, currentIndex, target) {
if (hasSameAlbum(track, nextTrack)) { if (hasSameAlbum(track, nextTrack)) {
// Large divider with album cover // Large divider with album cover
$(target).before( $(target).before(
'<li class="albumdivider"><a href="#" onclick="return showAlbum(\'' + track.album.uri + '\');">' + '<li class="albumdivider"><a href="#" onclick="return library.showAlbum(\'' + track.album.uri + '\');">' +
'<img id="' + getjQueryID(target + '-cover', track.uri) + '" class="artistcover" width="30" height="30"/>' + '<img id="' + getjQueryID(target + '-cover', track.uri) + '" class="artistcover" width="30" height="30"/>' +
'<h1><i class="' + getMediaClass(track.uri) + '"></i> ' + track.album.name + '</h1><p>' + '<h1><i class="' + getMediaClass(track.uri) + '"></i> ' + track.album.name + '</h1><p>' +
renderSongLiTrackArtists(track) + '</p></a></li>' renderSongLiTrackArtists(track) + '</p></a></li>'
@ -482,11 +482,11 @@ function getMediaClass (uri) {
function getMediaHuman (uri) { function getMediaHuman (uri) {
var scheme = getScheme(uri) var scheme = getScheme(uri)
for (var i = 0; i < uriHumanList.length; i++) { for (var i = 0; i < uriHumanList.length; i++) {
if (scheme === uriHumanList[i][0]) { if (scheme.toLowerCase() === uriHumanList[i][0].toLowerCase()) {
return uriHumanList[i][1] return uriHumanList[i][1]
} }
} }
return '' return uri
} }
function isServiceUri (uri) { function isServiceUri (uri) {

View File

@ -117,7 +117,7 @@ function setSongInfo (data) {
if (data.track.artists) { if (data.track.artists) {
for (var j = 0; j < data.track.artists.length; j++) { for (var j = 0; j < data.track.artists.length; j++) {
artistshtml += '<a href="#" onclick="return showArtist(\'' + data.track.artists[j].uri + '\');">' + data.track.artists[j].name + '</a>' artistshtml += '<a href="#" onclick="return library.showArtist(\'' + data.track.artists[j].uri + '\');">' + data.track.artists[j].name + '</a>'
artiststext += data.track.artists[j].name artiststext += data.track.artists[j].name
if (j !== data.track.artists.length - 1) { if (j !== data.track.artists.length - 1) {
artistshtml += ', ' artistshtml += ', '
@ -127,7 +127,7 @@ function setSongInfo (data) {
arttmp = artistshtml arttmp = artistshtml
} }
if (data.track.album && data.track.album.name) { if (data.track.album && data.track.album.name) {
$('#modalalbum').html('<a href="#" onclick="return showAlbum(\'' + data.track.album.uri + '\');">' + data.track.album.name + '</a>') $('#modalalbum').html('<a href="#" onclick="return library.showAlbum(\'' + data.track.album.uri + '\');">' + data.track.album.name + '</a>')
coverArt.getCover(data.track.uri, '#infocover, #controlspopupimage', 'extralarge') coverArt.getCover(data.track.uri, '#infocover, #controlspopupimage', 'extralarge')
} else { } else {
$('#modalalbum').html('') $('#modalalbum').html('')
@ -171,14 +171,14 @@ function popupTracks (e, listuri, trackuri, tlid) {
if (popupData[trackuri].artists) { if (popupData[trackuri].artists) {
if (popupData[trackuri].artists.length === 1) { if (popupData[trackuri].artists.length === 1) {
child = '<a href="#" onclick="showArtist(\'' + popupData[trackuri].artists[0].uri + '\');">Show Artist</a>' child = '<a href="#" onclick="library.showArtist(\'' + popupData[trackuri].artists[0].uri + '\');">Show Artist</a>'
$('.popupArtistName').html(popupData[trackuri].artists[0].name) $('.popupArtistName').html(popupData[trackuri].artists[0].name)
$('.popupArtistHref').attr('onclick', 'showArtist("' + popupData[trackuri].artists[0].uri + '");') $('.popupArtistHref').attr('onclick', 'library.showArtist("' + popupData[trackuri].artists[0].uri + '");')
$('.popupArtistsDiv').hide() $('.popupArtistsDiv').hide()
$('.popupArtistsLi').show() $('.popupArtistsLi').show()
} else { } else {
for (var j = 0; j < popupData[trackuri].artists.length; j++) { for (var j = 0; j < popupData[trackuri].artists.length; j++) {
child += '<li><a href="#" onclick="showArtist(\'' + popupData[trackuri].artists[j].uri + '\');"><span class="popupArtistName">' + popupData[trackuri].artists[j].name + '</span></a></li>' child += '<li><a href="#" onclick="library.showArtist(\'' + popupData[trackuri].artists[j].uri + '\');"><span class="popupArtistName">' + popupData[trackuri].artists[j].name + '</span></a></li>'
} }
$('.popupArtistsLi').hide() $('.popupArtistsLi').hide()
$('.popupArtistsLv').html(child).show() $('.popupArtistsLv').html(child).show()
@ -222,7 +222,7 @@ function popupTracks (e, listuri, trackuri, tlid) {
function showAlbumPopup (popupId) { function showAlbumPopup (popupId) {
uri = $(popupId).data('track') uri = $(popupId).data('track')
showAlbum(popupData[uri].album.uri) library.showAlbum(popupData[uri].album.uri)
} }
/** ******************** /** ********************
@ -232,14 +232,14 @@ function showAlbumPopup (popupId) {
function initSocketevents () { function initSocketevents () {
mopidy.on('state:online', function () { mopidy.on('state:online', function () {
showOffline(false) showOffline(false)
getCurrentPlaylist() library.getCurrentPlaylist()
updateStatusOfAll() updateStatusOfAll()
getPlaylists() library.getPlaylists()
getUriSchemes().then(function () { getUriSchemes().then(function () {
showFavourites() showFavourites()
}) })
getBrowseDir() library.getBrowseDir()
getSearchSchemes() library.getSearchSchemes()
showLoading(false) showLoading(false)
$(window).hashchange() $(window).hashchange()
}) })
@ -258,21 +258,21 @@ function initSocketevents () {
mopidy.on('event:playlistsLoaded', function (data) { mopidy.on('event:playlistsLoaded', function (data) {
showLoading(true) showLoading(true)
getPlaylists() library.getPlaylists()
}) })
mopidy.on('event:playlistChanged', function (data) { mopidy.on('event:playlistChanged', function (data) {
$('#playlisttracksdiv').hide() $('#playlisttracksdiv').hide()
$('#playlistslistdiv').show() $('#playlistslistdiv').show()
delete playlists[data.playlist.uri] delete playlists[data.playlist.uri]
getPlaylists() library.getPlaylists()
}) })
mopidy.on('event:playlistDeleted', function (data) { mopidy.on('event:playlistDeleted', function (data) {
$('#playlisttracksdiv').hide() $('#playlisttracksdiv').hide()
$('#playlistslistdiv').show() $('#playlistslistdiv').show()
delete playlists[data.uri] delete playlists[data.uri]
getPlaylists() library.getPlaylists()
}) })
mopidy.on('event:volumeChanged', function (data) { mopidy.on('event:volumeChanged', function (data) {
@ -296,7 +296,7 @@ function initSocketevents () {
}) })
mopidy.on('event:tracklistChanged', function (data) { mopidy.on('event:tracklistChanged', function (data) {
getCurrentPlaylist() library.getCurrentPlaylist()
}) })
mopidy.on('event:seeked', function (data) { mopidy.on('event:seeked', function (data) {
@ -424,7 +424,7 @@ function locationHashChanged () {
$('#navsearch a').addClass($.mobile.activeBtnClass) $('#navsearch a').addClass($.mobile.activeBtnClass)
$('#searchinput').focus() $('#searchinput').focus()
if (customTracklists['mbw:allresultscache'] === '') { if (customTracklists['mbw:allresultscache'] === '') {
initSearch($('#searchinput').val()) library.initSearch($('#searchinput').val())
} }
break break
case 'stream': case 'stream':
@ -432,12 +432,12 @@ function locationHashChanged () {
break break
case 'artists': case 'artists':
if (uri !== '') { if (uri !== '') {
showArtist(uri) library.showArtist(uri)
} }
break break
case 'albums': case 'albums':
if (uri !== '') { if (uri !== '') {
showAlbum(uri) library.showAlbum(uri)
} }
break break
} }

View File

@ -77,7 +77,7 @@ var coverArt = {
} }
} }
}, error: function (code, message) { }, error: function (code, message) {
console.log('Error retrieving album info from last.fm', code, message) console.error('Error retrieving album info from last.fm', code, message)
}}) }})
}, },
@ -94,7 +94,7 @@ var coverArt = {
} }
} }
}, error: function (code, message) { }, error: function (code, message) {
console.log('Error retrieving artist info from last.fm', code, message) console.error('Error retrieving artist info from last.fm', code, message)
}}) }})
} }
} }

View File

@ -1,348 +1,344 @@
var library = {
/** ******************************* /** *******************************
* Search * Search
*********************************/ *********************************/
function searchPressed (key) { searchPressed: function (key) {
var value = $('#searchinput').val() var value = $('#searchinput').val()
switchContent('search') switchContent('search')
if (key === 13) { if (key === 13) {
initSearch() library.initSearch()
return false return false
}
return true
}
// init search
function initSearch () {
var value = $('#searchinput').val()
var searchService = $('#selectSearchService').val()
if ((value.length < 100) && (value.length > 0)) {
showLoading(true)
// hide ios/android keyboard
document.activeElement.blur()
$('input').blur()
delete customTracklists[URI_SCHEME + ':allresultscache']
delete customTracklists[URI_SCHEME + ':artistresultscache']
delete customTracklists[URI_SCHEME + ':albumresultscache']
delete customTracklists[URI_SCHEME + ':trackresultscache']
$('#searchartists').hide()
$('#searchalbums').hide()
$('#searchtracks').hide()
if (searchService !== 'all') {
mopidy.library.search({'query': {any: [value]}, 'uris': [searchService + ':']}).then(processSearchResults, console.error)
} else {
mopidy.getUriSchemes().then(function (schemes) {
var query = {}
var uris = []
var regexp = $.map(schemes, function (scheme) {
return '^' + scheme + ':'
}).join('|')
var match = value.match(regexp)
if (match) {
var scheme = match[0]
query = {uri: [value]}
uris = [scheme]
} else {
query = {any: [value]}
}
mopidy.library.search({'query': query, 'uris': uris}).then(processSearchResults, console.error)
})
} }
} return true
} },
// init search
initSearch: function () {
var value = $('#searchinput').val()
var searchService = $('#selectSearchService').val()
$.cookie('searchScheme', searchService, { expires: 365 })
if ((value.length < 100) && (value.length > 0)) {
showLoading(true)
// hide ios/android keyboard
document.activeElement.blur()
$('input').blur()
delete customTracklists[URI_SCHEME + ':allresultscache']
delete customTracklists[URI_SCHEME + ':artistresultscache']
delete customTracklists[URI_SCHEME + ':albumresultscache']
delete customTracklists[URI_SCHEME + ':trackresultscache']
$('#searchartists').hide()
$('#searchalbums').hide()
$('#searchtracks').hide()
if (searchService !== 'all') {
mopidy.library.search({'query': {any: [value]}, 'uris': [searchService + ':']}).then(library.processSearchResults, console.error)
} else {
mopidy.getUriSchemes().then(function (schemes) {
var query = {}
var uris = []
var regexp = $.map(schemes, function (scheme) {
return '^' + scheme + ':'
}).join('|')
var match = value.match(regexp)
if (match) {
var scheme = match[0]
query = {uri: [value]}
uris = [scheme]
} else {
query = {any: [value]}
}
mopidy.library.search({'query': query, 'uris': uris}).then(library.processSearchResults, console.error)
})
}
}
},
/** ****************************************************** /** ******************************************************
* process results of a search * process results of a search
*********************************************************/ *********************************************************/
processSearchResults: function (resultArr) {
$(SEARCH_TRACK_TABLE).empty()
$(SEARCH_ARTIST_TABLE).empty()
$(SEARCH_ALBUM_TABLE).empty()
// # speed clone http://jsperf.com/cloning-an-object/2 // Merge results from different backends.
function clone (obj) { // TODO should of coures have multiple tables
var target = {} var results = {'tracks': [], 'artists': [], 'albums': []}
for (var i in obj) { var i, j
if (obj.hasOwnProperty(i)) { var emptyResult = true
target[i] = obj[i]
}
}
return target
}
function processSearchResults (resultArr) { for (i = 0; i < resultArr.length; i++) {
$(SEARCH_TRACK_TABLE).empty() if (resultArr[i].tracks) {
$(SEARCH_ARTIST_TABLE).empty() for (j = 0; j < resultArr[i].tracks.length; j++) {
$(SEARCH_ALBUM_TABLE).empty() results.tracks.push(resultArr[i].tracks[j])
emptyResult = false
// Merge results from different backends. }
// TODO should of coures have multiple tables
var results = {'tracks': [], 'artists': [], 'albums': []}
var i, j
var emptyResult = true
for (i = 0; i < resultArr.length; i++) {
if (resultArr[i].tracks) {
for (j = 0; j < resultArr[i].tracks.length; j++) {
results.tracks.push(resultArr[i].tracks[j])
emptyResult = false
} }
} if (resultArr[i].artists) {
if (resultArr[i].artists) { for (j = 0; j < resultArr[i].artists.length; j++) {
for (j = 0; j < resultArr[i].artists.length; j++) { results.artists.push(resultArr[i].artists[j])
results.artists.push(resultArr[i].artists[j]) emptyResult = false
emptyResult = false }
} }
} if (resultArr[i].albums) {
if (resultArr[i].albums) { for (j = 0; j < resultArr[i].albums.length; j++) {
for (j = 0; j < resultArr[i].albums.length; j++) { results.albums.push(resultArr[i].albums[j])
results.albums.push(resultArr[i].albums[j]) emptyResult = false
emptyResult = false
}
}
}
customTracklists[URI_SCHEME + ':trackresultscache'] = results.tracks
if (emptyResult) {
$('#searchtracks').show()
$(SEARCH_TRACK_TABLE).append(
'<li class="song albumli"><a href="#"><h1><i></i>No tracks found...</h1></a></li>'
)
toast('No results')
showLoading(false)
return false
}
if (results.artists.length > 0) {
$('#searchartists').show()
}
if (results.albums.length > 0) {
$('#searchalbums').show()
}
if (results.tracks.length > 0) {
$('#searchtracks').show()
}
// Returns a string where {x} in template is replaced by tokens[x].
function theme (template, tokens) {
return template.replace(/{[^}]+}/g, function (match) {
return tokens[match.slice(1, -1)]
})
}
// 'Show more' pattern
var showMorePattern = '<li onclick="$(this).hide().siblings().show(); return false;"><a>Show {count} more</a></li>'
// Artist results
var child = ''
var pattern = '<li><a href="#" onclick="return showArtist(this.id)" id={id}><i class="{class}"></i> <strong>{name}</strong></a></li>'
var tokens
for (i = 0; i < results.artists.length; i++) {
tokens = {
'id': results.artists[i].uri,
'name': results.artists[i].name,
'class': getMediaClass(results.artists[i].uri)
}
// Add 'Show all' item after a certain number of hits.
if (i === 4 && results.artists.length > 5) {
child += theme(showMorePattern, {'count': results.artists.length - i})
pattern = pattern.replace('<li>', '<li class="overflow">')
}
child += theme(pattern, tokens)
}
// Inject list items, refresh listview and hide superfluous items.
$(SEARCH_ARTIST_TABLE).html(child).listview('refresh').find('.overflow').hide()
// Album results
child = ''
pattern = '<li><a href="#" onclick="return showAlbum(this.id)" id="{albumId}">'
pattern += '<h5 data-role="heading"><i class="{class}"></i> {albumName}</h5>'
pattern += '<p data-role="desc">{artistName}</p>'
pattern += '</a></li>'
for (i = 0; i < results.albums.length; i++) {
tokens = {
'albumId': results.albums[i].uri,
'albumName': results.albums[i].name,
'artistName': '',
'albumYear': results.albums[i].date,
'class': getMediaClass(results.albums[i].uri)
}
if (results.albums[i].artists) {
for (j = 0; j < results.albums[i].artists.length; j++) {
if (results.albums[i].artists[j].name) {
tokens.artistName += results.albums[i].artists[j].name + ' '
} }
} }
} }
if (tokens.albumYear) {
tokens.artistName += '(' + tokens.albumYear + ')' customTracklists[URI_SCHEME + ':trackresultscache'] = results.tracks
}
// Add 'Show all' item after a certain number of hits. if (emptyResult) {
if (i === 4 && results.albums.length > 5) { $('#searchtracks').show()
child += theme(showMorePattern, {'count': results.albums.length - i}) $(SEARCH_TRACK_TABLE).append(
pattern = pattern.replace('<li>', '<li class="overflow">') '<li class="song albumli"><a href="#"><h1><i></i>No tracks found...</h1></a></li>'
)
toast('No results')
showLoading(false)
return false
} }
child += theme(pattern, tokens) if (results.artists.length > 0) {
} $('#searchartists').show()
// Inject list items, refresh listview and hide superfluous items. }
$(SEARCH_ALBUM_TABLE).html(child).listview('refresh').find('.overflow').hide()
// Track results if (results.albums.length > 0) {
resultsToTables(results.tracks, SEARCH_TRACK_TABLE, URI_SCHEME + ':trackresultscache') $('#searchalbums').show()
}
showLoading(false) if (results.tracks.length > 0) {
} $('#searchtracks').show()
}
function toggleSearch () { // Returns a string where {x} in template is replaced by tokens[x].
$('#albumresulttable tr').removeClass('hidden') function theme (template, tokens) {
$('#artistresulttable tr').removeClass('hidden') return template.replace(/{[^}]+}/g, function (match) {
} return tokens[match.slice(1, -1)]
})
}
// 'Show more' pattern
var showMorePattern = '<li onclick="$(this).hide().siblings().show(); return false;"><a>Show {count} more</a></li>'
// Artist results
var child = ''
var pattern = '<li><a href="#" onclick="return library.showArtist(this.id)" id={id}><i class="{class}"></i> <strong>{name}</strong></a></li>'
var tokens
for (i = 0; i < results.artists.length; i++) {
tokens = {
'id': results.artists[i].uri,
'name': results.artists[i].name,
'class': getMediaClass(results.artists[i].uri)
}
// Add 'Show all' item after a certain number of hits.
if (i === 4 && results.artists.length > 5) {
child += theme(showMorePattern, {'count': results.artists.length - i})
pattern = pattern.replace('<li>', '<li class="overflow">')
}
child += theme(pattern, tokens)
}
// Inject list items, refresh listview and hide superfluous items.
$(SEARCH_ARTIST_TABLE).html(child).listview('refresh').find('.overflow').hide()
// Album results
child = ''
pattern = '<li><a href="#" onclick="return library.showAlbum(this.id)" id="{albumId}">'
pattern += '<h5 data-role="heading"><i class="{class}"></i> {albumName}</h5>'
pattern += '<p data-role="desc">{artistName}</p>'
pattern += '</a></li>'
for (i = 0; i < results.albums.length; i++) {
tokens = {
'albumId': results.albums[i].uri,
'albumName': results.albums[i].name,
'artistName': '',
'albumYear': results.albums[i].date,
'class': getMediaClass(results.albums[i].uri)
}
if (results.albums[i].artists) {
for (j = 0; j < results.albums[i].artists.length; j++) {
if (results.albums[i].artists[j].name) {
tokens.artistName += results.albums[i].artists[j].name + ' '
}
}
}
if (tokens.albumYear) {
tokens.artistName += '(' + tokens.albumYear + ')'
}
// Add 'Show all' item after a certain number of hits.
if (i === 4 && results.albums.length > 5) {
child += theme(showMorePattern, {'count': results.albums.length - i})
pattern = pattern.replace('<li>', '<li class="overflow">')
}
child += theme(pattern, tokens)
}
// Inject list items, refresh listview and hide superfluous items.
$(SEARCH_ALBUM_TABLE).html(child).listview('refresh').find('.overflow').hide()
// Track results
resultsToTables(results.tracks, SEARCH_TRACK_TABLE, URI_SCHEME + ':trackresultscache')
showLoading(false)
},
/** ******************************* /** *******************************
* Playlists & Browse * Playlists & Browse
*********************************/ *********************************/
getPlaylists: function () {
// get playlists without tracks
mopidy.playlists.asList().then(processGetPlaylists, console.error)
},
function getPlaylists () { getBrowseDir: function (rootdir) {
// get playlists without tracks // get directory to browse
mopidy.playlists.asList().then(processGetPlaylists, console.error) showLoading(true)
} if (!rootdir) {
browseStack.pop()
rootdir = browseStack[browseStack.length - 1]
} else {
browseStack.push(rootdir)
}
if (!rootdir) {
rootdir = null
}
mopidy.library.browse({'uri': rootdir}).then(processBrowseDir, console.error)
},
function getBrowseDir (rootdir) { getCurrentPlaylist: function () {
// get directory to browse mopidy.tracklist.getTlTracks().then(processCurrentPlaylist, console.error)
showLoading(true) },
if (!rootdir) {
browseStack.pop()
rootdir = browseStack[browseStack.length - 1]
} else {
browseStack.push(rootdir)
}
if (!rootdir) {
rootdir = null
}
mopidy.library.browse({'uri': rootdir}).then(processBrowseDir, console.error)
}
function getCurrentPlaylist () {
mopidy.tracklist.getTlTracks().then(processCurrentPlaylist, console.error)
}
/** ****************************************************** /** ******************************************************
* Show tracks of playlist * Show tracks of playlist
********************************************************/ ********************************************************/
function togglePlaylists () { togglePlaylists: function () {
if ($(window).width() <= 960) { if ($(window).width() <= 960) {
$('#playlisttracksdiv').toggle(); $('#playlisttracksdiv').toggle();
// Hide other div // Hide other div
($('#playlisttracksdiv').is(':visible')) ? $('#playlistslistdiv').hide() : $('#playlistslistdiv').show() ($('#playlisttracksdiv').is(':visible')) ? $('#playlistslistdiv').hide() : $('#playlistslistdiv').show()
} else { } else {
$('#playlisttracksdiv').show() $('#playlisttracksdiv').show()
$('#playlistslistdiv').show() $('#playlistslistdiv').show()
}
return true
}
function showTracklist (uri) {
showLoading(true)
$(PLAYLIST_TABLE).empty()
togglePlaylists()
var tracks = getPlaylistTracks(uri).then(function (tracks) {
resultsToTables(tracks, PLAYLIST_TABLE, uri, 'return togglePlaylists();', true)
showLoading(false)
})
updatePlayIcons(uri)
$('#playlistslist li a').each(function () {
$(this).removeClass('playlistactive')
if (this.id === uri) {
$(this).addClass('playlistactive')
} }
}) return true
return false },
}
/** **** /** **********
* Lookups * Lookups
*/ ************/
showTracklist: function (uri) {
function showArtist (nwuri) {
$('#popupQueue').popup('close')
$('#popupTracks').popup('close')
$('#controlsmodal').popup('close')
$(ARTIST_TABLE).empty()
// TODO cache
$('#h_artistname').html('')
showLoading(true)
mopidy.library.lookup({'uris': [nwuri]}).then(function (resultDict) {
var resultArr = resultDict[nwuri]
resultArr.uri = nwuri
processArtistResults(resultArr)
}, console.error)
switchContent('artists', nwuri)
scrollToTop()
return false
}
function showAlbum (uri) {
$('#popupQueue').popup('close')
$('#popupTracks').popup('close')
$('#controlsmodal').popup('close')
$(ALBUM_TABLE).empty()
// fill from cache
var pl = getTracksFromUri(uri, true)
if (pl.length > 0) {
albumTracksToTable(pl, ALBUM_TABLE, uri)
var albumname = getAlbum(pl)
var artistname = getArtist(pl)
$('#h_albumname').html(albumname)
$('#h_albumartist').html(artistname)
$('#coverpopupalbumname').html(albumname)
$('#coverpopupartist').html(artistname)
showLoading(false)
mopidy.library.lookup({'uris': [uri]}).then(function (resultDict) {
var resultArr = resultDict[uri]
resultArr.uri = uri
processAlbumResults(resultArr)
}, console.error)
} else {
showLoading(true) showLoading(true)
$('#h_albumname').html('') $(PLAYLIST_TABLE).empty()
$('#h_albumartist').html('') library.togglePlaylists()
mopidy.library.lookup({'uris': [uri]}).then(function (resultDict) { var tracks = getPlaylistTracks(uri).then(function (tracks) {
var resultArr = resultDict[uri] resultsToTables(tracks, PLAYLIST_TABLE, uri, 'return library.togglePlaylists();', true)
resultArr.uri = uri showLoading(false)
processAlbumResults(resultArr) })
updatePlayIcons(uri)
$('#playlistslist li a').each(function () {
$(this).removeClass('playlistactive')
if (this.id === uri) {
$(this).addClass('playlistactive')
}
})
return false
},
showArtist: function (nwuri) {
$('#popupQueue').popup('close')
$('#popupTracks').popup('close')
$('#controlsmodal').popup('close')
$(ARTIST_TABLE).empty()
// TODO cache
$('#h_artistname').html('')
showLoading(true)
mopidy.library.lookup({'uris': [nwuri]}).then(function (resultDict) {
var resultArr = resultDict[nwuri]
resultArr.uri = nwuri
processArtistResults(resultArr)
}, console.error)
switchContent('artists', nwuri)
scrollToTop()
return false
},
showAlbum: function (uri) {
$('#popupQueue').popup('close')
$('#popupTracks').popup('close')
$('#controlsmodal').popup('close')
$(ALBUM_TABLE).empty()
// fill from cache
var pl = getTracksFromUri(uri, true)
if (pl.length > 0) {
albumTracksToTable(pl, ALBUM_TABLE, uri)
var albumname = getAlbum(pl)
var artistname = getArtist(pl)
$('#h_albumname').html(albumname)
$('#h_albumartist').html(artistname)
$('#coverpopupalbumname').html(albumname)
$('#coverpopupartist').html(artistname)
showLoading(false)
mopidy.library.lookup({'uris': [uri]}).then(function (resultDict) {
var resultArr = resultDict[uri]
resultArr.uri = uri
processAlbumResults(resultArr)
}, console.error)
} else {
showLoading(true)
$('#h_albumname').html('')
$('#h_albumartist').html('')
mopidy.library.lookup({'uris': [uri]}).then(function (resultDict) {
var resultArr = resultDict[uri]
resultArr.uri = uri
processAlbumResults(resultArr)
}, console.error)
}
// show page
switchContent('albums', uri)
scrollToTop()
return false
},
getSearchSchemes: function () {
var backendName
var searchScheme = $.cookie('searchScheme')
if (searchScheme) {
searchScheme = searchScheme.replace(/"/g, '')
} else {
searchScheme = 'all'
}
$('#selectSearchService').empty()
$('#selectSearchService').append(new Option('All backends', 'all'))
mopidy.getUriSchemes().then(function (schemesArray) {
for (var i = 0; i < schemesArray.length; i++) {
backendName = getMediaHuman(schemesArray[i])
backendName = backendName.charAt(0).toUpperCase() + backendName.slice(1)
$('#selectSearchService').append(new Option(backendName, schemesArray[i]))
}
$('#selectSearchService').val(searchScheme)
$('#selectSearchService').selectmenu('refresh', true)
}, console.error) }, console.error)
} }
// show page
switchContent('albums', uri)
scrollToTop()
return false
} }
function getSearchSchemes () { // TODO: Remove this once JavaScript codebase has been completely modularized
mopidy.getUriSchemes().then( // in favour of bundling everything using 'browserify'.
function (schemesArray) { if (typeof exports !== 'undefined') {
var humanIndex if (typeof module !== 'undefined' && module.exports) {
$('#selectSearchService').children().remove().end() module.exports = library
$('#selectSearchService').append(new Option('All services', 'all')) }
for (var i = 0; i < schemesArray.length; i++) {
for (var j = 0; j < uriHumanList.length; j++) {
if (uriHumanList[j][0] === schemesArray[i].toLowerCase()) {
$('#selectSearchService').append(new Option(uriHumanList[j][1], schemesArray[i]))
}
}
}
$('#selectSearchService').selectmenu('refresh', true)
}, console.error
)
} }

View File

@ -78,7 +78,7 @@ function processPlaystate (data) {
function processBrowseDir (resultArr) { function processBrowseDir (resultArr) {
$(BROWSE_TABLE).empty() $(BROWSE_TABLE).empty()
if (browseStack.length > 0) { if (browseStack.length > 0) {
renderSongLiBackButton(resultArr, BROWSE_TABLE, 'return getBrowseDir();') renderSongLiBackButton(resultArr, BROWSE_TABLE, 'return library.getBrowseDir();')
} }
if (!resultArr || resultArr.length === 0) { if (!resultArr || resultArr.length === 0) {
showLoading(false) showLoading(false)
@ -113,7 +113,7 @@ function processBrowseDir (resultArr) {
iconClass = getMediaClass(resultArr[i].uri) iconClass = getMediaClass(resultArr[i].uri)
} }
$(BROWSE_TABLE).append( $(BROWSE_TABLE).append(
'<li><a href="#" onclick="return getBrowseDir(this.id);" id="' + resultArr[i].uri + '">' + '<li><a href="#" onclick="return library.getBrowseDir(this.id);" id="' + resultArr[i].uri + '">' +
'<h1><i class="' + iconClass + '"></i> ' + resultArr[i].name + '</h1></a></li>' '<h1><i class="' + iconClass + '"></i> ' + resultArr[i].name + '</h1></a></li>'
) )
} }
@ -163,7 +163,7 @@ function processGetPlaylists (resultArr) {
var starred = '' var starred = ''
for (var i = 0; i < resultArr.length; i++) { for (var i = 0; i < resultArr.length; i++) {
var li_html = '<li><a href="#" onclick="return showTracklist(this.id);" id="' + resultArr[i].uri + '">' var li_html = '<li><a href="#" onclick="return library.showTracklist(this.id);" id="' + resultArr[i].uri + '">'
if (isSpotifyStarredPlaylist(resultArr[i])) { if (isSpotifyStarredPlaylist(resultArr[i])) {
starred = li_html + '&#9733; Spotify Starred Tracks</a></li>' + tmp starred = li_html + '&#9733; Spotify Starred Tracks</a></li>' + tmp
} else if (isFavouritesPlaylist(resultArr[i])) { } else if (isFavouritesPlaylist(resultArr[i])) {

View File

@ -1,6 +1,6 @@
CACHE MANIFEST CACHE MANIFEST
# 2016-03-14:v1 # 2016-03-19:v1
NETWORK: NETWORK:
* *

View File

@ -12,12 +12,6 @@ var coverArt = require('../mopidy_musicbox_webclient/static/js/images.js')
var images var images
before(function () { before(function () {
html =
'<span id="songelapsed" class="pull-left"></span>' +
'<span id="songlength" class="pull-right"></span>'
$(document).ready(function () {
$(document.body).add(html)
})
mopidy = sinon.stub(new Mopidy({callingConvention: 'by-position-or-by-name'})) mopidy = sinon.stub(new Mopidy({callingConvention: 'by-position-or-by-name'}))
images = $('<img id="img_mock">') images = $('<img id="img_mock">')
}) })
@ -156,7 +150,7 @@ describe('CoverArt', function () {
var getInfoStub = sinon.stub(coverArt.lastfm.album, 'getInfo') var getInfoStub = sinon.stub(coverArt.lastfm.album, 'getInfo')
getInfoStub.yieldsTo('error', 'code', 'message') getInfoStub.yieldsTo('error', 'code', 'message')
var consoleSpy = sinon.spy(console, 'log') var consoleSpy = sinon.spy(console, 'error')
coverArt.getCoverFromLastFm(track, images, '') coverArt.getCoverFromLastFm(track, images, '')
assert(consoleSpy.calledOnce) assert(consoleSpy.calledOnce)
@ -190,7 +184,7 @@ describe('CoverArt', function () {
var getInfoStub = sinon.stub(coverArt.lastfm.artist, 'getInfo') var getInfoStub = sinon.stub(coverArt.lastfm.artist, 'getInfo')
getInfoStub.yieldsTo('error', 'code', 'message') getInfoStub.yieldsTo('error', 'code', 'message')
var consoleSpy = sinon.spy(console, 'log') var consoleSpy = sinon.spy(console, 'error')
coverArt.getArtistImage('mockArtist', images, 'small') coverArt.getArtistImage('mockArtist', images, 'small')
assert(consoleSpy.calledOnce) assert(consoleSpy.calledOnce)

54
tests/test_library.js Normal file
View File

@ -0,0 +1,54 @@
var chai = require('chai')
var should = chai.should()
var expect = chai.expect
var assert = chai.assert
chai.use(require('chai-string'))
chai.use(require('chai-jquery'))
var sinon = require('sinon')
var coverArt = require('../mopidy_musicbox_webclient/static/js/library.js')
var selectID = '#selectSearchService'
var schemesArray
before(function () {
$(document.body).append('<select id="selectSearchService"></select>')
$('#selectSearchService').selectmenu()
})
describe('Library', function () {
describe('#getSearchSchemes()', function () {
beforeEach(function () {
schemesArray = ['mockScheme1', 'mockScheme2', 'mockScheme3']
mopidy = {
getUriSchemes: function () { return $.when(schemesArray) }
}
$(selectID).empty()
})
it('should add human-readable options for backend schemes', function () {
uriHumanList = [['mockScheme2', 'mockUriHuman2']]
library.getSearchSchemes()
assert($(selectID).children().length === schemesArray.length + 1)
$(selectID).children(':eq(2)').should.have.text('MockUriHuman2')
})
it('should get default value from cookie', function () {
$.cookie('searchScheme', 'mockScheme3')
library.getSearchSchemes()
$(selectID + ' option:selected').should.have.value('mockScheme3')
})
it('should default to "all" backends if no cookie is available', function () {
$.removeCookie('searchScheme')
library.getSearchSchemes()
$(selectID + ' option:selected').should.have.value('all')
})
it('should capitalize first character of backend schema', function () {
library.getSearchSchemes()
$(selectID).children(':eq(1)').should.have.text('MockScheme1')
})
})
})