Merge "Add some translations for Western Punjabi (pnb)"
[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 // Initialize the model
24 this.filtersModel.initializeFilters( filterStructure );
25 this.updateStateBasedOnUrl();
26 };
27
28 /**
29 * Update filter state (selection and highlighting) based
30 * on current URL and default values.
31 */
32 mw.rcfilters.Controller.prototype.updateStateBasedOnUrl = function () {
33 var uri = new mw.Uri();
34
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
39 $.extend(
40 true,
41 {},
42 this.filtersModel.getDefaultParams(),
43 // URI query overrides defaults
44 uri.query
45 )
46 )
47 );
48
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' ];
53 if ( color ) {
54 filterItem.setHighlightColor( color );
55 } else {
56 filterItem.clearHighlightColor();
57 }
58 } );
59
60 // Check all filter interactions
61 this.filtersModel.reassessFilterInteractions();
62 };
63
64 /**
65 * Reset to default filters
66 */
67 mw.rcfilters.Controller.prototype.resetToDefaults = function () {
68 this.filtersModel.setFiltersToDefaults();
69 this.filtersModel.clearAllHighlightColors();
70 // Check all filter interactions
71 this.filtersModel.reassessFilterInteractions();
72
73 this.updateChangesList();
74 };
75
76 /**
77 * Empty all selected filters
78 */
79 mw.rcfilters.Controller.prototype.emptyFilters = function () {
80 this.filtersModel.emptyAllFilters();
81 this.filtersModel.clearAllHighlightColors();
82 // Check all filter interactions
83 this.filtersModel.reassessFilterInteractions();
84
85 this.updateChangesList();
86 };
87
88 /**
89 * Update the selected state of a filter
90 *
91 * @param {string} filterName Filter name
92 * @param {boolean} [isSelected] Filter selected state
93 */
94 mw.rcfilters.Controller.prototype.toggleFilterSelect = function ( filterName, isSelected ) {
95 var filterItem = this.filtersModel.getItemByName( filterName );
96
97 isSelected = isSelected === undefined ? !filterItem.isSelected() : isSelected;
98
99 if ( filterItem.isSelected() !== isSelected ) {
100 this.filtersModel.toggleFilterSelected( filterName, isSelected );
101
102 this.updateChangesList();
103
104 // Check filter interactions
105 this.filtersModel.reassessFilterInteractions( filterItem );
106 }
107 };
108
109 /**
110 * Update the URL of the page to reflect current filters
111 *
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.
116 *
117 * @private
118 * @param {Object} [params] Extra parameters to add to the API call
119 */
120 mw.rcfilters.Controller.prototype.updateURL = function ( params ) {
121 var updatedUri,
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
126 } );
127 };
128
129 params = params || {};
130
131 updatedUri = this.getUpdatedUri();
132 updatedUri.extend( params );
133
134 if ( notEquivalent( updatedUri.query, new mw.Uri().query ) ) {
135 window.history.pushState( { tag: 'rcfilters' }, document.title, updatedUri.toString() );
136 }
137 };
138
139 /**
140 * Get an updated mw.Uri object based on the model state
141 *
142 * @return {mw.Uri} Updated Uri
143 */
144 mw.rcfilters.Controller.prototype.getUpdatedUri = function () {
145 var uri = new mw.Uri(),
146 highlightParams = this.filtersModel.getHighlightParameters();
147
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() );
153
154 // highlight params
155 Object.keys( highlightParams ).forEach( function ( paramName ) {
156 if ( highlightParams[ paramName ] ) {
157 uri.query[ paramName ] = highlightParams[ paramName ];
158 } else {
159 delete uri.query[ paramName ];
160 }
161 } );
162
163 return uri;
164 };
165
166 /**
167 * Fetch the list of changes from the server for the current filters
168 *
169 * @return {jQuery.Promise} Promise object that will resolve with the changes list
170 * or with a string denoting no results.
171 */
172 mw.rcfilters.Controller.prototype.fetchChangesList = function () {
173 var uri = this.getUpdatedUri(),
174 requestId = ++this.requestCounter,
175 latestRequest = function () {
176 return requestId === this.requestCounter;
177 }.bind( this );
178
179 return $.ajax( uri.toString(), { contentType: 'html' } )
180 .then(
181 // Success
182 function ( html ) {
183 var $parsed;
184 if ( !latestRequest() ) {
185 return $.Deferred().reject();
186 }
187
188 $parsed = $( $.parseHTML( html ) );
189
190 return {
191 // Changes list
192 changes: $parsed.find( '.mw-changeslist' ).first().contents(),
193 // Fieldset
194 fieldset: $parsed.find( 'fieldset.rcoptions' ).first()
195 };
196 },
197 // Failure
198 function ( responseObj ) {
199 var $parsed;
200
201 if ( !latestRequest() ) {
202 return $.Deferred().reject();
203 }
204
205 $parsed = $( $.parseHTML( responseObj.responseText ) );
206
207 // Force a resolve state to this promise
208 return $.Deferred().resolve( {
209 changes: 'NO_RESULTS',
210 fieldset: $parsed.find( 'fieldset.rcoptions' ).first()
211 } ).promise();
212 }
213 );
214 };
215
216 /**
217 * Update the list of changes and notify the model
218 *
219 * @param {Object} [params] Extra parameters to add to the API call
220 */
221 mw.rcfilters.Controller.prototype.updateChangesList = function ( params ) {
222 this.updateURL( params );
223 this.changesListModel.invalidate();
224 this.fetchChangesList()
225 .then(
226 // Success
227 function ( pieces ) {
228 var $changesListContent = pieces.changes,
229 $fieldset = pieces.fieldset;
230 this.changesListModel.update( $changesListContent, $fieldset );
231 }.bind( this )
232 // Do nothing for failure
233 );
234 };
235
236 /**
237 * Toggle the highlight feature on and off
238 */
239 mw.rcfilters.Controller.prototype.toggleHighlight = function () {
240 this.filtersModel.toggleHighlight();
241 this.updateURL();
242 };
243
244 /**
245 * Set the highlight color for a filter item
246 *
247 * @param {string} filterName Name of the filter item
248 * @param {string} color Selected color
249 */
250 mw.rcfilters.Controller.prototype.setHighlightColor = function ( filterName, color ) {
251 this.filtersModel.setHighlightColor( filterName, color );
252 this.updateURL();
253 };
254
255 /**
256 * Clear highlight for a filter item
257 *
258 * @param {string} filterName Name of the filter item
259 */
260 mw.rcfilters.Controller.prototype.clearHighlightColor = function ( filterName ) {
261 this.filtersModel.clearHighlightColor( filterName );
262 this.updateURL();
263 };
264
265 /**
266 * Clear both highlight and selection of a filter
267 *
268 * @param {string} filterName Name of the filter item
269 */
270 mw.rcfilters.Controller.prototype.clearFilter = function ( filterName ) {
271 var filterItem = this.filtersModel.getItemByName( filterName );
272
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 );
278 }
279 };
280
281 /**
282 * Synchronize the URL with the current state of the filters
283 * without adding an history entry.
284 */
285 mw.rcfilters.Controller.prototype.replaceUrl = function () {
286 window.history.replaceState(
287 { tag: 'rcfilters' },
288 document.title,
289 this.getUpdatedUri().toString()
290 );
291 };
292 }( mediaWiki, jQuery ) );