4.4.4 Prevents global shortcuts on inputs

- Adds a control on the origin of keyboard shortcuts
This prevents pausing the song when typing "space" in the search input.

- Moves volume management to player-service. Adds getters and setters.
The setter also checks the volume so it stays between 0 and 1.
This commit is contained in:
Hyzual 2015-02-15 14:47:58 +01:00
parent 91a5c6d3c3
commit faf004b5b0
10 changed files with 170 additions and 75 deletions

View file

@ -309,38 +309,52 @@ angular.module('JamStash')
} }
}; };
/**
* Returns true if the target of this event is an input
* @param {jQuery event} event
* @return {Boolean}
*/
function isTargetInput (event) {
return (event && event.target.tagName === "INPUT");
}
/* We define player-related methods here instead of in player controller /* We define player-related methods here instead of in player controller
in order to bind keypresses to <body> and have global shortcuts */ in order to bind keypresses to <body> and have global shortcuts.
$scope.togglePause = function () { We also check the event so we don't do anything if it's on an input */
if(globals.settings.Jukebox) { $scope.togglePause = function (event) {
$scope.sendToJukebox('stop'); if(!isTargetInput(event)) {
} else { if(globals.settings.Jukebox) {
player.togglePause(); $scope.sendToJukebox('stop');
} else {
player.togglePause();
}
} }
}; };
$scope.turnVolumeUp = function () { $scope.turnVolumeUp = function (event) {
var volume = player.volume; if(!isTargetInput(event)) {
if ((volume+0.1) > 1 || volume < 0) { var volume = player.turnVolumeUp();
volume = 0.9; persistence.saveVolume(volume);
} }
volume += 0.1;
player.volume = volume;
persistence.saveVolume(volume);
}; };
$scope.turnVolumeDown = function () { $scope.turnVolumeDown = function (event) {
var volume = player.volume; if(!isTargetInput(event)) {
if (volume > 1 || (volume-0.1) < 0) { var volume = player.turnVolumeDown();
volume = 0.1; persistence.saveVolume(volume);
} }
volume -= 0.1;
player.volume = volume;
persistence.saveVolume(volume);
}; };
$scope.nextTrack = player.nextTrack; $scope.nextTrack = function (event) {
$scope.previousTrack = player.previousTrack; if(!isTargetInput(event)) {
player.nextTrack();
}
};
$scope.previousTrack = function (event) {
if(!isTargetInput(event)) {
player.previousTrack();
}
};
$rootScope.addToJukebox = function (id) { $rootScope.addToJukebox = function (id) {
if (globals.settings.Debug) { console.log("LOAD JUKEBOX"); } if (globals.settings.Debug) { console.log("LOAD JUKEBOX"); }
@ -410,7 +424,7 @@ angular.module('JamStash')
persistence.loadQueue(); persistence.loadQueue();
persistence.loadTrackPosition(); persistence.loadTrackPosition();
} }
player.volume = persistence.getVolume(); player.setVolume(persistence.getVolume());
} }
/* End Startup */ /* End Startup */
}]); }]);

View file

@ -17,9 +17,8 @@ describe("Main controller", function() {
}); });
// Mock the player service // Mock the player service
player = jasmine.createSpyObj("player", ["togglePause"]); player = jasmine.createSpyObj("player", ["togglePause", "turnVolumeUp", "turnVolumeDown", "nextTrack", "previousTrack", "setVolume"]);
player.queue = []; player.queue = [];
player.volume = 1.0;
// Mock the persistence service // Mock the persistence service
persistence = jasmine.createSpyObj("persistence", ["loadQueue", "loadTrackPosition", "getVolume", "saveVolume"]); persistence = jasmine.createSpyObj("persistence", ["loadQueue", "loadTrackPosition", "getVolume", "saveVolume"]);
@ -108,41 +107,63 @@ describe("Main controller", function() {
}); });
}); });
describe("When I turn the volume up,", function() { it("When I turn the volume up, it sets the player's volume up and saves it using the persistence service", function() {
it("it sets the player's volume up by 10% and saves it using the persistence service", function() { player.turnVolumeUp.and.returnValue(0.6);
player.volume = 0.5; scope.turnVolumeUp();
scope.turnVolumeUp(); expect(player.turnVolumeUp).toHaveBeenCalled();
expect(persistence.saveVolume).toHaveBeenCalledWith(0.6);
expect(player.volume).toBe(0.6);
expect(persistence.saveVolume).toHaveBeenCalledWith(0.6);
});
it("if the player's resulting volume won't be between 0 and 1, it sets it to 1", function() {
player.volume = 5.91488;
scope.turnVolumeUp();
expect(player.volume).toBe(1.0);
});
}); });
describe("When I turn the volume down,", function() { it("When I turn the volume down, it sets the player's volume down and saves it using the persistence service", function() {
it("it sets the player's volume down by 10% and saves it using the persistence service", function() { player.turnVolumeDown.and.returnValue(0.4);
player.volume = 0.5; scope.turnVolumeDown();
scope.turnVolumeDown(); expect(player.turnVolumeDown).toHaveBeenCalled();
expect(persistence.saveVolume).toHaveBeenCalledWith(0.4);
});
expect(player.volume).toBe(0.4); it("When I go to the next track, it calls next track on the player", function() {
expect(persistence.saveVolume).toHaveBeenCalledWith(0.4); scope.nextTrack();
expect(player.nextTrack).toHaveBeenCalled();
});
it("When I go to the previous track, it calls previous track on the player", function() {
scope.previousTrack();
expect(player.previousTrack).toHaveBeenCalled();
});
describe("Given that I am targeting an input,", function() {
var event;
beforeEach(function() {
event = { target: { tagName: "INPUT" } };
}); });
it("if the player's resulting volume won't be between 0 and 1, it sets it to 0", function() { it("when I use a shortcut to toggle pause, it doesn't do anything", function() {
player.volume = 5.91488; scope.togglePause(event);
expect(player.togglePause).not.toHaveBeenCalled();
});
scope.turnVolumeDown(); it("when I use a shortcut to turn the volume up, it doesn't do anything", function() {
scope.turnVolumeUp(event);
expect(player.turnVolumeUp).not.toHaveBeenCalled();
expect(persistence.saveVolume).not.toHaveBeenCalled();
});
expect(player.volume).toBe(0); it("when I use a shortcut to turn the volume down, it doesn't do anything", function() {
scope.turnVolumeDown(event);
expect(player.turnVolumeDown).not.toHaveBeenCalled();
expect(persistence.saveVolume).not.toHaveBeenCalled();
});
it("when I use a shortcut to go to the next track, it doesn't do anything", function() {
scope.nextTrack(event);
expect(player.nextTrack).not.toHaveBeenCalled();
});
it("when I use a shortcut to go to the previous track, it doesn't do anything", function() {
scope.previousTrack(event);
expect(player.previousTrack).not.toHaveBeenCalled();
}); });
}); });
}); });
@ -154,7 +175,7 @@ describe("Main controller", function() {
$controller('AppController', controllerParams); $controller('AppController', controllerParams);
expect(persistence.getVolume).toHaveBeenCalled(); expect(persistence.getVolume).toHaveBeenCalled();
expect(player.volume).toBe(0.551835); expect(player.setVolume).toHaveBeenCalledWith(0.551835);
}); });
}); });
}); });

View file

@ -20,7 +20,7 @@
<link href="styles/Mobile.css" rel="stylesheet" type="text/css" data-name="main" /> <link href="styles/Mobile.css" rel="stylesheet" type="text/css" data-name="main" />
<link href="" rel="stylesheet" type="text/css" data-name="theme" /> <link href="" rel="stylesheet" type="text/css" data-name="theme" />
</head> </head>
<body ui-keypress="{'32 179': 'togglePause()', '43 61 187': 'turnVolumeUp()', '45 95 189': 'turnVolumeDown()'}" ui-keydown="{'right 176': 'nextTrack()', 'left 177': 'previousTrack()'}"> <body ui-keypress="{'32 179': 'togglePause($event)', '43 61 187': 'turnVolumeUp($event)', '45 95 189': 'turnVolumeDown($event)'}" ui-keydown="{'right 176': 'nextTrack($event)', 'left 177': 'previousTrack($event)'}">
<div id="container"> <div id="container">
<div id="header"> <div id="header">
<div id="messages"> <div id="messages">

View file

@ -160,7 +160,7 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
}); });
scope.$watch(function () { scope.$watch(function () {
return playerService.volume; return playerService.getVolume();
}, function (newVal) { }, function (newVal) {
$player.jPlayer('volume', newVal); $player.jPlayer('volume', newVal);
if (globals.settings.Debug) { console.log('Volume: ' + Math.round(newVal * 100) + '%'); } if (globals.settings.Debug) { console.log('Volume: ' + Math.round(newVal * 100) + '%'); }

View file

@ -22,10 +22,10 @@ describe("jplayer directive", function() {
$delegate.pauseSong = false; $delegate.pauseSong = false;
$delegate.restartSong = false; $delegate.restartSong = false;
$delegate.loadSong = false; $delegate.loadSong = false;
$delegate.volume = 1.0;
$delegate.getPlayingSong = jasmine.createSpy('getPlayingSong').and.callFake(function() { $delegate.getPlayingSong = jasmine.createSpy('getPlayingSong').and.callFake(function() {
return playingSong; return playingSong;
}); });
$delegate.getVolume = jasmine.createSpy('getVolume').and.returnValue(1.0);
$delegate.nextTrack = jasmine.createSpy('nextTrack'); $delegate.nextTrack = jasmine.createSpy('nextTrack');
$delegate.songEnded = jasmine.createSpy('songEnded'); $delegate.songEnded = jasmine.createSpy('songEnded');
$delegate.isLastSongPlaying = jasmine.createSpy('isLastSongPlaying'); $delegate.isLastSongPlaying = jasmine.createSpy('isLastSongPlaying');
@ -170,7 +170,7 @@ describe("jplayer directive", function() {
}); });
it("When the player service's volume changes, it sets jPlayer's volume", function() { it("When the player service's volume changes, it sets jPlayer's volume", function() {
playerService.volume = 0.2034; playerService.getVolume.and.returnValue(0.2034);
scope.$apply(); scope.$apply();
expect($player.jPlayer).toHaveBeenCalledWith('volume', 0.2034); expect($player.jPlayer).toHaveBeenCalledWith('volume', 0.2034);
}); });

View file

@ -8,6 +8,8 @@ angular.module('jamstash.player.service', ['angular-underscore/utils', 'jamstash
.factory('player', ['globals', function (globals) { .factory('player', ['globals', function (globals) {
'use strict'; 'use strict';
var playerVolume = 1.0;
var player = { var player = {
// playingIndex and playingSong aren't meant to be used, they only are public for unit-testing purposes // playingIndex and playingSong aren't meant to be used, they only are public for unit-testing purposes
_playingIndex: -1, _playingIndex: -1,
@ -16,9 +18,8 @@ angular.module('jamstash.player.service', ['angular-underscore/utils', 'jamstash
pauseSong: false, pauseSong: false,
restartSong: false, restartSong: false,
loadSong: false, loadSong: false,
volume: 1.0,
play: function(song) { play: function (song) {
// Find the song's index in the queue, if it's in there // Find the song's index in the queue, if it's in there
var index = player.indexOfSong(song); var index = player.indexOfSong(song);
player._playingIndex = (index !== undefined) ? index : -1; player._playingIndex = (index !== undefined) ? index : -1;
@ -31,7 +32,7 @@ angular.module('jamstash.player.service', ['angular-underscore/utils', 'jamstash
} }
}, },
togglePause: function() { togglePause: function () {
if (player.pauseSong) { if (player.pauseSong) {
player.pauseSong = false; player.pauseSong = false;
} else { } else {
@ -39,22 +40,22 @@ angular.module('jamstash.player.service', ['angular-underscore/utils', 'jamstash
} }
}, },
playFirstSong: function() { playFirstSong: function () {
player._playingIndex = 0; player._playingIndex = 0;
player.play(player.queue[0]); player.play(player.queue[0]);
}, },
load: function(song) { load: function (song) {
player.loadSong = true; player.loadSong = true;
player.play(song); player.play(song);
}, },
restart: function() { restart: function () {
player.restartSong = true; player.restartSong = true;
}, },
// Called from the player directive at the end of the current song // Called from the player directive at the end of the current song
songEnded: function() { songEnded: function () {
if (globals.settings.Repeat) { if (globals.settings.Repeat) {
// repeat current track // repeat current track
player.restart(); player.restart();
@ -68,7 +69,7 @@ angular.module('jamstash.player.service', ['angular-underscore/utils', 'jamstash
} }
}, },
nextTrack: function() { nextTrack: function () {
// Find the song's index in the queue, in case it changed (with a drag & drop) // Find the song's index in the queue, in case it changed (with a drag & drop)
var index = player.indexOfSong(player._playingSong); var index = player.indexOfSong(player._playingSong);
player._playingIndex = (index !== undefined) ? index : -1; player._playingIndex = (index !== undefined) ? index : -1;
@ -80,7 +81,7 @@ angular.module('jamstash.player.service', ['angular-underscore/utils', 'jamstash
} }
}, },
previousTrack: function() { previousTrack: function () {
// Find the song's index in the queue, in case it changed (with a drag & drop) // Find the song's index in the queue, in case it changed (with a drag & drop)
var index = player.indexOfSong(player._playingSong); var index = player.indexOfSong(player._playingSong);
player._playingIndex = (index !== undefined) ? index : -1; player._playingIndex = (index !== undefined) ? index : -1;
@ -94,17 +95,17 @@ angular.module('jamstash.player.service', ['angular-underscore/utils', 'jamstash
} }
}, },
emptyQueue: function() { emptyQueue: function () {
player.queue = []; player.queue = [];
return player; return player;
}, },
shuffleQueue: function() { shuffleQueue: function () {
player.queue = _(player.queue).shuffle(); player.queue = _(player.queue).shuffle();
return player; return player;
}, },
addSong: function(song) { addSong: function (song) {
player.queue.push(song); player.queue.push(song);
return player; return player;
}, },
@ -114,7 +115,7 @@ angular.module('jamstash.player.service', ['angular-underscore/utils', 'jamstash
return player; return player;
}, },
removeSong: function(song) { removeSong: function (song) {
var index = player.queue.indexOf(song); var index = player.queue.indexOf(song);
player.queue.splice(index, 1); player.queue.splice(index, 1);
return player; return player;
@ -125,21 +126,52 @@ angular.module('jamstash.player.service', ['angular-underscore/utils', 'jamstash
return player; return player;
}, },
getPlayingSong: function() { getPlayingSong: function () {
return player._playingSong; return player._playingSong;
}, },
isLastSongPlaying: function() { isLastSongPlaying: function () {
return ((player._playingIndex +1) === player.queue.length); return ((player._playingIndex +1) === player.queue.length);
}, },
indexOfSong: function(song) { indexOfSong: function (song) {
for (var i = player.queue.length - 1; i >= 0; i--) { for (var i = player.queue.length - 1; i >= 0; i--) {
if (angular.equals(song, player.queue[i])) { if (angular.equals(song, player.queue[i])) {
return i; return i;
} }
} }
return undefined; return undefined;
},
turnVolumeUp: function () {
var volume = playerVolume;
if ((volume + 0.1) > 1 || volume < 0) {
volume = 0.9;
}
volume += 0.1;
playerVolume = Math.round(volume * 100) / 100;
return volume;
},
turnVolumeDown: function () {
var volume = playerVolume;
if (volume > 1 || (volume - 0.1) < 0) {
volume = 0.1;
}
volume -= 0.1;
playerVolume = Math.round(volume * 100) / 100;
return volume;
},
getVolume: function () {
return playerVolume;
},
setVolume: function (volume) {
if (volume > 1) { volume = 1; }
else if(volume < 0) { volume = 0; }
playerVolume = Math.round(volume * 100) / 100;
return player;
} }
}; };

View file

@ -277,4 +277,32 @@ describe("Player service -", function() {
expect(player._playingIndex).toBe(-1); expect(player._playingIndex).toBe(-1);
}); });
}); });
describe("When I turn the volume up,", function() {
it("it sets the player's volume up by 10%", function() {
player.setVolume(0.5);
player.turnVolumeUp();
expect(player.getVolume()).toBe(0.6);
});
it("if the player's resulting volume won't be between 0 and 1, it sets it to 1", function() {
player.setVolume(5.91488);
player.turnVolumeUp();
expect(player.getVolume()).toBe(1.0);
});
});
describe("When I turn the volume down,", function() {
it("it sets the player's volume down by 10%", function() {
player.setVolume(0.5);
player.turnVolumeDown();
expect(player.getVolume()).toBe(0.4);
});
it("if the player's resulting volume won't be between 0 and 1, it sets it to 0", function() {
player.setVolume(0.05);
player.turnVolumeDown();
expect(player.getVolume()).toBe(0);
});
});
}); });

View file

@ -1,6 +1,6 @@
{ {
"name": "jamstash", "name": "jamstash",
"version": "4.4.3", "version": "4.4.4",
"description": "HTML5 Audio Streamer for Subsonic, Archive.org browsing and streaming", "description": "HTML5 Audio Streamer for Subsonic, Archive.org browsing and streaming",
"authors": [ "authors": [
"tsquillario (https://github.com/tsquillario)", "tsquillario (https://github.com/tsquillario)",

View file

@ -2,7 +2,7 @@
"manifest_version": 2, "manifest_version": 2,
"name": "Jamstash", "name": "Jamstash",
"description": "HTML5 Player for Subsonic & Archive.org", "description": "HTML5 Player for Subsonic & Archive.org",
"version": "4.4.3", "version": "4.4.4",
"app": { "app": {
"launch": { "launch": {
"web_url": "http://jamstash.com" "web_url": "http://jamstash.com"

View file

@ -1,6 +1,6 @@
{ {
"name": "jamstash", "name": "jamstash",
"version": "4.4.3", "version": "4.4.4",
"description": "HTML5 Audio Streamer for Subsonic, Archive.org browsing and streaming", "description": "HTML5 Audio Streamer for Subsonic, Archive.org browsing and streaming",
"author": "Trevor Squillario (https://github.com/tsquillario)", "author": "Trevor Squillario (https://github.com/tsquillario)",
"contributors": [ "contributors": [