diff --git a/mb_work_code_toolbox.changelog.md b/mb_work_code_toolbox.changelog.md new file mode 100644 index 00000000..d14c63d4 --- /dev/null +++ b/mb_work_code_toolbox.changelog.md @@ -0,0 +1 @@ +- **2022.8.8**: Internal changes: more TypeScript migration ([#499](https://github.com/ROpdebee/mb-userscripts/pull/499)) diff --git a/mb_work_code_toolbox.meta.js b/mb_work_code_toolbox.meta.js new file mode 100644 index 00000000..67f665eb --- /dev/null +++ b/mb_work_code_toolbox.meta.js @@ -0,0 +1,32 @@ +// ==UserScript== +// @name MB: Work code toolbox +// @description Copy work identifiers from various online repertoires and paste them into MB works with ease. Validate work code formatting: Highlight invalid or ill-formatted codes. +// @version 2022.8.8 +// @author ROpdebee +// @license MIT; https://opensource.org/licenses/MIT +// @namespace https://github.com/ROpdebee/mb-userscripts +// @homepageURL https://github.com/ROpdebee/mb-userscripts +// @supportURL https://github.com/ROpdebee/mb-userscripts/issues +// @downloadURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_work_code_toolbox.user.js +// @updateURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_work_code_toolbox.meta.js +// @include /^https?://(\w+\.)?musicbrainz\.org/(area|artist|collection|event|instrument|label|place|recording|release|release-group|series|work|url)/(create|[a-f\d-]{36}/edit)([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/release/[a-f\d-]{36}/edit-relationships([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/edit/\d+([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/edit/(open|subscribed(_editors)?)([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/(area|artist|collection|event|instrument|label|place|recording|release|release-group|series|work|url)/[a-f\d-]{36}/(open_)?edits([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/user/[^/]+/edits(/\w+)?([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/user/[^/]+/votes([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/search/edits\?.+?(#.+?)?$/ +// @include /^https?://(\w+\.)?musicbrainz\.org/work/[a-f\d-]{36}/.+([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/collection/[a-f\d-]{36}([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/artist/[a-f\d-]{36}/works([?#]|$)/ +// @include /^https?://online\.gema\.de/werke\/search\.faces([?#]|$)/ +// @include /^https?://iswcnet\.cisac\.org/.*([?#]|$)/ +// @run-at document-end +// @grant GM_getValue +// @grant GM.getValue +// @grant GM_setValue +// @grant GM.setValue +// @grant GM_deleteValue +// @grant GM.deleteValue +// ==/UserScript== \ No newline at end of file diff --git a/mb_work_code_toolbox.metadata.json b/mb_work_code_toolbox.metadata.json new file mode 100644 index 00000000..42de4659 --- /dev/null +++ b/mb_work_code_toolbox.metadata.json @@ -0,0 +1 @@ +{"version":"2022.8.8"} \ No newline at end of file diff --git a/mb_work_code_toolbox.user.js b/mb_work_code_toolbox.user.js new file mode 100644 index 00000000..28e7a22b --- /dev/null +++ b/mb_work_code_toolbox.user.js @@ -0,0 +1,1156 @@ +// ==UserScript== +// @name MB: Work code toolbox +// @description Copy work identifiers from various online repertoires and paste them into MB works with ease. Validate work code formatting: Highlight invalid or ill-formatted codes. +// @version 2022.8.8 +// @author ROpdebee +// @license MIT; https://opensource.org/licenses/MIT +// @namespace https://github.com/ROpdebee/mb-userscripts +// @homepageURL https://github.com/ROpdebee/mb-userscripts +// @supportURL https://github.com/ROpdebee/mb-userscripts/issues +// @downloadURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_work_code_toolbox.user.js +// @updateURL https://raw.github.com/ROpdebee/mb-userscripts/dist/mb_work_code_toolbox.meta.js +// @include /^https?://(\w+\.)?musicbrainz\.org/(area|artist|collection|event|instrument|label|place|recording|release|release-group|series|work|url)/(create|[a-f\d-]{36}/edit)([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/release/[a-f\d-]{36}/edit-relationships([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/edit/\d+([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/edit/(open|subscribed(_editors)?)([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/(area|artist|collection|event|instrument|label|place|recording|release|release-group|series|work|url)/[a-f\d-]{36}/(open_)?edits([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/user/[^/]+/edits(/\w+)?([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/user/[^/]+/votes([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/search/edits\?.+?(#.+?)?$/ +// @include /^https?://(\w+\.)?musicbrainz\.org/work/[a-f\d-]{36}/.+([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/collection/[a-f\d-]{36}([?#]|$)/ +// @include /^https?://(\w+\.)?musicbrainz\.org/artist/[a-f\d-]{36}/works([?#]|$)/ +// @include /^https?://online\.gema\.de/werke\/search\.faces([?#]|$)/ +// @include /^https?://iswcnet\.cisac\.org/.*([?#]|$)/ +// @run-at document-end +// @grant GM_getValue +// @grant GM.getValue +// @grant GM_setValue +// @grant GM.setValue +// @grant GM_deleteValue +// @grant GM.deleteValue +// ==/UserScript== + +// For original source code, see https://github.com/ROpdebee/mb-userscripts/tree/main/src/mb_work_code_toolbox +(function () { + 'use strict'; + + /* minified: babel helpers, babel-plugin-transform-async-to-promises, nativejsx */ + function ownKeys(t,e){var r=Object.keys(t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(t);e&&(n=n.filter((function(e){return Object.getOwnPropertyDescriptor(t,e).enumerable}))),r.push.apply(r,n);}return r}function _objectSpread2(t){for(var e=1;et.length)&&(e=t.length);for(var r=0,n=new Array(e);r=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var l,i=!0,a=!1;return {s:function(){r=r.call(t);},n:function(){var t=r.next();return i=t.done,t},e:function(t){a=!0,l=t;},f:function(){try{i||null==r.return||r.return();}finally{if(a)throw l}}}}const _Pact=function(){function t(){}return t.prototype.then=function(e,r){const n=new t,o=this.s;if(o){const t=1&o?e:r;if(t){try{_settle(n,1,t(this.v));}catch(l){_settle(n,2,l);}return n}return this}return this.o=function(t){try{const o=t.v;1&t.s?_settle(n,1,e?e(o):o):r?_settle(n,1,r(o)):_settle(n,2,o);}catch(l){_settle(n,2,l);}},n},t}();function _settle(t,e,r){if(!t.s){if(r instanceof _Pact){if(!r.s)return void(r.o=_settle.bind(null,t,e));1&e&&(e=r.s),r=r.v;}if(r&&r.then)return void r.then(_settle.bind(null,t,e),_settle.bind(null,t,2));t.s=e,t.v=r;const n=t.o;n&&n(t);}}"undefined"==typeof Symbol||Symbol.iterator||(Symbol.iterator=Symbol("Symbol.iterator"));const _asyncIteratorSymbol="undefined"!=typeof Symbol?Symbol.asyncIterator||(Symbol.asyncIterator=Symbol("Symbol.asyncIterator")):"@@asyncIterator",_earlyReturn={};!function(){function t(t){this._entry=t,this._pact=null,this._resolve=null,this._return=null,this._promise=null;}function e(t){return {value:t,done:!0}}function r(t){return {value:t,done:!1}}t.prototype._yield=function(t){return this._resolve(t&&t.then?t.then(r):r(t)),this._pact=new _Pact},t.prototype.next=function(t){const r=this;return r._promise=new Promise((function(n){const o=r._pact;if(null===o){const a=r._entry;if(null===a)return n(r._promise);function l(t){r._resolve(t&&t.then?t.then(e):e(t)),r._pact=null,r._resolve=null;}r._entry=null,r._resolve=n;var i=a(r);i&&i.then?i.then(l,(function(t){if(t===_earlyReturn)l(r._return);else {const e=new _Pact;r._resolve(e),r._pact=null,r._resolve=null,_resolve(e,2,t);}})):l(i);}else r._pact=null,r._resolve=n,_settle(o,1,t);}))},t.prototype.return=function(t){const r=this;return r._promise=new Promise((function(n){const o=r._pact;if(null===o)return null===r._entry?n(r._promise):(r._entry=null,n(t&&t.then?t.then(e):e(t)));r._return=t,r._resolve=n,r._pact=null,_settle(o,2,_earlyReturn);}))},t.prototype.throw=function(t){const e=this;return e._promise=new Promise((function(r,n){const o=e._pact;if(null===o)return null===e._entry?r(e._promise):(e._entry=null,n(t));e._resolve=r,e._pact=null,_settle(o,2,t);}))},t.prototype[_asyncIteratorSymbol]=function(){return this};}();var appendChildren=function(t,e){(e=Array.isArray(e)?e:[e]).forEach((function(e){e instanceof HTMLElement?t.appendChild(e):(e||"string"==typeof e)&&t.appendChild(document.createTextNode(e.toString()));}));}; + + /* minified: lib */ + class ConsoleSink{constructor(e){_defineProperty(this,"scriptName",void 0),_defineProperty(this,"onSuccess",this.onInfo.bind(this)),this.scriptName=e;}formatMessage(e){return "[".concat(this.scriptName,"] ").concat(e)}onDebug(e){console.debug(this.formatMessage(e));}onLog(e){console.log(this.formatMessage(e));}onInfo(e){console.info(this.formatMessage(e));}onWarn(e,t){e=this.formatMessage(e),t?console.warn(e,t):console.warn(e);}onError(e,t){e=this.formatMessage(e),t?console.error(e,t):console.error(e);}}let LogLevel;!function(e){e[e.DEBUG=0]="DEBUG",e[e.LOG=1]="LOG",e[e.INFO=2]="INFO",e[e.SUCCESS=3]="SUCCESS",e[e.WARNING=4]="WARNING",e[e.ERROR=5]="ERROR";}(LogLevel||(LogLevel={}));const HANDLER_NAMES={[LogLevel.DEBUG]:"onDebug",[LogLevel.LOG]:"onLog",[LogLevel.INFO]:"onInfo",[LogLevel.SUCCESS]:"onSuccess",[LogLevel.WARNING]:"onWarn",[LogLevel.ERROR]:"onError"},DEFAULT_OPTIONS={logLevel:LogLevel.INFO,sinks:[]};class Logger{constructor(e){_defineProperty(this,"_configuration",void 0),this._configuration=_objectSpread2(_objectSpread2({},DEFAULT_OPTIONS),e);}fireHandlers(e,t,n){e{const r=o[HANDLER_NAMES[e]];r&&(n?r.call(o,t,n):r.call(o,t));}));}debug(e){this.fireHandlers(LogLevel.DEBUG,e);}log(e){this.fireHandlers(LogLevel.LOG,e);}info(e){this.fireHandlers(LogLevel.INFO,e);}success(e){this.fireHandlers(LogLevel.SUCCESS,e);}warn(e,t){this.fireHandlers(LogLevel.WARNING,e,t);}error(e,t){this.fireHandlers(LogLevel.ERROR,e,t);}configure(e){Object.assign(this._configuration,e);}get configuration(){return this._configuration}addSink(e){this._configuration.sinks.push(e);}}const LOGGER=new Logger;var USERSCRIPT_ID="mb_work_code_toolbox";function existsInGM(e){return "undefined"!=typeof GM&&void 0!==GM[e]}function GMgetValue(e){return existsInGM("getValue")?GM.getValue(e):Promise.resolve(GM_getValue(e))}function GMsetValue(e,t){return existsInGM("setValue")?GM.setValue(e,t):(GM_setValue(e,t),Promise.resolve())}function GMdeleteValue(e){return existsInGM("deleteValue")?GM.deleteValue(e):(GM_deleteValue(e),Promise.resolve())}const GMinfo=existsInGM("info")?GM.info:GM_info;function groupBy(e,t,n){const o=new Map;var r,s=_createForOfIteratorHelper(e);try{for(s.s();!(r=s.n()).done;){var a;const e=r.value,s=t(e),i=n(e);o.has(s)?null===(a=o.get(s))||void 0===a||a.push(i):o.set(s,[i]);}}catch(i){s.e(i);}finally{s.f();}return o}function intersect(e,t){return e.filter((e=>t.includes(e)))}function difference(e,t){return e.filter((e=>!t.includes(e)))}function logFailure(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"An error occurred";e.catch((e=>{LOGGER.error(t,e);}));}class AssertionError extends Error{}function assert(e,t){if(!e)throw new AssertionError(null!=t?t:"Assertion failed")}function assertNonNull(e,t){assert(null!==e,null!=t?t:"Assertion failed: Expected value to be non-null");}function qs(e,t){const n=qsMaybe(e,t);return assertNonNull(n,"Could not find required element"),n}function qsMaybe(e,t){return (null!=t?t:document).querySelector(e)}function onDocumentLoaded(e){"loading"!==document.readyState?e():document.addEventListener("DOMContentLoaded",e);}function onAddEntityDialogLoaded(e,t){null===qsMaybe(".content-loading",e.parentElement)?t():e.addEventListener("load",(()=>{t();}));}function insertStylesheet(e,t){if(void 0===t&&(t="ROpdebee_".concat(USERSCRIPT_ID,"_css")),null!==qsMaybe("style#".concat(t)))return;const n=function(){var n=document.createElement("style");return n.setAttribute("id",t),appendChildren(n,e),n}.call(this);document.head.insertAdjacentElement("beforeend",n);}function parseVersion(e){return e.split(".").map((e=>parseInt(e)))}function versionLessThan(e,t){let n=0;for(;nt[n])return !1;n++;}return e.lengthversionLessThan(n,parseVersion(e.versionAdded))));0!==o.length&&showFeatureNotification(t.name,t.version,o.map((e=>e.description)));}function showFeatureNotification(e,t,n){insertStylesheet(css_248z,"ROpdebee_Update_Banner");const o=function(){var r=document.createElement("div");r.setAttribute("class","banner warning-header");var s=document.createElement("p");r.appendChild(s),appendChildren(s,"".concat(e," was updated to v").concat(t,"! "));var a=document.createElement("a");a.setAttribute("href",CHANGELOG_URL),s.appendChild(a);var i=document.createTextNode("See full changelog here");a.appendChild(i),appendChildren(s,". New features since last update:");var l=document.createElement("div");l.setAttribute("class","ROpdebee_feature_list"),r.appendChild(l);var c=document.createElement("ul");l.appendChild(c),appendChildren(c,n.map((e=>function(){var t=document.createElement("li");return appendChildren(t,e),t}.call(this))));var d=document.createElement("button");return d.setAttribute("class","dismiss-banner remove-item icon"),d.setAttribute("data-banner-name","alert"),d.setAttribute("type","button"),d.addEventListener("click",(()=>{o.remove(),localStorage.setItem(LAST_DISPLAYED_KEY,GM.info.script.version);})),r.appendChild(d),r}.call(this);qs("#page").insertAdjacentElement("beforebegin",o);} + + LOGGER.configure({ + logLevel: LogLevel.INFO, + }); + LOGGER.addSink(new ConsoleSink(USERSCRIPT_ID)); + + const VERSION = '2021.5.27'; + + function latinNetID(agencyId) { + return { + inRegexp: new RegExp("(\\d{0,7})(?:".concat(agencyId, ")?")), + outFormat: '$1', + keepLeadingZeroes: false + }; + } + + const CODE_FORMATS = { + 'AACIMH ID': { + inRegexp: /\d{0,7}/ + }, + 'ACAM ID': latinNetID('107'), + 'ACDAM ID': latinNetID('103'), + 'AEI ID': latinNetID('250'), + 'AGADU ID': latinNetID('004'), + 'AKKA/LAA ID': [{ + inRegexp: /\d{0,8}/ + }, { + inRegexp: /\d{5}M/, + keepLeadingZeroes: true + }], + 'AKM ID': { + inRegexp: /\d{0,8}(?:-?\d{2})?/ + }, + 'AMRA ID': { + inRegexp: /AWK\d{0,7}/ + }, + 'APA ID': latinNetID('015'), + 'APDAYC ID': [latinNetID('007'), { + inRegexp: /\d{8}/ + }], + 'APRA ID': { + inRegexp: /(?:GW|BG|JG|PM)\d{8}/ + }, + 'ARTISJUS ID': { + inRegexp: /4\d{9}/ + }, + 'ASCAP ID': { + inRegexp: /\d{0,14}/ + }, + 'BMI ID': { + inRegexp: /\d{0,8}/ + }, + 'BUMA/STEMRA ID': { + inRegexp: /W-\d{9}/ + }, + 'CASH ID': { + inRegexp: /[CMPU]-\d{10}/ + }, + 'CCLI ID': { + inRegexp: /\d{0,7}/ + }, + 'COMPASS ID': { + inRegexp: /\d{0,8}/ + }, + 'COTT ID': { + inRegexp: /\d{0,7}/ + }, + 'ECAD ID': { + inRegexp: /\d{0,8}/ + }, + 'GEMA ID': { + inRegexp: /(\d{0,8})[-‐](\d{3})/, + outFormat: '$1-$2' + }, + 'HFA ID': { + inRegexp: /[A-Z\d]{6}/ + }, + 'ICE ID': { + inRegexp: /\d{0,8}/ + }, + 'IMRO ID': { + inRegexp: /R\d{0,8}/ + }, + 'JASRAC ID': { + inRegexp: /(\d[\dA-Z]\d)-?(\d{4})-?(\d)/, + outFormat: '$1-$2-$3', + keepLeadingZeroes: true + }, + 'KODA ID': { + inRegexp: /\d{0,8}/ + }, + 'KOMCA ID': [{ + inRegexp: /\d{12}/ + }, { + inRegexp: /0000M\d{5,7}/, + keepLeadingZeroes: true + }], + 'LatinNet ID': { + inRegexp: /\d{3,4}/ + }, + 'MACP ID': { + inRegexp: /1\d{9}/ + }, + 'MÜST ID': { + inRegexp: /1\d{9}/ + }, + 'NexTone ID': { + inRegexp: /N\d{8}/ + }, + 'NICAUTOR ID': { + inRegexp: /\d{0,7}/ + }, + 'OSA ID': { + inRegexp: /(I\d{3})\.?(\d{2})\.?(\d{2})\.?(\d{2})/, + outFormat: '$1.$2.$3.$4' + }, + 'PRS tune code': { + inRegexp: /\d{4,6}[\dA-Z][A-Z]/ + }, + 'SABAM ID': { + inRegexp: /[A-Z\d]{7}\d{2}/, + ensureLength: 9, + padCharacter: '0', + message: 'SABAM uses zero-padding in its own repertory.' + }, + 'SACEM ID': { + inRegexp: /(\d{2})\s?(\d{3})\s?(\d{3})\s?(\d{2})/, + outFormat: '$1 $2 $3 $4' + }, + 'SACM ID': { + inRegexp: /[\dA-Z]\d{8}/, + ensureLength: 9, + padCharacter: '0', + message: 'SACM IDs are required to be zero-padded until 9 characters.' + }, + 'SACIM ID': { + inRegexp: /\d{0,7}/ + }, + 'SACVEN ID': latinNetID('060'), + 'SADAIC ID': latinNetID('061'), + 'SAYCE ID': { + inRegexp: /(\d{0,8})(?:065)?/, + outFormat: '$1' + }, + 'SAYCO ID': { + inRegexp: /(\d{0,8})(?:084)?/, + outFormat: '$1' + }, + 'SESAC ID': { + inRegexp: /\d{0,9}/ + }, + 'SGACEDOM ID': { + inRegexp: /\d{0,7}/ + }, + 'SGAE ID': { + inRegexp: /(\d{1,3})(?:\.?(\d{3}))?(?:\.?(\d{3}))?/, + outFormat: (_substring, p1, p2, p3) => [p1, p2, p3].filter(Boolean).join('.') + }, + 'SIAE ID': { + inRegexp: /\d{7,9}0\d/ + }, + 'SOBODAYCOM ID': { + inRegexp: /\d{0,7}/ + }, + 'SOCAN ID': { + inRegexp: /2?\d{8}/ + }, + 'SODRAC ID': { + inRegexp: /\d{0,7}/ + }, + 'SPA ID': { + inRegexp: /\d{0,7}/ + }, + 'SPAC ID': { + inRegexp: /\d{0,7}/ + }, + 'STEF ID': { + inRegexp: /\d{0,8}/ + }, + 'STIM ID': { + inRegexp: /\d{0,8}/ + }, + 'SUISA ID': { + inRegexp: /(\d{6})\s?(\d{3})\s?(\d{2})/, + outFormat: '$1 $2 $3', + ensureLength: 13, + padCharacter: '0' + }, + 'TEOSTO ID': { + inRegexp: /\d{8,9}/ + }, + 'TONO ID': { + inRegexp: /\d{0,8}/ + }, + 'ZAiKS ID': { + inRegexp: /\d{0,7}/ + } + }; + + function wrapRegex(start, regexp, end) { + return new RegExp(start + regexp.source + end); + } + + function validateCode(code, agencyId) { + const rules = CODE_FORMATS[agencyId]; + + if (!rules) { + return { + isValid: true, + input: code, + formattedCode: code, + wasChanged: false + }; + } + + if (Array.isArray(rules)) { + let partialResult; + + var _iterator = _createForOfIteratorHelper(rules), + _step; + + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + const rule = _step.value; + partialResult = validateCodeSingleRule(code, rule); + + if (partialResult.isValid) { + break; + } + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + + return partialResult; + } + + return validateCodeSingleRule(code, rules); + } + + function validateCodeSingleRule(code, rule) { + let inputRegexp = rule.inRegexp; + let outFormat = rule.outFormat; + + if (!outFormat) { + inputRegexp = wrapRegex('(', inputRegexp, ')'); + outFormat = '$1'; + } else { + inputRegexp = wrapRegex('(?:', inputRegexp, ')'); + } + + if (!rule.keepLeadingZeroes) { + inputRegexp = wrapRegex('0*', inputRegexp, ''); + } + + inputRegexp = wrapRegex('^', inputRegexp, '$'); + + if (rule.ensureLength && rule.padCharacter) { + code = code.padStart(rule.ensureLength, rule.padCharacter); + } + + if (!inputRegexp.test(code)) { + return { + isValid: false, + input: code, + message: rule.message + }; + } + + let formatted = code.replace(inputRegexp, outFormat); + + if (!formatted) { + console.error("Failed to format ".concat(code)); + formatted = code; + } + + return { + isValid: true, + input: code, + formattedCode: formatted, + wasChanged: formatted !== code + }; + } + + const agencyKeyTransformations = { + 'BUMA': 'BUMA/STEMRA ID', + 'MUST': 'MÜST ID', + 'PRS': 'PRS tune code', + 'SESAC Inc.': 'SESAC ID', + 'ZAIKS': 'ZAiKS ID' + }; + function agencyNameToID(agencyName) { + var _agencyKeyTransformat; + + return (_agencyKeyTransformat = agencyKeyTransformations[agencyName]) !== null && _agencyKeyTransformat !== void 0 ? _agencyKeyTransformat : agencyName + ' ID'; + } + + const ATTR_TRANSLATIONS = new Set(['Attributes', 'Eigenschaften', 'Attributs', 'Attributi', 'Eigenschappen']); + + function highlightElement(el, level, title) { + const 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) { + const result = 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(tbl => { + processTable(tbl); + }); + } + + function processTable(table) { + const columnIdx = 1 + [...table.querySelectorAll('thead th')].findIndex(th => ATTR_TRANSLATIONS.has(th.textContent)); + const attrLis = table.querySelectorAll("td:nth-child(".concat(columnIdx, ") li")); + attrLis.forEach(el => { + const match = /(.+)\s\((.+?)\)/.exec(el.textContent); + let code, agencyId; + + try { + var _ref = match; + + var _ref2 = _slicedToArray(_ref, 3); + + code = _ref2[1]; + agencyId = _ref2[2]; + } catch (_unused) { + return; + } + + checkElement(el, code, agencyId); + }); + } + + function processWorkPage() { + const attrs = document.querySelectorAll('dl.properties > dd.work-attribute'); + attrs.forEach(el => { + const agencyDD = el.previousSibling; + const agencyId = agencyDD.textContent.slice(0, -1); + checkElement(el, el.textContent, agencyId); + }); + } + + function processEditPage() { + document.querySelectorAll('table.details.edit-work, table.details.add-work').forEach(tbl => { + const attrRows = [...tbl.querySelectorAll('tr')].filter(tr => { + const attrName = tr.querySelector('th').textContent; + return attrName.endsWith(' ID:') || attrName === 'PRS tune code:'; + }); + attrRows.forEach(row => { + const agencyId = row.querySelector('th').textContent.slice(0, -1); + row.querySelectorAll('li').forEach(el => { + checkElement(el, el.textContent, agencyId); + }); + }); + }); + document.querySelectorAll('table.details.merge-works').forEach(tbl => { + tbl.querySelectorAll('table.tbl').forEach(innerTable => { + processTable(innerTable); + }); + }); + } + + function validateCodes() { + if ((document.location.pathname.startsWith('/artist/') || document.location.pathname.startsWith('/collection/')) && document.location.pathname.split('/')[3] !== 'edits') { + processTabulatedPage(); + } else if (document.location.pathname.startsWith('/work/') && document.location.pathname.split('/')[3] !== 'edits') { + processWorkPage(); + } else { + processEditPage(); + } + } + + if (document.location.hostname === 'musicbrainz.org' || document.location.hostname.endsWith('.musicbrainz.org')) { + onDocumentLoaded(maybeDisplayNewFeatures); + } + + function findDivByText(parent, text) { + const divs = [...parent.querySelectorAll('div')]; + return divs.filter(n => { + var _n$textContent; + + return ((_n$textContent = n.textContent) === null || _n$textContent === void 0 ? void 0 : _n$textContent.trim()) === text; + }); + } + + const LOG_STYLES = { + 'error': 'background-color: FireBrick; color: white; font-weight: bold;', + 'warning': 'background-color: Gold;', + 'info': 'background-color: GainsBoro;', + 'success': 'background-color: LightGreen;' + }; + + function normaliseID(id, agencyKey) { + const formatResult = validateCode(id, agencyKey); + + if (!formatResult.isValid) { + return id.replace(/^0+|[.\s-]/g, ''); + } + + return formatResult.formattedCode; + } + + function normaliseAgencyId(agencyId) { + return agencyId.replace(/-ID$/, ' ID').replace(/^ID (.+)/, '$1 ID').replace(/-tunniste$/, ' ID'); + } + + function getSelectedID(select) { + return normaliseAgencyId(select.options[select.selectedIndex].text.trim()); + } + + function setRowKey(select, agencyKey) { + const idx = [...select.options].findIndex(opt => normaliseAgencyId(opt.text.trim()) === agencyKey); + + if (idx < 0) { + throw new Error('Unknown agency key'); + } + + select.selectedIndex = idx; + } + + function computeAgencyConflicts(mbCodes, extCodes) { + const commonKeys = intersect(Object.keys(mbCodes), Object.keys(extCodes)); + return commonKeys.filter(k => mbCodes.get(k).length > 0).filter(k => difference(extCodes.get(k).map(c => normaliseID(c, k)), mbCodes.get(k).map(c => normaliseID(c, k))).length).map(k => [k, mbCodes.get(k), extCodes.get(k)]); + } + + function extractCodes(data) { + const transformed = Object.entries(data.agencyCodes).map(_ref => { + let _ref2 = _slicedToArray(_ref, 2), + key = _ref2[0], + codes = _ref2[1]; + + return [agencyNameToID(key), codes]; + }); + return new Map(transformed); + } + + function deduplicateCodes(codes, key) { + const seen = new Set(); + const results = []; + + var _iterator = _createForOfIteratorHelper(codes), + _step; + + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + const code = _step.value; + if (seen.has(normaliseID(code, key))) continue; + seen.add(normaliseID(code, key)); + results.push(code); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + + return results; + } + + function fillInput(inp, val) { + inp.value = val; + inp.style.backgroundColor = 'yellow'; + } + + const mainUIHTML = "
\n

ROpdebee's work code tools


\n
\n \n \n \n \n
\n

Log

\n

Validation errors

\n
"; + const VALIDATION_LOG_QUERY = 'div#ROpdebee_MB_Code_Validation_Errors'; + + class BaseWorkForm { + constructor(theForm) { + _defineProperty(this, "form", void 0); + + this.form = theForm; + this.form.ROpdebee_Work_Codes_Found = true; + this.addToolsUI(); + this.activateButtons(); + this.checkExistingCodes(); + } + + activateButtons() { + this.form.querySelector('button#ROpdebee_MB_Paste_Work').addEventListener('click', evt => { + evt.preventDefault(); + this.resetLog(); + this.readData(this.checkAndFill.bind(this)); + }); + this.form.querySelector('button#ROpdebee_MB_Format_Codes').addEventListener('click', evt => { + evt.preventDefault(); + this.resetLog(); + const formattedAny = this.formatExistingCodes(); + + if (formattedAny) { + this.fillEditNote([], 'existing', true); + } + }); + const autoFormatCheckbox = this.form.querySelector('input#ROpdebee_MB_Autoformat_Codes'); + autoFormatCheckbox.addEventListener('change', evt => { + evt.preventDefault(); + const target = evt.target; + + if (target.checked) { + localStorage.setItem(target.id, 'delete me to disable'); + } else { + localStorage.removeItem(target.id); + } + }); + autoFormatCheckbox.checked = !!localStorage.getItem('ROpdebee_MB_Autoformat_Codes'); + } + + checkExistingCodes() { + this.resetValidationLog(); + this.existingCodeInputs.forEach(_ref3 => { + let select = _ref3.select, + input = _ref3.input; + const agencyKey = getSelectedID(select); + const agencyCode = input.value; + const checkResult = validateCode(agencyCode, agencyKey); + + if (!checkResult.isValid) { + input.style.backgroundColor = 'red'; + this.addValidationError(agencyKey, agencyCode, checkResult.message); + } else if (checkResult.wasChanged) { + input.style.backgroundColor = 'orange'; + this.addFormatWarning(agencyKey, agencyCode); + } + }); + } + + formatExistingCodes() { + let formattedAny = false; + this.existingCodeInputs.forEach(_ref4 => { + let select = _ref4.select, + input = _ref4.input; + const agencyKey = getSelectedID(select); + const agencyCode = input.value; + const checkResult = validateCode(agencyCode, agencyKey); + + if (checkResult.isValid && checkResult.wasChanged) { + fillInput(input, checkResult.formattedCode); + this.log('info', "Changed ".concat(agencyKey, " ").concat(agencyCode, " to ").concat(checkResult.formattedCode)); + formattedAny = true; + } + }); + return formattedAny; + } + + resetLog() { + const logDiv = this.form.querySelector('div#ROpdebee_MB_Paste_Work_Log'); + logDiv.style.display = 'none'; + [...logDiv.children].slice(1).forEach(el => { + el.remove(); + }); + } + + resetValidationLog() { + const logDiv = this.form.querySelector(VALIDATION_LOG_QUERY); + logDiv.style.display = 'none'; + [...logDiv.children].slice(1).forEach(el => { + el.remove(); + }); + } + + get autoformatCodes() { + return this.form.querySelector('input#ROpdebee_MB_Autoformat_Codes').checked; + } + + get existingCodeInputs() { + return [...this.form.querySelectorAll('table#work-attributes tr')].map(row => ({ + 'select': row.querySelector('td > select'), + 'input': row.querySelector('td > input') + })).filter(_ref5 => { + let select = _ref5.select, + input = _ref5.input; + return select !== null && select.selectedIndex !== 0 && input !== null && input.value; + }); + } + + get existingCodes() { + return groupBy(this.existingCodeInputs, _ref6 => { + let select = _ref6.select; + return getSelectedID(select); + }, _ref7 => { + let value = _ref7.input.value; + return value; + }); + } + + get existingISWCs() { + return [...this.form.querySelectorAll('input[name^="edit-work.iswcs."]')].map(_ref8 => { + let value = _ref8.value; + return value; + }).filter(_ref9 => { + let length = _ref9.length; + return length; + }); + } + + findEmptyRow(parentSelector, inputName) { + const parent = this.form.querySelector(parentSelector); + const rows = [...parent.querySelectorAll('input[name*="' + inputName + '"]')]; + const emptyRows = rows.filter(_ref10 => { + let value = _ref10.value; + return value.length === 0; + }); + + if (emptyRows.length > 0) { + return emptyRows[0]; + } + + const newRowBtn = parent.querySelector('button.add-item'); + newRowBtn.click(); + return this.findEmptyRow(parentSelector, inputName); + } + + checkAndFill(rawData) { + const data = this.parseData(rawData); + console.log(data); + if (data === null) return; + const externalCodes = extractCodes(data); + const externalISWCs = data.iswcs; + const mbCodes = this.existingCodes; + const mbISWCs = this.existingISWCs; + const dupeAgencies = [...externalCodes.entries()].filter(_ref11 => { + let _ref12 = _slicedToArray(_ref11, 2), + codes = _ref12[1]; + + return codes.length > 1; + }).map(_ref13 => { + let _ref14 = _slicedToArray(_ref13, 1), + key = _ref14[0]; + + return key; + }); + + if (dupeAgencies.length > 0) { + const lis = dupeAgencies.reduce((acc, agency) => { + return acc + "
  • ".concat(agency, ": ").concat(externalCodes.get(agency).join(', '), "
  • "); + }, ''); + this.log('warning', "\n Found duplicate work codes in input.\n Please double-check whether all of these codes belong to this work.\n
      ".concat(lis, "
    ")); + } + + const newISWCs = difference(externalISWCs, mbISWCs); + const conflicts = computeAgencyConflicts(mbCodes, externalCodes); + + if (newISWCs.length > 0 && mbISWCs.length > 0) { + conflicts.unshift(['ISWC', mbISWCs, externalISWCs]); + } + + const confirmProm = conflicts.length > 0 ? this.promptForConfirmation(conflicts) : Promise.resolve(); + logFailure(confirmProm.then(() => { + const newCodes = this.retainOnlyNew(externalCodes, mbCodes); + this.fillData(newISWCs, newCodes, data['title'], data['source']); + const numWarnings = this.form.querySelectorAll('div#ROpdebee_MB_Paste_Work_Log > div').length; + this.log('success', 'Filled successfully' + (numWarnings ? " (".concat(numWarnings, " message(s))") : '')); + })); + } + + retainOnlyNew(externalCodes, mbCodes) { + const acc = new Map(); + + var _iterator2 = _createForOfIteratorHelper(externalCodes), + _step2; + + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + const _step2$value = _slicedToArray(_step2.value, 2), + key = _step2$value[0], + rawCodes = _step2$value[1]; + + const codes = deduplicateCodes(rawCodes, key); + + if (!mbCodes.has(key)) { + acc.set(key, codes); + } else { + const mbNormCodes = new Set(mbCodes.get(key).map(c => normaliseID(c, key))); + acc.set(key, codes.filter(id => !mbNormCodes.has(normaliseID(id, key)))); + } + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + + return acc; + } + + fillData(iswcs, codes, title, source) { + iswcs.forEach(this.fillISWC.bind(this)); + const entries = [...codes.entries()]; + entries.sort((_ref15, _ref16) => { + let _ref17 = _slicedToArray(_ref15, 1), + k1 = _ref17[0]; + + let _ref18 = _slicedToArray(_ref16, 1), + k2 = _ref18[0]; + + return k1.localeCompare(k2); + }); + const unknownAgencyCodes = []; + + for (var _i = 0, _entries = entries; _i < _entries.length; _i++) { + const _entries$_i = _slicedToArray(_entries[_i], 2), + agencyKey = _entries$_i[0], + agencyCodes = _entries$_i[1]; + + try { + this.fillAgencyCodes(agencyKey, agencyCodes); + } catch (err) { + if (err instanceof Error && err.message === 'Unknown agency key') { + unknownAgencyCodes.push([agencyKey, agencyCodes]); + } else { + throw err; + } + } + } + + if (unknownAgencyCodes.length > 0) { + const lis = unknownAgencyCodes.reduce((acc, _ref19) => { + let _ref20 = _slicedToArray(_ref19, 2), + agency = _ref20[0], + unknownCodes = _ref20[1]; + + return acc + "
  • ".concat(agency, ": ").concat(unknownCodes.join(', '), "
  • "); + }, ''); + this.log('warning', "\n Encountered unsupported agencies.\n If you encounter these a lot, please consider filing an MBS ticket.\n
      ".concat(lis, "
    ")); + } + + if (this.autoformatCodes) { + this.formatExistingCodes(); + } + + this.checkExistingCodes(); + this.maybeFillTitle(title); + this.fillEditNote(unknownAgencyCodes, source, this.autoformatCodes); + } + + maybeFillTitle(title) { + const titleInp = this.form.querySelector('input[name="edit-work.name"]'); + + if (titleInp.value) { + return; + } + + fillInput(titleInp, title.toLowerCase()); + titleInp.closest('div.row').querySelector('button.guesscase-title').click(); + } + + fillISWC(iswc) { + const row = this.findEmptyRow('div.form-row-text-list', 'edit-work.iswcs.'); + fillInput(row, iswc); + } + + fillAgencyCodes(agencyKey, agencyCodes) { + agencyCodes.forEach(code => { + const input = this.findEmptyRow('table#work-attributes', 'edit-work.attributes.'); + setRowKey(input.closest('tr').querySelector('td > select'), agencyKey); + fillInput(input, code); + }); + } + + fillEditNote(unknownAgencies, source, wasFormatted) { + const noteContent = unknownAgencies.reduce((acc, _ref21) => { + let _ref22 = _slicedToArray(_ref21, 2), + agencyKey = _ref22[0], + agencyCodes = _ref22[1]; + + return acc + agencyKey + ': ' + agencyCodes.join(', ') + '\n'; + }, unknownAgencies.length > 0 ? 'Unsupported agencies:\n' : ''); + + if (noteContent) { + this.fillEditNoteTop(noteContent); + } + + const fmtAppliedStr = wasFormatted ? VERSION : 'not applied'; + const editNoteBottom = "".concat(GMinfo.script.name, " v").concat(GMinfo.script.version, " (source: ").concat(source, ", formatting: ").concat(fmtAppliedStr, ")"); + this.fillEditNoteBottom(editNoteBottom); + } + + fillEditNoteTop(content) { + const note = this.form.querySelector('textarea[name="edit-work.edit_note"]'); + const noteParts = note.value.split('–\n'); + let top = noteParts[0]; + + if (!top) { + top = content + '\n'; + } else { + top += content; + } + + noteParts[0] = top; + note.value = noteParts.join('–\n'); + } + + fillEditNoteBottom(content) { + const note = this.form.querySelector('textarea[name="edit-work.edit_note"]'); + const noteParts = note.value.split('–\n'); + let bottom = noteParts[1]; + + if (!bottom) { + bottom = content; + } else { + bottom += '\n' + content; + } + + noteParts[0] = noteParts[0] || '\n'; + noteParts[1] = bottom; + note.value = noteParts.join('–\n'); + } + + readData(cb) { + logFailure(GMgetValue('workCodeData').then(data => { + if (!data) { + this.log('error', 'No data found. Did you copy anything?'); + return; + } + + cb(data); + return GMdeleteValue('workCodeData'); + })); + } + + parseData(raw) { + try { + return JSON.parse(raw); + } catch (err) { + this.log('error', 'Invalid data'); + console.log(raw); + console.log(err); + return null; + } + } + + promptForConfirmation(conflicts) { + const lis = conflicts.reduce((acc, _ref23) => { + let _ref24 = _slicedToArray(_ref23, 3), + agency = _ref24[0], + mbCodes = _ref24[1], + extCodes = _ref24[2]; + + return acc + "
  • ".concat(agency, ": [").concat(mbCodes.join(', '), "] vs [").concat(extCodes.join(', '), "]
  • "); + }, ''); + const msg = "Uh-oh. MB already has the following codes with conflicting data:\n Are you sure you want to fill these?\n Note: New codes will be added and will not replace the existing ones.
    \n
      ".concat(lis, "
    \n "); + this.log('warning', msg); + return new Promise(resolve => { + this.form.querySelector('.conflict-confirm').addEventListener('click', evt => { + evt.target.disabled = true; + evt.preventDefault(); + resolve(); + }); + }); + } + + log(level, html) { + const logDiv = this.form.querySelector('div#ROpdebee_MB_Paste_Work_Log'); + logDiv.insertAdjacentHTML('beforeend', "\n
    ").concat(html, "
    ")); + logDiv.style.display = 'block'; + logDiv.scrollTop = logDiv.scrollHeight; + } + + addValidationError(agencyKey, code, message) { + const logDiv = this.form.querySelector(VALIDATION_LOG_QUERY); + let msg = "".concat(code, " does not look like a valid ").concat(agencyKey, "."); + + if (message) { + msg += ' ' + message; + } + + logDiv.insertAdjacentHTML('beforeend', "
    ").concat(msg, "
    ")); + logDiv.style.display = 'block'; + logDiv.scrollTop = logDiv.scrollHeight; + } + + addFormatWarning(agencyKey, code) { + const logDiv = this.form.querySelector(VALIDATION_LOG_QUERY); + const msg = "".concat(code, " is not a well-formatted ").concat(agencyKey, "."); + logDiv.insertAdjacentHTML('beforeend', "
    ").concat(msg, "
    ")); + logDiv.style.display = 'block'; + logDiv.scrollTop = logDiv.scrollHeight; + } + + } + + class WorkEditForm extends BaseWorkForm { + addToolsUI() { + this.form.querySelector('.documentation').insertAdjacentHTML('beforebegin', mainUIHTML); + } + + } + + class IframeEditForm extends BaseWorkForm { + addToolsUI() { + this.form.querySelector('.half-width').insertAdjacentHTML('beforebegin', mainUIHTML); + this.form.querySelector('#ropdebee-work-menu').style.marginLeft = '0px'; + } + + } + + function editFormFactory(theForm, inIframe) { + if (inIframe) { + return new IframeEditForm(theForm); + } + + return new WorkEditForm(theForm); + } + + function handleMB() { + const editWorkFormQuery = 'form.edit-work'; + + function handleChange() { + const workForms = [...document.querySelectorAll(editWorkFormQuery)].map(f => [f, false]); + document.querySelectorAll('iframe').forEach(iframe => { + onAddEntityDialogLoaded(iframe, () => { + iframe.contentWindow.document.querySelectorAll(editWorkFormQuery).forEach(form => workForms.push([form, true])); + }); + }); + workForms.filter(f => !f[0].ROpdebee_Work_Codes_Found).forEach(_ref25 => { + let _ref26 = _slicedToArray(_ref25, 2), + f = _ref26[0], + inIframe = _ref26[1]; + + return editFormFactory(f, inIframe); + }); + } + + const theForm = document.querySelector(editWorkFormQuery); + + if (theForm && !theForm.ROpdebee_Work_Codes_Found) { + editFormFactory(theForm, false); + } + + const observer = new MutationObserver(handleChange); + observer.observe(document, { + subtree: true, + childList: true + }); + } + + const iswcRegex = /\bT-(?:\d{3}\.){2}\d{3}-\d\b/; + + function storeData(source, iswcs, codes, title) { + const obj = { + source, + iswcs, + title, + agencyCodes: Object.fromEntries(codes) + }; + console.log(obj); + return GMsetValue('workCodeData', JSON.stringify(obj)); + } + + const stringsDefaults = { + AGENCY_NAME_FIELD: 'Agency Name', + AGENCY_WORK_CODES: 'Agency Work Codes', + AGENCY_WORK_CODE_FIELD: 'Agency Work Code', + ARCHIVED_ISWCS: 'Archived ISWCs', + ORIGINAL_TITLE_FIELD: 'Original Title', + PREFERRED_ISWC_FIELD: 'Preferred ISWC' + }; + + const translateStrings = function () { + let strings; + return function (text) { + var _strings$text; + + if (!strings) { + const stringsJson = localStorage.getItem('strings'); + + if (!stringsJson) { + console.error('Could not extract translations!'); + return stringsDefaults[text]; + } + + strings = JSON.parse(stringsJson); + } + + return (_strings$text = strings[text]) !== null && _strings$text !== void 0 ? _strings$text : stringsDefaults[text]; + }; + }(); + + function handleISWCNet() { + function findAgencyWorkCodes(table) { + const codeTable = findDivByText(table, "".concat(translateStrings('AGENCY_WORK_CODES'), ":")).map(div => div.nextSibling); + if (codeTable.length === 0) return new Map(); + const rows = [...codeTable[0].querySelectorAll('tbody > tr')]; + const groupedCodes = groupBy(rows, row => row.querySelector("td[id=\"".concat(translateStrings('AGENCY_NAME_FIELD'), ":\"]")).textContent, row => row.querySelector("td[id=\"".concat(translateStrings('AGENCY_WORK_CODE_FIELD'), ":\"]")).textContent); + + if (groupedCodes.has('CASH')) { + groupedCodes.set('CASH', groupedCodes.get('CASH').map(code => "C-".concat(code))); + } + + return groupedCodes; + } + + function findIswcs(table) { + const iswcs = [table.querySelector("td[id=\"".concat(translateStrings('PREFERRED_ISWC_FIELD'), ":\"]")).textContent]; + findDivByText(table, translateStrings('ARCHIVED_ISWCS')).forEach(archivedTitle => { + const archivedISWCsDiv = archivedTitle.nextSibling; + iswcs.push(...archivedISWCsDiv.childNodes[0].textContent.split(', ')); + }); + return iswcs; + } + + function findTitle(table) { + return table.querySelector("td[id=\"".concat(translateStrings('ORIGINAL_TITLE_FIELD'), ":\"]")).textContent; + } + + function parseAndCopy(table) { + const workCodes = findAgencyWorkCodes(table); + const iswcs = findIswcs(table); + const title = findTitle(table); + logFailure(storeData('CISAC ISWCNet', iswcs, workCodes, title)); + } + + function handleChangeCisac(mutationRec) { + if (mutationRec.length === 0 || mutationRec[0].addedNodes.length === 0) return; + if (mutationRec[0].addedNodes[0].nodeName !== 'TR') return; + const viewMoreDiv = mutationRec[0].addedNodes[0].querySelector('[class^="ViewMore_viewMoreContainer"]'); + if (!viewMoreDiv) return; + const entry = viewMoreDiv.parentNode.parentNode.parentNode; + const button = document.createElement('button'); + button.textContent = 'Copy work codes'; + button.addEventListener('click', () => { + parseAndCopy(entry); + }); + viewMoreDiv.prepend(button); + } + + const observer = new MutationObserver(handleChangeCisac); + observer.observe(document, { + subtree: true, + childList: true + }); + } + + function handleGEMA() { + function findAgencyWorkCodes(tr) { + return new Map([['GEMA', [tr.querySelector('.workSocworkcde').textContent.match(/(\d{0,8})[-‐](\d{3})/)[0]]]]); + } + + function findIswcs(tr) { + return [tr.querySelector('.workIswc').textContent.match(iswcRegex)[0]]; + } + + function findTitle(tr) { + return tr.querySelector('.workSearchedTitle').textContent; + } + + function parseAndCopy(tr) { + const workCodes = findAgencyWorkCodes(tr); + const iswcs = findIswcs(tr); + const title = findTitle(tr); + logFailure(storeData('GEMA Repertoire Search', iswcs, workCodes, title)); + } + + function injectButtons() { + let parentNode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : document; + parentNode.querySelectorAll('[id="auswahlForm:searchResultItems:tb"] > tr').forEach(tr => { + const button = document.createElement('button'); + button.textContent = 'Copy work codes'; + button.addEventListener('click', event => { + event.preventDefault(); + parseAndCopy(tr); + }); + tr.querySelector('.empty').prepend(button); + }); + } + + function handleChangeGEMA(mutationRec) { + if (mutationRec.length === 0 || mutationRec[0].addedNodes.length === 0) return; + const searchResults = mutationRec[0].addedNodes[0]; + if (searchResults.nodeType !== Node.ELEMENT_NODE) return; + injectButtons(searchResults); + } + + if (Object.toJSON) { + JSON.stringify = Object.toJSON; + } + + const observer = new MutationObserver(handleChangeGEMA); + observer.observe(document.querySelector('div.body'), { + childList: true + }); + injectButtons(); + } + + const repertoireToHandler = { + 'iswcnet.cisac.org': handleISWCNet, + 'online.gema.de': handleGEMA + }; + + if (document.location.hostname === 'musicbrainz.org' || document.location.hostname.endsWith('.musicbrainz.org')) { + validateCodes(); + handleMB(); + } else { + repertoireToHandler[document.location.hostname](); + } + +})();