this.initializing = false;
this.prevLoggedItems = [];
+
+ this.FILTER_CHANGE = 'filterChange';
+ this.SHOW_NEW_CHANGES = 'showNewChanges';
+ this.LIVE_UPDATE = 'liveUpdate';
};
/* Initialization */
*/
mw.rcfilters.Controller.prototype.initialize = function ( filterStructure, namespaceStructure, tagList ) {
var parsedSavedQueries, limitDefault,
+ displayConfig = mw.config.get( 'StructuredChangeFiltersDisplayConfig' ),
controller = this,
views = {},
items = [],
// Convert the default from the old preference
// since the limit preference actually affects more
// than just the RecentChanges page
- limitDefault = Number( mw.user.options.get( 'rcfilters-rclimit', mw.user.options.get( 'rclimit', '50' ) ) );
+ limitDefault = Number( mw.user.options.get( 'rclimit', '50' ) );
// Add parameter range operations
views.range = {
hidden: true,
allowArbitrary: true,
validate: $.isNumeric,
+ range: {
+ min: 0, // The server normalizes negative numbers to 0 results
+ max: 1000
+ },
sortFunc: function ( a, b ) { return Number( a.name ) - Number( b.name ); },
'default': String( limitDefault ),
- isSticky: true,
- filters: [ 50, 100, 250, 500 ].map( function ( num ) {
+ // Temporarily making this not sticky until we resolve the problem
+ // with the misleading preference. Note that if this is to be permanent
+ // we should remove all sticky behavior methods completely
+ // See T172156
+ // isSticky: true,
+ excludedFromSavedQueries: true,
+ filters: displayConfig.limitArray.map( function ( num ) {
return controller._createFilterDataFromNumber( num, num );
} )
},
hidden: true,
allowArbitrary: true,
validate: $.isNumeric,
+ range: {
+ min: 0,
+ max: displayConfig.maxDays
+ },
sortFunc: function ( a, b ) { return Number( a.name ) - Number( b.name ); },
numToLabelFunc: function ( i ) {
return Number( i ) < 1 ?
Number( i );
},
'default': mw.user.options.get( 'rcdays', '30' ),
- isSticky: true,
+ // Temporarily making this not sticky while limit is not sticky, see above
+ // isSticky: true,
+ excludedFromSavedQueries: true,
filters: [
// Hours (1, 2, 6, 12)
- 0.04166, 0.0833, 0.25, 0.5,
- // Days
- 1, 3, 7, 14, 30
- ].map( function ( num ) {
- return controller._createFilterDataFromNumber(
- num,
- // Convert fractions of days to number of hours for the labels
- num < 1 ? Math.round( num * 24 ) : num
- );
- } )
+ 0.04166, 0.0833, 0.25, 0.5
+ // Days
+ ].concat( displayConfig.daysArray )
+ .map( function ( num ) {
+ return controller._createFilterDataFromNumber(
+ num,
+ // Convert fractions of days to number of hours for the labels
+ num < 1 ? Math.round( num * 24 ) : num
+ );
+ } )
+ }
+ ]
+ };
+
+ views.display = {
+ groups: [
+ {
+ name: 'display',
+ type: 'boolean',
+ title: '', // Because it's a hidden group, this title actually appears nowhere
+ hidden: true,
+ isSticky: true,
+ filters: [
+ {
+ name: 'enhanced',
+ 'default': String( mw.user.options.get( 'usenewrc', 0 ) )
+ }
+ ]
}
]
};
this.savedQueriesModel.initialize(
parsedSavedQueries,
this._getBaseFilterState(),
- // This is for backwards compatibility - delete all sticky filter states
- Object.keys( this.filtersModel.getStickyFiltersState() )
+ // This is for backwards compatibility - delete all excluded filter states
+ Object.keys( this.filtersModel.getExcludedFiltersState() )
);
// Check whether we need to load defaults.
* @param {string|string[]} arbitraryValues An array of arbitrary values to add to the group
*/
mw.rcfilters.Controller.prototype.addNumberValuesToGroup = function ( groupData, arbitraryValues ) {
- var controller = this;
+ var controller = this,
+ normalizeWithinRange = function ( range, val ) {
+ if ( val < range.min ) {
+ return range.min; // Min
+ } else if ( val >= range.max ) {
+ return range.max; // Max
+ }
+ return val;
+ };
arbitraryValues = Array.isArray( arbitraryValues ) ? arbitraryValues : [ arbitraryValues ];
+ // Normalize the arbitrary values and the default value for a range
+ if ( groupData.range ) {
+ arbitraryValues = arbitraryValues.map( function ( val ) {
+ return normalizeWithinRange( groupData.range, val );
+ } );
+
+ // Normalize the default, since that's user defined
+ if ( groupData.default !== undefined ) {
+ groupData.default = String( normalizeWithinRange( groupData.range, groupData.default ) );
+ }
+ }
+
// This is only true for single_option group
// We assume these are the only groups that will allow for
// arbitrary, since it doesn't make any sense for the other
// but if that value isn't already in the definition
groupData.filters
.map( function ( filterData ) {
- return filterData.name;
+ return String( filterData.name );
} )
- .indexOf( val ) === -1
+ .indexOf( String( val ) ) === -1
) {
// Add the filter information
groupData.filters.push( controller._createFilterDataFromNumber(
*/
mw.rcfilters.Controller.prototype.toggleInvertedNamespaces = function () {
this.filtersModel.toggleInvertedNamespaces();
- this.updateChangesList();
+
+ if (
+ this.filtersModel.getFiltersByView( 'namespaces' )
+ .filter( function ( filterItem ) {
+ return filterItem.isSelected();
+ } )
+ .length
+ ) {
+ // Only re-fetch results if there are namespace items that are actually selected
+ this.updateChangesList();
+ }
};
/**
mw.rcfilters.Controller.prototype.toggleLiveUpdate = function ( enable ) {
this.changesListModel.toggleLiveUpdate( enable );
if ( this.changesListModel.getLiveUpdate() && this.changesListModel.getNewChangesExist() ) {
- this.showNewChanges();
+ this.updateChangesList( null, this.LIVE_UPDATE );
}
};
if ( data.changes !== 'NO_RESULTS' ) {
if ( this.changesListModel.getLiveUpdate() ) {
- return this.updateChangesList( false, null, true, false );
+ return this.updateChangesList( null, this.LIVE_UPDATE );
} else {
this.changesListModel.setNewChangesExist( true );
}
* @private
*/
mw.rcfilters.Controller.prototype._shouldCheckForNewChanges = function () {
- var liveUpdateFeatureFlag = mw.config.get( 'wgStructuredChangeFiltersEnableLiveUpdate' ) ||
- new mw.Uri().query.liveupdate;
-
return !document.hidden &&
+ !this.filtersModel.hasConflict() &&
!this.changesListModel.getNewChangesExist() &&
!this.updatingChangesList &&
- liveUpdateFeatureFlag;
+ mw.rcfilters.featureFlags.liveUpdate;
};
/**
* fetching and showing the new changes
*/
mw.rcfilters.Controller.prototype.showNewChanges = function () {
- return this.updateChangesList( false, null, true, true );
+ return this.updateChangesList( null, this.SHOW_NEW_CHANGES );
};
/**
// These are filter states; highlight is stored as boolean
highlightedItems.highlight = this.filtersModel.isHighlightEnabled();
- // Delete all sticky filters
- this._deleteStickyValuesFromFilterState( selectedState );
+ // Delete all excluded filters
+ this._deleteExcludedValuesFromFilterState( selectedState );
// Add item
queryID = this.savedQueriesModel.addNewQuery(
*/
mw.rcfilters.Controller.prototype.applySavedQuery = function ( queryID ) {
var data, highlights,
- queryItem = this.savedQueriesModel.getItemByID( queryID );
+ queryItem = this.savedQueriesModel.getItemByID( queryID ),
+ currentMatchingQuery = this.findQueryMatchingCurrentState();
- if ( queryItem ) {
+ if (
+ queryItem &&
+ (
+ // If there's already a query, don't reload it
+ // if it's the same as the one that already exists
+ !currentMatchingQuery ||
+ currentMatchingQuery.getID() !== queryItem.getID()
+ )
+ ) {
data = queryItem.getData();
highlights = data.highlights;
// Update model state from filters
this.filtersModel.toggleFiltersSelected(
- // Merge filters with sticky values
- $.extend( true, {}, data.filters, this.filtersModel.getStickyFiltersState() )
+ // Merge filters with excluded values
+ $.extend( true, {}, data.filters, this.filtersModel.getExcludedFiltersState() )
);
// Update namespace inverted property
} );
highlightedItems.highlight = this.filtersModel.isHighlightEnabled();
- // Remove sticky filters
- this._deleteStickyValuesFromFilterState( selectedState );
+ // Remove anything that should be excluded from the saved query
+ // this includes sticky filters and filters marked with 'excludedFromSavedQueries'
+ this._deleteExcludedValuesFromFilterState( selectedState );
return this.savedQueriesModel.findMatchingQuery(
{
*
* @param {Object} filterState Filter state
*/
- mw.rcfilters.Controller.prototype._deleteStickyValuesFromFilterState = function ( filterState ) {
- // Remove sticky filters
- $.each( this.filtersModel.getStickyFiltersState(), function ( filterName ) {
+ mw.rcfilters.Controller.prototype._deleteExcludedValuesFromFilterState = function ( filterState ) {
+ // Remove excluded filters
+ $.each( this.filtersModel.getExcludedFiltersState(), function ( filterName ) {
delete filterState[ filterName ];
} );
};
// the initial defaults or from the URL value that is being normalized
this.updateDaysDefault( this.filtersModel.getGroup( 'days' ).getSelectedItems()[ 0 ].getParamName() );
this.updateLimitDefault( this.filtersModel.getGroup( 'limit' ).getSelectedItems()[ 0 ].getParamName() );
+
+ // TODO: Make these automatic by having the model go over sticky
+ // items and update their default values automatically
};
/**
* Update the limit default value
*
- * @param {number} newValue New value
+ * param {number} newValue New value
*/
- mw.rcfilters.Controller.prototype.updateLimitDefault = function ( newValue ) {
+ mw.rcfilters.Controller.prototype.updateLimitDefault = function ( /* newValue */ ) {
+ // HACK: Temporarily remove this from being sticky
+ // See T172156
+
+ /*
if ( !$.isNumeric( newValue ) ) {
return;
}
// Update the preference for this session
mw.user.options.set( 'rcfilters-rclimit', newValue );
}
+ */
+ return;
};
/**
* Update the days default value
*
- * @param {number} newValue New value
+ * param {number} newValue New value
*/
- mw.rcfilters.Controller.prototype.updateDaysDefault = function ( newValue ) {
+ mw.rcfilters.Controller.prototype.updateDaysDefault = function ( /* newValue */ ) {
+ // HACK: Temporarily remove this from being sticky
+ // See T172156
+
+ /*
if ( !$.isNumeric( newValue ) ) {
return;
}
// Update the preference for this session
mw.user.options.set( 'rcdays', newValue );
}
+ */
+ return;
+ };
+
+ /**
+ * Update the group by page default value
+ *
+ * @param {number} newValue New value
+ */
+ mw.rcfilters.Controller.prototype.updateGroupByPageDefault = function ( newValue ) {
+ if ( !$.isNumeric( newValue ) ) {
+ return;
+ }
+
+ newValue = Number( newValue );
+
+ if ( mw.user.options.get( 'usenewrc' ) !== newValue ) {
+ // Save the preference
+ new mw.Api().saveOption( 'usenewrc', newValue );
+ // Update the preference for this session
+ mw.user.options.set( 'usenewrc', newValue );
+ }
};
/**
/**
* Update the list of changes and notify the model
*
- * @param {boolean} [updateUrl=true] Whether the URL should be updated with the current state of the filters
* @param {Object} [params] Extra parameters to add to the API call
- * @param {boolean} [isLiveUpdate=false] The purpose of this update is to show new results for the same filters
- * @param {boolean} [invalidateCurrentChanges=true] Invalidate current changes by default (show spinner)
+ * @param {string} [updateMode='filterChange'] One of 'filterChange', 'liveUpdate', 'showNewChanges'
* @return {jQuery.Promise} Promise that is resolved when the update is complete
*/
- mw.rcfilters.Controller.prototype.updateChangesList = function ( updateUrl, params, isLiveUpdate, invalidateCurrentChanges ) {
- updateUrl = updateUrl === undefined ? true : updateUrl;
- invalidateCurrentChanges = invalidateCurrentChanges === undefined ? true : invalidateCurrentChanges;
- if ( updateUrl ) {
+ mw.rcfilters.Controller.prototype.updateChangesList = function ( params, updateMode ) {
+ updateMode = updateMode === undefined ? this.FILTER_CHANGE : updateMode;
+
+ if ( updateMode === this.FILTER_CHANGE ) {
this._updateURL( params );
}
- if ( invalidateCurrentChanges ) {
+ if ( updateMode === this.FILTER_CHANGE || updateMode === this.SHOW_NEW_CHANGES ) {
this.changesListModel.invalidate();
}
this.changesListModel.setNewChangesExist( false );
function ( pieces ) {
var $changesListContent = pieces.changes,
$fieldset = pieces.fieldset;
- this.changesListModel.update( $changesListContent, $fieldset, false, isLiveUpdate );
+ this.changesListModel.update(
+ $changesListContent,
+ $fieldset,
+ false,
+ // separator between old and new changes
+ updateMode === this.SHOW_NEW_CHANGES || updateMode === this.LIVE_UPDATE
+ );
}.bind( this )
// Do nothing for failure
)