NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name mb. ALL LINKS // @version 2024.10.3 // @description Hidden links include fanpage, social network, etc. (NO duplicates) Generated autolinks (configurable) includes plain web search, auto last.fm, Discogs and lyrics searches, etc. Shows begin/end dates on URL and provides edit link. Expands Wikidata links to wikipedia articles. // @namespace https://github.com/jesus2099/konami-command // @supportURL https://github.com/jesus2099/konami-command/labels/mb_ALL-LINKS // @downloadURL https://github.com/jesus2099/konami-command/raw/master/mb_ALL-LINKS.user.js // @author jesus2099 // @licence CC-BY-NC-SA-4.0; https://creativecommons.org/licenses/by-nc-sa/4.0/ // @licence GPL-3.0-or-later; http://www.gnu.org/licenses/gpl-3.0.txt // @since 2011-08-02; https://web.archive.org/web/20131103162328/userscripts.org/scripts/show/108889 / https://web.archive.org/web/20141011084020/userscripts-mirror.org/scripts/show/108889 // @icon data:image/gif;base64,R0lGODlhEAAQAMIDAAAAAIAAAP8AAP///////////////////yH5BAEKAAQALAAAAAAQABAAAAMuSLrc/jA+QBUFM2iqA2ZAMAiCNpafFZAs64Fr66aqjGbtC4WkHoU+SUVCLBohCQA7 // @require https://github.com/jesus2099/konami-command/raw/de88f870c0e6c633e02f32695e32c4f50329fc3e/lib/SUPER.js?version=2022.3.24.224 // @grant none // @match *://*.musicbrainz.org/area/* // @match *://*.musicbrainz.org/artist/* // @match *://*.musicbrainz.org/event/* // @match *://*.musicbrainz.org/genre/* // @match *://*.musicbrainz.org/instrument/* // @match *://*.musicbrainz.org/label/* // @match *://*.musicbrainz.org/place/* // @match *://*.musicbrainz.org/recording/* // @match *://*.musicbrainz.org/release/* // @match *://*.musicbrainz.org/release-group/* // @match *://*.musicbrainz.org/series/* // @match *://*.musicbrainz.org/url/* // @match *://*.musicbrainz.org/work/* // @exclude *.org/*/*/*annotat* // @exclude *.org/*/*/*create* // @exclude *.org/*/*/*delete* // @exclude *.org/*/*/*edit* // @exclude *.org/*/*/*merge* // @exclude *.org/*/*/*remove* // @exclude *.org/*/*/*split* // @exclude *.org/*/*/add-alias* // @exclude *.org/*/*annotat* // @exclude *.org/*/*create* // @exclude *.org/*/*delete* // @exclude *.org/*/*edit* // @exclude *.org/*/*merge* // @exclude *.org/*/*remove* // @exclude *.org/*/*split* // @exclude *.org/release/add // @exclude *.org/release/add?* // @run-at document-end // ==/UserScript== "use strict"; /* hint for Opera 12 users allow opera:config#UserPrefs|Allowscripttolowerwindow and opera:config#UserPrefs|Allowscripttoraisewindow */ const userjs = "jesus2099_all-links"; const nonLatinName = /[\u0384-\u1cf2\u1f00-\uffff]/; // U+2FA1D is currently out of js range var rawLanguages = JSON.parse(localStorage.getItem(userjs + "_languages")) || ["navigator", "musicbrainz"]; const path_ID_separator = "::::"; // Available tokens: // - for all entity pages: %entity-type% %entity-mbid% %entity-name% // - for "that" type entity pages: %that-mbid% %that-name% where "that" is an entity type in the above @include list // - for artist entity pages: %artist-sort-name% %artist-family-name-first% %artist-latin-script-name% // - for release entity pages: %release-barcode% // - for url entity pages: %url-target% (while %entity-name% and %url-name% are deliberately ignored) const hiddenLinks = { de: "Versteckte Links", en: "Hidden links", fr: "Liens cachés" }; var webSearchLinks = { title: { en: "Search the web", de: "Web durchsuchen", fr: "Chercher sur le Web", nl: "Zoeken op het Web" }, items: { webPageSearch: { title: {en: "Web pages", de: "Webseiten", fr: "Pages Web", nl: "Webpagina’s"}, target: [ "//duckduckgo.com/?q=%22%release-group-artist-credit%%22+%22%release-group-name%%22", "//duckduckgo.com/?q=%22%release-artist-credit%%22+%22%release-name%%22", "//duckduckgo.com/?q=%22%entity-name%%22" ] }, imageSearch: { title: {en: "Images", de: "Bilder", fr: "Images", nl: "Afbeeldingen"}, target: [ "//duckduckgo.com/?q=%22%release-group-artist-credit%%22+%22%release-group-name%%22+!i", "//duckduckgo.com/?q=%22%release-artist-credit%%22+%22%release-name%%22+!i", "//duckduckgo.com/?q=%22%entity-name%%22+!i" ] }, videoSearch: { title: {en: "Videos", de: "Videos", fr: "Vidéos", nl: "Video’s"}, target: [ "//duckduckgo.com/?q=%22%release-group-artist-credit%%22+%22%release-group-name%%22+!v", "//duckduckgo.com/?q=%22%release-artist-credit%%22+%22%release-name%%22+!v", "//duckduckgo.com/?q=%22%entity-name%%22+!v" ] }, waybackMachineHistory: { title: {en: "Archive history", de: "Archivierte Versionen", fr: "Versions archivées", nl: "Archiefgeschiedenis"}, target: "//web.archive.org/web/*/%url-target%" } } }; var whitelistSearchLinks = { title: { de: "Whitelist durchsuchen", en: "Search in the whitelist", fr: "Sites autorisés", nl: "Zoeken in de witte lijst" }, items: { genreDBs: { title: { de: "Nach Genre", en: "By genre", fr: "Par genre", nl: "Per Genre" }, items: { classical: { title: { de: "Klassik", en: "Classical", fr: "Classique", nl: "Klassiek" }, items: { // Skipped "Brahms" which has a search dropdown but no search page // Skipped "Classical Archives" which have an internal JSON search API only "DRAM": [ "https://jesus2099.gitlab.io/forward-request.html?_action=https://www.dramonline.org/search&_method=post&dram-search[q]=%release-group-name%&dram-search[t]=album", "https://jesus2099.gitlab.io/forward-request.html?_action=https://www.dramonline.org/search&_method=post&dram-search[q]=%release-name%&dram-search[t]=album", "https://jesus2099.gitlab.io/forward-request.html?_action=https://www.dramonline.org/search&_method=post&dram-search[q]=%work-name%&dram-search[t]=work", "https://jesus2099.gitlab.io/forward-request.html?_action=https://www.dramonline.org/search&_method=post&dram-search[q]=%recording-name%&dram-search[t]=track", ], "DRAM (composers)": "https://jesus2099.gitlab.io/forward-request.html?_action=https://www.dramonline.org/search&_method=post&dram-search[q]=%artist-name%&dram-search[t]=composer", "DRAM (performers)": "https://jesus2099.gitlab.io/forward-request.html?_action=https://www.dramonline.org/search&_method=post&dram-search[q]=%artist-name%&dram-search[t]=performer", "DRAM (ensembles)": "https://jesus2099.gitlab.io/forward-request.html?_action=https://www.dramonline.org/search&_method=post&dram-search[q]=%artist-name%&dram-search[t]=ensemble", "Operabase (composers)": { "bg cs da de el en es eu fi fr ga hu is it lt lv ja ko mt nl no pl ro ru sk sl sv zh": "https://www.operabase.com/composers/search/%language%?query=%artist-name%" }, "Operabase (other artists)": { "bg cs da de el en es eu fi fr ga hu is it lt lv ja ko mt nl no pl ro ru sk sl sv zh": "https://www.operabase.com/artists/search/%language%?query=%artist-name%" }, "Operabase (festivals)": { "bg cs da de el en es eu fi fr ga hu is it lt lv ja ko mt nl no pl ro ru sk sl sv zh": "https://www.operabase.com/festivals/search/%language%?query=%event-name%" }, "Operabase (venues)": { "bg cs da de el en es eu fi fr ga hu is it lt lv ja ko mt nl no pl ro ru sk sl sv zh": "https://www.operabase.com/venues/search/%language%?query=%place-name%" }, // Skipped "Operadis" which has no search engine online // Skipped "Overture/Doremus" which have a currently broken search engine } }, electronic: { title: { de: "Elektronik", en: "Eletronic", fr: "Électronique", nl: "Elektronisch" }, items: { "PsyDB": [ "https://www.psydb.net/search?q=%artist-name%", "https://www.psydb.net/search?q=%label-name%", "https://www.psydb.net/search?q=%release-group-name%", "https://www.psydb.net/search?q=%release-name%" ], // Skipped "Resident Advisor" which has a search dropdown but no search page // Skipped "RollDaBeats" which is down since December 2022 } }, folk: { title: { "en de": "Traditional", fr: "Traditionnelle", nl: "Traditionele" }, items: { "Cape Breton Fiddle Recordings": [ "https://www.cbfiddle.com/rx/tunesearch.cgi?search=%recording&-name%", "https://www.cbfiddle.com/rx/tunesearch.cgi?search=%work-name%" ], // Skipped "DanceDB" which has no free text search "Irish Traditional Music Tunes": [ "https://www.irishtune.info/album-search.php?value=%release-group-name%&form=title", "https://www.irishtune.info/album-search.php?value=%release-name%&form=title", "https://www.irishtune.info/search.php?lookfor=string&term=%recording-name%&type=any", "https://www.irishtune.info/search.php?lookfor=string&term=%work-name%&type=any" ], "Mainly Norfolk": [ "https://jesus2099.gitlab.io/forward-request.html?_action=https://mainlynorfolk.info/folk/records/search.php&_method=post&album=%release-group-name%", "https://jesus2099.gitlab.io/forward-request.html?_action=https://mainlynorfolk.info/folk/records/search.php&_method=post&album=%release-name%", "https://jesus2099.gitlab.io/forward-request.html?_action=https://mainlynorfolk.info/folk/songs/search.php&_method=post&song=%recording-name%", "https://jesus2099.gitlab.io/forward-request.html?_action=https://mainlynorfolk.info/folk/songs/search.php&_method=post&song=%work-name%" ], // Skipped "The Dance Gypsy" which is down since February 2022 "The Session": [ "https://thesession.org/recordings/search?q=%release-group-name%", "https://thesession.org/recordings/search?q=%release-name%", "https://thesession.org/tunes/search?q=%recording-name%", "https://thesession.org/tunes/search?q=%work-name%" ], "The Traditional Tune Archive": [ "https://tunearch.org/wiki/Special:Search?search=%artist-name%&fulltext=Dig+deeper&ns844=1&ns3002=1", "https://tunearch.org/wiki/Special:Search?search=%recording-name%&fulltext=Dig+deeper", "https://tunearch.org/wiki/Special:Search?search=%work-name%&fulltext=Dig+deeper" ] } }, // Skipped "Jazz Music Archives" which have a search dropdown but no search page metal: { title: { "de en nl": "Metal", fr: "Métal" }, items: { "Metal-Archives": [ "https://www.metal-archives.com/search?searchString=%label-name%&type=label_name", "https://www.metal-archives.com/search?searchString=%release-group-name%&type=album_title", "https://www.metal-archives.com/search?searchString=%release-name%&type=album_title" ], "Metal-Archives (aliases)": "https://www.metal-archives.com/search?searchString=%artist-name%&type=artist_alias", "Metal-Archives (bands)": "https://www.metal-archives.com/search?searchString=%artist-name%&type=band_name", "Spirit of Metal": [ { "cn de en es fr pl pt ru": "https://www.spirit-of-metal.com/labels.php?l=%language%&search=%label-name%" }, { "cn de en es fr pl pt ru": "https://www.spirit-of-metal.com/places.php?l=%language%&search=%place-name%" }, { "cn de en es fr pl pt ru": "https://www.spirit-of-metal.com/albums.php?l=%language%&search=%release-group-name%" }, { "cn de en es fr pl pt ru": "https://www.spirit-of-metal.com/albums.php?l=%language%&search=%release-name%" }, ], "Spirit of Metal (bands)": "https://www.spirit-of-metal.com/liste_groupe.php?recherche_groupe=%artist-name%", "Spirit of Metal (other artists)": { "cn de en es fr pl pt ru": "https://www.spirit-of-metal.com/artists.php?l=%language%&search=%artist-name%" } } }, rock: { title: "Rock", items: { "Prog Archives": [ "http://www.progarchives.com/google-search-results.asp?cx=artists&q=%artist-name%", "http://www.progarchives.com/google-search-results.asp?cx=artists&q=%release-group-name%" ], "Prog Archives (by barcode)": "http://www.progarchives.com/google-search-results.asp?cx=artists&q=%release-barcode%", "Prog Archives (by name)": "http://www.progarchives.com/google-search-results.asp?cx=artists&q=%release-name%", "Spirit of Rock": [ { "cn de en es fr pl pt ru": "https://www.spirit-of-rock.com/labels.php?l=%language%&search=%label-name%" }, { "cn de en es fr pl pt ru": "https://www.spirit-of-rock.com/places.php?l=%language%&search=%place-name%" }, { "cn de en es fr pl pt ru": "https://www.spirit-of-rock.com/albums.php?l=%language%&search=%release-group-name%" }, { "cn de en es fr pl pt ru": "https://www.spirit-of-rock.com/albums.php?l=%language%&search=%release-name%" }, ], "Spirit of Rock (bands)": "https://www.spirit-of-rock.com/liste_groupe.php?recherche_groupe=%artist-name%", "Spirit of Rock (other artists)": { "cn de en es fr pl pt ru": "https://www.spirit-of-rock.com/artists.php?l=%language%&search=%artist-name%" } } }, soundtrack: { title: { "de en": "Soundtrack", fr: "Bande son", nl: "Geluidsspoor" }, items: { "OverClock ReMix": [ "https://ocremix.org/site-search/?q=%artist-name%", "https://ocremix.org/site-search/?q=%label-name%", "https://ocremix.org/site-search/?q=%recording-name%", "https://ocremix.org/site-search/?q=%release-group-name%", "https://ocremix.org/site-search/?q=%release-name%", "https://ocremix.org/site-search/?q=%work-name%" ], "SoundtrackCollector": [ "https://www.soundtrackcollector.com/catalog/search.php?searchon=composer&searchtext=%artist-name%", "https://www.soundtrackcollector.com/catalog/search.php?searchon=track&searchtext=%recording-name%", "https://www.soundtrackcollector.com/catalog/search.php?searchon=title&searchtext=%release-group-name%", "https://www.soundtrackcollector.com/catalog/search.php?searchon=soundtrack&searchtext=%work-name%", ], "SoundtrackCollector (by barcode)": "https://www.soundtrackcollector.com/catalog/search.php?searchon=labelnr&searchtext=%release-barcode%", "SoundtrackCollector (by name)": "https://www.soundtrackcollector.com/catalog/search.php?searchon=title&searchtext=%release-name%" } // Skipped "Videogam.in" which is down since 2016/2017 }, theatre: { title: { de: "Theater", en: "Theatre", fr: "Théâtre", nl: "Theater" }, items: { "CastAlbums.org": [ "https://castalbums.org/search/?search=%artist-name%&type=People", "https://castalbums.org/search/?search=%release-group-name%&type=Recordings", "https://castalbums.org/search/?search=%release-name%&type=Recordings" ], "CastAlbums.org (songs)": [ "https://castalbums.org/search/?search=%recording-name%&type=Songs", "https://castalbums.org/search/?search=%work-name%&type=Songs" ], "CastAlbums.org (shows)": "https://castalbums.org/search/?search=%work-name%&type=Shows", // Skipped "IBDb" which search has a hidden request verification token input "IOBDb": [ "http://www.iobdb.com/Search?searchText=%artist-name%&searchDomain=CreditableEntity", "http://www.iobdb.com/Search?searchText=%label-name%&searchDomain=CreditableEntity", "http://www.iobdb.com/Search?searchText=%place-name%&searchDomain=Theatre", "http://www.iobdb.com/Search?searchText=%recording-name%&searchDomain=Any", "http://www.iobdb.com/Search?searchText=%work-name%&searchDomain=Any" ], "Theatricalia": [ "https://theatricalia.com/search?q=%artist-name%", "https://theatricalia.com/search?q=%label-name%", "https://theatricalia.com/search?q=%place-name%", "https://theatricalia.com/search?q=%recording-name%", "https://theatricalia.com/search?q=%work-name%" ] } }, literature: { title: { de: "Literatur", en: "Literature", fr: "Littérature", ja: "文学", }, items: { BookBrainz: [ "//bookbrainz.org/search?type=author&q=%artist-name%", "//bookbrainz.org/search?type=edition&q=%release-name%", "//bookbrainz.org/search?type=edition_group&q=%release-group-name%", "//bookbrainz.org/search?type=publisher&q=%label-name%", "//bookbrainz.org/search?type=series&q=%series-name%", "//bookbrainz.org/search?type=work&q=%work-name%" ], "BookBrainz (all entities)": "//bookbrainz.org/search?type=all_entities&q=%entity-name%", "Open Library": [ "//openlibrary.org/search/authors?q=%artist-name%", "//openlibrary.org/search?mode=everything&q=%work-name%", "//openlibrary.org/search?mode=everything&q=%release-name%" ], LibraryThing: [ "//www.librarything.com/search.php?searchtype=authorname&search=%artist-name%", "//www.librarything.com/search.php?searchtype=newwork_titles&search=%work-name%", "//www.librarything.com/search.php?searchtype=nseries&search=%series-name%" ], Goodreads: [ "//www.goodreads.com/search?utf8=%E2%9C%93&search_type=books&search%5Bfield%5D=author&q=%artist-name%", "//www.goodreads.com/search?utf8=%E2%9C%93&search_type=books&search%5Bfield%5D=title&q=%release-name%", "//www.goodreads.com/search?utf8=%E2%9C%93&search_type=books&search%5Bfield%5D=title&q=%work-name%", "//www.goodreads.com/search?q=%series-name%" ], // TODO?: Search on first name + last name: // ORCID: "//orcid.org/orcid-search/search?firstName=%artist-first-name%&lastName=%artist-last-name%&otherFields=true", ORCID: "https://orcid.org/orcid-search/search?searchQuery=%artist-name%", "LibriVox (author)": "https://librivox.org/search?search_form=advanced&author=%artist-name%", "LibriVox (reader)": "https://librivox.org/search?search_form=advanced&reader=%artist-name%", LibriVox: "https://librivox.org/search?search_form=advanced&title=%release-name%" } } } }, lyricsDBs: { title: { de: "Liedtext", en: "Lyrics", fr: "Paroles", nl: "Liedtekst" }, items: { Genius: [ "//genius.com/search?q=%artist-latin-script-name%", "//genius.com/search?q=%recording-name%", "//genius.com/search?q=%work-name%" ], "J-Lyric(歌手名)": "//j-lyric.net/index.php?ka=%artist-name%", "J-Lyric(曲名)": [ "//j-lyric.net/index.php?kt=%recording-name%", "//j-lyric.net/index.php?kt=%work-name%" ] } }, scoreDBs: { title: { de: "Partitur", en: "Score", fr: "Partitions", nl: "Bladmuziek" }, items: { "IMSLP/Petrucci Music Library": "//duckduckgo.com/?q=site:imslp.org+%22%work-name%%22" } }, regionalDBs: { title: { de: "Nach Gebiet", en: "By area", fr: "Par région", nl: "Per Gebied" }, items: { JP: { title: { fr: "Japon", en: "Japan" }, items: { "Amazon JP (cat)": "//amazon.jp/s/?url=search-alias%3Dpopular&field-keywords=%release-catalogue-number%", "Amazon JP (bars)": "//amazon.jp/s/?url=search-alias%3Dpopular&field-keywords=%release-barcode%", VGMdb: [ "//vgmdb.net/search?type=artist&q=%artist-name%", "//vgmdb.net/search?type=album&q=%release-group-name%", "//vgmdb.net/search?type=album&q=%release-name%", "//vgmdb.net/search?type=org&q=%label-name%", ], Yunisan: "//duckduckgo.com/?q=site:www22.big.or.jp+%22%2F%7Eyunisan%2Fvi%2F%22+%22%artist-name%%22", VKDB: "//duckduckgo.com/?q=site:vkdb.jp+%22%artist-name%%22" } }, KR: { title: { fr: "Corée", en: "Korea" }, items: { maniadb: "http://www.maniadb.com/search/%artist-name%/?sr=P" } }, DE: { title: { de: "Deutschland", en: "Germany", fr: "Allemagne", nl: "Duitsland" }, items: { "Musik-Sammler.de": [ "https://www.musik-sammler.de/search/%artist-name%/?q=artist", "https://www.musik-sammler.de/search/%release-name%/?q=medium", "https://www.musik-sammler.de/search/%release-group-name%/?q=album" ], "DNB - Deutsches Musikarchiv": {de: "https://portal.dnb.de/opac.htm?query=%28mat%3DMusic+OR+cod%3Dmt%29+AND+%release-name%&method=simpleSearch&cqlMode=true"} } }, FR: { title: { de: "Frankreich", en: "France", fr: "France", nl: "Frankrijk" }, items: { "Encyclopédisque": [ "http://www.encyclopedisque.fr/recherche.html?ra=%artist-name%&sp=1#resultat", "http://www.encyclopedisque.fr/recherche.html?rd=%release-name%&sp=1#resultat" ] } }, "CA-QC": { title: "Québec", items: { "Québec Info Musique": [ // TODO: make it work with "=" in title: https://musicbrainz.org/release-group/22d4bc68-c5f3-4e53-ab07-07d5920bbee1 "http://jesus2099.gitlab.io/forward-request.html?_action=http://quebecinfomusique.com/artistes/artiste.asp&_accept-charset=ISO-8859-1&search=%artist-name%", "http://jesus2099.gitlab.io/forward-request.html?_action=http://quebecinfomusique.com/albums/albums.asp&_accept-charset=ISO-8859-1&search=%release-name%", "http://jesus2099.gitlab.io/forward-request.html?_action=http://quebecinfomusique.com/albums/albums.asp&_accept-charset=ISO-8859-1&search=%release-group-name%", ] } }, NL: { title: { de: "Niederlande", en: "Netherlands", fr: "Pays-Bas", ja: "オランダ", nl: "Nederland" }, items: { "Muziekweb": [ "https://www.muziekweb.nl/en/Muziekweb/Cat/SingleSearch/Search?q=%artist-name%", "https://www.muziekweb.nl/en/Muziekweb/Cat/SingleSearch/Search?q=%label-name%", "https://www.muziekweb.nl/en/Muziekweb/Cat/SingleSearch/Search?q=%release-name%", "https://www.muziekweb.nl/en/Muziekweb/Cat/SingleSearch/Search?q=%recording-name%", "https://www.muziekweb.nl/en/Muziekweb/Cat/SingleSearch/Search?q=%work-name%" ] } }, SE: { title: { de: "Schweden", en: "Sweden", fr: "Suède", ja: "スウェーデン", nl: "Zweden", sv: "Sverige" }, items: { "Svensk Mediedatabas (SMDB)": [ "//smdb.kb.se/catalog/search?q=namn%3A%28%artist-family-name-first%%29", "//smdb.kb.se/catalog/search?q=%release-name%", ], "Svensk Mediedatabas (cat. no.)": "//smdb.kb.se/catalog/search?q=%release-catalogue-number%", } } } }, shops: { title: { fr: "Boutiques en ligne", en: "Online shops", }, items: { "Amazon (barcode)": "https://www.amazon.com/s?k=%release-barcode%", Bandcamp: "https://bandcamp.com/search?q=%artist-name%", Deezer: "https://www.deezer.com/search/%artist-name%", SoundCloud: "https://soundcloud.com/search/people?q=%artist-name%", Spotify: "https://open.spotify.com/search/%artist-name%", "Spotify (barcode)": "//open.spotify.com/search/upc:%release-barcode%", }, }, socialNetworks: { title: { de: "Soziale Netzwerke", fr: "Réseaux sociaux", en: "Social networks" }, items: { Facebook: "https://www.facebook.com/search/pages/?q=%artist-name%", Twitter: "https://twitter.com/search?f=users&q=%artist-name%", YouTube: "https://www.youtube.com/results?search_query=%artist-name%&sp=EgIQAg%253D%253D" }, }, otherDBs: { title: { de: "Andere Datenbanken", en: "Other databases", fr: "Autres bases de données", nl: "Andere databases" }, items: { AllMusic: { target: [ "//www.allmusic.com/search/artists/%artist-name%", "//www.allmusic.com/search/labels/%label-name%", "//www.allmusic.com/search/songs/%recording-name%", "//www.allmusic.com/search/all/%release-name%", "//www.allmusic.com/search/albums/%release-group-name%", "//www.allmusic.com/search/compositions/%work-name%" ] }, Discogs: [ "//www.discogs.com/%language%/search?q=%artist-name%&type=artist", "//www.discogs.com/%language%/search?title=%release-name%&type=release", "//www.discogs.com/%language%/search?title=%release-group-name%&type=release", "//www.discogs.com/%language%/search?q=%label-name%&type=label" ], "Discogs (artist credit)": [ "//www.discogs.com/%language%/search?title=%release-name%&artist=%release-artist-credit%&type=release", "//www.discogs.com/%language%/search?title=%release-group-name%&artist=%release-group-artist-credit%&type=release" ], "Discogs (barcode)": "//www.discogs.com/%language%/search/?q=%release-barcode%&type=release", MusicMoz: "//musicmoz.org/search/search.cgi?search=%entity-name%", "Dynamic Range DB": "//dr.loudness-war.info/?artist=%release-artist-credit%&album=%release-name%", GeoNames: [ "http://www.geonames.org/search.html?q=%area-name%", "http://www.geonames.org/advanced-search.html?q=%place-name%&featureClass=S" ], IMDb: "//www.imdb.com/find?q=%artist-name%&s=nm", TMDB: "//www.themoviedb.org/search/person?query=%artist-name%", IMVDb: [ "//imvdb.com/search?search_term=%artist-name%", "//imvdb.com/search?search_term=%label-name%", "//imvdb.com/search?search_term=%recording-name%", // TODO: implement %recording-artist-credit% like %release-artist-credit% for better search below // "//imvdb.com/search?search_term=%recording-name% %recording-artist-credit%", "//imvdb.com/search?search_term=%work-name%" ], ISNI: "//isni.oclc.org/xslt/CMD?ACT=SRCHA&IKT=8006&TRM=%artist-name%", VIAF: "//viaf.org/viaf/search?query=local.names+all+%22%artist-name%%22", "Last.fm (MBID)": { "en": "//last.fm/mbid/%artist-mbid%", "de es fr it ja pl pt ru sv tr zh": "//last.fm/%language%/mbid/%artist-mbid%" }, lastfmName: { title: {en: "Last.fm (name)", de: "Last.fm (Name)", es: "Last.fm (nombre)", fr: "Last.fm (nom)", it: "Last.fm (Nome)", ja: "Last.fm (名)", pl: "Last.fm (Nazwa)", pt: "Last.fm (nome)", ru: "Last.fm (имя)", sv: "Last.fm (namn)", tr: "Last.fm (ad)", zh: "Last.fm (名)"}, target: { "en": "//last.fm/search?q=%artist-name%", "de es fr it ja pl pt ru sv tr zh": "//last.fm/%language%/search?q=%artist-name%" } }, "Rate Your Music": [ "//rateyourmusic.com/search?searchtype=a&searchterm=%artist-name%", "//rateyourmusic.com/search?searchtype=b&searchterm=%label-name%", "//rateyourmusic.com/search?searchtype=l&searchterm=%release-name%", "//rateyourmusic.com/search?searchtype=r&searchterm=%work-name%" ], SecondHandSongs: [ "//secondhandsongs.com/search/artist?op_commonName=contains&commonName=%artist-name%&op_type=equals&display=commonName.type&sort=simplifiedCommonName", "//secondhandsongs.com/search/label?op_name=contains&name=%label-name%&display=name&sort=simplifiedName", "//secondhandsongs.com/search/performance?op_title=contains&title=%recording-name%&op_performer=contains&display=title.performer&sort=simplifiedTitle", "//secondhandsongs.com/search/release?op_title=contains&title=%release-name%&op_performer=contains&display=title.performer&sort=simplifiedTitle", "//secondhandsongs.com/search/work?op_title=contains&title=%work-name%&op_credits=contains&display=title.credits&sort=simplifiedTitle" ], "setlist.fm": [ "//www.setlist.fm/search?query=%artist-name%" ], WhoSampled: [ "//www.whosampled.com/search/artists/?q=%artist-name%", "//www.whosampled.com/search/tracks/?q=%recording-name%", "//www.whosampled.com/search/tracks/?q=%work-name%" ], Wikidata: "//www.wikidata.org/w?search=%entity-name%", Wikipedia: "//duckduckgo.com/?q=site:wikipedia.org+intitle%3A%22%entity-name%%22", LocalWikipedia: { title: "Wikipedia (%language%)", target: "//%language%.wikipedia.org/w?search=%entity-name%" } } } } }; var additionalSearchLinks = { title: { de: "Weiter suchen", en: "Search further", fr: "Chercher plus loin", nl: "Zoeken verder naar" }, items: { regionalDBs: { title: { de: "Nach Gebiet", en: "By area", fr: "Par région", nl: "Per Gebied" }, items: { FR: { title: { de: "Frankreich", en: "France", fr: "France", nl: "Frankrijk" }, items: { SACEM: [ { fr: "//repertoire.sacem.fr/resultats?filters=titles&query=%work-name%#searchBtn", "de en nl": "//repertoire.sacem.fr/en/results?filters=titles&query=%work-name%#searchBtn" }, { fr: "//repertoire.sacem.fr/resultats?filters=titles&query=%recording-name%#searchBtn", "de en nl": "//repertoire.sacem.fr/en/results?filters=titles&query=%recording-name%#searchBtn" }, { fr: "//repertoire.sacem.fr/resultats?filters=parties&query=%artist-name%#searchBtn", "de en nl": "//repertoire.sacem.fr/en/results?filters=parties&query=%artist-name%#searchBtn" }, { fr: "//repertoire.sacem.fr/resultats?filters=parties&query=%label-name%#searchBtn", "de en nl": "//repertoire.sacem.fr/en/results?filters=parties&query=%label-name%#searchBtn" } ] } }, JP: { title: { en: "Japan", fr: "Japon", ja: "日本" }, items: { CDJournal: [ "//jesus2099.gitlab.io/forward-request.html?_accept-charset=EUC-JP&_action=//www.cdjournal.com/search/do/&k=%artist-name%&target=a", "//jesus2099.gitlab.io/forward-request.html?_accept-charset=EUC-JP&_action=//www.cdjournal.com/search/do/&k=%release-name%&target=cd", ], JASRAC: "//jesus2099.gitlab.io/forward-request.html?_accept-charset=Shift_JIS&_method=post&_action=//www2.jasrac.or.jp/eJwid/main%3FtrxID%3DA00401-3%23result&IN_DEFAULT_WORKS_KOUHO_MAX=100&IN_DEFAULT_WORKS_KOUHO_SEQ=1&IN_WORKS_TITLE_NAME1=%recording-name%&IN_WORKS_TITLE_CONDITION=1&IN_DEFAULT_SEARCH_WORKS_NAIGAI=0&RESULT_CURRENT_PAGE=1", "MINC(アーティスト名・作家名)": "//www.minc.or.jp/artist/list/?nm=%artist-name%", "MINC(CD商品タイトル)": [ "//www.minc.or.jp/product/list/?tl=%release-group-name%&ta=%release-group-artist-credit%&type=search-form-title&match=2", "//www.minc.or.jp/product/list/?tl=%release-name%&ta=%release-artist-credit%&type=search-form-title&match=2", ], // TODO: Add token replacement in link labels before using all catalogue numbers (here only the first catalogue number is used) "MINC(品番/POS)": "//www.minc.or.jp/product/list/?dn=%release-catalogue-number%&type=search-form-diskno", "mercari (CD)": [ "//jp.mercari.com/search?keyword=%release-artist-credit%+%release-name%&t1_category_id=5&category_id=75", "//jp.mercari.com/search?keyword=%release-group-artist-credit%+%release-group-name%&t1_category_id=5&category_id=75", ], "Joshin web (cat)": "//joshinweb.jp/cdshops/Dps?KEY=RECODE&FM=0&KEYWORD=%release-catalogue-number%", "Joshin web (barcode)": "//joshinweb.jp/dp/%release-barcode%.html", "ヨドバシカメラ (cat)": "//www.yodobashi.com/category/91139/?word=%release-catalogue-number%", } }, USA: { items: { BMI: [ "//repertoire.bmi.com/Search/Search?SearchForm.Main_Search=Title&SearchForm.Main_Search_Text=%recording-name%", "//repertoire.bmi.com/Search/Search?SearchForm.Main_Search=Title&SearchForm.Main_Search_Text=%work-name%", "//repertoire.bmi.com/Search/Search?SearchForm.Main_Search=Publisher&SearchForm.Main_Search_Text=%label-name%" ], "BMI (performer)": "//repertoire.bmi.com/Search/Search?SearchForm.Main_Search=Performer&SearchForm.Main_Search_Text=%artist-name%", "BMI (writer)": "//repertoire.bmi.com/Search/Search?SearchForm.Main_Search=Writer%2FComposer&SearchForm.Main_Search_Text=%artist-name%" } } } }, isrcDBs: { title: "ISRC", items: { IFPI: "https://isrcsearch.ifpi.org/#!/search?upcCode=%release-barcode%&tab=lookup&showReleases&start=0&number=100", } }, otherDBs: { title: { de: "Andere Datenbanken", en: "Other databases", fr: "Autres bases de données", nl: "Andere databases" }, items: { GnuDb: [ "//gnudb.org/search/artist/%22%artist-name%%22", "//gnudb.org/search/%22%release-artist-credit%%22%20%22%release-name%%22", "//gnudb.org/search/%22%release-group-artist-credit%%22%20%22%release-group-name%%22", "//gnudb.org/search/song/%22%recording-name%%22", "//gnudb.org/search/song/%22%work-name%%22", ], "GnuDb (just title)": [ "//gnudb.org/search/album/%22%release-name%%22", "//gnudb.org/search/album/%22%release-group-name%%22", ], "GnuDb (abc)": [ "//gnudb.org/search/artist/%22%artist-latin-script-name%%22", ], } } } }; var searchLinks = {items: { additional: additionalSearchLinks, whitelist: whitelistSearchLinks, web: webSearchLinks }}; var disabledSearchLinks = {}; var FAVICON_CLASSES = { // from https://github.com/metabrainz/musicbrainz-server/blob/62d70e6e702d2f083bbdfe9f6a6056703a64ed30/root/static/scripts/common/constants.js#L53-L229 "45cat.com": "fortyfivecat", "45worlds.com": "fortyfiveworlds", "abc.net.au/triplejunearthed": "triplejunearthed", "adp.library.ucsb.edu": "dahr", "allmusic.com": "allmusic", "animenewsnetwork.com": "animenewsnetwork", "anison.info": "anisongeneration", "archive.org": "archive", "audiomack.com": "audiomack", "baidu.com": "baidu", "bandcamp.com": "bandcamp", "bandsintown.com": "bandsintown", "bbc.co.uk": "bbc", "beatport.com": "beatport", "bibliotekapiosenki.pl": "piosenki", "bigcartel.com": "bigcartel", "bookbrainz.org": "bookbrainz", "books.apple.com": "applebooks", "boomplay.com": "boomplay", "cancioneros.si": "cancioneros", "castalbums.org": "castalbums", "catalogue.bnf.fr": "bnfcatalogue", "cbfiddle.com/rx/": "cbfiddlerx", "ccmixter.org": "ccmixter", "cdjapan.co.jp": "cdjapan", "changetip.com": "changetip", "ci.nii.ac.jp": "cinii", "classicalarchives.com": "classicalarchives", "cpdl.org": "cpdl", "d-nb.info": "dnb", "dailymotion.com": "dailymotion", "deezer.com": "deezer", "dhhu.dk": "dhhu", "discogs.com": "discogs", "dogmazic.net": "dogmazic", "dramonline.org": "dram", "encyclopedisque.fr": "encyclopedisque", "ester.ee": "ester", "facebook.com": "facebook", "finna.fi": "finna", "finnmusic.net": "finnmusic", "flattr.com": "flattr", "fono.fi": "fonofi", "generasia.com/wiki": "generasia", "genius.com": "genius", "geonames.org": "geonames", "gutenberg.org": "gutenberg", "hoick.jp": "hoick", "ibdb.com": "ibdb", "idref.fr": "idref", "imdb.com": "imdb", "imslp.org": "imslp", "imvdb.com": "imvdb", "indiegogo.com": "indiegogo", "instagram.com": "instagram", "ircam.fr": "ircam", "irishtune.info": "irishtune", "iss.ndl.go.jp": "ndl", "itunes.apple.com": "itunes", "j-lyric.net": "jlyric", "jazzmusicarchives.com": "jazzmusicarchives", "joysound.com": "joysound", "junodownload.com": "junodownload", "kashinavi.com": "kashinavi", "kget.jp": "kget", "kickstarter.com": "kickstarter", "ko-fi.com": "kofi", "laboiteauxparoles.com": "laboiteauxparoles", "lantis.jp": "lantis", "last.fm": "lastfm", "lieder.net": "lieder", "linkedin.com": "linkedin", "livefans.jp": "livefans", "loc.gov": "loc", "loudr.fm": "loudr", "lyric.evesta.jp": "evestalyric", "mainlynorfolk.info": "mainlynorfolk", "melon.com": "melon", "metal-archives.com": "metalarchives", "mixcloud.com": "mixcloud", "mora.jp": "mora", "music.amazon": "amazonmusic", "music.apple.com": "applemusic", "music.bugs.co.kr": "bugs", "music.migu.cn": "migumusic", "music.youtube.com": "youtubemusic", "musicapopular.cl": "musicapopularcl", "musik-sammler.de": "musiksammler", "musixmatch.com": "musixmatch", "musopen.org": "musopen", "muziekweb.nl": "muziekweb", "muzikum.eu": "muzikum", "myspace.com": "myspace", "napster.com": "napster", "nicovideo.jp": "niconicovideo", "nla.gov.au": "trove", "ocremix.org": "ocremix", "offiziellecharts.de": "offiziellecharts", "online-bijbel.nl": "onlinebijbel", "opac.kbr.be": "kbr", "openlibrary.org": "openlibrary", "operabase.com": "operabase", "overture.doremus.org": "overture", "patreon.com": "patreon", "paypal.me": "paypal", "petitlyrics.com": "petitlyrics", "pinterest.com": "pinterest", "progarchives.com": "progarchives", "psydb.net": "psydb", "qim.com": "quebecinfomusique", "qobuz.com": "qobuz", "ra.co": "residentadvisor", "rateyourmusic.com": "rateyourmusic", "recochoku.jp": "recochoku", "reverbnation.com": "reverbnation", "rock.com.ar": "rockcomar", "rockensdanmarkskort.dk": "rockensdanmarkskort", "rockinchina.com": "ric", "rockipedia.no": "rockipedia", "rolldabeats.com": "rolldabeats", "runeberg.org": "runeberg", "saisaibatake.ame-zaiku.com/gakki": "gakki", "saisaibatake.ame-zaiku.com/musical": "gakki", "saisaibatake.ame-zaiku.com/musical_instrument": "gakki", "secondhandsongs.com": "secondhandsongs", "setlist.fm": "setlistfm", "smdb.kb.se": "smdb", "snaccooperative.org": "snac", "songfacts.com": "songfacts", "songkick.com": "songkick", "soundcloud.com": "soundcloud", "spirit-of-metal.com": "spiritofmetal", "spirit-of-rock.com": "spiritofrock", "spotify.com": "spotify", "stage48.net": "stage48", "target.com": "target", "tedcrane.com/DanceDB": "dancedb", "theatricalia.com": "theatricalia", "thedancegypsy.com": "thedancegypsy", "thesession.org": "thesession", "tidal.com": "tidal", "tiktok.com": "tiktok", "tipeee.com": "tipeee", "tobarandualchais.co.uk": "tobar", "touhoudb.com": "touhoudb", "tower.jp": "tower", "traxsource.com": "traxsource", "triplejunearthed.com": "triplejunearthed", "tunearch.org": "tunearch", "twitch.tv": "twitch", "twitter.com": "twitter", "uta-net.com": "utanet", "utaitedb.net": "utaitedb", "utamap.com": "utamap", "utaten.com": "utaten", "vgmdb.net": "vgmdb", "viaf.org": "viaf", "videogam.in": "videogamin", "vimeo.com/ondemand": "vimeoondemand", "vimeo.com": "vimeo", "vk.com": "vk", "vkdb.jp": "vkdb", "vocadb.net": "vocadb", "weibo.com": "weibo", "whosampled.com": "whosampled", "wikidata.org": "wikidata", "wikipedia.org": "wikipedia", "wikisource.org": "wikisource", "worldcat.org": "worldcat", "www.amazon": "amazon", "www.youtube.com": "youtube", "www5.atwiki.jp/hmiku/": "hmikuwiki", "yesasia.com": "yesasia", }; var favicons = { "deezer.com": "https://e-cdns-files.dzcdn.net/cache/images/common/favicon/favicon-16x16.526cde4edf20647be4ee32cdf35c1c13.png", "lastfm.": "//musicbrainz.org/static/images/favicons/lastfm-16.png", "rakuten.co.jp": "//plaza.rakuten.co.jp/favicon.ico", }; var favicontry = []; var guessOtherFavicons = true; var sidebar = document.getElementById("sidebar"); var tokenValues = {}; var entityUrlRelsWS = self.location.protocol + "//" + self.location.host + "/ws/2/%entity-type%/%entity-mbid%?inc=url-rels"; var extlinks; var j2css = document.createElement("style"); j2css.setAttribute("type", "text/css"); document.head.appendChild(j2css); j2css = j2css.sheet; j2css.insertRule("ul.external_links > li.defaultAutolink > input[type='checkbox'] { display: none; }", 0); j2css.insertRule("ul.external_links > li.defaultAutolink.disabled { text-decoration: line-through; display: none; }", 0); j2css.insertRule("ul.external_links.configure > li.defaultAutolink.disabled { display: list-item; }", 0); j2css.insertRule("ul.external_links.configure > li.defaultAutolink > input[type='checkbox'] { display: inline; }", 0); j2css.insertRule("div#sidebar [class^='" + userjs + "'], div#sidebar [class^='" + userjs + "'] + ul, div#sidebar [class*='" + userjs + "_wd-'] ul { background-color: #FF9; }", 0); j2css.insertRule("div#sidebar > ." + userjs + "_searchLinks.emptySection { display: none; }", 0); j2css.insertRule("div#sidebar > ." + userjs + "_searchLinks li.emptySection { display: none; }", 0); j2css.insertRule("div#sidebar > ." + userjs + "_searchLinks input[type='checkbox'] { display: none; }", 0); j2css.insertRule("div#sidebar > ." + userjs + "_searchLinks.disabled { text-decoration: line-through; display: none; }", 0); j2css.insertRule("div#sidebar > ." + userjs + "_searchLinks .disabled { text-decoration: line-through; display: none; }", 0); j2css.insertRule("div#sidebar > .configure." + userjs + "_searchLinks.emptySection { display: block; }", 0); j2css.insertRule("div#sidebar > .configure." + userjs + "_searchLinks li.emptySection { display: list-item; }", 0); j2css.insertRule("div#sidebar > .configure." + userjs + "_searchLinks input[type='checkbox'] { display: inline; }", 0); j2css.insertRule("div#sidebar > .configure." + userjs + "_searchLinks.disabled { display: block; }", 0); j2css.insertRule("div#sidebar > ul.configure." + userjs + "_searchLinks.disabled { display: none; }", 0); j2css.insertRule("div#sidebar > .configure." + userjs + "_searchLinks li.disabled { display: list-item; }", 0); j2css.insertRule("div#sidebar > .configure." + userjs + "_searchLinks ul.disabled { display: none; }", 0); j2css.insertRule("div#sidebar > ." + userjs + "_searchLinks h3 { margin: 0; }", 0); j2css.insertRule("div#sidebar > ." + userjs + "_searchLinks h4 { margin: 0; }", 0); j2css.insertRule("div#sidebar > ul." + userjs + "_userLinks > li.subsectionHeader { font-weight: 'bold'; padding: '0px'; float: 'right'; }", 0); main(); function main() { if (sidebar) { var entityMatch = self.location.href.match(/\/([a-z-]*)\/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}).*/i); var entityType = tokenValues["%entity-type%"] = entityMatch[1]; var entityMBID = tokenValues["%entity-mbid%"] = entityMatch[2]; tokenValues["%" + entityType + "-mbid%"] = entityMBID; /* Hidden links and autolinks */ if (entityType && entityMBID) { // Tokens for autolinks var entityHeaderClass = (entityType === "release-group" ? "rg" : entityType) + "header"; var entityNameNode = document.querySelector("div#content > div." + entityHeaderClass + " > h1 a, div#content > div." + entityHeaderClass + " > h1 span[href]"); /* for compatibilly with https://gist.github.com/jesus2099/4111760 */ if (entityNameNode) { var entityName = tokenValues["%entity-name%"] = entityNameNode.textContent.trim(); tokenValues["%" + entityType + "-name%"] = entityName; if (entityType == "artist") { var artistname = entityName; var artistsortname = sidebar.querySelector("dd.sort-name"); var artistsortnameSwapped = ""; artistsortname = tokenValues["%artist-sort-name%"] = artistsortname ? artistsortname.textContent : artistname; if (!artistname.match(nonLatinName)) { tokenValues["%artist-family-name-first%"] = artistsortname; } else { var tmpsn = artistsortname.split(","); for (var isn = tmpsn.length - 1; isn >= 0; isn--) { artistsortnameSwapped += tmpsn[isn].trim(); if (isn != 0) { artistsortnameSwapped += " "; } } tokenValues["%artist-family-name-first%"] = artistname; tokenValues["%artist-latin-script-name%"] = artistsortnameSwapped; } } else if (entityType === "release") { let barcodeNode = document.querySelector("#sidebar .barcode"); if (barcodeNode && barcodeNode.textContent.match(/^\d+$/)) { tokenValues["%release-barcode%"] = barcodeNode.textContent; } let catnoNode = document.querySelector("#sidebar .catalog-number"); if (catnoNode) { tokenValues["%release-catalogue-number%"] = catnoNode.textContent; } } else if (entityType == "url") { delete tokenValues["%entity-name%"]; delete tokenValues["%url-name%"]; tokenValues["%url-target%"] = entityName; } if (entityType.match(/^release(-group)?/)) { var artistCredit = document.querySelector("div#content > div." + entityHeaderClass + " > p.subheader"); if (artistCredit) { tokenValues["%" + entityType + "-artist-credit%"] = parseArtistCredit(artistCredit); } } } extlinks = sidebar.querySelector("h2.external-links + ul.external_links"); if (!extlinks) { var previous = sidebar.querySelector("div.sidebar-tags") || sidebar.querySelector("p.lastupdate"); extlinks = addAfter(createTag("ul", {a: {class: "external_links"}}, createTag("li", {}, "No white‐listed links yet.")), previous); addAfter(createTag("h2", {a: {class: "external-links"}}, "External links"), previous); } var editingBlock = sidebar.querySelector("h2.editing + ul.links"); if (editingBlock) { // We want to move external links (including release group ones) after editing block editingBlock = sidebar.insertBefore(sidebar.removeChild(editingBlock), sidebar.querySelector("h2.external-links")); sidebar.insertBefore(sidebar.removeChild(sidebar.querySelector("h2.editing")), editingBlock); } // Hidden links entityUrlRelsWS = entityUrlRelsWS.replace(/%entity-type%/, entityType).replace(/%entity-mbid%/, entityMBID); addHiddenLinks(); // Search links loadDisabledSearchLinks(); for (var sectionKey in searchLinks.items) if (Object.prototype.hasOwnProperty.call(searchLinks.items, sectionKey)) { addSearchLinksSection([sectionKey], sidebar); } // User links addUserLinks(sidebar); } /* Wikidata to Wikipedia */ if (rawLanguages && Array.isArray(rawLanguages) && rawLanguages.length > 0) { var languages = parseLanguages(rawLanguages); var wikidatas = sidebar.querySelectorAll("ul.external_links > li a[href*='wikidata.org/wiki/Q']"); for (var wd = 0; wd < wikidatas.length; wd++) { var wikidataID = wikidatas[wd].getAttribute("href").match(/Q\d+$/); if (wikidataID) { if (!wikidatas[wd].parentNode.querySelector("a.edit-languages")) { addAfter(createTag("div", {a: {class: "icon img"}, s: {backgroundImage: "url(/static/images/icons/cog.png)", opacity: ".5"}}, createTag("a", {a: {class: "edit-languages", title: "choose languages"}, s: {color: "transparent"}, e: {click: configureModule}}, "choose languages")), wikidatas[wd]); addAfter(document.createTextNode(" "), wikidatas[wd]); } var xhr = new XMLHttpRequest(); xhr.id = wikidataID[0]; getParent(wikidatas[wd], "li").classList.add(userjs + "_wd-" + xhr.id); wikidatas[wd].parentNode.appendChild(createTag("img", {a: {alt: "checking available wikipedia languages…", src: "/static/images/icons/loading.gif"}})); xhr.addEventListener("load", function(event) { var wikidataListItem = sidebar.querySelector("ul.external_links > li." + userjs + "_wd-" + this.id); removeNode(wikidataListItem.querySelector("img[src$='loading.gif']")); var wikidata = JSON.parse(this.responseText); if (wikidata && wikidata.entities && (wikidata = wikidata.entities[this.id])) { for (var languageCode = 0; languageCode < languages.length; languageCode++) { var wikiEntry = wikidata.sitelinks[languages[languageCode] + "wiki"]; if (wikiEntry) { var href = wikiEntry.url.replace(/^https?:/, ""); var ul; if (!sidebar.querySelector("ul.external_links > li a[href$='" + href + "']")) { if (!ul) { ul = wikidataListItem.appendChild(createTag("ul", {s: {listStyle: "none"}})); } ul.appendChild(createTag("li", {a: {class: "wikipedia-favicon"}, s: {marginLeft: "-22px"}}, [languages[languageCode], ": ", createTag("a", {a: {href: href}}, wikiEntry.title)])); } } } } }); xhr.open("get", "https://www.wikidata.org/wiki/Special:EntityData/" + xhr.id + ".json", true); xhr.send(null); } } } } } function addExternalLink(parameters/* text, target, begin, end, sntarget, mbid, enabledDefaultAutolink */) { var newLink = true; let li; if (parameters.target) { // This is a link if (typeof parameters.target === "string") { console.debug(GM_info.script.name + " ### Strange it now does not run for everything?\ntarget: " + parameters.target); var escapedTarget = parameters.target.replace(/'/g, "\\'"); var existingLink = sidebar.querySelector("ul.external_links > li a[href$='" + escapedTarget.replace(/^https?:/, "") + "']") // MBS adds a trailing code to ASIN URL https://github.com/metabrainz/musicbrainz-server/blob/7b16dfae25fef7ef570bbc96e2d7cb71f123139e/lib/DBDefs/Default.pm#L318-L328 || parameters.target.match(/\bamazon\.ca\b/) && sidebar.querySelector("ul.external_links > li a[href$='" + escapedTarget.replace(/^https?:/, "") + "?tag=music0b72-20']") || parameters.target.match(/\bamazon\.co\.jp\b/) && sidebar.querySelector("ul.external_links > li a[href$='" + escapedTarget.replace(/^https?:/, "") + "?tag=musicbrainzjp-22']") || parameters.target.match(/\bamazon\.co\.uk\b/) && sidebar.querySelector("ul.external_links > li a[href$='" + escapedTarget.replace(/^https?:/, "") + "?tag=music080d-21']") || parameters.target.match(/\bamazon\.com\b/) && sidebar.querySelector("ul.external_links > li a[href$='" + escapedTarget.replace(/^https?:/, "") + "?tag=musicbrainz0d-20']") || parameters.target.match(/\bamazon\.de\b/) && sidebar.querySelector("ul.external_links > li a[href$='" + escapedTarget.replace(/^https?:/, "") + "?tag=music059-21']") || parameters.target.match(/\bamazon\.fr\b/) && sidebar.querySelector("ul.external_links > li a[href$='" + escapedTarget.replace(/^https?:/, "") + "?tag=music083d-21']") || parameters.target.match(/\bamazon\.it\b/) && sidebar.querySelector("ul.external_links > li a[href$='" + escapedTarget.replace(/^https?:/, "") + "?tag=music084d-21']") || parameters.target.match(/\bamazon\.es\b/) && sidebar.querySelector("ul.external_links > li a[href$='" + escapedTarget.replace(/^https?:/, "") + "?tag=music02e-21']") // MBS adds a trailing slash to viaf URL https://github.com/jesus2099/konami-command/issues/293#issuecomment-376875307 || parameters.target.match(/\bviaf\b/) && sidebar.querySelector("ul.external_links > li a[href$='" + escapedTarget.replace(/^https?:/, "") + "/']"); if (existingLink) { newLink = false; li = getParent(existingLink, "li"); } else { li = createTag("li", {a: {ref: parameters.text}}); if (parameters.end) { li.appendChild(createTag("span", {a: {class: "deleted", title: parameters.target}}, parameters.text)); } else { li.appendChild(createTag("a", {a: {href: parameters.target}}, parameters.text)); } } if (parameters.sntarget && newLink) { li.appendChild(document.createTextNode(" (")); li.appendChild(createTag("a", {a: {href: parameters.sntarget, title: "search with latin name"}}, "lat.")); li.appendChild(document.createTextNode(")")); } if (parameters.begin || parameters.end) { var ardates = createTag("span", {s: {whiteSpace: "nowrap"}}, " ("); if (!parameters.begin && parameters.end == "????") { ardates.appendChild(archivedDate(parameters.end, parameters.target)); } else { if (parameters.begin) { ardates.appendChild(archivedDate(parameters.begin, parameters.target)); } if (parameters.begin != parameters.end) { ardates.appendChild(document.createTextNode("—")); } if (parameters.end && parameters.begin != parameters.end) { ardates.appendChild(archivedDate(parameters.end, parameters.target)); } } ardates.appendChild(document.createTextNode(")")); ardates.normalize(); li.appendChild(ardates); } if (parameters.mbid) { addAfter(createTag("div", {a: {class: "icon img edit-item"}, s: {opacity: ".5"}}, createTag("a", {a: {title: "edit this URL relationship", href: "/url/" + parameters.mbid + "/edit"}, s: {color: "transparent"}}, "edit")), li.querySelector("a")); addAfter(document.createTextNode(" "), li.querySelector("a")); } } setFavicon(li, (typeof parameters.target == "string") ? parameters.target : parameters.target.action); } else { // This is a Hidden links header li = createTag("li", {s: {fontWeight: "bold"}, a: {class: userjs + "_hidden-links " + "separator"}}, parameters.text); if (parameters.text.indexOf(" ") === 0) { // Level 1 header li.style.setProperty("padding-top", "0px"); extlinks.insertBefore(li, extlinks.lastChild); } else { // Level 2 header li.style.setProperty("padding", "0px"); li.style.setProperty("float", "right"); extlinks.appendChild(li); } } if (newLink) { if (parameters.target) { extlinks.appendChild(li); } } return newLink; } function addHiddenLinks() { loading(true); var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(event) { if (this.readyState == 4) { if (this.status == 200) { loading(false); var res = this.responseXML; var url, urls = res.evaluate("//mb:relation-list[@target-type='url']/mb:relation", res, nsr, XPathResult.ANY_TYPE, null); var haslinks = false; while ((url = urls.iterateNext())) { var target = res.evaluate("./mb:target", url, nsr, XPathResult.ANY_TYPE, null); target = target.iterateNext(); var begin, end; if ((begin = res.evaluate("./mb:begin", url, nsr, XPathResult.ANY_TYPE, null).iterateNext())) { begin = begin.textContent; } if ((end = res.evaluate("./mb:end", url, nsr, XPathResult.ANY_TYPE, null).iterateNext())) { end = end.textContent; } else if (res.evaluate("./mb:ended", url, nsr, XPathResult.ANY_TYPE, null).iterateNext()) { end = "????"; } if (target) { if (addExternalLink({text: url.getAttribute("type"), target: target.textContent, mbid: target.getAttribute("id"), begin: begin, end: end})) { if (!haslinks) { haslinks = true; addExternalLink({text: " " + getLocalisedText(hiddenLinks)}); } } } } } else if (this.status >= 400 || this.status == 0) { var txt = this.responseText.match(/<error><text>(.+)<\/text><text>/); txt = txt ? txt[1] : ""; error(this.status, txt); } } }; xhr.open("GET", entityUrlRelsWS, true); xhr.send(null); } function addSearchLinksSection(sectionPath, parentNode) { var level = sectionPath.length; var section = pathToItem(sectionPath); var sectionID = pathToID(sectionPath); var sectionTitleNode = createTag("h" + (1 + level), {a: {ref: sectionID}}, section.title ? getLocalisedText(section.title) : sectionPath[sectionPath.length - 1]); if (level === 1) { sectionTitleNode.classList.add(userjs + "_searchLinks"); if (parentNode == extlinks.parentNode) { addAfter(sectionTitleNode, extlinks); } else { parentNode.appendChild(sectionTitleNode); } if (section === webSearchLinks) { sectionTitleNode.appendChild(document.createTextNode(" ")); sectionTitleNode.appendChild( createTag("a", {a: {title: "filter search links"}, s: {padding: "1px 3px"}, e: {click: configureModule}}, createTag("img", {a: {src: "/static/images/icons/filter.png", alt: "filter search links", title: "filter search links"}}) ) ); } } else { parentNode.appendChild(sectionTitleNode); } var sectionListNode = addAfter(createTag("ul", {a: {class: "external_links"}}), sectionTitleNode); if (level === 1) { sectionListNode.classList.add(userjs + "_searchLinks"); } if (section !== webSearchLinks) { var sectionCBox = sectionTitleNode.appendChild( createTag("input", {a: {type: "checkbox"}, s: {float: "right", margin: "1px"}, e: {click: function(event) { toggleStorage(this.parentNode.getAttribute("ref")); this.parentNode.nextElementSibling.classList.toggle("disabled", !this.checked); if (this.parentNode.parentNode.id !== "sidebar") { this.parentNode.parentNode.classList.toggle("disabled", !this.checked); toggleEmpty(this.parentNode.parentNode, !this.checked); } else { this.parentNode.classList.toggle("disabled", !this.checked); } }}}) ); sectionCBox.checked = !disabledSearchLinks[sectionID]; if (disabledSearchLinks[sectionID]) { sectionListNode.classList.add("disabled"); if (level === 1) { sectionTitleNode.classList.add("disabled"); } else { parentNode.classList.add("disabled"); } } } var hasNothing = true; var hasVisibleContent = false; for (let itemKey in section.items) if (Object.prototype.hasOwnProperty.call(section.items, itemKey)) { var item = section.items[itemKey]; var itemPath = sectionPath.concat([itemKey]); var itemID = pathToID(itemPath); var itemNode = createTag("li", {a: {ref: itemID}}); if (typeof item === "object" && item.items) { hasNothing = false; sectionListNode.appendChild(itemNode); var subIsVisible = addSearchLinksSection(itemPath, itemNode); if (subIsVisible) { hasVisibleContent = true; } else { itemNode.classList.add("emptySection"); } } else { // "key": <target> → "key": {"target": <target>} if (typeof item !== "object" || !item.target) { item = {target: item}; // section.items[itemKey] = item; // not sure I need this } var itemTarget = false; if (!Array.isArray(item.target)) { item.target = [item.target]; } for (var t = 0; t < item.target.length; t++) { itemTarget = replaceAllTokens(getLocalisedText(item.target[t]), true); if (itemTarget) { break; } } if (itemTarget) { hasNothing = false; sectionListNode.appendChild(itemNode); itemNode.appendChild(createTag("a", {a: {href: itemTarget}}, item.title ? getLocalisedText(item.title) : itemKey)); setFavicon(itemNode, itemTarget); var itemCBox = itemNode.appendChild( createTag("input", {a: {type: "checkbox"}, s: {float: "right", margin: "1px"}, e: {click: function(event) { this.parentNode.classList.toggle("disabled", !this.checked); toggleStorage(this.parentNode.getAttribute("ref")); toggleEmpty(this.parentNode, !this.checked); }}}) ); itemCBox.checked = !disabledSearchLinks[itemID]; if (disabledSearchLinks[itemID]) { itemNode.classList.add("disabled"); } else { hasVisibleContent = true; } } } } if (!hasVisibleContent) { if (level === 1 && section !== webSearchLinks) { sectionTitleNode.classList.add("emptySection"); sectionListNode.classList.add("emptySection"); } if (hasNothing) { var noItemNote = { de: "nichts für diesen Entitätstyp", en: "nothing for this entity type", fr: "rien pour ce type d’entité", nl: "niets voor dit soort entiteit" }; sectionListNode.appendChild(createTag("li", {s: {fontStyle: "italic", opacity: "0.5"}}, getLocalisedText(noItemNote))); } } return hasVisibleContent && !disabledSearchLinks[sectionID]; } function addUserLinks(parentNode) { var loadedUserLinks = JSON.parse(localStorage.getItem(userjs + "_user-autolinks")) || {}; var filteredUserLinks = {}; var currentSection = ""; var currentSectionIsEmpty = true; for (let title in loadedUserLinks) if (Object.prototype.hasOwnProperty.call(loadedUserLinks, title)) { let target = loadedUserLinks[title]; if (!target || target === "") { if (currentSectionIsEmpty) { delete filteredUserLinks[currentSection]; } currentSection = title; currentSectionIsEmpty = true; filteredUserLinks[title] = null; } if (typeof target === "string") { target = replaceAllTokens(target, true); if (target) { currentSectionIsEmpty = false; filteredUserLinks[title] = target; } } } if (currentSectionIsEmpty) { delete filteredUserLinks[currentSection]; } if (!Object.getOwnPropertyNames(filteredUserLinks).length) { return; } var userLinksTitle = { de: "Meine Links", en: "My links", fr: "Mes liens", nl: "Mijn links" }; var userLinksTitleNode = createTag("h2", {}, getLocalisedText(userLinksTitle)); userLinksTitleNode.appendChild(document.createTextNode(" ")); userLinksTitleNode.appendChild( createTag("a", {a: {title: "configure user autolinks"}, s: {padding: "0px"}, e: {click: configureModule}}, createTag("img", {a: {src: "/static/images/icons/cog.png", alt: "configure user autolinks", title: "configure user autolinks"}}) ) ); if (parentNode == extlinks.parentNode) { addAfter(userLinksTitleNode, extlinks); } else { parentNode.appendChild(userLinksTitleNode); } var userLinksListNode = createTag("ul", {a: {class: userjs + "_userLinks external_links"}}); addAfter(userLinksListNode, userLinksTitleNode); for (let title in filteredUserLinks) if (Object.prototype.hasOwnProperty.call(filteredUserLinks, title)) { let target = filteredUserLinks[title]; let itemNode = createTag("li", {}); if (target === null) { itemNode.classList.add("subsectionHeader", "separator"); itemNode.appendChild(document.createTextNode(title)); } else { itemNode.appendChild(createTag("a", {a: {href: target}}, title)); setFavicon(itemNode, target); } userLinksListNode.appendChild(itemNode); } } function getLocalisedText(textSet) { if (typeof textSet === "string") { if (textSet.match(/%language%/)) { return textSet.replace(/%language%/g, parseLanguages(["musicbrainz", "navigator"])[0]); } else { return textSet; } } // Manages both "fr" and multinligual "fr vi ja en" formats var expanded = {}; for (var key in textSet) if (Object.prototype.hasOwnProperty.call(textSet, key)) { var allKeys = key.split(" "); for (var ak = 0; ak < allKeys.length; ak++) expanded[allKeys[ak]] = textSet[key].replace(/%language%/g, allKeys[ak]); } textSet = expanded; var languages = parseLanguages(["musicbrainz", "navigator"]); for (var l = 0; l < languages.length; l++) { if (Object.prototype.hasOwnProperty.call(textSet, languages[l])) { return textSet[languages[l]]; } } return textSet[Object.getOwnPropertyNames(textSet)[0]]; } function idToPath(id) { return id.replace(userjs + "_searchLinks" + path_ID_separator, "").split(path_ID_separator); } function loadDisabledSearchLinks() { var loadedSettings = JSON.parse(localStorage.getItem(userjs + "_disabled-search-links")) || {}; for (var itemID in loadedSettings) if (Object.prototype.hasOwnProperty.call(loadedSettings, itemID)) { var itemPath = idToPath(itemID); if (itemPath && pathToItem(itemPath)) { disabledSearchLinks[itemID] = true; } } delete disabledSearchLinks[pathToID(["web"])]; localStorage.setItem(userjs + "_disabled-search-links", JSON.stringify(disabledSearchLinks)); } function pathToItem(path) { var item = searchLinks; for (var i = 0; i < path.length; i++) { item = item.items[path[i]]; if (!item) { return false; } } return item; } function pathToID(path) { var id = userjs + "_searchLinks"; for (var i = 0; i < path.length; i++) id = id + path_ID_separator + path[i]; return id; } function replaceAllTokens(string, encode) { var stringTokens = string.match(/%[a-z]+(?:-[a-z]+)+%/g); if (stringTokens) for (var t = 0; t < stringTokens.length; t++) { var token = stringTokens[t]; if (!Object.prototype.hasOwnProperty.call(tokenValues, token)) { return false; } string = string.replace(token, encode && token != "%url-target%" ? encodeURIComponent(tokenValues[token]) : tokenValues[token]); } return string; } function setFavicon(li, url) { var favclass = "no"; // MusicBrainz cached favicon CSS classes var searchdomain = url.match(/site:([^+]*)\+/); var urldomain = searchdomain ? searchdomain[1] : url.split("/")[2]; for (var classdomain in FAVICON_CLASSES) if (Object.prototype.hasOwnProperty.call(FAVICON_CLASSES, classdomain)) { if (urldomain.match(classdomain)) { favclass = FAVICON_CLASSES[classdomain]; break; } } if (favclass != "no") { li.classList.add(favclass + "-favicon"); } else { // Static favicon URL dictionary var favurlfound = false; for (var part in favicons) if (Object.prototype.hasOwnProperty.call(favicons, part)) { if (url.indexOf(part) != -1) { favurlfound = favicons[part]; break; } } if (!guessOtherFavicons && !favurlfound) { li.classList.add("no-favicon"); } else { // arbitrary /favicon.ico load try out if (guessOtherFavicons && !favurlfound) { favurlfound = url.match(/^(?:https?:)?(\/\/[^/]+)\/(?:.+_action=https?:(\/\/[^/]+)\/)?/); if (favurlfound) { favurlfound = (favurlfound[1] != "//jesus2099.gitlab.io" ? favurlfound[1] : favurlfound[2]) + "/favicon.ico"; } } var ifit = favicontry.length; favicontry[ifit] = new Image(); favicontry[ifit].addEventListener("error", function (event) { this.li.classList.add("no-favicon"); }); favicontry[ifit].addEventListener("load", function (event) { clearTimeout(this.to); this.li.style.setProperty("background-image", "url(" + this.src + ")"); this.li.style.setProperty("background-size", "16px 16px"); }); favicontry[ifit].li = li; favicontry[ifit].src = favurlfound; favicontry[ifit].to = setTimeout(function() { // don’t wait for more than 5 seconds favicontry[ifit].src = ""; favicontry[ifit].li.classList.add("no-favicon"); }, 5000); } } } function toggleEmpty(itemNode, hide) { if (!hide) { if (itemNode.parentNode.parentNode.id === "sidebar") { itemNode.parentNode.classList.remove("emptySection"); itemNode.parentNode.previousElementSibling.classList.remove("emptySection"); } else { itemNode.parentNode.parentNode.classList.remove("emptySection"); toggleEmpty(itemNode.parentNode.parentNode, hide); } } else if (!itemNode.parentNode.classList.contains(pathToID(["web"]))) { var allDisabled = true; var siblings = itemNode.parentNode.children; for (var n = 0; n < siblings.length; n++) { if ((!siblings[n].classList.contains("disabled")) && (!siblings[n].classList.contains("emptySection"))) { allDisabled = false; break; } } if (allDisabled) { if (itemNode.parentNode.parentNode.id === "sidebar") { itemNode.parentNode.classList.add("emptySection"); itemNode.parentNode.previousElementSibling.classList.add("emptySection"); } else { itemNode.parentNode.parentNode.classList.add("emptySection"); toggleEmpty(itemNode.parentNode.parentNode, hide); } } } } function toggleStorage(itemID) { var toggledSettings = JSON.parse(localStorage.getItem(userjs + "_disabled-search-links")) || {}; if (toggledSettings[itemID]) { delete toggledSettings[itemID]; } else { toggledSettings[itemID] = true; } localStorage.setItem(userjs + "_disabled-search-links", JSON.stringify(toggledSettings)); } /* function weirdobg() { var weirdo = userjs + " _" + (new Date().getTime()); try { open("", weirdo).blur(); } catch (error) {} self.focus(); return weirdo; } */ function error(code, text) { var ldng = document.getElementById(userjs + "_loading"); if (ldng) { ldng.setAttribute("id", userjs + "_error"); ldng.style.setProperty("background", "pink"); ldng.replaceChild(document.createTextNode("Error " + code), ldng.firstChild); ldng.appendChild(createTag("a", {a: {href: entityUrlRelsWS}}, "*")); ldng.appendChild(document.createTextNode(" in ")); ldng.appendChild(createTag("a", {a: {href: "http://userscripts-mirror.org/scripts/show/108889"}}, "all links")); ldng.appendChild(document.createTextNode(" (")); ldng.appendChild(createTag("a", {e: {click: function(event) { var err = document.getElementById(userjs + "_error"); if (err) { err.parentNode.removeChild(err); } addHiddenLinks(); }}}, "retry")); ldng.appendChild(document.createTextNode(")")); ldng.appendChild(document.createElement("br")); ldng.appendChild(createTag("i", {}, text)); } else { loading(true); error(code, text); } } function loading(on) { var ldng = document.getElementById(userjs + "_loading"); if (on && !ldng) { extlinks.appendChild(createTag("li", {a: {id: userjs + "_loading"}}, createTag("img", {a: {alt: "loading all links…", src: "/static/images/icons/loading.gif"}}))); var li = extlinks.querySelector("ul.external_links > li.all-relationships"); if (li) { li.style.setProperty("display", "none"); } } else if (ldng && !on) { ldng.parentNode.removeChild(ldng); } } function archivedDate(date, url) { var text = date == "????" ? "ended" : date.replace(/-/g, "‐"); if (!url.match(/\.archive\.org\//)) { var archiveStamp = "*"; if (date != "????") { archiveStamp = date.replace(/\D/g, ""); while (archiveStamp.length < 14) archiveStamp += "0"; } return createTag("a", {a: {href: "//wayback.archive.org/web/" + archiveStamp + "/" + url.replace(/https?:\/\//, ""), title: "Internet Archive Wayback Machine capture"}}, text); } else { return document.createTextNode(text); } } function nsr(prefix) { switch (prefix) { case "mb": return "http://musicbrainz.org/ns/mmd-2.0#"; default: return null; } } function guessNavigatorLanguages() { if (Array.isArray(navigator.languages)) { return navigator.languages; } else { var language = navigator.language || navigator.userLanguage || navigator.browserLanguage || navigator.systemLanguage; if (language) { return [language]; } else { return []; } } } function parseLanguages(inputLanguages) { var detectedLanguages = []; for (let il = 0; il < inputLanguages.length; il++) { let nextLanguage = inputLanguages[il]; switch (nextLanguage) { case "navigator": detectedLanguages = detectedLanguages.concat(guessNavigatorLanguages()); break; case "musicbrainz": detectedLanguages.push(document.documentElement.getAttribute("lang") || "en"); break; default: detectedLanguages.push(nextLanguage); } } var outputLanguages = []; for (let dl = 0; dl < detectedLanguages.length; dl++) { let nextLanguage = detectedLanguages[dl]; if (outputLanguages.indexOf(nextLanguage) < 0) { outputLanguages.push(nextLanguage); if (nextLanguage.match("-")) { var splitLanguage = nextLanguage.split("-")[0]; if (outputLanguages.indexOf(splitLanguage) < 0) { outputLanguages.push(splitLanguage); } } } } return outputLanguages; } function parseArtistCredit(subheader) { var artistCredit = ""; for (var i = 0, acStarted = false; i < subheader.childNodes.length && !(subheader.childNodes[i].nodeType === Node.ELEMENT_NODE && subheader.childNodes[i].tagName === "SPAN" && subheader.childNodes[i].classList.contains("small")); i++) { if (!acStarted && subheader.childNodes[i].nodeType === Node.ELEMENT_NODE && (subheader.childNodes[i].matches("a[href^='/artist/']") || subheader.childNodes[i].querySelector("a[href^='/artist/']"))) { acStarted = true; } if (acStarted) { artistCredit += subheader.childNodes[i].textContent; } } return artistCredit.trim(); } function configureModule(event) { switch (event.target.getAttribute("title")) { case "configure user autolinks": // TODO: provide a real editor var loadedUserAutolinks = localStorage.getItem(userjs + "_user-autolinks") || {}; var newUserAutolinks = prompt("Edit your user autolinks\nCopy/paste in a real editor\nSorry for such an awful prompt\n\nAvailable variables:\n- for all entity pages: %entity-type%, %entity-mbid% and %entity-name%\n- for \"foobar\" entity pages: %foobar-mbid% and %foobar-name% where \"foobar\" is an entity type.\n- for artist entity pages: %artist-sort-name%, %artist-family-name-first% and %artist-latin-script-name%\n- for url entity pages: %url-target% (while %entity-name% and %url-name% are deliberately ignored)\n\nExample: {\"Search for reviews\": \"//duckduckgo.com/?q=%22%entity-name%%22+reviews\",\n\"Search for fans\": \"//duckduckgo.com/?q=%22%artist-name%%22+fans\",\n\"Works\": \"/ws/2/artist/%artist-mbid%?inc=works\",\n\"La FNAC\": \"http://recherche.fnac.com/SearchResult/ResultList.aspx?SCat=3%211&Search=%release-name%&sft=1&sa=0\"}", loadedUserAutolinks); if (newUserAutolinks && newUserAutolinks != loadedUserAutolinks && JSON.stringify(newUserAutolinks)) { localStorage.setItem(userjs + "_user-autolinks", newUserAutolinks); } break; case "filter search links": /* var topSectionNodes = */ sidebar.children; for (var n = 0; n < sidebar.children.length; n++) { if (sidebar.children[n].classList.contains(userjs + "_searchLinks")) { sidebar.children[n].classList.toggle("configure"); } } break; case "choose languages": var defaultLanguages = parseLanguages(["navigator", "musicbrainz"]); var navigatorLanguages = guessNavigatorLanguages(); var musicbrainzLanguage = document.documentElement.getAttribute("lang") || "en"; var loadedLanguages = (localStorage.getItem(userjs + "_languages") || JSON.stringify(rawLanguages)).replace(/,/g, "$& ").replace(/\s+/g, " "); var newLanguages = prompt("Choose your favourite language(s)\n\nType a language array: [\"favourite language\", \"second favourite\", …, \"least favourite\"]\n\nTwo meta languages can be used:\n- \"navigator\" for navigator settings, currently " + (navigatorLanguages.length > 0 ? "detected as " + JSON.stringify(navigatorLanguages).replace(/,/g, "$& ").replace(/\s+/g, " ") : "undetected") + "\n- \"musicbrainz\" for selected MusicBrainz UI language, currently " + (musicbrainzLanguage ? "detected as [" + JSON.stringify(musicbrainzLanguage) + "]" : "undetected") + "\n\nDefault:\n- [\"navigator\", \"musicbrainz\"], currently expands to " + JSON.stringify(defaultLanguages).replace(/,/g, "$& ").replace(/\s+/g, " ") + "\n\nSome examples:\n- [\"musicbrainz\", \"fr-FR\", \"en-GB\", \"vi\", \"ja\", \"navigator\"]\n- [\"fr\", \"en\", \"vi\", \"ja\"]\n- [\"en-GB\"]\n- [\"fr-FR\", \"navigator\", \"en-GB\", \"musicbrainz\"]\n- []" + "\n\nCurrent setting expands to " + JSON.stringify(parseLanguages(JSON.parse(loadedLanguages))).replace(/,/g, "$& ").replace(/\s+/g, " "), loadedLanguages); if ( newLanguages && (newLanguages = newLanguages.match(/\[(\s*["'](navigator|musicbrainz|\w{2}(-\w{2,}(-\w{2,})?)?)['"]\s*,?\s*)*]/)) && (newLanguages = newLanguages[0]) && newLanguages != loadedLanguages && JSON.parse(newLanguages) ) { localStorage.setItem(userjs + "_languages", newLanguages); rawLanguages = JSON.parse(newLanguages); } break; } }