From: Matthias Schmidt Date: Tue, 19 Mar 2019 17:22:09 +0000 (+0100) Subject: Merge branch '3.0' X-Git-Tag: 3.1.9~22 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=6e2b9a1c2f73fbb497f0f26bf9361429f65fff53;p=GitHub%2FWoltLab%2FWCF.git Merge branch '3.0' --- 6e2b9a1c2f73fbb497f0f26bf9361429f65fff53 diff --cc wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Comment/Add.js index e4c07e29e5,0000000000..8e2db638d8 mode 100644,000000..100644 --- a/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Comment/Add.js +++ b/wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Comment/Add.js @@@ -1,385 -1,0 +1,390 @@@ +/** + * 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-2018 WoltLab GmbH + * @license GNU Lesser General Public License + * @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(WCF_CLICK_EVENT, (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(WCF_CLICK_EVENT, 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 = ''; + } + + 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('

\u200b

'); + + 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(WCF_CLICK_EVENT, this._submitGuestDialog.bind(this)); + elBySel('button[data-type="cancel"]', dialog.content).addEventListener(WCF_CLICK_EVENT, 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; +});