hashCode: () => string;
}
+ interface JQuery {
+ redactor(...args: any[]): JQuery;
+ }
+
type ArbitraryObject = Record<string, unknown>;
}
* modified version. Changes made to this class need to be verified
* against the response implementation.
*
- * @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/Add
+ * @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/Add
*/
-define([
- 'Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Dom/Traverse', 'Ui/Dialog', 'Ui/Notification', 'WoltLabSuite/Core/Ui/Scroll', 'EventKey', 'User', 'WoltLabSuite/Core/Controller/Captcha'
-], function (Ajax, Core, EventHandler, Language, DomChangeListener, DomUtil, DomTraverse, UiDialog, UiNotification, UiScroll, EventKey, User, ControllerCaptcha) {
+define(["require", "exports", "tslib", "../../Ajax", "../../Controller/Captcha", "../../Core", "../../Dom/Change/Listener", "../../Dom/Util", "../../Event/Handler", "../../Language", "../Dialog", "../Scroll", "../../User", "../Notification"], function (require, exports, tslib_1, Ajax, Captcha_1, Core, Listener_1, Util_1, EventHandler, Language, Dialog_1, UiScroll, User_1, UiNotification) {
"use strict";
- if (!COMPILER_TARGET_DEFAULT) {
- var Fake = function () { };
- Fake.prototype = {
- init: function () { },
- _submitGuestDialog: function () { },
- _submit: function () { },
- _getParameters: function () { },
- _validate: function () { },
- throwError: function () { },
- _showLoadingOverlay: function () { },
- _hideLoadingOverlay: function () { },
- _reset: function () { },
- _handleError: function () { },
- _getEditor: function () { },
- _insertMessage: function () { },
- _ajaxSuccess: function () { },
- _ajaxFailure: function () { },
- _ajaxSetup: function () { },
- _cancelGuestDialog: function () { }
- };
- return Fake;
- }
- /**
- * @constructor
- */
- function UiCommentAdd(container) { this.init(container); }
- UiCommentAdd.prototype = {
+ Ajax = tslib_1.__importStar(Ajax);
+ Captcha_1 = tslib_1.__importDefault(Captcha_1);
+ Core = tslib_1.__importStar(Core);
+ Listener_1 = tslib_1.__importDefault(Listener_1);
+ Util_1 = tslib_1.__importDefault(Util_1);
+ EventHandler = tslib_1.__importStar(EventHandler);
+ Language = tslib_1.__importStar(Language);
+ Dialog_1 = tslib_1.__importDefault(Dialog_1);
+ UiScroll = tslib_1.__importStar(UiScroll);
+ User_1 = tslib_1.__importDefault(User_1);
+ UiNotification = tslib_1.__importStar(UiNotification);
+ class UiCommentAdd {
/**
* Initializes a new quick reply field.
- *
- * @param {Element} container container element
*/
- init: function (container) {
- this._container = container;
- this._content = elBySel('.jsOuterEditorContainer', this._container);
- this._textarea = elBySel('.wysiwygTextarea', this._container);
+ constructor(container) {
this._editor = null;
this._loadingOverlay = null;
- this._content.addEventListener('click', (function (event) {
- if (this._content.classList.contains('collapsed')) {
+ this._container = container;
+ this._content = this._container.querySelector(".jsOuterEditorContainer");
+ this._textarea = this._container.querySelector(".wysiwygTextarea");
+ this._content.addEventListener("click", (event) => {
+ if (this._content.classList.contains("collapsed")) {
event.preventDefault();
- this._content.classList.remove('collapsed');
+ this._content.classList.remove("collapsed");
this._focusEditor();
}
- }).bind(this));
+ });
// handle submit button
- var submitButton = elBySel('button[data-type="save"]', this._container);
- submitButton.addEventListener('click', this._submit.bind(this));
- },
+ const submitButton = this._container.querySelector('button[data-type="save"]');
+ submitButton.addEventListener("click", (ev) => this._submit(ev));
+ }
/**
* Scrolls the editor into view and sets the caret to the end of the editor.
- *
- * @protected
*/
- _focusEditor: function () {
- UiScroll.element(this._container, (function () {
- window.jQuery(this._textarea).redactor('WoltLabCaret.endOfEditor');
- }).bind(this));
- },
+ _focusEditor() {
+ UiScroll.element(this._container, () => {
+ window.jQuery(this._textarea).redactor("WoltLabCaret.endOfEditor");
+ });
+ }
/**
* Submits the guest dialog.
- *
- * @param {Event} event
- * @protected
*/
- _submitGuestDialog: function (event) {
+ _submitGuestDialog(event) {
// only submit when enter key is pressed
- if (event.type === 'keypress' && !EventKey.Enter(event)) {
+ if (event instanceof KeyboardEvent && event.key !== "Enter") {
return;
}
- var usernameInput = elBySel('input[name=username]', event.currentTarget.closest('.dialogContent'));
- if (usernameInput.value === '') {
- elInnerError(usernameInput, Language.get('wcf.global.form.error.empty'));
- usernameInput.closest('dl').classList.add('formError');
+ const target = event.currentTarget;
+ const dialogContent = target.closest(".dialogContent");
+ const usernameInput = dialogContent.querySelector("input[name=username]");
+ if (usernameInput.value === "") {
+ Util_1.default.innerError(usernameInput, Language.get("wcf.global.form.error.empty"));
+ usernameInput.closest("dl").classList.add("formError");
return;
}
- var parameters = {
+ let parameters = {
parameters: {
data: {
- username: usernameInput.value
- }
- }
+ username: usernameInput.value,
+ },
+ },
};
- if (ControllerCaptcha.has('commentAdd')) {
- var data = ControllerCaptcha.getData('commentAdd');
+ if (Captcha_1.default.has("commentAdd")) {
+ const data = Captcha_1.default.getData("commentAdd");
if (data instanceof Promise) {
- data.then((function (data) {
+ void data.then((data) => {
parameters = Core.extend(parameters, data);
this._submit(undefined, parameters);
- }).bind(this));
+ });
}
else {
parameters = Core.extend(parameters, data);
else {
this._submit(undefined, parameters);
}
- },
+ }
/**
* Validates the message and submits it to the server.
- *
- * @param {Event?} event event object
- * @param {Object?} additionalParameters additional parameters sent to the server
- * @protected
*/
- _submit: function (event, additionalParameters) {
+ _submit(event, additionalParameters) {
if (event) {
event.preventDefault();
}
}
this._showLoadingOverlay();
// build parameters
- var parameters = this._getParameters();
- EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_text', parameters.data);
- if (!User.userId && !additionalParameters) {
+ const parameters = this._getParameters();
+ EventHandler.fire("com.woltlab.wcf.redactor2", "submit_text", parameters.data);
+ if (!User_1.default.userId && !additionalParameters) {
parameters.requireGuestDialog = true;
}
Ajax.api(this, Core.extend({
- parameters: parameters
+ parameters: parameters,
}, additionalParameters));
- },
+ }
/**
* Returns the request parameters to add a comment.
- *
- * @return {{data: {message: string, objectID: number, objectTypeID: number}}}
- * @protected
*/
- _getParameters: function () {
- var commentList = this._container.closest('.commentList');
+ _getParameters() {
+ const commentList = this._container.closest(".commentList");
return {
data: {
message: this._getEditor().code.get(),
- objectID: ~~elData(commentList, 'object-id'),
- objectTypeID: ~~elData(commentList, 'object-type-id')
- }
+ objectID: ~~commentList.dataset.objectId,
+ objectTypeID: ~~commentList.dataset.objectTypeId,
+ },
};
- },
+ }
/**
* Validates the message and invokes listeners to perform additional validation.
- *
- * @return {boolean} validation result
- * @protected
*/
- _validate: function () {
+ _validate() {
// remove all existing error elements
- elBySelAll('.innerError', this._container, elRemove);
+ this._container.querySelectorAll(".innerError").forEach((el) => el.remove());
// check if editor contains actual content
if (this._getEditor().utils.isEmpty()) {
- this.throwError(this._textarea, Language.get('wcf.global.form.error.empty'));
+ this.throwError(this._textarea, Language.get("wcf.global.form.error.empty"));
return false;
}
- var data = {
+ const data = {
api: this,
editor: this._getEditor(),
message: this._getEditor().code.get(),
- valid: true
+ valid: true,
};
- EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_text', data);
- return (data.valid !== false);
- },
+ EventHandler.fire("com.woltlab.wcf.redactor2", "validate_text", 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 === 'empty' ? Language.get('wcf.global.form.error.empty') : message));
- },
+ throwError(element, message) {
+ Util_1.default.innerError(element, message === "empty" ? Language.get("wcf.global.form.error.empty") : message);
+ }
/**
* Displays a loading spinner while the request is processed by the server.
- *
- * @protected
*/
- _showLoadingOverlay: function () {
+ _showLoadingOverlay() {
if (this._loadingOverlay === null) {
- this._loadingOverlay = elCreate('div');
- this._loadingOverlay.className = 'commentLoadingOverlay';
+ this._loadingOverlay = document.createElement("div");
+ this._loadingOverlay.className = "commentLoadingOverlay";
this._loadingOverlay.innerHTML = '<span class="icon icon96 fa-spinner"></span>';
}
- this._content.classList.add('loading');
+ this._content.classList.add("loading");
this._content.appendChild(this._loadingOverlay);
- },
+ }
/**
* Hides the loading spinner.
- *
- * @protected
*/
- _hideLoadingOverlay: function () {
- this._content.classList.remove('loading');
- var loadingOverlay = elBySel('.commentLoadingOverlay', this._content);
+ _hideLoadingOverlay() {
+ this._content.classList.remove("loading");
+ const loadingOverlay = this._content.querySelector(".commentLoadingOverlay");
if (loadingOverlay !== null) {
- loadingOverlay.parentNode.removeChild(loadingOverlay);
+ loadingOverlay.remove();
}
- },
+ }
/**
* Resets the editor contents and notifies event listeners.
- *
- * @protected
*/
- _reset: function () {
- this._getEditor().code.set('<p>\u200b</p>');
- EventHandler.fire('com.woltlab.wcf.redactor2', 'reset_text');
- if (document.activeElement) {
+ _reset() {
+ this._getEditor().code.set("<p>\u200b</p>");
+ EventHandler.fire("com.woltlab.wcf.redactor2", "reset_text");
+ if (document.activeElement instanceof HTMLElement) {
document.activeElement.blur();
}
- this._content.classList.add('collapsed');
- },
+ this._content.classList.add("collapsed");
+ }
/**
* Handles errors occurred during server processing.
- *
- * @param {Object} data response data
- * @protected
*/
- _handleError: function (data) {
- //noinspection JSUnresolvedVariable
+ _handleError(data) {
this.throwError(this._textarea, data.returnValues.errorType);
- },
+ }
/**
* Returns the current editor instance.
- *
- * @return {Object} editor instance
- * @protected
*/
- _getEditor: function () {
+ _getEditor() {
if (this._editor === null) {
- if (typeof window.jQuery === 'function') {
- this._editor = window.jQuery(this._textarea).data('redactor');
+ if (typeof window.jQuery === "function") {
+ this._editor = window.jQuery(this._textarea).data("redactor");
}
else {
throw new Error("Unable to access editor, jQuery has not been loaded yet.");
}
}
return this._editor;
- },
+ }
/**
* Inserts the rendered message.
- *
- * @param {Object} data response data
- * @return {Element} scroll target
- * @protected
*/
- _insertMessage: function (data) {
+ _insertMessage(data) {
// insert HTML
- //noinspection JSCheckFunctionSignatures
- DomUtil.insertHtml(data.returnValues.template, this._container, 'after');
- UiNotification.show(Language.get('wcf.global.success.add'));
- DomChangeListener.trigger();
+ Util_1.default.insertHtml(data.returnValues.template, this._container, "after");
+ UiNotification.show(Language.get("wcf.global.success.add"));
+ Listener_1.default.trigger();
return this._container.nextElementSibling;
- },
- /**
- * @param {{returnValues:{guestDialog:string}}} data
- * @protected
- */
- _ajaxSuccess: function (data) {
- if (!User.userId && data.returnValues.guestDialog) {
- UiDialog.openStatic('jsDialogGuestComment', data.returnValues.guestDialog, {
+ }
+ _ajaxSuccess(data) {
+ if (!User_1.default.userId && data.returnValues.guestDialog) {
+ Dialog_1.default.openStatic("jsDialogGuestComment", data.returnValues.guestDialog, {
closable: false,
- onClose: function () {
- if (ControllerCaptcha.has('commentAdd')) {
- ControllerCaptcha.delete('commentAdd');
+ onClose: () => {
+ if (Captcha_1.default.has("commentAdd")) {
+ Captcha_1.default.delete("commentAdd");
}
},
- title: Language.get('wcf.global.confirmation.title')
+ title: Language.get("wcf.global.confirmation.title"),
});
- var dialog = UiDialog.getDialog('jsDialogGuestComment');
- elBySel('input[type=submit]', dialog.content).addEventListener('click', this._submitGuestDialog.bind(this));
- elBySel('button[data-type="cancel"]', dialog.content).addEventListener('click', this._cancelGuestDialog.bind(this));
- elBySel('input[type=text]', dialog.content).addEventListener('keypress', this._submitGuestDialog.bind(this));
+ const dialog = Dialog_1.default.getDialog("jsDialogGuestComment");
+ const submitButton = dialog.content.querySelector("input[type=submit]");
+ submitButton.addEventListener("click", (ev) => this._submitGuestDialog(ev));
+ const cancelButton = dialog.content.querySelector('button[data-type="cancel"]');
+ cancelButton.addEventListener("click", () => this._cancelGuestDialog());
+ const input = dialog.content.querySelector("input[type=text]");
+ input.addEventListener("keypress", (ev) => this._submitGuestDialog(ev));
}
else {
- var scrollTarget = this._insertMessage(data);
- if (!User.userId) {
- UiDialog.close('jsDialogGuestComment');
+ const scrollTarget = this._insertMessage(data);
+ if (!User_1.default.userId) {
+ Dialog_1.default.close("jsDialogGuestComment");
}
this._reset();
this._hideLoadingOverlay();
- window.setTimeout((function () {
+ window.setTimeout(() => {
UiScroll.element(scrollTarget);
- }).bind(this), 100);
+ }, 100);
}
- },
- _ajaxFailure: function (data) {
+ }
+ _ajaxFailure(data) {
this._hideLoadingOverlay();
- //noinspection JSUnresolvedVariable
if (data === null || data.returnValues === undefined || data.returnValues.errorType === undefined) {
return true;
}
this._handleError(data);
return false;
- },
- _ajaxSetup: function () {
+ }
+ _ajaxSetup() {
return {
data: {
- actionName: 'addComment',
- className: 'wcf\\data\\comment\\CommentAction'
+ actionName: "addComment",
+ className: "wcf\\data\\comment\\CommentAction",
},
- silent: true
+ silent: true,
};
- },
+ }
/**
* Cancels the guest dialog and restores the comment editor.
*/
- _cancelGuestDialog: function () {
- UiDialog.close('jsDialogGuestComment');
+ _cancelGuestDialog() {
+ Dialog_1.default.close("jsDialogGuestComment");
this._hideLoadingOverlay();
}
- };
+ }
+ Core.enableLegacyInheritance(UiCommentAdd);
return UiCommentAdd;
});
+++ /dev/null
-/**
- * Handles the comment add feature.
- *
- * Warning: This implementation is also used for responses, but in a slightly
- * modified version. Changes made to this class need to be verified
- * against the response implementation.
- *
- * @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/Add
- */
-define([
- 'Ajax', 'Core', 'EventHandler', 'Language', 'Dom/ChangeListener', 'Dom/Util', 'Dom/Traverse', 'Ui/Dialog', 'Ui/Notification', 'WoltLabSuite/Core/Ui/Scroll', 'EventKey', 'User', 'WoltLabSuite/Core/Controller/Captcha'
-],
-function(
- Ajax, Core, EventHandler, Language, DomChangeListener, DomUtil, DomTraverse, UiDialog, UiNotification, UiScroll, EventKey, User, ControllerCaptcha
-) {
- "use strict";
-
- if (!COMPILER_TARGET_DEFAULT) {
- var Fake = function() {};
- Fake.prototype = {
- init: function() {},
- _submitGuestDialog: function() {},
- _submit: function() {},
- _getParameters: function () {},
- _validate: function() {},
- throwError: function() {},
- _showLoadingOverlay: function() {},
- _hideLoadingOverlay: function() {},
- _reset: function() {},
- _handleError: function() {},
- _getEditor: function() {},
- _insertMessage: function() {},
- _ajaxSuccess: function() {},
- _ajaxFailure: function() {},
- _ajaxSetup: function() {},
- _cancelGuestDialog: function() {}
- };
- return Fake;
- }
-
- /**
- * @constructor
- */
- function UiCommentAdd(container) { this.init(container); }
- UiCommentAdd.prototype = {
- /**
- * Initializes a new quick reply field.
- *
- * @param {Element} container container element
- */
- init: function(container) {
- this._container = container;
- this._content = elBySel('.jsOuterEditorContainer', this._container);
- this._textarea = elBySel('.wysiwygTextarea', this._container);
- this._editor = null;
- this._loadingOverlay = null;
-
- this._content.addEventListener('click', (function (event) {
- if (this._content.classList.contains('collapsed')) {
- event.preventDefault();
-
- this._content.classList.remove('collapsed');
-
- this._focusEditor();
- }
- }).bind(this));
-
- // handle submit button
- var submitButton = elBySel('button[data-type="save"]', this._container);
- submitButton.addEventListener('click', this._submit.bind(this));
- },
-
- /**
- * Scrolls the editor into view and sets the caret to the end of the editor.
- *
- * @protected
- */
- _focusEditor: function () {
- UiScroll.element(this._container, (function () {
- window.jQuery(this._textarea).redactor('WoltLabCaret.endOfEditor');
- }).bind(this));
- },
-
- /**
- * Submits the guest dialog.
- *
- * @param {Event} event
- * @protected
- */
- _submitGuestDialog: function(event) {
- // only submit when enter key is pressed
- if (event.type === 'keypress' && !EventKey.Enter(event)) {
- return;
- }
-
- var usernameInput = elBySel('input[name=username]', event.currentTarget.closest('.dialogContent'));
- if (usernameInput.value === '') {
- elInnerError(usernameInput, Language.get('wcf.global.form.error.empty'));
- usernameInput.closest('dl').classList.add('formError');
-
- return;
- }
-
- var parameters = {
- parameters: {
- data: {
- username: usernameInput.value
- }
- }
- };
-
- if (ControllerCaptcha.has('commentAdd')) {
- var data = ControllerCaptcha.getData('commentAdd');
- if (data instanceof Promise) {
- data.then((function (data) {
- parameters = Core.extend(parameters, data);
- this._submit(undefined, parameters);
- }).bind(this));
- }
- else {
- parameters = Core.extend(parameters, data);
- this._submit(undefined, parameters);
- }
- }
- else {
- this._submit(undefined, parameters);
- }
- },
-
- /**
- * Validates the message and submits it to the server.
- *
- * @param {Event?} event event object
- * @param {Object?} additionalParameters additional parameters sent to the server
- * @protected
- */
- _submit: function(event, additionalParameters) {
- if (event) {
- event.preventDefault();
- }
-
- if (!this._validate()) {
- // validation failed, bail out
- return;
- }
-
- this._showLoadingOverlay();
-
- // build parameters
- var parameters = this._getParameters();
-
- EventHandler.fire('com.woltlab.wcf.redactor2', 'submit_text', parameters.data);
-
- if (!User.userId && !additionalParameters) {
- parameters.requireGuestDialog = true;
- }
-
- Ajax.api(this, Core.extend({
- parameters: parameters
- }, additionalParameters));
- },
-
- /**
- * Returns the request parameters to add a comment.
- *
- * @return {{data: {message: string, objectID: number, objectTypeID: number}}}
- * @protected
- */
- _getParameters: function () {
- var commentList = this._container.closest('.commentList');
-
- return {
- data: {
- message: this._getEditor().code.get(),
- objectID: ~~elData(commentList, 'object-id'),
- objectTypeID: ~~elData(commentList, 'object-type-id')
- }
- };
- },
-
- /**
- * Validates the message and invokes listeners to perform additional validation.
- *
- * @return {boolean} validation result
- * @protected
- */
- _validate: function() {
- // remove all existing error elements
- elBySelAll('.innerError', this._container, elRemove);
-
- // check if editor contains actual content
- if (this._getEditor().utils.isEmpty()) {
- this.throwError(this._textarea, Language.get('wcf.global.form.error.empty'));
- return false;
- }
-
- var data = {
- api: this,
- editor: this._getEditor(),
- message: this._getEditor().code.get(),
- valid: true
- };
-
- EventHandler.fire('com.woltlab.wcf.redactor2', 'validate_text', 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 === 'empty' ? Language.get('wcf.global.form.error.empty') : message));
- },
-
- /**
- * Displays a loading spinner while the request is processed by the server.
- *
- * @protected
- */
- _showLoadingOverlay: function() {
- if (this._loadingOverlay === null) {
- this._loadingOverlay = elCreate('div');
- this._loadingOverlay.className = 'commentLoadingOverlay';
- this._loadingOverlay.innerHTML = '<span class="icon icon96 fa-spinner"></span>';
- }
-
- this._content.classList.add('loading');
- this._content.appendChild(this._loadingOverlay);
- },
-
- /**
- * Hides the loading spinner.
- *
- * @protected
- */
- _hideLoadingOverlay: function() {
- this._content.classList.remove('loading');
-
- var loadingOverlay = elBySel('.commentLoadingOverlay', this._content);
- if (loadingOverlay !== null) {
- loadingOverlay.parentNode.removeChild(loadingOverlay);
- }
- },
-
- /**
- * Resets the editor contents and notifies event listeners.
- *
- * @protected
- */
- _reset: function() {
- this._getEditor().code.set('<p>\u200b</p>');
-
- EventHandler.fire('com.woltlab.wcf.redactor2', 'reset_text');
-
- if (document.activeElement) {
- document.activeElement.blur();
- }
-
- this._content.classList.add('collapsed');
- },
-
- /**
- * Handles errors occurred during server processing.
- *
- * @param {Object} data response data
- * @protected
- */
- _handleError: function(data) {
- //noinspection JSUnresolvedVariable
- this.throwError(this._textarea, data.returnValues.errorType);
- },
-
- /**
- * Returns the current editor instance.
- *
- * @return {Object} editor instance
- * @protected
- */
- _getEditor: function() {
- if (this._editor === null) {
- if (typeof window.jQuery === 'function') {
- this._editor = window.jQuery(this._textarea).data('redactor');
- }
- else {
- throw new Error("Unable to access editor, jQuery has not been loaded yet.");
- }
- }
-
- return this._editor;
- },
-
- /**
- * Inserts the rendered message.
- *
- * @param {Object} data response data
- * @return {Element} scroll target
- * @protected
- */
- _insertMessage: function(data) {
- // insert HTML
- //noinspection JSCheckFunctionSignatures
- DomUtil.insertHtml(data.returnValues.template, this._container, 'after');
-
- UiNotification.show(Language.get('wcf.global.success.add'));
-
- DomChangeListener.trigger();
-
- return this._container.nextElementSibling;
- },
-
- /**
- * @param {{returnValues:{guestDialog:string}}} data
- * @protected
- */
- _ajaxSuccess: function(data) {
- if (!User.userId && data.returnValues.guestDialog) {
- UiDialog.openStatic('jsDialogGuestComment', data.returnValues.guestDialog, {
- closable: false,
- onClose: function() {
- if (ControllerCaptcha.has('commentAdd')) {
- ControllerCaptcha.delete('commentAdd');
- }
- },
- title: Language.get('wcf.global.confirmation.title')
- });
-
- var dialog = UiDialog.getDialog('jsDialogGuestComment');
- elBySel('input[type=submit]', dialog.content).addEventListener('click', this._submitGuestDialog.bind(this));
- elBySel('button[data-type="cancel"]', dialog.content).addEventListener('click', this._cancelGuestDialog.bind(this));
- elBySel('input[type=text]', dialog.content).addEventListener('keypress', this._submitGuestDialog.bind(this));
- }
- else {
- var scrollTarget = this._insertMessage(data);
-
- if (!User.userId) {
- UiDialog.close('jsDialogGuestComment');
- }
-
- this._reset();
-
- this._hideLoadingOverlay();
-
- window.setTimeout((function () {
- UiScroll.element(scrollTarget);
- }).bind(this), 100);
- }
- },
-
- _ajaxFailure: function(data) {
- this._hideLoadingOverlay();
-
- //noinspection JSUnresolvedVariable
- if (data === null || data.returnValues === undefined || data.returnValues.errorType === undefined) {
- return true;
- }
-
- this._handleError(data);
-
- return false;
- },
-
- _ajaxSetup: function() {
- return {
- data: {
- actionName: 'addComment',
- className: 'wcf\\data\\comment\\CommentAction'
- },
- silent: true
- };
- },
-
- /**
- * Cancels the guest dialog and restores the comment editor.
- */
- _cancelGuestDialog: function() {
- UiDialog.close('jsDialogGuestComment');
-
- this._hideLoadingOverlay();
- }
- };
-
- return UiCommentAdd;
-});
--- /dev/null
+/**
+ * Handles the comment add feature.
+ *
+ * Warning: This implementation is also used for responses, but in a slightly
+ * modified version. Changes made to this class need to be verified
+ * against the response implementation.
+ *
+ * @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/Add
+ */
+
+import * as Ajax from "../../Ajax";
+import { AjaxCallbackSetup, ResponseData } from "../../Ajax/Data";
+import ControllerCaptcha from "../../Controller/Captcha";
+import * as Core from "../../Core";
+import DomChangeListener from "../../Dom/Change/Listener";
+import DomUtil from "../../Dom/Util";
+import * as EventHandler from "../../Event/Handler";
+import * as Language from "../../Language";
+import UiDialog from "../Dialog";
+import { RedactorEditor } from "../Redactor/Editor";
+import * as UiScroll from "../Scroll";
+import User from "../../User";
+import * as UiNotification from "../Notification";
+
+interface AjaxResponse {
+ returnValues: {
+ guestDialog?: string;
+ template: string;
+ };
+}
+
+class UiCommentAdd {
+ protected readonly _container: HTMLElement;
+ protected readonly _content: HTMLElement;
+ protected readonly _textarea: HTMLTextAreaElement;
+ protected _editor: RedactorEditor | null = null;
+ protected _loadingOverlay: HTMLElement | null = null;
+
+ /**
+ * Initializes a new quick reply field.
+ */
+ constructor(container: HTMLElement) {
+ this._container = container;
+ this._content = this._container.querySelector(".jsOuterEditorContainer") as HTMLElement;
+ this._textarea = this._container.querySelector(".wysiwygTextarea") as HTMLTextAreaElement;
+
+ this._content.addEventListener("click", (event) => {
+ if (this._content.classList.contains("collapsed")) {
+ event.preventDefault();
+
+ this._content.classList.remove("collapsed");
+
+ this._focusEditor();
+ }
+ });
+
+ // handle submit button
+ const submitButton = this._container.querySelector('button[data-type="save"]') as HTMLButtonElement;
+ submitButton.addEventListener("click", (ev) => this._submit(ev));
+ }
+
+ /**
+ * Scrolls the editor into view and sets the caret to the end of the editor.
+ */
+ protected _focusEditor(): void {
+ UiScroll.element(this._container, () => {
+ window.jQuery(this._textarea).redactor("WoltLabCaret.endOfEditor");
+ });
+ }
+
+ /**
+ * Submits the guest dialog.
+ */
+ protected _submitGuestDialog(event: MouseEvent | KeyboardEvent): void {
+ // only submit when enter key is pressed
+ if (event instanceof KeyboardEvent && event.key !== "Enter") {
+ return;
+ }
+
+ const target = event.currentTarget as HTMLInputElement;
+ const dialogContent = target.closest(".dialogContent") as HTMLElement;
+ const usernameInput = dialogContent.querySelector("input[name=username]") as HTMLInputElement;
+ if (usernameInput.value === "") {
+ DomUtil.innerError(usernameInput, Language.get("wcf.global.form.error.empty"));
+ usernameInput.closest("dl")!.classList.add("formError");
+
+ return;
+ }
+
+ let parameters: ArbitraryObject = {
+ parameters: {
+ data: {
+ username: usernameInput.value,
+ },
+ },
+ };
+
+ if (ControllerCaptcha.has("commentAdd")) {
+ const data = ControllerCaptcha.getData("commentAdd");
+ if (data instanceof Promise) {
+ void data.then((data) => {
+ parameters = Core.extend(parameters, data) as ArbitraryObject;
+ this._submit(undefined, parameters);
+ });
+ } else {
+ parameters = Core.extend(parameters, data as ArbitraryObject) as ArbitraryObject;
+ this._submit(undefined, parameters);
+ }
+ } else {
+ this._submit(undefined, parameters);
+ }
+ }
+
+ /**
+ * Validates the message and submits it to the server.
+ */
+ protected _submit(event: MouseEvent | undefined, additionalParameters?: ArbitraryObject): void {
+ if (event) {
+ event.preventDefault();
+ }
+
+ if (!this._validate()) {
+ // validation failed, bail out
+ return;
+ }
+
+ this._showLoadingOverlay();
+
+ // build parameters
+ const parameters = this._getParameters();
+
+ EventHandler.fire("com.woltlab.wcf.redactor2", "submit_text", parameters.data as any);
+
+ if (!User.userId && !additionalParameters) {
+ parameters.requireGuestDialog = true;
+ }
+
+ Ajax.api(
+ this,
+ Core.extend(
+ {
+ parameters: parameters,
+ },
+ additionalParameters as ArbitraryObject,
+ ),
+ );
+ }
+
+ /**
+ * Returns the request parameters to add a comment.
+ */
+ protected _getParameters(): ArbitraryObject {
+ const commentList = this._container.closest(".commentList") as HTMLElement;
+
+ return {
+ data: {
+ message: this._getEditor().code.get(),
+ objectID: ~~commentList.dataset.objectId!,
+ objectTypeID: ~~commentList.dataset.objectTypeId!,
+ },
+ };
+ }
+
+ /**
+ * Validates the message and invokes listeners to perform additional validation.
+ */
+ protected _validate(): boolean {
+ // remove all existing error elements
+ this._container.querySelectorAll(".innerError").forEach((el) => el.remove());
+
+ // check if editor contains actual content
+ if (this._getEditor().utils.isEmpty()) {
+ this.throwError(this._textarea, Language.get("wcf.global.form.error.empty"));
+ return false;
+ }
+
+ const data = {
+ api: this,
+ editor: this._getEditor(),
+ message: this._getEditor().code.get(),
+ valid: true,
+ };
+
+ EventHandler.fire("com.woltlab.wcf.redactor2", "validate_text", 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 === "empty" ? Language.get("wcf.global.form.error.empty") : message);
+ }
+
+ /**
+ * Displays a loading spinner while the request is processed by the server.
+ */
+ protected _showLoadingOverlay(): void {
+ if (this._loadingOverlay === null) {
+ this._loadingOverlay = document.createElement("div");
+ this._loadingOverlay.className = "commentLoadingOverlay";
+ this._loadingOverlay.innerHTML = '<span class="icon icon96 fa-spinner"></span>';
+ }
+
+ this._content.classList.add("loading");
+ this._content.appendChild(this._loadingOverlay);
+ }
+
+ /**
+ * Hides the loading spinner.
+ */
+ protected _hideLoadingOverlay(): void {
+ this._content.classList.remove("loading");
+
+ const loadingOverlay = this._content.querySelector(".commentLoadingOverlay");
+ if (loadingOverlay !== null) {
+ loadingOverlay.remove();
+ }
+ }
+
+ /**
+ * Resets the editor contents and notifies event listeners.
+ */
+ protected _reset(): void {
+ this._getEditor().code.set("<p>\u200b</p>");
+
+ EventHandler.fire("com.woltlab.wcf.redactor2", "reset_text");
+
+ if (document.activeElement instanceof HTMLElement) {
+ document.activeElement.blur();
+ }
+
+ this._content.classList.add("collapsed");
+ }
+
+ /**
+ * Handles errors occurred during server processing.
+ */
+ protected _handleError(data: ResponseData): void {
+ this.throwError(this._textarea, data.returnValues.errorType);
+ }
+
+ /**
+ * Returns the current editor instance.
+ */
+ protected _getEditor(): RedactorEditor {
+ if (this._editor === null) {
+ if (typeof window.jQuery === "function") {
+ this._editor = window.jQuery(this._textarea).data("redactor") as RedactorEditor;
+ } else {
+ throw new Error("Unable to access editor, jQuery has not been loaded yet.");
+ }
+ }
+
+ return this._editor;
+ }
+
+ /**
+ * Inserts the rendered message.
+ */
+ protected _insertMessage(data: AjaxResponse): HTMLElement {
+ // insert HTML
+ DomUtil.insertHtml(data.returnValues.template, this._container, "after");
+
+ UiNotification.show(Language.get("wcf.global.success.add"));
+
+ DomChangeListener.trigger();
+
+ return this._container.nextElementSibling as HTMLElement;
+ }
+
+ _ajaxSuccess(data: AjaxResponse): void {
+ if (!User.userId && data.returnValues.guestDialog) {
+ UiDialog.openStatic("jsDialogGuestComment", data.returnValues.guestDialog, {
+ closable: false,
+ onClose: () => {
+ if (ControllerCaptcha.has("commentAdd")) {
+ ControllerCaptcha.delete("commentAdd");
+ }
+ },
+ title: Language.get("wcf.global.confirmation.title"),
+ });
+
+ const dialog = UiDialog.getDialog("jsDialogGuestComment")!;
+
+ const submitButton = dialog.content.querySelector("input[type=submit]") as HTMLButtonElement;
+ submitButton.addEventListener("click", (ev) => this._submitGuestDialog(ev));
+ const cancelButton = dialog.content.querySelector('button[data-type="cancel"]') as HTMLButtonElement;
+ cancelButton.addEventListener("click", () => this._cancelGuestDialog());
+
+ const input = dialog.content.querySelector("input[type=text]") as HTMLInputElement;
+ input.addEventListener("keypress", (ev) => this._submitGuestDialog(ev));
+ } else {
+ const scrollTarget = this._insertMessage(data);
+
+ if (!User.userId) {
+ UiDialog.close("jsDialogGuestComment");
+ }
+
+ this._reset();
+
+ this._hideLoadingOverlay();
+
+ window.setTimeout(() => {
+ UiScroll.element(scrollTarget);
+ }, 100);
+ }
+ }
+
+ _ajaxFailure(data: ResponseData): boolean {
+ this._hideLoadingOverlay();
+
+ if (data === null || data.returnValues === undefined || data.returnValues.errorType === undefined) {
+ return true;
+ }
+
+ this._handleError(data);
+
+ return false;
+ }
+
+ _ajaxSetup(): ReturnType<AjaxCallbackSetup> {
+ return {
+ data: {
+ actionName: "addComment",
+ className: "wcf\\data\\comment\\CommentAction",
+ },
+ silent: true,
+ };
+ }
+
+ /**
+ * Cancels the guest dialog and restores the comment editor.
+ */
+ protected _cancelGuestDialog(): void {
+ UiDialog.close("jsDialogGuestComment");
+
+ this._hideLoadingOverlay();
+ }
+}
+
+Core.enableLegacyInheritance(UiCommentAdd);
+
+export = UiCommentAdd;