DEV: uses forked Mousetrap to avoid leaking listeners (#14198)

This commit is contained in:
Joffrey JAFFEUX 2021-09-08 14:48:13 +02:00 committed by GitHub
parent e1581f6dfd
commit 95b15acb1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1217 additions and 1177 deletions

View File

@ -2,11 +2,6 @@ define("message-bus-client", ["exports"], function (__exports__) {
__exports__.default = window.MessageBus;
});
define("mousetrap-global-bind", ["exports"], function (__exports__) {
// In the Rails app it's applied from the vendored file
__exports__.default = {};
});
define("ember-buffered-proxy/proxy", ["exports"], function (__exports__) {
__exports__.default = window.BufferedProxy;
});
@ -19,8 +14,8 @@ define("xss", ["exports"], function (__exports__) {
__exports__.default = window.filterXSS;
});
define("mousetrap", ["exports"], function (__exports__) {
__exports__.default = window.Mousetrap;
define("@discourse/itsatrap", ["exports"], function (__exports__) {
__exports__.default = window.ItsATrap;
});
define("@popperjs/core", ["exports"], function (__exports__) {

View File

@ -1,5 +1,4 @@
import Application from "@ember/application";
import Mousetrap from "mousetrap";
import { buildResolver } from "discourse-common/resolver";
import { isTesting } from "discourse-common/config/environment";
@ -13,11 +12,6 @@ const Discourse = Application.extend({
paste: "paste",
},
reset() {
this._super(...arguments);
Mousetrap.reset();
},
Resolver: buildResolver("discourse"),
_prepareInitializer(moduleName) {

View File

@ -11,7 +11,7 @@ import { AUTO_DELETE_PREFERENCES } from "discourse/models/bookmark";
import Component from "@ember/component";
import I18n from "I18n";
import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts";
import Mousetrap from "mousetrap";
import ItsATrap from "@discourse/itsatrap";
import { Promise } from "rsvp";
import { TIME_SHORTCUT_TYPES } from "discourse/lib/time-shortcut";
import { action } from "@ember/object";
@ -37,6 +37,7 @@ export default Component.extend({
_savingBookmarkManually: null,
_saving: null,
_deleting: null,
_itsatrap: null,
postDetectedLocalDate: null,
postDetectedLocalTime: null,
postDetectedLocalTimezone: null,
@ -44,7 +45,6 @@ export default Component.extend({
userTimezone: null,
showOptions: null,
model: null,
afterSave: null,
@on("init")
@ -62,6 +62,7 @@ export default Component.extend({
prefilledDatetime: null,
userTimezone: this.currentUser.resolvedTimezone(this.currentUser),
showOptions: false,
_itsatrap: new ItsATrap(),
});
this.registerOnCloseHandler(this._onModalClose.bind(this));
@ -123,9 +124,8 @@ export default Component.extend({
_bindKeyboardShortcuts() {
KeyboardShortcuts.pause();
this._mousetrap = new Mousetrap();
Object.keys(BOOKMARK_BINDINGS).forEach((shortcut) => {
this._mousetrap.bind(shortcut, () => {
this._itsatrap.bind(shortcut, () => {
let binding = BOOKMARK_BINDINGS[shortcut];
this.send(binding.handler);
return false;
@ -266,7 +266,9 @@ export default Component.extend({
willDestroyElement() {
this._super(...arguments);
this._mousetrap.reset();
this._itsatrap?.destroy();
this.set("_itsatrap", null);
KeyboardShortcuts.unpause();
},

View File

@ -9,7 +9,7 @@ import { emojiUrlFor, generateCookFunction } from "discourse/lib/text";
import { later, schedule, scheduleOnce } from "@ember/runloop";
import Component from "@ember/component";
import I18n from "I18n";
import Mousetrap from "mousetrap";
import ItsATrap from "@discourse/itsatrap";
import { Promise } from "rsvp";
import { SKIP } from "discourse/lib/autocomplete";
import { categoryHashtagTriggerRule } from "discourse/lib/category-hashtags";
@ -238,7 +238,7 @@ export default Component.extend(TextareaTextManipulation, {
classNames: ["d-editor"],
ready: false,
lastSel: null,
_mouseTrap: null,
_itsatrap: null,
showLink: true,
emojiPickerIsActive: false,
emojiStore: service("emoji-store"),
@ -278,12 +278,12 @@ export default Component.extend(TextareaTextManipulation, {
scheduleOnce("afterRender", this, this._readyNow);
this._mouseTrap = new Mousetrap(this._textarea);
this._itsatrap = new ItsATrap(this._textarea);
const shortcuts = this.get("toolbar.shortcuts");
Object.keys(shortcuts).forEach((sc) => {
const button = shortcuts[sc];
this._mouseTrap.bind(sc, () => {
this._itsatrap.bind(sc, () => {
button.action(button);
return false;
});
@ -335,7 +335,9 @@ export default Component.extend(TextareaTextManipulation, {
this.appEvents.off("composer:replace-text", this, "_replaceText");
}
this._mouseTrap.reset();
this._itsatrap?.destroy();
this._itsatrap = null;
$(this.element.querySelector(".d-editor-preview")).off("click.preview");
if (isTesting()) {

View File

@ -21,7 +21,7 @@ import {
thisWeekend,
} from "discourse/lib/time-utils";
import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts";
import Mousetrap from "mousetrap";
import ItsATrap from "@discourse/itsatrap";
export default Component.extend({
statusType: readOnly("topicTimer.status_type"),
@ -43,12 +43,13 @@ export default Component.extend({
"autoCloseAfterLastPost"
),
duration: null,
_itsatrap: null,
init() {
this._super(...arguments);
KeyboardShortcuts.pause();
this._mousetrap = new Mousetrap();
this.set("_itsatrap", new ItsATrap());
this.set("duration", this.initialDuration);
},
@ -65,7 +66,9 @@ export default Component.extend({
willDestroyElement() {
this._super(...arguments);
this._mousetrap.reset();
this._itsatrap.destroy();
this.set("_itsatrap", null);
KeyboardShortcuts.unpause();
},

View File

@ -5,7 +5,7 @@ import PanEvents, {
import { cancel, later, schedule } from "@ember/runloop";
import Docking from "discourse/mixins/docking";
import MountWidget from "discourse/components/mount-widget";
import Mousetrap from "mousetrap";
import ItsATrap from "@discourse/itsatrap";
import RerenderOnDoNotDisturbChange from "discourse/mixins/rerender-on-do-not-disturb-change";
import { observes } from "discourse-common/utils/decorators";
import { topicTitleDecorators } from "discourse/components/topic-title";
@ -24,7 +24,7 @@ const SiteHeaderComponent = MountWidget.extend(
_panMenuOffset: 0,
_scheduledRemoveAnimate: null,
_topic: null,
_mousetrap: null,
_itsatrap: null,
@observes(
"currentUser.unread_notifications",
@ -258,8 +258,8 @@ const SiteHeaderComponent = MountWidget.extend(
}
const header = document.querySelector("header.d-header");
this._mousetrap = new Mousetrap(header);
this._mousetrap.bind(["right", "left"], (e) => {
this._itsatrap = new ItsATrap(header);
this._itsatrap.bind(["right", "left"], (e) => {
const activeTab = document.querySelector(".glyphs .menu-link.active");
if (activeTab) {
@ -294,7 +294,8 @@ const SiteHeaderComponent = MountWidget.extend(
cancel(this._scheduledRemoveAnimate);
this._mousetrap.reset();
this._itsatrap?.destroy();
this._itsatrap = null;
document.removeEventListener("click", this._dismissFirstNotification);
},

View File

@ -67,6 +67,8 @@ export default Component.extend({
customDate: null,
customTime: null,
_itsatrap: null,
defaultCustomReminderTime: `0${START_OF_DAY_HOUR}:00`,
@on("init")
@ -101,7 +103,8 @@ export default Component.extend({
willDestroyElement() {
this._super(...arguments);
this.mousetrap.unbind(Object.keys(BINDINGS));
this._itsatrap.unbind(Object.keys(BINDINGS));
},
parsePrefilledDatetime() {
@ -143,7 +146,7 @@ export default Component.extend({
_bindKeyboardShortcuts() {
Object.keys(BINDINGS).forEach((shortcut) => {
this.mousetrap.bind(shortcut, () => {
this._itsatrap.bind(shortcut, () => {
let binding = BINDINGS[shortcut];
this.send(binding.handler, ...binding.args);
return false;

View File

@ -1,15 +1,11 @@
import KeyboardShortcuts from "discourse/lib/keyboard-shortcuts";
import Mousetrap from "mousetrap";
import bindGlobal from "mousetrap-global-bind";
import ItsATrap from "@discourse/itsatrap";
export default {
name: "keyboard-shortcuts",
initialize(container) {
// Ensure mousetrap-global-bind is executed
void bindGlobal;
KeyboardShortcuts.init(Mousetrap, container);
KeyboardShortcuts.init(ItsATrap, container);
KeyboardShortcuts.bindEvents();
},

View File

@ -122,10 +122,8 @@ export default {
},
teardown() {
if (this.keyTrapper) {
this.keyTrapper.reset();
this.keyTrapper = null;
}
this.keyTrapper?.destroy();
this.keyTrapper = null;
this.container = null;
},
@ -207,7 +205,7 @@ export default {
**/
addShortcut(shortcut, callback, opts = {}) {
// we trim but leave whitespace between characters, as shortcuts
// like `z z` are valid for Mousetrap
// like `z z` are valid for ItsATrap
shortcut = shortcut.trim();
let newBinding = Object.assign({ handler: callback }, opts);
this.bindKey(shortcut, newBinding);

View File

@ -40,7 +40,7 @@
onTimeSelected=(action "onTimeSelected")
customOptions=customTimeShortcutOptions
additionalOptionsToShow=additionalTimeShortcutOptions
mousetrap=_mousetrap
_itsatrap=_itsatrap
}}
{{else}}
<div class="alert alert-info">{{html-safe (i18n "bookmarks.no_timezone" basePath=(base-path))}}</div>

View File

@ -24,7 +24,7 @@
onTimeSelected=onTimeSelected
customOptions=customTimeShortcutOptions
hiddenOptions=hiddenTimeShortcutOptions
mousetrap=_mousetrap
_itsatrap=_itsatrap
}}
{{/if}}
{{#if useDuration}}

View File

@ -53,8 +53,6 @@
"loader.js": "^4.7.0",
"message-bus-client": "^3.3.0",
"messageformat": "0.1.5",
"mousetrap": "^1.6.5",
"mousetrap-global-bind": "^1.1.0",
"pretender": "^3.4.7",
"pretty-text": "^1.0.0",
"qunit": "^2.14.0",
@ -62,7 +60,8 @@
"sass": "^1.32.8",
"select-kit": "^1.0.0",
"sinon": "^9.2.0",
"virtual-dom": "^2.1.1"
"virtual-dom": "^2.1.1",
"@discourse/itsatrap": "^2.0.10"
},
"engines": {
"node": ">= 12.*",

View File

@ -25,8 +25,7 @@
//= require jquery.tagsinput.js
//= require jquery.sortable.js
//= require lodash.js
//= require mousetrap.js
//= require mousetrap-global-bind.js
//= require itsatrap.js
//= require rsvp.js
//= require show-html.js
//= require uppy.js

View File

@ -18,8 +18,7 @@
//= require jquery.tagsinput.js
//= require jquery.sortable.js
//= require lodash.js
//= require mousetrap.js
//= require mousetrap-global-bind.js
//= require itsatrap.js
//= require rsvp.js
//= require show-html.js
//= require uppy.js

View File

@ -928,6 +928,11 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@discourse/itsatrap@^2.0.10":
version "2.0.10"
resolved "https://registry.yarnpkg.com/@discourse/itsatrap/-/itsatrap-2.0.10.tgz#c7e750eeb32b54e769e952c4ecc472213eb1385a"
integrity sha512-Jn1gdiyHMGUsmUfLFf4Q7VnTAv0l7NePbegU6pKhKHEmbzV3FosGxq30fTOYgVyTS1bxqGjlA6LvQttJpv3ROw==
"@ember-data/rfc395-data@^0.0.4":
version "0.0.4"
resolved "https://registry.yarnpkg.com/@ember-data/rfc395-data/-/rfc395-data-0.0.4.tgz#ecb86efdf5d7733a76ff14ea651a1b0ed1f8a843"
@ -8586,16 +8591,6 @@ morgan@^1.10.0:
on-finished "~2.3.0"
on-headers "~1.0.2"
mousetrap-global-bind@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mousetrap-global-bind/-/mousetrap-global-bind-1.1.0.tgz#cd7de9222bd0646fa2e010d54c84a74c26a88edd"
integrity sha1-zX3pIivQZG+i4BDVTISnTCaojt0=
mousetrap@^1.6.5:
version "1.6.5"
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.6.5.tgz#8a766d8c272b08393d5f56074e0b5ec183485bf9"
integrity sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==
mout@^1.0.0:
version "1.2.2"
resolved "https://registry.yarnpkg.com/mout/-/mout-1.2.2.tgz#c9b718a499806a0632cede178e80f436259e777d"

View File

@ -123,7 +123,7 @@ def dependencies
}, {
source: 'markdown-it/dist/markdown-it.js'
}, {
source: 'mousetrap/mousetrap.js'
source: '@discourse/itsatrap/itsatrap.js'
}, {
source: 'moment/moment.js'
}, {
@ -138,8 +138,6 @@ def dependencies
}, {
source: 'moment-timezone-names-translations/locales/.',
destination: 'moment-timezone-names-locale'
}, {
source: 'mousetrap/plugins/global-bind/mousetrap-global-bind.js'
}, {
source: 'resumablejs/resumable.js'
}, {

View File

@ -10,9 +10,9 @@
"@highlightjs/cdn-assets": "^10.6.0",
"@json-editor/json-editor": "^2.5.2",
"@popperjs/core": "v2.9.3",
"@uppy/core": "^2.0.1",
"@uppy/aws-s3": "^2.0.1",
"@uppy/aws-s3-multipart": "^2.0.2",
"@uppy/core": "^2.0.1",
"@uppy/drop-target": "^1.0.1",
"@uppy/xhr-upload": "^2.0.1",
"ace-builds": "1.4.12",
@ -33,7 +33,6 @@
"moment": "2.29.1",
"moment-timezone": "0.5.31",
"moment-timezone-names-translations": "https://github.com/discourse/moment-timezone-names-translations",
"mousetrap": "https://github.com/discourse/mousetrap#firefox-alt-key",
"pikaday": "1.8.0",
"resumablejs": "1.1.0",
"spectrum-colorpicker": "1.8.0",
@ -42,7 +41,8 @@
"workbox-expiration": "^4.3.1",
"workbox-routing": "^4.3.1",
"workbox-strategies": "^4.3.1",
"workbox-sw": "^4.3.1"
"workbox-sw": "^4.3.1",
"@discourse/itsatrap": "^2.0.10"
},
"devDependencies": {
"@arkweid/lefthook": "^0.7.2",

1159
vendor/assets/javascripts/itsatrap.js vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,46 +0,0 @@
/**
* 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) {
if (! Mousetrap) {
return;
}
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();
}) (typeof Mousetrap !== "undefined" ? Mousetrap : undefined);

File diff suppressed because it is too large Load Diff

View File

@ -245,6 +245,11 @@
exec-sh "^0.3.2"
minimist "^1.2.0"
"@discourse/itsatrap@^2.0.10":
version "2.0.10"
resolved "https://registry.yarnpkg.com/@discourse/itsatrap/-/itsatrap-2.0.10.tgz#c7e750eeb32b54e769e952c4ecc472213eb1385a"
integrity sha512-Jn1gdiyHMGUsmUfLFf4Q7VnTAv0l7NePbegU6pKhKHEmbzV3FosGxq30fTOYgVyTS1bxqGjlA6LvQttJpv3ROw==
"@ember-data/rfc395-data@^0.0.4":
version "0.0.4"
resolved "https://registry.yarnpkg.com/@ember-data/rfc395-data/-/rfc395-data-0.0.4.tgz#ecb86efdf5d7733a76ff14ea651a1b0ed1f8a843"
@ -3116,10 +3121,6 @@ moment@2.29.1:
resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.0.tgz#fcbef955844d91deb55438613ddcec56e86a3425"
integrity sha512-z6IJ5HXYiuxvFTI6eiQ9dm77uE0gyy1yXNApVHqTcnIKfY9tIwEjlzsZ6u1LQXvVgKeTnv9Xm7NDvJ7lso3MtA==
"mousetrap@https://github.com/discourse/mousetrap#firefox-alt-key":
version "1.6.5"
resolved "https://github.com/discourse/mousetrap#cc8e2c0b9229e1a01ce68de4f339b6fd35503041"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"