/* 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);
else
continue;
if (4 <= num && num <= 6) {
if (heading.getElementsByTagName("span"))
heading = heading.getElementsByTagName("span");
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(/^+ ?/, "");
// 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 ].
// Spaces must later be converted to hyphens or underscores.
// Reference: https://drafts.csswg.org/selectors-4/#id-selectors
if (/^+$/.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;
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 = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "")
.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";
});
}
function createNavToggleForInflectionTable(it) {
if (checkAndSetToggleified(it)) {
return;
}
// The table caption is the clickable element
var itCaption = $(it).find("caption").get(0);
// Step 1, don't react when a subitem is clicked.
$(itCaption).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 = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "")
.append($navToggle)
.prependTo(itCaption);
itCaption.style.cursor = "pointer";
var toggleCategory = $(it).data("toggle-category")
|| getToggleCategory(it, "other boxes");
itCaption.onclick = window.VisibilityToggles.register(toggleCategory,
function show() {
$navToggle.html(NavigationBarHide);
if (it) {
it.classList.remove("inflection-table-collapsed");
}
},
function hide() {
$navToggle.html(NavigationBarShow);
if (it) {
it.classList.add("inflection-table-collapsed");
}
});
// Check to see if we are on a browser that is known to support
// visibility: collapse, which permits inflection table headings to wrap.
// WebKit needs to be special-cased, as technically it does support
// visibility: collapse, but it just implements it as a synonym for
// visibility: hidden, which is useless. (as of November 2024)
// Yes, I know User-Agent sniffing is so 2004... but WebKit is the new IE
if (
CSS && CSS.supports && CSS.supports("visibility:collapse") &&
// exclude WebKit/Safari, excepting Blink engines which have a frozen WebKit version number
(navigator.userAgent.indexOf("AppleWebKit/") === -1 || navigator.userAgent.indexOf("AppleWebKit/537.36") > -1)
) {
it.classList.remove("no-vc");
} else {
// Strange behaviour occurs when you set the table caption to nowrap
// The toggle crashes into the caption text
// This spacer element prevents that
$("<span>").addClass("no-vc-spacer").appendTo(itCaption);
}
}
/* ==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;
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 = $("<a>").attr("role", "button").attr("tabindex", "0");
$(dl || liComp).before($("<span>").addClass("HQToggle").attr("data-nosnippet", "").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 = $("<a>").attr("role", "button").attr("tabindex", "0");
$("<span>").addClass("NavToggle").attr("data-nosnippet", "").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.split("-");
if (className == "vsToggleCategory") {
toggleCategory = className;
}
}
}
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== */
function enableListSwitchGeneric(rootElement) {
if (checkAndSetToggleified(rootElement)) {
return;
}
var $rootElement = $(rootElement);
// Create a toggle button.
var $toggleElement = $("<div>").addClass("list-switcher-element");
var $navToggle = $("<span>").addClass("NavToggle").attr("data-nosnippet", "");
var $toggleButton = $("<a>").attr("role", "button").attr("tabindex", "0");
// Add the toggle button to the DOM tree.
$navToggle.append($toggleButton).prependTo($toggleElement);
$toggleElement.insertAfter($rootElement);
$toggleElement.show();
// 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, "other lists");
// Determine the text for the $toggleButton.
var showButtonText = "show more ▼";
var hideButtonText = "show less ▲";
// Register the visibility toggle.
$toggleElement.on("click", window.VisibilityToggles.register(toggleCategory,
function show() {
$toggleButton.html(hideButtonText);
if (rootElement) {
rootElement.classList.remove("list-switcher-collapsed");
}
},
function hide() {
$toggleButton.html(showButtonText);
if (rootElement) {
rootElement.classList.add("list-switcher-collapsed");
}
}));
// Register a resize observer to see if we need to keep the
// show/hide toggle visible
var termList = rootElement.querySelector(':scope > .term-list');
if (termList && window.ResizeObserver) {
var resizeObserver = new ResizeObserver(function(entries) {
if (entries && entries.contentBoxSize) {
// Work out what the max-height would be, in pixels
// As a hack, this value is stored in the CSS `bottom`
// property, as `max-height` is only in place when
// the list is collapsed, but we need to do this check
// even when the list is not collapsed
var maxHeightPx = parseFloat(getComputedStyle(rootElement).bottom);
// If box height is less than its max height + 20 px, suppress
// collapsibility. The 20 px buffer prevents the situation where
// clicking "show more" expands the box by just a few pixels
if (entries.contentBoxSize.blockSize <= maxHeightPx + 20) {
$toggleElement.hide();
if (rootElement.classList.contains("list-switcher-collapsed")) {
rootElement.classList.remove("list-switcher-collapsed");
rootElement.classList.add("list-switcher-collapsibility-suppressed");
}
} else {
$toggleElement.show();
if (rootElement.classList.contains("list-switcher-collapsibility-suppressed")) {
rootElement.classList.remove("list-switcher-collapsibility-suppressed");
rootElement.classList.add("list-switcher-collapsed");
}
}
}
});
resizeObserver.observe(termList);
}
}
// based on ], ],
// ]
function setupNyms(index, dlTag) {
// ]
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.dataset.relationClass = className;
return true;
}
});
});
function setupToggle(elements, category, visibleByDefault) {
if (elements.length === 0) return null;
var toggler = $("<a>").attr("role", "button").attr("tabindex", "0");
var text = elements.map(function (e) {
var linkCount = e.querySelectorAll("span").length;
return e.dataset.relationClass.replace("-", " ") +
(linkCount > 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($("<span>")
.addClass("nyms-toggle")
.attr("data-nosnippet", "")
.append(toggler)
.css("margin-left", "5px"));
toggler.click(window.VisibilityToggles.register(category, show, hide, visibleByDefault));
}
var synonyms = relations.filter(function (e) {
return .indexOf(e.dataset.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 = $("<a>").attr("role", "button").attr("tabindex", "0");
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($("<span>")
.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);
});
$(".inflection-table-collapsible", $content).each(function(){
createNavToggleForInflectionTable(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 () {
enableListSwitchGeneric(this);
});
});
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.childNodes.length; j++) {
var li = ols.childNodes;
if (li.nodeName.toUpperCase() == "LI") {
setupHiddenQuotes(li);
}
}
}
});
})();