MediaWiki:Gadget-Hideable.js

From Pikipedia, the Pikmin wiki
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.
/* table.hideable, version 1.1.1 by Greenpickle (GPL3) */

var hideImageURL = 'https://pikmin.wiki.gallery/images/hide.png';
var showImageURL = 'https://pikmin.wiki.gallery/images/show.png';
var hideableColClass = 'hideable';
var hiddenColClass = 'hidden';
var hideableShowClass = 'showcol';

function getElementsByTagNames (node) {
	// return an array of elements in the node with the given tag names
	var nodes = [];
	for (var i = 1; i < arguments.length; i++) {
		var newNodes = node.getElementsByTagName(arguments[i]);
		try {
			// no idea where this'll fail if it does
			newNodes = Array.prototype.slice.call(newNodes);
			nodes = nodes.concat(newNodes);
		} catch (e) {
			// do it the slow way
			for (var j = 0; j < newNodes.length; j++) nodes.push(newNodes[j]);
		}
	}
	return nodes;
}

function matchTagName (node) {
	// check if node.tagName is one of given names, case-insensitively
	if (node.tagName === undefined) return false;
	tag = node.tagName.toLowerCase();
	for (var i = 1; i < arguments.length; i++) {
		if (arguments[i].toLowerCase() == tag) return true;
	}
	return false;
}

function getContainer (node, tag) {
	// return nearest parent with given tag name
	tag = tag.toLowerCase();
	var container = node;
	do {
		container = container.parentNode;
		if (container === document) return undefined;
	} while (container.tagName.toLowerCase() != tag);
	return container;
}

function nextElement (e) {
	do e = e.nextSibling;
	while (e !== null && e.nodeType != 1);
	return e;
}

function previousElement (e) {
	do e = e.previousSibling;
	while (e !== null && e.nodeType != 1);
	return e;
}

var hasClass = (function () {
	var reCache = {};
	return function (element, className) {
		return (reCache[className] ? reCache[className] : (reCache[className]
			= new RegExp("(?:\\s|^)" + className + "(?:\\s|$)")))
			.test(element.className);
	};
})();

var hideable = {};

hideable.showCallback = function () {
	hideable.show(this.parentNode);
	return false;
};

hideable.hideCallback = function () {
	hideable.hide(this.parentNode);
	return false;
};

hideable.createLink = function (isHide) {
	// create a show/hide link
	var lnk = document.createElement('a');
	lnk.href = '#';
	lnk.onclick = isHide ? hideable.hideCallback : hideable.showCallback;
	var img = document.createElement('img');
	img.alt = isHide ? 'hide' : 'show';
	img.src = isHide ? hideImageURL : showImageURL;
	lnk.appendChild(img);
	return lnk;
};

hideable.getColSpan = function (cell, orig) {
	return orig ? cell.origColSpan || cell.colSpan : cell.colSpan;
};

hideable.setColSpan = function (cell, colSpan) {
	if (cell.origColSpan === undefined)
		// store original colspan
		cell.origColSpan = cell.colSpan;
	cell.colSpan = colSpan;
};

hideable.getCol = function (targetCell, orig) {
	var row = getContainer(targetCell, 'tr');
	if (row === undefined) throw('cell not in a table row');
	// sum colspans along row
	var col = 0;
	var children = getElementsByTagNames(row, 'th', 'td');
	for (var i = 0; i < children.length; i++) {
		var cell = children[i];
		if (cell === targetCell) break;
		if (orig || cell.style.display != 'none')
			// cell is not hidden, or we want hidden cells: add its colspan
			col += hideable.getColSpan(cell, orig);
	}
	return col;
};

hideable.cellAtCol = function (row, targetCol, orig) {
	var col = 0;
	var cells = getElementsByTagNames(row, 'th', 'td');
	for (var i = 0; i < cells.length; i++) {
		var cell = cells[i];
		if (orig || cell.style.display != 'none') {
			// cell is not hidden, or we want hidden cells: add its colspan
			col += hideable.getColSpan(cell, orig);
			if (col > targetCol) return cell;
		}
	}
};

hideable.cellsInCol = function (cell) {
	// return array of cells in the same column as the given one
	if (!matchTagName(cell, 'td', 'th')) throw('not a table cell');
	var table = getContainer(cell, 'table');
	if (table === undefined) throw('cell not in a table');
	var col = hideable.getCol(cell, true);
	var rows = table.getElementsByTagName('tr');
	var cells = [];
	for (var i = 0; i < rows.length; i++) {
		cells.push(hideable.cellAtCol(rows[i], col, true));
	}
	return cells;
};

hideable.hide = function (cell) {
	var cells = hideable.cellsInCol(cell);
	for (var i = 0; i < cells.length; i++) {
		if (i == 0) {
			// replace header with 'show' button
			var showCell = document.createElement(cells[i].tagName);
			showCell.colspan = cells[i].colSpan;
			showCell.className = hideableShowClass;
			showCell.appendChild(hideable.createLink(false));
			hideable.hiddenHeaders[hideable.getCol(cells[i], false)] =
				cells[i].parentNode.replaceChild(showCell, cells[i]);
		} else {
			// hide this column's cells
			cells[i].style.display = 'none';
			// expand next visible column's cells, if any, to this one
			var expand = cells[i];
			do expand = nextElement(expand);
			while (expand !== null &&
				(expand.nodeType != 1 || expand.style.display == 'none'))
			if (expand === null) {
				// couldn't find a next column: look for a previous one
				expand = cells[i];
				do expand = previousElement(expand);
				while (expand !== null &&
					(expand.nodeType != 1 || expand.style.display == 'none'))
			}
			if (expand !== null)
				hideable.setColSpan(expand, expand.colSpan + cells[i].colSpan);
		}
	}
};

hideable.show = function (cell) {
	var cells = hideable.cellsInCol(cell);
	for (var i = 0; i < cells.length; i++) {
		// show this column's cells
		cells[i].style.display = '';
		if (i == 0) {
			// remove 'show' button
			var col = hideable.getCol(cells[i], false);
			var origCell = hideable.hiddenHeaders[col];
			cells[i] = cells[i].parentNode.replaceChild(origCell, cells[i]);
		} else {
			cell = cells[i];
			// work out where we want the ends of the cell to be
			var leftEdge = hideable.getCol(cell, true);
			var rightEdge = leftEdge + (cell.origColSpan || cell.colSpan);
			var change = 0;
			var prevCell = previousElement(cell);
			while (prevCell !== null) {
				if (prevCell.style.display == 'none')
					// move left to cover hidden cells directly to the left
					leftEdge -= prevCell.origColSpan || prevCell.colSpan;
				else {
					// shrink the first visible cell to the left if it covers
					// any hidden cells we want this cell to cover
					var pos = hideable.getCol(prevCell, false);
					if (pos + prevCell.colSpan > leftEdge) {
						change = pos + prevCell.colSpan - leftEdge;
						hideable.setColSpan(prevCell, leftEdge - pos);
					}
					break;
				}
				prevCell = previousElement(prevCell);
			}
			var nextCell = nextElement(cell);
			var flowRight = 0;
			// need to explicitly set to undefined as we reuse it
			var nextVisible = undefined;
			while (nextCell !== null) {
				if (nextCell.style.display == 'none')
					// expand to cover hidden cells to the right
					flowRight += nextCell.origColSpan || nextCell.colSpan;
				else {
					// until we encounter a visible one, which should cover
					// them instead
					flowRight = 0;
					nextVisible = nextCell;
					break;
				}
				nextCell = nextElement(nextCell);
			}
			rightEdge += flowRight;
			// expand cell as far right as needed
			hideable.setColSpan(cell, rightEdge - leftEdge);
			change -= cell.colSpan;
			if (nextVisible !== undefined)
				// expand or shrink the visible cell directly to the right to
				// adjust for the changes we've made
				hideable.setColSpan(nextVisible, nextVisible.colSpan + change);
		}
	}
};

hideable.init = function () {
	var tables = document.getElementsByTagName('table');
	if (tables.length == 0) return;
	hideable.hiddenHeaders = [];
	// load images
	new Image().src = hideImageURL;
	new Image().src = showImageURL;
	for (var i = 0; i < tables.length; i++) {
		// operate on first row
		var row = tables[i].getElementsByTagName('tr')[0];
		if(row === undefined) return;
		var cells = getElementsByTagNames(row, 'th', 'td');
		for (var j = 0; j < cells.length; j++) {
			if (hasClass(cells[j], hideableColClass)) {
				// add 'hide' button
				cells[j].appendChild(hideable.createLink(true));
				// hide column now if want to
				if (hasClass(cells[j], hiddenColClass))
					hideable.hide(cells[j]);
			}
		}
	}
};

$( hideable.init );