diff --git a/Gruntfile.js b/Gruntfile.js index 09b5ebf..592c389 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -25,6 +25,9 @@ module.exports = function (grunt) { dist: 'dist' }; + // Serve static files + var serveStatic = require('serve-static'); + // Paths to ssh config & private key var sshConfigFile = '.ssh/testServer.json'; var sshKeyFile = '.ssh/test-server-key/'; @@ -87,9 +90,9 @@ module.exports = function (grunt) { return [ connect().use( '/bower_components', - connect.static('./bower_components') + serveStatic('./bower_components') ), - connect.static(appConfig.app) + serveStatic(appConfig.app) ]; } } diff --git a/app/app.js b/app/app.js index 072da6c..54d66a0 100755 --- a/app/app.js +++ b/app/app.js @@ -6,7 +6,6 @@ angular.module('JamStash', [ 'ngRoute', 'ngSanitize', 'ngLodash', - 'ui.keypress', 'jamstash.subsonic.controller', 'jamstash.archive.controller', 'jamstash.player.controller', diff --git a/app/common/directives.js b/app/common/directives.js index 227312a..abe5200 100644 --- a/app/common/directives.js +++ b/app/common/directives.js @@ -1,6 +1,7 @@ 'use strict'; -angular.module('JamStash').directive('sortable', function () { +angular.module('JamStash') +.directive('sortable', function () { return { link: function (scope, elm, attrs) { elm.sortable({ @@ -46,30 +47,6 @@ angular.module('JamStash').directive('sortable', function () { } }; }]) -.directive('stopEvent', function () { - return { - restrict: 'A', - link: function (scope, element, attr) { - element.bind(attr.stopEvent, function (e) { - e.stopPropagation(); - }); - } - }; -}) -.directive('ngEnter', function () { - return { - scope: { onEnter: '&' }, - link: function (scope, element) { - console.log(scope); - element.bind("keydown keypress", function (event) { - if (event.which === 13) { - scope.onEnter(); - scope.$apply(); - } - }); - } - }; -}) .directive('ngDownload', ['$compile', function ($compile) { return { restrict: 'E', @@ -92,16 +69,20 @@ angular.module('JamStash').directive('sortable', function () { } }; }]) -.directive('stopEvent', function () { +.directive('stopEvent', ['lodash', function (_) { return { restrict: 'A', link: function (scope, element, attr) { - element.bind(attr.stopEvent, function (e) { - e.stopPropagation(); - }); + if (attr && attr.stopEvent) { + _.forEach(attr.stopEvent.split(','), function (eventName) { + element.bind(eventName, function (e) { + e.stopPropagation(); + }); + }); + } } }; -}) +}]) .directive('ngEnter', function () { return function (scope, element, attrs) { element.bind("keydown keypress", function (event) { @@ -114,22 +95,4 @@ angular.module('JamStash').directive('sortable', function () { } }); }; -}) -.directive("ngMsgs", function() { - /* Not Using */ - return { - restrict: 'E', - transclude : false, - scope: { - msgs: "=" - }, - template: '{{ item }}', - link: function (scope, elm, attrs) { - scope.$watch(scope.Messages, function () { - var content = $compile((template)(scope)); - elm.append(content); - $(elm).parent().fadeIn(); - }); - } - }; -}); \ No newline at end of file +}); diff --git a/app/common/main-controller.js b/app/common/main-controller.js index 29eaa63..47f7856 100644 --- a/app/common/main-controller.js +++ b/app/common/main-controller.js @@ -154,9 +154,9 @@ angular.module('JamStash') }); }; - $(document).on("click", ".message", function(){ - $(this).remove(); - }); + $(document).on("click", ".message", function(){ + $(this).remove(); + }); // Global Functions window.onbeforeunload = function () { @@ -171,39 +171,51 @@ angular.module('JamStash') $(this).fadeOut(function () { $(this).remove(); }); return false; }) - $document.keydown(function (e) { - $scope.scrollToIndex(e); + + // Shortcut processing + $(document).keydown(function (e) { + $scope.processKeyEvent(e); }); $scope.scrollToIndex = function (e) { - var source = e.target.id; - if (e.target.tagName.toUpperCase() != 'INPUT') { - var unicode = e.charCode ? e.charCode : e.keyCode; - if (globals.settings.Debug) { console.log('Keycode Triggered: ' + unicode); } - if (unicode == 49) { // 1 - $('#action_Queue').click(); - } else if (unicode == 50) { - $('#action_Library').click(); - } else if (unicode == 51) { - $('#action_Archive').click(); - } else if (unicode == 52) { - $('#action_Settings').click(); - } else if (unicode == 53) { - } else if (unicode == 54) { // 6 + $scope.processKeyEvent(e); + return true; + }; + $scope.processKeyEvent = function (e) { + if (e.isDefaultPrevented() || + e.repeat || + e.altKey || e.metaKey || e.ctrlKey || + (e.target && _.contains(['input', 'textarea', 'select'], e.target.tagName.toLowerCase()))) { + return; + } + + var key = e.key; + if (globals.settings.Debug) { console.log('Key pressed: ' + key); } + if (key == "Esc" || key == "Escape") { + $rootScope.hideQueue(); + } else if (key == " " || key == "Space") { + player.togglePause(); + } else if (key == "ArrowLeft" || key == "Left") { + player.previousTrack(); + } else if (key == "ArrowRight" || key == "Right") { + player.nextTrack(); + } else if (key == "-" || key == "_") { + persistence.saveVolume(player.turnVolumeDown()); + } else if (key == "=" || key == "+") { + persistence.saveVolume(player.turnVolumeUp()); + } else if (/^[a-z]$/i.test(key) && $('#tabLibrary').is(':visible')) { + if (/^[x-z]$/i.test(key)) { + key = 'x-z'; } - if (unicode >= 65 && unicode <= 90 && $('#tabLibrary').is(':visible')) { // a-z - var key = utils.findKeyForCode(unicode); - if (key == 'x' || key == 'y' || key == 'z') { - key = 'x-z'; - } - var el = '#' + key.toUpperCase(); - if ($(el).length > 0) { - $('#left-component').stop().scrollTo(el, 400); - } - } else if (unicode == 36 && $('#tabLibrary').is(':visible')) { // home - $('#left-component').stop().scrollTo('#MusicFolders', 400); + var el = '#' + key.toUpperCase(); + if ($(el).length > 0) { + $('#left-component').stop().scrollTo(el, 400); } } - return true; + else{ + return; + } + $scope.$apply(); + e.preventDefault(); }; $scope.scrollToIndexName = function (index) { var el = '#' + index; @@ -246,82 +258,6 @@ 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 - in order to bind keypresses to and have global shortcuts. - We also check the event so we don't do anything if it's on an input */ - $scope.togglePause = function (event) { - if(!isTargetInput(event)) { - if(globals.settings.Jukebox) { - $scope.sendToJukebox('stop'); - } else { - player.togglePause(); - } - } - }; - - $scope.turnVolumeUp = function (event) { - if(!isTargetInput(event)) { - var volume = player.turnVolumeUp(); - persistence.saveVolume(volume); - } - }; - - $scope.turnVolumeDown = function (event) { - if(!isTargetInput(event)) { - var volume = player.turnVolumeDown(); - persistence.saveVolume(volume); - } - }; - - $scope.nextTrack = function (event) { - if(!isTargetInput(event)) { - player.nextTrack(); - } - }; - $scope.previousTrack = function (event) { - if(!isTargetInput(event)) { - player.previousTrack(); - } - }; - - $rootScope.addToJukebox = function (id) { - if (globals.settings.Debug) { console.log("LOAD JUKEBOX"); } - $.ajax({ - url: globals.BaseURL() + '/jukeboxControl.view?' + globals.BaseParams() + '&action=set&id=' + id, - method: 'GET', - dataType: globals.settings.Protocol, - timeout: globals.settings.Timeout, - success: function (data) { - /* - if (data["subsonic-response"].podcasts.channel !== undefined) { - } - deferred.resolve(podcasts); - */ - $.get(globals.BaseURL() + '/jukeboxControl.view?' + globals.BaseParams() + '&action=start'); - } - }); - }; - $rootScope.sendToJukebox = function (action) { - if (globals.settings.Debug) { console.log("SEND JUKEBOX " + action); } - $.ajax({ - url: globals.BaseURL() + '/jukeboxControl.view?' + globals.BaseParams() + '&action=' + action, - method: 'GET', - dataType: globals.settings.Protocol, - timeout: globals.settings.Timeout, - success: function (data) { - } - }); - }; - $scope.toggleStar = function (item) { subsonic.toggleStar(item).then(function (newStarred) { item.starred = newStarred; diff --git a/app/common/main-controller_test.js b/app/common/main-controller_test.js index 8780b1f..14496c2 100644 --- a/app/common/main-controller_test.js +++ b/app/common/main-controller_test.js @@ -3,7 +3,7 @@ describe("Main controller", function () { 'use strict'; var controllerParams, $controller, $q, scope, mockGlobals, player, utils, persistence, subsonic, notifications, - deferred; + deferred, mockKeypress; beforeEach(function () { mockGlobals = { settings: { @@ -18,6 +18,16 @@ describe("Main controller", function () { $provide.value('globals', mockGlobals); }); + // Mock a keypress to the application + mockKeypress = function(scope, key, target){ + scope.processKeyEvent({ + key: key, + target: target, + isDefaultPrevented: function(){}, + preventDefault: function(){} + }); + } + // Mock the player service player = jasmine.createSpyObj("player", [ "togglePause", @@ -78,10 +88,6 @@ describe("Main controller", function () { }); }); - xdescribe("toggleSetting -", function () { - - }); - describe("", function () { beforeEach(function () { $controller('AppController', controllerParams); @@ -99,24 +105,16 @@ describe("Main controller", function () { expect(scope.showQueue).toHaveBeenCalled(); }); - describe("When I toggle pause,", function () { - it("given that we're using the Jukebox mode, it sends a 'stop' command to the jukebox", function () { - mockGlobals.settings.Jukebox = true; - spyOn(scope, "sendToJukebox"); - - scope.togglePause(); - expect(scope.sendToJukebox).toHaveBeenCalledWith('stop'); - }); - - it("it toggles pause using the player service", function () { - scope.togglePause(); + describe("When I toggle pause using the keyboard shortcut,", function () { + it("it toggles pause on the player service", function () { + mockKeypress(scope, ' '); expect(player.togglePause).toHaveBeenCalled(); }); }); it("When I turn the volume up, it sets the player's volume up and saves it using the persistence service", function () { player.turnVolumeUp.and.returnValue(0.6); - scope.turnVolumeUp(); + mockKeypress(scope, '+'); expect(player.turnVolumeUp).toHaveBeenCalled(); expect(persistence.saveVolume).toHaveBeenCalledWith(0.6); @@ -124,52 +122,49 @@ describe("Main controller", function () { it("When I turn the volume down, it sets the player's volume down and saves it using the persistence service", function () { player.turnVolumeDown.and.returnValue(0.4); - scope.turnVolumeDown(); + mockKeypress(scope, '-'); expect(player.turnVolumeDown).toHaveBeenCalled(); expect(persistence.saveVolume).toHaveBeenCalledWith(0.4); }); it("When I go to the next track, it calls next track on the player", function () { - scope.nextTrack(); + mockKeypress(scope, 'ArrowRight'); expect(player.nextTrack).toHaveBeenCalled(); }); it("When I go to the previous track, it calls previous track on the player", function () { - scope.previousTrack(); + mockKeypress(scope, 'ArrowLeft'); expect(player.previousTrack).toHaveBeenCalled(); }); describe("Given that I am targeting an input,", function () { - var event; - beforeEach(function () { - event = { target: { tagName: "INPUT" } }; - }); + var target = { 'tagName': "iNPUt" } ; it("when I use a shortcut to toggle pause, it doesn't do anything", function () { - scope.togglePause(event); + mockKeypress(scope, ' ', target); expect(player.togglePause).not.toHaveBeenCalled(); }); it("when I use a shortcut to turn the volume up, it doesn't do anything", function () { - scope.turnVolumeUp(event); + mockKeypress(scope, '+', target); expect(player.turnVolumeUp).not.toHaveBeenCalled(); expect(persistence.saveVolume).not.toHaveBeenCalled(); }); it("when I use a shortcut to turn the volume down, it doesn't do anything", function () { - scope.turnVolumeDown(event); + mockKeypress(scope, '-', target); 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); + mockKeypress(scope, 'RightArrow', target); 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); + mockKeypress(scope, 'LeftArrow', target); expect(player.previousTrack).not.toHaveBeenCalled(); }); }); diff --git a/app/common/songs.html b/app/common/songs.html index ce7d216..0503db2 100644 --- a/app/common/songs.html +++ b/app/common/songs.html @@ -1,10 +1,10 @@
  • - - - - - + + + + +
    diff --git a/app/index.html b/app/index.html index 6bdc810..20ab872 100755 --- a/app/index.html +++ b/app/index.html @@ -26,7 +26,7 @@ - +