3 * Controller for the filters in Recent Changes
5 * @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters view model
6 * @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel Changes list view model
8 mw
.rcfilters
.Controller
= function MwRcfiltersController( filtersModel
, changesListModel
) {
9 this.filtersModel
= filtersModel
;
10 this.changesListModel
= changesListModel
;
11 this.requestCounter
= 0;
15 OO
.initClass( mw
.rcfilters
.Controller
);
18 * Initialize the filter and parameter states
20 * @param {Object} filterStructure Filter definition and structure for the model
22 mw
.rcfilters
.Controller
.prototype.initialize = function ( filterStructure
) {
23 // Initialize the model
24 this.filtersModel
.initializeFilters( filterStructure
);
25 this.updateStateBasedOnUrl();
29 * Update filter state (selection and highlighting) based
30 * on current URL and default values.
32 mw
.rcfilters
.Controller
.prototype.updateStateBasedOnUrl = function () {
33 var uri
= new mw
.Uri();
35 // Set filter states based on defaults and URL params
36 this.filtersModel
.toggleFiltersSelected(
37 this.filtersModel
.getFiltersFromParameters(
38 // Merge defaults with URL params for initialization
42 this.filtersModel
.getDefaultParams(),
43 // URI query overrides defaults
49 // Initialize highlights
50 this.filtersModel
.toggleHighlight( !!uri
.query
.highlight
);
51 this.filtersModel
.getItems().forEach( function ( filterItem
) {
52 var color
= uri
.query
[ filterItem
.getName() + '_color' ];
54 filterItem
.setHighlightColor( color
);
56 filterItem
.clearHighlightColor();
60 // Check all filter interactions
61 this.filtersModel
.reassessFilterInteractions();
65 * Reset to default filters
67 mw
.rcfilters
.Controller
.prototype.resetToDefaults = function () {
68 this.filtersModel
.setFiltersToDefaults();
69 this.filtersModel
.clearAllHighlightColors();
70 // Check all filter interactions
71 this.filtersModel
.reassessFilterInteractions();
73 this.updateChangesList();
77 * Empty all selected filters
79 mw
.rcfilters
.Controller
.prototype.emptyFilters = function () {
80 this.filtersModel
.emptyAllFilters();
81 this.filtersModel
.clearAllHighlightColors();
82 // Check all filter interactions
83 this.filtersModel
.reassessFilterInteractions();
85 this.updateChangesList();
89 * Update the selected state of a filter
91 * @param {string} filterName Filter name
92 * @param {boolean} [isSelected] Filter selected state
94 mw
.rcfilters
.Controller
.prototype.toggleFilterSelect = function ( filterName
, isSelected
) {
95 var filterItem
= this.filtersModel
.getItemByName( filterName
);
97 isSelected
= isSelected
=== undefined ? !filterItem
.isSelected() : isSelected
;
99 if ( filterItem
.isSelected() !== isSelected
) {
100 this.filtersModel
.toggleFilterSelected( filterName
, isSelected
);
102 this.updateChangesList();
104 // Check filter interactions
105 this.filtersModel
.reassessFilterInteractions( filterItem
);
110 * Update the URL of the page to reflect current filters
112 * This should not be called directly from outside the controller.
113 * If an action requires changing the URL, it should either use the
114 * highlighting actions below, or call #updateChangesList which does
115 * the uri corrections already.
118 * @param {Object} [params] Extra parameters to add to the API call
120 mw
.rcfilters
.Controller
.prototype.updateURL = function ( params
) {
122 notEquivalent = function ( obj1
, obj2
) {
123 var keys
= Object
.keys( obj1
).concat( Object
.keys( obj2
) );
124 return keys
.some( function ( key
) {
125 return obj1
[ key
] != obj2
[ key
]; // eslint-disable-line eqeqeq
129 params
= params
|| {};
131 updatedUri
= this.getUpdatedUri();
132 updatedUri
.extend( params
);
134 if ( notEquivalent( updatedUri
.query
, new mw
.Uri().query
) ) {
135 window
.history
.pushState( { tag
: 'rcfilters' }, document
.title
, updatedUri
.toString() );
140 * Get an updated mw.Uri object based on the model state
142 * @return {mw.Uri} Updated Uri
144 mw
.rcfilters
.Controller
.prototype.getUpdatedUri = function () {
145 var uri
= new mw
.Uri(),
146 highlightParams
= this.filtersModel
.getHighlightParameters();
148 // Add to existing queries in URL
149 // TODO: Clean up the list of filters; perhaps 'falsy' filters
150 // shouldn't appear at all? Or compare to existing query string
151 // and see if current state of a specific filter is needed?
152 uri
.extend( this.filtersModel
.getParametersFromFilters() );
155 Object
.keys( highlightParams
).forEach( function ( paramName
) {
156 if ( highlightParams
[ paramName
] ) {
157 uri
.query
[ paramName
] = highlightParams
[ paramName
];
159 delete uri
.query
[ paramName
];
167 * Fetch the list of changes from the server for the current filters
169 * @return {jQuery.Promise} Promise object that will resolve with the changes list
170 * or with a string denoting no results.
172 mw
.rcfilters
.Controller
.prototype.fetchChangesList = function () {
173 var uri
= this.getUpdatedUri(),
174 requestId
= ++this.requestCounter
,
175 latestRequest = function () {
176 return requestId
=== this.requestCounter
;
179 return $.ajax( uri
.toString(), { contentType
: 'html' } )
184 if ( !latestRequest() ) {
185 return $.Deferred().reject();
188 $parsed
= $( $.parseHTML( html
) );
192 changes
: $parsed
.find( '.mw-changeslist' ).first().contents(),
194 fieldset
: $parsed
.find( 'fieldset.rcoptions' ).first()
198 function ( responseObj
) {
201 if ( !latestRequest() ) {
202 return $.Deferred().reject();
205 $parsed
= $( $.parseHTML( responseObj
.responseText
) );
207 // Force a resolve state to this promise
208 return $.Deferred().resolve( {
209 changes
: 'NO_RESULTS',
210 fieldset
: $parsed
.find( 'fieldset.rcoptions' ).first()
217 * Update the list of changes and notify the model
219 * @param {Object} [params] Extra parameters to add to the API call
221 mw
.rcfilters
.Controller
.prototype.updateChangesList = function ( params
) {
222 this.updateURL( params
);
223 this.changesListModel
.invalidate();
224 this.fetchChangesList()
227 function ( pieces
) {
228 var $changesListContent
= pieces
.changes
,
229 $fieldset
= pieces
.fieldset
;
230 this.changesListModel
.update( $changesListContent
, $fieldset
);
232 // Do nothing for failure
237 * Toggle the highlight feature on and off
239 mw
.rcfilters
.Controller
.prototype.toggleHighlight = function () {
240 this.filtersModel
.toggleHighlight();
245 * Set the highlight color for a filter item
247 * @param {string} filterName Name of the filter item
248 * @param {string} color Selected color
250 mw
.rcfilters
.Controller
.prototype.setHighlightColor = function ( filterName
, color
) {
251 this.filtersModel
.setHighlightColor( filterName
, color
);
256 * Clear highlight for a filter item
258 * @param {string} filterName Name of the filter item
260 mw
.rcfilters
.Controller
.prototype.clearHighlightColor = function ( filterName
) {
261 this.filtersModel
.clearHighlightColor( filterName
);
266 * Clear both highlight and selection of a filter
268 * @param {string} filterName Name of the filter item
270 mw
.rcfilters
.Controller
.prototype.clearFilter = function ( filterName
) {
271 var filterItem
= this.filtersModel
.getItemByName( filterName
);
273 if ( filterItem
.isSelected() || filterItem
.isHighlighted() ) {
274 this.filtersModel
.clearHighlightColor( filterName
);
275 this.filtersModel
.toggleFilterSelected( filterName
, false );
276 this.updateChangesList();
277 this.filtersModel
.reassessFilterInteractions( filterItem
);
282 * Synchronize the URL with the current state of the filters
283 * without adding an history entry.
285 mw
.rcfilters
.Controller
.prototype.replaceUrl = function () {
286 window
.history
.replaceState(
287 { tag
: 'rcfilters' },
289 this.getUpdatedUri().toString()
292 }( mediaWiki
, jQuery
) );