2 * Try to color the cells of comparison tables based on their contents.
6 (function comparecells() {
7 /* Create a new IFRAME to get a "clean" Window object, so we can use its
8 * console. Sometimes sites (e.g. Twitter) override console.log and even
9 * the entire console object. "delete console.log" or "delete console"
10 * does not always work, and messing with the prototype seemed more
11 * brittle than this. */
12 let console = (function () {
13 let iframe = document.getElementById('xxxJanConsole');
15 iframe = document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
16 iframe.id = 'xxxJanConsole';
17 iframe.style.display = 'none';
19 (document.body || document.documentElement).appendChild(iframe);
22 return iframe && iframe.contentWindow && iframe.contentWindow.console || {
28 * Get the text content for the given element.
30 function getTextFromElement(element) {
31 /* TODO: take IMG@alt, BUTTON@value etc. into account */
32 return element.textContent.trim().toLowerCase();
36 * Get a Uint8Array of the SHA-256 bytes for the given string.
38 async function getSha256Bytes(string) {
41 typeof crypto === 'object' && typeof crypto.subtle === 'object' && typeof crypto.subtle.digest === 'function'
42 && typeof Uint8Array === 'function'
43 && typeof TextEncoder === 'function'
45 return new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder('utf-8').encode(string)));
52 async function getColorsForValue(value) {
53 /* Cache the calculated values. */
54 getColorsForValue.cellValuesToRgb = getColorsForValue.cellValuesToRgb || {};
56 if (!getColorsForValue.cellValuesToRgb[value]) {
57 let normalizedValue = value.trim().toLowerCase();
79 if (yesValues.indexOf(normalizedValue) > -1) {
80 /* Make "Yes" cells green. */
81 getColorsForValue.cellValuesToRgb[value] = [ 150, 255, 32 ];
82 } else if (noValues.indexOf(normalizedValue) > -1) {
83 /* Make "No" cells green. */
84 getColorsForValue.cellValuesToRgb[value] = [ 238, 32, 32 ];
85 } else if ((shaBytes = await getSha256Bytes(normalizedValue))) {
86 /* Give other cells a color based on their content’s SHA
87 * hash to ensure “consistent random colors” every time. */
88 getColorsForValue.cellValuesToRgb[value] = [
94 /* If the SHA hash could not be calculated, just use random
95 * values. These will change on every execution. */
96 getColorsForValue.cellValuesToRgb[value] = [
104 /* Calculate/approximate the lightness (tweaked from “RGB to HSL”) to
105 * determine whether black or white text is best suited. */
106 let isLight = 150 < (
107 getColorsForValue.cellValuesToRgb[value][0] * 0.299
108 + getColorsForValue.cellValuesToRgb[value][1] * 0.587
109 + getColorsForValue.cellValuesToRgb[value][2] * 0.114
113 backgroundColor: 'rgb(' + getColorsForValue.cellValuesToRgb[value].join(', ') + ')',
118 ? '1px 1px 3px white'
119 : '1px 1px 3px black'
123 /* The main function. */
124 (function execute(document) {
125 Array.from(document.querySelectorAll('table')).forEach(table => {
126 Array.from(table.tBodies).forEach(tBody => {
127 if (tBody.rows.length < 3) {
128 console.log('Compare cells: skipping table body ', tBody, ' because it only has ', tBody.rows.length, ' rows');
132 Array.from(tBody.rows).forEach(tr => {
133 /* Determine the values. */
135 let uniqueCellValues = new Set();
137 Array.from(tr.cells).forEach((cell, i) => {
138 /* Don't take the header cells into account. */
139 if (cell.tagName.toUpperCase() === 'TH') {
143 /* Assume the first cell is a header cell, even if it is not a TH. */
148 cellValues[i] = getTextFromElement(cell);
149 uniqueCellValues.add(cellValues[i]);
152 /* Color (or not) the cells based on the values. */
153 let isFirstValue = true;
155 cellValues.forEach(async function(cellValue, i) {
156 let hasTwoUniqueValues = uniqueCellValues.size == 2;
158 firstValue = cellValue;
159 isFirstValue = false;
167 uniqueCellValues.size == 1 ||
168 (hasTwoUniqueValues && cellValue === firstValue) ||
169 cellValue.trim() === ''
171 backgroundColor = 'inherit';
173 textShadow = 'inherit';
175 backgroundColor = (await getColorsForValue(cellValue)).backgroundColor;
176 color = (await getColorsForValue(cellValue)).color;
177 textShadow = (await getColorsForValue(cellValue)).textShadow;
180 tr.cells[i].style.setProperty('background-color', backgroundColor, 'important');
181 tr.cells[i].style.setProperty('color', color, 'important');
182 tr.cells[i].style.setProperty('text-shadow', textShadow, 'important');
188 /* Recurse for frames and iframes. */
190 Array.from(document.querySelectorAll('frame, iframe, object[type^="text/html"], object[type^="application/xhtml+xml"]')).forEach(function (elem) {
191 execute(elem.contentDocument);
194 /* Catch exceptions for out-of-domain access, but do not do anything with them. */