From ed355f64bf659df96b7241aad54d66f16a03924b Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Tue, 5 Jan 2021 18:43:16 +0100 Subject: [PATCH] Convert `Media/Manager/Editor` to TypeScript --- .../WoltLabSuite/Core/Media/Manager/Editor.js | 455 +++++++---------- .../WoltLabSuite/Core/Media/Manager/Editor.js | 470 ------------------ .../WoltLabSuite/Core/Media/Manager/Editor.ts | 403 +++++++++++++++ 3 files changed, 588 insertions(+), 740 deletions(-) delete mode 100644 wcfsetup/install/files/ts/WoltLabSuite/Core/Media/Manager/Editor.js create mode 100644 wcfsetup/install/files/ts/WoltLabSuite/Core/Media/Manager/Editor.ts diff --git a/wcfsetup/install/files/js/WoltLabSuite/Core/Media/Manager/Editor.js b/wcfsetup/install/files/js/WoltLabSuite/Core/Media/Manager/Editor.js index f83f92b5de..1f3bb5f1c7 100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Media/Manager/Editor.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Media/Manager/Editor.js @@ -1,361 +1,277 @@ /** * Provides the media manager dialog for selecting media for Redactor editors. * - * @author Matthias Schmidt - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @module WoltLabSuite/Core/Media/Manager/Editor + * @author Matthias Schmidt + * @copyright 2001-2021 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Media/Manager/Editor */ -define(['Core', 'Dictionary', 'Dom/Traverse', 'EventHandler', 'Language', 'Permission', 'Ui/Dialog', 'WoltLabSuite/Core/Controller/Clipboard', 'WoltLabSuite/Core/Media/Manager/Base'], function (Core, Dictionary, DomTraverse, EventHandler, Language, Permission, UiDialog, ControllerClipboard, MediaManagerBase) { +define(["require", "exports", "tslib", "./Base", "../../Core", "../../Event/Handler", "../../Dom/Traverse", "../../Language", "../../Ui/Dialog", "../../Controller/Clipboard"], function (require, exports, tslib_1, Base_1, Core, EventHandler, DomTraverse, Language, UiDialog, Clipboard) { "use strict"; - if (!COMPILER_TARGET_DEFAULT) { - var Fake = function () { }; - Fake.prototype = { - _addButtonEventListeners: function () { }, - _buildInsertDialog: function () { }, - _click: function () { }, - _getInsertDialogId: function () { }, - _getThumbnailSizes: function () { }, - _insertMedia: function () { }, - _insertMediaGallery: function () { }, - _insertMediaItem: function () { }, - _openInsertDialog: function () { }, - insertMedia: function () { }, - getMode: function () { }, - setupMediaElement: function () { }, - _dialogClose: function () { }, - _dialogInit: function () { }, - _dialogSetup: function () { }, - _dialogShow: function () { }, - _editMedia: function () { }, - _editorClose: function () { }, - _editorSuccess: function () { }, - _removeClipboardCheckboxes: function () { }, - _setMedia: function () { }, - addMedia: function () { }, - clipboardInsertMedia: function () { }, - getDialog: function () { }, - getOption: function () { }, - removeMedia: function () { }, - resetMedia: function () { }, - setMedia: function () { } - }; - return Fake; - } - /** - * @constructor - */ - function MediaManagerEditor(options) { - options = Core.extend({ - callbackInsert: null - }, options); - MediaManagerBase.call(this, options); - this._forceClipboard = true; - this._activeButton = null; - var context = (this._options.editor) ? this._options.editor.core.toolbar()[0] : undefined; - this._buttons = elByClass(this._options.buttonClass || 'jsMediaEditorButton', context); - for (var i = 0, length = this._buttons.length; i < length; i++) { - this._buttons[i].addEventListener('click', this._click.bind(this)); - } - this._mediaToInsert = new Dictionary(); - this._mediaToInsertByClipboard = false; - this._uploadData = null; - this._uploadId = null; - if (this._options.editor && !this._options.editor.opts.woltlab.attachments) { - var editorId = elData(this._options.editor.$editor[0], 'element-id'); - var uuid1 = EventHandler.add('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, this._editorUpload.bind(this)); - var uuid2 = EventHandler.add('com.woltlab.wcf.redactor2', 'pasteFromClipboard_' + editorId, this._editorUpload.bind(this)); - EventHandler.add('com.woltlab.wcf.redactor2', 'destory_' + editorId, function () { - EventHandler.remove('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, uuid1); - EventHandler.remove('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, uuid2); + Base_1 = tslib_1.__importDefault(Base_1); + Core = tslib_1.__importStar(Core); + EventHandler = tslib_1.__importStar(EventHandler); + DomTraverse = tslib_1.__importStar(DomTraverse); + Language = tslib_1.__importStar(Language); + UiDialog = tslib_1.__importStar(UiDialog); + Clipboard = tslib_1.__importStar(Clipboard); + class MediaManagerEditor extends Base_1.default { + constructor(options) { + options = Core.extend({ + callbackInsert: null, + }, options); + super(options); + this._forceClipboard = true; + this._activeButton = null; + const context = this._options.editor ? this._options.editor.core.toolbar()[0] : undefined; + this._buttons = (context || window.document).getElementsByClassName(this._options.buttonClass || "jsMediaEditorButton"); + Array.from(this._buttons).forEach((button) => { + button.addEventListener("click", (ev) => this._click(ev)); }); - EventHandler.add('com.woltlab.wcf.media.upload', 'success', this._mediaUploaded.bind(this)); + this._mediaToInsert = new Map(); + this._mediaToInsertByClipboard = false; + this._uploadData = null; + this._uploadId = null; + if (this._options.editor && !this._options.editor.opts.woltlab.attachments) { + const editorId = this._options.editor.$editor[0].dataset.elementId; + const uuid1 = EventHandler.add("com.woltlab.wcf.redactor2", `dragAndDrop_${editorId}`, (data) => this._editorUpload(data)); + const uuid2 = EventHandler.add("com.woltlab.wcf.redactor2", `pasteFromClipboard_${editorId}`, (data) => this._editorUpload(data)); + EventHandler.add("com.woltlab.wcf.redactor2", `destroy_${editorId}`, () => { + EventHandler.remove("com.woltlab.wcf.redactor2", `dragAndDrop_${editorId}`, uuid1); + EventHandler.remove("com.woltlab.wcf.redactor2", `dragAndDrop_${editorId}`, uuid2); + }); + EventHandler.add("com.woltlab.wcf.media.upload", "success", (data) => this._mediaUploaded(data)); + } } - } - Core.inherit(MediaManagerEditor, MediaManagerBase, { - /** - * @see WoltLabSuite/Core/Media/Manager/Base#_addButtonEventListeners - */ - _addButtonEventListeners: function () { - MediaManagerEditor._super.prototype._addButtonEventListeners.call(this); - if (!this._mediaManagerMediaList) + _addButtonEventListeners() { + super._addButtonEventListeners(); + if (!this._mediaManagerMediaList) { return; - var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI'); - for (var i = 0, length = listItems.length; i < length; i++) { - var listItem = listItems[i]; - var insertIcon = elByClass('jsMediaInsertButton', listItem)[0]; + } + DomTraverse.childrenByTag(this._mediaManagerMediaList, "LI").forEach((listItem) => { + const insertIcon = listItem.querySelector(".jsMediaInsertButton"); if (insertIcon) { - insertIcon.classList.remove('jsMediaInsertButton'); - insertIcon.addEventListener('click', this._openInsertDialog.bind(this)); + insertIcon.classList.remove("jsMediaInsertButton"); + insertIcon.addEventListener("click", (ev) => this._openInsertDialog(ev)); } - } - }, + }); + } /** * Builds the dialog to setup inserting media files. */ - _buildInsertDialog: function () { - var thumbnailOptions = ''; - var thumbnailSizes = this._getThumbnailSizes(); - for (var i = 0, length = thumbnailSizes.length; i < length; i++) { - thumbnailOptions += ''; - } - thumbnailOptions += ''; - var dialog = '
' - /*+ (this._mediaToInsert.size > 1 ? '
' - + '
' + Language.get('wcf.media.insert.type') + '
' - + '
' - + '' - + '
' - + '
' : '')*/ - + '
' - + '
' + Language.get('wcf.media.insert.imageSize') + '
' - + '
' - + '' - + '
' - + '
' - + '
' - + '
' - + '' - + '
'; + _buildInsertDialog() { + let thumbnailOptions = ""; + this._getThumbnailSizes().forEach((thumbnailSize) => { + thumbnailOptions += + '"; + }); + thumbnailOptions += '"; + const dialog = ` +
+
+
${Language.get("wcf.media.insert.imageSize")}
+
+ +
+
+
+
+ +
`; UiDialog.open({ - _dialogSetup: (function () { + _dialogSetup: () => { return { id: this._getInsertDialogId(), options: { onClose: this._editorClose.bind(this), - onSetup: function (content) { - elByClass('buttonPrimary', content)[0].addEventListener('click', this._insertMedia.bind(this)); - // toggle thumbnail size selection based on selected insert type - /*var insertType = elBySel('select[name=insertType]', content); - if (insertType !== null) { - var thumbnailSelection = elByClass('thumbnailSizeSelection', content)[0]; - insertType.addEventListener('change', function(event) { - if (event.currentTarget.value === 'gallery') { - elHide(thumbnailSelection); - } - else { - elShow(thumbnailSelection); - } - }); - }*/ - var thumbnailSelection = elBySel('.thumbnailSizeSelection', content); - elShow(thumbnailSelection); - }.bind(this), - title: Language.get('wcf.media.insert') + onSetup: (content) => { + content.querySelector(".buttonPrimary").addEventListener("click", (ev) => this._insertMedia(ev)); + content.querySelector(".thumbnailSizeSelection").style.display = "block"; + }, + title: Language.get("wcf.media.insert"), }, - source: dialog + source: dialog, }; - }).bind(this) + }, }); - }, - /** - * @see WoltLabSuite/Core/Media/Manager/Base#_click - */ - _click: function (event) { + } + _click(event) { this._activeButton = event.currentTarget; - MediaManagerEditor._super.prototype._click.call(this, event); - }, - /** - * @see WoltLabSuite/Core/Media/Manager/Base#_dialogShow - */ - _dialogShow: function () { - MediaManagerEditor._super.prototype._dialogShow.call(this); + super._click(event); + } + _dialogShow() { + super._dialogShow(); // check if data needs to be uploaded if (this._uploadData) { - if (this._uploadData.file) { - this._upload.uploadFile(this._uploadData.file); + const fileUploadData = this._uploadData; + if (fileUploadData.file) { + this._upload.uploadFile(fileUploadData.file); } else { - this._uploadId = this._upload.uploadBlob(this._uploadData.blob); + const blobUploadData = this._uploadData; + this._uploadId = this._upload.uploadBlob(blobUploadData.blob); } this._uploadData = null; } - }, + } /** * Handles pasting and dragging and dropping files into the editor. - * - * @param {object} data data of the uploaded file */ - _editorUpload: function (data) { + _editorUpload(data) { this._uploadData = data; UiDialog.open(this); - }, + } /** * Returns the id of the insert dialog based on the media files to be inserted. - * - * @return {string} insert dialog id */ - _getInsertDialogId: function () { - var dialogId = 'mediaInsert'; - this._mediaToInsert.forEach(function (media, mediaId) { - dialogId += '-' + mediaId; + _getInsertDialogId() { + let dialogId = "mediaInsert"; + this._mediaToInsert.forEach((media, mediaId) => { + dialogId += `-${mediaId}`; }); return dialogId; - }, + } /** * Returns the supported thumbnail sizes (excluding `original`) for all media images to be inserted. - * - * @return {string[]} */ - _getThumbnailSizes: function () { - var sizes = []; - var supportedSizes = ['small', 'medium', 'large']; - var size, supportSize; - for (var i = 0, length = supportedSizes.length; i < length; i++) { - size = supportedSizes[i]; - supportSize = true; + _getThumbnailSizes() { + return ["small", "medium", "large"] + .map((size) => { + let supportSize = true; this._mediaToInsert.forEach(function (media) { - if (!media[size + 'ThumbnailType']) { + if (!media[size + "ThumbnailType"]) { supportSize = false; } }); if (supportSize) { - sizes.push(size); + return size; } - } - return sizes; - }, + return null; + }) + .filter((s) => s !== null); + } /** - * Inserts media files into redactor. - * - * @param {Event?} event - * @param {string?} thumbnailSize - * @param {boolean?} closeEditor + * Inserts media files into the editor. */ - _insertMedia: function (event, thumbnailSize, closeEditor) { + _insertMedia(event, thumbnailSize, closeEditor = false) { if (closeEditor === undefined) closeEditor = true; - var insertType = 'separate'; + const insertType = "separate"; // update insert options with selected values if method is called by clicking on 'insert' button // in dialog if (event) { UiDialog.close(this._getInsertDialogId()); - var dialogContent = event.currentTarget.closest('.dialogContent'); - /*if (this._mediaToInsert.size > 1) { - insertType = elBySel('select[name=insertType]', dialogContent).value; - }*/ - thumbnailSize = elBySel('select[name=thumbnailSize]', dialogContent).value; + const dialogContent = event.currentTarget.closest(".dialogContent"); + const thumbnailSizeSelect = dialogContent.querySelector("select[name=thumbnailSize]"); + thumbnailSize = thumbnailSizeSelect.value; } if (this._options.callbackInsert !== null) { this._options.callbackInsert(this._mediaToInsert, insertType, thumbnailSize); } else { - if (insertType === 'separate') { + if (insertType === "separate") { this._options.editor.buffer.set(); - this._mediaToInsert.forEach(this._insertMediaItem.bind(this, thumbnailSize)); + this._mediaToInsert.forEach((media) => this._insertMediaItem(thumbnailSize, media)); } else { this._insertMediaGallery(); } } if (this._mediaToInsertByClipboard) { - var mediaIds = []; + const mediaIds = []; this._mediaToInsert.forEach(function (media) { - mediaIds.push(media.mediaID); + mediaIds.push(~~media.mediaID); }); - ControllerClipboard.unmark('com.woltlab.wcf.media', mediaIds); + Clipboard.unmark("com.woltlab.wcf.media", mediaIds); } - this._mediaToInsert = new Dictionary(); + this._mediaToInsert = new Map(); this._mediaToInsertByClipboard = false; // close manager dialog if (closeEditor) { UiDialog.close(this); } - }, + } /** - * Inserts a series of uploaded images using a slider. - * - * @protected + * Inserts a series of uploaded images into the editor using a slider. */ - _insertMediaGallery: function () { - var mediaIds = []; - this._mediaToInsert.forEach(function (item) { - mediaIds.push(item.mediaID); + _insertMediaGallery() { + const mediaIds = []; + this._mediaToInsert.forEach(function (media) { + mediaIds.push(media.mediaID); }); this._options.editor.buffer.set(); - this._options.editor.insert.text("[wsmg='" + mediaIds.join(',') + "'][/wsmg]"); - }, + this._options.editor.insert.text("[wsmg='" + mediaIds.join(",") + "'][/wsmg]"); + } /** - * Inserts a single media item. - * - * @param {string} thumbnailSize preferred image dimension, is ignored for non-images - * @param {Object} item media item data - * @protected + * Inserts a single media item into the editor. */ - _insertMediaItem: function (thumbnailSize, item) { - if (item.isImage) { - var sizes = ['small', 'medium', 'large', 'original']; - // check if size is actually available - var available = '', size; - for (var i = 0; i < 4; i++) { - size = sizes[i]; - if (item[size + 'ThumbnailHeight'] != 0) { + _insertMediaItem(thumbnailSize, media) { + if (media.isImage) { + let available = ""; + ["small", "medium", "large", "original"].some((size) => { + if (media[size + "ThumbnailHeight"] != 0) { available = size; if (thumbnailSize == size) { - break; + return true; } } - } + return false; + }); thumbnailSize = available; - if (!thumbnailSize) - thumbnailSize = 'original'; - var link = item.link; - if (thumbnailSize !== 'original') { - link = item[thumbnailSize + 'ThumbnailLink']; + if (!thumbnailSize) { + thumbnailSize = "original"; } - this._options.editor.insert.html(''); + let link = media.link; + if (thumbnailSize !== "original") { + link = media[thumbnailSize + "ThumbnailLink"]; + } + this._options.editor.insert.html(``); } else { - this._options.editor.insert.text("[wsm='" + item.mediaID + "'][/wsm]"); + this._options.editor.insert.text(`[wsm='${media.mediaID}'][/wsm]`); } - }, + } /** * Is called after media files are successfully uploaded to insert copied media. - * - * @param {object} data upload data */ - _mediaUploaded: function (data) { + _mediaUploaded(data) { if (this._uploadId !== null && this._upload === data.upload) { - if (this._uploadId === data.uploadId || (Array.isArray(this._uploadId) && this._uploadId.indexOf(data.uploadId) !== -1)) { - this._mediaToInsert = Dictionary.fromObject(data.media); - this._insertMedia(null, 'medium', false); + if (this._uploadId === data.uploadId || + (Array.isArray(this._uploadId) && this._uploadId.indexOf(data.uploadId) !== -1)) { + this._mediaToInsert = new Map(data.media.entries()); + this._insertMedia(null, "medium", false); this._uploadId = null; } } - }, + } /** * Handles clicking on the insert button. - * - * @param {Event} event insert button click event */ - _openInsertDialog: function (event) { - this.insertMedia([~~elData(event.currentTarget, 'object-id')]); - }, + _openInsertDialog(event) { + const target = event.currentTarget; + this.insertMedia([~~target.dataset.objectId]); + } /** * Is called to insert the media files with the given ids into an editor. - * - * @param {int[]} mediaIds */ - clipboardInsertMedia: function (mediaIds) { + clipboardInsertMedia(mediaIds) { this.insertMedia(mediaIds, true); - }, + } /** * Prepares insertion of the media files with the given ids. - * - * @param {array} mediaIds ids of the media files to be inserted - * @param {boolean?} insertedByClipboard is true if the media files are inserted by clipboard */ - insertMedia: function (mediaIds, insertedByClipboard) { - this._mediaToInsert = new Dictionary(); + insertMedia(mediaIds, insertedByClipboard) { + this._mediaToInsert = new Map(); this._mediaToInsertByClipboard = insertedByClipboard || false; // open the insert dialog if all media files are images - var imagesOnly = true, media; - for (var i = 0, length = mediaIds.length; i < length; i++) { + let imagesOnly = true, media; + for (let i = 0, length = mediaIds.length; i < length; i++) { media = this._media.get(mediaIds[i]); this._mediaToInsert.set(media.mediaID, media); if (!media.isImage) { @@ -363,44 +279,43 @@ define(['Core', 'Dictionary', 'Dom/Traverse', 'EventHandler', 'Language', 'Permi } } if (imagesOnly) { - var thumbnailSizes = this._getThumbnailSizes(); + const thumbnailSizes = this._getThumbnailSizes(); if (thumbnailSizes.length) { UiDialog.close(this); - var dialogId = this._getInsertDialogId(); + const dialogId = this._getInsertDialogId(); if (UiDialog.getDialog(dialogId)) { - UiDialog.openStatic(dialogId); + UiDialog.openStatic(dialogId, null); } else { this._buildInsertDialog(); } } else { - this._insertMedia(undefined, 'original'); + this._insertMedia(undefined, "original"); } } else { this._insertMedia(); } - }, - /** - * @see WoltLabSuite/Core/Media/Manager/Base#getMode - */ - getMode: function () { - return 'editor'; - }, - /** - * @see WoltLabSuite/Core/Media/Manager/Base#setupMediaElement - */ - setupMediaElement: function (media, mediaElement) { - MediaManagerEditor._super.prototype.setupMediaElement.call(this, media, mediaElement); + } + getMode() { + return "editor"; + } + setupMediaElement(media, mediaElement) { + super.setupMediaElement(media, mediaElement); // add media insertion icon - var buttons = elBySel('nav.buttonGroupNavigation > ul', mediaElement); - var listItem = elCreate('li'); - listItem.className = 'jsMediaInsertButton'; - elData(listItem, 'object-id', media.mediaID); + const buttons = mediaElement.querySelector("nav.buttonGroupNavigation > ul"); + const listItem = document.createElement("li"); + listItem.className = "jsMediaInsertButton"; + listItem.dataset.objectId = media.mediaID; buttons.appendChild(listItem); - listItem.innerHTML = ' '; + listItem.innerHTML = ` + + + + `; } - }); + } + Core.enableLegacyInheritance(MediaManagerEditor); return MediaManagerEditor; }); diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Media/Manager/Editor.js b/wcfsetup/install/files/ts/WoltLabSuite/Core/Media/Manager/Editor.js deleted file mode 100644 index 5a6ea27da5..0000000000 --- a/wcfsetup/install/files/ts/WoltLabSuite/Core/Media/Manager/Editor.js +++ /dev/null @@ -1,470 +0,0 @@ -/** - * Provides the media manager dialog for selecting media for Redactor editors. - * - * @author Matthias Schmidt - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License - * @module WoltLabSuite/Core/Media/Manager/Editor - */ -define(['Core', 'Dictionary', 'Dom/Traverse', 'EventHandler', 'Language', 'Permission', 'Ui/Dialog', 'WoltLabSuite/Core/Controller/Clipboard', 'WoltLabSuite/Core/Media/Manager/Base'], - function(Core, Dictionary, DomTraverse, EventHandler, Language, Permission, UiDialog, ControllerClipboard, MediaManagerBase) { - "use strict"; - - if (!COMPILER_TARGET_DEFAULT) { - var Fake = function() {}; - Fake.prototype = { - _addButtonEventListeners: function() {}, - _buildInsertDialog: function() {}, - _click: function() {}, - _getInsertDialogId: function() {}, - _getThumbnailSizes: function() {}, - _insertMedia: function() {}, - _insertMediaGallery: function() {}, - _insertMediaItem: function() {}, - _openInsertDialog: function() {}, - insertMedia: function() {}, - getMode: function() {}, - setupMediaElement: function() {}, - _dialogClose: function() {}, - _dialogInit: function() {}, - _dialogSetup: function() {}, - _dialogShow: function() {}, - _editMedia: function() {}, - _editorClose: function() {}, - _editorSuccess: function() {}, - _removeClipboardCheckboxes: function() {}, - _setMedia: function() {}, - addMedia: function() {}, - clipboardInsertMedia: function() {}, - getDialog: function() {}, - getOption: function() {}, - removeMedia: function() {}, - resetMedia: function() {}, - setMedia: function() {} - }; - return Fake; - } - - /** - * @constructor - */ - function MediaManagerEditor(options) { - options = Core.extend({ - callbackInsert: null - }, options); - - MediaManagerBase.call(this, options); - - this._forceClipboard = true; - this._activeButton = null; - var context = (this._options.editor) ? this._options.editor.core.toolbar()[0] : undefined; - this._buttons = elByClass(this._options.buttonClass || 'jsMediaEditorButton', context); - for (var i = 0, length = this._buttons.length; i < length; i++) { - this._buttons[i].addEventListener('click', this._click.bind(this)); - } - this._mediaToInsert = new Dictionary(); - this._mediaToInsertByClipboard = false; - this._uploadData = null; - this._uploadId = null; - - if (this._options.editor && !this._options.editor.opts.woltlab.attachments) { - var editorId = elData(this._options.editor.$editor[0], 'element-id'); - - var uuid1 = EventHandler.add('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, this._editorUpload.bind(this)); - var uuid2 = EventHandler.add('com.woltlab.wcf.redactor2', 'pasteFromClipboard_' + editorId, this._editorUpload.bind(this)); - - EventHandler.add('com.woltlab.wcf.redactor2', 'destory_' + editorId, function() { - EventHandler.remove('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, uuid1); - EventHandler.remove('com.woltlab.wcf.redactor2', 'dragAndDrop_' + editorId, uuid2); - }); - - EventHandler.add('com.woltlab.wcf.media.upload', 'success', this._mediaUploaded.bind(this)); - } - } - Core.inherit(MediaManagerEditor, MediaManagerBase, { - /** - * @see WoltLabSuite/Core/Media/Manager/Base#_addButtonEventListeners - */ - _addButtonEventListeners: function() { - MediaManagerEditor._super.prototype._addButtonEventListeners.call(this); - - if (!this._mediaManagerMediaList) return; - - var listItems = DomTraverse.childrenByTag(this._mediaManagerMediaList, 'LI'); - for (var i = 0, length = listItems.length; i < length; i++) { - var listItem = listItems[i]; - - var insertIcon = elByClass('jsMediaInsertButton', listItem)[0]; - if (insertIcon) { - insertIcon.classList.remove('jsMediaInsertButton'); - insertIcon.addEventListener('click', this._openInsertDialog.bind(this)); - } - } - }, - - /** - * Builds the dialog to setup inserting media files. - */ - _buildInsertDialog: function() { - var thumbnailOptions = ''; - - var thumbnailSizes = this._getThumbnailSizes(); - for (var i = 0, length = thumbnailSizes.length; i < length; i++) { - thumbnailOptions += ''; - } - thumbnailOptions += ''; - - var dialog = '
' - /*+ (this._mediaToInsert.size > 1 ? '
' - + '
' + Language.get('wcf.media.insert.type') + '
' - + '
' - + '' - + '
' - + '
' : '')*/ - + '
' - + '
' + Language.get('wcf.media.insert.imageSize') + '
' - + '
' - + '' - + '
' - + '
' - + '
' - + '
' - + '' - + '
'; - - UiDialog.open({ - _dialogSetup: (function() { - return { - id: this._getInsertDialogId(), - options: { - onClose: this._editorClose.bind(this), - onSetup: function(content) { - elByClass('buttonPrimary', content)[0].addEventListener('click', this._insertMedia.bind(this)); - - // toggle thumbnail size selection based on selected insert type - /*var insertType = elBySel('select[name=insertType]', content); - if (insertType !== null) { - var thumbnailSelection = elByClass('thumbnailSizeSelection', content)[0]; - insertType.addEventListener('change', function(event) { - if (event.currentTarget.value === 'gallery') { - elHide(thumbnailSelection); - } - else { - elShow(thumbnailSelection); - } - }); - }*/ - var thumbnailSelection = elBySel('.thumbnailSizeSelection', content); - elShow(thumbnailSelection); - }.bind(this), - title: Language.get('wcf.media.insert') - }, - source: dialog - }; - }).bind(this) - }); - }, - - /** - * @see WoltLabSuite/Core/Media/Manager/Base#_click - */ - _click: function(event) { - this._activeButton = event.currentTarget; - - MediaManagerEditor._super.prototype._click.call(this, event); - }, - - /** - * @see WoltLabSuite/Core/Media/Manager/Base#_dialogShow - */ - _dialogShow: function() { - MediaManagerEditor._super.prototype._dialogShow.call(this); - - // check if data needs to be uploaded - if (this._uploadData) { - if (this._uploadData.file) { - this._upload.uploadFile(this._uploadData.file); - } - else { - this._uploadId = this._upload.uploadBlob(this._uploadData.blob); - } - - this._uploadData = null; - } - }, - - /** - * Handles pasting and dragging and dropping files into the editor. - * - * @param {object} data data of the uploaded file - */ - _editorUpload: function(data) { - this._uploadData = data; - - UiDialog.open(this); - }, - - /** - * Returns the id of the insert dialog based on the media files to be inserted. - * - * @return {string} insert dialog id - */ - _getInsertDialogId: function() { - var dialogId = 'mediaInsert'; - - this._mediaToInsert.forEach(function(media, mediaId) { - dialogId += '-' + mediaId; - }); - - return dialogId; - }, - - /** - * Returns the supported thumbnail sizes (excluding `original`) for all media images to be inserted. - * - * @return {string[]} - */ - _getThumbnailSizes: function() { - var sizes = []; - - var supportedSizes = ['small', 'medium', 'large']; - var size, supportSize; - for (var i = 0, length = supportedSizes.length; i < length; i++) { - size = supportedSizes[i]; - - supportSize = true; - this._mediaToInsert.forEach(function(media) { - if (!media[size + 'ThumbnailType']) { - supportSize = false; - } - }); - - if (supportSize) { - sizes.push(size); - } - } - - return sizes; - }, - - /** - * Inserts media files into redactor. - * - * @param {Event?} event - * @param {string?} thumbnailSize - * @param {boolean?} closeEditor - */ - _insertMedia: function(event, thumbnailSize, closeEditor) { - if (closeEditor === undefined) closeEditor = true; - - var insertType = 'separate'; - - // update insert options with selected values if method is called by clicking on 'insert' button - // in dialog - if (event) { - UiDialog.close(this._getInsertDialogId()); - - var dialogContent = event.currentTarget.closest('.dialogContent'); - - /*if (this._mediaToInsert.size > 1) { - insertType = elBySel('select[name=insertType]', dialogContent).value; - }*/ - thumbnailSize = elBySel('select[name=thumbnailSize]', dialogContent).value; - } - - if (this._options.callbackInsert !== null) { - this._options.callbackInsert(this._mediaToInsert, insertType, thumbnailSize); - } - else { - if (insertType === 'separate') { - this._options.editor.buffer.set(); - - this._mediaToInsert.forEach(this._insertMediaItem.bind(this, thumbnailSize)); - } - else { - this._insertMediaGallery(); - } - } - - if (this._mediaToInsertByClipboard) { - var mediaIds = []; - this._mediaToInsert.forEach(function(media) { - mediaIds.push(media.mediaID); - }); - - ControllerClipboard.unmark('com.woltlab.wcf.media', mediaIds); - } - - this._mediaToInsert = new Dictionary(); - this._mediaToInsertByClipboard = false; - - // close manager dialog - if (closeEditor) { - UiDialog.close(this); - } - }, - - /** - * Inserts a series of uploaded images using a slider. - * - * @protected - */ - _insertMediaGallery: function() { - var mediaIds = []; - this._mediaToInsert.forEach(function(item) { - mediaIds.push(item.mediaID); - }); - - this._options.editor.buffer.set(); - this._options.editor.insert.text("[wsmg='" + mediaIds.join(',') + "'][/wsmg]"); - }, - - /** - * Inserts a single media item. - * - * @param {string} thumbnailSize preferred image dimension, is ignored for non-images - * @param {Object} item media item data - * @protected - */ - _insertMediaItem: function(thumbnailSize, item) { - if (item.isImage) { - var sizes = ['small', 'medium', 'large', 'original']; - - // check if size is actually available - var available = '', size; - for (var i = 0; i < 4; i++) { - size = sizes[i]; - - if (item[size + 'ThumbnailHeight'] != 0) { - available = size; - - if (thumbnailSize == size) { - break; - } - } - } - - thumbnailSize = available; - - if (!thumbnailSize) thumbnailSize = 'original'; - - var link = item.link; - if (thumbnailSize !== 'original') { - link = item[thumbnailSize + 'ThumbnailLink']; - } - - this._options.editor.insert.html(''); - } - else { - this._options.editor.insert.text("[wsm='" + item.mediaID + "'][/wsm]"); - } - }, - - /** - * Is called after media files are successfully uploaded to insert copied media. - * - * @param {object} data upload data - */ - _mediaUploaded: function(data) { - if (this._uploadId !== null && this._upload === data.upload) { - if (this._uploadId === data.uploadId || (Array.isArray(this._uploadId) && this._uploadId.indexOf(data.uploadId) !== -1)) { - this._mediaToInsert = Dictionary.fromObject(data.media); - this._insertMedia(null, 'medium', false); - - this._uploadId = null; - } - } - }, - - /** - * Handles clicking on the insert button. - * - * @param {Event} event insert button click event - */ - _openInsertDialog: function(event) { - this.insertMedia([~~elData(event.currentTarget, 'object-id')]); - }, - - /** - * Is called to insert the media files with the given ids into an editor. - * - * @param {int[]} mediaIds - */ - clipboardInsertMedia: function(mediaIds) { - this.insertMedia(mediaIds, true); - }, - - /** - * Prepares insertion of the media files with the given ids. - * - * @param {array} mediaIds ids of the media files to be inserted - * @param {boolean?} insertedByClipboard is true if the media files are inserted by clipboard - */ - insertMedia: function(mediaIds, insertedByClipboard) { - this._mediaToInsert = new Dictionary(); - this._mediaToInsertByClipboard = insertedByClipboard || false; - - // open the insert dialog if all media files are images - var imagesOnly = true, media; - for (var i = 0, length = mediaIds.length; i < length; i++) { - media = this._media.get(mediaIds[i]); - this._mediaToInsert.set(media.mediaID, media); - - if (!media.isImage) { - imagesOnly = false; - } - } - - if (imagesOnly) { - var thumbnailSizes = this._getThumbnailSizes(); - if (thumbnailSizes.length) { - UiDialog.close(this); - var dialogId = this._getInsertDialogId(); - if (UiDialog.getDialog(dialogId)) { - UiDialog.openStatic(dialogId); - } - else { - this._buildInsertDialog(); - } - } - else { - this._insertMedia(undefined, 'original'); - } - } - else { - this._insertMedia(); - } - }, - - /** - * @see WoltLabSuite/Core/Media/Manager/Base#getMode - */ - getMode: function() { - return 'editor'; - }, - - /** - * @see WoltLabSuite/Core/Media/Manager/Base#setupMediaElement - */ - setupMediaElement: function(media, mediaElement) { - MediaManagerEditor._super.prototype.setupMediaElement.call(this, media, mediaElement); - - // add media insertion icon - var buttons = elBySel('nav.buttonGroupNavigation > ul', mediaElement); - - var listItem = elCreate('li'); - listItem.className = 'jsMediaInsertButton'; - elData(listItem, 'object-id', media.mediaID); - buttons.appendChild(listItem); - - listItem.innerHTML = ' '; - } - }); - - return MediaManagerEditor; -}); diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Media/Manager/Editor.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Media/Manager/Editor.ts new file mode 100644 index 0000000000..642deb8335 --- /dev/null +++ b/wcfsetup/install/files/ts/WoltLabSuite/Core/Media/Manager/Editor.ts @@ -0,0 +1,403 @@ +/** + * Provides the media manager dialog for selecting media for Redactor editors. + * + * @author Matthias Schmidt + * @copyright 2001-2021 WoltLab GmbH + * @license GNU Lesser General Public License + * @module WoltLabSuite/Core/Media/Manager/Editor + */ + +import MediaManager from "./Base"; +import * as Core from "../../Core"; +import { Media, MediaManagerEditorOptions, MediaUploadSuccessEventData } from "../Data"; +import * as EventHandler from "../../Event/Handler"; +import * as DomTraverse from "../../Dom/Traverse"; +import * as Language from "../../Language"; +import * as UiDialog from "../../Ui/Dialog"; +import * as Clipboard from "../../Controller/Clipboard"; +import { OnDropPayload } from "../../Ui/Redactor/DragAndDrop"; + +type PasteFromClipboard = { + blob: Blob; +}; + +class MediaManagerEditor extends MediaManager { + protected _activeButton; + protected readonly _buttons: HTMLCollectionOf; + protected _mediaToInsert: Map; + protected _mediaToInsertByClipboard: boolean; + protected _uploadData: OnDropPayload | PasteFromClipboard | null; + protected _uploadId: number | null; + + constructor(options: Partial) { + options = Core.extend( + { + callbackInsert: null, + }, + options, + ); + + super(options); + + this._forceClipboard = true; + this._activeButton = null; + const context = this._options.editor ? this._options.editor.core.toolbar()[0] : undefined; + this._buttons = (context || window.document).getElementsByClassName( + this._options.buttonClass || "jsMediaEditorButton", + ) as HTMLCollectionOf; + Array.from(this._buttons).forEach((button) => { + button.addEventListener("click", (ev) => this._click(ev)); + }); + this._mediaToInsert = new Map(); + this._mediaToInsertByClipboard = false; + this._uploadData = null; + this._uploadId = null; + + if (this._options.editor && !this._options.editor.opts.woltlab.attachments) { + const editorId = this._options.editor.$editor[0].dataset.elementId as string; + + const uuid1 = EventHandler.add("com.woltlab.wcf.redactor2", `dragAndDrop_${editorId}`, (data: OnDropPayload) => + this._editorUpload(data), + ); + const uuid2 = EventHandler.add( + "com.woltlab.wcf.redactor2", + `pasteFromClipboard_${editorId}`, + (data: OnDropPayload) => this._editorUpload(data), + ); + + EventHandler.add("com.woltlab.wcf.redactor2", `destroy_${editorId}`, () => { + EventHandler.remove("com.woltlab.wcf.redactor2", `dragAndDrop_${editorId}`, uuid1); + EventHandler.remove("com.woltlab.wcf.redactor2", `dragAndDrop_${editorId}`, uuid2); + }); + + EventHandler.add("com.woltlab.wcf.media.upload", "success", (data) => this._mediaUploaded(data)); + } + } + + protected _addButtonEventListeners(): void { + super._addButtonEventListeners(); + + if (!this._mediaManagerMediaList) { + return; + } + + DomTraverse.childrenByTag(this._mediaManagerMediaList, "LI").forEach((listItem) => { + const insertIcon = listItem.querySelector(".jsMediaInsertButton"); + if (insertIcon) { + insertIcon.classList.remove("jsMediaInsertButton"); + insertIcon.addEventListener("click", (ev: MouseEvent) => this._openInsertDialog(ev)); + } + }); + } + + /** + * Builds the dialog to setup inserting media files. + */ + protected _buildInsertDialog(): void { + let thumbnailOptions = ""; + + this._getThumbnailSizes().forEach((thumbnailSize) => { + thumbnailOptions += + '"; + }); + thumbnailOptions += '"; + + const dialog = ` +
+
+
${Language.get("wcf.media.insert.imageSize")}
+
+ +
+
+
+
+ +
`; + + UiDialog.open({ + _dialogSetup: () => { + return { + id: this._getInsertDialogId(), + options: { + onClose: this._editorClose.bind(this), + onSetup: (content) => { + content.querySelector(".buttonPrimary")!.addEventListener("click", (ev) => this._insertMedia(ev)); + + (content.querySelector(".thumbnailSizeSelection") as HTMLElement).style.display = "block"; + }, + title: Language.get("wcf.media.insert"), + }, + source: dialog, + }; + }, + }); + } + + protected _click(event: MouseEvent): void { + this._activeButton = event.currentTarget; + + super._click(event); + } + + protected _dialogShow(): void { + super._dialogShow(); + + // check if data needs to be uploaded + if (this._uploadData) { + const fileUploadData = this._uploadData as OnDropPayload; + if (fileUploadData.file) { + this._upload.uploadFile(fileUploadData.file); + } else { + const blobUploadData = this._uploadData as PasteFromClipboard; + this._uploadId = this._upload.uploadBlob(blobUploadData.blob); + } + + this._uploadData = null; + } + } + + /** + * Handles pasting and dragging and dropping files into the editor. + */ + protected _editorUpload(data: OnDropPayload): void { + this._uploadData = data; + + UiDialog.open(this); + } + + /** + * Returns the id of the insert dialog based on the media files to be inserted. + */ + protected _getInsertDialogId(): string { + let dialogId = "mediaInsert"; + + this._mediaToInsert.forEach((media, mediaId) => { + dialogId += `-${mediaId}`; + }); + + return dialogId; + } + + /** + * Returns the supported thumbnail sizes (excluding `original`) for all media images to be inserted. + */ + protected _getThumbnailSizes(): string[] { + return ["small", "medium", "large"] + .map((size) => { + let supportSize = true; + this._mediaToInsert.forEach(function (media) { + if (!media[size + "ThumbnailType"]) { + supportSize = false; + } + }); + + if (supportSize) { + return size; + } + + return null; + }) + .filter((s) => s !== null) as string[]; + } + + /** + * Inserts media files into the editor. + */ + protected _insertMedia(event?: Event | null, thumbnailSize?: string, closeEditor = false): void { + if (closeEditor === undefined) closeEditor = true; + + const insertType = "separate"; + + // update insert options with selected values if method is called by clicking on 'insert' button + // in dialog + if (event) { + UiDialog.close(this._getInsertDialogId()); + + const dialogContent = (event.currentTarget as HTMLElement).closest(".dialogContent")!; + const thumbnailSizeSelect = dialogContent.querySelector("select[name=thumbnailSize]") as HTMLSelectElement; + thumbnailSize = thumbnailSizeSelect.value; + } + + if (this._options.callbackInsert !== null) { + this._options.callbackInsert(this._mediaToInsert, insertType, thumbnailSize!); + } else { + if (insertType === "separate") { + this._options.editor!.buffer.set(); + + this._mediaToInsert.forEach((media) => this._insertMediaItem(thumbnailSize!, media)); + } else { + this._insertMediaGallery(); + } + } + + if (this._mediaToInsertByClipboard) { + const mediaIds: number[] = []; + this._mediaToInsert.forEach(function (media) { + mediaIds.push(~~media.mediaID); + }); + + Clipboard.unmark("com.woltlab.wcf.media", mediaIds); + } + + this._mediaToInsert = new Map(); + this._mediaToInsertByClipboard = false; + + // close manager dialog + if (closeEditor) { + UiDialog.close(this); + } + } + + /** + * Inserts a series of uploaded images into the editor using a slider. + */ + protected _insertMediaGallery(): void { + const mediaIds: number[] = []; + this._mediaToInsert.forEach(function (media) { + mediaIds.push(media.mediaID); + }); + + this._options.editor!.buffer.set(); + this._options.editor!.insert.text("[wsmg='" + mediaIds.join(",") + "'][/wsmg]"); + } + + /** + * Inserts a single media item into the editor. + */ + protected _insertMediaItem(thumbnailSize: string, media: Media): void { + if (media.isImage) { + let available = ""; + ["small", "medium", "large", "original"].some((size) => { + if (media[size + "ThumbnailHeight"] != 0) { + available = size; + + if (thumbnailSize == size) { + return true; + } + } + + return false; + }); + + thumbnailSize = available; + + if (!thumbnailSize) { + thumbnailSize = "original"; + } + + let link = media.link; + if (thumbnailSize !== "original") { + link = media[thumbnailSize + "ThumbnailLink"]; + } + + this._options.editor!.insert.html( + ``, + ); + } else { + this._options.editor!.insert.text(`[wsm='${media.mediaID}'][/wsm]`); + } + } + + /** + * Is called after media files are successfully uploaded to insert copied media. + */ + protected _mediaUploaded(data: MediaUploadSuccessEventData): void { + if (this._uploadId !== null && this._upload === data.upload) { + if ( + this._uploadId === data.uploadId || + (Array.isArray(this._uploadId) && this._uploadId.indexOf(data.uploadId) !== -1) + ) { + this._mediaToInsert = new Map(data.media.entries()); + this._insertMedia(null, "medium", false); + + this._uploadId = null; + } + } + } + + /** + * Handles clicking on the insert button. + */ + protected _openInsertDialog(event: MouseEvent): void { + const target = event.currentTarget as HTMLElement; + + this.insertMedia([~~target.dataset.objectId!]); + } + + /** + * Is called to insert the media files with the given ids into an editor. + */ + public clipboardInsertMedia(mediaIds: number[]): void { + this.insertMedia(mediaIds, true); + } + + /** + * Prepares insertion of the media files with the given ids. + */ + public insertMedia(mediaIds: number[], insertedByClipboard?: boolean): void { + this._mediaToInsert = new Map(); + this._mediaToInsertByClipboard = insertedByClipboard || false; + + // open the insert dialog if all media files are images + let imagesOnly = true, + media; + for (let i = 0, length = mediaIds.length; i < length; i++) { + media = this._media.get(mediaIds[i]); + this._mediaToInsert.set(media.mediaID, media); + + if (!media.isImage) { + imagesOnly = false; + } + } + + if (imagesOnly) { + const thumbnailSizes = this._getThumbnailSizes(); + if (thumbnailSizes.length) { + UiDialog.close(this); + const dialogId = this._getInsertDialogId(); + if (UiDialog.getDialog(dialogId)) { + UiDialog.openStatic(dialogId, null); + } else { + this._buildInsertDialog(); + } + } else { + this._insertMedia(undefined, "original"); + } + } else { + this._insertMedia(); + } + } + + public getMode(): string { + return "editor"; + } + + public setupMediaElement(media: Media, mediaElement: HTMLElement): void { + super.setupMediaElement(media, mediaElement); + + // add media insertion icon + const buttons = mediaElement.querySelector("nav.buttonGroupNavigation > ul")!; + + const listItem = document.createElement("li"); + listItem.className = "jsMediaInsertButton"; + listItem.dataset.objectId = (media.mediaID as unknown) as string; + buttons.appendChild(listItem); + + listItem.innerHTML = ` + + + + `; + } +} + +Core.enableLegacyInheritance(MediaManagerEditor); + +export = MediaManagerEditor; -- 2.20.1