MediaWiki:Gadget-readableRC-core.js
Jump to navigation
Jump to search
Note: After publishing, you may have to bypass your browser's cache to see the changes.
- Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
- Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
- Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
- Opera: Press Ctrl-F5.
1 // <nowiki>
2 // Formats the rows on Special:RecentChanges where all the information runs together
3 // into three columns (page, diff/byte change, and user links) to make it more readable
4 //
5 // @author Iiii_I_I_I
6 //
7 // @TODO: support "Group changes by page" option being unchecked in Special:Preferences?
8
9 (function ($, mw) {
10 function runReadableRC($content) {
11 if (!$content.hasClass('mw-changeslist')) {
12 return;
13 }
14
15 $content.addClass('gadget-rc-enabled');
16
17 var rows = document.querySelectorAll('.mw-changeslist-line, ' + // does not include grouped edits
18 '.mw-rcfilters-ui-highlights-enhanced-toplevel, ' + // first child in grouped edits
19 '.mw-rcfilters-ui-highlights-enhanced-nested'); // subsequent children in grouped edits
20
21 for (var i = 0; i < rows.length; i++) {
22 var row = rows[i];
23
24 // nested rows (only visible after expanding grouped edits)
25 if (row.classList.contains('mw-rcfilters-ui-highlights-enhanced-nested')) {
26 row.classList.add('gadget-rc-nested');
27
28 if (row.classList.contains('mw-changeslist-edit')) {
29 cleanNestedEdit(row);
30 }
31 else if (row.classList.contains('mw-changeslist-log')) {
32 cleanNestedLog(row);
33 }
34 }
35 // top-level rows (visible without uncollapsing any groups)
36 else {
37 // ignore parent element of grouped edits (text is in children, not container)
38 if (row.classList.contains('mw-collapsible')) {
39 continue;
40 }
41
42 // top-level row in grouped edits/log entries
43 if (row.classList.contains('mw-rcfilters-ui-highlights-enhanced-toplevel')) {
44 var parent = row.closest('.mw-collapsible');
45
46 if (parent.classList.contains('mw-changeslist-edit')) {
47 cleanMultipleEdits(row);
48 }
49 else if (parent.classList.contains('mw-changeslist-log')) {
50 cleanMultipleLogs(row);
51 }
52 }
53 // row for single edit/log entries
54 else {
55 if (row.classList.contains('mw-changeslist-edit')) {
56 cleanSingleEdit(row);
57 }
58 else if (row.classList.contains('mw-changeslist-log')) {
59 cleanSingleLog(row);
60 }
61 }
62 }
63 }
64 }
65
66 function insert(row, newNode, referenceNode) {
67 return row.querySelector(referenceNode).parentNode.insertBefore(row.querySelector(newNode), row.querySelector(referenceNode));
68 }
69
70 function wrap(referenceNode, wrapperEl, wrapperClass) {
71 var wrapper = document.createElement(wrapperEl);
72
73 referenceNode.parentNode.insertBefore(wrapper, referenceNode);
74 wrapper.appendChild(referenceNode);
75 wrapper.classList.add(wrapperClass);
76 }
77
78 function cleanNestedLog(row) {
79 // wrap "log name" (actually timestamp) together
80 wrap(row.querySelector('.mw-enhanced-rc-time'), 'td', 'gadget-rc-logname');
81
82 // placeholder column with separator dots
83 wrap(row.querySelector('.mw-changeslist-separator'), 'td', 'gadget-rc-logdots');
84
85 cleanUserLinks(row);
86
87 // rename <td>
88 row.querySelector('td.mw-enhanced-rc-nested').className = 'gadget-rc-logentry';
89
90 // rearrange newly created <td>s
91 insert(row, '.gadget-rc-logname', '.gadget-rc-logentry');
92 insert(row, '.gadget-rc-logdots', '.gadget-rc-logentry');
93 }
94
95 function cleanNestedEdit(row) {
96 // wrap "page name" (actually revision links) together
97 wrap(row.querySelector('.mw-enhanced-rc-time'), 'td', 'gadget-rc-pagename');
98
99 // remove two dot separators
100 row.querySelector('.mw-changeslist-separator').remove();
101 row.querySelector('.mw-changeslist-separator').remove();
102
103 // wrap user stuff together
104 $('.mw-userlink, .mw-usertoollinks, .comment, .mw-rollback-link, .mw-tag-markers, .history-deleted', row).wrapAll('<td class="gadget-rc-userlinks" />');
105
106 cleanUserLinks(row);
107
108 // "rollback x edit(s)" -> "rollback"
109 // link does not exist if it is a page creation or user does not have the right
110 if (row.querySelector('.mw-rollback-link')) {
111 row.querySelector('.mw-rollback-link a').textContent = 'rollback';
112 }
113
114 // rename <td>
115 row.querySelector('td.mw-enhanced-rc-nested').className = 'gadget-rc-diff';
116
117 // rearrange newly created <td>s
118 insert(row, '.gadget-rc-userlinks', '.gadget-rc-diff');
119 insert(row, '.gadget-rc-pagename', '.gadget-rc-userlinks');
120 insert(row, '.gadget-rc-diff', '.gadget-rc-userlinks');
121 }
122
123 function cleanMultipleLogs(row) {
124 // log name
125 wrap(row.querySelector('.mw-rc-unwatched'), 'td', 'gadget-rc-logname');
126
127 // remove square brackets from grouped usernames; cannot simply use remove()
128 // since there might be other text in the same node, eg. "(4×)]"
129 var users = row.querySelector('.changedby').childNodes;
130
131 users[0].textContent = users[0].textContent.slice(1); // [
132 users[users.length - 1].textContent = users[users.length - 1].textContent.slice(0, -1); // ]
133
134 // placeholder column with separator dots
135 wrap(row.querySelector('.mw-changeslist-separator'), 'td', 'gadget-rc-logdots');
136
137 // rename <td>
138 row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-logentry';
139
140 // rearrange newly created <td>s
141 insert(row, '.gadget-rc-logname', '.gadget-rc-logentry');
142 insert(row, '.gadget-rc-logdots', '.gadget-rc-logentry');
143
144 // stupid empty node
145 row.querySelector('.gadget-rc-logentry').childNodes[0].remove();
146 }
147
148 function cleanMultipleEdits(row) {
149 // page name
150 wrap(row.querySelector('.mw-title'), 'td', 'gadget-rc-pagename');
151
152 // "x changes" -> "diff"
153 if (row.querySelector('.mw-changeslist-groupdiff')) {
154 row.querySelector('.mw-changeslist-groupdiff').textContent = 'diff';
155 }
156 // new pages have a text node instead of a link
157 else {
158 row.querySelector('.mw-changeslist-links span:first-child').textContent = 'diff';
159 }
160
161 // "history" -> "hist"
162 if (row.querySelector('.mw-changeslist-history')) {
163 row.querySelector('.mw-changeslist-history').textContent = 'hist';
164 }
165 // nonexistent pages (redirect-suppressed move or deleted) have a text node instead of a link
166 else {
167 var newHist = row.querySelector('.mw-changeslist-line-inner').childNodes[4].nodeValue.replace('history', 'hist');
168
169 row.querySelector('.mw-changeslist-line-inner').childNodes[4].nodeValue = newHist;
170 }
171
172 // list of user(s)
173 wrap(row.querySelector('.changedby'), 'td', 'gadget-rc-userlinks');
174
175 // remove square brackets from grouped usernames; cannot simply use remove()
176 // since there might be other text in the same node, eg. "(4×)]"
177 var users = row.querySelector('.changedby').childNodes;
178
179 users[0].textContent = users[0].textContent.slice(1); // [
180 users[users.length - 1].textContent = users[users.length - 1].textContent.slice(0, -1); // ]
181
182 // rename <td>
183 row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-diff';
184
185 // rearrange newly created <td>s
186 insert(row, '.gadget-rc-userlinks', '.gadget-rc-diff');
187 insert(row, '.gadget-rc-pagename', '.gadget-rc-userlinks');
188 insert(row, '.gadget-rc-diff', '.gadget-rc-userlinks');
189 }
190
191 function cleanSingleLog(row) {
192 // log name
193 wrap(row.querySelector('.mw-changeslist-line-inner-logLink'), 'td', 'gadget-rc-logname');
194
195 // placeholder column with separator dots
196 wrap(row.querySelector('.mw-changeslist-line-inner-separatorAfterLinks'), 'td', 'gadget-rc-logdots');
197
198 cleanUserLinks(row);
199
200 // rename <td>
201 row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-logentry';
202
203 // rearrange newly created <td>s
204 insert(row, '.gadget-rc-logname', '.gadget-rc-logentry');
205 insert(row, '.gadget-rc-logdots', '.gadget-rc-logentry');
206 }
207
208 function cleanSingleEdit(row) {
209 // page name
210 wrap(row.querySelector('.mw-title'), 'td', 'gadget-rc-pagename');
211
212 // "rollback x edit(s)" -> "rollback"
213 // link does not exist if it is a page creation or user does not have the right
214 if (row.querySelector('.mw-rollback-link')) {
215 row.querySelector('.mw-rollback-link a').textContent = 'rollback';
216 }
217
218 // user info
219 $('.mw-userlink, .mw-usertoollinks, .comment, .mw-rollback-link, .mw-tag-markers, .history-deleted', row).wrapAll('<td class="gadget-rc-userlinks" />');
220
221 cleanUserLinks(row);
222
223 // rename <td>
224 row.querySelector('.mw-changeslist-line-inner').className = 'gadget-rc-diff';
225
226 // rearrange newly created <td>s
227 insert(row, '.gadget-rc-userlinks', '.gadget-rc-diff');
228 insert(row, '.gadget-rc-pagename', '.gadget-rc-userlinks');
229 insert(row, '.gadget-rc-diff', '.gadget-rc-userlinks');
230 }
231
232 // (talk | contribs | block) -> (t|c|b)
233 // possible variations: (talk), (talk | contribs), (talk | block), (talk | contribs | block), (username removed)
234 function cleanUserLinks(row) {
235 // if username has been revdeled (shows "(username removed)"), then exit
236 if (row.querySelector('.history-deleted')) {
237 return;
238 }
239
240 row.querySelector('.mw-usertoollinks-talk').textContent = 't';
241
242 // IPs don't have dedicated contribs link - it's linked in their username
243 if (row.querySelector('.mw-usertoollinks-contribs')) {
244 row.querySelector('.mw-usertoollinks-contribs').textContent = 'c';
245 }
246
247 // non-admins don't have block link
248 if (row.querySelector('.mw-usertoollinks-block')) {
249 row.querySelector('.mw-usertoollinks-block').textContent = 'b';
250 }
251 }
252
253 function init() {
254 var info = new OO.ui.PopupButtonWidget({
255 classes: ['gadget-rc-button'],
256 framed: false,
257 label: 'Broken?',
258 popup: {
259 head: true,
260 label: 'Is the RecentChanges layout broken?',
261 $content: $("<p><a href='/w/MediaWiki:Gadget-readableRC-core.js'>ReadableRC</a>, which formats this " +
262 "page into columns, might have an error. If something looks wrong, disable this gadget in " +
263 "your preferences and report the issue to <a href='/w/User_talk:Iiii_I_I_I'>Iiii I I I</a>.</p>"),
264 padded: true,
265 align: 'force-right',
266 hideCloseButton: true
267 }
268 })
269
270 mw.hook('structuredChangeFilters.ui.initialized').add(function () {
271 runReadableRC($('.mw-changeslist'));
272 mw.hook('wikipage.content').add(runReadableRC);
273 $('.mw-rcfilters-ui-liveUpdateButtonWidget').append(info.$element);
274 });
275 }
276
277 $(init);
278 })(jQuery, mediaWiki);
279
280 // </nowiki>