Benutzer:Lustiger seth/list2table.js
Erscheinungsbild
Hinweis: Leere nach dem Veröffentlichen den Browser-Cache, um die Änderungen sehen zu können.
- Firefox/Safari: Umschalttaste drücken und gleichzeitig Aktualisieren anklicken oder entweder Strg+F5 oder Strg+R (⌘+R auf dem Mac) drücken
- Google Chrome: Umschalttaste+Strg+R (⌘+Umschalttaste+R auf dem Mac) drücken
- Edge: Strg+F5 drücken oder Strg drücken und gleichzeitig Aktualisieren anklicken
/*
task: replace lists with tables in watchlist/history/contribs and some other logs
and add input fields for filtering per column
german docs: [[w:de:user:lustiger_seth/list2table]]
requirements: tested in firefox and with vector (2022) only and mainly in dewiki with english interface.
usage:
mw.loader.load('//de.wikipedia.org/w/index.php?title=User:lustiger_seth/list2table.js&action=raw&ctype=text/javascript');
or via [[m:user:lustiger_seth/global.js]]
*/
(function (){
'use strict';
function add_filters_to_table(table){
const filters = table.querySelectorAll('thead input');
filters.forEach((input, index) => {
input.addEventListener('input', function () {
applyFilters(table, filters);
});
});
}
function applyFilters(table, filters){
const rows = table.querySelectorAll('tbody tr');
rows.forEach(row => {
let display = '';
filters.forEach((input, index) => {
const filterValue = input.value;
const cell = row.cells[index];
if(cell){
const match = filterValue.match(/^\/(.*)\/(i?)$/);
if(match){
const re = new RegExp(match[1], match[2]);
if(!re.test(cell.textContent)){
display = 'none';
}
}else if(!cell.textContent.includes(filterValue)){
display = 'none';
}
}
});
row.style.display = display;
});
}
function flatten_log_entries(elem){
let log_entry = elem.querySelector('.mw-changeslist-log-entry');
let moep = false;
if(log_entry !== null){
while(log_entry.querySelector('bdi')){
// remove bdi elements, but keep content
letChildrenEatParent(log_entry.querySelector('bdi'));
}
// group some children containing the comment in new parent
const before_child = log_entry.querySelector('.mw-usertoollinks');
const last_child = log_entry.querySelector('.comment');
const newGroup = document.createElement('span');
let currentChild = before_child.nextSibling;
while(currentChild && currentChild !== last_child){
const nextSibling = currentChild.nextSibling;
newGroup.appendChild(currentChild);
currentChild = nextSibling;
}
newGroup.appendChild(last_child);
// move "diff" to front
log_entry.insertBefore(newGroup, before_child.nextSibling);
log_entry.innerHTML = log_entry.innerHTML.replace(
/\((<a [^>]+>diff<\/a>\s*\|\s*<a [^>]+>change visibility<\/a>)\)/,
'<span class="mw-changeslist-links">$1</span>');
// set page title as mw-title
log_entry.innerHTML = log_entry.innerHTML.replace(
/(.*changed visibility of a revision on page) (<a [^>]+>.*?<\/a>)\s*(:\s*content )/,
'<span class="mw-title">$2</span>$1$3');
// watchlist: log entries in deletion log lines
letChildrenEatParent(elem.querySelector('.mw-changeslist-log-entry'));
}
// watchlist: deletion log lines
letChildrenEatParent(elem.querySelector('.mw-changeslist-line-inner'));
}
function get_flagged_rev_class(elem){
let flaggedrevs_class = '';
elem.classList.forEach(c => {
if(c.startsWith("flaggedrevs")){
flaggedrevs_class = c;
}
});
if(flaggedrevs_class === '' && elem.children[0].className.startsWith("flaggedrevs")){
flaggedrevs_class = elem.children[0].className;
letChildrenEatParent(elem.querySelector('.' + flaggedrevs_class));
}
return flaggedrevs_class;
}
function get_list_patterns(){
const patterns = {
'classes': {
'plainlinks': {
name: "abuselog",
show_sections: false,
conditions: {
'date': {
'category_re': new RegExp(/^(?:<span>|)20.*?:\s*</),
're': /(20.*?):\s*</,
'filter': true,
},
'user': {
'category_re': new RegExp(/ class="[^"]*mw-userlink\b/),
'filter': true,
},
'user tools': {
'category_re': new RegExp(/class="mw-usertoollinks/),
're': /(<span class="mw-usertoollinks">.*<\/span>)/,
'post_process': function(str){
str.replace(/\((.*)\)/, '$1')
return post_process_user_tools(str);
},
},
'rule': {
'category_re': new RegExp(/^<a [^>]+>(?:global |)filter [0-9]+</),
'post_process': function(str){
return str.replace(/>global filter /, '>g')
.replace(/>filter /, '>');
},
},
'did': {
'category_re': new RegExp(/^(?:<span>|), performing the action "[^"]+" on/),
're': /performing the action "([^"]+)" on/,
'filter': true,
},
'page': {
'category_re': new RegExp(/^<a [^>]*\bhref="\/w\/index\.php\?title=(?:(?!&diff=prev)[^"])+"[^>]+\btitle="[^"]+"[^>]*>[^>]+<\/a>/),
'filter': true,
},
'action': {
'category_re': new RegExp(/^(?:<span>|)\.\s*Actions taken: [^;]+;\s*Filter description:/),
're': new RegExp(/Actions taken: ([^;]+);\s*Filter description:/),
'filter': true,
},
'description': {
'category_re': new RegExp(/^(?:<span>|)\.\s*Actions taken: [^;]+;\s*Filter description:\s*/),
're': new RegExp(/Actions taken: [^;]+;\s*Filter description:\s*(.*?) \(/),
'filter': true,
},
'tools': {
'category_re': new RegExp(/^<a href="\/wiki\/[^"]+(?:Logbuch|examine\/log)\/[0-9]+" title="[^"]+">[^>]+<\/a>/),
'post_process': function(str){
return str.replace(/>details<\/a>/, '>d</a>/')
.replace(/>examine</, '>e<');
},
},
},
},
'mw-contributions-list': {
name: "contribs",
show_sections: false,
conditions: {
'vis': {
'category_re': new RegExp(/ class="mw-revdelundel-link"/),
're': /\((.*)\)/,
'post_process': function(str){
return str.replace(/>(?:change visibility|\+\/−)</, '>vis<');
}
},
'time': {
'category_re': new RegExp(/ class="[^"]*mw-changeslist-date/),
'filter': true,
},
'rev': {
'category_re': new RegExp(/ class="mw-changeslist-links"/),
'post_process': function(str){
return '<nobr>' + str.replace(/<\/span>\s*<span>/, '<\/span>/<span>')
.replace(/Unterschied/, 'diff')
.replace(/Versionen/, 'hist')
+ '</nobr>';
},
},
'bytes': {
'category_re': new RegExp(/class="mw-plusminus-[a-z]+ mw-diff-bytes"/),
'filter': {
'width': '3em'
},
},
'flag': {
'category_re': new RegExp(/<abbr class="(?:minoredit|newpage)"|class="mw-uctop"/),
'post_process': function(str){
return str.replace(/>(?:current|aktuell)</, '>c<');
},
},
'page': {
'category_re': new RegExp(/ class="[^"]*mw-contributions-title"/),
'post_process': function(str){
return str.replace(/>Wikipedia:/, '>WP:')
.replace(/>Wikipedia Diskussion:/, '>WD:')
.replace(/>Benutzer(?:in|):/, '>user:')
.replace(/>Benutzer(?:in|) Diskussion:/, '>user talk:')
.replace(/>Diskussion:/, '>talk:')
.replace(/>Hilfe:/, '>H:')
.replace(/>Hilfe Diskussion:/, '>HD:')
.replace(/>Portal:/, '>P:')
.replace(/>Portal Diskussion:/, '>PD:');
},
'filter': true,
},
'comment': {
'category_re': new RegExp(/class="comment\b/),
'filter': true,
},
'edit tools': {
'category_re': new RegExp(/class="mw-changeslist-links[^"]+">/),
'post_process': function(str){ return post_process_edit_tools(str); },
},
'tags': {
'category_re': new RegExp(/class="mw-tag-markers"/),
'post_process': function(str){ return post_process_tags(str); },
},
},
global_css: function(){
const css_classes = [
'.comment--without-parentheses',
'.mw-changeslist-links',
'.mw-diff-bytes',
'.mw-uctop',
'.mw-rollback-link',
'.mw-tag-markers',
'.mw-changeslist-links > span:not(:first-child)'
];
css_classes.forEach(c => {
document.styleSheets[0].addRule(c + '::before','content: "";');
document.styleSheets[0].addRule(c + '::after','content: "";');
});
return true;
},
},
'special': {
name: "watchlist",
show_sections: true,
conditions: {
'rev': {
'category_re': new RegExp(/ class="mw-changeslist-links"/),
'post_process': function(str){
if(str.includes('/delete"')){
return str.replace(/>Deletion log</, '>log<')
.replace(/<\/a>\s*<span/, '</a>/<span')
.replace(/>change visibility</, '>vis<')
.replace(/<\/a>\s*\|\s*<a/, '</a>/<a');
}
return '<nobr>' + str.replace(/<\/span><span>/, '<\/span>/<span>') + '</nobr>';
},
},
'page': {
'category_re': new RegExp(/ class="mw-title"/),
'post_process': function(str){
return str.replace(/>Wikipedia:/, '>WP:')
.replace(/>Wikipedia Diskussion:/, '>WD:')
.replace(/>Benutzer(?:in|):/, '>user:')
.replace(/>Benutzer(?:in|) Diskussion:/, '>user talk:')
.replace(/>Diskussion:/, '>talk:')
.replace(/>Hilfe:/, '>H:')
.replace(/>Hilfe Diskussion:/, '>HD:')
.replace(/>Portal:/, '>P:')
.replace(/>Portal Diskussion:/, '>PD:');
},
'filter': true,
},
'time': {
'category_re': new RegExp(/ class="mw-changeslist-date mw-changeslist-time"/)
},
'bytes': {
'category_re': new RegExp(/ class="mw-plusminus-[a-z]+ mw-diff-bytes"/)
},
'user': {
'category_re': new RegExp(/ class="mw-userlink\b/),
'filter': true,
},
'u. links': {
'category_re': new RegExp(/class="mw-usertoollinks mw-changeslist-links"/),
're': /(<span class="mw-usertoollinks mw-changeslist-links">.*<\/span>)/,
'post_process': function(str){ return post_process_user_tools(str); },
},
'comment': {
'category_re': new RegExp(/class="comment\b/),
'post_process': function(str){
return str.replace(/>\((.*)\)</, '>$1<');
},
'filter': true,
},
'tags': {
'category_re': new RegExp(/class="mw-tag-markers"/),
'post_process': function(str){ return post_process_tags(str); },
},
'edit tools': {
'category_re': new RegExp(/class="mw-changeslist-links mw-pager-tools/),
'post_process': function(str){ return post_process_edit_tools(str); },
},
},
global_css: function(){
const css_classes = [
'.comment--without-parentheses',
'.mw-changeslist-links',
'.mw-diff-bytes',
'.mw-uctop',
'.mw-tag-markers',
'.mw-changeslist-links > span:not(:first-child)'
];
css_classes.forEach(c => {
document.styleSheets[0].addRule(c + '::before','content: "";');
document.styleSheets[0].addRule(c + '::after','content: "";');
});
return true;
},
},
},
'ids': {
'pagehistory': {
name: "history",
show_sections: false,
conditions: {
'cur/prev': {
'category_re': new RegExp(/class="mw-history-histlinks mw-changeslist-links"/),
'post_process': function(str){
return '<nobr>' + str.replace(/<\/span><span>/, '</span>/<span>')
.replace(/Aktuell/, 'jetzt')
.replace(/Vorherige/, 'vorher') + '</nobr>';
},
},
'sel': {
'category_re': new RegExp(/^<input[^>]+(?:name="(?:oldid|diff)"|form="mw-history-revisionactions")/),
'post_process': function(str){
return '<nobr>' + str.replace(/ disabled=""/, '') + '</nobr>';
},
},
'time': {
'category_re': new RegExp(/ class="[^"]*mw-changeslist-date"/),
'filter': true,
},
'user': {
'category_re': new RegExp(/class="history-user"/),
're': /(<a\s[^>]+><bdi>[^>]+<\/bdi>\s*<\/a>|<span [^>]+>\([^)]+\)<\/span>)/,
'filter': true,
},
'user tools': {
'category_re': new RegExp(/class="mw-usertoollinks mw-changeslist-links"/),
're': /(<span class="mw-usertoollinks mw-changeslist-links">.*<\/span>)/,
'post_process': function(str){ return post_process_user_tools(str); },
},
'flag': {
'category_re': new RegExp(/<abbr class="(?:minoredit|newpage)"/)
},
'size': {
'category_re': new RegExp(/class="history-size mw-diff-bytes"/),
're': /data-mw-bytes="([0-9]+)"/,
'filter': {
'width': '3em'
},
},
'diff': {
'category_re': new RegExp(/class="mw-plusminus-[a-z]+ mw-diff-bytes"/),
'filter': {
'width': '3em'
},
},
'comment': {
'category_re': new RegExp(/class="comment\b/),
'filter': true,
},
'edit tools': {
'category_re': new RegExp(/class="mw-changeslist-links[^"]*">/),
'post_process': function(str){ return post_process_edit_tools(str); },
},
'tags': {
'category_re': new RegExp(/class="mw-tag-markers"/),
'post_process': function(str){ return post_process_tags(str); },
},
'chk': {
'category_re': new RegExp(/class="fr-hist-basic-/),
'post_process': function(str){
return str.replace(/\[(?:automatically checked|automatisch gesichtet)\]/, 'auto')
.replace(/\[checked (by .*)\]/, '$1')
.replace(/\[gesichtet (von .*)\]/, '$1');
},
},
},
global_css: function(){
const css_classes = [
'.comment--without-parentheses',
'.mw-changeslist-links',
'.mw-diff-bytes',
'.mw-uctop',
'.mw-tag-markers',
'.mw-changeslist-links > span:not(:first-child)'
];
css_classes.forEach(c => {
document.styleSheets[0].addRule(c + '::before','content: "";');
document.styleSheets[0].addRule(c + '::after','content: "";');
});
return true;
}
}
}
};
return patterns;
}
function letChildrenEatParent(parentElem){
if(parentElem){
let found = false;
while(parentElem.firstChild){
parentElem.parentNode.insertBefore(parentElem.firstChild, parentElem);
found = true;
}
if(found){
parentElem.parentNode.removeChild(parentElem);
}
}
}
function list_elem2row(elem, conditions, prev_row){
let converted = false;
let row = '';
if(elem.tagName === 'LI'){
const flaggedrevs_class = get_flagged_rev_class(elem);
row += "<tr" + (flaggedrevs_class == '' ? '': ' class="' + flaggedrevs_class + '"') + ">";
flatten_log_entries(elem);
wrap_text_nodes_in_span(elem);
let content = {};
// fetch data from list entry and put to js-struct
Array.from(elem.children).forEach(cell => {
for(let [cell_header, conds] of Object.entries(conditions)){
if(conds['category_re'].test(cell.outerHTML)){
if(content[cell_header] === undefined){
content[cell_header] = '';
}
if(conds['re'] !== undefined){
const parts = conds['re'].exec(cell.outerHTML);
content[cell_header] += parts.slice(1).join('');
converted = true;
}else{
content[cell_header] += cell.outerHTML;
}
}
}
});
// add cells content to html table row
for(let [cell_header, conds] of Object.entries(conditions)){
row += "<td>";
if(content[cell_header] !== undefined){
if(conds['post_process'] !== undefined){
content[cell_header] = conds['post_process'](content[cell_header]);
}
//TODO: not sure about this one; might not be helpful in some cases
//if(prev_row[cell_header] == content[cell_header] && content[cell_header].length > 5){
// row += '-"-';
//}else{
row += content[cell_header];
prev_row[cell_header] = content[cell_header];
//}
}else{
prev_row[cell_header] = '';
}
row += "</td>";
}
row += "</tr>";
}
return {row: row, converted: converted};
}
function list2table(list, pattern){
if(list && list.children.length > 0){
let table = '<table id="list2table" class="wikitable"><thead><tr>';
for(let h in pattern.conditions){
if(pattern.conditions[h].filter){
const filter = pattern.conditions[h].filter;
table += '<th><input type="text" style="width:'
+ (filter['width'] ? filter['width'] : '100%')
+ '; box-sizing: border-box;"></th>';
}else{
table += '<th><input type="hidden"></th>';
}
}
table += "</tr><tr>";
for(let h in pattern.conditions){
table += "<th>" + h + "</th>";
}
let found_something = 0;
table += "</tr></thead><tbody>";
let prev_row = {};
Array.from(list.children).forEach(elem => {
const li_elems = (elem.tagName === 'UL' ? Array.from(elem.children) : [elem]);
li_elems.forEach(li => {
const row = list_elem2row(li, pattern.conditions, prev_row);
if(row.converted){
++found_something;
table += row.row;
}
});
});
table += "</tbody></table>";
if(found_something > 0){
if(pattern.global_css !== undefined){
pattern.global_css();
}
const mySpan = document.createElement("span");
mySpan.innerHTML = table;
list.parentNode.replaceChild(mySpan, list);
add_filters_to_table(document.getElementById('list2table'));
}
}
}
function post_process_tags(str){
return str.replace(/<a .*?>(?:Tags?|Markierung(?:en|))<\/a>:\s*/, '')
.replace(/Advanced mobile edit/, 'adv. mob.')
.replace(/(?:iOS app edit|Bearbeitung mit iOS-App)/, 'iOS')
.replace(/(?:Manual revert|Manuelle Zurücksetzung)/, 'manual rev')
.replace(/(?:Mobile app edit|Bearbeitung von einer mobilen Anwendung)/, 'mob. app')
.replace(/(?:Mobile edit|Mobile Bearbeitung)/, 'mob. web')
.replace(/(?:Mobile web edit|Mobile Web-Bearbeitung)/, 'mob.')
.replace(/(?:Rollback|Zurücksetzung)/, 'rollback')
.replace(/(?:Reverted|Zurückgesetzt)/, 'rev')
.replace(/(?:2017 source edit|2017-Quelltext-Bearbeitung)/, '2017')
.replace(/(?:Visual edit|Visuelle Bearbeitung)/, 'VE');
}
function post_process_user_tools(str){
return str.replace(/>(?:talk|Diskussion)</, '>💬<')
.replace(/>(?:contribs|Beiträge)</, '>📝<')
.replace(/>(?:block|Sperren)</, '>🚫<')
.replace(/<\/span>\s+<span>/g, '</span><span>');
}
function post_process_edit_tools(str){
return str.replace(/<span class="mw-rollback-link">.*?<\/span>/, '')
.replace(/>updated since your last visit</, '>updated<')
.replace(/>(?:undo|rückgängig)</, '>👎<')
.replace(/>(?:thank|danken)</, '>👍<');
}
function wrap_text_nodes_in_span(li_elem){
li_elem.childNodes.forEach(node => {
if (node.nodeType === Node.TEXT_NODE) {
const span = document.createElement('span');
span.textContent = node.textContent;
node.parentNode.replaceChild(span, node);
}
});
}
if(mw.config.get('wgCanonicalSpecialPageName') !== 'AbuseFilter'){
const patterns = get_list_patterns();
const classes_of_lists = Object.keys(patterns.classes);
if(!document.getElementById('pagehistory')){
classes_of_lists.forEach(class_name => {
const lists = document.getElementsByClassName(class_name);
if(lists && lists.length > 0){
if(patterns.classes[class_name].show_sections){
Array.from(lists).forEach(list => {
list2table(list, patterns.classes[class_name]);
});
}else{
list2table(lists.item(0).parentNode, patterns.classes[class_name]);
}
}
});
}
const ids_of_lists = Object.keys(patterns.ids);
ids_of_lists.forEach(id => {
const list = document.getElementById(id);
if(list){
list2table(list, patterns.ids[id]);
}
});
}
})();