Merge "OutputPage: Support UploadPath in testTransformResourcePath()"
[lhc/web/wiklou.git] / resources / src / mediawiki.rcfilters / mw.rcfilters.init.js
1 /*!
2 * JavaScript for Special:RecentChanges
3 */
4 ( function ( mw, $ ) {
5 /**
6 * @class mw.rcfilters
7 * @singleton
8 */
9 var rcfilters = {
10 /** */
11 init: function () {
12 var filtersModel = new mw.rcfilters.dm.FiltersViewModel(),
13 changesListModel = new mw.rcfilters.dm.ChangesListViewModel(),
14 controller = new mw.rcfilters.Controller( filtersModel, changesListModel ),
15 $overlay = $( '<div>' )
16 .addClass( 'mw-rcfilters-ui-overlay' ),
17 filtersWidget = new mw.rcfilters.ui.FilterWrapperWidget(
18 controller, filtersModel, { $overlay: $overlay } );
19
20 // eslint-disable-next-line no-new
21 new mw.rcfilters.ui.ChangesListWrapperWidget(
22 changesListModel, $( '.mw-changeslist, .mw-changeslist-empty' ) );
23
24 // eslint-disable-next-line no-new
25 new mw.rcfilters.ui.FormWrapperWidget(
26 changesListModel, $( '.rcoptions form' ) );
27
28 controller.initialize( {
29 registration: {
30 title: mw.msg( 'rcfilters-filtergroup-registration' ),
31 type: 'send_unselected_if_any',
32 fullCoverage: true,
33 filters: [
34 {
35 name: 'hideliu',
36 label: mw.msg( 'rcfilters-filter-registered-label' ),
37 description: mw.msg( 'rcfilters-filter-registered-description' )
38 },
39 {
40 name: 'hideanons',
41 label: mw.msg( 'rcfilters-filter-unregistered-label' ),
42 description: mw.msg( 'rcfilters-filter-unregistered-description' )
43 }
44 ]
45 },
46 userExpLevel: {
47 title: mw.msg( 'rcfilters-filtergroup-userExpLevel' ),
48 // Type 'string_options' means that the group is evaluated by
49 // string values separated by comma; for example, param=opt1,opt2
50 // If all options are selected they are replaced by the term "all".
51 // The filters are the values for the parameter defined by the group.
52 // ** In this case, the parameter name is the group name. **
53 type: 'string_options',
54 separator: ',',
55 fullCoverage: false,
56 filters: [
57 {
58 name: 'newcomer',
59 label: mw.msg( 'rcfilters-filter-userExpLevel-newcomer-label' ),
60 description: mw.msg( 'rcfilters-filter-userExpLevel-newcomer-description' ),
61 conflicts: [ 'hideanons' ]
62 },
63 {
64 name: 'learner',
65 label: mw.msg( 'rcfilters-filter-userExpLevel-learner-label' ),
66 description: mw.msg( 'rcfilters-filter-userExpLevel-learner-description' ),
67 conflicts: [ 'hideanons' ]
68 },
69 {
70 name: 'experienced',
71 label: mw.msg( 'rcfilters-filter-userExpLevel-experienced-label' ),
72 description: mw.msg( 'rcfilters-filter-userExpLevel-experienced-description' ),
73 conflicts: [ 'hideanons' ]
74 }
75 ]
76 },
77 authorship: {
78 title: mw.msg( 'rcfilters-filtergroup-authorship' ),
79 // Type 'send_unselected_if_any' means that the controller will go over
80 // all unselected filters in the group and use their parameters
81 // as truthy in the query string.
82 // This is to handle the "negative" filters. We are showing users
83 // a positive message ("Show xxx") but the filters themselves are
84 // based on "hide YYY". The purpose of this is to correctly map
85 // the functionality to the UI, whether we are dealing with 2
86 // parameters in the group or more.
87 type: 'send_unselected_if_any',
88 fullCoverage: true,
89 filters: [
90 {
91 name: 'hidemyself',
92 label: mw.msg( 'rcfilters-filter-editsbyself-label' ),
93 description: mw.msg( 'rcfilters-filter-editsbyself-description' )
94 },
95 {
96 name: 'hidebyothers',
97 label: mw.msg( 'rcfilters-filter-editsbyother-label' ),
98 description: mw.msg( 'rcfilters-filter-editsbyother-description' )
99 }
100 ]
101 },
102 automated: {
103 title: mw.msg( 'rcfilters-filtergroup-automated' ),
104 type: 'send_unselected_if_any',
105 fullCoverage: true,
106 filters: [
107 {
108 name: 'hidebots',
109 label: mw.msg( 'rcfilters-filter-bots-label' ),
110 description: mw.msg( 'rcfilters-filter-bots-description' ),
111 'default': true
112 },
113 {
114 name: 'hidehumans',
115 label: mw.msg( 'rcfilters-filter-humans-label' ),
116 description: mw.msg( 'rcfilters-filter-humans-description' ),
117 'default': false
118 }
119 ]
120 },
121 significance: {
122 title: mw.msg( 'rcfilters-filtergroup-significance' ),
123 type: 'send_unselected_if_any',
124 fullCoverage: true,
125 filters: [
126 {
127 name: 'hideminor',
128 label: mw.msg( 'rcfilters-filter-minor-label' ),
129 description: mw.msg( 'rcfilters-filter-minor-description' )
130 },
131 {
132 name: 'hidemajor',
133 label: mw.msg( 'rcfilters-filter-major-label' ),
134 description: mw.msg( 'rcfilters-filter-major-description' )
135 }
136 ]
137 },
138 changetype: {
139 title: mw.msg( 'rcfilters-filtergroup-changetype' ),
140 type: 'send_unselected_if_any',
141 fullCoverage: true,
142 filters: [
143 {
144 name: 'hidepageedits',
145 label: mw.msg( 'rcfilters-filter-pageedits-label' ),
146 description: mw.msg( 'rcfilters-filter-pageedits-description' ),
147 'default': false
148 },
149 {
150 name: 'hidenewpages',
151 label: mw.msg( 'rcfilters-filter-newpages-label' ),
152 description: mw.msg( 'rcfilters-filter-newpages-description' ),
153 'default': false
154 },
155 {
156 name: 'hidecategorization',
157 label: mw.msg( 'rcfilters-filter-categorization-label' ),
158 description: mw.msg( 'rcfilters-filter-categorization-description' ),
159 'default': true
160 },
161 {
162 name: 'hidelog',
163 label: mw.msg( 'rcfilters-filter-logactions-label' ),
164 description: mw.msg( 'rcfilters-filter-logactions-description' ),
165 'default': false
166 }
167 ]
168 }
169 } );
170
171 $( '.rcoptions' ).before( filtersWidget.$element );
172 $( 'body' ).append( $overlay );
173
174 // HACK: Remove old-style filter links for filters handled by the widget
175 // Ideally the widget would handle all filters and we'd just remove .rcshowhide entirely
176 $( '.rcshowhide' ).children().each( function () {
177 // HACK: Interpret the class name to get the filter name
178 // This should really be set as a data attribute
179 var i,
180 name = null,
181 // Some of the older browsers we support don't have .classList,
182 // so we have to interpret the class attribute manually.
183 classes = this.getAttribute( 'class' ).split( ' ' );
184 for ( i = 0; i < classes.length; i++ ) {
185 if ( classes[ i ].substr( 0, 'rcshow'.length ) === 'rcshow' ) {
186 name = classes[ i ].substr( 'rcshow'.length );
187 break;
188 }
189 }
190 if ( name === null ) {
191 return;
192 }
193 if ( name === 'hidemine' ) {
194 // HACK: the span for hidemyself is called hidemine
195 name = 'hidemyself';
196 }
197 // This span corresponds to a filter that's in our model, so remove it
198 if ( filtersModel.getItemByName( name ) ) {
199 // HACK: Remove the text node after the span.
200 // If there isn't one, we're at the end, so remove the text node before the span.
201 // This would be unnecessary if we added separators with CSS.
202 if ( this.nextSibling && this.nextSibling.nodeType === Node.TEXT_NODE ) {
203 this.parentNode.removeChild( this.nextSibling );
204 } else if ( this.previousSibling && this.previousSibling.nodeType === Node.TEXT_NODE ) {
205 this.parentNode.removeChild( this.previousSibling );
206 }
207 // Remove the span itself
208 this.parentNode.removeChild( this );
209 }
210 } );
211
212 window.addEventListener( 'popstate', function () {
213 controller.updateFromURL();
214 controller.updateChangesList();
215 } );
216 }
217 };
218
219 $( rcfilters.init );
220
221 module.exports = rcfilters;
222
223 }( mediaWiki, jQuery ) );