// ==UserScript== // @name MB: Validate Work Codes // @version 2021.12.18 // @description Validate work identifier codes: Highlight invalid or ill-formatted work codes. // @author ROpdebee // @license MIT; https://opensource.org/licenses/MIT // @namespace https://github.com/ROpdebee/mb-userscripts // @downloadURL https://raw.github.com/ROpdebee/mb-userscripts/main/mb_validate_work_codes.user.js // @updateURL https://raw.github.com/ROpdebee/mb-userscripts/main/mb_validate_work_codes.user.js // @match *://*.musicbrainz.org/work/* // @match *://*.musicbrainz.org/artist/*/works // @match *://*.musicbrainz.org/artist/*/works?* // @match *://*.musicbrainz.org/edit/* // @match *://*.musicbrainz.org/*/edits* // @match *://*.musicbrainz.org/collection/* // @exclude *://*.musicbrainz.org/work/*/edit // @require https://code.jquery.com/jquery-3.6.0.min.js // @require https://raw.github.com/ROpdebee/mb-userscripts/main/lib/work_identifiers.js?v=2021.5.27 // @run-at document-end // ==/UserScript== // A bit too similar to mb_collapse_work_attributes to my liking, but eh. let $ = this.$ = this.jQuery = jQuery.noConflict(true); const ATTR_TRANSLATIONS = [ 'Attributes', 'Eigenschaften', 'Attributs', 'Attributi', 'Eigenschappen']; function highlightElement(el, level, title) { let color = level === 'error' ? 'FireBrick' : 'Orange'; el.style.color = color; el.style.fontWeight = 'bold'; if (title) { el.title = title; el.style.textDecoration = 'underline dotted 2px'; } } function checkElement(el, code, agencyId) { let result = MBWorkIdentifiers.validateCode(code, agencyId); if (!result.isValid) { highlightElement(el, 'error', result.message); } else if (result.wasChanged) { highlightElement(el, 'warning', result.formattedCode); } } function processTabulatedPage() { document.querySelectorAll('table.tbl').forEach(processTable); } function processTable(table) { let columnIdx = 1 + [...table.querySelectorAll('thead th')] .findIndex(th => ATTR_TRANSLATIONS.includes(th.innerText)); let attrLis = table.querySelectorAll('td:nth-child(' + columnIdx + ') li'); attrLis.forEach(el => { let match = /(.+)\s\((.+?)\)/.exec(el.innerText); let text, code, agencyId; try { [text, code, agencyId] = match; } catch { return; } checkElement(el, code, agencyId); }); } function processWorkPage() { let attrs = $('dl.properties > dd.work-attribute'); attrs.each((i, el) => { let agencyDD = el.previousSibling; let agencyId = agencyDD.innerText.slice(0, -1); checkElement(el, el.innerText, agencyId); }); } function processEditPage() { $('table.details.edit-work, table.details.add-work').each((i, tbl) => { let attrRows = $(tbl) .find('tr') .filter((i, tr) => { let attrName = tr.querySelector('th').innerText; return attrName.endsWith(' ID:') || attrName === 'PRS tune code:'; }); attrRows.each((i, row) => { let agencyId = row.querySelector('th').innerText.slice(0, -1); row.querySelectorAll('li').forEach(el => { checkElement(el, el.innerText, agencyId); }); }); }); $('table.details.merge-works').each((i, tbl) => { tbl.querySelectorAll('table.tbl').forEach(processTable); }); } if ((location.pathname.startsWith('/artist/') || location.pathname.startsWith('/collection/')) && location.pathname.split('/')[3] !== 'edits') { processTabulatedPage(); } else if (location.pathname.startsWith('/work/') && location.pathname.split('/')[3] !== 'edits') { processWorkPage(); } else { processEditPage(); }