User:Jean-Frédéric/ExLudo.js

From Wikidata
Jump to navigation Jump to search

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 expansions and mods on item pages for video games
 * 
 * To use it, add the following line to your common.js:
 * mw.loader.load("//www.wikidata.org/w/index.php?title=User:Jean-Frédéric/ExLudo.js&action=raw&ctype=text/javascript");
 * 
 * 
 * @license CC0-1.0
 * @version 0.0.10
*/
/* jshint esversion: 6 */

(function () {

	let translations = {
		en: {
			/* Headings */
			"expansions": "Expansions",
			"mods": "Mods",
			"on-compilations": "On the compilations",
			"based-on": "Are based on this game",
			"using-this-engine": "Using this engine",
			"in-series": "In this series",
			"in-franchise": "Part of this franchise",
			"award-winners": "Winners",
			"sub-genres": "Sub-genres",
			"demos": "Demos",
			"in-universe": "Takes place in this universe",
			
			/* Formatting */
			"brackets-format": " ($1)",
			"comma-format": ", ",
			"year-format": "$1",
		},
		fr: {
			"expansions": "Extensions",
			"mods": "Mods",
			"on-compilations": "Présent sur les compilations",
			"based-on": "Basés sur ce jeu",
			"using-this-engine": "Utilisant ce moteur de jeu",
			"in-series": "Dans cette série",
			"in-franchise": "Dans cette franchise",
			"in-universe": "Dans cet univers",
			"award-winners": "Lauréats",
			"sub-genres": "Sous-genres",
			"demos": "Démos",
		},
	};

	let langs = [
		// 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_games = [
		"Q7889",
	];

	let p31values_franchise = [
		"Q196600",
	];

	let p31values_engines = [
		"Q193564",
		"Q62505",
		"Q63533016",
		"Q355134",
		"Q17083583"
	];
	
	let p31values_on_compilations = [
		"Q7889",
		"Q209163",
	];

	let p31values_based_on = [
		"Q7889",
	];

	let p31values_series = [
		"Q7058673",
	];

	let p31values_award = [
		"Q18328126",
	];

	let p31values_genres = [
		"Q659563",
		"Q42907216"
	];

	let p31values_universe = [
		"Q559618",
	];

	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_games)) {

			function game_query (prop, heading) {
				let query = "\
					select ?item ?itemLabel (min(year(?date)) as ?year) ?dlc ?expansion {\
					  ?item wdt:" + prop+ " wd:" + e.id + ".\
					  optional { ?item wdt:P577 ?date }\
					  optional { ?item wdt:P31 ?dlc filter (?dlc = wd:Q1066707) }\
					  optional { ?item wdt:P31 ?expansion filter (?expansion = wd:Q209163) }\
					  service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
					} group by ?item ?itemLabel ?dlc ?expansion order by ?year";


				runquery(query, $.i18n(heading), function (i, v) {
					let qid = v.item.value.replace(/.*\//, "");
					let attr = [];

/*					let format = [];
					if (obj.rows[0].hasOwnProperty("dlc"))
						format.push("[DLC]");

*/					if (v.year)
						attr.push($.i18n("year-format", v.year.value));
					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>");
				});
			}

			game_query("P8646", "expansions");
			game_query("P7075", "mods");
			game_query("P12050", "demos");
		}

		if (hasp31(e.claims, p31values_on_compilations)) {
			let query = "\
				select ?item ?itemLabel (min(year(?date)) as ?year)  {\
				  ?item wdt:P527 wd:" + e.id + ".\
				  ?item wdt:P31 wd:Q16070115.\
				  optional { ?item wdt:P577 ?date }\
				  service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				} group by ?item ?itemLabel order by ?year";


			runquery(query, $.i18n("on-compilations"), 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>");
			});

		}

		if (hasp31(e.claims, p31values_engines)) {
			let query = "\
				select ?item ?itemLabel (min(year(?date)) as ?year)  {\
				  ?item wdt:P408 wd:" + e.id + ".\
				  optional { ?item wdt:P577 ?date }\
				  service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				} group by ?item ?itemLabel order by ?year";


			runquery(query, $.i18n("using-this-engine"), 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>");
			});

		}

		if (hasp31(e.claims, p31values_based_on)) {
			let query = "\
				select ?item ?itemLabel ?subject ?subjectLabel (min(year(?date)) as ?year)  {\
				  ?item p:P144 ?based_on_statement.\
                  ?based_on_statement ps:P144 wd:" + e.id + ".\
                  optional { ?based_on_statement  pq:P2868 ?subject }\
				  optional { ?item wdt:P577 ?date }\
				  service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				} group by ?item ?itemLabel ?subject ?subjectLabel order by ?year";


			runquery(query, $.i18n("based-on"), function (i, v) {
				let qid = v.item.value.replace(/.*\//, "");
				let attr = [];
				let year = "";
				if (v.year)
					attr.push($.i18n("year-format", v.year.value));
				if (v.subject) {
					let fqid = v.subject.value.replace(/.*\//, "");
					let flink = "<a href=\"/wiki/" + fqid + "\">" + mw.html.escape(v.subjectLabel.value) + "</a>";
					attr.push(flink);
				}
				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>");
			});

		}

		if (hasp31(e.claims, p31values_series)) {
			let query = `
				select ?item ?itemLabel ?subitem ?subitemLabel (min(year(?date)) as ?year) ?subitemyear
				with {
				  select ?subitem ?subitemLabel ?item (min(year(?date)) as ?subitemyear) {
				    ?subitem wdt:P8646 ?item.
                    optional { ?subitem wdt:P577 ?date }
				    ?item wdt:P179 wd:${e.id}.
				  } group by ?subitem ?subitemLabel ?item
				} as %subitems
				{
				  ?item wdt:P179 wd:${e.id}.
				  values ?type { wd:Q7889 wd:Q16070115 wd:Q4393107 wd:Q65963104 }
				  ?item wdt:P31 ?type.
				  optional { ?item wdt:P577 ?date }
				  optional { include %subitems }
				  service wikibase:label { bd:serviceParam wikibase:language "${langs},en" }
				} group by ?item ?itemLabel ?subitem ?subitemLabel ?subitemyear order by ?year ?itemLabel ?subitemLabel
			`;

			runquery(query, $.i18n("in-series"), function (qid, obj) {
				// The "true" parameter passed to runquery (after this callback
				// function) causes it to group rows by ?item. The individual
				// rows are in obj.rows.
				// Here it creates a variable for the first row, for convenience
				// when using the data which is the same for each row.
				let v = obj.rows[0];
				let subitems = [];

				for (let row of obj.rows) {
					if (row.hasOwnProperty("subitem")) {

						let attr = [];

						if (v.subitemyear)
							attr.push($.i18n("year-format", v.subitemyear.value));

						let attrtext = "";
						if (attr.length > 0)
							attrtext = $.i18n("brackets-format", attr.join($.i18n("comma-format")));

						let subid = row.subitem.value.replace(/.*\//, "");
						let subitem = `<li><a href="/wiki/${ subid }">${ mw.html.escape(row.subitemLabel.value) }</a> ${attrtext} </li>`;
						subitems.push(subitem);
					}
				}
				
				let attr = [];
				let year = "";
				if (v.year)
					attr.push($.i18n("year-format", v.year.value));
				if (v.subject) {
					let fqid = v.subject.value.replace(/.*\//, "");
					let flink = "<a href=\"/wiki/" + fqid + "\">" + mw.html.escape(v.subjectLabel.value) + "</a>";
					attr.push(flink);
				}
				let attrtext = "";
				if (attr.length > 0)
					attrtext = $.i18n("brackets-format", attr.join($.i18n("comma-format")));
				let sublist = "";
				if (subitems.length) {
					sublist = "<ul>" + subitems.join("") + "</ul>";
				}
				list.push("<li><a href=\"/wiki/" + qid + "\">" + mw.html.escape(v.itemLabel.value) + "</a>" + attrtext + sublist + "</li>");

			}, true);

		}


		if (hasp31(e.claims, p31values_franchise)) {
			let query = "\
				select ?item ?itemLabel (min(?dates) as ?date) (min(year(?dates)) as ?year) ?print ?tv ?movie ?theatre ?game ?board ?card ?album ?audio ?amusement ?show{\
					?item wdt:P8345 wd:" + e.id + ".\
					MINUS {?item wdt:P31/wdt:P279* wd:Q14897293.}. \
					MINUS {?item wdt:P31 wd:Q15831596.}\
					optional { ?item wdt:P577 ?publication_date }.\
    				optional { ?item wdt:P580 ?start_date }.\
    				bind(COALESCE(?publication_date, ?start_date) as ?dates) .\
					optional { ?item wdt:P31 ?print filter (?print IN (wd:Q2831984, wd:Q3297186, wd:Q14406742, wd:Q103076, wd:Q8274, wd:Q1760610, wd:Q21198342, wd:Q7725634, wd:Q47461344, wd:Q104213567)) }\
					optional { ?item wdt:P31 ?tv filter (?tv IN (wd:Q5398426, wd:Q63952888, wd:Q104775758, wd:Q220898, wd:Q1259759, wd:Q581714, wd:Q117467246)) }\
					optional { ?item wdt:P31 ?movie filter (?movie IN (wd:Q29168811, wd:Q24869, wd:Q202866, wd:Q24862, wd:Q11424, wd:Q20650540)) }\
					optional { ?item wdt:P31 ?theatre filter (?theatre IN (wd:Q58483083)) }\
					optional { ?item wdt:P31 ?game filter (?game IN (wd:Q7889, wd:Q16070115)) }\
					optional { ?item wdt:P31 ?board filter (?board IN (wd:Q131436, wd:Q1643932)) }\
					optional { ?item wdt:P31 ?card filter (?card IN (wd:Q734698)) }\
					optional { ?item wdt:P31 ?album filter (?album IN (wd:Q482994)) }\
					optional { ?item wdt:P31 ?audio filter (?audio IN (wd:Q105543609)) }\
					optional { ?item wdt:P31 ?amusement filter (?amusement IN (wd:Q1144661, wd:Q512504, wd:Q1934961)) }\
					optional { ?item wdt:P31 ?show filter (?show IN (wd:Q15116915, wd:Q85475422)) }\
					service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				} group by ?item ?itemLabel ?print ?tv ?movie ?theatre ?game ?board ?card ?album ?audio ?amusement ?show order by ?date" ;

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

				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("print"))
					format.push("📚");
				if (obj.rows[0].hasOwnProperty("tv"))
					format.push("📺");
				if (obj.rows[0].hasOwnProperty("movie"))
					format.push("🎬");
				if (obj.rows[0].hasOwnProperty("theatre"))
					format.push("🎭");
				if (obj.rows[0].hasOwnProperty("game"))
					format.push("🎮");
				if (obj.rows[0].hasOwnProperty("board"))
					format.push("🎲");
				if (obj.rows[0].hasOwnProperty("card"))
					format.push("🎴");
				if (obj.rows[0].hasOwnProperty("album"))
					format.push("💿");
				if (obj.rows[0].hasOwnProperty("audio"))
					format.push("🎵");
				if (obj.rows[0].hasOwnProperty("amusement"))
					format.push("🎢");
				if (obj.rows[0].hasOwnProperty("show"))
					format.push("🎪");
				list.push(
					"<li>"
					+ "<span class=\"format\">" + format.join("") + "</span> "
					+ "<a href=\"/wiki/" + qid + "\">"
					+ mw.html.escape(obj.rows[0].itemLabel.value)
					+ "</a>"
					+ date
					+ "</li>"
				);
			}, true);
		}

		if (hasp31(e.claims, p31values_universe)) {
			let query = "\
				select ?item ?itemLabel (min(?dates) as ?date) (min(year(?dates)) as ?year) ?print ?tv ?movie ?theatre ?game ?board ?card ?album ?audio ?amusement ?show{\
					?item wdt:P1434 wd:" + e.id + ".\
					optional { ?item wdt:P577 ?publication_date }.\
    				optional { ?item wdt:P580 ?start_date }.\
    				bind(COALESCE(?publication_date, ?start_date) as ?dates) .\
					optional { ?item wdt:P31 ?print filter (?print IN (wd:Q2831984, wd:Q3297186, wd:Q14406742, wd:Q103076, wd:Q8274, wd:Q1760610, wd:Q21198342, wd:Q7725634, wd:Q47461344, wd:Q104213567)) }\
					optional { ?item wdt:P31 ?tv filter (?tv IN (wd:Q5398426, wd:Q63952888, wd:Q104775758, wd:Q220898, wd:Q1259759, wd:Q581714, wd:Q117467246)) }\
					optional { ?item wdt:P31 ?movie filter (?movie IN (wd:Q29168811, wd:Q24869, wd:Q202866, wd:Q24862, wd:Q11424, wd:Q20650540)) }\
					optional { ?item wdt:P31 ?theatre filter (?theatre IN (wd:Q58483083)) }\
					optional { ?item wdt:P31 ?game filter (?game IN (wd:Q7889, wd:Q16070115)) }\
					optional { ?item wdt:P31 ?board filter (?board IN (wd:Q131436, wd:Q1643932)) }\
					optional { ?item wdt:P31 ?card filter (?card IN (wd:Q734698)) }\
					optional { ?item wdt:P31 ?album filter (?album IN (wd:Q482994)) }\
					optional { ?item wdt:P31 ?audio filter (?audio IN (wd:Q105543609)) }\
					optional { ?item wdt:P31 ?amusement filter (?amusement IN (wd:Q1144661, wd:Q512504, wd:Q1934961)) }\
					optional { ?item wdt:P31 ?show filter (?show IN (wd:Q15116915, wd:Q85475422)) }\
					service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				} group by ?item ?itemLabel ?print ?tv ?movie ?theatre ?game ?board ?card ?album ?audio ?amusement ?show order by ?date" ;

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

				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("print"))
					format.push("📚");
				if (obj.rows[0].hasOwnProperty("tv"))
					format.push("📺");
				if (obj.rows[0].hasOwnProperty("movie"))
					format.push("🎬");
				if (obj.rows[0].hasOwnProperty("theatre"))
					format.push("🎭");
				if (obj.rows[0].hasOwnProperty("game"))
					format.push("🎮");
				if (obj.rows[0].hasOwnProperty("board"))
					format.push("🎲");
				if (obj.rows[0].hasOwnProperty("card"))
					format.push("🎴");
				if (obj.rows[0].hasOwnProperty("album"))
					format.push("💿");
				if (obj.rows[0].hasOwnProperty("audio"))
					format.push("🎵");
				if (obj.rows[0].hasOwnProperty("amusement"))
					format.push("🎢");
				if (obj.rows[0].hasOwnProperty("show"))
					format.push("🎪");
				list.push(
					"<li>"
					+ "<span class=\"format\">" + format.join("") + "</span> "
					+ "<a href=\"/wiki/" + qid + "\">"
					+ mw.html.escape(obj.rows[0].itemLabel.value)
					+ "</a>"
					+ date
					+ "</li>"
				);
			}, true);
		}

		if (hasp31(e.claims, p31values_award)) {
			let query = "\
				select ?item ?itemLabel (year(?time) as ?year) {\
				  ?item p:P166 ?statement.\
                  ?statement ps:P166 wd:" + e.id + ".\
				  optional { ?statement pq:P585 ?time }\
				  service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				} order by ?year";


			runquery(query, $.i18n("award-winners"), function (i, v) {
				let qid = v.item.value.replace(/.*\//, "");
				let attr = [];
				let year = "";
				if (v.year)
					attr.push($.i18n("year-format", v.year.value));
				if (v.subject) {
					let fqid = v.subject.value.replace(/.*\//, "");
					let flink = "<a href=\"/wiki/" + fqid + "\">" + mw.html.escape(v.subjectLabel.value) + "</a>";
					attr.push(flink);
				}
				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>");
			});

		}

		if (hasp31(e.claims, p31values_genres)) {
			let query = "\
				select ?item ?itemLabel {\
				  ?item wdt:P279 wd:" + e.id + ".\
				  service wikibase:label { bd:serviceParam wikibase:language \"" + langs + ",en\". }\
				}";


			runquery(query, $.i18n("sub-genres"), function (i, v) {
				let qid = v.item.value.replace(/.*\//, "");
				let attr = [];
				let year = "";
				if (v.subject) {
					let fqid = v.subject.value.replace(/.*\//, "");
					let flink = "<a href=\"/wiki/" + fqid + "\">" + mw.html.escape(v.subjectLabel.value) + "</a>";
					attr.push(flink);
				}
				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>");
			});

		}

	});
	});

})();