User:Nikki/KeyShortcuts.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 script is based on [[MediaWiki:Gadget-KeyShortcuts.js]]
   and adds key shortcuts for a variety of common actions, including:
    * Editing labels and lemmas.
    * Adding new statements, forms and senses.
    * Adding another statement for the same property.
    * Editing or adding a reference to the last saved statement.
    * Adding a statement to the last saved form or sense.
    * Opening the merge dialog.
    * Copying the entity ID.
    * Toggling the display of labels for all languages.

   The shortcut `?` will show a popup dialog with a list of supported shortcuts.

   If you'd like to use it, add the following to your common.js:
   mw.loader.load("//www.wikidata.org/w/index.php?title=User:Nikki/KeyShortcuts.js&action=raw&ctype=text/javascript");
*/
// jshint esnext: false, esversion: 6

$(function () {
	'use strict';
	if (!mw.config.exists("wbEntityId") && mw.config.get("wgNamespaceNumber") !== 640)
		return;

	let translations = {
		"en": {
			"heading-general": "General",
			"heading-lexemes": "Lexemes",
			"key-add-statement": "Add statement",
			"key-add-another-statement": "Add another statement",
			"key-edit-last-statement": "Edit last statement",
			"key-view-history": "View history",
			"key-view-item": "View item",
			"key-new-item": "New item",
			"key-edit-terms": "Edit terms/lemma",
			"key-merge": "Open merge dialog",
			"key-select-id": "Select and copy ID",
			"key-add-reference": "Add reference",
			"key-nameguzzler": "Open VIP's labels",
			"key-add-wikipedia-link": "Add Wikipedia link",
			"key-toggle-languages": "Toggle all/fewer languages",
			"key-toggle-termbox": "Toggle termbox",
			"key-toggle-references": "Show/hide references",
			"key-toggle-help": "Toggle this help",
			"key-add-form": "Add form",
			"key-add-form-statement": "Add form statement",
			"key-new-lexeme": "New lexeme",
			"key-add-sense": "Add sense",
			"key-add-sense-statement": "Add sense statement",
			"key-add-ipa": "Add IPA",
			"key-toggle-sidebar": "Show/hide sidebar",
		},
		"de": {
			"heading-general": "Allgemein",
			"heading-lexemes": "Lexeme",
			"key-add-statement": "Neue Aussage hinzufügen",
			"key-add-another-statement": "Noch eine Aussage hinzufügen",
			"key-edit-last-statement": "Vorige Aussage bearbeiten",
			"key-view-history": "Versionsgeschichte zeigen",
			"key-view-item": "Objekt ziegen",
			"key-new-item": "Neues Objekt",
			"key-edit-terms": "Bezeichnungen/Lemma bearbeiten",
			"key-merge": "Zusammenlegendialog öffnen",
			"key-select-id": "ID auswählen und kopieren",
			"key-add-reference": "Fundstelle hinzufügen",
			"key-nameguzzler": "VIP's labels öffnen",
			"key-add-wikipedia-link": "Wikipedia-Link hinzufügen",
			"key-toggle-languages": "Zusätzliche Sprachen ein-/ausblenden",
			"key-toggle-termbox": "Termbox ein-/ausblenden",
			"key-toggle-references": "Fundstellen ein-/ausblenden",
			"key-toggle-help": "Diese Hilfe ein-/ausblenden",
			"key-add-form": "Form hinzufügen",
			"key-add-form-statement": "Formaussage hinzufügen",
			"key-new-lexeme": "Neues Lexem",
			"key-add-sense": "Sense hinzufügen",
			"key-add-sense-statement": "Sense-Aussage hinzufügen",
			"key-add-ipa": "IPA hinzufügen",
		},
		"ja": {
			"heading-lexemes": "語彙素",
			"key-add-reference": "情報源を追加",
			"key-add-form": "語形を追加",
			"key-add-sense": "語義を追加",
		},
		"nb": {
			"heading-general": "Generelt",
			"heading-lexemes": "Leksemer",
			"key-add-statement": "Legg til utsagn",
			"key-add-another-statement": "Legg til et utsagn til",
			"key-edit-last-statement": "Rediger forrige utsagn",
			"key-view-history": "Vis historikk",
			"key-view-item": "Vis element",
			"key-new-item": "Nytt element",
			"key-edit-terms": "Rediger begreper/lemma",
			"key-merge": "Åpne flettedialogen",
			"key-select-id": "Merk og kopier ID-en",
			"key-add-reference": "Legg til referanse",
			"key-nameguzzler": "Åpne VIP's labels",
			"key-add-wikipedia-link": "Legg til Wikipedia-lenke",
			"key-toggle-languages": "Vis/skjul flere språk",
			"key-toggle-termbox": "Vis/skjul begrepsboksen",
			"key-toggle-references": "Vis/skjul referanser",
			"key-toggle-help": "Vis/skjul denne hjelpeboksen",
			"key-add-form": "Legg til form",
			"key-add-form-statement": "Legg til utsagn for form",
			"key-new-lexeme": "Nytt leksem",
			"key-add-sense": "Legg til betydning",
			"key-add-sense-statement": "Legg til utsagn for betydning",
			"key-add-ipa": "Legg til IPA",
			"key-toggle-sidebar": "Vis/skjul sidemenyen",
		},
		"nn": {
			"heading-general": "Generelt",
			"heading-lexemes": "Leksem",
			"key-add-statement": "Legg til utsagn",
			"key-add-another-statement": "Legg til eit utsagn til",
			"key-edit-last-statement": "Endre førre utsagn",
			"key-view-history": "Sjå historikk",
			"key-view-item": "Sjå element",
			"key-new-item": "Nytt element",
			"key-edit-terms": "Endre omgrep/lemma",
			"key-merge": "Opn flettedialogen",
			"key-select-id": "Merk og kopier ID-en",
			"key-add-reference": "Legg til referanse",
			"key-nameguzzler": "Opn VIP's labels",
			"key-add-wikipedia-link": "Legg til Wikipedia-lenkje",
			"key-toggle-languages": "Vis/skjul fleire språk",
			"key-toggle-termbox": "Vis/skjul omgrepsboksen",
			"key-toggle-references": "Vis/skjul referansar",
			"key-toggle-help": "Vis/skjul denne hjelpeboksen",
			"key-add-form": "Legg til form",
			"key-add-form-statement": "Legg til utsagn for form",
			"key-new-lexeme": "Nytt leksem",
			"key-add-sense": "Legg til tyding",
			"key-add-sense-statement": "Legg til utsagn for tyding",
			"key-add-ipa": "Legg til IPA",
			"key-toggle-sidebar": "Vis/skjul sidemenyen",
		},
	};
	$.i18n().load(translations);

	var n_laststatement = "";

	var mergeDialog = function () {
		$(window.mergeTool.launchDialog).on("keydown", function (event) {
			if (event.keyCode == 13 && !event.isDefaultPrevented()) {
				$(".oo-ui-processDialog-actions-primary a").each(function () { this.click() });
			}
		});
	},
	addStatement = function () {
		$(".wikibase-statementgrouplistview > .wikibase-addtoolbar").first().find("a").first().click();
	},
	editTerms = function () {
		if (mw.config.get("wgCanonicalNamespace") == "Lexeme") {
			$(".lemma-widget_edit").click();
		} else {
			$(".wikibase-entitytermsview .wikibase-toolbar-button-edit a").last().click();
		}
	},
	addForm = function () {
		$(".wikibase-lexeme-forms-section > .wikibase-addtoolbar").last().find("a").first().click();
	},
	addSense = function () {
		$(".wikibase-lexeme-senses-section > .wikibase-addtoolbar").last().find("a").first().click();
	},
	addConstraint = function () { // currently only works when there is already at least one
		$("#P2302 .wikibase-toolbar-wrapper > .wikibase-addtoolbar a").click();
	},
	addWikipedia = function () {
		$(".wikibase-sitelinkgrouplistview .wikibase-toolbar-button-edit a").eq(0).click();
		setTimeout(function () {
			$(".wikibase-sitelinkview-siteid-container").find("input").eq(0).trigger("focus");
		}, 500);
	},
	viewHistory = function () {
		$("a[accesskey='h']")[0].click();
	},
	viewItem = function () {
		$("a[accesskey='c']")[0].click();
	},
	newItem = function () {
		// For some reason click() doesn't work for this link
		document.location.href = document.querySelector("#n-special-newitem a").href;
	},
	newLexeme = function () {
		// For some reason click() doesn't work for this link
		document.location.href = document.querySelector("#n-special-newlexeme a").href;
	},
	toggleAllLanguages = function () {
		$(".wikibase-entitytermsforlanguagelistview-more a").click();
	},
	toggleTermbox = function () {
		$(".wikibase-entitytermsview-entitytermsforlanguagelistview-toggler").click();
	},
	addSenseStatement = function () {
		$(".wikibase-lexeme-senses-section .wikibase-lexeme-sense")
			.last()
			.find(".wikibase-statementgrouplistview > .wikibase-addtoolbar .wikibase-toolbar-button-add a")
			.click();
	},
	addFormStatement = function () {
		$(".wikibase-lexeme-forms-section .wikibase-lexeme-form")
			.last()
			.find(".wikibase-statementgrouplistview > .wikibase-addtoolbar .wikibase-toolbar-button-add a")
			.click();
	},
	editLastStatement = function () {
		let $st;
		if (typeof n_laststatement !== undefined && n_laststatement !== "")
			$st = $(document.getElementsByClassName("wikibase-statement-" + n_laststatement)[0]);
		else
			$st = $(".wikibase-entityview-main > .wikibase-statementgrouplistview .wikibase-statementview").last();

		if (!$st)
			return;

		$st.find(".wikibase-edittoolbar-container .wikibase-toolbar-button-edit a")
			.click();
	},
	addReference = function () {
		$(document.getElementsByClassName("wikibase-statement-" + n_laststatement)[0])
			.find(".wikibase-statementview-references .wikibase-toolbar-button-add a:first")
			.click();
	},
	addAnotherStatement = function () {
		$(document.getElementsByClassName("wikibase-statement-" + n_laststatement)[0])
			.parents(".wikibase-statementgroupview")
			.find(".wikibase-statementlistview > .wikibase-toolbar-wrapper .wikibase-toolbar-button-add a")
			.click();
	},
	expandReferences = function () {
		// if available from [[User:Nikki/ExpandReferences.js]]
		$("#claims small span a").click();
	},
	selectQid = function () {
		var cname = "wikibase-title-id";
		if (document.body.classList.contains("wb-lexemepage"))
			cname = "wb-lexeme-header_id";
		else if (mw.config.get("wgNamespaceNumber") === 640) // EntitySchema
			cname = "entityschema-title-id";
		var qe = document.getElementsByClassName(cname)[0];
		
		// Move brackets from HTML content to CSS pseudo-selectors
		// This prevents them from being included in the selection
		qe.innerHTML = qe.innerHTML.replace(/[()]/g, "");
		mw.util.addCSS(`
			h1 .wikibase-title-id::before,
			.wb-lexeme-header_id:before,
			.entityschema-title-id:before {
				content: "(";
			}
			h1 .wikibase-title-id::after,
			.wb-lexeme-header_id:after,
			.entityschema-title-id:after {
				content: ")";
			}
		`);	

		var selection = window.getSelection();
		var rangex = document.createRange();
		rangex.selectNodeContents(qe);
		selection.removeAllRanges();
		selection.addRange(rangex);
		
		document.execCommand("copy");
	},
	submitStatement = function () {
		$(".ui-inputextender-extended").closest(".wb-edit.wikibase-statementview").find(".wikibase-toolbar-button-save a").click();
	},
	editFirstSense = function () {
		$(".wikibase-lexeme-sense > .wikibase-edittoolbar-container .wikibase-toolbar-button-edit a").eq(0).click();
	},
	addIPA = function () {
		// if available from [[User:Nikki/LexemeAddIPA.js]]
		$("#addipa").click();
	},
	nameGuzzler = function () {
		// if available from [[User:Jitrixis/nameGuzzler.js]]
		$("#t-nameGuzzler a").click();
	},
	toggleSidebar = function () {
		// if available from [[User:Nikki/NarrowUI.js]] or [[User:Nikki/CollapsibleSidebar.js]]
		$("#sidebarCollapse").click();
	},
	toggleHelp = function () {
		$("#n_keyshortcuts_help").toggle();
	};

	$(this).keydown(function (event) {
		if (
			!event.ctrlKey &&
			!event.altKey &&
			!event.metaKey &&
			($(":focus").length === 0 || $("a:focus").length)
		) {
			switch (event.key) {
				case "a": addStatement(); return false;
				case "b": toggleSidebar(); return false;
				case "C": addConstraint(); return false;
				case "e": editLastStatement(); return false;
				case "f": addForm(); return false;
				case "h": viewHistory(); return false;
				case "i": viewItem(); return false;
				case "l": editTerms(); return false;
				case "m": mergeDialog(); return false;
				case "q": selectQid(); return false;
				case "r": addReference(); return false;
				case "s": addSense(); return false;
				case "v": nameGuzzler(); return false;
				case "w": addWikipedia(); return false;
				case "x": toggleAllLanguages(); return false;
				case "X": toggleTermbox(); return false;
				case "z": editFirstSense(); return false;
				case "A": addAnotherStatement(); return false;
				case "F": addFormStatement(); return false;
				case "I": newItem(); return false;
				case "L": newLexeme(); return false;
				case "S": addSenseStatement(); return false;
				case "+": expandReferences(); return false;
				case "@": addIPA(); return false;
				case "?": toggleHelp(); return false;
			}
		} else if (
			$(":focus").length === 1
			&& document.activeElement.classList.contains("ui-suggester-input")
			&& event.key == "Enter"
		) {
			submitStatement();
		}
	});

	mw.hook("wikibase.statement.saved").add(function (qid, s) { n_laststatement = s; });

	var n_keymap = {
		"heading-general": {
			"a": "key-add-statement",
			"A": "key-add-another-statement",
			"b": "key-toggle-sidebar",
			"e": "key-edit-last-statement",
			"h": "key-view-history",
			"i": "key-view-item",
			"I": "key-new-item",
			"l": "key-edit-terms",
			"m": "key-merge",
			"q": "key-select-id",
			"r": "key-add-reference",
			"v": "key-open-nameguzzler",
			"w": "key-add-wikipedia-link",
			"x": "key-toggle-languages",
			"X": "key-toggle-termbox",
			"+": "key-toggle-references",
			"?": "key-toggle-help",
		},
		"heading-lexemes": {
			"f": "key-add-form",
			"F": "key-add-form-statement",
			"L": "key-new-lexeme",
			"s": "key-add-sense",
			"S": "key-add-sense-statement",
			"@": "key-add-ipa",
		}
	};

	mw.util.addCSS(`
		#n_keyshortcuts_help {
			background-color: white;
			border: 2px solid black;
			display: none;
			inset-inline-end: 0; /* = right: 0 */
			margin: 15vh 3vw;
			max-height: 75vh;
			overflow: auto;
			padding: 0.7em;
			position: fixed;
			top: 0;
			width: fit-content;
			z-index: 100;
		}
		#n_keyshortcuts_help ul {
			list-style: none;
			padding-inline: 0;
			margin-inline: 0.5em;
		}
		#n_keyshortcuts_help li {
			margin-block-end: 0.4em;
		}
		/* see [[Module:key]] */
		#n_keyshortcuts_help kbd {
			border: 1px solid #aaa;
			border-radius: 0.2em;
			box-shadow: 0.1em 0.1em 0.2em rgba(0, 0, 0, 0.1);
			background-color: #f9f9f9;
			background-image: linear-gradient(to bottom, #eee, #f9f9f9, #eee);
			font-size: 0.85em;
			padding: 0.1em 0.3em;
			unicode-bidi: isolate;
		}
	`);

	var n_help_html = "<div id='n_keyshortcuts_help'>";
	for (var i in n_keymap) {
		n_help_html = n_help_html + $.i18n(i) + "<ul>";
		for (var j in n_keymap[i]) {
			// hacky way of only showing + if the user is also using the script for expanding/collapsing references
			if (j === "+" && !$("#claims small span a").length)
				continue;
			if (j === "@" && !$("#addipa").length)
				continue;
			if (j === "v" && !$("#t-nameGuzzler").length)
				continue;
			n_help_html = n_help_html + "<li><kbd>" + j + "</kbd> " + $.i18n(n_keymap[i][j]) + "</li>";
		}
		n_help_html = n_help_html + "</ul>";
	}
	n_help_html = n_help_html + "</div>";

	$(document.body).append(n_help_html);

	mw.hook("wikibase.statement.stopEditing").add(function(guid) {
		var st = document.getElementById(guid);
		if (st) {
			st.querySelector(".wikibase-statementview-rankselector").tabIndex = -1;
		} else {
			console.error(`wikibase.statement.stopEditing fired for statement ${guid} but couldn't find that statement on the page. (T232777)`);
		}
	});
	mw.hook("wikibase.statement.startEditing").add(function(guid) {
		var st = document.getElementById(guid);
		if (st) {
			st.querySelector(".wikibase-statementview-rankselector").tabIndex = 0;
		} else {
			console.error(`wikibase.statement.startEditing fired for statement ${guid} but couldn't find that statement on the page. (T232777)`);
		}
	});

	function rankselector(e) {
		if (e.key !== "ArrowUp" && e.key !== "ArrowDown" && e.key !== "Enter")
			return; // Not a supported key for this element

		e.preventDefault();

		// The rank selector menu isn't fully initialised until clicked
		// so click once to activate it and again to hide it
		let selector = e.target.querySelector(".wikibase-rankselector");
		selector.click();
		selector.click();

		let menu = document.querySelector(".wikibase-rankselector-menu .ui-state-active");
		if (e.key === "ArrowUp" && menu.previousElementSibling) {
			menu.previousElementSibling.click();
		} else if (e.key === "ArrowDown" && menu.nextElementSibling) {
			menu.nextElementSibling.click();
		} else if (e.key === "Enter") {
			e.target.parentNode.parentNode.querySelector(".wikibase-toolbar-button-save a").click();
		}
	}

	mw.hook("wikibase.entityPage.entityView.rendered").add(function () {
		for (let el of document.querySelectorAll(".wikibase-statementview-rankselector")) {
			el.onkeydown = rankselector;
		}
	});

	$("body").on("valueviewafterstartediting", function (x) {
		let p = x.target.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
		if (p.id !== "new")
			return;
		let el = p.querySelector(".wikibase-statementview-rankselector");
		el.onkeydown = rankselector;
		el.tabIndex = 0;
	});

});