2 * JavaScript for Special:RecentChanges
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
} );
20 // eslint-disable-next-line no-new
21 new mw
.rcfilters
.ui
.ChangesListWrapperWidget(
22 filtersModel
, changesListModel
, $( '.mw-changeslist, .mw-changeslist-empty' ) );
24 // eslint-disable-next-line no-new
25 new mw
.rcfilters
.ui
.FormWrapperWidget(
26 changesListModel
, controller
, $( 'fieldset.rcoptions' ) );
28 controller
.initialize( {
30 title
: mw
.msg( 'rcfilters-filtergroup-registration' ),
31 type
: 'send_unselected_if_any',
36 label
: mw
.msg( 'rcfilters-filter-registered-label' ),
37 description
: mw
.msg( 'rcfilters-filter-registered-description' ),
38 'class': 'mw-changeslist-liu'
42 label
: mw
.msg( 'rcfilters-filter-unregistered-label' ),
43 description
: mw
.msg( 'rcfilters-filter-unregistered-description' ),
44 'class': 'mw-changeslist-anon'
49 title
: mw
.msg( 'rcfilters-filtergroup-userExpLevel' ),
50 // Type 'string_options' means that the group is evaluated by
51 // string values separated by comma; for example, param=opt1,opt2
52 // If all options are selected they are replaced by the term "all".
53 // The filters are the values for the parameter defined by the group.
54 // ** In this case, the parameter name is the group name. **
55 type
: 'string_options',
61 label
: mw
.msg( 'rcfilters-filter-userExpLevel-newcomer-label' ),
62 description
: mw
.msg( 'rcfilters-filter-userExpLevel-newcomer-description' ),
63 conflicts
: [ 'hideanons' ],
64 'class': 'mw-changeslist-user-newcomer'
68 label
: mw
.msg( 'rcfilters-filter-userExpLevel-learner-label' ),
69 description
: mw
.msg( 'rcfilters-filter-userExpLevel-learner-description' ),
70 conflicts
: [ 'hideanons' ],
71 'class': 'mw-changeslist-user-learner'
75 label
: mw
.msg( 'rcfilters-filter-userExpLevel-experienced-label' ),
76 description
: mw
.msg( 'rcfilters-filter-userExpLevel-experienced-description' ),
77 conflicts
: [ 'hideanons' ],
78 'class': 'mw-changeslist-user-experienced'
83 title
: mw
.msg( 'rcfilters-filtergroup-authorship' ),
84 // Type 'send_unselected_if_any' means that the controller will go over
85 // all unselected filters in the group and use their parameters
86 // as truthy in the query string.
87 // This is to handle the "negative" filters. We are showing users
88 // a positive message ("Show xxx") but the filters themselves are
89 // based on "hide YYY". The purpose of this is to correctly map
90 // the functionality to the UI, whether we are dealing with 2
91 // parameters in the group or more.
92 type
: 'send_unselected_if_any',
97 label
: mw
.msg( 'rcfilters-filter-editsbyself-label' ),
98 description
: mw
.msg( 'rcfilters-filter-editsbyself-description' ),
99 'class': 'mw-changeslist-self'
102 name
: 'hidebyothers',
103 label
: mw
.msg( 'rcfilters-filter-editsbyother-label' ),
104 description
: mw
.msg( 'rcfilters-filter-editsbyother-description' ),
105 'class': 'mw-changeslist-others'
110 title
: mw
.msg( 'rcfilters-filtergroup-automated' ),
111 type
: 'send_unselected_if_any',
116 label
: mw
.msg( 'rcfilters-filter-bots-label' ),
117 description
: mw
.msg( 'rcfilters-filter-bots-description' ),
119 'class': 'mw-changeslist-bot'
123 label
: mw
.msg( 'rcfilters-filter-humans-label' ),
124 description
: mw
.msg( 'rcfilters-filter-humans-description' ),
126 'class': 'mw-changeslist-human'
131 title
: mw
.msg( 'rcfilters-filtergroup-significance' ),
132 type
: 'send_unselected_if_any',
137 label
: mw
.msg( 'rcfilters-filter-minor-label' ),
138 description
: mw
.msg( 'rcfilters-filter-minor-description' ),
139 'class': 'mw-changeslist-minor'
143 label
: mw
.msg( 'rcfilters-filter-major-label' ),
144 description
: mw
.msg( 'rcfilters-filter-major-description' ),
145 'class': 'mw-changeslist-major'
150 title
: mw
.msg( 'rcfilters-filtergroup-changetype' ),
151 type
: 'send_unselected_if_any',
155 name
: 'hidepageedits',
156 label
: mw
.msg( 'rcfilters-filter-pageedits-label' ),
157 description
: mw
.msg( 'rcfilters-filter-pageedits-description' ),
159 'class': 'mw-changeslist-src-mw-edit'
163 name
: 'hidenewpages',
164 label
: mw
.msg( 'rcfilters-filter-newpages-label' ),
165 description
: mw
.msg( 'rcfilters-filter-newpages-description' ),
167 'class': 'mw-changeslist-src-mw-new'
170 name
: 'hidecategorization',
171 label
: mw
.msg( 'rcfilters-filter-categorization-label' ),
172 description
: mw
.msg( 'rcfilters-filter-categorization-description' ),
174 'class': 'mw-changeslist-src-mw-categorize'
178 label
: mw
.msg( 'rcfilters-filter-logactions-label' ),
179 description
: mw
.msg( 'rcfilters-filter-logactions-description' ),
181 'class': 'mw-changeslist-src-mw-log'
187 $( '.rcfilters-container' ).append( filtersWidget
.$element
);
188 $( 'body' ).append( $overlay
);
190 // HACK: Remove old-style filter links for filters handled by the widget
191 // Ideally the widget would handle all filters and we'd just remove .rcshowhide entirely
192 $( '.rcshowhide' ).children().each( function () {
193 // HACK: Interpret the class name to get the filter name
194 // This should really be set as a data attribute
197 // Some of the older browsers we support don't have .classList,
198 // so we have to interpret the class attribute manually.
199 classes
= this.getAttribute( 'class' ).split( ' ' );
200 for ( i
= 0; i
< classes
.length
; i
++ ) {
201 if ( classes
[ i
].substr( 0, 'rcshow'.length
) === 'rcshow' ) {
202 name
= classes
[ i
].substr( 'rcshow'.length
);
206 if ( name
=== null ) {
209 if ( name
=== 'hidemine' ) {
210 // HACK: the span for hidemyself is called hidemine
213 // This span corresponds to a filter that's in our model, so remove it
214 if ( filtersModel
.getItemByName( name
) ) {
215 // HACK: Remove the text node after the span.
216 // If there isn't one, we're at the end, so remove the text node before the span.
217 // This would be unnecessary if we added separators with CSS.
218 if ( this.nextSibling
&& this.nextSibling
.nodeType
=== Node
.TEXT_NODE
) {
219 this.parentNode
.removeChild( this.nextSibling
);
220 } else if ( this.previousSibling
&& this.previousSibling
.nodeType
=== Node
.TEXT_NODE
) {
221 this.parentNode
.removeChild( this.previousSibling
);
223 // Remove the span itself
224 this.parentNode
.removeChild( this );
228 window
.addEventListener( 'popstate', function () {
229 controller
.updateChangesList();
232 $( 'a.mw-helplink' ).attr(
234 'https://www.mediawiki.org/wiki/Special:MyLanguage/Help:New_filters_for_edit_review'
241 module
.exports
= rcfilters
;
243 }( mediaWiki
, jQuery
) );