/*rainbowEverything=*/(function(window) { var parseRgbColorStrToHsl = function(rgbColorStr) { // input: "rgb(12, 34, 56)"; var rgbValues = rgbColorStr.substring(4).replace(/\s/g, "").split(","), r = parseFloat(rgbValues[0])/255, g = parseFloat(rgbValues[1])/255, b = parseFloat(rgbValues[2])/255; var max = Math.max(r, g, b), min = Math.min(r, g, b); var h = 0, s = 0, l = (max + min) / 2; if (max == min) { h = s = 0; } else { var d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return [h, s, l]; } var replaceTextNode = function(parentNode, targetTextNode) { var textContent = targetTextNode.nodeValue; var originalColorStr = document.defaultView.getComputedStyle(parentNode).color; var originalColorHsl = parseRgbColorStrToHsl(originalColorStr); // fixme: ES5 String.split() is broken with surrogate pairs return function() { if (!textContent) return; textContent .split("") .map(function(char, index) { var h = (index % 20) * (360 / 20.0), s = (originalColorHsl[1] * 60 + 33) + "%", l = (originalColorHsl[2] * 60 + 33) + "%", colorStr = "hsl(" + h + "," + s + "," + l + ")"; var font = document.createElement("font"); //prevent layout-breaking font.textContent = char; font.style.color = colorStr; return font; }) .forEach(function(newNode) { parentNode.insertBefore(newNode, targetTextNode); }); parentNode.removeChild(targetTextNode); }; }; // cannot manipulate NodeList[] during traversal, // therefore we use a job queue to performan all replacements at the end var jobFuncQueue = []; var traverseTextNodeRecursively = function(parentNode) { var childNodes = parentNode.childNodes; [].forEach.call(childNodes, function(node, index) { switch (node.nodeType) { case Node.ELEMENT_NODE: traverseTextNodeRecursively(node); case Node.TEXT_NODE: var jobFunc = replaceTextNode(parentNode, node); if (jobFunc) { jobFuncQueue.push(jobFunc); } } }); }; traverseTextNodeRecursively(document.body); jobFuncQueue.forEach(function(jobFunc) { if (jobFunc) jobFunc(); }); })(window);