User:Nikki/ExMusica.js

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/**
 * This adds links to band members on item pages for musical groups
 * and links to recordings/performances on item pages for musical works.
 * 
 * To use it, add the following line to your common.js:
 * mw.loader.load("//www.wikidata.org/w/index.php?title=User:Nikki/ExMusica.js&action=raw&ctype=text/javascript");
 * 
 * @license CC0-1.0
 * @version 0.6.1
*/
/* jshint esversion: 6 */

(function () {

	let translations = {
		en: {
			/* Headings */
			"members": "Members",
			"on-tracklist-of": "On the tracklist of",
			"parts": "Parts",
			"recordings-performances": "Recordings and performances",
			"reissues": "Reissues",
			"releases": "Releases",
			"remixes": "Remixes",
			"soundtrack-to": "Soundtrack to",
			"translations": "Translations",
			"uses-melody": "Uses this melody",

			"no-performers": "no performers",
			
			/* Formatting */
			"brackets-format": " ($1)",
			"comma-format": ", ",
			"year-format": "$1",
		},
		de: {
			"members": "Mitglieder",
			"on-tracklist-of": "Auf der Titelliste von",
			"parts": "Teile",
			"releases": "Veröffentlichungen",
			"soundtrack-to": "Soundtrack zu",
			"translations": "Übersetzungen",
		},
		ja: {
			"members": "メンバー",
			"reissues": "リイシュー",
			"translations": "翻訳",
			"brackets-format": "($1)",
			"comma-format": "、",
			"year-format": "$1年",
		},
	};

	let langs = [...new Set([
		// If the UI language is English, the chain is only ["en"] and
		// using slice() to remove the last array element will result in
		// an empty array. To avoid that, we use shift() to get the first
		// element and slice() to get anything between the first and last
		// elements separately.
		mw.language.getFallbackLanguageChain().shift(),
		mw.language.getFallbackLanguageChain().slice(1, -1),
		mw.config.get("wgULSBabelLanguages") || [],
		mw.config.get("wgULSAcceptLanguageList") || [],
		"en"
	].flat().map(function (x) { return x.toLowerCase(); }))].join(",");


	// P31 values to run this script on
	let p31values_members = [
		"Q207338", "Q42998", "Q215380", "Q216337", "Q239582", "Q641066",
		"Q762048", "Q1261230", "Q1426710", "Q5741069", "Q9212979", "Q10269077",
		"Q11264216", "Q11446438", "Q19464263", "Q25391823", "Q56816954",
	];
	let p31values_works = [
		"Q105543609",
		"Q58483083",
	];
	let p31values_releases = [
		"Q482994",
		"Q134556",
		"Q169930",
		"Q106042566"
	];
	let p31values_melodies = [
		"Q105543609",
	];
	let p31values_translations = [
		"Q105543609",
	];
	let p31values_parts = [
		"Q100775361",
	];
	let p31values_soundtracks = [
		"Q482994", "Q134556", "Q169930",
		"Q6128115", "Q20089094", "Q10590726",
		"Q20135338", "Q394970", "Q1892995",
		"Q841983", "Q368281", "Q2031291",
	];
	let p31values_tracks = [
		"Q55850593",
		"Q55850643",
		"Q7302866",
	];

	let list = [];
	let newlist = {};

	function hasp31(claims, list) {
		for (let i = 0; i < claims.P31.length; i++) {
			if (list.includes( claims.P31[i].mainsnak.datavalue.value.id ))
				return true;
		}
		return false;
	}

	function runquery(query, heading, callback, usenew) {
		$.post("https://query.wikidata.org/sparql?format=json", { query: query }, function (data) {
			if (!data.results.bindings.length)
				return;

			list = [];
			newlist = {};
			$.each(data.results.bindings, function (i, v) {
				let qid = v.item.value.replace(/.*\//, "");				
				if (!newlist.hasOwnProperty(qid))
					newlist[qid] = { "qid": qid, "rows": [] };
				newlist[qid].rows.push(v);
			});
			
			let dobj = data.results.bindings;
			if (usenew)
				dobj = newlist;

			$.each(dobj, callback);
			$(".wikibase-sitelinkgrouplistview").append(
				"<div style=\"clear: both\">"
				+ "<h3 style=\"display: inline\">" + mw.html.escape(heading) + "</h3>"
				+ "<ul>" + list.join("\n") + "</ul>"
				+ "</div>"
			);
		});
	}

	mw.loader.using("jquery.i18n").then(function() {
	mw.hook("wikibase.entityPage.entityLoaded").add(function (e) {
		if (e.type !== "item")
			return;

		if (!e.claims.hasOwnProperty("P31"))
			return;

		$.i18n().load(translations);

		if (hasp31(e.claims, p31values_members)) {
			let query = "\
				select ?item ?itemLabel ?date {\
					?item p:P463 ?s.\
					?s ps:P463 wd:" + e.id + ".\
					optional { ?s pq:P580 ?start }\
					optional { ?s pq:P582 ?end }\
					bind(if(bound(?start) || bound(?end), concat(coalesce(str(year(?start)),\"\"),\"–\",coalesce(str(year(?end)),\"\")), \"\") as ?date).\
					service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				} order by bound(?end) ?end bound(?start) ?start ?itemLabel";

			runquery(query, $.i18n("members"), function (i, v) {
				let qid = v.item.value.replace(/.*\//, "");
	            let date = "";
				if (v.date.value != "") {
					date = $.i18n("brackets-format", $.i18n("year-format", v.date.value));
				}
				list.push("<li><a href=\"/wiki/" + qid + "\">" + mw.html.escape(v.itemLabel.value) + "</a>" + mw.html.escape(date) + "</li>");
			});

		} else if (hasp31(e.claims, p31values_works)) {
			let query = "\
				select ?item ?performer ?itemLabel ?performerLabel (min(year(?date)) as ?year) ?audio ?video ?tv ?contest ?inst ?live (sample(?p1476) as ?title) {\
					?item wdt:P2550 wd:" + e.id + ".\
					optional { ?item wdt:P175 ?performer }\
					optional { ?item wdt:P10135 ?date }\
					optional { ?item wdt:P31 ?audio filter (?audio = wd:Q55850593) }\
					optional { ?item wdt:P31 ?inst filter (?inst = wd:Q55850643) }\
					optional { ?item wdt:P31 ?video filter (?video = wd:Q193977) }\
					optional { ?item wdt:P31 ?tv filter (?tv = wd:Q78331840) }\
					optional { ?item wdt:P31 ?live filter (?live = wd:Q106805967) }\
					optional { ?item wdt:P31 ?contest filter (?contest in (wd:Q63483382, wd:Q63481999, wd:Q106588852, wd:Q107080092)) }\
					optional { ?item wdt:P1476 ?p1476 }\
					service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				} group by ?item ?performer ?itemLabel ?performerLabel ?audio ?video ?tv ?contest ?inst ?live";

			runquery(query, $.i18n("recordings-performances"), function (qid) {
				let obj = newlist[qid];

				let performers = [];
				for (let v of obj.rows) {
					if (v.performer) {
						let pqid = v.performer.value.replace(/.*\//, "");
						let perf = "<a href=\"/wiki/" + pqid + "\">" + mw.html.escape(v.performerLabel.value) + "</a>";
						performers.push(perf);
					}
				}
				if (performers.length === 0)
					performers.push("<em>" + $.i18n("no-performers") + "</em>");

				let date = "";
				if (obj.rows[0].year)
					date = $.i18n("brackets-format", $.i18n("year-format", obj.rows[0].year.value));

				let format = [];
				if (obj.rows[0].hasOwnProperty("audio"))
					format.push("🎵🎤");
				if (obj.rows[0].hasOwnProperty("inst"))
					format.push("🎵");
				if (obj.rows[0].hasOwnProperty("live"))
					format.push("🎵🎪");
				if (obj.rows[0].hasOwnProperty("video"))
					format.push("🎬");
				if (obj.rows[0].hasOwnProperty("tv"))
					format.push("📺");
				if (obj.rows[0].hasOwnProperty("contest"))
					format.push("🏆");

				let title = obj.rows[0].title || obj.rows[0].itemLabel;

				list.push(
					"<li>"
					+ "<span class=\"format\">" + format.join("") + "</span> "
					+ "<a href=\"/wiki/" + qid + "\" lang=\"" + title["xml:lang"] + "\">"
					+ mw.html.escape(title.value)
					+ "</a> - "
					+ performers.join(", ")
					+ date
					+ "</li>"
				);
			}, true);
		} else if (hasp31(e.claims, p31values_releases)) {
			function release_query (prop, heading) {
				let query = "\
					select ?item (min(?year_) as ?year) ?country ?format ?itemLabel ?countryLabel ?formatLabel {\
						?item wdt:" + prop+ " wd:" + e.id + ".\
						optional { ?item wdt:P577 ?date. bind(year(?date) as ?year_). }\
						optional { ?item wdt:P437 ?format }\
						optional { ?item wdt:P291 ?country. }\
						service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
					} group by ?item ?country ?format ?itemLabel ?countryLabel ?formatLabel order by ?year";

				runquery(query, $.i18n(heading), function (i, v) {
					let qid = v.item.value.replace(/.*\//, "");
					let attr = [];
					if (v.year)
						attr.push($.i18n("year-format", v.year.value));
					if (v.format) {
						let fqid = v.format.value.replace(/.*\//, "");
						let flink = "<a href=\"/wiki/" + fqid + "\">" + mw.html.escape(v.formatLabel.value) + "</a>";
						attr.push(flink);
					}
					if (v.country) {
						let cqid = v.country.value.replace(/.*\//, "");
						let clink = "<a href=\"/wiki/" + cqid + "\">" + mw.html.escape(v.countryLabel.value) + "</a>";
						attr.push(clink);
					}
					let attrtext = "";
					if (attr.length > 0)
						attrtext = $.i18n("brackets-format", attr.join($.i18n("comma-format")));
					list.push("<li><a href=\"/wiki/" + qid + "\">" + mw.html.escape(v.itemLabel.value) + "</a>" + attrtext + "</li>");
				});
			}

			release_query("P9237", "reissues");
			release_query("P9831", "releases");

		} else if (hasp31(e.claims, p31values_parts)) {
			let query = "\
				select ?item ?itemLabel ?code {\
					?item p:P361 ?st.\
					?st ps:P361 wd:" + e.id + ".\
					optional { ?st pq:P528 ?code }\
					bind(replace(?code, \"^([^0-9]*[0-9]+)([^0-9].*)?\", \"$1\") as ?ncode).\
					service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				} order by (!bound(?code)) strlen(?ncode) ?code ?itemLabel";

			runquery(query, $.i18n("parts"), function (i, v) {
				let qid = v.item.value.replace(/.*\//, "");
				let code = "";
				if (v.code)
					code = $.i18n("brackets-format", v.code.value);
				list.push("<li><a href=\"/wiki/" + qid + "\">" + mw.html.escape(v.itemLabel.value) + "</a>" + mw.html.escape(code) + "</li>");
			});
		} else if (hasp31(e.claims, p31values_tracks)) {
			function track_query (prop, heading) {
				let query = "\
					select ?item (coalesce(?title, ?label) as ?itemLabel) (year(?date) as ?year) {\
						?item wdt:" + prop + " wd:" +  e.id + ".\
						optional { ?item wdt:P577 ?date; }\
						optional { ?item wdt:P1476 ?title }\
						service wikibase:label {\
							bd:serviceParam wikibase:language \"" + langs + ",en\".\
							?item rdfs:label ?label\
						}\
					} order by (!bound(?date)) ?date";

				runquery(query, $.i18n(heading), function (i, v) {
					let qid = v.item.value.replace(/.*\//, "");
					let date = "";
					if (v.year)
						date = $.i18n("brackets-format", $.i18n("year-format", v.year.value));
					list.push("<li><a href=\"/wiki/" + qid + "\">" + mw.html.escape(v.itemLabel.value) + "</a>" + date + "</li>");
				});
			}

			track_query("P658", "on-tracklist-of");
			track_query("P9810", "remixes");
		}
		if (hasp31(e.claims, p31values_melodies)) {
			let query = "\
				select ?item ?itemLabel (group_concat(?langLabel_; separator=\", \") as ?langLabel) {\
					?item wdt:P1625 wd:" + e.id + ".\
					optional { ?item wdt:P407 ?lang }\
					service wikibase:label {\
						bd:serviceParam wikibase:language \"" + langs + ",en\".\
						?item rdfs:label ?itemLabel.\
						?lang rdfs:label ?langLabel_.\
					}\
				} group by ?item ?itemLabel order by ?itemLabel ?langLabel ?item";

			runquery(query, $.i18n("uses-melody"), function (i, v) {
				let qid = v.item.value.replace(/.*\//, "");
				let lang = "";
				if (v.langLabel) {
					lang = $.i18n("brackets-format", v.langLabel.value);
				}
				list.push("<li><a href=\"/wiki/" + qid + "\">" + mw.html.escape(v.itemLabel.value) + "</a>" + mw.html.escape(lang) + "</li>");
			});
		}
		if (hasp31(e.claims, p31values_translations)) {
			let query = "\
				select ?item ?itemLabel (group_concat(?langLabel_; separator=\", \") as ?langLabel) {\
					?item wdt:P9745 wd:" + e.id + ".\
					optional { ?item wdt:P407 ?lang }\
					service wikibase:label {\
						bd:serviceParam wikibase:language \"" + langs + ",en\".\
						?item rdfs:label ?itemLabel.\
						?lang rdfs:label ?langLabel_.\
					}\
				} group by ?item ?itemLabel order by ?itemLabel ?langs ?item";

			runquery(query, $.i18n("translations"), function (i, v) {
				let qid = v.item.value.replace(/.*\//, "");
				let lang = "";
				if (v.langLabel) {
					lang = $.i18n("brackets-format", v.langLabel.value);
				}
				list.push("<li><a href=\"/wiki/" + qid + "\">" + mw.html.escape(v.itemLabel.value) + "</a>" + mw.html.escape(lang) + "</li>");
			});
		}
		if (hasp31(e.claims, p31values_soundtracks)) {
			let query = "\
				select ?item ?itemLabel (min(year(?date)) as ?year) {\
					?item wdt:P406 wd:" + e.id + ".\
					optional { ?item wdt:P577 ?date }\
					service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				}  group by ?item ?itemLabel order by ?year ?itemLabel";

			runquery(query, $.i18n("soundtrack-to"), function (i, v) {
				let qid = v.item.value.replace(/.*\//, "");
				let attr = [];
				let year = "";
				if (v.year)
					year = $.i18n("brackets-format", $.i18n("year-format", v.year.value));
				list.push("<li><a href=\"/wiki/" + qid + "\">" + mw.html.escape(v.itemLabel.value) + "</a>" + year + "</li>");
			});
		}

	});
	});

})();