diff --git a/app/assets/javascripts/discourse/components/composer-editor.js.es6 b/app/assets/javascripts/discourse/components/composer-editor.js.es6 index db6cde6c0f1..d5e0c8fe3ae 100644 --- a/app/assets/javascripts/discourse/components/composer-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/composer-editor.js.es6 @@ -108,6 +108,13 @@ export default Ember.Component.extend({ this._resetUpload(true); }, + @observes("focusTarget") + setFocus() { + if (this.get("focusTarget") === "editor") { + this.$("textarea").putCursorAtEnd(); + } + }, + @computed markdownOptions() { return { diff --git a/app/assets/javascripts/discourse/components/d-editor.js.es6 b/app/assets/javascripts/discourse/components/d-editor.js.es6 index fca09f4288d..73afe5e34e5 100644 --- a/app/assets/javascripts/discourse/components/d-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/d-editor.js.es6 @@ -256,15 +256,6 @@ export default Ember.Component.extend({ const mouseTrap = Mousetrap(this.$(".d-editor-input")[0]); const shortcuts = this.get("toolbar.shortcuts"); - // for some reason I am having trouble bubbling this so hack it in - mouseTrap.bind(["ctrl+alt+f"], event => { - this.appEvents.trigger("header:keyboard-trigger", { - type: "search", - event - }); - return true; - }); - Object.keys(shortcuts).forEach(sc => { const button = shortcuts[sc]; mouseTrap.bind(sc, () => { @@ -323,7 +314,6 @@ export default Ember.Component.extend({ Object.keys(this.get("toolbar.shortcuts")).forEach(sc => mouseTrap.unbind(sc) ); - mouseTrap.unbind("ctrl+/", "command+/"); this.$(".d-editor-preview").off("click.preview"); }, diff --git a/app/assets/javascripts/discourse/controllers/composer.js.es6 b/app/assets/javascripts/discourse/controllers/composer.js.es6 index aa077e74676..4d299ef2f22 100644 --- a/app/assets/javascripts/discourse/controllers/composer.js.es6 +++ b/app/assets/javascripts/discourse/controllers/composer.js.es6 @@ -133,9 +133,10 @@ export default Ember.Controller.extend({ @computed( "model.replyingToTopic", "model.creatingPrivateMessage", - "model.targetUsernames" + "model.targetUsernames", + "model.composeState" ) - focusTarget(replyingToTopic, creatingPM, usernames) { + focusTarget(replyingToTopic, creatingPM, usernames, composeState) { if (this.capabilities.isIOS) { return "none"; } @@ -153,6 +154,10 @@ export default Ember.Controller.extend({ return "reply"; } + if (composeState === Composer.FULLSCREEN) { + return "editor"; + } + return "title"; }, diff --git a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 index fb690805c6a..2fbc5c4cf36 100644 --- a/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 +++ b/app/assets/javascripts/discourse/lib/keyboard-shortcuts.js.es6 @@ -7,7 +7,7 @@ const bindings = { "!": { postAction: "showFlags" }, "#": { handler: "goToPost", anonymous: true }, "/": { handler: "toggleSearch", anonymous: true }, - "ctrl+alt+f": { handler: "toggleSearch", anonymous: true }, + "ctrl+alt+f": { handler: "toggleSearch", anonymous: true, global: true }, "=": { handler: "toggleHamburgerMenu", anonymous: true }, "?": { handler: "showHelpModal", anonymous: true }, ".": { click: ".alert.alert-info.clickable", anonymous: true }, // show incoming/updated topics @@ -67,7 +67,7 @@ const bindings = { "shift+s": { click: "#topic-footer-buttons button.share", anonymous: true }, // share topic "shift+u": { handler: "goToUnreadPost" }, "shift+z shift+z": { handler: "logout" }, - "shift+f11": { handler: "fullscreenComposer" }, + "shift+f11": { handler: "fullscreenComposer", global: true }, t: { postAction: "replyAsNewTopic" }, u: { handler: "goBack", anonymous: true }, "x r": { @@ -101,7 +101,12 @@ export default { if (binding.path) { this._bindToPath(binding.path, key); } else if (binding.handler) { - this._bindToFunction(binding.handler, key); + if (binding.global) { + // global shortcuts will trigger even while focusing on input/textarea + this._globalBindToFunction(binding.handler, key); + } else { + this._bindToFunction(binding.handler, key); + } } else if (binding.postAction) { this._bindToSelectedPost(binding.postAction, key); } else if (binding.click) { @@ -399,6 +404,12 @@ export default { }); }, + _globalBindToFunction(func, binding) { + if (typeof this[func] === "function") { + this.keyTrapper.bindGlobal(binding, _.bind(this[func], this)); + } + }, + _bindToFunction(func, binding) { if (typeof this[func] === "function") { this.keyTrapper.bind(binding, _.bind(this[func], this)); diff --git a/app/assets/javascripts/discourse/templates/composer.hbs b/app/assets/javascripts/discourse/templates/composer.hbs index 4f313982e77..9f512baa83d 100644 --- a/app/assets/javascripts/discourse/templates/composer.hbs +++ b/app/assets/javascripts/discourse/templates/composer.hbs @@ -111,7 +111,8 @@ importQuote=(action "importQuote") togglePreview=(action "togglePreview") showToolbar=showToolbar - afterRefresh=(action "afterRefresh")}} + afterRefresh=(action "afterRefresh") + focusTarget=focusTarget}}
{{plugin-outlet name="composer-fields-below" args=(hash model=model)}} diff --git a/app/assets/javascripts/vendor.js b/app/assets/javascripts/vendor.js index 76a27a347e3..5279b2c8912 100644 --- a/app/assets/javascripts/vendor.js +++ b/app/assets/javascripts/vendor.js @@ -22,6 +22,7 @@ //= require jquery.sortable.js //= require lodash.js //= require mousetrap.js +//= require mousetrap-global-bind.js //= require rsvp.js //= require show-html.js //= require break_string diff --git a/lib/tasks/javascript.rake b/lib/tasks/javascript.rake index 2a06412b1e0..c2a1004a585 100644 --- a/lib/tasks/javascript.rake +++ b/lib/tasks/javascript.rake @@ -73,6 +73,8 @@ task 'javascript:update' do destination: 'moment-locale', }, { source: 'moment-timezone/builds/moment-timezone-with-data.js' + }, { + source: 'mousetrap/plugins/global-bind/mousetrap-global-bind.js' }, { source: 'resumablejs/resumable.js' }, { diff --git a/vendor/assets/javascripts/mousetrap-global-bind.js b/vendor/assets/javascripts/mousetrap-global-bind.js new file mode 100644 index 00000000000..1c4ac1487a7 --- /dev/null +++ b/vendor/assets/javascripts/mousetrap-global-bind.js @@ -0,0 +1,43 @@ +/** + * adds a bindGlobal method to Mousetrap that allows you to + * bind specific keyboard shortcuts that will still work + * inside a text input field + * + * usage: + * Mousetrap.bindGlobal('ctrl+s', _saveChanges); + */ +/* global Mousetrap:true */ +(function(Mousetrap) { + var _globalCallbacks = {}; + var _originalStopCallback = Mousetrap.prototype.stopCallback; + + Mousetrap.prototype.stopCallback = function(e, element, combo, sequence) { + var self = this; + + if (self.paused) { + return true; + } + + if (_globalCallbacks[combo] || _globalCallbacks[sequence]) { + return false; + } + + return _originalStopCallback.call(self, e, element, combo); + }; + + Mousetrap.prototype.bindGlobal = function(keys, callback, action) { + var self = this; + self.bind(keys, callback, action); + + if (keys instanceof Array) { + for (var i = 0; i < keys.length; i++) { + _globalCallbacks[keys[i]] = true; + } + return; + } + + _globalCallbacks[keys] = true; + }; + + Mousetrap.init(); +}) (Mousetrap);