您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Aligns the columns in "Merge Recordings" edits for easier comparison.
// ==UserScript== // @name MusicBrainz: Align Columns in "Merge Recordings" // @namespace https://musicbrainz.org/user/chaban // @version 1.0.1 // @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); })();