MediaWiki:Gadget-readableRC-core.js

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>