Highlights sets of identical barcodes and toggles checkboxes for merging on click
// ==UserScript==
// @name MusicBrainz: Highlight identical barcodes and toggle merge checkboxes
// @namespace https://musicbrainz.org/user/chaban
// @version 1.2
// @tag ai-created
// @description Highlights sets of identical barcodes and toggles checkboxes for merging on click
// @author chaban
// @license MIT
// @match *://*.musicbrainz.org/*/*/releases*
// @match *://*.musicbrainz.org/release-group/*
// @match *://*.musicbrainz.org/label/*
// @match *://*.musicbrainz.org/*/*/*edits
// @match *://*.musicbrainz.org/edit/*
// @match *://*.musicbrainz.org/user/*/edits*
// @match *://*.musicbrainz.org/search/edits*
// @icon https://musicbrainz.org/static/images/favicons/android-chrome-512x512.png
// @grant none
// @run-at document-idle
// ==/UserScript==
(function() {
'use strict';
const identifierToColor = {};
const identifierToCheckboxes = {};
function getRandomColor() {
const letters = '89ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * letters.length)];
}
return color;
}
function removeLeadingZeros(barcode) {
return barcode.replace(/^0+/, '');
}
/**
* Toggles the checkboxes for the entire group associated with the clicked barcode cell.
* @param {Event} event The click event.
*/
function toggleMergeCheckbox(event) {
const clickedBarcodeCell = event.currentTarget;
const clickedIdentifier = clickedBarcodeCell.dataset.barcodeIdentifier;
if (!clickedIdentifier || !identifierToCheckboxes[clickedIdentifier]) {
return;
}
const currentGroupCheckboxes = identifierToCheckboxes[clickedIdentifier];
const shouldCheck = !currentGroupCheckboxes.some(cb => cb.checked);
const allCheckboxesOnPage = document.querySelectorAll('input[name="add-to-merge"][type="checkbox"]');
allCheckboxesOnPage.forEach(checkbox => {
const row = checkbox.closest('tr');
if (!row) return;
const barcodeCellInThisRow = row.querySelector('.barcode-cell[data-barcode-identifier]');
if (barcodeCellInThisRow && barcodeCellInThisRow.dataset.barcodeIdentifier === clickedIdentifier) {
checkbox.checked = shouldCheck;
} else {
checkbox.checked = false;
}
});
}
/**
* Processes a given table element to find and highlight identical barcodes.
* @param {HTMLElement} table The table element to process.
*/
function processTable(table) {
const barcodeCellsInTable = {};
let barcodeColumnIndex = -1;
let formatColumnIndex = -1;
let headerRow = table.querySelector('thead tr');
if (!headerRow) {
headerRow = table.querySelector('tr:has(th)');
}
if (!headerRow) {
headerRow = table.querySelector('tbody tr');
}
if (headerRow) {
const headerCells = Array.from(headerRow.children);
headerCells.forEach((th, index) => {
const headerText = th.textContent.trim();
if (headerText === 'Barcode') {
barcodeColumnIndex = index;
}
if (headerText === 'Format') {
formatColumnIndex = index;
}
});
}
const dataRows = table.querySelectorAll('tbody tr, tr:not(:has(th)):not(:first-child)');
dataRows.forEach(row => {
let barcodeCell = null;
let formatCell = null;
if (barcodeColumnIndex !== -1 && row.children[barcodeColumnIndex]) {
barcodeCell = row.children[barcodeColumnIndex];
}
if (formatColumnIndex !== -1 && row.children[formatColumnIndex]) {
formatCell = row.children[formatColumnIndex];
}
if (!barcodeCell || barcodeCell.tagName === 'TH') {
const potentialBarcodeCell = row.querySelector('.barcode-cell');
if (potentialBarcodeCell && potentialBarcodeCell.tagName === 'TD') {
barcodeCell = potentialBarcodeCell;
}
}
if (barcodeCell && barcodeCell.tagName === 'TD') {
const barcode = barcodeCell.textContent.trim();
const format = formatCell ? formatCell.textContent.trim() : '';
const mergeCheckbox = row.querySelector('input[name="add-to-merge"][type="checkbox"]');
if (barcode !== '[none]' && barcode !== '') {
const normalizedBarcode = removeLeadingZeros(barcode);
const identifier = `${normalizedBarcode}-${format}`;
barcodeCell.dataset.barcodeIdentifier = identifier;
if (!barcodeCellsInTable[identifier]) {
barcodeCellsInTable[identifier] = [];
}
barcodeCellsInTable[identifier].push(barcodeCell);
if (mergeCheckbox) {
if (!identifierToCheckboxes[identifier]) {
identifierToCheckboxes[identifier] = [];
}
identifierToCheckboxes[identifier].push(mergeCheckbox);
}
}
}
});
for (const identifier in barcodeCellsInTable) {
if (barcodeCellsInTable[identifier].length > 1) {
let color = identifierToColor[identifier];
if (!color) {
color = getRandomColor();
identifierToColor[identifier] = color;
}
barcodeCellsInTable[identifier].forEach(cell => {
cell.style.backgroundColor = color;
cell.style.fontWeight = 'bold';
cell.style.padding = '2px 4px';
cell.style.borderRadius = '3px';
if (identifierToCheckboxes[identifier] && identifierToCheckboxes[identifier].length > 0) {
cell.style.cursor = 'pointer';
cell.addEventListener('click', toggleMergeCheckbox);
} else {
cell.style.cursor = 'auto';
cell.removeEventListener('click', toggleMergeCheckbox);
}
});
}
}
}
function highlightBarcodesOnPage() {
document.querySelectorAll('.mergeable-table, table.merge-releases').forEach(table => {
processTable(table);
});
}
highlightBarcodesOnPage();
})();