0
0
Fork 1
Spiegel von https://github.com/paviliondev/discourse-custom-wizard.git synchronisiert 2024-11-25 10:40:28 +01:00
discourse-custom-wizard/assets/javascripts/wizard/components/wizard-composer-editor.js.es6

324 Zeilen
9 KiB
JavaScript

import ComposerEditor from "discourse/components/composer-editor";
import {
default as discourseComputed,
on,
} from "discourse-common/utils/decorators";
import { findRawTemplate } from "discourse-common/lib/raw-templates";
import { throttle } from "@ember/runloop";
import { scheduleOnce, next } from "@ember/runloop";
import {
safariHacksDisabled,
caretPosition,
inCodeBlock,
} from "discourse/lib/utilities";
import highlightSyntax from "discourse/lib/highlight-syntax";
import { getToken } from "wizard/lib/ajax";
import {
validateUploadedFiles,
getUploadMarkdown,
} from "discourse/lib/uploads";
import { cacheShortUploadUrl } from "pretty-text/upload-short-url";
import { alias } from "@ember/object/computed";
import { uploadIcon } from "discourse/lib/uploads";
import WizardI18n from '../lib/wizard-i18n';
const uploadMarkdownResolvers = [];
const uploadHandlers = [];
export default ComposerEditor.extend({
classNameBindings: ["fieldClass"],
allowUpload: true,
showLink: false,
showHyperlinkBox: false,
topic: null,
showToolbar: true,
focusTarget: "reply",
canWhisper: false,
lastValidatedAt: "lastValidatedAt",
popupMenuOptions: [],
draftStatus: "null",
replyPlaceholder: alias("field.placeholder"),
@on("didInsertElement")
_composerEditorInit() {
const $input = $(this.element.querySelector(".d-editor-input"));
const $preview = $(this.element.querySelector(".d-editor-preview-wrapper"));
if (this.siteSettings.enable_mentions) {
$input.autocomplete({
template: findRawTemplate("user-selector-autocomplete"),
dataSource: (term) => this.userSearchTerm.call(this, term),
key: "@",
transformComplete: (v) => v.username || v.name,
afterComplete: (value) => {
this.composer.set("reply", value);
scheduleOnce("afterRender", () => $input.blur().focus());
},
triggerRule: (textarea) =>
!inCodeBlock(textarea.value, caretPosition(textarea)),
});
}
if (this._enableAdvancedEditorPreviewSync()) {
this._initInputPreviewSync($input, $preview);
} else {
$input.on("scroll", () =>
throttle(this, this._syncEditorAndPreviewScroll, $input, $preview, 20)
);
}
this._bindUploadTarget();
},
@discourseComputed
allowedFileTypes() {
return this.siteSettings.authorized_extensions
.split("|")
.map((ext) => "." + ext)
.join(",");
},
@discourseComputed("currentUser")
uploadIcon(currentUser) {
return uploadIcon(false, this.siteSettings);
},
_setUploadPlaceholderSend() {
if (!this.composer.get("reply")) {
this.composer.set("reply", "");
}
this._super(...arguments);
},
_bindUploadTarget() {
this._super(...arguments);
const $element = $(this.element);
// adding dropZone property post initialization
$element.fileupload("option", "dropZone", $element);
$element.off("fileuploadsubmit");
$element.on("fileuploadsubmit", (e, data) => {
const max = this.siteSettings.simultaneous_uploads;
// Limit the number of simultaneous uploads
if (max > 0 && data.files.length > max) {
bootbox.alert(
WizardI18n("post.errors.too_many_dragged_and_dropped_files", { max })
);
return false;
}
// Look for a matching file upload handler contributed from a plugin
const matcher = (handler) => {
const ext = handler.extensions.join("|");
const regex = new RegExp(`\\.(${ext})$`, "i");
return regex.test(data.files[0].name);
};
const matchingHandler = uploadHandlers.find(matcher);
if (data.files.length === 1 && matchingHandler) {
if (!matchingHandler.method(data.files[0], this)) {
return false;
}
}
// If no plugin, continue as normal
const isPrivateMessage = this.get("composer.privateMessage");
data.formData = { type: "composer" };
data.formData.authenticity_token = getToken();
if (isPrivateMessage) {
data.formData.for_private_message = true;
}
if (this._pasted) {
data.formData.pasted = true;
}
const opts = {
user: this.currentUser,
siteSettings: this.siteSettings,
isPrivateMessage,
allowStaffToUploadAnyFileInPm: this.siteSettings
.allow_staff_to_upload_any_file_in_pm,
};
const isUploading = validateUploadedFiles(data.files, opts);
this.setProperties({ uploadProgress: 0, isUploading });
return isUploading;
});
$element.on("fileuploadprogressall", (e, data) => {
this.set(
"uploadProgress",
parseInt((data.loaded / data.total) * 100, 10)
);
});
$element.on("fileuploadfail", (e, data) => {
this._setUploadPlaceholderDone(data);
this._resetUpload(true);
const userCancelled = this._xhr && this._xhr._userCancelled;
this._xhr = null;
if (!userCancelled) {
displayErrorForUpload(data, this.siteSettings);
}
});
$element.on("fileuploadsend", (e, data) => {
this._pasted = false;
this._validUploads++;
this._setUploadPlaceholderSend(data);
this.appEvents.trigger("wizard-editor:insert-text", {
fieldId: this.field.id,
text: this.uploadPlaceholder,
});
if (data.xhr && data.originalFiles.length === 1) {
this.set("isCancellable", true);
this._xhr = data.xhr();
}
});
$element.on("fileuploaddone", (e, data) => {
let upload = data.result;
this._setUploadPlaceholderDone(data);
if (!this._xhr || !this._xhr._userCancelled) {
const markdown = uploadMarkdownResolvers.reduce(
(md, resolver) => resolver(upload) || md,
getUploadMarkdown(upload)
);
cacheShortUploadUrl(upload.short_url, upload);
this.appEvents.trigger("wizard-editor:replace-text", {
fieldId: this.field.id,
oldVal: this.uploadPlaceholder.trim(),
newVal: markdown,
});
this._resetUpload(false);
} else {
this._resetUpload(true);
}
});
},
_resetUpload(removePlaceholder) {
next(() => {
if (this._validUploads > 0) {
this._validUploads--;
}
if (this._validUploads === 0) {
this.setProperties({
uploadProgress: 0,
isUploading: false,
isCancellable: false,
});
}
if (removePlaceholder) {
this.appEvents.trigger("wizard-editor:replace-text", {
fieldId: this.field.id,
oldVal: this.uploadPlaceholder,
newVal: "",
});
}
this._resetUploadFilenamePlaceholder();
});
},
_registerImageScaleButtonClick($preview) {
const imageScaleRegex = /!\[(.*?)\|(\d{1,4}x\d{1,4})(,\s*\d{1,3}%)?(.*?)\]\((upload:\/\/.*?)\)(?!(.*`))/g;
$preview.off("click", ".scale-btn").on("click", ".scale-btn", (e) => {
const index = parseInt($(e.target).parent().attr("data-image-index"), 10);
const scale = e.target.attributes["data-scale"].value;
const matchingPlaceholder = this.get("composer.reply").match(
imageScaleRegex
);
if (matchingPlaceholder) {
const match = matchingPlaceholder[index];
if (match) {
const replacement = match.replace(
imageScaleRegex,
`![$1|$2, ${scale}%$4]($5)`
);
this.appEvents.trigger("wizard-editor:replace-text", {
fieldId: this.field.id,
oldVal: matchingPlaceholder[index],
newVal: replacement,
options: {
regex: imageScaleRegex,
index,
},
});
}
}
e.preventDefault();
return;
});
},
click(e) {
if ($(e.target).hasClass("wizard-composer-hyperlink")) {
this.set("showHyperlinkBox", false);
}
},
actions: {
extraButtons(toolbar) {
const component = this;
if (this.allowUpload && this.uploadIcon && !this.site.mobileView) {
toolbar.addButton({
id: "upload",
group: "insertions",
icon: this.uploadIcon,
title: "upload",
sendAction: (event) => component.send("showUploadModal", event),
});
}
toolbar.addButton({
id: "link",
group: "insertions",
shortcut: "K",
trimLeading: true,
unshift: true,
sendAction: (event) => component.set("showHyperlinkBox", true),
});
},
previewUpdated($preview) {
highlightSyntax($preview[0], this.siteSettings, this.session);
this._super(...arguments);
},
addLink(linkName, linkUrl) {
let link = `[${linkName}](${linkUrl})`;
this.appEvents.trigger("wizard-editor:insert-text", {
fieldId: this.field.id,
text: link,
});
this.set("showHyperlinkBox", false);
},
hideBox() {
this.set("showHyperlinkBox", false);
},
showUploadModal() {
$(this.element.querySelector(".wizard-composer-upload")).trigger("click");
},
},
});