Bahai9
Bahai9
Menu
Main page
About Bahai9
Recent changes
Random page
Help about MediaWiki
In other projects
Tools
What links here
Related changes
Upload file
Special pages
Printable version
Permanent link
Page information
Message
Discussion
View source
View history
Not logged in
Talk
Contributions
Create account
Log in
Navigation
Main page
About Bahai9
Recent changes
Random page
Help about MediaWiki
In other projects
Other projects
Indexes
Bahai-library
Tools
What links here
Related changes
Upload file
Special pages
Printable version
Permanent link
Page information

MediaWiki:Common.js

From Bahai9
Jump to:navigation, 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)
  • Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/* globals document, window, URLSearchParams, location, event, console, localStorage,
  $, mw, -- Set by MediaWiki */
/* eslint-disable
  no-tabs, padded-blocks, no-multiple-empty-lines,
  vars-on-top
  -- Disabling for readability
*/
/* eslint-disable
  no-var, prefer-named-capture-group, require-c-regexp,
  object-shorthand,
  unicorn/no-document-cookie,
  unicorn/prefer-query-selector, unicorn/prefer-includes, unicorn/no-for-loop,
  unicorn/prefer-default-parameters
  -- Disabling some due to older browser support/lack of ES6+ support
  */
/* eslint indent: ['error', 'tab'] -- Differs from eslint-config-ash-nazg */
/*
CONTENTS

1. Character subset menu
2. Collapsible headings
3. Collapsible lists
4. Toggling table of contents
5. Toggling headings
6. Search category pages
7. Prepopulating user category pages
8. Sister project links (convert to specific page)
9. Hiding category sections when no categories
10. Bahai.org and Phelps' Partial Inventory Tablet cross-references
11. Add traffic statistics link
*/

// MediaWiki vars (use mw.config.get() for some): https://www.mediawiki.org/wiki/Manual:Interface/JavaScript
/* Any JavaScript here will be loaded for all users on every page load. */

'use strict';

/**********************
*** add menu on edit page /for selecting subsets of special characters
*** by [[user:Pathoschild]]
***   - note: must match MediaWiki:Edittools
**********************/
function addCharSubsetMenu () {
	var specialchars = document.getElementById('specialchars');

	if (specialchars) {
		var menu = '<select style="display:inline" onChange="chooseCharSubset(selectedIndex)">';
		menu += '<option>Select</option>';
		menu += '<option>Ligatures and symbols</option>';
		menu += '<option>Accents</option>';
		menu += '<option>Tildes</option>';
		menu += '<option>Cedillas</option>';
		menu += '<option>Diereses</option>';
		menu += '<option>Circumflexes</option>';
		menu += '<option>Macrons</option>';
		menu += '<option>Other diacritics</option>';
		menu += '<option>Greek</option>';
		menu += '<option>Hebrew</option>';
		menu += '<option>Cyrillic</option>';
		menu += '<option>IPA</option>';
		menu += '</select>';
		specialchars.innerHTML = menu + specialchars.innerHTML.replace(/_newline_/gm, '\n');

		/* default subset - try to use a cookie some day */
		chooseCharSubset(0);
	}
}

/**
 * Select subsection of special characters.
 * @param {int} s
 * @returns {void}
 */
function chooseCharSubset (s) {
	var l = document.getElementById('specialchars').getElementsByTagName('p');
	for (var i = 0; i < l.length; i++) {
		l[i].style.display = i === s ? 'inline' : 'none';
		l[i].style.visibility = i === s ? 'visible' : 'hidden';
	}
}

addCharSubsetMenu(); // addOnloadHook();

// Collapsible headings
var a = $('<a href="javascript:void(0);">collapse</a>').click(function () {
	var currentHeading = parseInt($(this).parent().parent()[0].nodeName.match(/\d$/));
	var sameOrHigherHeading = 'h' + Array.from({length: 6}, function (x, i) {return i+1;}).slice(0, currentHeading).join(',h');
	var method = this.textContent === 'collapse' ? 'hide' : 'show';
	$(this).parent().parent().nextUntil(sameOrHigherHeading)[method]();
	this.textContent = this.textContent === 'collapse' ? 'show' : 'collapse';
});
$('h1,h2,h3,h4,h5,h6').children('.mw-editsection').append(' [', a, ']');

// add collapsible list if an empty span is present indicating to do so

if ($('.enable_collapsibleList').length || $('.enable_collapsibleList_collapsed').length) {
	$('ul,ol').each(function () {
		$(this).addClass('collapsibleList');
		var li = $(this).find('li:has(ul,ol)').addClass('collapsibleListOpen');
		if (!li.find('span').length) {
			li.prepend('<span style="user-select: none;">&nbsp; &nbsp; &nbsp;</span>');
		}
	});
}

// User:Brettz9 added with some JSLint/XHTML/JSDoc clean-up, optimization/convenience changes, OL support, and beginning of work to allow pre-expansion of lists
/*
MEDIAWIKI LIST NOTES

Nested DL hierarchies do not work properly with Mediawiki (see bug at http://www.mediawiki.org/wiki/Thread:Project:Support_desk/How_to_create_nested_definition_lists%3F )

Resources:
1. http://www.mediawiki.org/wiki/Help:Formatting
2. http://en.wikipedia.org/wiki/Help:List
*/
/*
CollapsibleLists.js

An object allowing lists to dynamically expand and collapse

Original version created by Stephen Morley - http://code.stephenmorley.org/javascript/collapsible-lists/ -
and released under the terms of the CC0 1.0 Universal legal code:

http://creativecommons.org/publicdomain/zero/1.0/legalcode

Ideas
1. Remember expanded states
2. Make DL collapsible via its DD
3. Support collapsing of DL component children (DD and DT)

Todo summary: (see "todo"'s below)
1. Optimize iteration algorithms
2. When #1 is completed, check top-most class of list (or its parent) for `doNotRecurse` and pre-expansion options
*/

// eslint-disable-next-line no-unused-vars -- Might expand
var CollapsibleLists;
(function () {
	// Create the CollapsibleLists object
	var collapsibleLists,
		autoApplyWithNoGlobal = true; // Configure as to whether to auto-apply to lists on the page (without creating a global) or whether to allow manual control via a global

	/**
	* Opens or closes the list elements of the given type
	*   within the specified node.
	* @private
	* @static
	* @param {Element} node The node containing the list elements
	* @param {'ul'|'ol'} [listType] Type of list; defaults to "ul"
	* @returns {int}
	*/
	function toggle (node, listType) {
		listType = listType || 'ul,ol';
		// Determine whether to open or close the lists
		var index, li,
			open = node.className.match(/(^| )collapsibleListClosed( |$)/),
			// Loop over the list elements with the node
			lists = node.querySelectorAll(listType),
			listsLength = lists.length;

		for (index = 0; index < listsLength; index++) {

			// Find the ancestor list item of this list
			li = lists[index];
			while (li.nodeName.toLowerCase() !== 'li') {
				li = li.parentNode;
			}

			// Style the list if it is within this node
			if (li === node) {
				lists[index].style.display = (open ? 'block' : 'none');
			}
		}

		if (listsLength) {
			// Remove the current class from the node
			node.className = node.className.replace(
				/(^| )collapsibleList(Open|Closed)( |$)/, ''
			);

			// If the node contains lists, set its class
			if (listsLength > 0) {
				node.className += ' collapsibleList' + (open ? 'Open' : 'Closed');
			}
		}
		return listsLength;
	}

 /**
  * @callback ClickHandler
  * @private
  * @static
  * @param {Event} e The click event
  * @returns {void}
  */

	/**
	* Returns a function that toggles the display status of any
	*   list elements within the specified node. The parameter is...
	* @private
	* @static
	* @param {Element} node The node containing the list elements
	* @returns {ClickHandler}
	*/
	function createClickListener (node) {

		// Return the function
		/**
     * @type {ClickHandler}
     */
		return function (e) {

			// Ensure the event object is defined
			e = e || window.event;

			// Find the list item containing the target of the event
			var li = e.target || e.srcElement;
			while (li.nodeName.toLowerCase() !== 'li') {
				li = li.parentNode;
			}

			// Toggle the state of the node if it was the target of the event
			if (li === node) {
				toggle(node);
			}
		};
	}

	/**
	* @private
	* @static
	* @param {Event} e The event to prevent
	* @returns {void}
	*/
	function preventDefault (e) {
		e.preventDefault();
	}

	/**
	* @private
	* @static
	* @returns {void}
	*/
	function preventDefaultIE () {
		// eslint-disable-next-line no-restricted-globals -- IE
		event.returnValue = false;
	}

	/**
	 * @param {boolean} leaveExpanded
 	 * @private
 	 * @static
     * @returns {void}
	 */
	function applyLists (leaveExpanded) {
		collapsibleLists.apply(false, leaveExpanded);
	}

	/**
	* Makes sublists of a given list type collapsible.
	* @private
	* @static
	* @param {Element} list The list element
	* @param {'ul'|'ol'} [listType] The list type under which sublists should be made collapsible
  * @returns {void}
	*/
	function applySubListsForListType (list, listType) {
		listType = listType || 'ul,ol';
		// Todo: This is unnecessarily redundant and thus expensive for deeply nested lists;
		//			 should instead check direct children recursively
		var subIndex,
			subLists = list.getElementsByTagName(listType),
			subListsLength = subLists.length;
		for (subIndex = 0; subIndex < subListsLength; subIndex++) {
			// Tempory fix to at least avoid multiple classes
			if (!(/(^| )collapsibleList( |$)/).test(subLists[subIndex].className)) {
				subLists[subIndex].className += ' collapsibleList';
			}
		}
	}

	collapsibleLists = {
		/**
		* Makes all lists with the class 'collapsibleList' collapsible.
		* @param {boolean} [doNotRecurse] True if sub-lists should not be made collapsible
		* @param {boolean} [leaveExpanded] True if list and its sublists should be left pre-expanded
    * @returns {void}
		*/
		apply: function (doNotRecurse, leaveExpanded) {
			this.applyForListType(doNotRecurse, leaveExpanded);
		},
		/**
		* Makes lists of the given type with the class 'collapsibleList' collapsible.
		* @param {boolean} [doNotRecurse] True if sub-lists should not be made collapsible
		* @param {boolean} [leaveExpanded] True if list and its sublists should be left pre-expanded
		* @param {'ul'|'ol'} [listType] Type of list; defaults to "ul"
		*/
		applyForListType: function (doNotRecurse, leaveExpanded, listType) {
			listType = listType || 'ul,ol';
			// Loop over the lists
			var index, list,
				// Todo: This is unnecessarily redundant and thus inefficient; should instead
				//			 iterate over direct children
				lists = document.querySelectorAll(listType),
				listsLength = lists.length,
				listPattern = /(^| )collapsibleList( |$)/;
			for (index = 0; index < listsLength; index++) {
				list = lists[index];
				// Check whether this list should be made collapsible
				if (listPattern.test(list.className) ||
					listPattern.test(list.parentNode.className) // For convenience when used with Mediawiki simple syntax lists (which cannot specify classes on the list)
				) {

					// Make this list collapsible
					this.applyTo(list, true, leaveExpanded);

					// Check whether sub-lists should also be made collapsible
					// Todo: When iteration algorithm is fixed, check class at top of list (or parent of list) to allow doNotRecurse specification
					if (!doNotRecurse) {
						// Add the collapsibleList class to the sub-lists
						applySubListsForListType(list);
					}
				}
			}
		},

		/**
		* Makes the specified list collapsible.
		* @param {Element} node The list element
		* @param {boolean} [doNotRecurse] True if sub-lists should not be made collapsible
		* @param {boolean} [leaveExpanded] True if list and its sublists should be left pre-expanded
		*/
		applyTo: function (node, doNotRecurse, leaveExpanded) {

			// Loop over the list items within this node
			var index,
				lis = node.querySelectorAll('li'),
				lisLength = lis.length;
			for (index = 0; index < lisLength; index++) {

				// Todo: When iteration algorithm is fixed, check class at top of list (or parent of list) to allow doNotRecurse specification
				// Check whether this list item should be collapsible
				if (!doNotRecurse || node === lis[index].parentNode) {

                    var attachNode = lis[index].querySelector('span');
                    if (!attachNode) {
                    	continue;
                    }

					// Prevent text from being selected unintentionally
					if (attachNode.parentNode.addEventListener) {
						attachNode.parentNode.addEventListener('mousedown', preventDefault, false);
					} else {
						attachNode.parentNode.attachEvent('onselectstart', preventDefaultIE);
					}

					// Add the click listener
					if (attachNode.parentNode.addEventListener) {
						attachNode.parentNode.addEventListener('click', createClickListener(lis[index]), false);
					} else {
						attachNode.parentNode.attachEvent('onclick', createClickListener(lis[index]));
					}

					// Close the lists within this list item
					// Todo: When iteration algorithm is fixed, check class at top of list (or parent of list) to allow expansion via class
					if (!leaveExpanded) {
						toggle(lis[index]);
					}
				}
			}
		}
	};
	if (autoApplyWithNoGlobal) {
		/*
		if (window.addEventListener) {
			window.addEventListener('DOMContentLoaded', applyLists, false);
		}
		else {
			window.attachEvent('onload', applyLists);
		}
		*/
		applyLists(!$('.enable_collapsibleList_collapsed').length);
	} else {
		CollapsibleLists = collapsibleLists;
	}
}());


/**
 * @param {Element} el
 * @param {string} newText
 * @returns {void}
 */
/*
function changeText (el, newText) {
	if (el.firstChild && el.firstChild.nodeValue) {
		el.firstChild.nodeValue = newText;
	}
}
*/

var t, toc, toggleLink;
try {
	t = document.getElementById('toc');
	if (t) { // No TOC for pages with no headings
		t.style.display = 'block';
		toc = t.getElementsByTagName('ul')[0];
		toggleLink = document.getElementById('toctogglecheckbox');
		// if (tocIsHidden()) {
		toggleToc();
		// }
	}
} catch (error) {
	// eslint-disable-next-line no-console -- Error surfacing
	console.log('erred', error);
}

/**
 * @returns {boolean}
 */
function tocIsHidden () {
	return !toc || !toggleLink || window.getComputedStyle(toc).display !== 'block';
}

/**
 * @returns {void}
 */
function toggleToc () {
	var hidden = tocIsHidden();
	if (hidden && document.cookie.indexOf('hidetoc=0') > -1) {
		toggleLink.click();
		// changeText(toggleLink, tocShowText);
		// toc.style.display = 'none';
		// eslint-disable-next-line sonarjs/no-duplicated-branches -- Might expand
	} else if (!hidden && document.cookie.indexOf('hidetoc=1') > -1) {
		toggleLink.click();
		// changeText(toggleLink, tocHideText);
		// toc.style.display = 'block';
	}
}

if (toggleLink) {
	toggleLink.addEventListener('click', function () {
		var isHidden = tocIsHidden();
		document.cookie = isHidden ? 'hidetoc=1' : 'hidetoc=0';
	});
}
// </source>

/**
 * @param {boolean} forceConceal
 * @returns {void}
 */
function toggleHeadings (forceConceal) {
	var headings = $('#content h2:not(#mw-toc-heading), #content h3, #content h4, #content h5, #content h6');
	var th = $('#toggle-headings');
	if (!forceConceal && localStorage.getItem('b9-hide-headings')) {
		localStorage.removeItem('b9-hide-headings');
		headings.show();
		th.text('Hide headings');
	} else {
		localStorage.setItem('b9-hide-headings', '1');
		headings.hide();
		th.text('Show headings');
	}
}

$(
	'<li><br /><a href="javascript:void(0);" id="toggle-headings">Hide headings</a></li>'
).appendTo(
	// '#p-tb .vector-menu-content-list'
	'.sidebar .navbar-nav'
).click(function () {
	toggleHeadings();
});

if (localStorage.getItem('b9-hide-headings')) {
	toggleHeadings(true);
}


// Search category pages
// If a category page
if (mw.config.get('wgNamespaceNumber') === 14 && $('#mw-pages').length) {
	$('<br><br><form action="/index.php"><label><b>Search category pages</b>: <input id="categoryWorkSearch" /></label>' +
		'<input type="submit" value="go"/></form>').submit(function () {
		var $el = $('<input type="hidden" name="search" />');
		$el.attr({
			value: 'incategory:"' + mw.config.get('wgTitle') + '" ' +
				$(this).find('#categoryWorkSearch').val()
		});
		$(this).append($el);
	}).appendTo('#mw-pages > p'); // .prependTo('#mw-content-text');
}

// Prepopulating user category pages
var params = new URLSearchParams(location.search);
if (params.has('prepopulate') && params.get('action') === 'edit' && !$('#wpTextbox1').val()) {
	var title = params.get('title');
	var match = title.match(/User:[^/]*([^&]*)$/);
	var text = '[[Category:User' + match[1] + ']]';
    $('#wpTextbox1').val(text);
}

// Sister project links (convert to specific page)
// If want these added server-side, could add as header/footer using https://www.mediawiki.org/wiki/Extension:Header_Footer
if (mw.config.get('wgNamespaceNumber') === 0) { // Namespaces: https://www.mediawiki.org/wiki/Extension_default_namespaces
	var pageName = mw.config.get('wgPageName');
	var encPageName = encodeURIComponent(pageName);
	var noUnderscoresPageName = encPageName.replace(/_/g, '+');
	// $('a.nav-link:contains("Bahai.media")').prop('href', 'https://bahai.media/index.php?search=' + noUnderscoresPageName + '&title=Special%3ASearch');
	// $('a.nav-link:contains("Bahai.works")').prop('href', 'https://bahai.works/index.php?search=' + noUnderscoresPageName + '&title=Special%3ASearch');
	// $('a.nav-link:contains("Bahaipedia.org")').prop('href', 'https://bahaipedia.org/index.php?search=' + noUnderscoresPageName + '&title=Special%3ASearch');
	// $('a.nav-link:contains("Bahai.org Writings")').prop('href', 'https://www.bahai.org/library/authoritative-texts/search?q=' + noUnderscoresPageName);
	$('a.nav-link:contains("Indexes")').prop('href', 'https://bahai-browser.org/indexes/json/?indexTerm=' + noUnderscoresPageName);
	$('a.nav-link:contains("Bahai-library")').prop('href', 'https://www.google.com/search?q=site%3Abahai-library.com+' + noUnderscoresPageName);
	// $('a.nav-link:contains("Bahai-library tags")').prop('href', 'https://bahai-library.com/tags/' + (pageName === 'Main_Page' ? '' : encodeURI(pageName)));
	// $('a.nav-link:contains("Wikipedia")').prop('href', 'https://en.wikipedia.org/?search=' + noUnderscoresPageName + '&title=Special%3ASearch');

    // Sister wikis via bahaidata.org
	$('.p-wikibase-otherprojects-toggle').hide();
	$('.nav-menu-label.other').after(
        $('<div id="bahai-wikibase-links" class="nav-item"></div>').append(
            $('.sidebar-menu .wb-otherproject-link > a')
        )
    );
}

/**
 * 9. Hide category container when no categories can exist (special pages) ***************************
 * 
 */
var catlinksElement = document.querySelector('.catlinks-allhidden');
var navElement = document.querySelector('nav.p-navbar.not-collapsible.small.mb-2');

if (catlinksElement && navElement) {
  navElement.parentNode.removeChild(navElement);
}

/**
 * 10. Bahai.org and Phelps Partial Inventory Tablet cross-references
 */

var wikibaseItemId = mw.config.get('wgWikibaseItemId');

if (wikibaseItemId) {
	var getMoreInfoLink = $('<a class="nav-link" href="javascript:void(0);">Show more links</a>');
	$('#bahai-wikibase-links').before(
		$('<div class="nav-item"></div>').append(getMoreInfoLink)
	);
	getMoreInfoLink.on('click', function () {
	    fetch('https://bahai-library.com/' + wikibaseItemId + '?format=json').then(function (resp) {
			return resp.json();
		}).then(function (json) {
			getMoreInfoLink.parent().remove();
			[
				['tag', 'Bahai-library tags', 'https://bahai-library.com/tag/'],
				['phelps', 'Partial Inventory', 'https://bahai-library.com/inventory/']
			].forEach(function (info) {
				if (!json[info[0]]) {
					return;
				}
				$('#bahai-wikibase-links').before(
					$('<div class="nav-item"><a class="nav-link" href="' +
					  encodeURI(info[2]) + encodeURIComponent(json[info[0]].replaceAll(' ', '_')) +
				    '">' + info[1] + '</a></div>')
				);
			});
			[
				['wikipedia', 'Wikipedia'],
				['bahaiOrg', 'Bahai.org']
			].forEach(function (info) {
				if (!json[info[0]]) {
					return;
				}
				$('#bahai-wikibase-links').before(
					$('<div class="nav-item"><a class="nav-link" href="' +
					  // encodeURI( // Already escaped
					  	json[info[0]] +
				  	  //) +
				    '">' + info[1] + '</a></div>')
				);
			});
		});
	});
}


/**
 * 11. Add a URL in the footer to the traffic statistics pages 
 */

$(function () {
    var allowedNamespaces = [0, 14];
    var ns = mw.config.get('wgNamespaceNumber');

    if (!allowedNamespaces.includes(ns) || mw.config.get('wgAction') !== 'view') return;

    var pageTitle = mw.config.get('wgPageName');
    var decodedTitle = pageTitle.replace(/_/g, ' ');
    var encodedTitle = encodeURIComponent(decodedTitle).replace(/%20/g, '+');

    var statsURL = 'https://digitalbahairesources.org/pageview-analysis?website_id=5&titles=%5B%22' +
        encodedTitle + '%22%5D&from_year=2024&from_month=7&to_year=2025&to_month=7';

    var $link = $('<a>')
        .attr('href', statsURL)
        .attr('target', '_blank')
        .text('📊 View Traffic Statistics')
        .css({ marginLeft: '1em' });

    var $container = $('#footer-info > div').first();
    if ($container.length) {
        $container.append($link);
    }
});

// if (mw.config.get('wgCategories').includes("Works of Bahá'u'lláh")) {
//   var wikibaseItemId = mw.config.get('wgWikibaseItemId');
//   mw.loader.using('mediawiki.api').then(function () {
//     var api = new mw.Api();
//     api.get({
//       action: 'expandtemplates',
//       prop: 'wikitext',
//       text: '{{#property:P50|from=' + wikibaseItemId + '}} ' + '{{#property:P51|from=' + wikibaseItemId + '}}'
//     }).done(function (data) {
//       var props = data.expandtemplates.wikitext.split(' ');
//       var bahaiOrg = props[0],
//         inventory = props[1];
//       if (bahaiOrg) {
//         $('#bahai-wikibase-links').before(
//         	$('<div class="nav-item"><a class="nav-link" href="' + encodeURI(bahaiOrg) + '">Bahai.org</a></div>')
//         );
//       }
//       if (inventory) {
//       	// See also http://blog.loomofreality.org/?page_id=252
//       	var inv = inventory.replace(/</g, '&lt;').replace(/&/g, '&amp;');
//       	$('#bahai-wikibase-links').before(
//       		$('<div class="nav-item"><a class="nav-link" href="https://bahai-library.com/inventory/' + inv + '">Inventory ID: ' +
//         	  inv +
//       	  '</a></div>')
//   		);
//       }
//     });
//   });
// }
Retrieved from "https://bahai9.com/index.php?title=MediaWiki:Common.js&oldid=22692"
This page was last edited on 17 July 2025, at 23:25.
Content is available under Creative Commons Attribution-Share Alike or custom copyright unless otherwise noted.
Privacy policy
About Bahai9
Disclaimers
Powered by MediaWiki