// Check all filter interactions
this.filtersModel.reassessFilterInteractions();
- this.updateURL();
this.updateChangesList();
};
// Check all filter interactions
this.filtersModel.reassessFilterInteractions();
- this.updateURL();
this.updateChangesList();
};
obj[ filterName ] = isSelected;
this.filtersModel.updateFilters( obj );
- this.updateURL();
this.updateChangesList();
// Check filter interactions
/**
* Update the URL of the page to reflect current filters
+ *
+ * This should not be called directly from outside the controller.
+ * If an action requires changing the URL, it should either use the
+ * highlighting actions below, or call #updateChangesList which does
+ * the uri corrections already.
+ *
+ * @private
+ * @param {Object} [params] Extra parameters to add to the API call
*/
- mw.rcfilters.Controller.prototype.updateURL = function () {
- var uri = this.getUpdatedUri();
+ mw.rcfilters.Controller.prototype.updateURL = function ( params ) {
+ var uri;
+
+ params = params || {};
+
+ uri = this.getUpdatedUri();
+ uri.extend( params );
+
window.history.pushState( { tag: 'rcfilters' }, document.title, uri.toString() );
};
* Fetch the list of changes from the server for the current filters
*
* @return {jQuery.Promise} Promise object that will resolve with the changes list
+ * or with a string denoting no results.
*/
mw.rcfilters.Controller.prototype.fetchChangesList = function () {
var uri = this.getUpdatedUri(),
latestRequest = function () {
return requestId === this.requestCounter;
}.bind( this );
- uri.extend( this.filtersModel.getParametersFromFilters() );
+
return $.ajax( uri.toString(), { contentType: 'html' } )
- .then( function ( html ) {
- return latestRequest() ?
- $( $.parseHTML( html ) ).find( '.mw-changeslist' ).first().contents() :
- null;
- } ).then( null, function () {
- return latestRequest() ? 'NO_RESULTS' : null;
- } );
+ .then(
+ // Success
+ function ( html ) {
+ var $parsed;
+ if ( !latestRequest() ) {
+ return $.Deferred().reject();
+ }
+
+ $parsed = $( $.parseHTML( html ) );
+
+ return {
+ // Changes list
+ changes: $parsed.find( '.mw-changeslist' ).first().contents(),
+ // Fieldset
+ fieldset: $parsed.find( 'fieldset.rcoptions' ).first()
+ };
+ },
+ // Failure
+ function ( responseObj ) {
+ var $parsed;
+
+ if ( !latestRequest() ) {
+ return $.Deferred().reject();
+ }
+
+ $parsed = $( $.parseHTML( responseObj.responseText ) );
+
+ // Force a resolve state to this promise
+ return $.Deferred().resolve( {
+ changes: 'NO_RESULTS',
+ fieldset: $parsed.find( 'fieldset.rcoptions' ).first()
+ } ).promise();
+ }
+ );
};
/**
* Update the list of changes and notify the model
+ *
+ * @param {Object} [params] Extra parameters to add to the API call
*/
- mw.rcfilters.Controller.prototype.updateChangesList = function () {
+ mw.rcfilters.Controller.prototype.updateChangesList = function ( params ) {
+ this.updateURL( params );
this.changesListModel.invalidate();
this.fetchChangesList()
- .always( function ( changesListContent ) {
- if ( changesListContent ) {
- this.changesListModel.update( changesListContent );
- }
- }.bind( this ) );
+ .then(
+ // Success
+ function ( pieces ) {
+ var $changesListContent = pieces.changes,
+ $fieldset = pieces.fieldset;
+
+ this.changesListModel.update( $changesListContent, $fieldset );
+ }.bind( this )
+ // Do nothing for failure
+ );
};
/**
*
* @constructor
* @param {mw.rcfilters.dm.ChangesListViewModel} model Changes list view model
+ * @param {mw.rcfilters.Controller} controller RCfilters controller
* @param {jQuery} $formRoot Root element of the form to attach to
* @param {Object} config Configuration object
*/
- mw.rcfilters.ui.FormWrapperWidget = function MwRcfiltersUiFormWrapperWidget( model, $formRoot, config ) {
+ mw.rcfilters.ui.FormWrapperWidget = function MwRcfiltersUiFormWrapperWidget( model, controller, $formRoot, config ) {
config = config || {};
// Parent
mw.rcfilters.ui.FormWrapperWidget.parent.call( this, $.extend( {}, config, {
$element: $formRoot
} ) );
+ // Mixin constructors
+ OO.ui.mixin.PendingElement.call( this, config );
this.model = model;
+ this.controller = controller;
this.$submitButton = this.$element.find( 'form input[type=submit]' );
+ this.$element
+ .on( 'click', 'a[data-params]', this.onLinkClick.bind( this ) );
+
+ this.$element
+ .on( 'submit', 'form', this.onFormSubmit.bind( this ) );
+
// Events
this.model.connect( this, {
invalidate: 'onModelInvalidate',
update: 'onModelUpdate'
} );
+ // Initialize
+ this.cleanupForm();
this.$element
.addClass( 'mw-rcfilters-ui-FormWrapperWidget' )
.addClass( 'mw-rcfilters-ui-ready' );
/* Initialization */
OO.inheritClass( mw.rcfilters.ui.FormWrapperWidget, OO.ui.Widget );
+ OO.mixinClass( mw.rcfilters.ui.FormWrapperWidget, OO.ui.mixin.PendingElement );
+
+ /**
+ * Clean up the base form we're getting from the back-end.
+ * Remove <strong> tags and replace those with classes, so
+ * we can toggle those on click.
+ */
+ mw.rcfilters.ui.FormWrapperWidget.prototype.cleanupForm = function () {
+ this.$element.find( '[data-keys] strong' ).each( function () {
+ $( this )
+ .parent().addClass( 'mw-rcfilters-staticfilters-selected' );
+
+ $( this )
+ .replaceWith( $( this ).contents() );
+ } );
+ };
+
+ /**
+ * Respond to link click
+ *
+ * @param {jQuery.Event} e Event
+ * @return {boolean} false
+ */
+ mw.rcfilters.ui.FormWrapperWidget.prototype.onLinkClick = function ( e ) {
+ var $element = $( e.target ),
+ data = $element.data( 'params' ),
+ keys = $element.data( 'keys' ),
+ $similarElements = $element.parent().find( '[data-keys="' + keys + '"]' );
+
+ // Only highlight choice if this link isn't a show/hide link
+ if ( !$element.parents( '.rcshowhideoption' ).length ) {
+ // Remove the class from similar elements
+ $similarElements.removeClass( 'mw-rcfilters-staticfilters-selected' );
+ // Add the class to this element
+ $element.addClass( 'mw-rcfilters-staticfilters-selected' );
+ }
+
+ e.stopPropagation();
+
+ this.controller.updateChangesList( data );
+ return false;
+ };
+
+ /**
+ * Respond to form submit event
+ *
+ * @param {jQuery.Event} e Event
+ * @return {boolean} false
+ */
+ mw.rcfilters.ui.FormWrapperWidget.prototype.onFormSubmit = function ( e ) {
+ var data = {};
+
+ // Collect all data from form
+ $( e.target ).find( 'input:not([type="hidden"],[type="submit"]), select' ).each( function () {
+ if ( !$( this ).is( ':checkbox' ) || $( this ).is( ':checked' ) ) {
+ data[ $( this ).prop( 'name' ) ] = $( this ).val();
+ }
+ } );
+
+ this.controller.updateChangesList( data );
+ return false;
+ };
/**
* Respond to model invalidate
*/
mw.rcfilters.ui.FormWrapperWidget.prototype.onModelInvalidate = function () {
+ this.pushPending();
this.$submitButton.prop( 'disabled', true );
};
/**
- * Respond to model update
+ * Respond to model update, replace the show/hide links with the ones from the
+ * server so they feature the correct state.
+ *
+ * @param {jQuery|string} $changesList Updated changes list
+ * @param {jQuery} $fieldset Updated fieldset
*/
- mw.rcfilters.ui.FormWrapperWidget.prototype.onModelUpdate = function () {
+ mw.rcfilters.ui.FormWrapperWidget.prototype.onModelUpdate = function ( $changesList, $fieldset ) {
this.$submitButton.prop( 'disabled', false );
+
+ // Replace the links we have in the content
+ // We don't want to replace the entire thing, because there is a big difference between
+ // the links in the backend and the links we have initialized, since we are removing
+ // the ones that are implemented in the new system
+ this.$element.find( '.rcshowhide' ).children().each( function () {
+ // Go over existing links and replace only them
+ var classes = $( this ).attr( 'class' ).split( ' ' ),
+ // Look for that item in the fieldset from the server
+ $remoteItem = $fieldset.find( '.' + classes.join( '.' ) );
+
+ if ( $remoteItem ) {
+ $( this ).replaceWith( $remoteItem );
+ }
+ } );
+
+ this.popPending();
};
}( mediaWiki ) );