Convert `Ui/Comment/Edit` to TypeScript
authorAlexander Ebert <ebert@woltlab.com>
Fri, 6 Nov 2020 12:35:15 +0000 (13:35 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Fri, 6 Nov 2020 12:35:15 +0000 (13:35 +0100)
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Comment/Edit.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Notification.js
wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Comment/Edit.js [deleted file]
wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Comment/Edit.ts [new file with mode: 0644]
wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Notification.ts

index 0c30e19cca162d0335ef1b498eb1421edee8d44d..74f0e07d8f02d8c24a4e96b7d7523a42265c1188 100644 (file)
 /**
  * Provides editing support for comments.
  *
- * @author     Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLabSuite/Core/Ui/Comment/Edit
+ * @author  Alexander Ebert
+ * @copyright  2001-2019 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Ui/Comment/Edit
  */
-define([
-    'Ajax', 'Core', 'Dictionary', 'Environment',
-    'EventHandler', 'Language', 'List', 'Dom/ChangeListener', 'Dom/Traverse',
-    'Dom/Util', 'Ui/Notification', 'Ui/ReusableDropdown', 'WoltLabSuite/Core/Ui/Scroll'
-], function (Ajax, Core, Dictionary, Environment, EventHandler, Language, List, DomChangeListener, DomTraverse, DomUtil, UiNotification, UiReusableDropdown, UiScroll) {
+define(["require", "exports", "tslib", "../../Ajax", "../../Core", "../../Dom/Change/Listener", "../../Dom/Util", "../../Environment", "../../Event/Handler", "../../Language", "../Scroll", "../Notification"], function (require, exports, tslib_1, Ajax, Core, Listener_1, Util_1, Environment, EventHandler, Language, UiScroll, UiNotification) {
     "use strict";
-    if (!COMPILER_TARGET_DEFAULT) {
-        var Fake = function () { };
-        Fake.prototype = {
-            init: function () { },
-            rebuild: function () { },
-            _click: function () { },
-            _prepare: function () { },
-            _showEditor: function () { },
-            _restoreMessage: function () { },
-            _save: function () { },
-            _validate: function () { },
-            throwError: function () { },
-            _showMessage: function () { },
-            _hideEditor: function () { },
-            _restoreEditor: function () { },
-            _destroyEditor: function () { },
-            _getEditorId: function () { },
-            _getObjectId: function () { },
-            _ajaxFailure: function () { },
-            _ajaxSuccess: function () { },
-            _ajaxSetup: function () { }
-        };
-        return Fake;
-    }
-    /**
-     * @constructor
-     */
-    function UiCommentEdit(container) { this.init(container); }
-    UiCommentEdit.prototype = {
+    Ajax = tslib_1.__importStar(Ajax);
+    Core = tslib_1.__importStar(Core);
+    Listener_1 = tslib_1.__importDefault(Listener_1);
+    Util_1 = tslib_1.__importDefault(Util_1);
+    Environment = tslib_1.__importStar(Environment);
+    EventHandler = tslib_1.__importStar(EventHandler);
+    Language = tslib_1.__importStar(Language);
+    UiScroll = tslib_1.__importStar(UiScroll);
+    UiNotification = tslib_1.__importStar(UiNotification);
+    class UiCommentEdit {
         /**
          * Initializes the comment edit manager.
-         *
-         * @param      {Element}       container       container element
          */
-        init: function (container) {
+        constructor(container) {
             this._activeElement = null;
-            this._callbackClick = null;
-            this._comments = new List();
-            this._container = container;
+            this._comments = new Set();
             this._editorContainer = null;
+            this._container = container;
             this.rebuild();
-            DomChangeListener.add('Ui/Comment/Edit_' + DomUtil.identify(this._container), this.rebuild.bind(this));
-        },
+            Listener_1.default.add("Ui/Comment/Edit_" + Util_1.default.identify(this._container), this.rebuild.bind(this));
+        }
         /**
          * Initializes each applicable message, should be called whenever new
          * messages are being displayed.
          */
-        rebuild: function () {
-            elBySelAll('.comment', this._container, (function (comment) {
+        rebuild() {
+            this._container.querySelectorAll(".comment").forEach((comment) => {
                 if (this._comments.has(comment)) {
                     return;
                 }
-                if (elDataBool(comment, 'can-edit')) {
-                    var button = elBySel('.jsCommentEditButton', comment);
+                if (Core.stringToBool(comment.dataset.canEdit || "")) {
+                    const button = comment.querySelector(".jsCommentEditButton");
                     if (button !== null) {
-                        if (this._callbackClick === null) {
-                            this._callbackClick = this._click.bind(this);
-                        }
-                        button.addEventListener('click', this._callbackClick);
+                        button.addEventListener("click", (ev) => this._click(ev));
                     }
                 }
                 this._comments.add(comment);
-            }).bind(this));
-        },
+            });
+        }
         /**
          * Handles clicks on the edit button.
-         *
-         * @param      {?Event}        event           event object
-         * @protected
          */
-        _click: function (event) {
+        _click(event) {
             event.preventDefault();
             if (this._activeElement === null) {
-                this._activeElement = event.currentTarget.closest('.comment');
+                const target = event.currentTarget;
+                this._activeElement = target.closest(".comment");
                 this._prepare();
                 Ajax.api(this, {
-                    actionName: 'beginEdit',
-                    objectIDs: [this._getObjectId(this._activeElement)]
+                    actionName: "beginEdit",
+                    objectIDs: [this._getObjectId(this._activeElement)],
                 });
             }
             else {
-                UiNotification.show('wcf.message.error.editorAlreadyInUse', null, 'warning');
+                UiNotification.show("wcf.message.error.editorAlreadyInUse", null, "warning");
             }
-        },
+        }
         /**
          * Prepares the message for editor display.
-         *
-         * @protected
          */
-        _prepare: function () {
-            this._editorContainer = elCreate('div');
-            this._editorContainer.className = 'commentEditorContainer';
+        _prepare() {
+            this._editorContainer = document.createElement("div");
+            this._editorContainer.className = "commentEditorContainer";
             this._editorContainer.innerHTML = '<span class="icon icon48 fa-spinner"></span>';
-            var content = elBySel('.commentContentContainer', this._activeElement);
+            const content = this._activeElement.querySelector(".commentContentContainer");
             content.insertBefore(this._editorContainer, content.firstChild);
-        },
+        }
         /**
          * Shows the message editor.
-         *
-         * @param      {Object}        data            ajax response data
-         * @protected
          */
-        _showEditor: function (data) {
-            var id = this._getEditorId();
-            var icon = elBySel('.icon', this._editorContainer);
-            elRemove(icon);
-            var editor = elCreate('div');
-            editor.className = 'editorContainer';
-            //noinspection JSUnresolvedVariable
-            DomUtil.setInnerHtml(editor, data.returnValues.template);
-            this._editorContainer.appendChild(editor);
+        _showEditor(data) {
+            const id = this._getEditorId();
+            const editorContainer = this._editorContainer;
+            const icon = editorContainer.querySelector(".icon");
+            icon.remove();
+            const editor = document.createElement("div");
+            editor.className = "editorContainer";
+            Util_1.default.setInnerHtml(editor, data.returnValues.template);
+            editorContainer.appendChild(editor);
             // bind buttons
-            var formSubmit = elBySel('.formSubmit', editor);
-            var buttonSave = elBySel('button[data-type="save"]', formSubmit);
-            buttonSave.addEventListener('click', this._save.bind(this));
-            var buttonCancel = elBySel('button[data-type="cancel"]', formSubmit);
-            buttonCancel.addEventListener('click', this._restoreMessage.bind(this));
-            EventHandler.add('com.woltlab.wcf.redactor', 'submitEditor_' + id, (function (data) {
+            const formSubmit = editorContainer.querySelector(".formSubmit");
+            const buttonSave = formSubmit.querySelector('button[data-type="save"]');
+            buttonSave.addEventListener("click", () => this._save());
+            const buttonCancel = formSubmit.querySelector('button[data-type="cancel"]');
+            buttonCancel.addEventListener("click", () => this._restoreMessage());
+            EventHandler.add("com.woltlab.wcf.redactor", `submitEditor_${id}`, (data) => {
                 data.cancel = true;
                 this._save();
-            }).bind(this));
-            var editorElement = elById(id);
-            if (Environment.editor() === 'redactor') {
-                window.setTimeout((function () {
+            });
+            const editorElement = document.getElementById(id);
+            if (Environment.editor() === "redactor") {
+                window.setTimeout(() => {
                     UiScroll.element(this._activeElement);
-                }).bind(this), 250);
+                }, 250);
             }
             else {
                 editorElement.focus();
             }
-        },
+        }
         /**
          * Restores the message view.
-         *
-         * @protected
          */
-        _restoreMessage: function () {
+        _restoreMessage() {
             this._destroyEditor();
-            elRemove(this._editorContainer);
+            this._editorContainer.remove();
             this._activeElement = null;
-        },
+        }
         /**
          * Saves the editor message.
-         *
-         * @protected
          */
-        _save: function () {
-            var parameters = {
+        _save() {
+            const parameters = {
                 data: {
-                    message: ''
-                }
+                    message: "",
+                },
             };
-            var id = this._getEditorId();
-            EventHandler.fire('com.woltlab.wcf.redactor2', 'getText_' + id, parameters.data);
+            const id = this._getEditorId();
+            EventHandler.fire("com.woltlab.wcf.redactor2", `getText_${id}`, parameters.data);
             if (!this._validate(parameters)) {
                 // validation failed
                 return;
             }
-            EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_' + id, parameters);
+            EventHandler.fire("com.woltlab.wcf.redactor2", `submit_${id}`, parameters);
             Ajax.api(this, {
-                actionName: 'save',
+                actionName: "save",
                 objectIDs: [this._getObjectId(this._activeElement)],
-                parameters: parameters
+                parameters: parameters,
             });
             this._hideEditor();
-        },
+        }
         /**
          * Validates the message and invokes listeners to perform additional validation.
-         *
-         * @param       {Object}        parameters      request parameters
-         * @return      {boolean}       validation result
-         * @protected
          */
-        _validate: function (parameters) {
+        _validate(parameters) {
             // remove all existing error elements
-            elBySelAll('.innerError', this._activeElement, elRemove);
+            this._activeElement.querySelectorAll(".innerError").forEach((el) => el.remove());
             // check if editor contains actual content
-            var editorElement = elById(this._getEditorId());
-            if (window.jQuery(editorElement).data('redactor').utils.isEmpty()) {
-                this.throwError(editorElement, Language.get('wcf.global.form.error.empty'));
+            const editorElement = document.getElementById(this._getEditorId());
+            const redactor = window.jQuery(editorElement).data("redactor");
+            if (redactor.utils.isEmpty()) {
+                this.throwError(editorElement, Language.get("wcf.global.form.error.empty"));
                 return false;
             }
-            var data = {
+            const data = {
                 api: this,
                 parameters: parameters,
-                valid: true
+                valid: true,
             };
-            EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_' + this._getEditorId(), data);
-            return (data.valid !== false);
-        },
+            EventHandler.fire("com.woltlab.wcf.redactor2", "validate_" + this._getEditorId(), data);
+            return data.valid;
+        }
         /**
          * Throws an error by adding an inline error to target element.
-         *
-         * @param       {Element}       element         erroneous element
-         * @param       {string}        message         error message
          */
-        throwError: function (element, message) {
-            elInnerError(element, message);
-        },
+        throwError(element, message) {
+            Util_1.default.innerError(element, message);
+        }
         /**
          * Shows the update message.
-         *
-         * @param      {Object}        data            ajax response data
-         * @protected
          */
-        _showMessage: function (data) {
+        _showMessage(data) {
             // set new content
-            //noinspection JSCheckFunctionSignatures
-            DomUtil.setInnerHtml(elBySel('.commentContent .userMessage', this._editorContainer.parentNode), data.returnValues.message);
+            const container = this._editorContainer.parentElement.querySelector(".commentContent .userMessage");
+            Util_1.default.setInnerHtml(container, data.returnValues.message);
             this._restoreMessage();
             UiNotification.show();
-        },
+        }
         /**
          * Hides the editor from view.
-         *
-         * @protected
          */
-        _hideEditor: function () {
-            elHide(elBySel('.editorContainer', this._editorContainer));
-            var icon = elCreate('span');
-            icon.className = 'icon icon48 fa-spinner';
+        _hideEditor() {
+            const editorContainer = this._editorContainer.querySelector(".editorContainer");
+            Util_1.default.hide(editorContainer);
+            const icon = document.createElement("span");
+            icon.className = "icon icon48 fa-spinner";
             this._editorContainer.appendChild(icon);
-        },
+        }
         /**
          * Restores the previously hidden editor.
-         *
-         * @protected
          */
-        _restoreEditor: function () {
-            var icon = elBySel('.fa-spinner', this._editorContainer);
-            elRemove(icon);
-            var editorContainer = elBySel('.editorContainer', this._editorContainer);
-            if (editorContainer !== null)
-                elShow(editorContainer);
-        },
+        _restoreEditor() {
+            const icon = this._editorContainer.querySelector(".fa-spinner");
+            icon.remove();
+            const editorContainer = this._editorContainer.querySelector(".editorContainer");
+            if (editorContainer !== null) {
+                Util_1.default.show(editorContainer);
+            }
+        }
         /**
          * Destroys the editor instance.
-         *
-         * @protected
          */
-        _destroyEditor: function () {
-            EventHandler.fire('com.woltlab.wcf.redactor2', 'autosaveDestroy_' + this._getEditorId());
-            EventHandler.fire('com.woltlab.wcf.redactor2', 'destroy_' + this._getEditorId());
-        },
+        _destroyEditor() {
+            EventHandler.fire("com.woltlab.wcf.redactor2", `autosaveDestroy_${this._getEditorId()}`);
+            EventHandler.fire("com.woltlab.wcf.redactor2", `destroy_${this._getEditorId()}`);
+        }
         /**
          * Returns the unique editor id.
-         *
-         * @return     {string}        editor id
-         * @protected
          */
-        _getEditorId: function () {
-            return 'commentEditor' + this._getObjectId(this._activeElement);
-        },
+        _getEditorId() {
+            return `commentEditor${this._getObjectId(this._activeElement)}`;
+        }
         /**
          * Returns the element's `data-object-id` value.
-         *
-         * @param      {Element}       element         target element
-         * @return     {int}
-         * @protected
          */
-        _getObjectId: function (element) {
-            return ~~elData(element, 'object-id');
-        },
-        _ajaxFailure: function (data) {
-            var editor = elBySel('.redactor-layer', this._editorContainer);
+        _getObjectId(element) {
+            return ~~element.dataset.objectId;
+        }
+        _ajaxFailure(data) {
+            const editor = this._editorContainer.querySelector(".redactor-layer");
             // handle errors occurring on editor load
             if (editor === null) {
                 this._restoreMessage();
                 return true;
             }
             this._restoreEditor();
-            //noinspection JSUnresolvedVariable
             if (!data || data.returnValues === undefined || data.returnValues.errorType === undefined) {
                 return true;
             }
-            //noinspection JSUnresolvedVariable
-            elInnerError(editor, data.returnValues.errorType);
+            Util_1.default.innerError(editor, data.returnValues.errorType);
             return false;
-        },
-        _ajaxSuccess: function (data) {
+        }
+        _ajaxSuccess(data) {
             switch (data.actionName) {
-                case 'beginEdit':
+                case "beginEdit":
                     this._showEditor(data);
                     break;
-                case 'save':
+                case "save":
                     this._showMessage(data);
                     break;
             }
-        },
-        _ajaxSetup: function () {
-            var objectTypeId = ~~elData(this._container, 'object-type-id');
+        }
+        _ajaxSetup() {
+            const objectTypeId = ~~this._container.dataset.objectTypeId;
             return {
                 data: {
-                    className: 'wcf\\data\\comment\\CommentAction',
+                    className: "wcf\\data\\comment\\CommentAction",
                     parameters: {
                         data: {
-                            objectTypeID: objectTypeId
-                        }
-                    }
+                            objectTypeID: objectTypeId,
+                        },
+                    },
                 },
-                silent: true
+                silent: true,
             };
         }
-    };
+    }
+    Core.enableLegacyInheritance(UiCommentEdit);
     return UiCommentEdit;
 });
index 6651195eaf1c36c2fba18ac15298dca3faf81d9f..3b5d89d471879a4ae0bf75bdd61167618fa86010 100644 (file)
@@ -19,8 +19,9 @@ define(["require", "exports", "tslib", "../Language"], function (require, export
     let _notificationElement;
     let _timeout;
     function init() {
-        if (_didInit)
+        if (_didInit) {
             return;
+        }
         _didInit = true;
         _notificationElement = document.createElement("div");
         _notificationElement.id = "systemNotification";
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Comment/Edit.js b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Comment/Edit.js
deleted file mode 100644 (file)
index 8de559d..0000000
+++ /dev/null
@@ -1,387 +0,0 @@
-/**
- * Provides editing support for comments.
- * 
- * @author     Alexander Ebert
- * @copyright  2001-2019 WoltLab GmbH
- * @license    GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @module     WoltLabSuite/Core/Ui/Comment/Edit
- */
-define(
-       [
-               'Ajax',         'Core',            'Dictionary',          'Environment',
-               'EventHandler', 'Language',        'List',                'Dom/ChangeListener', 'Dom/Traverse',
-               'Dom/Util',     'Ui/Notification', 'Ui/ReusableDropdown', 'WoltLabSuite/Core/Ui/Scroll'
-       ],
-       function(
-               Ajax,            Core,              Dictionary,            Environment,
-               EventHandler,    Language,          List,                  DomChangeListener,    DomTraverse,
-               DomUtil,         UiNotification,    UiReusableDropdown,    UiScroll
-       )
-{
-       "use strict";
-       
-       if (!COMPILER_TARGET_DEFAULT) {
-               var Fake = function() {};
-               Fake.prototype = {
-                       init: function() {},
-                       rebuild: function() {},
-                       _click: function() {},
-                       _prepare: function() {},
-                       _showEditor: function() {},
-                       _restoreMessage: function() {},
-                       _save: function() {},
-                       _validate: function() {},
-                       throwError: function() {},
-                       _showMessage: function() {},
-                       _hideEditor: function() {},
-                       _restoreEditor: function() {},
-                       _destroyEditor: function() {},
-                       _getEditorId: function() {},
-                       _getObjectId: function() {},
-                       _ajaxFailure: function() {},
-                       _ajaxSuccess: function() {},
-                       _ajaxSetup: function() {}
-               };
-               return Fake;
-       }
-       
-       /**
-        * @constructor
-        */
-       function UiCommentEdit(container) { this.init(container); }
-       UiCommentEdit.prototype = {
-               /**
-                * Initializes the comment edit manager.
-                * 
-                * @param       {Element}       container       container element
-                */
-               init: function(container) {
-                       this._activeElement = null;
-                       this._callbackClick = null;
-                       this._comments = new List();
-                       this._container = container;
-                       this._editorContainer = null;
-                       
-                       this.rebuild();
-                       
-                       DomChangeListener.add('Ui/Comment/Edit_' + DomUtil.identify(this._container), this.rebuild.bind(this));
-               },
-               
-               /**
-                * Initializes each applicable message, should be called whenever new
-                * messages are being displayed.
-                */
-               rebuild: function() {
-                       elBySelAll('.comment', this._container, (function (comment) {
-                               if (this._comments.has(comment)) {
-                                       return;
-                               }
-                               
-                               if (elDataBool(comment, 'can-edit')) {
-                                       var button = elBySel('.jsCommentEditButton', comment);
-                                       if (button !== null) {
-                                               if (this._callbackClick === null) {
-                                                       this._callbackClick = this._click.bind(this);
-                                               }
-                                               
-                                               button.addEventListener('click', this._callbackClick);
-                                       }
-                               }
-                               
-                               this._comments.add(comment);
-                       }).bind(this));
-               },
-               
-               /**
-                * Handles clicks on the edit button.
-                * 
-                * @param       {?Event}        event           event object
-                * @protected
-                */
-               _click: function(event) {
-                       event.preventDefault();
-                       
-                       if (this._activeElement === null) {
-                               this._activeElement = event.currentTarget.closest('.comment');
-                               
-                               this._prepare();
-                               
-                               Ajax.api(this, {
-                                       actionName: 'beginEdit',
-                                       objectIDs: [this._getObjectId(this._activeElement)]
-                               });
-                       }
-                       else {
-                               UiNotification.show('wcf.message.error.editorAlreadyInUse', null, 'warning');
-                       }
-               },
-               
-               /**
-                * Prepares the message for editor display.
-                * 
-                * @protected
-                */
-               _prepare: function() {
-                       this._editorContainer = elCreate('div');
-                       this._editorContainer.className = 'commentEditorContainer';
-                       this._editorContainer.innerHTML = '<span class="icon icon48 fa-spinner"></span>';
-                       
-                       var content = elBySel('.commentContentContainer', this._activeElement);
-                       content.insertBefore(this._editorContainer, content.firstChild);
-               },
-               
-               /**
-                * Shows the message editor.
-                * 
-                * @param       {Object}        data            ajax response data
-                * @protected
-                */
-               _showEditor: function(data) {
-                       var id = this._getEditorId();
-                       
-                       var icon = elBySel('.icon', this._editorContainer);
-                       elRemove(icon);
-                       
-                       var editor = elCreate('div');
-                       editor.className = 'editorContainer';
-                       //noinspection JSUnresolvedVariable
-                       DomUtil.setInnerHtml(editor, data.returnValues.template);
-                       this._editorContainer.appendChild(editor);
-                       
-                       // bind buttons
-                       var formSubmit = elBySel('.formSubmit', editor);
-                       
-                       var buttonSave = elBySel('button[data-type="save"]', formSubmit);
-                       buttonSave.addEventListener('click', this._save.bind(this));
-                       
-                       var buttonCancel = elBySel('button[data-type="cancel"]', formSubmit);
-                       buttonCancel.addEventListener('click', this._restoreMessage.bind(this));
-                       
-                       EventHandler.add('com.woltlab.wcf.redactor', 'submitEditor_' + id, (function(data) {
-                               data.cancel = true;
-                               
-                               this._save();
-                       }).bind(this));
-                       
-                       var editorElement = elById(id);
-                       if (Environment.editor() === 'redactor') {
-                               window.setTimeout((function() {
-                                       UiScroll.element(this._activeElement);
-                               }).bind(this), 250);
-                       }
-                       else {
-                               editorElement.focus();
-                       }
-               },
-               
-               /**
-                * Restores the message view.
-                * 
-                * @protected
-                */
-               _restoreMessage: function() {
-                       this._destroyEditor();
-                       
-                       elRemove(this._editorContainer);
-                       
-                       this._activeElement = null;
-               },
-               
-               /**
-                * Saves the editor message.
-                * 
-                * @protected
-                */
-               _save: function() {
-                       var parameters = {
-                               data: {
-                                       message: ''
-                               }
-                       };
-                       
-                       var id = this._getEditorId();
-                       
-                       EventHandler.fire('com.woltlab.wcf.redactor2', 'getText_' + id, parameters.data);
-                       
-                       if (!this._validate(parameters)) {
-                               // validation failed
-                               return;
-                       }
-                       
-                       EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_' + id, parameters);
-                       
-                       Ajax.api(this, {
-                               actionName: 'save',
-                               objectIDs: [this._getObjectId(this._activeElement)],
-                               parameters: parameters
-                       });
-                       
-                       this._hideEditor();
-               },
-               
-               /**
-                * Validates the message and invokes listeners to perform additional validation.
-                *
-                * @param       {Object}        parameters      request parameters
-                * @return      {boolean}       validation result
-                * @protected
-                */
-               _validate: function(parameters) {
-                       // remove all existing error elements
-                       elBySelAll('.innerError', this._activeElement, elRemove);
-                       
-                       // check if editor contains actual content
-                       var editorElement = elById(this._getEditorId());
-                       if (window.jQuery(editorElement).data('redactor').utils.isEmpty()) {
-                               this.throwError(editorElement, Language.get('wcf.global.form.error.empty'));
-                               return false;
-                       }
-                       
-                       var data = {
-                               api: this,
-                               parameters: parameters,
-                               valid: true
-                       };
-                       
-                       EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_' + this._getEditorId(), data);
-                       
-                       return (data.valid !== false);
-               },
-               
-               /**
-                * Throws an error by adding an inline error to target element.
-                *
-                * @param       {Element}       element         erroneous element
-                * @param       {string}        message         error message
-                */
-               throwError: function(element, message) {
-                       elInnerError(element, message);
-               },
-               
-               /**
-                * Shows the update message.
-                * 
-                * @param       {Object}        data            ajax response data
-                * @protected
-                */
-               _showMessage: function(data) {
-                       // set new content
-                       //noinspection JSCheckFunctionSignatures
-                       DomUtil.setInnerHtml(elBySel('.commentContent .userMessage', this._editorContainer.parentNode), data.returnValues.message);
-                       
-                       this._restoreMessage();
-                       
-                       UiNotification.show();
-               },
-               
-               /**
-                * Hides the editor from view.
-                * 
-                * @protected
-                */
-               _hideEditor: function() {
-                       elHide(elBySel('.editorContainer', this._editorContainer));
-                       
-                       var icon = elCreate('span');
-                       icon.className = 'icon icon48 fa-spinner';
-                       this._editorContainer.appendChild(icon);
-               },
-               
-               /**
-                * Restores the previously hidden editor.
-                * 
-                * @protected
-                */
-               _restoreEditor: function() {
-                       var icon = elBySel('.fa-spinner', this._editorContainer);
-                       elRemove(icon);
-                       
-                       var editorContainer = elBySel('.editorContainer', this._editorContainer);
-                       if (editorContainer !== null) elShow(editorContainer);
-               },
-               
-               /**
-                * Destroys the editor instance.
-                * 
-                * @protected
-                */
-               _destroyEditor: function() {
-                       EventHandler.fire('com.woltlab.wcf.redactor2', 'autosaveDestroy_' + this._getEditorId());
-                       EventHandler.fire('com.woltlab.wcf.redactor2', 'destroy_' + this._getEditorId());
-               },
-               
-               /**
-                * Returns the unique editor id.
-                * 
-                * @return      {string}        editor id
-                * @protected
-                */
-               _getEditorId: function() {
-                       return 'commentEditor' + this._getObjectId(this._activeElement);
-               },
-               
-               /**
-                * Returns the element's `data-object-id` value.
-                * 
-                * @param       {Element}       element         target element
-                * @return      {int}
-                * @protected
-                */
-               _getObjectId: function(element) {
-                       return ~~elData(element, 'object-id');
-               },
-               
-               _ajaxFailure: function(data) {
-                       var editor = elBySel('.redactor-layer', this._editorContainer);
-                       
-                       // handle errors occurring on editor load
-                       if (editor === null) {
-                               this._restoreMessage();
-                               
-                               return true;
-                       }
-                       
-                       this._restoreEditor();
-                       
-                       //noinspection JSUnresolvedVariable
-                       if (!data || data.returnValues === undefined || data.returnValues.errorType === undefined) {
-                               return true;
-                       }
-                       
-                       //noinspection JSUnresolvedVariable
-                       elInnerError(editor, data.returnValues.errorType);
-                       
-                       return false;
-               },
-               
-               _ajaxSuccess: function(data) {
-                       switch (data.actionName) {
-                               case 'beginEdit':
-                                       this._showEditor(data);
-                                       break;
-                                       
-                               case 'save':
-                                       this._showMessage(data);
-                                       break;
-                       }
-               },
-               
-               _ajaxSetup: function() {
-                       var objectTypeId = ~~elData(this._container, 'object-type-id');
-                       
-                       return {
-                               data: {
-                                       className: 'wcf\\data\\comment\\CommentAction',
-                                       parameters: {
-                                               data: {
-                                                       objectTypeID: objectTypeId
-                                               }
-                                       }
-                               },
-                               silent: true
-                       };
-               }
-       };
-       
-       return UiCommentEdit;
-});
diff --git a/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Comment/Edit.ts b/wcfsetup/install/files/ts/WoltLabSuite/Core/Ui/Comment/Edit.ts
new file mode 100644 (file)
index 0000000..662333b
--- /dev/null
@@ -0,0 +1,329 @@
+/**
+ * Provides editing support for comments.
+ *
+ * @author  Alexander Ebert
+ * @copyright  2001-2019 WoltLab GmbH
+ * @license  GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module  WoltLabSuite/Core/Ui/Comment/Edit
+ */
+
+import * as Ajax from "../../Ajax";
+import { AjaxCallbackSetup, ResponseData } from "../../Ajax/Data";
+import * as Core from "../../Core";
+import DomChangeListener from "../../Dom/Change/Listener";
+import DomUtil from "../../Dom/Util";
+import * as Environment from "../../Environment";
+import * as EventHandler from "../../Event/Handler";
+import * as Language from "../../Language";
+import { RedactorEditor } from "../Redactor/Editor";
+import * as UiScroll from "../Scroll";
+import * as UiNotification from "../Notification";
+
+interface AjaxResponse {
+  actionName: string;
+  returnValues: {
+    message: string;
+    template: string;
+  };
+}
+
+class UiCommentEdit {
+  protected _activeElement: HTMLElement | null = null;
+  protected readonly _comments = new Set<HTMLElement>();
+  protected readonly _container: HTMLElement;
+  protected _editorContainer: HTMLElement | null = null;
+
+  /**
+   * Initializes the comment edit manager.
+   */
+  constructor(container: HTMLElement) {
+    this._container = container;
+
+    this.rebuild();
+
+    DomChangeListener.add("Ui/Comment/Edit_" + DomUtil.identify(this._container), this.rebuild.bind(this));
+  }
+
+  /**
+   * Initializes each applicable message, should be called whenever new
+   * messages are being displayed.
+   */
+  rebuild(): void {
+    this._container.querySelectorAll(".comment").forEach((comment: HTMLElement) => {
+      if (this._comments.has(comment)) {
+        return;
+      }
+
+      if (Core.stringToBool(comment.dataset.canEdit || "")) {
+        const button = comment.querySelector(".jsCommentEditButton") as HTMLAnchorElement;
+        if (button !== null) {
+          button.addEventListener("click", (ev) => this._click(ev));
+        }
+      }
+
+      this._comments.add(comment);
+    });
+  }
+
+  /**
+   * Handles clicks on the edit button.
+   */
+  protected _click(event: MouseEvent): void {
+    event.preventDefault();
+
+    if (this._activeElement === null) {
+      const target = event.currentTarget as HTMLElement;
+      this._activeElement = target.closest(".comment") as HTMLElement;
+
+      this._prepare();
+
+      Ajax.api(this, {
+        actionName: "beginEdit",
+        objectIDs: [this._getObjectId(this._activeElement)],
+      });
+    } else {
+      UiNotification.show("wcf.message.error.editorAlreadyInUse", null, "warning");
+    }
+  }
+
+  /**
+   * Prepares the message for editor display.
+   */
+  protected _prepare(): void {
+    this._editorContainer = document.createElement("div");
+    this._editorContainer.className = "commentEditorContainer";
+    this._editorContainer.innerHTML = '<span class="icon icon48 fa-spinner"></span>';
+
+    const content = this._activeElement!.querySelector(".commentContentContainer")!;
+    content.insertBefore(this._editorContainer, content.firstChild);
+  }
+
+  /**
+   * Shows the message editor.
+   */
+  protected _showEditor(data: AjaxResponse): void {
+    const id = this._getEditorId();
+    const editorContainer = this._editorContainer!;
+
+    const icon = editorContainer.querySelector(".icon")!;
+    icon.remove();
+
+    const editor = document.createElement("div");
+    editor.className = "editorContainer";
+    DomUtil.setInnerHtml(editor, data.returnValues.template);
+    editorContainer.appendChild(editor);
+
+    // bind buttons
+    const formSubmit = editorContainer.querySelector(".formSubmit") as HTMLElement;
+
+    const buttonSave = formSubmit.querySelector('button[data-type="save"]') as HTMLButtonElement;
+    buttonSave.addEventListener("click", () => this._save());
+
+    const buttonCancel = formSubmit.querySelector('button[data-type="cancel"]') as HTMLButtonElement;
+    buttonCancel.addEventListener("click", () => this._restoreMessage());
+
+    EventHandler.add("com.woltlab.wcf.redactor", `submitEditor_${id}`, (data) => {
+      data.cancel = true;
+
+      this._save();
+    });
+
+    const editorElement = document.getElementById(id) as HTMLElement;
+    if (Environment.editor() === "redactor") {
+      window.setTimeout(() => {
+        UiScroll.element(this._activeElement!);
+      }, 250);
+    } else {
+      editorElement.focus();
+    }
+  }
+
+  /**
+   * Restores the message view.
+   */
+  protected _restoreMessage(): void {
+    this._destroyEditor();
+
+    this._editorContainer!.remove();
+
+    this._activeElement = null;
+  }
+
+  /**
+   * Saves the editor message.
+   */
+  protected _save(): void {
+    const parameters = {
+      data: {
+        message: "",
+      },
+    };
+
+    const id = this._getEditorId();
+
+    EventHandler.fire("com.woltlab.wcf.redactor2", `getText_${id}`, parameters.data);
+
+    if (!this._validate(parameters)) {
+      // validation failed
+      return;
+    }
+
+    EventHandler.fire("com.woltlab.wcf.redactor2", `submit_${id}`, parameters);
+
+    Ajax.api(this, {
+      actionName: "save",
+      objectIDs: [this._getObjectId(this._activeElement!)],
+      parameters: parameters,
+    });
+
+    this._hideEditor();
+  }
+
+  /**
+   * Validates the message and invokes listeners to perform additional validation.
+   */
+  protected _validate(parameters: ArbitraryObject): boolean {
+    // remove all existing error elements
+    this._activeElement!.querySelectorAll(".innerError").forEach((el) => el.remove());
+
+    // check if editor contains actual content
+    const editorElement = document.getElementById(this._getEditorId())!;
+    const redactor: RedactorEditor = window.jQuery(editorElement).data("redactor");
+    if (redactor.utils.isEmpty()) {
+      this.throwError(editorElement, Language.get("wcf.global.form.error.empty"));
+      return false;
+    }
+
+    const data = {
+      api: this,
+      parameters: parameters,
+      valid: true,
+    };
+
+    EventHandler.fire("com.woltlab.wcf.redactor2", "validate_" + this._getEditorId(), data);
+
+    return data.valid;
+  }
+
+  /**
+   * Throws an error by adding an inline error to target element.
+   */
+  throwError(element: HTMLElement, message: string): void {
+    DomUtil.innerError(element, message);
+  }
+
+  /**
+   * Shows the update message.
+   */
+  protected _showMessage(data: AjaxResponse): void {
+    // set new content
+    const container = this._editorContainer!.parentElement!.querySelector(
+      ".commentContent .userMessage",
+    ) as HTMLElement;
+    DomUtil.setInnerHtml(container, data.returnValues.message);
+
+    this._restoreMessage();
+
+    UiNotification.show();
+  }
+
+  /**
+   * Hides the editor from view.
+   */
+  protected _hideEditor(): void {
+    const editorContainer = this._editorContainer!.querySelector(".editorContainer") as HTMLElement;
+    DomUtil.hide(editorContainer);
+
+    const icon = document.createElement("span");
+    icon.className = "icon icon48 fa-spinner";
+    this._editorContainer!.appendChild(icon);
+  }
+
+  /**
+   * Restores the previously hidden editor.
+   */
+  protected _restoreEditor(): void {
+    const icon = this._editorContainer!.querySelector(".fa-spinner")!;
+    icon.remove();
+
+    const editorContainer = this._editorContainer!.querySelector(".editorContainer") as HTMLElement;
+    if (editorContainer !== null) {
+      DomUtil.show(editorContainer);
+    }
+  }
+
+  /**
+   * Destroys the editor instance.
+   */
+  protected _destroyEditor(): void {
+    EventHandler.fire("com.woltlab.wcf.redactor2", `autosaveDestroy_${this._getEditorId()}`);
+    EventHandler.fire("com.woltlab.wcf.redactor2", `destroy_${this._getEditorId()}`);
+  }
+
+  /**
+   * Returns the unique editor id.
+   */
+  protected _getEditorId(): string {
+    return `commentEditor${this._getObjectId(this._activeElement!)}`;
+  }
+
+  /**
+   * Returns the element's `data-object-id` value.
+   */
+  protected _getObjectId(element: HTMLElement): number {
+    return ~~element.dataset.objectId!;
+  }
+
+  _ajaxFailure(data: ResponseData): boolean {
+    const editor = this._editorContainer!.querySelector(".redactor-layer") as HTMLElement;
+
+    // handle errors occurring on editor load
+    if (editor === null) {
+      this._restoreMessage();
+
+      return true;
+    }
+
+    this._restoreEditor();
+
+    if (!data || data.returnValues === undefined || data.returnValues.errorType === undefined) {
+      return true;
+    }
+
+    DomUtil.innerError(editor, data.returnValues.errorType);
+
+    return false;
+  }
+
+  _ajaxSuccess(data: AjaxResponse): void {
+    switch (data.actionName) {
+      case "beginEdit":
+        this._showEditor(data);
+        break;
+
+      case "save":
+        this._showMessage(data);
+        break;
+    }
+  }
+
+  _ajaxSetup(): ReturnType<AjaxCallbackSetup> {
+    const objectTypeId = ~~this._container.dataset.objectTypeId!;
+
+    return {
+      data: {
+        className: "wcf\\data\\comment\\CommentAction",
+        parameters: {
+          data: {
+            objectTypeID: objectTypeId,
+          },
+        },
+      },
+      silent: true,
+    };
+  }
+}
+
+Core.enableLegacyInheritance(UiCommentEdit);
+
+export = UiCommentEdit;
index 69314ca8d09a5a64f0faa7d9665f58a791a7270e..47d7f592cab7c0dd8c5b666fcc94970c7baa94f7 100644 (file)
@@ -20,7 +20,9 @@ let _notificationElement: HTMLElement;
 let _timeout: number;
 
 function init() {
-  if (_didInit) return;
+  if (_didInit) {
+    return;
+  }
   _didInit = true;
 
   _notificationElement = document.createElement("div");
@@ -51,7 +53,7 @@ function hide() {
 /**
  * Displays a notification.
  */
-export function show(message?: string, callback?: Callback, cssClassName?: string): void {
+export function show(message?: string, callback?: Callback | null, cssClassName?: string): void {
   if (_busy) {
     return;
   }