diff --git a/Changelog.md b/Changelog.md index 4ee6a29ab86b7f99bff24a31d17167ce176af1ff..b285e50b6ca5fd0eeba214a602771ec964ec612a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -15,6 +15,7 @@ Note: Although this is a minor release, the configuration file changed because t * Refactored post interactions on the single post view [#7089](https://github.com/diaspora/diaspora/pull/7089) * Extract inline JavaScript [#7113](https://github.com/diaspora/diaspora/pull/7113) * Port conversations inbox to backbone.js [#7108](https://github.com/diaspora/diaspora/pull/7108) +* Refactored stream shortcuts for more flexibility [#7127](https://github.com/diaspora/diaspora/pull/7127) ## Bug fixes * Post comments no longer get collapsed when interacting with a post [#7040](https://github.com/diaspora/diaspora/pull/7040) diff --git a/app/assets/javascripts/app/helpers/shortcuts.js b/app/assets/javascripts/app/helpers/shortcuts.js new file mode 100644 index 0000000000000000000000000000000000000000..c8d79f3fad5dff5fe4df32bd580661e2bd1e6531 --- /dev/null +++ b/app/assets/javascripts/app/helpers/shortcuts.js @@ -0,0 +1,31 @@ +// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3-or-Later +(function() { + app.helpers.Shortcuts = function(evtname, fn) { + var textAcceptingInputTypes = [ + "color", + "date", + "datetime", + "datetime-local", + "email", + "month", + "number", + "password", + "range", + "search", + "select", + "text", + "textarea", + "time", + "url", + "week" + ]; + + $("body").on(evtname, function(event) { + // make sure that the user is not typing in an input field + if (textAcceptingInputTypes.indexOf(event.target.type) === -1) { + fn(event); + } + }); + }; +})(); +// @license-end diff --git a/app/assets/javascripts/app/views/stream/shortcuts.js b/app/assets/javascripts/app/views/stream/shortcuts.js index 56d9df05d7bf016da56335f5809c8b3a96f72919..a19a1fa060ae5a2fd56bd20666bb1e0c4b857cfb 100644 --- a/app/assets/javascripts/app/views/stream/shortcuts.js +++ b/app/assets/javascripts/app/views/stream/shortcuts.js @@ -3,18 +3,12 @@ app.views.StreamShortcuts = Backbone.View.extend({ _headerSize: 60, - events: { - "keydown": "_onHotkeyDown", - "keyup": "_onHotkeyUp" + initialize: function() { + app.helpers.Shortcuts("keydown", this._onHotkeyDown.bind(this)); + app.helpers.Shortcuts("keyup", this._onHotkeyUp.bind(this)); }, _onHotkeyDown: function(event) { - //make sure that the user is not typing in an input field - var textAcceptingInputTypes = ["textarea", "select", "text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", "datetime-local", "search", "color"]; - if(jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1){ - return; - } - // trigger the events based on what key was pressed switch (String.fromCharCode( event.which ).toLowerCase()) { case "j": @@ -28,12 +22,6 @@ app.views.StreamShortcuts = Backbone.View.extend({ }, _onHotkeyUp: function(event) { - //make sure that the user is not typing in an input field - var textAcceptingInputTypes = ["textarea", "select", "text", "password", "number", "email", "url", "range", "date", "month", "week", "time", "datetime", "datetime-local", "search", "color"]; - if(jQuery.inArray(event.target.type, textAcceptingInputTypes) > -1){ - return; - } - // trigger the events based on what key was pressed switch (String.fromCharCode( event.which ).toLowerCase()) { case "c": diff --git a/spec/javascripts/app/helpers/shortcuts_spec.js b/spec/javascripts/app/helpers/shortcuts_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..b738d50c4b5790afb71d637816d071d1c58e7604 --- /dev/null +++ b/spec/javascripts/app/helpers/shortcuts_spec.js @@ -0,0 +1,17 @@ +describe("app.helpers.Shortcuts", function() { + it("calls the function when the event has been fired outside of an input field", function() { + var spy = jasmine.createSpy(); + spec.content().append("<div class='hotkey-div'></div>"); + app.helpers.Shortcuts("keydown", spy); + $(".hotkey-div").trigger("keydown"); + expect(spy).toHaveBeenCalled(); + }); + + it("doesn't call the function when the event has been fired in an input field", function() { + var spy = jasmine.createSpy(); + spec.content().append("<textarea class='hotkey-textarea'></textarea>"); + app.helpers.Shortcuts("keydown", spy); + $(".hotkey-textarea").trigger("keydown"); + expect(spy).not.toHaveBeenCalled(); + }); +}); diff --git a/spec/javascripts/app/views/stream/shortcuts_spec.js b/spec/javascripts/app/views/stream/shortcuts_spec.js index 3933c63244f465c19a3c1eadecf7eaa500ca349f..ba58b54c13363f4751a12c472925a3e254912dbe 100644 --- a/spec/javascripts/app/views/stream/shortcuts_spec.js +++ b/spec/javascripts/app/views/stream/shortcuts_spec.js @@ -13,131 +13,95 @@ describe("app.views.StreamShortcuts", function () { expect(spec.content().find("div.stream-element.loaded").length).toBe(2); }); - describe("pressing 'j'", function(){ - it("should call 'gotoNext' if not pressed in an input field", function(){ - spyOn(this.view, 'gotoNext'); - var e = $.Event("keydown", { which: Keycodes.J, target: {type: "div"} }); - this.view._onHotkeyDown(e); - expect(this.view.gotoNext).toHaveBeenCalled(); - }); - - it("'gotoNext' should call 'selectPost'", function(){ - spyOn(this.view, 'selectPost'); - this.view.gotoNext(); - expect(this.view.selectPost).toHaveBeenCalled(); - }); - - it("shouldn't do anything if the user types in an input field", function(){ - spyOn(this.view, 'gotoNext'); - spyOn(this.view, 'selectPost'); - var e = $.Event("keydown", { which: Keycodes.J, target: {type: "textarea"} }); - this.view._onHotkeyDown(e); - expect(this.view.gotoNext).not.toHaveBeenCalled(); - expect(this.view.selectPost).not.toHaveBeenCalled(); + describe("initialize", function() { + it("setups the shortcuts", function() { + spyOn(app.helpers, "Shortcuts").and.callThrough(); + spyOn(app.views.StreamShortcuts.prototype, "_onHotkeyDown"); + spyOn(app.views.StreamShortcuts.prototype, "_onHotkeyUp"); + this.view = new app.views.StreamShortcuts({el: $(document)}); + expect(app.helpers.Shortcuts.calls.count()).toBe(2); + + $("body").trigger($.Event("keydown", {which: Keycodes.J, target: {type: "textarea"}})); + $("body").trigger($.Event("keyup", {which: Keycodes.J, target: {type: "textarea"}})); + expect(app.views.StreamShortcuts.prototype._onHotkeyDown).not.toHaveBeenCalled(); + expect(app.views.StreamShortcuts.prototype._onHotkeyUp).not.toHaveBeenCalled(); + + var e = $.Event("keydown", {which: Keycodes.J, target: {type: "div"}}); + $("body").trigger(e); + expect(app.views.StreamShortcuts.prototype._onHotkeyDown).toHaveBeenCalledWith(e); + + e = $.Event("keyup", {which: Keycodes.J, target: {type: "div"}}); + $("body").trigger(e); + expect(app.views.StreamShortcuts.prototype._onHotkeyUp).toHaveBeenCalledWith(e); }); }); - describe("pressing 'k'", function(){ - it("should call 'gotoPrev' if not pressed in an input field", function(){ - spyOn(this.view, 'gotoPrev'); - var e = $.Event("keydown", { which: Keycodes.K, target: {type: "div"} }); + describe("_onHotkeyDown", function() { + it("calls goToNext when the user pressed 'J'", function() { + spyOn(this.view, "gotoNext"); + var e = $.Event("keydown", {which: Keycodes.J, target: {type: "div"}}); this.view._onHotkeyDown(e); - expect(this.view.gotoPrev).toHaveBeenCalled(); - }); - - it("'gotoPrev' should call 'selectPost'", function(){ - spyOn(this.view, 'selectPost'); - this.view.gotoPrev(); - expect(this.view.selectPost).toHaveBeenCalled(); + expect(this.view.gotoNext).toHaveBeenCalled(); }); - it("shouldn't do anything if the user types in an input field", function(){ - spyOn(this.view, 'gotoPrev'); - spyOn(this.view, 'selectPost'); - var e = $.Event("keydown", { which: Keycodes.K, target: {type: "textarea"} }); + it("calls gotoPrev when the user pressed 'K'", function() { + spyOn(this.view, "gotoPrev"); + var e = $.Event("keydown", {which: Keycodes.K, target: {type: "div"}}); this.view._onHotkeyDown(e); - expect(this.view.gotoPrev).not.toHaveBeenCalled(); - expect(this.view.selectPost).not.toHaveBeenCalled(); + expect(this.view.gotoPrev).toHaveBeenCalled(); }); }); - describe("pressing 'c'", function(){ - it("should click on the comment-button if not pressed in an input field", function(){ - spyOn(this.view, 'commentSelected'); - var e = $.Event("keyup", { which: Keycodes.C, target: {type: "div"} }); + describe("_onHotkeyUp", function() { + it("calls commentSelected when the user pressed 'C'", function() { + spyOn(this.view, "commentSelected"); + var e = $.Event("keyup", {which: Keycodes.C, target: {type: "div"}}); this.view._onHotkeyUp(e); expect(this.view.commentSelected).toHaveBeenCalled(); }); - it("shouldn't do anything if the user types in an input field", function(){ - spyOn(this.view, 'commentSelected'); - var e = $.Event("keyup", { which: Keycodes.C, target: {type: "textarea"} }); - this.view._onHotkeyUp(e); - expect(this.view.commentSelected).not.toHaveBeenCalled(); - }); - }); - - describe("pressing 'l'", function(){ - it("should click on the like-button if not pressed in an input field", function(){ - spyOn(this.view, 'likeSelected'); - var e = $.Event("keyup", { which: Keycodes.L, target: {type: "div"} }); + it("calls likeSelected when the user pressed 'L'", function() { + spyOn(this.view, "likeSelected"); + var e = $.Event("keyup", {which: Keycodes.L, target: {type: "div"}}); this.view._onHotkeyUp(e); expect(this.view.likeSelected).toHaveBeenCalled(); }); - it("shouldn't do anything if the user types in an input field", function(){ - spyOn(this.view, 'likeSelected'); - var e = $.Event("keyup", { which: Keycodes.L, target: {type: "textarea"} }); + it("calls expandSelected when the user pressed 'M'", function() { + spyOn(this.view, "expandSelected"); + var e = $.Event("keyup", {which: Keycodes.M, target: {type: "div"}}); this.view._onHotkeyUp(e); - expect(this.view.likeSelected).not.toHaveBeenCalled(); + expect(this.view.expandSelected).toHaveBeenCalled(); }); - }); - describe("pressing 'r'", function(){ - it("should click on the reshare-button if not pressed in an input field", function(){ - spyOn(this.view, 'reshareSelected'); - var e = $.Event("keyup", { which: Keycodes.R, target: {type: "div"} }); + it("calls openFirstLinkSelected when the user pressed 'O'", function() { + spyOn(this.view, "openFirstLinkSelected"); + var e = $.Event("keyup", {which: Keycodes.O, target: {type: "div"}}); this.view._onHotkeyUp(e); - expect(this.view.reshareSelected).toHaveBeenCalled(); + expect(this.view.openFirstLinkSelected).toHaveBeenCalled(); }); - it("shouldn't do anything if the user types in an input field", function(){ - spyOn(this.view, 'reshareSelected'); - var e = $.Event("keyup", { which: Keycodes.R, target: {type: "textarea"} }); + it("calls reshareSelected when the user pressed 'R'", function() { + spyOn(this.view, "reshareSelected"); + var e = $.Event("keyup", {which: Keycodes.R, target: {type: "div"}}); this.view._onHotkeyUp(e); - expect(this.view.reshareSelected).not.toHaveBeenCalled(); + expect(this.view.reshareSelected).toHaveBeenCalled(); }); }); - describe("pressing 'm'", function(){ - it("should click on the more-button if not pressed in an input field", function(){ - spyOn(this.view, 'expandSelected'); - var e = $.Event("keyup", { which: Keycodes.M, target: {type: "div"} }); - this.view._onHotkeyUp(e); - expect(this.view.expandSelected).toHaveBeenCalled(); - }); - - it("shouldn't do anything if the user types in an input field", function(){ - spyOn(this.view, 'expandSelected'); - var e = $.Event("keyup", { which: Keycodes.M, target: {type: "textarea"} }); - this.view._onHotkeyUp(e); - expect(this.view.expandSelected).not.toHaveBeenCalled(); + describe("gotoNext", function() { + it("calls selectPost", function() { + spyOn(this.view, "selectPost"); + this.view.gotoNext(); + expect(this.view.selectPost).toHaveBeenCalled(); }); }); - describe("pressing 'o'", function(){ - it("should click on the more-button if not pressed in an input field", function(){ - spyOn(this.view, 'openFirstLinkSelected'); - var e = $.Event("keyup", { which: Keycodes.O, target: {type: "div"} }); - this.view._onHotkeyUp(e); - expect(this.view.openFirstLinkSelected).toHaveBeenCalled(); - }); - - it("shouldn't do anything if the user types in an input field", function(){ - spyOn(this.view, 'openFirstLinkSelected'); - var e = $.Event("keyup", { which: Keycodes.O, target: {type: "textarea"} }); - this.view._onHotkeyUp(e); - expect(this.view.openFirstLinkSelected).not.toHaveBeenCalled(); + describe("gotoPrev", function() { + it("calls selectPost", function() { + spyOn(this.view, "selectPost"); + this.view.gotoPrev(); + expect(this.view.selectPost).toHaveBeenCalled(); }); }); });