MediaWiki:Gadget-defaultVisibilityToggles.js

/* jshint undef: true, esversion: 5 */ /* globals $, jQuery, mw, window, getComputedStyle */

(function defaultVisibilityTogglesIIFE { "use strict";

if (window.noDefaultVisibilityToggles) return;

/* == NavBars == */ var NavigationBarHide = "hide ▲"; var NavigationBarShow = "show ▼";

// Check if an element has been activated with a toggle. // For convenience, this has the side effect of marking the element as having // a toggle, if it is not already marked. // Allows the functions to avoid toggleifying elements more than once, which // can lead to multiple "show" buttons, for instance. // The argument must be an Element, not a jQuery object. function checkAndSetToggleified(element) { if (element.isToggleified) { return true; }	element.isToggleified = true; }

function getToggleCategory(element, defaultCategory) { if ($(element).find("table").first.is(".translations")) return "translations"; var heading = element; while ((heading = heading.previousElementSibling)) { // tagName is always uppercase: // https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName var num = heading.tagName.match(/H(\d)/); if (num) num = Number(num[1]); else continue; if (4 <= num && num <= 6) { if (heading.getElementsByTagName("span")[1]) heading = heading.getElementsByTagName("span")[0]; var text = jQuery(heading).text .toLowerCase // jQuery's .text is inconsistent about whitespace: .replace(/^\s+|\s+$/g, "").replace(/\s+/g, " ") // remove numbers added by the "Auto-number headings" pref: .replace(/^[1-9][0-9.]+ ?/, ""); // Toggle category must be convertible to a valid CSS identifier so			// that it can be used in an id selector in jQuery in			// ToggleCategory.prototype.newSidebarToggle // in MediaWiki:Gadget-VisibilityToggles.js. // Spaces must later be converted to hyphens or underscores. // Reference: https://drafts.csswg.org/selectors-4/#id-selectors if (/^[a-zA-Z0-9\s_-]+$/.test(text)) return text; else break; } else if (num) break; }	return defaultCategory; }

function createNavToggle(navFrame) { if (checkAndSetToggleified(navFrame)) { return; }	var navHead, navContent; for (var i = 0, children = navFrame.childNodes; i < children.length; ++i) { var child = children[i]; if (child.nodeName === "DIV") { var classList = child.classList; if (classList.contains("NavHead")) navHead = child; if (classList.contains("NavContent")) navContent = child; }	}	if (!(navHead && navContent)) return; // Step 1, don't react when a subitem is clicked. $(navHead).find("a").on("click", function (e) {		e.stopPropagation;		e.stopImmediatePropagation;	}); // Step 2, toggle visibility when bar is clicked. // NOTE This function was chosen due to some funny behaviour in Safari. var $navToggle = $("").attr("role", "button").attr("tabindex", "0"); $(" ").addClass("NavToggle") .append($navToggle) .prependTo(navHead); navHead.style.cursor = "pointer"; var toggleCategory = $(navFrame).data("toggle-category") || getToggleCategory(navFrame, "other boxes"); navHead.onclick = window.VisibilityToggles.register(toggleCategory,		function show {			$navToggle.html(NavigationBarHide);			if (navContent)				navContent.style.display = "block";		},		function hide {			$navToggle.html(NavigationBarShow);			if (navContent)				navContent.style.display = "none";		}); }

/* ==Hidden Quotes== */ function setupHiddenQuotes(li) { if (checkAndSetToggleified(li)) { return; }	var HQToggle, liComp, dl; var HQShow = "quotations ▼"; var HQHide = "quotations ▲"; function show { HQToggle.html(HQHide); $(li).children("ul").show; }	function hide { HQToggle.html(HQShow); $(li).children("ul").hide; }	for (var k = 0; k < li.childNodes.length; k++) { // Look at each component of the definition. liComp = li.childNodes[k]; if (liComp.nodeName.toLowerCase === "dl" && !dl) { dl = liComp; }		// If we find a ul or dl, we have quotes or example sentences, and thus need a button. if (/^(ul|UL)$/.test(liComp.nodeName)) { HQToggle = $("").attr("role", "button").attr("tabindex", "0"); $(dl || liComp).before($(" ").addClass("HQToggle").append(HQToggle).css('margin-left', '5px')); HQToggle.on("click", window.VisibilityToggles.register("quotations", show, hide)); break; }	} }

/* == View Switching == */

function viewSwitching(rootElement) { if (checkAndSetToggleified(rootElement)) { return; }	var $rootElement = $(rootElement); var showButtonText = $rootElement.data("vs-showtext") || "more ▼"; var hideButtonText = $rootElement.data("vs-hidetext") || "less ▲"; var toSkip = $rootElement.find(".vsSwitcher").find("*"); var elemsToHide = $rootElement.find(".vsHide").not(toSkip); var elemsToShow = $rootElement.find(".vsShow").not(toSkip); // Find the element to place the toggle button in. var toggleElement = $rootElement.find(".vsToggleElement").not(toSkip).first; // The toggleElement becomes clickable in its entirety, but // we need to prevent this if a contained link is clicked instead. toggleElement.find("a").on("click", function (e) {		e.stopPropagation;		e.stopImmediatePropagation;	}); // Add the toggle button. var toggleButton = $("").attr("role", "button").attr("tabindex", "0"); $(" ").addClass("NavToggle").append(toggleButton).prependTo(toggleElement); // Determine the visibility toggle category (for the links in the bar on the left). var toggleCategory = $rootElement.data("toggle-category"); if (!toggleCategory) { var classNames = $rootElement.attr("class").split(/\s+/); for (var i = 0; i < classNames.length; ++i) { var className = classNames[i].split("-"); if (className[0] == "vsToggleCategory") { toggleCategory = className[1]; }		}	}	if (!toggleCategory) toggleCategory = "others"; // Register the visibility toggle. toggleElement.css("cursor", "pointer"); toggleElement.on("click", window.VisibilityToggles.register(toggleCategory, function show { toggleButton.html(hideButtonText); elemsToShow.hide; elemsToHide.show; },		function hide { toggleButton.html(showButtonText); elemsToShow.show; elemsToHide.hide; })); }

/* ==List switching== */ // Number of rows of list items to show in the hidden state of a "list switcher" list. // Customize by adding // window.listSwitcherCount = ; // to your common.js before loading this script. window.listSwitcherRowCount = window.listSwitcherRowCount || 3;

function getListItemsToHide($listItems, columnCount, rowsInShowState) { var count = $listItems.length; var itemsPerColumn = Math.ceil(count / columnCount); var $elemsToHide = $; if (itemsPerColumn > rowsInShowState) { for (var i = 0; i < columnCount; ++i) { var columnStart = i * itemsPerColumn; $elemsToHide = $elemsToHide .add($listItems.slice(columnStart + rowsInShowState, columnStart + itemsPerColumn)); }	}	return $elemsToHide; }

function enableListSwitch (rootElement, rowsInShowState) { if (checkAndSetToggleified(rootElement)) { return; }	var $rootElement = $(rootElement); // Number of columns must be set in data-term-list-column-count attribute // of the element with class term-list. var $termList = $rootElement.find(".term-list"); // Find the element to place the toggle button in. var $toggleElement = $rootElement.find(".list-switcher-element"); var ul = $termList.find("ul")[0]; if (!ul) return; var columnCountCss = getComputedStyle(ul).getPropertyValue("column-count"); var columnCount = columnCountCss === "auto" ? 1 : parseInt(columnCountCss, 10); if (!(columnCount && columnCount > 0)) { $toggleElement.hide; return; }	var $listItems = $rootElement.find("ul").first.find("li"); var $toHide = getListItemsToHide($listItems, columnCount, rowsInShowState); // Don't do anything if there aren't any items to hide. if ($toHide.length === 0) { $toggleElement.hide; return; }	$toggleElement.css("cursor", "pointer"); // Add the toggle button. var $toggleButton = $("").attr("role", "button").attr("tabindex", "0"); var rootBackgroundColor = $termList.css("background-color"), rootBackground = $termList.css("background"); var $navToggle = $(" ").addClass("NavToggle"); if (rootBackgroundColor || rootBackground) $navToggle.css(rootBackgroundColor ? "background-color" : "background",			rootBackgroundColor || rootBackground); // The $toggleElement becomes clickable in its entirety, but // we need to prevent this if a contained link is clicked instead. $toggleElement.children("a").on("click", function (e) {		e.stopPropagation;		e.stopImmediatePropagation;	}); // Determine the visibility toggle category (for the links in the bar on the	// left). It will either be the value of the "data-toggle-category" // attribute or will be based on the text of the closest preceding // fourth-to-sixth-level header. var toggleCategory = $rootElement.data("toggle-category") || getToggleCategory($rootElement[0], "other lists"); // Determine the text for the $toggleButton. var showButtonText = $toggleElement.data("showtext") || "more ▼"; var hideButtonText = $toggleElement.data("hidetext") || "less ▲"; // Register the visibility toggle. $toggleElement.on("click", window.VisibilityToggles.register(toggleCategory, function show { $toggleButton.html(hideButtonText); $toHide.show; },		function hide { $toggleButton.html(showButtonText); $toHide.hide; }));	// Add the toggle button to the DOM tree. $navToggle.append($toggleButton).prependTo($toggleElement); $toggleElement.show; }

// based on User:Erutuon/scripts/semhide.js, User:Jberkel/semhide.js, // User:Ungoliant_MMDCCLXIV/synshide.js function setupNyms(index, dlTag) { // Semantic relations var relationClasses = [ 'synonym', 'antonym', 'hypernym', 'hyponym', 'meronym', 'holonym', 'troponym', 'comeronym', 'coordinate-term', 'near-synonym', 'imperfective', 'perfective', 'alternative-form' ]; var relations = $(dlTag).find('dd > .nyms').get.filter(		function(element) {			return Array.prototype.some.call(element.classList, function (className) { if (relationClasses.indexOf(className) !== -1) { $(element).data('relationClass', className); return true; }			});		});	function setupToggle(elements, category, visibleByDefault) { if (elements.length === 0) return null; var toggler = $(''); var text = elements.map(function (e) {			return $(e).data('relationClass').replace('-', ' ') +				 (($(e).find('a').length > 1) ? 's' : '');		}).join(', '); function show { toggler.html(text + ' ▲'); $(dlTag).show; $(elements).show; }		function hide { toggler.html(text + ' ▼'); if ($(dlTag).children.length === elements.length) { $(dlTag).hide; } else { $(elements).hide; }		}		$(dlTag).before($(' ')		  .addClass('nyms-toggle')		   .append(toggler)		   .css('margin-left', '5px')); toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault)); }	var synonyms = relations.filter(function (e) {		return ['synonym', 'antonym', 'near-synonym', 'coordinate-term', 'alternative-form'].indexOf($(e).data('relationClass')) !== -1;	}); var other = relations.filter(function (e) { return synonyms.indexOf(e) === -1; }); setupToggle(synonyms, 'synonyms', true /* show by default */); setupToggle(other, 'semantic relations'); }

function setupUsageExampleCollapses(index, dlTag) { var usexTags = $(dlTag).find('dd > .h-usage-example').get; function setupToggle(elements, category, visibleByDefault) { if (elements.length === 0) return null; var toggler = $(''); function show { toggler.html(category + ' ▲'); $(dlTag).show; $(elements).show; }		function hide { toggler.html(category + ' ▼'); if ($(dlTag).children.length === elements.length) { $(dlTag).hide; } else { $(elements).hide; }		}		$(dlTag).before($(' ')		  .addClass('nyms-toggle')		   .append(toggler)		   .css('margin-left', '5px')); toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault)); }	var collocations = usexTags.filter(function (e) {		return $(e).hasClass('collocation');	}); var usexes = usexTags.filter(function (e) { return collocations.indexOf(e) === -1; }); setupToggle(usexes, 'usage examples', true /* show by default */); setupToggle(collocations, 'collocations', true /* show by default */); }

window.createNavToggle = createNavToggle; window.setupHiddenQuotes = setupHiddenQuotes; window.viewSwitching = viewSwitching; window.getToggleCategory = getToggleCategory;

/* == Apply four functions defined above == */ mw.hook('wikipage.content').add(function($content) {	// NavToggles	$('.NavFrame', $content).each(function{ createNavToggle(this); });	// order nyms -> usexes -> quotes, to match the conventional order in entries	// synonyms and such under definitions	// if (mw.config.get('wgNamespaceNumber') === 0) {		$('dl:has(dd > .nyms)', $content).each(setupNyms);	// }	// usage examples and collocations	var namespaceNumber = mw.config.get('wgNamespaceNumber');	if (window.defaultVisibilityTogglesForUsageExamples) {		if (namespaceNumber === 0) {			$('ol > li dl:has(dd > .h-usage-example)', $content).each(setupUsageExampleCollapses);		}	}

// quotes if (namespaceNumber === 0 || namespaceNumber === 100 || namespaceNumber === 118) { // First, find all the ordered lists, i.e. all the series of definitions. $('ol > li', $content).each(function{			setupHiddenQuotes(this);		}); }

//view switching $('.vsSwitcher', $content).each(function{		viewSwitching(this);	});

// list switching $(".list-switcher", $content).each(function {		enableListSwitch(this, window.listSwitcherRowCount);	}); });

jQuery(mw).on("LivePreviewDone", function (ev, sels) {	var ols = jQuery(sels.join(",")).find("ol");	for (var i = 0; i < ols.length; i++) {		for (var j = 0; j < ols[i].childNodes.length; j++) {			var li = ols[i].childNodes[j];			if (li.nodeName.toUpperCase == "LI") {				setupHiddenQuotes(li);			}		}	} });

});