DEV: reduces jquery usage and memory leaks in composer (#14924)

Removes more than 60 jquery function leaks in one `Acceptance: Composer` run.
This commit is contained in:
Joffrey JAFFEUX 2021-11-16 10:27:05 +01:00 committed by GitHub
parent ef881fdedc
commit ae16b0a9d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 315 additions and 213 deletions

View File

@ -11,9 +11,9 @@ import discourseDebounce from "discourse-common/lib/debounce";
import { headerHeight } from "discourse/components/site-header"; import { headerHeight } from "discourse/components/site-header";
import positioningWorkaround from "discourse/lib/safari-hacks"; import positioningWorkaround from "discourse/lib/safari-hacks";
const START_EVENTS = "touchstart mousedown"; const START_DRAG_EVENTS = ["touchstart", "mousedown"];
const DRAG_EVENTS = "touchmove mousemove"; const DRAG_EVENTS = ["touchmove", "mousemove"];
const END_EVENTS = "touchend mouseup"; const END_DRAG_EVENTS = ["touchend", "mouseup"];
const THROTTLE_RATE = 20; const THROTTLE_RATE = 20;
@ -54,17 +54,15 @@ export default Component.extend(KeyEnterEscape, {
}, },
movePanels(size) { movePanels(size) {
$("#main-outlet").css("padding-bottom", size ? size : ""); document.querySelector("#main-outlet").style.paddingBottom = size
? `${size}px`
: "";
// signal the progress bar it should move! // signal the progress bar it should move!
this.appEvents.trigger("composer:resized"); this.appEvents.trigger("composer:resized");
}, },
@observes( @observes("composeState", "composer.{action,canEditTopicFeaturedLink}")
"composeState",
"composer.action",
"composer.canEditTopicFeaturedLink"
)
resize() { resize() {
schedule("afterRender", () => { schedule("afterRender", () => {
if (!this.element || this.isDestroying || this.isDestroyed) { if (!this.element || this.isDestroying || this.isDestroyed) {
@ -76,8 +74,11 @@ export default Component.extend(KeyEnterEscape, {
}, },
debounceMove() { debounceMove() {
const h = $("#reply-control:not(.saving)").height() || 0; let height = 0;
this.movePanels(h); if (!this.element.classList.contains("saving")) {
height = this.element.offsetHeight;
}
this.movePanels(height);
}, },
keyUp() { keyUp() {
@ -105,45 +106,13 @@ export default Component.extend(KeyEnterEscape, {
}, },
setupComposerResizeEvents() { setupComposerResizeEvents() {
const $composer = $(this.element); this.origComposerSize = 0;
const $grippie = $(this.element.querySelector(".grippie")); this.lastMousePos = 0;
const $document = $(document);
let origComposerSize = 0;
let lastMousePos = 0;
const performDrag = (event) => { START_DRAG_EVENTS.forEach((startDragEvent) => {
$composer.trigger("div-resizing"); this.element
this.appEvents.trigger("composer:div-resizing"); .querySelector(".grippie")
$composer.addClass("clear-transitions"); ?.addEventListener(startDragEvent, this.startDragHandler);
const currentMousePos = mouseYPos(event);
let size = origComposerSize + (lastMousePos - currentMousePos);
const winHeight = $(window).height();
size = Math.min(size, winHeight - headerHeight());
this.movePanels(size);
$composer.height(size);
};
const throttledPerformDrag = ((event) => {
event.preventDefault();
throttle(this, performDrag, event, THROTTLE_RATE);
}).bind(this);
const endDrag = (() => {
this.appEvents.trigger("composer:resize-ended");
$document.off(DRAG_EVENTS, throttledPerformDrag);
$document.off(END_EVENTS, endDrag);
$composer.removeClass("clear-transitions");
$composer.focus();
}).bind(this);
$grippie.on(START_EVENTS, (event) => {
event.preventDefault();
origComposerSize = $composer.height();
lastMousePos = mouseYPos(event);
$document.on(DRAG_EVENTS, throttledPerformDrag);
$document.on(END_EVENTS, endDrag);
this.appEvents.trigger("composer:resize-started");
}); });
if (this._visualViewportResizing()) { if (this._visualViewportResizing()) {
@ -152,6 +121,58 @@ export default Component.extend(KeyEnterEscape, {
} }
}, },
@bind
performDragHandler() {
this.appEvents.trigger("composer:div-resizing");
this.element.classList.add("clear-transitions");
const currentMousePos = mouseYPos(event);
let size = this.origComposerSize + (this.lastMousePos - currentMousePos);
size = Math.min(size, window.innerHeight - headerHeight());
this.movePanels(size);
this.element.style.height = size ? `${size}px` : "";
},
@bind
startDragHandler(event) {
event.preventDefault();
this.origComposerSize = this.element.offsetHeight;
this.lastMousePos = mouseYPos(event);
DRAG_EVENTS.forEach((dragEvent) => {
document.addEventListener(dragEvent, this.throttledPerformDrag);
});
END_DRAG_EVENTS.forEach((endDragEvent) => {
document.addEventListener(endDragEvent, this.endDragHandler);
});
this.appEvents.trigger("composer:resize-started");
},
@bind
endDragHandler() {
this.appEvents.trigger("composer:resize-ended");
DRAG_EVENTS.forEach((dragEvent) => {
document.removeEventListener(dragEvent, this.throttledPerformDrag);
});
END_DRAG_EVENTS.forEach((endDragEvent) => {
document.removeEventListener(endDragEvent, this.endDragHandler);
});
this.element.classList.remove("clear-transitions");
this.element.focus();
},
@bind
throttledPerformDrag(event) {
event.preventDefault();
throttle(this, this.performDragHandler, event, THROTTLE_RATE);
},
@bind @bind
viewportResize() { viewportResize() {
const composerVH = window.visualViewport.height * 0.01, const composerVH = window.visualViewport.height * 0.01,
@ -207,10 +228,17 @@ export default Component.extend(KeyEnterEscape, {
willDestroyElement() { willDestroyElement() {
this._super(...arguments); this._super(...arguments);
if (this._visualViewportResizing()) { if (this._visualViewportResizing()) {
window.visualViewport.removeEventListener("resize", this.viewportResize); window.visualViewport.removeEventListener("resize", this.viewportResize);
} }
START_DRAG_EVENTS.forEach((startDragEvent) => {
this.element
.querySelector(".grippie")
?.removeEventListener(startDragEvent, this.startDragHandler);
});
cancel(this._lastKeyTimeout); cancel(this._lastKeyTimeout);
}, },

View File

@ -12,6 +12,7 @@ import {
tinyAvatar, tinyAvatar,
} from "discourse/lib/utilities"; } from "discourse/lib/utilities";
import discourseComputed, { import discourseComputed, {
bind,
observes, observes,
on, on,
} from "discourse-common/utils/decorators"; } from "discourse-common/utils/decorators";
@ -138,9 +139,7 @@ export default Component.extend(ComposerUpload, {
@discourseComputed @discourseComputed
showLink() { showLink() {
return ( return this.currentUser && this.currentUser.link_posting_access !== "none";
this.currentUser && this.currentUser.get("link_posting_access") !== "none"
);
}, },
@observes("focusTarget") @observes("focusTarget")
@ -189,7 +188,8 @@ export default Component.extend(ComposerUpload, {
}; };
}, },
userSearchTerm(term) { @bind
_userSearchTerm(term) {
const topicId = this.get("topic.id"); const topicId = this.get("topic.id");
// maybe this is a brand new topic, so grab category from composer // maybe this is a brand new topic, so grab category from composer
const categoryId = const categoryId =
@ -218,34 +218,42 @@ export default Component.extend(ComposerUpload, {
return extensions.map((ext) => `.${ext}`).join(); return extensions.map((ext) => `.${ext}`).join();
}, },
@bind
_afterMentionComplete(value) {
this.composer.set("reply", value);
// ensures textarea scroll position is correct
schedule("afterRender", () => {
const input = this.element.querySelector(".d-editor-input");
input?.blur();
input?.focus();
});
},
@on("didInsertElement") @on("didInsertElement")
_composerEditorInit() { _composerEditorInit() {
const $input = $(this.element.querySelector(".d-editor-input")); const $input = $(this.element.querySelector(".d-editor-input"));
const $preview = $(this.element.querySelector(".d-editor-preview-wrapper"));
if (this.siteSettings.enable_mentions) { if (this.siteSettings.enable_mentions) {
$input.autocomplete({ $input.autocomplete({
template: findRawTemplate("user-selector-autocomplete"), template: findRawTemplate("user-selector-autocomplete"),
dataSource: (term) => this.userSearchTerm.call(this, term), dataSource: this._userSearchTerm,
key: "@", key: "@",
transformComplete: (v) => v.username || v.name, transformComplete: (v) => v.username || v.name,
afterComplete: (value) => { afterComplete: this._afterMentionComplete,
this.composer.set("reply", value);
// ensures textarea scroll position is correct
schedule("afterRender", () => $input.blur().focus());
},
triggerRule: (textarea) => triggerRule: (textarea) =>
!inCodeBlock(textarea.value, caretPosition(textarea)), !inCodeBlock(textarea.value, caretPosition(textarea)),
}); });
} }
if (this._enableAdvancedEditorPreviewSync()) { if (this._enableAdvancedEditorPreviewSync()) {
this._initInputPreviewSync($input, $preview); const input = this.element.querySelector(".d-editor-input");
const preview = this.element.querySelector(".d-editor-preview-wrapper");
this._initInputPreviewSync(input, preview);
} else { } else {
$input.on("scroll", () => this.element
throttle(this, this._syncEditorAndPreviewScroll, $input, $preview, 20) .querySelector(".d-editor-input")
); ?.addEventListener("scroll", this._throttledSyncEditorAndPreviewScroll);
} }
// Focus on the body unless we have a title // Focus on the body unless we have a title
@ -316,30 +324,47 @@ export default Component.extend(ComposerUpload, {
this.set("shouldBuildScrollMap", true); this.set("shouldBuildScrollMap", true);
}, },
_initInputPreviewSync($input, $preview) { @bind
_handleInputInteraction(event) {
const preview = this.element.querySelector(".d-editor-preview-wrapper");
if (!$(preview).is(":visible")) {
return;
}
preview.removeEventListener("scroll", this._handleInputOrPreviewScroll);
event.target.addEventListener("scroll", this._handleInputOrPreviewScroll);
},
@bind
_handleInputOrPreviewScroll(event) {
this._syncScroll(
this._syncEditorAndPreviewScroll,
$(event.target),
$(this.element.querySelector(".d-editor-preview-wrapper"))
);
},
@bind
_handlePreviewInteraction(event) {
this.element
.querySelector(".d-editor-input")
?.removeEventListener("scroll", this._handleInputOrPreviewScroll);
event.target?.addEventListener("scroll", this._handleInputOrPreviewScroll);
},
_initInputPreviewSync(input, preview) {
REBUILD_SCROLL_MAP_EVENTS.forEach((event) => { REBUILD_SCROLL_MAP_EVENTS.forEach((event) => {
this.appEvents.on(event, this, this._resetShouldBuildScrollMap); this.appEvents.on(event, this, this._resetShouldBuildScrollMap);
}); });
schedule("afterRender", () => { schedule("afterRender", () => {
$input.on("touchstart mouseenter", () => { input?.addEventListener("touchstart", this._handleInputInteraction);
if (!$preview.is(":visible")) { input?.addEventListener("mouseenter", this._handleInputInteraction);
return;
}
$preview.off("scroll");
$input.on("scroll", () => { preview?.addEventListener("touchstart", this._handlePreviewInteraction);
this._syncScroll(this._syncEditorAndPreviewScroll, $input, $preview); preview?.addEventListener("mouseenter", this._handlePreviewInteraction);
});
});
$preview.on("touchstart mouseenter", () => {
$input.off("scroll");
$preview.on("scroll", () => {
this._syncScroll(this._syncPreviewAndEditorScroll, $input, $preview);
});
});
}); });
}, },
@ -353,13 +378,15 @@ export default Component.extend(ComposerUpload, {
}, },
_teardownInputPreviewSync() { _teardownInputPreviewSync() {
[ const input = this.element.querySelector(".d-editor-input");
$(this.element.querySelector(".d-editor-input")), input?.removeEventListener("mouseEnter", this._handleInputInteraction);
$(this.element.querySelector(".d-editor-preview-wrapper")), input?.removeEventListener("touchstart", this._handleInputInteraction);
].forEach(($element) => { input?.removeEventListener("scroll", this._handleInputOrPreviewScroll);
$element.off("mouseenter touchstart");
$element.off("scroll"); const preview = this.element.querySelector(".d-editor-preview-wrapper");
}); preview?.removeEventListener("mouseEnter", this._handlePreviewInteraction);
preview?.removeEventListener("touchstart", this._handlePreviewInteraction);
preview?.removeEventListener("scroll", this._handleInputOrPreviewScroll);
REBUILD_SCROLL_MAP_EVENTS.forEach((event) => { REBUILD_SCROLL_MAP_EVENTS.forEach((event) => {
this.appEvents.off(event, this, this._resetShouldBuildScrollMap); this.appEvents.off(event, this, this._resetShouldBuildScrollMap);
@ -453,6 +480,19 @@ export default Component.extend(ComposerUpload, {
return scrollMap; return scrollMap;
}, },
@bind
_throttledSyncEditorAndPreviewScroll(event) {
const $preview = $(this.element.querySelector(".d-editor-preview-wrapper"));
throttle(
this,
this._syncEditorAndPreviewScroll,
$(event.target),
$preview,
20
);
},
_syncEditorAndPreviewScroll($input, $preview, scrollMap) { _syncEditorAndPreviewScroll($input, $preview, scrollMap) {
if (this._enableAdvancedEditorPreviewSync()) { if (this._enableAdvancedEditorPreviewSync()) {
let scrollTop; let scrollTop;
@ -599,91 +639,103 @@ export default Component.extend(ComposerUpload, {
}); });
}, },
_registerImageScaleButtonClick($preview) { @bind
$preview.off("click", ".scale-btn").on("click", ".scale-btn", (e) => { _handleImageScaleButtonClick(event) {
if (!event.target.classList.contains("scale-btn")) {
return;
}
const index = parseInt(
event.target.closest(".button-wrapper").dataset.imageIndex,
10
);
const scale = event.target.dataset.scale;
const matchingPlaceholder = this.get("composer.reply").match(
IMAGE_MARKDOWN_REGEX
);
if (matchingPlaceholder) {
const match = matchingPlaceholder[index];
if (match) {
const replacement = match.replace(
IMAGE_MARKDOWN_REGEX,
`![$1|$2, ${scale}%$4]($5)`
);
this.appEvents.trigger(
"composer:replace-text",
matchingPlaceholder[index],
replacement,
{ regex: IMAGE_MARKDOWN_REGEX, index }
);
}
}
event.preventDefault();
return;
},
@bind
_handleAltTextInputKeypress(event) {
if (!event.target.classList.contains("alt-text-input")) {
return;
}
if (event.key === "[" || event.key === "]") {
event.preventDefault();
}
if (event.key === "Enter") {
const index = parseInt( const index = parseInt(
$(e.target).closest(".button-wrapper").attr("data-image-index"), $(event.target).closest(".button-wrapper").attr("data-image-index"),
10 10
); );
const scale = e.target.attributes["data-scale"].value;
const matchingPlaceholder = this.get("composer.reply").match( const matchingPlaceholder = this.get("composer.reply").match(
IMAGE_MARKDOWN_REGEX IMAGE_MARKDOWN_REGEX
); );
const match = matchingPlaceholder[index];
const replacement = match.replace(
IMAGE_MARKDOWN_REGEX,
`![${$(event.target).val()}|$2$3$4]($5)`
);
if (matchingPlaceholder) { this.appEvents.trigger("composer:replace-text", match, replacement);
const match = matchingPlaceholder[index];
if (match) { const parentContainer = $(event.target).closest(
const replacement = match.replace( ".alt-text-readonly-container"
IMAGE_MARKDOWN_REGEX, );
`![$1|$2, ${scale}%$4]($5)` const altText = parentContainer.find(".alt-text");
); const altTextButton = parentContainer.find(".alt-text-edit-btn");
altText.show();
this.appEvents.trigger( altTextButton.show();
"composer:replace-text", $(event.target).hide();
matchingPlaceholder[index], }
replacement,
{ regex: IMAGE_MARKDOWN_REGEX, index }
);
}
}
e.preventDefault();
return;
});
}, },
_registerImageAltTextButtonClick($preview) { @bind
$preview _handleAltTextEditButtonClick(event) {
.off("click", ".alt-text-edit-btn") if (!event.target.classList.contains("alt-text-edit-btn")) {
.on("click", ".alt-text-edit-btn", (e) => { return;
const parentContainer = $(e.target).closest( }
".alt-text-readonly-container"
);
const altText = parentContainer.find(".alt-text");
const correspondingInput = parentContainer.find(".alt-text-input");
$(e.target).hide(); const parentContainer = $(event.target).closest(
altText.hide(); ".alt-text-readonly-container"
correspondingInput.val(altText.text()); );
correspondingInput.show(); const altText = parentContainer.find(".alt-text");
e.preventDefault(); const correspondingInput = parentContainer.find(".alt-text-input");
});
$preview $(event.target).hide();
.off("keypress", ".alt-text-input") altText.hide();
.on("keypress", ".alt-text-input", (e) => { correspondingInput.val(altText.text());
if (e.key === "[" || e.key === "]") { correspondingInput.show();
e.preventDefault(); event.preventDefault();
} },
if (e.key === "Enter") { _registerImageAltTextButtonClick(preview) {
const index = parseInt( preview.addEventListener("click", this._handleAltTextEditButtonClick);
$(e.target).closest(".button-wrapper").attr("data-image-index"), preview.addEventListener("keypress", this._handleAltTextInputKeypress);
10
);
const matchingPlaceholder = this.get("composer.reply").match(
IMAGE_MARKDOWN_REGEX
);
const match = matchingPlaceholder[index];
const replacement = match.replace(
IMAGE_MARKDOWN_REGEX,
`![${$(e.target).val()}|$2$3$4]($5)`
);
this.appEvents.trigger("composer:replace-text", match, replacement);
const parentContainer = $(e.target).closest(
".alt-text-readonly-container"
);
const altText = parentContainer.find(".alt-text");
const altTextButton = parentContainer.find(".alt-text-edit-btn");
altText.show();
altTextButton.show();
$(e.target).hide();
}
});
}, },
@on("willDestroyElement") @on("willDestroyElement")
@ -701,6 +753,20 @@ export default Component.extend(ComposerUpload, {
if (this._enableAdvancedEditorPreviewSync()) { if (this._enableAdvancedEditorPreviewSync()) {
this._teardownInputPreviewSync(); this._teardownInputPreviewSync();
} }
if (!this._enableAdvancedEditorPreviewSync()) {
this.element
.querySelector(".d-editor-input")
?.removeEventListener(
"scroll",
this._throttledSyncEditorAndPreviewScroll
);
}
const preview = this.element.querySelector(".d-editor-preview-wrapper");
preview?.removeEventListener("click", this._handleImageScaleButtonClick);
preview?.removeEventListener("click", this._handleAltTextEditButtonClick);
preview?.removeEventListener("keypress", this._handleAltTextInputKeypress);
}, },
onExpandPopupMenuOptions(toolbarEvent) { onExpandPopupMenuOptions(toolbarEvent) {
@ -863,8 +929,8 @@ export default Component.extend(ComposerUpload, {
); );
} }
this._registerImageScaleButtonClick($preview); preview.addEventListener("click", this._handleImageScaleButtonClick);
this._registerImageAltTextButtonClick($preview); this._registerImageAltTextButtonClick(preview);
this.trigger("previewRefreshed", preview); this.trigger("previewRefreshed", preview);
this.afterRefresh($preview); this.afterRefresh($preview);

View File

@ -5,6 +5,7 @@ import {
translateModKey, translateModKey,
} from "discourse/lib/utilities"; } from "discourse/lib/utilities";
import discourseComputed, { import discourseComputed, {
bind,
observes, observes,
on, on,
} from "discourse-common/utils/decorators"; } from "discourse-common/utils/decorators";
@ -286,31 +287,9 @@ export default Component.extend(TextareaTextManipulation, {
}); });
// disable clicking on links in the preview // disable clicking on links in the preview
$(this.element.querySelector(".d-editor-preview")).on( this.element
"click.preview", .querySelector(".d-editor-preview")
(e) => { .addEventListener("click", this._handlePreviewLinkClick);
if (wantsNewWindow(e)) {
return;
}
const $target = $(e.target);
if ($target.is("a.mention")) {
this.appEvents.trigger(
"click.discourse-preview-user-card-mention",
$target
);
}
if ($target.is("a.mention-group")) {
this.appEvents.trigger(
"click.discourse-preview-group-card-mention-group",
$target
);
}
if ($target.is("a")) {
e.preventDefault();
return false;
}
}
);
if (this.composerEvents) { if (this.composerEvents) {
this.appEvents.on("composer:insert-block", this, "_insertBlock"); this.appEvents.on("composer:insert-block", this, "_insertBlock");
@ -323,6 +302,32 @@ export default Component.extend(TextareaTextManipulation, {
} }
}, },
@bind
_handlePreviewLinkClick(event) {
if (wantsNewWindow(event)) {
return;
}
if (event.target.tagName === "A") {
if (event.target.classList.contains("mention")) {
this.appEvents.trigger(
"click.discourse-preview-user-card-mention",
$(event.target)
);
}
if (event.target.classList.contains("mention-group")) {
this.appEvents.trigger(
"click.discourse-preview-group-card-mention-group",
$(event.target)
);
}
event.preventDefault();
return false;
}
},
@on("willDestroyElement") @on("willDestroyElement")
_shutDown() { _shutDown() {
if (this.composerEvents) { if (this.composerEvents) {
@ -334,7 +339,9 @@ export default Component.extend(TextareaTextManipulation, {
this._itsatrap?.destroy(); this._itsatrap?.destroy();
this._itsatrap = null; this._itsatrap = null;
$(this.element.querySelector(".d-editor-preview")).off("click.preview"); this.element
.querySelector(".d-editor-preview")
?.removeEventListener("click", this._handlePreviewLinkClick);
this._previewMutationObserver?.disconnect(); this._previewMutationObserver?.disconnect();

View File

@ -46,8 +46,6 @@ const keys = {
let inputTimeout; let inputTimeout;
export default function (options) { export default function (options) {
const autocompletePlugin = this;
if (this.length === 0) { if (this.length === 0) {
return; return;
} }
@ -55,13 +53,11 @@ export default function (options) {
if (options === "destroy" || options.updateData) { if (options === "destroy" || options.updateData) {
cancel(inputTimeout); cancel(inputTimeout);
$(this) this[0].removeEventListener("keydown", handleKeyDown);
.off("keyup.autocomplete") this[0].removeEventListener("keyup", handleKeyUp);
.off("keydown.autocomplete") this[0].removeEventListener("paste", handlePaste);
.off("paste.autocomplete") this[0].removeEventListener("click", closeAutocomplete);
.off("click.autocomplete"); window.removeEventListener("click", closeAutocomplete);
$(window).off("click.autocomplete");
if (options === "destroy") { if (options === "destroy") {
return; return;
@ -116,8 +112,12 @@ export default function (options) {
const isInput = me[0].tagName === "INPUT" && !options.treatAsTextarea; const isInput = me[0].tagName === "INPUT" && !options.treatAsTextarea;
let inputSelectedItems = []; let inputSelectedItems = [];
function handlePaste() {
later(() => me.trigger("keydown"), 50);
}
function closeAutocomplete() { function closeAutocomplete() {
_autoCompletePopper && _autoCompletePopper.destroy(); _autoCompletePopper?.destroy();
if (div) { if (div) {
div.hide().remove(); div.hide().remove();
@ -276,7 +276,7 @@ export default function (options) {
this.val(""); this.val("");
completeStart = 0; completeStart = 0;
wrap.click(function () { wrap.click(function () {
autocompletePlugin.focus(); this.focus();
return true; return true;
}); });
} }
@ -447,24 +447,17 @@ export default function (options) {
closeAutocomplete(); closeAutocomplete();
}); });
$(window).on("click.autocomplete", () => closeAutocomplete());
$(this).on("click.autocomplete", () => closeAutocomplete());
$(this).on("paste.autocomplete", () => {
later(() => me.trigger("keydown"), 50);
});
function checkTriggerRule(opts) { function checkTriggerRule(opts) {
return options.triggerRule ? options.triggerRule(me[0], opts) : true; return options.triggerRule ? options.triggerRule(me[0], opts) : true;
} }
$(this).on("keyup.autocomplete", function (e) { function handleKeyUp(e) {
if (options.debounced) { if (options.debounced) {
discourseDebounce(this, performAutocomplete, e, INPUT_DELAY); discourseDebounce(this, performAutocomplete, e, INPUT_DELAY);
} else { } else {
performAutocomplete(e); performAutocomplete(e);
} }
}); }
function performAutocomplete(e) { function performAutocomplete(e) {
if ([keys.esc, keys.enter].indexOf(e.which) !== -1) { if ([keys.esc, keys.enter].indexOf(e.which) !== -1) {
@ -503,7 +496,7 @@ export default function (options) {
} }
} }
$(this).on("keydown.autocomplete", function (e) { function handleKeyDown(e) {
let c, i, initial, prev, prevIsGood, stopFound, term, total, userToComplete; let c, i, initial, prev, prevIsGood, stopFound, term, total, userToComplete;
let cp; let cp;
@ -602,7 +595,9 @@ export default function (options) {
// We're cancelling it, really. // We're cancelling it, really.
return true; return true;
} }
e.stopImmediatePropagation(); e.stopImmediatePropagation();
e.preventDefault();
return false; return false;
case keys.upArrow: case keys.upArrow:
selectedOption = selectedOption - 1; selectedOption = selectedOption - 1;
@ -652,7 +647,13 @@ export default function (options) {
return true; return true;
} }
} }
}); }
window.addEventListener("click", closeAutocomplete);
this[0].addEventListener("click", closeAutocomplete);
this[0].addEventListener("paste", handlePaste);
this[0].addEventListener("keyup", handleKeyUp);
this[0].addEventListener("keydown", handleKeyDown);
return this; return this;
} }

View File

@ -1,3 +1,4 @@
import { bind } from "discourse-common/utils/decorators";
import Mixin from "@ember/object/mixin"; import Mixin from "@ember/object/mixin";
import toMarkdown from "discourse/lib/to-markdown"; import toMarkdown from "discourse/lib/to-markdown";
import { isTesting } from "discourse-common/config/environment"; import { isTesting } from "discourse-common/config/environment";
@ -6,7 +7,6 @@ import {
determinePostReplaceSelection, determinePostReplaceSelection,
safariHacksDisabled, safariHacksDisabled,
} from "discourse/lib/utilities"; } from "discourse/lib/utilities";
import { bind } from "discourse-common/utils/decorators";
import { next, schedule } from "@ember/runloop"; import { next, schedule } from "@ember/runloop";
const isInside = (text, regex) => { const isInside = (text, regex) => {