Merge "RC Filters UI: 'restore defaults' clears highlight"
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / mw.rcfilters.Controller.js
1 ( function ( mw, $ ) {
2 /**
3 * Controller for the filters in Recent Changes
4 *
5 * @param {mw.rcfilters.dm.FiltersViewModel} filtersModel Filters view model
6 * @param {mw.rcfilters.dm.ChangesListViewModel} changesListModel Changes list view model
7 */
8 mw.rcfilters.Controller = function MwRcfiltersController( filtersModel, changesListModel ) {
9 this.filtersModel = filtersModel;
10 this.changesListModel = changesListModel;
11 this.requestCounter = 0;
12 };
13
14 /* Initialization */
15 OO.initClass( mw.rcfilters.Controller );
16
17 /**
18 * Initialize the filter and parameter states
19 *
20 * @param {Object} filterStructure Filter definition and structure for the model
21 */
22 mw.rcfilters.Controller.prototype.initialize = function ( filterStructure ) {
23 var uri = new mw.Uri();
24
25 // Initialize the model
26 this.filtersModel.initializeFilters( filterStructure );
27
28 // Set filter states based on defaults and URL params
29 this.filtersModel.updateFilters(
30 this.filtersModel.getFiltersFromParameters(
31 // Merge defaults with URL params for initialization
32 $.extend(
33 true,
34 {},
35 this.filtersModel.getDefaultParams(),
36 // URI query overrides defaults
37 uri.query
38 )
39 )
40 );
41
42 // Initialize highlights
43 this.filtersModel.toggleHighlight( !!uri.query.highlight );
44 this.filtersModel.getItems().forEach( function ( filterItem ) {
45 var color = uri.query[ filterItem.getName() + '_color' ];
46 if ( !color ) {
47 return;
48 }
49
50 filterItem.setHighlightColor( color );
51 } );
52
53 // Check all filter interactions
54 this.filtersModel.reassessFilterInteractions();
55 };
56
57 /**
58 * Reset to default filters
59 */
60 mw.rcfilters.Controller.prototype.resetToDefaults = function () {
61 this.filtersModel.setFiltersToDefaults();
62 this.filtersModel.clearAllHighlightColors();
63 // Check all filter interactions
64 this.filtersModel.reassessFilterInteractions();
65
66 this.updateChangesList();
67 };
68
69 /**
70 * Empty all selected filters
71 */
72 mw.rcfilters.Controller.prototype.emptyFilters = function () {
73 this.filtersModel.emptyAllFilters();
74 this.filtersModel.clearAllHighlightColors();
75 // Check all filter interactions
76 this.filtersModel.reassessFilterInteractions();
77
78 this.updateChangesList();
79 };
80
81 /**
82 * Update the selected state of a filter
83 *
84 * @param {string} filterName Filter name
85 * @param {boolean} [isSelected] Filter selected state
86 */
87 mw.rcfilters.Controller.prototype.toggleFilterSelect = function ( filterName, isSelected ) {
88 var obj = {},
89 filterItem = this.filtersModel.getItemByName( filterName );
90
91 isSelected = isSelected === undefined ? !filterItem.isSelected() : isSelected;
92
93 if ( filterItem.isSelected() !== isSelected ) {
94 obj[ filterName ] = isSelected;
95 this.filtersModel.updateFilters( obj );
96
97 this.updateChangesList();
98
99 // Check filter interactions
100 this.filtersModel.reassessFilterInteractions( this.filtersModel.getItemByName( filterName ) );
101 }
102 };
103
104 /**
105 * Update the URL of the page to reflect current filters
106 *
107 * This should not be called directly from outside the controller.
108 * If an action requires changing the URL, it should either use the
109 * highlighting actions below, or call #updateChangesList which does
110 * the uri corrections already.
111 *
112 * @private
113 * @param {Object} [params] Extra parameters to add to the API call
114 */
115 mw.rcfilters.Controller.prototype.updateURL = function ( params ) {
116 var uri;
117
118 params = params || {};
119
120 uri = this.getUpdatedUri();
121 uri.extend( params );
122
123 window.history.pushState( { tag: 'rcfilters' }, document.title, uri.toString() );
124 };
125
126 /**
127 * Get an updated mw.Uri object based on the model state
128 *
129 * @return {mw.Uri} Updated Uri
130 */
131 mw.rcfilters.Controller.prototype.getUpdatedUri = function () {
132 var uri = new mw.Uri(),
133 highlightParams = this.filtersModel.getHighlightParameters();
134
135 // Add to existing queries in URL
136 // TODO: Clean up the list of filters; perhaps 'falsy' filters
137 // shouldn't appear at all? Or compare to existing query string
138 // and see if current state of a specific filter is needed?
139 uri.extend( this.filtersModel.getParametersFromFilters() );
140
141 // highlight params
142 Object.keys( highlightParams ).forEach( function ( paramName ) {
143 if ( highlightParams[ paramName ] ) {
144 uri.query[ paramName ] = highlightParams[ paramName ];
145 } else {
146 delete uri.query[ paramName ];
147 }
148 } );
149
150 return uri;
151 };
152
153 /**
154 * Fetch the list of changes from the server for the current filters
155 *
156 * @return {jQuery.Promise} Promise object that will resolve with the changes list
157 * or with a string denoting no results.
158 */
159 mw.rcfilters.Controller.prototype.fetchChangesList = function () {
160 var uri = this.getUpdatedUri(),
161 requestId = ++this.requestCounter,
162 latestRequest = function () {
163 return requestId === this.requestCounter;
164 }.bind( this );
165
166 return $.ajax( uri.toString(), { contentType: 'html' } )
167 .then(
168 // Success
169 function ( html ) {
170 var $parsed;
171 if ( !latestRequest() ) {
172 return $.Deferred().reject();
173 }
174
175 $parsed = $( $.parseHTML( html ) );
176
177 return {
178 // Changes list
179 changes: $parsed.find( '.mw-changeslist' ).first().contents(),
180 // Fieldset
181 fieldset: $parsed.find( 'fieldset.rcoptions' ).first()
182 };
183 },
184 // Failure
185 function ( responseObj ) {
186 var $parsed;
187
188 if ( !latestRequest() ) {
189 return $.Deferred().reject();
190 }
191
192 $parsed = $( $.parseHTML( responseObj.responseText ) );
193
194 // Force a resolve state to this promise
195 return $.Deferred().resolve( {
196 changes: 'NO_RESULTS',
197 fieldset: $parsed.find( 'fieldset.rcoptions' ).first()
198 } ).promise();
199 }
200 );
201 };
202
203 /**
204 * Update the list of changes and notify the model
205 *
206 * @param {Object} [params] Extra parameters to add to the API call
207 */
208 mw.rcfilters.Controller.prototype.updateChangesList = function ( params ) {
209 this.updateURL( params );
210 this.changesListModel.invalidate();
211 this.fetchChangesList()
212 .then(
213 // Success
214 function ( pieces ) {
215 var $changesListContent = pieces.changes,
216 $fieldset = pieces.fieldset;
217 this.changesListModel.update( $changesListContent, $fieldset );
218 }.bind( this )
219 // Do nothing for failure
220 );
221 };
222
223 /**
224 * Toggle the highlight feature on and off
225 */
226 mw.rcfilters.Controller.prototype.toggleHighlight = function () {
227 this.filtersModel.toggleHighlight();
228 this.updateURL();
229 };
230
231 /**
232 * Set the highlight color for a filter item
233 *
234 * @param {string} filterName Name of the filter item
235 * @param {string} color Selected color
236 */
237 mw.rcfilters.Controller.prototype.setHighlightColor = function ( filterName, color ) {
238 this.filtersModel.setHighlightColor( filterName, color );
239 this.updateURL();
240 };
241
242 /**
243 * Clear highlight for a filter item
244 *
245 * @param {string} filterName Name of the filter item
246 */
247 mw.rcfilters.Controller.prototype.clearHighlightColor = function ( filterName ) {
248 this.filtersModel.clearHighlightColor( filterName );
249 this.updateURL();
250 };
251 }( mediaWiki, jQuery ) );