Aligns the columns in "Merge Recordings" edits for easier comparison.
当前为
// ==UserScript==
// @name MusicBrainz: Align Colums in "Merge Recordings"
// @namespace https://musicbrainz.org/user/chaban
// @version 1.0.0
// @tag ai-created
// @description Aligns the columns in "Merge Recordings" edits for easier comparison.
// @author chaban
// @license MIT
// @match *://*.musicbrainz.org/edit/*
// @match *://*.musicbrainz.org/search/edits*
// @match *://*.musicbrainz.org/*/*/edits
// @match *://*.musicbrainz.org/*/*/open_edits
// @match *://*.musicbrainz.org/user/*/edits*
// @grant none
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
const CONTEXT_SELECTOR = 'table.details.merge-recordings';
const RIGID_COLUMNS = ['ISRCs', 'Length', 'AcoustIDs'];
const resetStyles = (tables) => {
tables.forEach(table => {
table.style.cssText = '';
table.querySelectorAll('thead th').forEach(th => th.style.width = '');
Array.from(table.rows).forEach(row => row.style.height = '');
});
};
const calculateColumnWidths = (tables, headerMaps) => {
const allHeaderNames = [...new Set(headerMaps.flat())];
const columnWidths = new Map(allHeaderNames.map(name => [name, 0]));
tables.forEach((table, tableIndex) => {
const currentHeaders = headerMaps[tableIndex];
Array.from(table.rows).forEach(row => {
Array.from(row.cells).forEach((cell, cellIndex) => {
const headerName = currentHeaders?.[cellIndex];
if (headerName) {
const currentMaxWidth = columnWidths.get(headerName) || 0;
const cellWidth = cell.scrollWidth + 8;
if (cellWidth > currentMaxWidth) {
columnWidths.set(headerName, cellWidth);
}
}
});
});
});
return columnWidths;
};
const applyColumnWidths = (tables, columnWidths, headerMaps) => {
tables.forEach((table, tableIndex) => {
table.style.tableLayout = 'fixed';
table.style.width = '100%';
const headers = table.querySelectorAll('thead th');
const currentHeaderNames = headerMaps[tableIndex];
let totalRigidWidth = 0;
const flexibleColumnCount = currentHeaderNames.filter(name => !RIGID_COLUMNS.includes(name)).length;
headers.forEach((header, index) => {
const headerName = currentHeaderNames?.[index];
if (headerName && RIGID_COLUMNS.includes(headerName)) {
const width = columnWidths.get(headerName);
if (width > 0) {
header.style.width = `${width}px`;
totalRigidWidth += width;
}
}
});
if (flexibleColumnCount > 0) {
const remainingWidth = `calc(${100 / flexibleColumnCount}% - ${totalRigidWidth / flexibleColumnCount}px)`;
headers.forEach((header, index) => {
const headerName = currentHeaderNames?.[index];
if (headerName && !RIGID_COLUMNS.includes(headerName)) {
header.style.width = remainingWidth;
}
});
}
});
};
const alignRowHeights = (tables) => {
requestAnimationFrame(() => {
const maxRows = Math.max(...tables.map(table => table.rows.length));
for (let i = 0; i < maxRows; i++) {
const correspondingRows = tables.map(table => table.rows[i]).filter(Boolean);
if (correspondingRows.length < 2) continue;
correspondingRows.forEach(row => row.style.height = 'auto');
const maxHeight = Math.max(...correspondingRows.map(row => row.getBoundingClientRect().height));
correspondingRows.forEach(row => {
row.style.height = `${maxHeight}px`;
});
}
});
};
const alignTables = (tables) => {
if (!tables || tables.length < 2) return;
resetStyles(tables);
const headerMaps = tables.map(table =>
Array.from(table.querySelectorAll('thead th')).map(th => th.textContent.trim())
);
if (headerMaps.some(headers => headers.length === 0)) return;
const columnWidths = calculateColumnWidths(tables, headerMaps);
applyColumnWidths(tables, columnWidths, headerMaps);
alignRowHeights(tables);
};
const main = () => {
const contexts = document.querySelectorAll(CONTEXT_SELECTOR);
contexts.forEach(context => {
const tables = Array.from(context.querySelectorAll('.tbl'));
if (tables.length >= 2) {
alignTables(tables);
const observer = new MutationObserver(() => {
clearTimeout(context._alignTimeout);
context._alignTimeout = setTimeout(() => alignTables(tables), 150);
});
tables.forEach(table => {
observer.observe(table.querySelector('tbody'), {
childList: true,
subtree: true
});
});
}
});
};
window.addEventListener('load', main);
})();