Modularise controls.js. Fixes 124
Make default click action user-configurable. Fixes #133. Optimise updating of now-playing icons. Fixes #184.
This commit is contained in:
parent
3dfbd76a83
commit
7daf5a7383
@ -7,5 +7,6 @@
|
|||||||
"indent": [2, 4, {"SwitchCase": 1}],
|
"indent": [2, 4, {"SwitchCase": 1}],
|
||||||
"no-undef": 0, // TODO: Set this to '2' once Javascript has been modularised.
|
"no-undef": 0, // TODO: Set this to '2' once Javascript has been modularised.
|
||||||
"no-unused-vars": 0, // TODO: Set this to '2' once Javascript has been modularised.
|
"no-unused-vars": 0, // TODO: Set this to '2' once Javascript has been modularised.
|
||||||
|
"camelcase": 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,12 +79,20 @@ v2.3.0 (UNRELEASED)
|
|||||||
(Addresses: `#130 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/130>`_).
|
(Addresses: `#130 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/130>`_).
|
||||||
- Upgrade Media Progress Timer to version 3.0.0.
|
- Upgrade Media Progress Timer to version 3.0.0.
|
||||||
- Now retrieves album cover and artist images using MusicBrainzID, if available.
|
- Now retrieves album cover and artist images using MusicBrainzID, if available.
|
||||||
|
- New configuration parameter ``on_track_click`` can be used to customize the action that is performed when the
|
||||||
|
user clicks on a track in a list. Valid options are: ``PLAY_NOW``, ``PLAY_NEXT``, ``ADD_THIS_BOTTOM``,
|
||||||
|
``ADD_ALL_BOTTOM``, ``PLAY_ALL`` (default), and ``DYNAMIC`` (repeats last action).
|
||||||
|
(Addresses: `#133 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/133>`_).
|
||||||
|
- Optimized updating of 'now playing' icons in tracklists.
|
||||||
|
(Addresses: `#184 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/184>`_).
|
||||||
|
|
||||||
**Fixes**
|
**Fixes**
|
||||||
|
|
||||||
- Don't create Mopidy models manually. (Fixes: `#172 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/172>`_).
|
- Don't create Mopidy models manually. (Fixes: `#172 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/172>`_).
|
||||||
- Context menu is now available for all tracks in browse pane. (Fixes: `#126 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/126>`_).
|
- Context menu is now available for all tracks in browse pane. (Fixes: `#126 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/126>`_).
|
||||||
- last.fm artist image lookups should now always return the correct image for similarly named artists.
|
- last.fm artist image lookups should now always return the correct image for similarly named artists.
|
||||||
|
- Ensure that browsed tracks are always added to the queue using the track URI rather than the track's position in the folder.
|
||||||
|
(Fixes: `#124 <https://github.com/pimusicbox/mopidy-musicbox-webclient/issues/124>`_).
|
||||||
|
|
||||||
v2.2.0 (2016-03-01)
|
v2.2.0 (2016-03-01)
|
||||||
-------------------
|
-------------------
|
||||||
|
|||||||
@ -22,6 +22,13 @@ class Extension(ext.Extension):
|
|||||||
schema['musicbox'] = config.Boolean(optional=True)
|
schema['musicbox'] = config.Boolean(optional=True)
|
||||||
schema['websocket_host'] = config.Hostname(optional=True)
|
schema['websocket_host'] = config.Hostname(optional=True)
|
||||||
schema['websocket_port'] = config.Port(optional=True)
|
schema['websocket_port'] = config.Port(optional=True)
|
||||||
|
schema['on_track_click'] = config.String(optional=True,
|
||||||
|
choices=['PLAY_NOW',
|
||||||
|
'PLAY_NEXT',
|
||||||
|
'ADD_THIS_BOTTOM',
|
||||||
|
'ADD_ALL_BOTTOM',
|
||||||
|
'PLAY_ALL',
|
||||||
|
'DYNAMIC'])
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
def setup(self, registry):
|
def setup(self, registry):
|
||||||
|
|||||||
@ -3,3 +3,4 @@ enabled = true
|
|||||||
musicbox = false
|
musicbox = false
|
||||||
websocket_host =
|
websocket_host =
|
||||||
websocket_port =
|
websocket_port =
|
||||||
|
on_track_click = PLAY_ALL
|
||||||
|
|||||||
@ -244,8 +244,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.smalldivider {
|
.smalldivider {
|
||||||
font-size: 25% !important;
|
font-size: 10%;
|
||||||
height: 5px !important;
|
height: 2px;
|
||||||
background-color: #ddd !important;
|
background-color: #ddd !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,6 +342,11 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popupArtistLi,
|
||||||
|
.popupAlbumLi {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
|
||||||
.popupArtistName,
|
.popupArtistName,
|
||||||
.popupTrackName,
|
.popupTrackName,
|
||||||
.popupAlbumName,
|
.popupAlbumName,
|
||||||
@ -361,7 +366,7 @@
|
|||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#controlspopupimage,
|
#albumCoverImg,
|
||||||
#coverpopupimage,
|
#coverpopupimage,
|
||||||
#artistpopupimage {
|
#artistpopupimage {
|
||||||
display: block;
|
display: block;
|
||||||
@ -372,16 +377,56 @@
|
|||||||
max-height: 90%;
|
max-height: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#popupTracksLv li,
|
/* Override to make buttons more visible in popups.*/
|
||||||
#popupQueueLv li,
|
#popupTracks .ui-btn-up-c,
|
||||||
#popupBrowseLv li {
|
#popupQueue .ui-btn-up-c {
|
||||||
border-bottom: 1px solid #aaa;
|
background: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
#popupTracksLv,
|
/* Custom icons for popup listviews - see http://demos.jquerymobile.com/1.3.2/#CustomIcons */
|
||||||
#popupQueueLv,
|
.ui-icon-playAll:after,
|
||||||
#popupBrowseLv li {
|
.ui-icon-play:after,
|
||||||
border: 1px solid #aaa;
|
.ui-icon-playNext:after,
|
||||||
|
.ui-icon-add:after,
|
||||||
|
.ui-icon-addAll:after,
|
||||||
|
.ui-icon-remove:after {
|
||||||
|
color: #34495e;
|
||||||
|
font-family: 'FontAwesome';
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-icon-playAll:after {
|
||||||
|
content: '\f050';
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-icon-play:after {
|
||||||
|
content: '\f04b';
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-icon-playNext:after {
|
||||||
|
content: '\f149';
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-icon-add:after {
|
||||||
|
content: '\f196';
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-icon-addAll:after {
|
||||||
|
content: '\f0fe';
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-icon-remove:after {
|
||||||
|
content: '\f00d';
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-icon-playAll,
|
||||||
|
.ui-icon-play,
|
||||||
|
.ui-icon-playNext,
|
||||||
|
.ui-icon-add,
|
||||||
|
.ui-icon-addAll,
|
||||||
|
.ui-icon-remove {
|
||||||
|
background-color: unset;
|
||||||
|
background-image: none;
|
||||||
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popupDialog {
|
.popupDialog {
|
||||||
@ -523,7 +568,7 @@ a {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#controlspopupimage {
|
#albumCoverImg {
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
max-height: 90%;
|
max-height: 90%;
|
||||||
margin-bottom: 3px;
|
margin-bottom: 3px;
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-websocket-url="{{websocketUrl}}" data-is-musicbox="{{isMusicBox}}" data-has-alarmclock="{{hasAlarmClock}}">
|
<body data-websocket-url="{{websocketUrl}}" data-is-musicbox="{{isMusicBox}}" data-has-alarmclock="{{hasAlarmClock}}" data-on-track-click="{{onTrackClick}}">
|
||||||
<div data-role="page" id="page" class="ui-responsive-panel" data-theme="c">
|
<div data-role="page" id="page" class="ui-responsive-panel" data-theme="c">
|
||||||
<div data-role="panel" id="panel" data-position="left" data-theme="a" data-display="reveal" data-position-fixed="true">
|
<div data-role="panel" id="panel" data-position="left" data-theme="a" data-display="reveal" data-position-fixed="true">
|
||||||
|
|
||||||
@ -87,7 +87,7 @@
|
|||||||
</li>
|
</li>
|
||||||
<li data-icon="false">
|
<li data-icon="false">
|
||||||
<div><!-- slider for volume -->
|
<div><!-- slider for volume -->
|
||||||
<a href="#" onclick="doMute(); return false;"><span title="Toggle mute"><i id="mutebt" class="fa fa-volume-up"></i></span></a>
|
<a href="#" onclick="controls.doMute(); return false;"><span title="Toggle mute"><i id="mutebt" class="fa fa-volume-up"></i></span></a>
|
||||||
<label for="volumeslider" class="ui-hidden-accessible">Volume</label>
|
<label for="volumeslider" class="ui-hidden-accessible">Volume</label>
|
||||||
<input id="volumeslider" data-highlight="true" name="volumeslider" data-mini="true" type="range" min="0"
|
<input id="volumeslider" data-highlight="true" name="volumeslider" data-mini="true" type="range" min="0"
|
||||||
value="0" max="100"/>
|
value="0" max="100"/>
|
||||||
@ -112,47 +112,28 @@
|
|||||||
<a href="#" onclick="closePopups();"><img id="artistpopupimage" src="" alt="Album artist"/></a>
|
<a href="#" onclick="closePopups();"><img id="artistpopupimage" src="" alt="Album artist"/></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-role="popup" data-transition="none" data-theme="c" id="popupBrowse">
|
<div data-role="popup" data-transition="none" data-theme="b" id="popupTracks">
|
||||||
<div data-role="collapsible-set">
|
|
||||||
<ul data-role="listview" data-icon="false" id="popupBrowseLv">
|
|
||||||
<li>
|
|
||||||
<a href="#" onclick="return playBrowsedTracks(PLAY_ALL);">Play All</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="#" onclick="return playBrowsedTracks(PLAY_NOW);">Play <span class="popupTrackName"></span></a>
|
|
||||||
</li>
|
|
||||||
<li class="addqueue">
|
|
||||||
<a href="#" onclick="return playBrowsedTracks(PLAY_NEXT);">Play Track Next</a>
|
|
||||||
</li>
|
|
||||||
<li class="addqueue">
|
|
||||||
<a href="#" onclick="return playBrowsedTracks(ADD_THIS_BOTTOM);">Add Track to Bottom of Queue</a>
|
|
||||||
</li>
|
|
||||||
<li class="addqueue">
|
|
||||||
<a href="#" onclick="return playBrowsedTracks(ADD_ALL_BOTTOM);">Add All to Bottom of Queue</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-role="popup" data-transition="none" data-theme="c" id="popupTracks">
|
|
||||||
<div data-role="collapsible-set">
|
<div data-role="collapsible-set">
|
||||||
<ul data-role="listview" data-icon="false" id="popupTracksLv">
|
<ul data-role="listview" data-icon="false" id="popupTracksLv">
|
||||||
<li>
|
<li data-icon="playAll" data-iconshadow="false">
|
||||||
<a href="#" onclick="return playTrack(PLAY_NOW);">Play <span class="popupTrackName"></span></a>
|
<a href="#" onclick="return controls.playTracks(PLAY_ALL, mopidy);">Play All</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="addqueue">
|
<li data-icon="play">
|
||||||
<a href="#" onclick="return playTrack(PLAY_NEXT);">Play Track Next</a>
|
<a href="#" onclick="return controls.playTracks(PLAY_NOW, mopidy);">Play <span class="popupTrackName"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li class="addqueue">
|
<li data-icon="playNext" class="addqueue">
|
||||||
<a href="#" onclick="return playTrack(ADD_THIS_BOTTOM);">Add Track to Bottom of Queue</a>
|
<a href="#" onclick="return controls.playTracks(PLAY_NEXT, mopidy);">Play Track Next</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="addqueue">
|
<li data-icon="add" class="addqueue">
|
||||||
<a href="#" onclick="return playTrack(ADD_ALL_BOTTOM);" id="liaddtobottom">Add all to Bottom of Queue</a>
|
<a href="#" onclick="return controls.playTracks(ADD_THIS_BOTTOM, mopidy);">Add Track to Bottom of Queue</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li data-icon="addAll" class="addqueue">
|
||||||
|
<a href="#" onclick="return controls.playTracks(ADD_ALL_BOTTOM, mopidy);">Add All to Bottom of Queue</a>
|
||||||
|
</li>
|
||||||
|
<li class="popupAlbumLi">
|
||||||
<a href="#" onclick="showAlbumPopup('#popupTracks')">Show Album <span class="popupAlbumName"></span></a>
|
<a href="#" onclick="showAlbumPopup('#popupTracks')">Show Album <span class="popupAlbumName"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li id="popupArtistsLi">
|
<li class="popupArtistsLi">
|
||||||
<a href="#" onclick="showArtist()" class="popupArtistHref">Show Artist <span class="popupArtistName"></span>
|
<a href="#" onclick="showArtist()" class="popupArtistHref">Show Artist <span class="popupArtistName"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -164,19 +145,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-role="popup" data-transition="none" data-theme="c" id="popupQueue">
|
<div data-role="popup" data-transition="none" data-theme="b" id="popupQueue">
|
||||||
<div data-role="collapsible-set">
|
<div data-role="collapsible-set">
|
||||||
<ul data-role="listview" data-icon="false" id="popupQueueLv">
|
<ul data-role="listview" data-icon="false" id="popupQueueLv">
|
||||||
<li>
|
<li data-icon="play">
|
||||||
<a href="#" onclick="return playTrackQueue();">Play <span class="popupTrackName"></span></a>
|
<a href="#" onclick="return controls.playQueueTrack();">Play <span class="popupTrackName"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li data-icon="remove">
|
||||||
<a href="#" onclick="return removeTrack();">Remove from Queue</a>
|
<a href="#" onclick="return controls.removeTrack();">Remove from Queue</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li class="popupAlbumLi">
|
||||||
<a href="#" onclick="showAlbumPopup('#popupQueue')">Show Album <span class="popupAlbumName"></span></a>
|
<a href="#" onclick="showAlbumPopup('#popupQueue')">Show Album <span class="popupAlbumName"></span></a>
|
||||||
</li>
|
</li>
|
||||||
<li id="popupArtistsLi">
|
<li class="popupArtistsLi">
|
||||||
<a href="#" onclick="showArtist()" class="popupArtistHref">Show Artist <span class="popupArtistName"></span>
|
<a href="#" onclick="showArtist()" class="popupArtistHref">Show Artist <span class="popupArtistName"></span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
@ -192,9 +173,9 @@
|
|||||||
<form>
|
<form>
|
||||||
<p>Save current queue to a playlist.
|
<p>Save current queue to a playlist.
|
||||||
<input id="saveinput" placeholder="Playlist name" class="span2" data-clear-btn="true"
|
<input id="saveinput" placeholder="Playlist name" class="span2" data-clear-btn="true"
|
||||||
onkeypress="return savePressed(event.keyCode);" type="text"/>
|
onkeypress="return controls.savePressed(event.keyCode);" type="text"/>
|
||||||
<div data-role="controlgroup" data-type="horizontal" align="center">
|
<div data-role="controlgroup" data-type="horizontal" align="center">
|
||||||
<button class="btn" type="button" onclick="return saveQueue();">
|
<button class="btn" type="button" onclick="return controls.saveQueue();">
|
||||||
Ok
|
Ok
|
||||||
</button>
|
</button>
|
||||||
<button class="btn" type="button" onclick="return $('#popupSave').popup('close');">
|
<button class="btn" type="button" onclick="return $('#popupSave').popup('close');">
|
||||||
@ -289,7 +270,7 @@
|
|||||||
|
|
||||||
<div id="nowPlayingpane" data-role="content" class="pane">
|
<div id="nowPlayingpane" data-role="content" class="pane">
|
||||||
|
|
||||||
<img id="controlspopupimage" src="images/default_cover.png" alt="Album cover"/>
|
<img id="albumCoverImg" src="images/default_cover.png" alt="Album cover"/>
|
||||||
|
|
||||||
<div class="nowPlaying-artistInfo">
|
<div class="nowPlaying-artistInfo">
|
||||||
<h3 id="modalname"></h3>
|
<h3 id="modalname"></h3>
|
||||||
@ -311,7 +292,7 @@
|
|||||||
<h4>Playlists</h4>
|
<h4>Playlists</h4>
|
||||||
</div>
|
</div>
|
||||||
<div align="right" class="ui-block-b" data-role="controlgroup" data-type="horizontal">
|
<div align="right" class="ui-block-b" data-role="controlgroup" data-type="horizontal">
|
||||||
<button class="btn" type="button" title="Refresh playlists" onclick="return refreshPlaylists();">
|
<button class="btn" type="button" title="Refresh playlists" onclick="return controls.refreshPlaylists();">
|
||||||
<i class="fa fa-refresh"></i>
|
<i class="fa fa-refresh"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -343,11 +324,11 @@
|
|||||||
<h4>Play Queue</h4>
|
<h4>Play Queue</h4>
|
||||||
</div>
|
</div>
|
||||||
<div align="right" class="ui-block-b" data-role="controlgroup" data-type="horizontal">
|
<div align="right" class="ui-block-b" data-role="controlgroup" data-type="horizontal">
|
||||||
<button class="btn" type="button" title="Save queue to playlist" onclick="return showSavePopup();">
|
<button class="btn" type="button" title="Save queue to playlist" onclick="return controls.showSavePopup();">
|
||||||
<i class="fa fa-floppy-o"></i>
|
<i class="fa fa-bookmark-o"></i>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn" type="button" title="Clear queue" onclick="return clearQueue();">
|
<button class="btn" type="button" title="Clear queue" onclick="return controls.clearQueue();">
|
||||||
<i class="fa fa-trash-o"></i>
|
<i class="fa fa-times"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -426,18 +407,18 @@
|
|||||||
<div class="ui-block-a" style="padding: 5px">
|
<div class="ui-block-a" style="padding: 5px">
|
||||||
<form>
|
<form>
|
||||||
<p>Play a specific stream/track and optionally save it to your favourites.
|
<p>Play a specific stream/track and optionally save it to your favourites.
|
||||||
<button class="btn" type="button" onclick="return getCurrentlyPlaying();">
|
<button class="btn" type="button" onclick="return controls.getCurrentlyPlaying();">
|
||||||
Get currently playing
|
Get currently playing
|
||||||
</button>
|
</button>
|
||||||
<input id="streamuriinput" placeholder="URI" class="span2" data-clear-btn="true"
|
<input id="streamuriinput" placeholder="URI" class="span2" data-clear-btn="true"
|
||||||
onkeypress="return streamPressed(event.keyCode);" type="text"/>
|
onkeypress="return controls.streamPressed(event.keyCode);" type="text"/>
|
||||||
<button class="btn" type="button" onclick="return playStreamUri();">
|
<button class="btn" type="button" onclick="return controls.playStreamUri();">
|
||||||
Play
|
Play
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<input id="streamnameinput" placeholder="Name" class="span2" data-clear-btn="true"
|
<input id="streamnameinput" placeholder="Name" class="span2" data-clear-btn="true"
|
||||||
onkeypress="return streamPressed(event.keyCode);" type="text"/>
|
onkeypress="return controls.streamPressed(event.keyCode);" type="text"/>
|
||||||
<button class="btn" type="button" onclick="return addFavourite();">
|
<button class="btn" type="button" onclick="return controls.addFavourite();">
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
<br/>
|
<br/>
|
||||||
@ -459,7 +440,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="playicon">
|
<div class="playicon">
|
||||||
<a href="#" onclick="doPlay(); return false"><span id="btplay" title="Play"><i class="fa fa-play"></i></span></a>
|
<a href="#" onclick="controls.doPlay(); return false"><span id="btplay" title="Play"><i class="fa fa-play"></i></span></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -467,16 +448,16 @@
|
|||||||
<div data-role="footer" data-tap-toggle="false" data-position="fixed" id="nowPlayingFooter">
|
<div data-role="footer" data-tap-toggle="false" data-position="fixed" id="nowPlayingFooter">
|
||||||
<div class="footerControls" style="padding-left: 10px;">
|
<div class="footerControls" style="padding-left: 10px;">
|
||||||
<div style="float: left;">
|
<div style="float: left;">
|
||||||
<a href="#" onclick="doPrevious(); return false"><span id="btprev" title="Previous"><i class="fa fa-fast-backward"></i></span></a>
|
<a href="#" onclick="controls.doPrevious(); return false"><span id="btprev" title="Previous"><i class="fa fa-fast-backward"></i></span></a>
|
||||||
<a href="#" onclick="doPlay(); return false"><span id="btplayNowPlaying" title="Play"><i class="fa fa-play"></i></span></a>
|
<a href="#" onclick="controls.doPlay(); return false"><span id="btplayNowPlaying" title="Play"><i class="fa fa-play"></i></span></a>
|
||||||
<a href="#" onclick="doNext(); return false"><span id="btnext" title="Next"><i class="fa fa-fast-forward"></i></span></a>
|
<a href="#" onclick="controls.doNext(); return false"><span id="btnext" title="Next"><i class="fa fa-fast-forward"></i></span></a>
|
||||||
</div>
|
</div>
|
||||||
<div style="float: right; margin-right: 10px;">
|
<div style="float: right; margin-right: 10px;">
|
||||||
<a href="#" onclick="doRandom(); return false"><span id="randombt" title="Random"><i class="fa fa-random"></i></span></a>
|
<a href="#" onclick="controls.doRandom(); return false"><span id="randombt" title="Random"><i class="fa fa-random"></i></span></a>
|
||||||
<a href="#" onclick="doRepeat(); return false"><span id="repeatbt" title="Repeat"><i class="fa fa-repeat"></i></span></a>
|
<a href="#" onclick="controls.doRepeat(); return false"><span id="repeatbt" title="Repeat"><i class="fa fa-repeat"></i></span></a>
|
||||||
<a href="#" onclick="doConsume(); return false"><span id="consumebt" title="Consume"><i class="fa fa-cutlery"></i></span></a>
|
<a href="#" onclick="controls.doConsume(); return false"><span id="consumebt" title="Consume"><i class="fa fa-cutlery"></i></span></a>
|
||||||
<a href="#" onclick="doSingle(); return false"><span id="singlebt" title="Single"><i class="fa fa-dot-circle-o"></i></span></a>
|
<a href="#" onclick="controls.doSingle(); return false"><span id="singlebt" title="Single"><i class="fa fa-dot-circle-o"></i></span></a>
|
||||||
<a href="#" onclick="doShuffle(); return false"><span id="shufflebt" title="Shuffle"><i class="fa fa-arrows-v"></i></span></a>
|
<a href="#" onclick="controls.doShuffle(); return false"><span id="shufflebt" title="Shuffle"><i class="fa fa-arrows-v"></i></span></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
1239
mopidy_musicbox_webclient/static/js/controls.js
vendored
1239
mopidy_musicbox_webclient/static/js/controls.js
vendored
File diff suppressed because it is too large
Load Diff
@ -20,7 +20,7 @@ var volumeSliding = false
|
|||||||
var positionChanging
|
var positionChanging
|
||||||
|
|
||||||
var initgui = true
|
var initgui = true
|
||||||
var popupData = {}
|
var popupData = {} // TODO: Refactor into one shared cache
|
||||||
var songlength = 0
|
var songlength = 0
|
||||||
|
|
||||||
var artistshtml = ''
|
var artistshtml = ''
|
||||||
@ -36,12 +36,11 @@ var STREAMS_PLAYLIST_SCHEME = 'm3u'
|
|||||||
var uriSchemes = {}
|
var uriSchemes = {}
|
||||||
|
|
||||||
// array of cached playlists (not only user-playlists, also search, artist, album-playlists)
|
// array of cached playlists (not only user-playlists, also search, artist, album-playlists)
|
||||||
var playlists = {}
|
var playlists = {} // TODO: Refactor into one shared cache
|
||||||
var currentplaylist
|
var currentplaylist
|
||||||
var customTracklists = []
|
var customTracklists = [] // TODO: Refactor into one shared cache
|
||||||
|
|
||||||
var browseStack = []
|
var browseStack = []
|
||||||
var browseTracks = []
|
|
||||||
|
|
||||||
var ua = navigator.userAgent
|
var ua = navigator.userAgent
|
||||||
var isMobileSafari = /Mac/.test(ua) && /Mobile/.test(ua)
|
var isMobileSafari = /Mac/.test(ua) && /Mobile/.test(ua)
|
||||||
@ -68,9 +67,7 @@ PLAY_NEXT = 1
|
|||||||
ADD_THIS_BOTTOM = 2
|
ADD_THIS_BOTTOM = 2
|
||||||
ADD_ALL_BOTTOM = 3
|
ADD_ALL_BOTTOM = 3
|
||||||
PLAY_ALL = 4
|
PLAY_ALL = 4
|
||||||
PLAY_NOW_SEARCH = 5
|
DYNAMIC = 5
|
||||||
|
|
||||||
MAX_TABLEROWS = 50
|
|
||||||
|
|
||||||
// the first part of Mopidy extensions which serve radio streams
|
// the first part of Mopidy extensions which serve radio streams
|
||||||
var radioExtensionsList = ['somafm', 'tunein', 'dirble', 'audioaddict']
|
var radioExtensionsList = ['somafm', 'tunein', 'dirble', 'audioaddict']
|
||||||
@ -185,9 +182,9 @@ function albumTracksToTable (pl, target, uri) {
|
|||||||
nextTrack = i < pl.length - 1 ? pl[i + 1] : undefined
|
nextTrack = i < pl.length - 1 ? pl[i + 1] : undefined
|
||||||
track = pl[i]
|
track = pl[i]
|
||||||
popupData[track.uri] = track
|
popupData[track.uri] = track
|
||||||
renderSongLi(previousTrack, track, nextTrack, uri, '', ALBUM_TABLE, i, pl.length)
|
renderSongLi(previousTrack, track, nextTrack, uri, '', target, i, pl.length)
|
||||||
}
|
}
|
||||||
updatePlayIcons(songdata.track.uri, songdata.tlid)
|
updatePlayIcons(songdata.track.uri, songdata.tlid, controls.getIconForAction())
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSongLi (previousTrack, track, nextTrack, uri, tlid, target, currentIndex, listLength) {
|
function renderSongLi (previousTrack, track, nextTrack, uri, tlid, target, currentIndex, listLength) {
|
||||||
@ -204,16 +201,14 @@ function renderSongLi (previousTrack, track, nextTrack, uri, tlid, target, curre
|
|||||||
$(target).append('<li class="albumli"><a href="#"><h1><i class="' + getMediaClass(track.uri) + '"></i> ' + track.name + ' [Stream]</h1></a></li>')
|
$(target).append('<li class="albumli"><a href="#"><h1><i class="' + getMediaClass(track.uri) + '"></i> ' + track.name + ' [Stream]</h1></a></li>')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Play by tlid if available.
|
|
||||||
// TODO: Need to consolidate all of the 'play...' functions
|
if (target === CURRENT_PLAYLIST_TABLE && typeof tlid === 'number' && tlid >= 0) { // Current queue: Show popup menu icon. onClick plays track.
|
||||||
if (tlid && target === BROWSE_TABLE) {
|
|
||||||
onClick = 'return playBrowsedTracks(PLAY_ALL, ' + tlid + ');'
|
|
||||||
} else if (tlid) {
|
|
||||||
tlidParameter = '\',\'' + tlid
|
tlidParameter = '\',\'' + tlid
|
||||||
onClick = 'return playTrackQueueByTlid(\'' + track.uri + '\',\'' + tlid + '\');'
|
onClick = 'return controls.playQueueTrack(' + tlid + ');'
|
||||||
} else {
|
} else { // All other tracklist: Show default action icon. onClick performs default action
|
||||||
onClick = 'return playTrackByUri(\'' + track.uri + '\',\'' + uri + '\');'
|
onClick = 'return controls.playTracks(\'\', mopidy, \'' + track.uri + '\', \'' + uri + '\');'
|
||||||
}
|
}
|
||||||
|
|
||||||
$(target).append(
|
$(target).append(
|
||||||
'<li class="song albumli" id="' + getjQueryID(target, track.uri) + '" tlid="' + tlid + '">' +
|
'<li class="song albumli" id="' + getjQueryID(target, track.uri) + '" tlid="' + tlid + '">' +
|
||||||
'<a href="#" class="moreBtn" onclick="return popupTracks(event, \'' + uri + '\',\'' + track.uri + tlidParameter + '\');">' +
|
'<a href="#" class="moreBtn" onclick="return popupTracks(event, \'' + uri + '\',\'' + track.uri + tlidParameter + '\');">' +
|
||||||
@ -223,8 +218,8 @@ function renderSongLi (previousTrack, track, nextTrack, uri, tlid, target, curre
|
|||||||
if (listLength === 1 || !hasSameAlbum(previousTrack, track) && !hasSameAlbum(track, nextTrack)) {
|
if (listLength === 1 || !hasSameAlbum(previousTrack, track) && !hasSameAlbum(track, nextTrack)) {
|
||||||
renderSongLiAlbumInfo(track, target)
|
renderSongLiAlbumInfo(track, target)
|
||||||
}
|
}
|
||||||
// TODO: remove this hard-coded condition for 'ALBUM_TABLE'
|
// TODO: remove this hard-coded conditions for 'ALBUM_TABLE' and 'BROWSE_TABLE'
|
||||||
if (target !== ALBUM_TABLE && !hasSameAlbum(previousTrack, track)) {
|
if (target !== ALBUM_TABLE && target !== BROWSE_TABLE && !hasSameAlbum(previousTrack, track)) {
|
||||||
// Starting to render a new album in the list.
|
// Starting to render a new album in the list.
|
||||||
renderSongLiDivider(track, nextTrack, currentIndex, target)
|
renderSongLiDivider(track, nextTrack, currentIndex, target)
|
||||||
}
|
}
|
||||||
@ -339,7 +334,7 @@ function resultsToTables (results, target, uri, onClickBack, backIsOptional) {
|
|||||||
renderSongLi(previousTrack, track, nextTrack, uri, tlid, target, i, results.length)
|
renderSongLi(previousTrack, track, nextTrack, uri, tlid, target, i, results.length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updatePlayIcons(songdata.track.uri, songdata.tlid)
|
updatePlayIcons(songdata.track.uri, songdata.tlid, controls.getIconForAction())
|
||||||
}
|
}
|
||||||
|
|
||||||
// process updated playlist to gui
|
// process updated playlist to gui
|
||||||
@ -397,7 +392,7 @@ function getUris (tracks) {
|
|||||||
|
|
||||||
function getTracksFromUri (uri, full_track_data) {
|
function getTracksFromUri (uri, full_track_data) {
|
||||||
var returnTracksOrUris = function (tracks) {
|
var returnTracksOrUris = function (tracks) {
|
||||||
return (full_track_data || false) ? tracks : getUris(tracks)
|
return full_track_data ? tracks : getUris(tracks)
|
||||||
}
|
}
|
||||||
if (customTracklists[uri]) {
|
if (customTracklists[uri]) {
|
||||||
return returnTracksOrUris(customTracklists[uri])
|
return returnTracksOrUris(customTracklists[uri])
|
||||||
@ -552,7 +547,7 @@ function getjQueryID (identifier, uri, includePrefix) {
|
|||||||
} else if (identifier.charAt(0) !== '#' && includePrefix) {
|
} else if (identifier.charAt(0) !== '#' && includePrefix) {
|
||||||
identifier = '#' + identifier
|
identifier = '#' + identifier
|
||||||
}
|
}
|
||||||
return identifier + '-' + fixedEncodeURIComponent(uri).replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '')
|
return identifier + '-' + fixedEncodeURIComponent(uri).replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '') // eslint-disable-line no-useless-escape
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strict URI encoding as per https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
|
// Strict URI encoding as per https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
* Song Info Sreen *
|
* Song Info Sreen *
|
||||||
********************/
|
********************/
|
||||||
function resetSong () {
|
function resetSong () {
|
||||||
setPlayState(false)
|
controls.setPlayState(false)
|
||||||
setPosition(0)
|
controls.setPosition(0)
|
||||||
var data = {}
|
var data = {}
|
||||||
data.tlid = -1
|
data.tlid = -1
|
||||||
data.track = {}
|
data.track = {}
|
||||||
@ -84,7 +84,7 @@ function setSongInfo (data) {
|
|||||||
data.track.name = decodeURI(name[name.length - 1])
|
data.track.name = decodeURI(name[name.length - 1])
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePlayIcons(data.track.uri, data.tlid)
|
updatePlayIcons(data.track.uri, data.tlid, controls.getIconForAction())
|
||||||
artistshtml = ''
|
artistshtml = ''
|
||||||
artiststext = ''
|
artiststext = ''
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ function setSongInfo (data) {
|
|||||||
} else {
|
} else {
|
||||||
$('#modalalbum').html('')
|
$('#modalalbum').html('')
|
||||||
}
|
}
|
||||||
images.setAlbumImage(data.track.uri, '#infocover, #controlspopupimage', mopidy)
|
images.setAlbumImage(data.track.uri, '#infocover, #albumCoverImg', mopidy)
|
||||||
|
|
||||||
$('#modalartist').html(arttmp)
|
$('#modalartist').html(arttmp)
|
||||||
|
|
||||||
@ -153,7 +153,6 @@ function closePopups () {
|
|||||||
$('#artistpopup').popup('close')
|
$('#artistpopup').popup('close')
|
||||||
$('#coverpopup').popup('close')
|
$('#coverpopup').popup('close')
|
||||||
$('#popupQueue').popup('close')
|
$('#popupQueue').popup('close')
|
||||||
$('#controlspopup').popup('close')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function popupTracks (e, listuri, trackuri, tlid) {
|
function popupTracks (e, listuri, trackuri, tlid) {
|
||||||
@ -163,6 +162,9 @@ function popupTracks (e, listuri, trackuri, tlid) {
|
|||||||
$('.popupTrackName').html(popupData[trackuri].name)
|
$('.popupTrackName').html(popupData[trackuri].name)
|
||||||
if (popupData[trackuri].album && popupData[trackuri].album.name) {
|
if (popupData[trackuri].album && popupData[trackuri].album.name) {
|
||||||
$('.popupAlbumName').html(popupData[trackuri].album.name)
|
$('.popupAlbumName').html(popupData[trackuri].album.name)
|
||||||
|
$('.popupAlbumLi').show()
|
||||||
|
} else {
|
||||||
|
$('.popupAlbumLi').hide()
|
||||||
}
|
}
|
||||||
var child = ''
|
var child = ''
|
||||||
|
|
||||||
@ -194,9 +196,6 @@ function popupTracks (e, listuri, trackuri, tlid) {
|
|||||||
if (divid === 'current') {
|
if (divid === 'current') {
|
||||||
$('.addqueue').hide()
|
$('.addqueue').hide()
|
||||||
popupName = '#popupQueue'
|
popupName = '#popupQueue'
|
||||||
} else if (divid === 'browse') {
|
|
||||||
$('.addqueue').show()
|
|
||||||
popupName = '#popupBrowse'
|
|
||||||
} else {
|
} else {
|
||||||
$('.addqueue').show()
|
$('.addqueue').show()
|
||||||
popupName = '#popupTracks'
|
popupName = '#popupTracks'
|
||||||
@ -232,8 +231,8 @@ function initSocketevents () {
|
|||||||
library.getCurrentPlaylist()
|
library.getCurrentPlaylist()
|
||||||
updateStatusOfAll()
|
updateStatusOfAll()
|
||||||
library.getPlaylists()
|
library.getPlaylists()
|
||||||
getUriSchemes().then(function () {
|
controls.getUriSchemes().then(function () {
|
||||||
showFavourites()
|
controls.showFavourites()
|
||||||
})
|
})
|
||||||
library.getBrowseDir()
|
library.getBrowseDir()
|
||||||
library.getSearchSchemes(searchBlacklist, mopidy)
|
library.getSearchSchemes(searchBlacklist, mopidy)
|
||||||
@ -250,7 +249,7 @@ function initSocketevents () {
|
|||||||
|
|
||||||
mopidy.on('event:trackPlaybackStarted', function (data) {
|
mopidy.on('event:trackPlaybackStarted', function (data) {
|
||||||
setSongInfo(data.tl_track)
|
setSongInfo(data.tl_track)
|
||||||
setPlayState(true)
|
controls.setPlayState(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
mopidy.on('event:playlistsLoaded', function (data) {
|
mopidy.on('event:playlistsLoaded', function (data) {
|
||||||
@ -273,21 +272,21 @@ function initSocketevents () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
mopidy.on('event:volumeChanged', function (data) {
|
mopidy.on('event:volumeChanged', function (data) {
|
||||||
setVolume(data.volume)
|
controls.setVolume(data.volume)
|
||||||
})
|
})
|
||||||
|
|
||||||
mopidy.on('event:muteChanged', function (data) {
|
mopidy.on('event:muteChanged', function (data) {
|
||||||
setMute(data.mute)
|
controls.setMute(data.mute)
|
||||||
})
|
})
|
||||||
|
|
||||||
mopidy.on('event:playbackStateChanged', function (data) {
|
mopidy.on('event:playbackStateChanged', function (data) {
|
||||||
switch (data.new_state) {
|
switch (data.new_state) {
|
||||||
case 'paused':
|
case 'paused':
|
||||||
case 'stopped':
|
case 'stopped':
|
||||||
setPlayState(false)
|
controls.setPlayState(false)
|
||||||
break
|
break
|
||||||
case 'playing':
|
case 'playing':
|
||||||
setPlayState(true)
|
controls.setPlayState(true)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -297,7 +296,7 @@ function initSocketevents () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
mopidy.on('event:seeked', function (data) {
|
mopidy.on('event:seeked', function (data) {
|
||||||
setPosition(parseInt(data.time_position))
|
controls.setPosition(parseInt(data.time_position))
|
||||||
if (play) {
|
if (play) {
|
||||||
syncedProgressTimer.start()
|
syncedProgressTimer.start()
|
||||||
}
|
}
|
||||||
@ -420,9 +419,7 @@ function locationHashChanged () {
|
|||||||
case 'search':
|
case 'search':
|
||||||
$('#navsearch a').addClass($.mobile.activeBtnClass)
|
$('#navsearch a').addClass($.mobile.activeBtnClass)
|
||||||
$('#searchinput').focus()
|
$('#searchinput').focus()
|
||||||
if (customTracklists['mbw:allresultscache'] === '') {
|
library.initSearch($('#searchinput').val())
|
||||||
library.initSearch($('#searchinput').val())
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
case 'stream':
|
case 'stream':
|
||||||
$('#navstream a').addClass('ui-state-active ui-state-persist ui-btn-active')
|
$('#navstream a').addClass('ui-state-active ui-state-persist ui-btn-active')
|
||||||
@ -519,7 +516,7 @@ $(document).ready(function (event) {
|
|||||||
$('#songinfo').click(function () {
|
$('#songinfo').click(function () {
|
||||||
return switchContent('nowPlaying')
|
return switchContent('nowPlaying')
|
||||||
})
|
})
|
||||||
$('#controlspopupimage').click(function () {
|
$('#albumCoverImg').click(function () {
|
||||||
return switchContent('current')
|
return switchContent('current')
|
||||||
})
|
})
|
||||||
$('#navToggleFullscreen').click(function () {
|
$('#navToggleFullscreen').click(function () {
|
||||||
@ -559,15 +556,15 @@ $(document).ready(function (event) {
|
|||||||
var actualkey = String.fromCharCode(unicode)
|
var actualkey = String.fromCharCode(unicode)
|
||||||
switch (actualkey) {
|
switch (actualkey) {
|
||||||
case ' ':
|
case ' ':
|
||||||
doPlay()
|
controls.doPlay()
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
break
|
break
|
||||||
case '>':
|
case '>':
|
||||||
doNext()
|
controls.doNext()
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
break
|
break
|
||||||
case '<':
|
case '<':
|
||||||
doPrevious()
|
controls.doPrevious()
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -586,8 +583,8 @@ $(document).ready(function (event) {
|
|||||||
$.event.special.swipe.durationThreshold = 500
|
$.event.special.swipe.durationThreshold = 500
|
||||||
|
|
||||||
// swipe songinfo and panel
|
// swipe songinfo and panel
|
||||||
$('#normalFooter, #nowPlayingFooter').on('swiperight', doPrevious)
|
$('#normalFooter, #nowPlayingFooter').on('swiperight', controls.doPrevious)
|
||||||
$('#normalFooter, #nowPlayingFooter').on('swipeleft', doNext)
|
$('#normalFooter, #nowPlayingFooter').on('swipeleft', controls.doNext)
|
||||||
$('#nowPlayingpane, .ui-body-c, #header, #panel, .pane').on('swiperight', function (event) {
|
$('#nowPlayingpane, .ui-body-c, #header, #panel, .pane').on('swiperight', function (event) {
|
||||||
if (!$(event.target).is('#normalFooter') && !$(event.target).is('#nowPlayingFooter')) {
|
if (!$(event.target).is('#normalFooter') && !$(event.target).is('#nowPlayingFooter')) {
|
||||||
$('#panel').panel('open')
|
$('#panel').panel('open')
|
||||||
@ -609,37 +606,55 @@ $(document).ready(function (event) {
|
|||||||
$('#trackslider').on('slidestop', function () {
|
$('#trackslider').on('slidestop', function () {
|
||||||
$('#trackslider').off('change')
|
$('#trackslider').off('change')
|
||||||
syncedProgressTimer.updatePosition($(this).val())
|
syncedProgressTimer.updatePosition($(this).val())
|
||||||
doSeekPos($(this).val())
|
controls.doSeekPos($(this).val())
|
||||||
})
|
})
|
||||||
|
|
||||||
$('#volumeslider').on('slidestart', function () { volumeSliding = true })
|
$('#volumeslider').on('slidestart', function () { volumeSliding = true })
|
||||||
$('#volumeslider').on('slidestop', function () { volumeSliding = false })
|
$('#volumeslider').on('slidestop', function () { volumeSliding = false })
|
||||||
$('#volumeslider').on('change', function () { doVolume($(this).val()) })
|
$('#volumeslider').on('change', function () { controls.doVolume($(this).val()) })
|
||||||
})
|
})
|
||||||
|
|
||||||
function updatePlayIcons (uri, tlid) {
|
function updatePlayIcons (uri, tlid, popupMenuIcon) {
|
||||||
// Update styles of listviews
|
// Update styles of listviews
|
||||||
|
if (arguments.length < 3) {
|
||||||
|
throw new Error('Missing parameters for "updatePlayIcons" function call.')
|
||||||
|
}
|
||||||
var listviews = [PLAYLIST_TABLE, SEARCH_TRACK_TABLE, ARTIST_TABLE, ALBUM_TABLE, BROWSE_TABLE]
|
var listviews = [PLAYLIST_TABLE, SEARCH_TRACK_TABLE, ARTIST_TABLE, ALBUM_TABLE, BROWSE_TABLE]
|
||||||
var target = CURRENT_PLAYLIST_TABLE.substr(1)
|
var target = CURRENT_PLAYLIST_TABLE.substr(1)
|
||||||
$(CURRENT_PLAYLIST_TABLE).children('li').each(function () {
|
if (uri && typeof tlid === 'number' && tlid >= 0) {
|
||||||
var eachTlid = $(this).attr('tlid')
|
$(CURRENT_PLAYLIST_TABLE).children('li.song.albumli').each(function () {
|
||||||
if (typeof eachTlid !== 'undefined') {
|
var eachTlid = $(this).attr('tlid')
|
||||||
eachTlid = parseInt(eachTlid)
|
if (typeof eachTlid !== 'undefined') {
|
||||||
}
|
eachTlid = parseInt(eachTlid)
|
||||||
if (this.id === getjQueryID(target, uri) && eachTlid === tlid) {
|
}
|
||||||
$(this).addClass('currenttrack')
|
if (this.id === getjQueryID(target, uri) && eachTlid === tlid) {
|
||||||
} else {
|
if (!$(this).hasClass('currenttrack')) {
|
||||||
$(this).removeClass('currenttrack')
|
$(this).addClass('currenttrack')
|
||||||
}
|
}
|
||||||
})
|
} else if ($(this).hasClass('currenttrack')) {
|
||||||
|
$(this).removeClass('currenttrack')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var popupElement
|
||||||
|
|
||||||
for (var i = 0; i < listviews.length; i++) {
|
for (var i = 0; i < listviews.length; i++) {
|
||||||
target = listviews[i].substr(1)
|
target = listviews[i].substr(1)
|
||||||
$(listviews[i]).children('li').each(function () {
|
$(listviews[i]).children('li.song.albumli').each(function () {
|
||||||
if (this.id === getjQueryID(target, uri)) {
|
if (uri) {
|
||||||
$(this).addClass('currenttrack2')
|
if (this.id === getjQueryID(target, uri)) {
|
||||||
} else {
|
$(this).addClass('currenttrack2')
|
||||||
$(this).removeClass('currenttrack2')
|
} else {
|
||||||
|
$(this).removeClass('currenttrack2')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (popupMenuIcon) {
|
||||||
|
popupElement = $(this).children('a.moreBtn').children('i').first()
|
||||||
|
if (!popupElement.hasClass(popupMenuIcon)) {
|
||||||
|
popupElement.removeClass()
|
||||||
|
popupElement.addClass(popupMenuIcon)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,9 +37,6 @@
|
|||||||
document.activeElement.blur()
|
document.activeElement.blur()
|
||||||
$('input').blur()
|
$('input').blur()
|
||||||
|
|
||||||
delete customTracklists[URI_SCHEME + ':allresultscache']
|
|
||||||
delete customTracklists[URI_SCHEME + ':artistresultscache']
|
|
||||||
delete customTracklists[URI_SCHEME + ':albumresultscache']
|
|
||||||
delete customTracklists[URI_SCHEME + ':trackresultscache']
|
delete customTracklists[URI_SCHEME + ':trackresultscache']
|
||||||
$('#searchartists').hide()
|
$('#searchartists').hide()
|
||||||
$('#searchalbums').hide()
|
$('#searchalbums').hide()
|
||||||
@ -258,7 +255,7 @@
|
|||||||
resultsToTables(tracks, PLAYLIST_TABLE, uri, 'return library.togglePlaylists();', true)
|
resultsToTables(tracks, PLAYLIST_TABLE, uri, 'return library.togglePlaylists();', true)
|
||||||
showLoading(false)
|
showLoading(false)
|
||||||
})
|
})
|
||||||
updatePlayIcons(uri)
|
updatePlayIcons(uri, '', controls.getIconForAction())
|
||||||
$('#playlistslist li a').each(function () {
|
$('#playlistslist li a').each(function () {
|
||||||
$(this).removeClass('playlistactive')
|
$(this).removeClass('playlistactive')
|
||||||
if (this.id === uri) {
|
if (this.id === uri) {
|
||||||
|
|||||||
@ -16,49 +16,49 @@ function processCurrenttrack (data) {
|
|||||||
* process results of volume
|
* process results of volume
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
function processVolume (data) {
|
function processVolume (data) {
|
||||||
setVolume(data)
|
controls.setVolume(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ******************************************************
|
/** ******************************************************
|
||||||
* process results of mute
|
* process results of mute
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
function processMute (data) {
|
function processMute (data) {
|
||||||
setMute(data)
|
controls.setMute(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ******************************************************
|
/** ******************************************************
|
||||||
* process results of a repeat
|
* process results of a repeat
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
function processRepeat (data) {
|
function processRepeat (data) {
|
||||||
setRepeat(data)
|
controls.setRepeat(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ******************************************************
|
/** ******************************************************
|
||||||
* process results of random
|
* process results of random
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
function processRandom (data) {
|
function processRandom (data) {
|
||||||
setRandom(data)
|
controls.setRandom(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ******************************************************
|
/** ******************************************************
|
||||||
* process results of consume
|
* process results of consume
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
function processConsume (data) {
|
function processConsume (data) {
|
||||||
setConsume(data)
|
controls.setConsume(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ******************************************************
|
/** ******************************************************
|
||||||
* process results of single
|
* process results of single
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
function processSingle (data) {
|
function processSingle (data) {
|
||||||
setSingle(data)
|
controls.setSingle(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ******************************************************
|
/** ******************************************************
|
||||||
* process results of current position
|
* process results of current position
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
function processCurrentposition (data) {
|
function processCurrentposition (data) {
|
||||||
setPosition(parseInt(data))
|
controls.setPosition(parseInt(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ******************************************************
|
/** ******************************************************
|
||||||
@ -66,9 +66,9 @@ function processCurrentposition (data) {
|
|||||||
*********************************************************/
|
*********************************************************/
|
||||||
function processPlaystate (data) {
|
function processPlaystate (data) {
|
||||||
if (data === 'playing') {
|
if (data === 'playing') {
|
||||||
setPlayState(true)
|
controls.setPlayState(true)
|
||||||
} else {
|
} else {
|
||||||
setPlayState(false)
|
controls.setPlayState(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,26 +84,24 @@ function processBrowseDir (resultArr) {
|
|||||||
showLoading(false)
|
showLoading(false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
browseTracks = []
|
|
||||||
uris = []
|
uris = []
|
||||||
var ref, track, previousTrack, nextTrack
|
var ref, previousRef, nextRef
|
||||||
var uri = resultArr[0].uri
|
var uri = resultArr[0].uri
|
||||||
var length = 0 || resultArr.length
|
var length = 0 || resultArr.length
|
||||||
|
customTracklists[BROWSE_TABLE] = []
|
||||||
|
|
||||||
for (var i = 0, index = 0; i < resultArr.length; i++) {
|
for (var i = 0, index = 0; i < resultArr.length; i++) {
|
||||||
if (resultArr[i].type === 'track') {
|
if (resultArr[i].type === 'track') {
|
||||||
|
previousRef = ref || undefined
|
||||||
|
nextRef = i < resultArr.length - 1 ? resultArr[i + 1] : undefined
|
||||||
ref = resultArr[i]
|
ref = resultArr[i]
|
||||||
|
// TODO: consolidate usage of various arrays for caching URIs, Refs, and Tracks
|
||||||
popupData[ref.uri] = ref
|
popupData[ref.uri] = ref
|
||||||
browseTracks.push(ref)
|
customTracklists[BROWSE_TABLE].push(ref)
|
||||||
uris.push(ref.uri)
|
uris.push(ref.uri)
|
||||||
|
|
||||||
$(BROWSE_TABLE).append(
|
renderSongLi(previousRef, ref, nextRef, BROWSE_TABLE, '', BROWSE_TABLE, index, resultArr.length)
|
||||||
'<li class="song albumli" id="' + getjQueryID(BROWSE_TABLE, ref.uri) + '">' +
|
|
||||||
'<a href="#" class="moreBtn" onclick="return popupTracks(event, \'' + uri + '\', \'' + ref.uri + '\', \'' + index + '\');">' +
|
|
||||||
'<i class="fa fa-ellipsis-v"></i></a>' +
|
|
||||||
'<a href="#" class="browsetrack" onclick="return playBrowsedTracks(PLAY_ALL, ' + index + ');">' +
|
|
||||||
'<h1><i></i> ' + ref.name + '</h1></a></li>'
|
|
||||||
)
|
|
||||||
index++
|
index++
|
||||||
} else {
|
} else {
|
||||||
var iconClass = ''
|
var iconClass = ''
|
||||||
@ -119,7 +117,7 @@ function processBrowseDir (resultArr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePlayIcons(songdata.track.uri, songdata.tlid)
|
updatePlayIcons(songdata.track.uri, songdata.tlid, controls.getIconForAction())
|
||||||
|
|
||||||
if (uris.length > 0) {
|
if (uris.length > 0) {
|
||||||
mopidy.library.lookup({'uris': uris}).then(function (resultDict) {
|
mopidy.library.lookup({'uris': uris}).then(function (resultDict) {
|
||||||
@ -134,6 +132,7 @@ function processBrowseDir (resultArr) {
|
|||||||
nextTrack = undefined
|
nextTrack = undefined
|
||||||
}
|
}
|
||||||
track = resultDict[ref.uri][0]
|
track = resultDict[ref.uri][0]
|
||||||
|
popupData[track.uri] = track // Need full track info in popups in order to display albums and artists.
|
||||||
if (uris.length === 1 || (previousTrack && !hasSameAlbum(previousTrack, track) && !hasSameAlbum(track, nextTrack))) {
|
if (uris.length === 1 || (previousTrack && !hasSameAlbum(previousTrack, track) && !hasSameAlbum(track, nextTrack))) {
|
||||||
renderSongLiAlbumInfo(track, BROWSE_TABLE)
|
renderSongLiAlbumInfo(track, BROWSE_TABLE)
|
||||||
}
|
}
|
||||||
@ -211,7 +210,7 @@ function processCurrentPlaylist (resultArr) {
|
|||||||
currentplaylist = resultArr
|
currentplaylist = resultArr
|
||||||
resultsToTables(currentplaylist, CURRENT_PLAYLIST_TABLE)
|
resultsToTables(currentplaylist, CURRENT_PLAYLIST_TABLE)
|
||||||
mopidy.playback.getCurrentTlTrack().then(processCurrenttrack, console.error)
|
mopidy.playback.getCurrentTlTrack().then(processCurrenttrack, console.error)
|
||||||
updatePlayIcons(songdata.track.uri, songdata.tlid)
|
updatePlayIcons(songdata.track.uri, songdata.tlid, controls.getIconForAction())
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ******************************************************
|
/** ******************************************************
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
CACHE MANIFEST
|
CACHE MANIFEST
|
||||||
|
|
||||||
# 2016-04-12:v1
|
# 2016-04-26:v2
|
||||||
|
|
||||||
NETWORK:
|
NETWORK:
|
||||||
*
|
*
|
||||||
|
|||||||
@ -23,8 +23,8 @@
|
|||||||
|
|
||||||
<div data-role="content" data-theme="b">
|
<div data-role="content" data-theme="b">
|
||||||
<h3>System</h3>
|
<h3>System</h3>
|
||||||
<a href="#" onclick="haltSystem(); return false;" data-role="button" data-rel="dialog" data-transition="slidedown" data-theme="b">Shutdown</a>
|
<a href="#" onclick="controls.haltSystem(); return false;" data-role="button" data-rel="dialog" data-transition="slidedown" data-theme="b">Shutdown</a>
|
||||||
<a href="#" onclick="rebootSystem(); return false;" data-role="button" data-rel="dialog" data-transition="slidedown" data-theme="b">Reboot</a>
|
<a href="#" onclick="controls.rebootSystem(); return false;" data-role="button" data-rel="dialog" data-transition="slidedown" data-theme="b">Reboot</a>
|
||||||
<a href="index.html" data-role="button" data-rel="back" data-theme="a">Cancel</a>
|
<a href="index.html" data-role="button" data-rel="back" data-theme="a">Cancel</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -37,6 +37,7 @@ class IndexHandler(tornado.web.RequestHandler):
|
|||||||
'isMusicBox': json.dumps(webclient.is_music_box()),
|
'isMusicBox': json.dumps(webclient.is_music_box()),
|
||||||
'websocketUrl': webclient.get_websocket_url(self.request),
|
'websocketUrl': webclient.get_websocket_url(self.request),
|
||||||
'hasAlarmClock': json.dumps(webclient.has_alarm_clock()),
|
'hasAlarmClock': json.dumps(webclient.has_alarm_clock()),
|
||||||
|
'onTrackClick': webclient.get_default_click_action()
|
||||||
}
|
}
|
||||||
self.__path = path
|
self.__path = path
|
||||||
self.__title = string.Template('MusicBox on $hostname')
|
self.__title = string.Template('MusicBox on $hostname')
|
||||||
|
|||||||
@ -44,3 +44,6 @@ class Webclient(object):
|
|||||||
|
|
||||||
def is_music_box(self):
|
def is_music_box(self):
|
||||||
return self.ext_config.get('musicbox', False)
|
return self.ext_config.get('musicbox', False)
|
||||||
|
|
||||||
|
def get_default_click_action(self):
|
||||||
|
return self.ext_config.get('on_track_click', 'PLAY_ALL')
|
||||||
|
|||||||
108
tests/js/dummy_tracklist.js
Normal file
108
tests/js/dummy_tracklist.js
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
(function (root, factory) {
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
define([], factory)
|
||||||
|
} else if (typeof module === 'object' && module.exports) {
|
||||||
|
module.exports = factory()
|
||||||
|
} else {
|
||||||
|
root.DummyTracklist = factory()
|
||||||
|
}
|
||||||
|
}(this, function () {
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
/* A dummy tracklist with partial support for mocking mopidy.core.TracklistController.
|
||||||
|
*
|
||||||
|
* Returns resolved promises to simulate functionality of Mopidy.js.
|
||||||
|
*/
|
||||||
|
function DummyTracklist () {
|
||||||
|
if (!(this instanceof DummyTracklist)) {
|
||||||
|
return new DummyTracklist()
|
||||||
|
}
|
||||||
|
this._tlTracks = []
|
||||||
|
this._nextTlid = 1
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add tracks to the tracklist. params.uris should contain an array of strings for the URIs to be added. */
|
||||||
|
DummyTracklist.prototype.add = function (params) {
|
||||||
|
if (!params || !params.uris) {
|
||||||
|
throw new Error('No tracks provided to add.')
|
||||||
|
}
|
||||||
|
if (params.tracks || params.uri) {
|
||||||
|
throw new Error('DummyTracklist.add does not support deprecated "tracks" and "uri" parameters.')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add tracks to end of tracklist if no position is provided
|
||||||
|
params.at_position = params.at_position || this._tlTracks.length
|
||||||
|
var tlTrack
|
||||||
|
var tlTracks = []
|
||||||
|
for (var i = 0; i < params.uris.length; i++) {
|
||||||
|
tlTrack = {
|
||||||
|
tlid: this._nextTlid++,
|
||||||
|
track: {
|
||||||
|
uri: params.uris[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tlTracks.push(tlTrack)
|
||||||
|
this._tlTracks.splice(params.at_position + i, 0, tlTrack)
|
||||||
|
}
|
||||||
|
|
||||||
|
return $.when(tlTracks)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clears the tracklist */
|
||||||
|
DummyTracklist.prototype.clear = function () {
|
||||||
|
this._tlTracks = []
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retuns a list containing tlTracks that contain the provided
|
||||||
|
* criteria.uri or has ID criteria.tlid.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
DummyTracklist.prototype.filter = function (criteria) {
|
||||||
|
if (!criteria || (!criteria.uri && !criteria.tlid)) {
|
||||||
|
throw new Error('No URI or tracklist ID provided to filter on.')
|
||||||
|
}
|
||||||
|
|
||||||
|
var matches = []
|
||||||
|
if (criteria.uri) { // Look for matching URIs
|
||||||
|
for (var i = 0; i < criteria.uri.length; i++) {
|
||||||
|
for (var j = 0; j < this._tlTracks.length; j++) {
|
||||||
|
if (this._tlTracks[j].track.uri === criteria.uri[i]) {
|
||||||
|
matches.push(this._tlTracks[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (criteria.tlid) { // Look for matching tracklist IDs
|
||||||
|
for (i = 0; i < criteria.tlid.length; i++) {
|
||||||
|
for (j = 0; j < this._tlTracks.length; j++) {
|
||||||
|
if (this._tlTracks[j].tlid === criteria.tlid[i]) {
|
||||||
|
matches.push(this._tlTracks[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $.when(matches)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retuns index of the currently 'playing' track. */
|
||||||
|
DummyTracklist.prototype.index = function (params) {
|
||||||
|
if (!params) {
|
||||||
|
if (this._tlTracks.length > 1) {
|
||||||
|
// Always just assume that the second track is playing
|
||||||
|
return $.when(1)
|
||||||
|
} else {
|
||||||
|
return $.when(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var i = 0; i < this._tlTracks.length; i++) {
|
||||||
|
if (this._tlTracks[i].tlid === params.tlid || (params.tl_track && params.tl_track.tlid === this._tlTracks[i].tlid)) {
|
||||||
|
return $.when(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $.when(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return DummyTracklist
|
||||||
|
}))
|
||||||
265
tests/js/test_controls.js
Normal file
265
tests/js/test_controls.js
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
var chai = require('chai')
|
||||||
|
var expect = chai.expect
|
||||||
|
var assert = chai.assert
|
||||||
|
chai.use(require('chai-string'))
|
||||||
|
chai.use(require('chai-jquery'))
|
||||||
|
|
||||||
|
var sinon = require('sinon')
|
||||||
|
|
||||||
|
var controls = require('../../mopidy_musicbox_webclient/static/js/controls.js')
|
||||||
|
var DummyTracklist = require('./dummy_tracklist.js')
|
||||||
|
|
||||||
|
describe('controls', function () {
|
||||||
|
var mopidy
|
||||||
|
var div_element
|
||||||
|
var QUEUE_TRACKS = [ // Simulate an existing queue with three tracks loaded.
|
||||||
|
{uri: 'track:tlTrackMock1'},
|
||||||
|
{uri: 'track:tlTrackMock2'},
|
||||||
|
{uri: 'track:tlTrackMock3'}
|
||||||
|
]
|
||||||
|
var NEW_TRACKS = [ // Simulate the user browsing to a folder with three tracks inside it.
|
||||||
|
{uri: 'track:trackMock1'},
|
||||||
|
{uri: 'tunein:track:trackMock2'}, // Stream
|
||||||
|
{uri: 'track:trackMock3'}
|
||||||
|
]
|
||||||
|
var addSpy
|
||||||
|
|
||||||
|
before(function () {
|
||||||
|
$(document.body).append('<div data-role="popup" id="popupTracks"></div>')
|
||||||
|
$('#popupTracks').popup() // Initialize popup
|
||||||
|
$(document.body).data('on-track-click', 'PLAY_ALL') // Set default click action
|
||||||
|
|
||||||
|
mopidy = sinon.stub(new Mopidy({callingConvention: 'by-position-or-by-name'}))
|
||||||
|
|
||||||
|
var playback = {
|
||||||
|
play: sinon.stub(),
|
||||||
|
stop: sinon.stub()
|
||||||
|
|
||||||
|
}
|
||||||
|
mopidy.playback = playback
|
||||||
|
mopidy.playback.stop.returns($.when())
|
||||||
|
// Mock the Mopidy tracklist so that we have a predictable state to test against.
|
||||||
|
mopidy.tracklist = new DummyTracklist()
|
||||||
|
addSpy = sinon.spy(mopidy.tracklist, 'add')
|
||||||
|
clearSpy = sinon.spy(mopidy.tracklist, 'clear')
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
mopidy.tracklist.clear()
|
||||||
|
clearSpy.reset()
|
||||||
|
mopidy.tracklist.add({uris: getUris(QUEUE_TRACKS)})
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
mopidy.playback.play.reset()
|
||||||
|
addSpy.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
after(function () {
|
||||||
|
mopidy.tracklist.add.restore()
|
||||||
|
mopidy.tracklist.clear.restore()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#playTracks()', function () {
|
||||||
|
it('PLAY_ALL should clear tracklist first before populating with tracks', function () {
|
||||||
|
customTracklists[BROWSE_TABLE] = NEW_TRACKS
|
||||||
|
controls.playTracks(PLAY_ALL, mopidy, NEW_TRACKS[0].uri, BROWSE_TABLE)
|
||||||
|
assert(clearSpy.called)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not clear tracklist for events other than PLAY_ALL', function () {
|
||||||
|
customTracklists[BROWSE_TABLE] = NEW_TRACKS
|
||||||
|
controls.playTracks(PLAY_NOW, mopidy, NEW_TRACKS[0].uri, BROWSE_TABLE)
|
||||||
|
assert(clearSpy.notCalled)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should raise exception if trackUri parameter is not provided and "track" data attribute is empty', function () {
|
||||||
|
assert.throw(function () { controls.playTracks('', mopidy) }, Error)
|
||||||
|
|
||||||
|
controls.playTracks(PLAY_ALL, mopidy, NEW_TRACKS[0].uri, BROWSE_TABLE)
|
||||||
|
assert(mopidy.playback.play.calledWithMatch({tlid: mopidy.tracklist._tlTracks[0].tlid}))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should raise exception if playListUri parameter is not provided and "track" data attribute is empty', function () {
|
||||||
|
assert.throw(function () { controls.playTracks('', mopidy, NEW_TRACKS[0].uri) }, Error)
|
||||||
|
|
||||||
|
controls.playTracks(PLAY_ALL, mopidy, NEW_TRACKS[0].uri, BROWSE_TABLE)
|
||||||
|
assert(mopidy.playback.play.calledWithMatch({tlid: mopidy.tracklist._tlTracks[0].tlid}))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should raise exception if unknown tracklist action is provided', function () {
|
||||||
|
var getTrackURIsForActionStub = sinon.stub(controls, '_getTrackURIsForAction') // Stub to bypass earlier exception
|
||||||
|
assert.throw(function () { controls.playTracks('99', mopidy, NEW_TRACKS[0].uri, BROWSE_TABLE) }, Error)
|
||||||
|
getTrackURIsForActionStub.restore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use "track" and "list" data attributes as fallback if parameters are not provided', function () {
|
||||||
|
$('#popupTracks').data('track', 'track:trackMock1') // Simulate 'track:trackMock1' being clicked.
|
||||||
|
$('#popupTracks').data('list', BROWSE_TABLE)
|
||||||
|
customTracklists[BROWSE_TABLE] = NEW_TRACKS
|
||||||
|
|
||||||
|
controls.playTracks(PLAY_ALL, mopidy)
|
||||||
|
assert(mopidy.playback.play.calledWithMatch({tlid: mopidy.tracklist._tlTracks[0].tlid}))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('PLAY_NOW, PLAY_NEXT, and ADD_THIS_BOTTOM should only add one track to the tracklist', function () {
|
||||||
|
controls.playTracks(PLAY_NOW, mopidy, NEW_TRACKS[0].uri)
|
||||||
|
assert(addSpy.calledWithMatch({at_position: 2, uris: [NEW_TRACKS[0].uri]}), 'PLAY_NOW did not add correct track')
|
||||||
|
addSpy.reset()
|
||||||
|
|
||||||
|
mopidy.tracklist.clear()
|
||||||
|
mopidy.tracklist.add({uris: getUris(QUEUE_TRACKS)})
|
||||||
|
|
||||||
|
controls.playTracks(PLAY_NEXT, mopidy, NEW_TRACKS[0].uri)
|
||||||
|
assert(addSpy.calledWithMatch({at_position: 2, uris: [NEW_TRACKS[0].uri]}), 'PLAY_NEXT did not add correct track')
|
||||||
|
addSpy.reset()
|
||||||
|
|
||||||
|
mopidy.tracklist.clear()
|
||||||
|
mopidy.tracklist.add({uris: getUris(QUEUE_TRACKS)})
|
||||||
|
|
||||||
|
controls.playTracks(ADD_THIS_BOTTOM, mopidy, NEW_TRACKS[0].uri)
|
||||||
|
assert(addSpy.calledWithMatch({uris: [NEW_TRACKS[0].uri]}), 'ADD_THIS_BOTTOM did not add correct track')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('PLAY_ALL and ADD_ALL_BOTTOM should add all tracks to tracklist', function () {
|
||||||
|
controls.playTracks(PLAY_ALL, mopidy, NEW_TRACKS[0].uri)
|
||||||
|
assert(addSpy.calledWithMatch({uris: getUris(NEW_TRACKS)}), 'PLAY_ALL did not add correct tracks')
|
||||||
|
addSpy.reset()
|
||||||
|
|
||||||
|
mopidy.tracklist.clear()
|
||||||
|
mopidy.tracklist.add({uris: getUris(QUEUE_TRACKS)})
|
||||||
|
|
||||||
|
controls.playTracks(ADD_ALL_BOTTOM, mopidy, NEW_TRACKS[0].uri)
|
||||||
|
assert(addSpy.calledWithMatch({uris: getUris(NEW_TRACKS)}), 'ADD_ALL_BOTTOM did not add correct tracks')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('PLAY_NOW and PLAY_NEXT should insert track after currently playing track', function () {
|
||||||
|
controls.playTracks(PLAY_NOW, mopidy, NEW_TRACKS[0].uri)
|
||||||
|
assert(addSpy.calledWithMatch({at_position: 2, uris: [NEW_TRACKS[0].uri]}), 'PLAY_NOW did not insert track at correct position')
|
||||||
|
addSpy.reset()
|
||||||
|
|
||||||
|
mopidy.tracklist.clear()
|
||||||
|
mopidy.tracklist.add({uris: getUris(QUEUE_TRACKS)})
|
||||||
|
|
||||||
|
controls.playTracks(PLAY_NEXT, mopidy, NEW_TRACKS[0].uri)
|
||||||
|
assert(addSpy.calledWithMatch({at_position: 2, uris: [NEW_TRACKS[0].uri]}), 'PLAY_NEXT did not insert track at correct position')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('only PLAY_NOW and PLAY_ALL should trigger playback', function () {
|
||||||
|
controls.playTracks(PLAY_NOW, mopidy, 2)
|
||||||
|
assert(mopidy.playback.play.calledWithMatch({tlid: mopidy.tracklist._tlTracks[2].tlid}), 'PLAY_NOW did not start playback of correct track')
|
||||||
|
mopidy.playback.play.reset()
|
||||||
|
|
||||||
|
mopidy.tracklist.clear()
|
||||||
|
mopidy.tracklist.add({uris: getUris(QUEUE_TRACKS)})
|
||||||
|
|
||||||
|
controls.playTracks(PLAY_NEXT, mopidy, NEW_TRACKS[0].uri)
|
||||||
|
assert.isFalse(mopidy.playback.play.called, 'PLAY_NEXT should not have triggered playback to start')
|
||||||
|
mopidy.playback.play.reset()
|
||||||
|
|
||||||
|
mopidy.tracklist.clear()
|
||||||
|
mopidy.tracklist.add({uris: getUris(QUEUE_TRACKS)})
|
||||||
|
|
||||||
|
controls.playTracks(ADD_THIS_BOTTOM, mopidy, NEW_TRACKS[0].uri)
|
||||||
|
assert.isFalse(mopidy.playback.play.called, 'ADD_THIS_BOTTOM should not have triggered playback to start')
|
||||||
|
mopidy.playback.play.reset()
|
||||||
|
|
||||||
|
mopidy.tracklist.clear()
|
||||||
|
mopidy.tracklist.add({uris: getUris(QUEUE_TRACKS)})
|
||||||
|
|
||||||
|
controls.playTracks(PLAY_ALL, mopidy, NEW_TRACKS[2].uri)
|
||||||
|
assert(mopidy.playback.play.calledWithMatch({tlid: mopidy.tracklist._tlTracks[2].tlid}), 'PLAY_ALL did not start playback of correct track')
|
||||||
|
mopidy.playback.play.reset()
|
||||||
|
|
||||||
|
mopidy.tracklist.clear()
|
||||||
|
mopidy.tracklist.add({uris: getUris(QUEUE_TRACKS)})
|
||||||
|
|
||||||
|
controls.playTracks(ADD_ALL_BOTTOM, mopidy, NEW_TRACKS[0].uri)
|
||||||
|
assert.isFalse(mopidy.playback.play.called, 'ADD_ALL_BOTTOM should not have triggered playback to start')
|
||||||
|
mopidy.playback.play.reset()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should store last action in cookie if on-track-click mode is set to "DYNAMIC"', function () {
|
||||||
|
$(document.body).data('on-track-click', 'DYNAMIC')
|
||||||
|
var cookieStub = sinon.stub($, 'cookie')
|
||||||
|
controls.playTracks(PLAY_NOW, mopidy, 2)
|
||||||
|
assert(cookieStub.calledWithMatch('onTrackClick', PLAY_NOW, {expires: 365}))
|
||||||
|
cookieStub.reset()
|
||||||
|
|
||||||
|
$(document.body).data('on-track-click', 'PLAY_NOW')
|
||||||
|
controls.playTracks(PLAY_NOW, mopidy, 2)
|
||||||
|
assert(cookieStub.notCalled)
|
||||||
|
cookieStub.restore()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#getAction()', function () {
|
||||||
|
it('should use default action if none is specified', function () {
|
||||||
|
window.MOCK_DEFAULT = 99 // Define global variable to test against.
|
||||||
|
$(document.body).data('on-track-click', 'MOCK_DEFAULT')
|
||||||
|
assert.equal(controls.getAction(), 99)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should get action from cookie if action is set to "DYNAMIC"', function () {
|
||||||
|
$(document.body).data('on-track-click', 'DYNAMIC')
|
||||||
|
var cookieStub = sinon.stub($, 'cookie')
|
||||||
|
controls.getAction()
|
||||||
|
assert(cookieStub.called)
|
||||||
|
cookieStub.restore()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should default to "PLAY_ALL" if no cookie is available for "DYNAMIC"', function () {
|
||||||
|
$(document.body).data('on-track-click', 'DYNAMIC')
|
||||||
|
$.removeCookie('onTrackClick')
|
||||||
|
assert.equal(controls.getAction(), PLAY_ALL)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#getIconForAction()', function () {
|
||||||
|
it('should return correct FontAwesome class for each tracklist action', function () {
|
||||||
|
assert.equal(controls.getIconForAction(PLAY_ALL), 'fa fa-fast-forward')
|
||||||
|
assert.equal(controls.getIconForAction(PLAY_NOW), 'fa fa-play')
|
||||||
|
assert.equal(controls.getIconForAction(PLAY_NEXT), 'fa fa-level-down')
|
||||||
|
assert.equal(controls.getIconForAction(ADD_THIS_BOTTOM), 'fa fa-plus-square-o')
|
||||||
|
assert.equal(controls.getIconForAction(ADD_ALL_BOTTOM), 'fa fa-plus-square')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should raise error if unknown tracklist action is provided', function () {
|
||||||
|
assert.throw(function () { controls.getIconForAction(99) }, Error)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle action identifier strings in addition to integers', function () {
|
||||||
|
assert.equal(controls.getIconForAction('0'), 'fa fa-play')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use default tracklist action if no parameter is provided', function () {
|
||||||
|
assert.equal(controls.getIconForAction(), 'fa fa-fast-forward')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#_getTrackURIsForAction()', function () {
|
||||||
|
it('should return just "trackUri" for PLAY_NOW, PLAY_NEXT, and ADD_THIS_BOTTOM', function () {
|
||||||
|
assert.equal(controls._getTrackURIsForAction(PLAY_NOW, 'mockUri')[0], 'mockUri')
|
||||||
|
assert.equal(controls._getTrackURIsForAction(PLAY_NEXT, 'mockUri')[0], 'mockUri')
|
||||||
|
assert.equal(controls._getTrackURIsForAction(ADD_THIS_BOTTOM, 'mockUri')[0], 'mockUri')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should get tracks from "playlistUri" for PLAY_ALL, and ADD_ALL_BOTTOM', function () {
|
||||||
|
customTracklists[BROWSE_TABLE] = NEW_TRACKS
|
||||||
|
|
||||||
|
var tracks = controls._getTrackURIsForAction(PLAY_ALL, NEW_TRACKS[0], BROWSE_TABLE)
|
||||||
|
assert.equal(tracks.length, NEW_TRACKS.length)
|
||||||
|
for (var i = 0; i < tracks.length; i++) {
|
||||||
|
assert.equal(tracks[i], NEW_TRACKS[i].uri)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should raise error if unknown tracklist action is provided', function () {
|
||||||
|
assert.throw(function () { controls._getTrackURIsForAction(99) }, Error)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle action identifier strings in addition to integers', function () {
|
||||||
|
assert.equal(controls._getTrackURIsForAction('0', 'mockUri')[0], 'mockUri')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -243,6 +243,9 @@ describe('images', function () {
|
|||||||
getImagesSpy.reset()
|
getImagesSpy.reset()
|
||||||
setDeprecatedAlbumImageSpy.reset()
|
setDeprecatedAlbumImageSpy.reset()
|
||||||
})
|
})
|
||||||
|
after(function () {
|
||||||
|
mopidy.library.getImages.restore()
|
||||||
|
})
|
||||||
|
|
||||||
it('should use default image if no track URI is provided', function () {
|
it('should use default image if no track URI is provided', function () {
|
||||||
images.setAlbumImage('', img_element, mopidy)
|
images.setAlbumImage('', img_element, mopidy)
|
||||||
@ -302,7 +305,7 @@ describe('images', function () {
|
|||||||
|
|
||||||
assert.isTrue(lookupSpy.calledOnce)
|
assert.isTrue(lookupSpy.calledOnce)
|
||||||
expect($(img_element).prop('src')).to.endWith('mockAlbumImageUri')
|
expect($(img_element).prop('src')).to.endWith('mockAlbumImageUri')
|
||||||
lookupSpy.restore()
|
mopidy.library.lookup.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should use default image if track.album or track.artist is not available', function () {
|
it('should use default image if track.album or track.artist is not available', function () {
|
||||||
@ -313,7 +316,7 @@ describe('images', function () {
|
|||||||
|
|
||||||
assert.isTrue(lookupSpy.calledOnce)
|
assert.isTrue(lookupSpy.calledOnce)
|
||||||
expect($(img_element).prop('src')).to.endWith(images.DEFAULT_ALBUM_URL)
|
expect($(img_element).prop('src')).to.endWith(images.DEFAULT_ALBUM_URL)
|
||||||
lookupSpy.restore()
|
mopidy.library.lookup.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should fall back to retrieving image from last.fm if none provided by Mopidy', function () {
|
it('should fall back to retrieving image from last.fm if none provided by Mopidy', function () {
|
||||||
@ -414,7 +417,7 @@ describe('images', function () {
|
|||||||
getImagesSpy.reset()
|
getImagesSpy.reset()
|
||||||
})
|
})
|
||||||
after(function () {
|
after(function () {
|
||||||
getImagesSpy.restore()
|
mopidy.library.getImages.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should use default image if no artist URI is provided', function () {
|
it('should use default image if no artist URI is provided', function () {
|
||||||
|
|||||||
@ -223,7 +223,7 @@ describe('SyncedTimer', function () {
|
|||||||
clock.tick(1001)
|
clock.tick(1001)
|
||||||
syncedProgressTimer._scheduleSync(1000)
|
syncedProgressTimer._scheduleSync(1000)
|
||||||
assert(clearSpy.calledWith(scheduleID))
|
assert(clearSpy.calledWith(scheduleID))
|
||||||
clearSpy.restore()
|
window.clearTimeout.restore()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -358,7 +358,7 @@ describe('SyncedTimer', function () {
|
|||||||
|
|
||||||
assert(scheduleSpy.calledWith(0))
|
assert(scheduleSpy.calledWith(0))
|
||||||
syncedProgressTimer.stop()
|
syncedProgressTimer.stop()
|
||||||
scheduleSpy.restore()
|
syncedProgressTimer._scheduleSync.restore()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -392,7 +392,7 @@ describe('SyncedTimer', function () {
|
|||||||
|
|
||||||
assert.isFalse(syncedProgressTimer._isSyncScheduled)
|
assert.isFalse(syncedProgressTimer._isSyncScheduled)
|
||||||
assert(cancelSpy.calledWith(syncedProgressTimer._scheduleID))
|
assert(cancelSpy.calledWith(syncedProgressTimer._scheduleID))
|
||||||
cancelSpy.restore()
|
window.clearTimeout.restore()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -422,7 +422,7 @@ describe('SyncedTimer', function () {
|
|||||||
|
|
||||||
assert.isTrue(formatSpy.called)
|
assert.isTrue(formatSpy.called)
|
||||||
expect(syncedProgressTimer.positionNode.nodeValue).to.equal('0:01')
|
expect(syncedProgressTimer.positionNode.nodeValue).to.equal('0:01')
|
||||||
formatSpy.restore()
|
SyncedProgressTimer.format.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should set position to "" if timer has not been initialized', function () {
|
it('should set position to "" if timer has not been initialized', function () {
|
||||||
|
|||||||
@ -18,6 +18,7 @@ class ExtensionTests(unittest.TestCase):
|
|||||||
assert 'enabled = true' in config
|
assert 'enabled = true' in config
|
||||||
assert 'websocket_host =' in config
|
assert 'websocket_host =' in config
|
||||||
assert 'websocket_port =' in config
|
assert 'websocket_port =' in config
|
||||||
|
assert 'on_track_click = PLAY_ALL' in config
|
||||||
|
|
||||||
def test_get_config_schema(self):
|
def test_get_config_schema(self):
|
||||||
ext = Extension()
|
ext = Extension()
|
||||||
@ -27,6 +28,7 @@ class ExtensionTests(unittest.TestCase):
|
|||||||
assert 'musicbox' in schema
|
assert 'musicbox' in schema
|
||||||
assert 'websocket_host' in schema
|
assert 'websocket_host' in schema
|
||||||
assert 'websocket_port' in schema
|
assert 'websocket_port' in schema
|
||||||
|
assert 'on_track_click' in schema
|
||||||
|
|
||||||
def test_setup(self):
|
def test_setup(self):
|
||||||
registry = mock.Mock()
|
registry = mock.Mock()
|
||||||
|
|||||||
@ -76,3 +76,6 @@ class WebclientTests(unittest.TestCase):
|
|||||||
|
|
||||||
def test_is_musicbox(self):
|
def test_is_musicbox(self):
|
||||||
assert not self.mmw.is_music_box()
|
assert not self.mmw.is_music_box()
|
||||||
|
|
||||||
|
def test_default_click_action(self):
|
||||||
|
assert self.mmw.get_default_click_action() == 'PLAY_ALL'
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user