MediaWiki:Gadget-Hideable.js

/* 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 );