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