Merge "RCFilters UI: Select filter when searching and add it on 'enter'"
[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 // Check all filter interactions
63 this.filtersModel.reassessFilterInteractions();
64
65 this.updateChangesList();
66 };
67
68 /**
69 * Empty all selected filters
70 */
71 mw.rcfilters.Controller.prototype.emptyFilters = function () {
72 this.filtersModel.emptyAllFilters();
73 this.filtersModel.clearAllHighlightColors();
74 // Check all filter interactions
75 this.filtersModel.reassessFilterInteractions();
76
77 this.updateChangesList();
78 };
79
80 /**
81 * Update the selected state of a filter
82 *
83 * @param {string} filterName Filter name
84 * @param {boolean} [isSelected] Filter selected state
85 */
86 mw.rcfilters.Controller.prototype.toggleFilterSelect = function ( filterName, isSelected ) {
87 var obj = {},
88 filterItem = this.filtersModel.getItemByName( filterName );
89
90 isSelected = isSelected === undefined ? !filterItem.isSelected() : isSelected;
91
92 if ( filterItem.isSelected() !== isSelected ) {
93 obj[ filterName ] = isSelected;
94 this.filtersModel.updateFilters( obj );
95
96 this.updateChangesList();
97
98 // Check filter interactions
99 this.filtersModel.reassessFilterInteractions( this.filtersModel.getItemByName( filterName ) );
100 }
101 };
102
103 /**
104 * Update the URL of the page to reflect current filters
105 *
106 * This should not be called directly from outside the controller.
107 * If an action requires changing the URL, it should either use the
108 * highlighting actions below, or call #updateChangesList which does
109 * the uri corrections already.
110 *
111 * @private
112 * @param {Object} [params] Extra parameters to add to the API call
113 */
114 mw.rcfilters.Controller.prototype.updateURL = function ( params ) {
115 var uri;
116
117 params = params || {};
118
119 uri = this.getUpdatedUri();
120 uri.extend( params );
121
122 window.history.pushState( { tag: 'rcfilters' }, document.title, uri.toString() );
123 };
124
125 /**
126 * Get an updated mw.Uri object based on the model state
127 *
128 * @return {mw.Uri} Updated Uri
129 */
130 mw.rcfilters.Controller.prototype.getUpdatedUri = function () {
131 var uri = new mw.Uri(),
132 highlightParams = this.filtersModel.getHighlightParameters();
133
134 // Add to existing queries in URL
135 // TODO: Clean up the list of filters; perhaps 'falsy' filters
136 // shouldn't appear at all? Or compare to existing query string
137 // and see if current state of a specific filter is needed?
138 uri.extend( this.filtersModel.getParametersFromFilters() );
139
140 // highlight params
141 Object.keys( highlightParams ).forEach( function ( paramName ) {
142 if ( highlightParams[ paramName ] ) {
143 uri.query[ paramName ] = highlightParams[ paramName ];
144 } else {
145 delete uri.query[ paramName ];
146 }
147 } );
148
149 return uri;
150 };
151
152 /**
153 * Fetch the list of changes from the server for the current filters
154 *
155 * @return {jQuery.Promise} Promise object that will resolve with the changes list
156 * or with a string denoting no results.
157 */
158 mw.rcfilters.Controller.prototype.fetchChangesList = function () {
159 var uri = this.getUpdatedUri(),
160 requestId = ++this.requestCounter,
161 latestRequest = function () {
162 return requestId === this.requestCounter;
163 }.bind( this );
164
165 return $.ajax( uri.toString(), { contentType: 'html' } )
166 .then(
167 // Success
168 function ( html ) {
169 var $parsed;
170 if ( !latestRequest() ) {
171 return $.Deferred().reject();
172 }
173
174 $parsed = $( $.parseHTML( html ) );
175
176 return {
177 // Changes list
178 changes: $parsed.find( '.mw-changeslist' ).first().contents(),
179 // Fieldset
180 fieldset: $parsed.find( 'fieldset.rcoptions' ).first()
181 };
182 },
183 // Failure
184 function ( responseObj ) {
185 var $parsed;
186
187 if ( !latestRequest() ) {
188 return $.Deferred().reject();
189 }
190
191 $parsed = $( $.parseHTML( responseObj.responseText ) );
192
193 // Force a resolve state to this promise
194 return $.Deferred().resolve( {
195 changes: 'NO_RESULTS',
196 fieldset: $parsed.find( 'fieldset.rcoptions' ).first()
197 } ).promise();
198 }
199 );
200 };
201
202 /**
203 * Update the list of changes and notify the model
204 *
205 * @param {Object} [params] Extra parameters to add to the API call
206 */
207 mw.rcfilters.Controller.prototype.updateChangesList = function ( params ) {
208 this.updateURL( params );
209 this.changesListModel.invalidate();
210 this.fetchChangesList()
211 .then(
212 // Success
213 function ( pieces ) {
214 var $changesListContent = pieces.changes,
215 $fieldset = pieces.fieldset;
216
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 ) );