scriptspry / BitBoxes

// ==UserScript==
// @name         BitBoxes
// @namespace    https://scriptspry.com/
// @version      0.1
// @license      MIT
// @description  Checkboxes for bitbucket cause we need them! ☐☑☒
// @author       You
// @match        https://bitbucket.org/*
// @grant        none
// ==/UserScript==

((() => {
	/**
	* Gets an array of the matching text nodes contained by the specified element.
	* @param  {!Element} elem
	*     The DOM element which will be traversed.
	* @param  {function(!Node,!Element):boolean} opt_fnFilter
	*     Optional function that if a true-ish value is returned will cause the
	*     text node in question to be added to the array to be returned from
	*     getTextNodesIn().  The first argument passed will be the text node in
	*     question while the second will be the parent of the text node.
	* @return {!Array.<!--Node-->}
	*     Array of the matching text nodes contained by the specified element.
	*
	* // Source & Thanks to: http://cwestblog.com/2014/03/14/javascript-getting-all-text-nodes/
	*
	**/
	function getTextNodesIn(elem, opt_fnFilter) {
		let textNodes = [];
		if (elem) {
			for (let nodes = elem.childNodes, i = nodes.length; i--;) {
				const node = nodes[i];
				const nodeType = node.nodeType;
				if (nodeType == 3) {
					if (!opt_fnFilter || opt_fnFilter(node, elem)) {
						textNodes.push(node);
					}
				}
				else if (nodeType == 1 || nodeType == 9 || nodeType == 11) {
					textNodes = textNodes.concat(getTextNodesIn(node, opt_fnFilter));
				}
			}
		}
		return textNodes;
	}

	const replacements = new Map([
		[new RegExp('\\[_\\]', 'igm'), '☐'],
		[new RegExp('\\[x\\]', 'igm'), '☒'],
		[new RegExp('\\[-\\]', 'igm'), '☑'],
		[new RegExp('\\[v\\]', 'igm'), '☑'],
		[new RegExp('\\[\\*\\]', 'igm'), '☑']
	]);
	const regexes = Array.from(replacements.keys());
	const hasSymbol = node => regexes.find(s => node.textContent.match(s));

	const boxify = () => {
		document.querySelectorAll('li').forEach(li => {
			getTextNodesIn(li, hasSymbol).forEach(textNode => {
				for (const [symbol, replacement] of replacements)
					textNode.textContent = textNode.textContent.replace(symbol, replacement);
			});
		});
	};

	if (jQuery !== undefined)
		jQuery(boxify);
	else
		window.addEventListener('load', 'DOMContentLoaded', boxify, false);
}))();