Matthias Mullie has uploaded a new change for review. https://gerrit.wikimedia.org/r/108634
Change subject: Move flow( 'loadReplyForm' ) to base/action.js ...................................................................... Move flow( 'loadReplyForm' ) to base/action.js Change-Id: Iee67765d60851deb62e4ce6b87b7869eedae224b --- M modules/base/action.js M modules/base/ui-functions.js M modules/discussion/post.js M modules/discussion/styles/nojs.less M modules/discussion/styles/post.less M modules/discussion/styles/topic.less M modules/discussion/topic.js M templates/post.html.php M templates/topic.html.php 9 files changed, 203 insertions(+), 192 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/Flow refs/changes/34/108634/1 diff --git a/modules/base/action.js b/modules/base/action.js index e1cd551..57bd5fa 100644 --- a/modules/base/action.js +++ b/modules/base/action.js @@ -23,26 +23,43 @@ }; /** + * HTML of the cancel link (well, jQuery node with the html of the link) + * + * @returns {jQuery} + */ + mw.flow.action.prototype.cancelLink = function () { + return $( + '<a href="#" class="flow-cancel-link mw-ui-button mw-ui-quiet">' + + mw.msg( 'flow-cancel' ) + + '</a>' + + // add some whitespace to separate from what's next ;) + ' ' + ); + }; + + /** * HTML of the edit form (well, jQuery node with the html of the edit form) * * @returns {jQuery} */ mw.flow.action.prototype.editForm = function () { - return $( + var $form = $( '<form class="flow-edit-form">' + '<textarea class="mw-ui-input flow-edit-content"></textarea>' + '<div class="flow-form-controls flow-edit-controls">' + - '<a href="#" class="flow-cancel-link mw-ui-button mw-ui-quiet">' + - mw.msg( 'flow-cancel' ) + - '</a>' + + // cancel link will be added here '<input type="submit" class="mw-ui-button mw-ui-constructive flow-edit-submit" value="' + mw.msg( 'flow-edit-' + this.object.type + '-submit' ) + '">' + '</div>' + '</form>' ); + + this.cancelLink().prependTo( $form.find( '.flow-form-controls' ) ); + + return $form; }; /** - * Builds the edit form, using flow( setupEditForm ). + * Creates an edit form. * * @param {object} data this.prepareResult return value * @param {function} [loadFunction] callback to be executed when form is loaded @@ -53,32 +70,34 @@ } // build form DOM & attach to content - var $postForm = this.editForm(); - $postForm.appendTo( this.object.$container ); + var $form = this.editForm(); + $form.appendTo( this.object.$container ); // add class to identify this form as being active this.object.$container.addClass( 'flow-edit-form-active' ); // bind click on cancel, which should destroy this form - $postForm.find( '.flow-cancel-link' ).click( function ( event ) { + $form.find( '.flow-cancel-link' ).click( function ( event ) { event.preventDefault(); - $postForm.slideUp( 'fast', this.destroyEditForm.bind( this ) ); + $form.slideUp( 'fast', this.destroyEditForm.bind( this ) ); }.bind( this ) ); // setup preview - $postForm.flow( 'setupPreview' ); + $form.flow( 'setupPreview' ); // setup submission callback - $postForm.flow( 'setupFormHandler', + $form.flow( 'setupFormHandler', '.flow-edit-submit', this.submitFunction.bind( this, data ), - this.loadParametersCallback.bind( this ), + this.loadParametersCallback.bind( this, $form ), this.validateCallback.bind( this ), - this.promiseCallback.bind( this ) + function ( deferred ) { + deferred.done( this.destroyEditForm.bind( this ) ); + }.bind( this ) ); // setup disabler (disables submit button until content is entered) - $postForm.flow( 'setupEmptyDisabler', + $form.flow( 'setupEmptyDisabler', ['.flow-edit-content'], '.flow-edit-submit' ); @@ -87,21 +106,21 @@ * Setting focus inside an event that grants focus (like * clicking the edit icon), is tricky. This is a workaround. */ - setTimeout( function( $postForm, data, loadFunction ) { - var $textarea = $postForm.find( 'textarea' ), + setTimeout( function( $form, data, loadFunction ) { + var $textarea = $form.find( 'textarea' ), initialContent = this.initialContent( data ); mw.flow.editor.load( $textarea, initialContent.content, initialContent.format ) .done( loadFunction ); // scroll to the form - $postForm.scrollIntoView( { + $form.scrollIntoView( { complete: function () { mw.flow.editor.focus( $textarea ); mw.flow.editor.moveCursorToEnd( $textarea ); } } ); - }.bind( this, $postForm, data, loadFunction ), 0 ); + }.bind( this, $form, data, loadFunction ), 0 ); }; /** @@ -115,13 +134,143 @@ }; /** + * Creates a reply form. + * + * Unlike edit forms, reply forms already exit in HTML, we just have + * to "activate" some JS magic. + * + * @param {function} [loadFunction] callback to be executed when form is loaded + */ + mw.flow.action.prototype.loadReplyForm = function ( loadFunction ) { + if ( this.object.$container.hasClass( 'flow-reply-form-active' ) ) { + return; + } + + var $form = this.$form.find( 'form' ); + + // add class to identify this form as being active + this.object.$container.addClass( 'flow-reply-form-active' ); + this.$form.addClass( 'flow-reply-form-active' ); + + // hide topic reply form + // can't do this in CSS, since selectors traveling upwards don't really exist ;) + this.object.$container.closest( '.flow-topic-container' ).find( '.flow-topic-reply-container' ).hide(); + // make sure to show this form, though + this.$form.show(); + + // add cancel link + this.cancelLink() + .click( function ( event ) { + event.preventDefault(); + $form.slideUp( 'fast', this.destroyReplyForm.bind( this ) ); + }.bind( this ) ) + .prependTo( $form.find( '.flow-form-controls' ) ); + + // setup preview + $form.flow( 'setupPreview' ); + + // setup submission callback + $form.flow( 'setupFormHandler', + '.flow-reply-submit', + this.submitFunction.bind( this ), + this.loadParametersCallback.bind( this, $form ), + this.validateCallback.bind( this ), + function ( deferred ) { + deferred.done( this.destroyReplyForm.bind( this ) ); + }.bind( this ) + ); + + // setup disabler (disables submit button until content is entered) + $form.flow( 'setupEmptyDisabler', + ['.flow-reply-content'], + '.flow-reply-submit' + ); + + /* + * Setting focus inside an event that grants focus (like + * clicking the reply button), is tricky. This is a workaround. + */ + setTimeout( function( $form, loadFunction ) { + var $textarea = $form.find( 'textarea' ), + initialContent = this.initialContent(); + + $textarea + .removeClass( 'flow-reply-box-closed' ) + // Override textarea height; doesn't need to be too large initially, + // it'll auto-expand + .attr( 'rows', '6' ) + // Textarea will auto-expand + textarea padding will cause the + // resize grabber to be positioned badly (in FF) so get rid of it + .css( 'resize', 'none' ) + .focus(); + + mw.flow.editor.load( $textarea, initialContent.content, initialContent.format ) + .done( loadFunction ); + + // scroll to the form + $form.scrollIntoView( { + complete: function () { + mw.flow.editor.focus( $textarea ); + mw.flow.editor.moveCursorToEnd( $textarea ); + } + } ); + }.bind( this, $form, loadFunction ), 0 ); + }; + + /** + * Hides the reply form & restores content. + */ + mw.flow.action.prototype.destroyReplyForm = function () { + var $form = this.$form.find( 'form' ), + $textarea = $form.find( 'textarea' ); + + this.object.$container.removeClass( 'flow-reply-form-active' ); + this.$form.removeClass( 'flow-reply-form-active' ); + + mw.flow.editor.destroy( $textarea ); + $form.flow( 'hidePreview' ); + + // when closed, display topic reply form again + // can't do this in CSS, since selectors traveling upwards don't really exist ;) + this.object.$container.closest( '.flow-topic-container' ).find( '.flow-topic-reply-container' ).show(); + + /* + * Because we're not entirely killing the forms, we have to clean up + * after cancelling a form (e.g. reply-forms may be re-used across posts + * - when max threading depth has been reached) + * + * After submitting the reply, kill the events that were bound to the + * submit button via setupEmptyDisabler and setupFormHandler. + */ + $form.find( '.flow-reply-submit' ).off(); + $form.find( '.flow-cancel-link, .flow-content-preview, .flow-preview-submit, .flow-error' ).remove(); + + // when hidden via slideUp, some inline CSS is added to keep the + // elements hidden, but we want it in it's original state (where CSS + // scoping :not(.flow-reply-form-active) will make the form stay hidden) + $form.css( 'display', '' ); + }; + + /** + * Submit function for flow( 'setupFormHandler' ). + * + * @param {object} data this.prepareResult return value + * @return {jQuery.Deferred} + */ + mw.flow.action.prototype.submitFunction = function ( data ) { + // base action.js has no default submitFunction - will need custom implementation + throw 'submitFunction not yet implemented'; + }; + + /** * Feeds parameters to flow( 'setupFormHandler' ). * + * @param {jQuery} $form * @return {array} Array with params, to be fed to validateCallback & * submitFunction */ - mw.flow.action.prototype.loadParametersCallback = function () { - var $textarea = this.object.$container.find( '.flow-edit-content' ), + mw.flow.action.prototype.loadParametersCallback = function ( $form ) { + var $textarea = $form.find( 'textarea' ), content = mw.flow.editor.getContent( $textarea ); return [ content ]; @@ -136,13 +285,6 @@ */ mw.flow.action.prototype.validateCallback = function ( content ) { return !!content; - }; - - /** - * @param {jQuery.Deferred} deferred - */ - mw.flow.action.prototype.promiseCallback = function ( deferred ) { - deferred.done( this.destroyEditForm.bind( this ) ); }; /** diff --git a/modules/base/ui-functions.js b/modules/base/ui-functions.js index ebac6d2..095fda9 100644 --- a/modules/base/ui-functions.js +++ b/modules/base/ui-functions.js @@ -79,146 +79,6 @@ }, /** - * Sets up a reply form. - * - * Unlike edit forms, reply forms already exit in HTML, we just have - * to "activate" some JS magic. - * - * @param {string} type The type of edit form (single word) - * @param {string|object} initialContent The content to pre-fill. - * An object, with the keys 'content' and 'format'. Or a plain string of wikitext. - * @param {function} submitFunction Function to call in order to submit the form. - * One parameter, the content. - * @param {function} loadFunction Function to call once the form is loaded. - * @return {Promise} A promise that will be resolved or rejected - * when the form submission has returned. - */ - 'loadReplyForm' : function( type, initialContent, submitFunction, loadFunction ) { - var $formContainer = $( this ), - $form = $formContainer.find( 'form' ), - $textarea = $formContainer.find( 'textarea' ), - deferredObject = $.Deferred(); - - if ( $formContainer.hasClass( 'flow-form-active' ) ) { - // This is a bit yucky, but should behave correctly in most cases - return deferredObject.promise(); - } - - // add class to identify this form as being active - $formContainer.addClass( 'flow-form-active' ); - - // hide topic reply form - $formContainer.closest( '.flow-topic-container' ).find( '.flow-topic-reply-container' ).hide(); - - // show this form - $formContainer.show(); - - // add cancel link - $( '<a />' ) - .attr( 'href', '#' ) - .addClass( 'flow-cancel-link mw-ui-button mw-ui-quiet' ) - .text( mw.msg( 'flow-cancel' ) ) - .click( function ( e ) { - e.preventDefault(); - - mw.flow.editor.destroy( $textarea ); - $form.flow( 'hidePreview' ); - - // when closed, display topic reply form again - $formContainer - .closest( '.flow-topic-container' ) - .find( '.flow-topic-reply-container' ) - .show(); - - /* - * Because we're not entirely killing the forms, we have - * to clean up after cancelling a form (e.g. reply-forms - * may be re-used across posts - when max threading - * depth has been reached) - * - * After submitting the reply, kill the events that were - * bound to the submit button via setupEmptyDisabler and - * setupFormHandler. - */ - $formContainer.find( '.flow-'+type+'-reply-submit' ).off(); - $formContainer.removeClass( 'flow-form-active' ); - $( this ).remove(); - $formContainer.find( '.flow-content-preview, .flow-preview-submit' ).remove(); - } ) - .after( ' ' ) - .prependTo( $formContainer.find( '.flow-form-controls') ); - - // setup preview - $form.flow( 'setupPreview' ); - - // setup submission callback - $formContainer.flow( 'setupFormHandler', - '.flow-'+type+'-reply-submit', - submitFunction, - function () { - var content = mw.flow.editor.getContent( $formContainer.find( '.flow-'+type+'-reply-content' ) ); - return [ content ]; - }, - function ( content ) { - return content; - }, - function ( promise ) { - promise - .done( function () { - deferredObject.resolve.apply( $formContainer, arguments ); - } ) - .fail( function() { - deferredObject.reject.apply( $formContainer, arguments ); - } ); - } - ); - - // setup disabler (disables submit button until content is entered) - $formContainer.flow( 'setupEmptyDisabler', - ['.flow-'+type+'-reply-content'], - '.flow-'+type+'-reply-submit' - ); - - if ( typeof initialContent != 'object' ) { - initialContent = { - 'content' : initialContent, - 'format' : 'wikitext' - }; - } - - /* - * Setting focus inside an event that grants focus (like - * clicking the reply button), is tricky. This is a workaround. - */ - setTimeout( function( $formContainer, initialContent, loadFunction ) { - var $textarea = $formContainer.find( 'textarea' ); - - $textarea - .removeClass( 'flow-reply-box-closed' ) - // Override textarea height; doesn't need to be too large initially, - // it'll auto-expand - .attr( 'rows', '6' ) - // Textarea will auto-expand + textarea padding will cause the - // resize grabber to be positioned badly (in FF) so get rid of it - .css( 'resize', 'none' ) - .focus(); - - mw.flow.editor.load( $textarea, initialContent.content, initialContent.format ) - .done( loadFunction ); - - // scroll to the form - $formContainer.scrollIntoView( { - complete: function () { - mw.flow.editor.focus( $textarea ); - mw.flow.editor.moveCursorToEnd( $textarea ); - } - } ); - }.bind( this, $formContainer, initialContent, loadFunction ), 0 ); - - return deferredObject.promise(); - }, - - /** * @param {string} submitSelector jQuery selector string to capture submit button * @param {function} submitFunction Function to execute when submitting form * @param {function} loadParametersCallback Function to load parameters to be submitted diff --git a/modules/discussion/post.js b/modules/discussion/post.js index 4c0ebef..016f329 100644 --- a/modules/discussion/post.js +++ b/modules/discussion/post.js @@ -316,11 +316,9 @@ * @param {function} [loadFunction] callback to be executed when form is loaded */ mw.flow.action.post.reply.prototype.loadReplyForm = function ( loadFunction ) { - this.$form.flow( - 'loadReplyForm', - this.object.type, - this.initialContent(), - this.submitFunction.bind( this ), + // call parent loadReplyForm function + mw.flow.action.prototype.loadReplyForm.call( + this, loadFunction ); }; diff --git a/modules/discussion/styles/nojs.less b/modules/discussion/styles/nojs.less index 8521e87..d8af60f 100644 --- a/modules/discussion/styles/nojs.less +++ b/modules/discussion/styles/nojs.less @@ -38,7 +38,7 @@ } // don't shrink reply form (it can't auto-expand) - .flow-topic-reply-content { + .flow-reply-content { height: auto; } diff --git a/modules/discussion/styles/post.less b/modules/discussion/styles/post.less index 07bb8b7..5d3c025 100644 --- a/modules/discussion/styles/post.less +++ b/modules/discussion/styles/post.less @@ -28,6 +28,19 @@ display: block; } } + + // when reply form is displayed, class .flow-edit-form-active will be added, + // so we can show/hide elements as needed + &:not(.flow-reply-form-active) { + .flow-reply-link { + display: inline; + } + } + &.flow-reply-form-active { + .flow-reply-link { + display: none; + } + } } .flow-post-main { @@ -185,7 +198,7 @@ padding: 10px 0 10px 22px; display: none; - &.flow-form-active { + &.flow-reply-form-active { display: block; } } diff --git a/modules/discussion/styles/topic.less b/modules/discussion/styles/topic.less index 567651c..e3b2210 100644 --- a/modules/discussion/styles/topic.less +++ b/modules/discussion/styles/topic.less @@ -262,7 +262,7 @@ display: none; } - &.flow-form-active { + &.flow-new-form-active { .flow-newtopic-step2 { display: block; } @@ -285,7 +285,7 @@ // hide controls when form is inactive // class is added via JS, so controls are always visible for non-JS - &.flow-form-active { + &.flow-reply-form-active { .flow-form-controls { display: block; } @@ -293,8 +293,8 @@ // Make the "comment on topic" textarea look like the "start a new topic" // input field. - &:not(.flow-form-active) { - .flow-topic-reply-content { + &:not(.flow-reply-form-active) { + .flow-reply-content { height: 34px; } } diff --git a/modules/discussion/topic.js b/modules/discussion/topic.js index e983d3a..6b5438c 100644 --- a/modules/discussion/topic.js +++ b/modules/discussion/topic.js @@ -355,7 +355,7 @@ this.$form = this.object.$container.find( '.flow-topic-reply-container' ); // Overload click in textarea, triggering full reply form - this.$form.find( '.flow-topic-reply-content' ).click( this.reply.bind( this ) ); + this.$form.find( '.flow-reply-content' ).click( this.reply.bind( this ) ); }; // extend edit action from "shared functionality" mw.flow.action class @@ -381,11 +381,9 @@ * @param {function} [loadFunction] callback to be executed when form is loaded */ mw.flow.action.topic.reply.prototype.loadReplyForm = function ( loadFunction ) { - this.$form.flow( - 'loadReplyForm', - this.object.type, - this.initialContent(), - this.submitFunction.bind( this ), + // call parent loadReplyForm function + mw.flow.action.prototype.loadReplyForm.call( + this, loadFunction ); }; @@ -451,12 +449,12 @@ e.preventDefault(); // don't re-bind if form is already active - if ( this.$form.hasClass( 'flow-form-active' ) ) { + if ( this.$form.hasClass( 'flow-new-form-active' ) ) { return; } // mark form as active - this.$form.addClass( 'flow-form-active' ); + this.$form.addClass( 'flow-new-form-active' ); // load the form this.loadNewForm(); @@ -529,13 +527,13 @@ $( '.flow-newtopic-submit', this.$form ).off(); // cleanup what's been added via JS - this.$form.removeClass( 'flow-form-active' ); + this.$form.removeClass( 'flow-new-form-active' ); this.$form.find( '.flow-cancel-link, .flow-content-preview, .flow-preview-submit' ).remove(); this.$form.find( '.flow-error' ).remove(); // slideUp adds some inline CSS to keep the elements hidden, // but we want it in it's original state (where CSS scoping - // :not(.flow-form-active) will make the form stay hidden) + // :not(.flow-new-form-active) will make the form stay hidden) this.$form.find( '.flow-newtopic-step2' ).css( 'display', '' ); }.bind( this ) ); }; diff --git a/templates/post.html.php b/templates/post.html.php index c600d33..2fc823e 100644 --- a/templates/post.html.php +++ b/templates/post.html.php @@ -39,7 +39,7 @@ Html::textarea( $block->getName() . '[content]', '', array( 'placeholder' => $placeHolder, 'title' => $placeHolder, - 'class' => 'flow-post-reply-content mw-ui-input', + 'class' => 'flow-reply-content mw-ui-input', 'rows' => '10', ) ) . // NOTE: cancel button will be added via JS, makes no sense in non-JS context @@ -48,7 +48,7 @@ Html::element( 'input', array( 'type' => 'submit', 'value' => $postView->replySubmit(), - 'class' => 'mw-ui-button mw-ui-constructive flow-post-reply-submit', + 'class' => 'mw-ui-button mw-ui-constructive flow-reply-submit', ) ) . Html::closeElement( 'div' ) . Html::closeElement( 'form' ) . diff --git a/templates/topic.html.php b/templates/topic.html.php index f725d7e..49ee37b 100644 --- a/templates/topic.html.php +++ b/templates/topic.html.php @@ -39,14 +39,14 @@ ) ) . Html::textarea( $block->getName() . '[topic-reply-content]', '', array( 'placeholder' => wfMessage( 'flow-reply-topic-placeholder', $user->getName(), $title )->text(), - 'class' => 'mw-ui-input flow-topic-reply-content', + 'class' => 'mw-ui-input flow-reply-content', 'rows' => '10', ) ) . '<div class="flow-form-controls">' . Html::element( 'input', array( 'type' => 'submit', 'value' => wfMessage( 'flow-reply-submit', $this->getCreatorText( $root ) ), - 'class' => 'mw-ui-button mw-ui-constructive flow-topic-reply-submit', + 'class' => 'mw-ui-button mw-ui-constructive flow-reply-submit', ) ) . Html::closeElement( 'div' ) . Html::closeElement( 'form' ) . -- To view, visit https://gerrit.wikimedia.org/r/108634 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Iee67765d60851deb62e4ce6b87b7869eedae224b Gerrit-PatchSet: 1 Gerrit-Project: mediawiki/extensions/Flow Gerrit-Branch: master Gerrit-Owner: Matthias Mullie <mmul...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits