Source Code for PHP & JavaScript Colour Picker

At the moment the purpose of this page is just to show the three files which are in use to make this work. That's index.html, colour.js and colour.css. When I find/make time to augment it a little, this page will be further prettified with syntax highlighting and some pre-amble about each file. I'll also put a few links in, such as one to take you back to the tool. I will also provide links to validate both the HTML and the CSS, although it may all be a bit dicey given that a lot of content is generated by the javascript, and I'm not sure if the validator can take account of that.

html

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-gb" lang="en">
<head>
    <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=iso-8859-1" />
    <title>JavaScript Colour Picker - evilstreak</title>
    <link rel="stylesheet" type="text/css" href="colour.css" />
    <script type="text/javascript" src="colour.js" />
</head>
<body onload="javascript:resetPalette();">

<div id="menu">
<ul>
    <li><a href="javascript:showInstructions();" title="Find out how to use the colour picker.">Instructions</a></li>
    <li><a href="source.php" title="View the PHP and JavaScript that make the colour picker work.">View Source</a></li>
</ul>
<ul>
    <li><a href="javascript:resetPalette();" title="Reset the colour picker to its original state, wiping all your stored colours.">Reset All</a></li>
    <li><a href="javascript:refreshPalette(0,0,0);" title="Reset the palette to the 216 web-safe palette, but keep all your stored colours.">Web-safe</a></li>
</ul>
<ul>
    <li class="inline">
        <a id="sm" href="javascript:setStepSize(0x08);" title="Set the step size to small. This will make the palette colours correlate strongly.">S</a>
        <a id="md" href="javascript:setStepSize(0x11);" title="Set the step size to medium. This will make the palette colours correlate weakly.">M</a>
        <a id="lg" href="javascript:setStepSize(0x33);" title="Set the step size to large. This will display the web-safe palette.">L</a>
    </li>
    <li style="clear:both"><a href="javascript:storeColour();" title="Add the current colour to your &quot;paintbox&quot; to compare against other colours.">Store Colour</a></li>
    <li><a id="link" href="" title="Get a link to this page with your stored colours so you can share it with friends.">Create Link</a></li>
</ul>
<ul>
    <li id="current">#000000</li>
</ul>
</div><!-- end of #menu -->

<div id="links">
<ul>
    <li><a href="javascript:void(0);" title="Copyright &copy; 2005 Dominic Baggott">Copyright</a></li>
    <li><a href="archive/" title="Take  look at previous versions of this tool, some a lot less efficient than this one!">Old Versions</a></li>
    <li><a href="http://dbwebdesign.net/" title="Visit the author's site.">DB Web Design</a></li>
</ul>
</div><!-- end of #links -->

<div id="palette"></div>
<div id="paintbox"></div>

</body>
</html>

js

/* private void refreshTextColour ()
*
* this function refreshes the text colour used on the screen
* picking either white or black for optimum legibility
*/
function refreshTextColour()
{
    // this looks for the rgb values in "rgb(x, x, x)"
    var re = /^rgb\((\d{1,3}),\s(\d{1,3}),\s(\d{1,3})\)/;
    var colours = re.exec(document.body.style.background);

    // this decides whether black or white is more visible
    // see [http://www.w3.org/TR/AERT#color-contrast]
    r = colours[1] * 299;
    g = colours[2] * 587;
    b = colours[3] * 114;
    rgb = ((r + g + b) / 1000) > 127 ? '000' : 'fff';

    // set the text color
    document.body.style.color = '#' + rgb;
}

/* private string makeHex (int r, int g, int b)
*
* this function glues together three rgb values into
* a single string, making sure each part is 2 digits
*/
function makeHex(r,g,b)
{
    r = parseInt(r,10).toString(16);
    if (r.length != 2) r = '0' + r;
    g = parseInt(g,10).toString(16);
    if (g.length != 2) g = '0' + g;
    b = parseInt(b,10).toString(16);
    if (b.length != 2) b = '0' + b;
    return (r + g + b);
}

/* public void refreshPalette (int r, int g, int b)
*
* this function refreshes the main colour palette, by
* taking the given set of rgb values, and building up
* from 2 steps below to 3 steps above. this results
* in 216 colours (6**3) being sent to the screen.
*/
function refreshPalette(r,g,b)
{
    var r0,g0,b0;
    if (r < stepSize * 2) r0 = 0;
    else if (r > 255 - stepSize * 5) r0 = 255 - stepSize * 5;
    else r0 = r - stepSize * 2;

    if (g < stepSize * 2) g0 = 0;
    else if (g > 255 - stepSize * 5) g0 = 255 - stepSize * 5;
    else g0 = g - stepSize * 2;

    if (b < stepSize * 2) b0 = 0;
    else if (b > 255 - stepSize * 5) b0 = 255 - stepSize * 5;
    else b0 = b - stepSize * 2;

    // temporary variable to hold data
    var newPalette = '';

    // build new palette
    for (var i = 0, r = r0; i < 6; i++, r += stepSize)
    {
        // this div and the ones nested in it simply
        // allow the block wrapping to work
        newPalette += '<div>';
        for (var j = 0, g = g0; j < 6; j++, g += stepSize)
        {
            newPalette += '<div>';
            for (var k = 0, b = b0; k < 6; k++, b += stepSize)
            {
                var rgb = makeHex(r,g,b);
                // this is the link which holds the css styling
                // to make it a block
                newPalette += '<a ' +
                    'href="javascript:setColour('+r+','+g+','+b+');" ' +
                    'title="#' + rgb + '" ' +
                    'style="background:#' + rgb + '">' +
                    '</a>';
            }
            newPalette += '</div>';
        }
        newPalette += '</div>';
    }

    // send to the screen
    document.getElementById('palette').innerHTML = newPalette;
}

/* public void resetPalette ()
*
* this function resets the colour picker back to its
* original state, wiping all stored colours.
*/
function resetPalette()
{
    // remove all paintbox patches
    paintbox = document.getElementById('paintbox');
    while (paintbox.childNodes.length > 0)
        paintbox.removeChild(paintbox.childNodes[0]);

    // set palette to display web-safe colours
    setStepSize(0x33);
    setColour(0,0,0);

    // set step size to medium
    setStepSize(0x11);
}

/* public void showInstructions ()
*
* this function resets the colour picker back to its
* original state, wiping all stored colours.
*/
function showInstructions ()
{
    document.getElementById('palette').innerHTML =
    '<ul>' +
    '<li>guidance</li>' +
        '<ul>' +
        '<li>click any square to focus the colour patches in the palette around that colour. the step size affects how tightly the colours will correlate.</li>' +
        '</ul>' +
    '</li>' +
    '<li>menu</li>' +
        '<ul>' +
        '<li><strong>instructions</strong> will bring these instructions up, but then i guess you know that now!</li>' +
        '<li><strong>view source</strong> will display the source code from the 3 files to make this tool, along with comments.</li>' +
        '<li><strong>reset all</strong> will fully reset the state of the colour picker. all of the colours in your "paintbox" will be lost, along with the current selections.</li>' +
        '<li><strong>web-safe</strong> will put the colours of the palette back to the web-safe colours. it will leave your selected colour and step size the same.</li>' +
        '<li><strong>step size</strong> options allow you to control how close the colours in the palette are to each other.</li>' +
            '<ul>' +
            '<li><strong>small</strong> step size will make the colours correlate highly.</li>' +
            '<li><strong>medium</strong> step size will make the colours correlate weakly.</li>' +
            '<li><strong>large</strong> step size will not make the colours correlate at all, in fact it will give you the web-safe palette.</li>' +
            '</ul>' +
        '</ul>' +
    '</li>' +
    '</ul>';
}

/* public void setStepSize (int size)
*
* this function sets the stepSize in use to the
* value of the parameter passed. 0x33, 0x11 and
* 0x08 are the three values currently used.
*/
function setStepSize (size)
{
    // store parameter so other functions can access it
    stepSize = size;

    // refreshTextColour();

    // reset all links to default appearance
    document.getElementById('sm').style.fontWeight = 'normal';
    document.getElementById('md').style.fontWeight = 'normal';
    document.getElementById('lg').style.fontWeight = 'normal';

    // determine highlighted link(s)
    switch (stepSize)
    {
        case 0x08:
            var id = 'sm';
            break;
        case 0x11:
            var id = 'md';
            break;
        case 0x33:
            var id = 'lg';
            break;
    }
    document.getElementById(id).style.fontWeight = 'bold';
}

/* public void setColour (int r, int g, int b)
*
* this function sets the background colour to the
* value of the parameters passed, and refreshed
* the palette to focus around that colour.
*/
function setColour (r,g,b)
{
    document.body.style.background = '#' + makeHex(r,g,b);
    document.getElementById('current').innerHTML = '#' + makeHex(r,g,b);
    refreshTextColour();
    refreshPalette(r,g,b);
}

function storeColour ()
{
    // this looks for the rgb values in "rgb(x, x, x)"
    var re = /^rgb\((\d{1,3}),\s(\d{1,3}),\s(\d{1,3})\)/;
    var colours = re.exec(document.body.style.background);

    // this decides whether black or white is more visible
    // see [http://www.w3.org/TR/AERT#color-contrast]
    var r = colours[1] * 299;
    var g = colours[2] * 587;
    var b = colours[3] * 114;
    var x_colour = ((r + g + b) / 1000) > 127 ? 'light' : 'dark';

    var r = colours[1];
    var g = colours[2];
    var b = colours[3];

    pb_entry = document.createElement('div');
    pb_patch = document.createElement('a');
    pb_delete = document.createElement('a');

    pb_entry.style.top = '430px';

    pb_patch.setAttribute('class','pb_patch');
    pb_patch.setAttribute('href','javascript:setColour('+r+','+g+','+b+');');
    pb_patch.setAttribute('title','#' + makeHex(r,g,b));
    pb_patch.style.background = '#' + makeHex(r,g,b);

    pb_delete.setAttribute('class','pb_delete_' + x_colour);
    pb_delete.setAttribute('href','#');
    pb_delete.setAttribute('title','Delete this colour from your &quot;paintbox&quot;.');
    pb_delete.appendChild(document.createTextNode('x'));

    pb_delete.onclick = function () { javascript:this.parentNode.parentNode.removeChild(this.parentNode); return false; };

    pb_entry.appendChild(pb_patch);
    pb_entry.appendChild(pb_delete);

    document.getElementById('paintbox').appendChild(pb_entry);
}

css

body
{
    background: #000;
    color: #fff;
    font-family: Verdana, Tahoma, Sans-serif;
    font-size: 10px;
}


/* menu & links on RHS of screen */

div#menu, div#links
{
    position: absolute;
    right: 0px;
    width: 100px;
}

div#menu  { top:    0; }
div#links { bottom: 0; }

div#menu ul,
div#links ul
{
    margin: 0;
    padding: 0;
    line-height: 2.5em;
    text-align: center;
}

div#menu ul  { margin-bottom: 20px; }
div#links ul { margin-top:    20px; }

div#menu li,
div#links li
{
    list-style: none;
    border-left: 1px dashed #fff;
}

div#menu li
{
    border-bottom: 1px dashed #fff;
}
div#links li
{
    border-top: 1px dashed #fff;
}

div#menu ul li:first-child
{
    border-top: 1px dashed #fff;
}

div#menu ul:first-child li
{
    border-top: 0;
}

div#menu li.inline
{
    height: 2.5em;
}

div#menu a#sm,
div#menu a#md,
div#menu a#lg
{
    display: block;
    line-height: 2.5em;
    float: left;
    width: 33px;
}

div#menu a, div#links a
{
    display: block;
    padding: 0;
    line-height: 2.5em;
    color: #f93;
}

div#menu a:hover,
div#links a:hover
{
    background: #fff;
    color: #900;
}

/* main colour palette */

div#palette
{
    width: 540px;
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -180px 0 0 -270px;
}

div#palette div
{
    float: left;
}

div#palette a
{
    display: block;
    width: 30px;
    height: 30px;
}

/* paintbox */

div#paintbox
{
    position: absolute;
    left: 30px;
}

div#paintbox div
{
    padding-top: 30px;
    text-align: right;
}

div#paintbox a.pb_patch
{
    display: block;
    height: 50px;
    width: 50px;
}

div#paintbox a.pb_delete_dark,
div#paintbox a.pb_delete_light
{
    position: relative;
    top: -15px;
    padding: 1px 4px 3px;
    text-decoration: none;
}

div#paintbox a.pb_delete_dark
{
    color: #fff;
}

div#paintbox a.pb_delete_dark:hover
{
    background: #ccc;
    color: #900;
}

div#paintbox a.pb_delete_light
{
    color: #000;
}

div#paintbox a.pb_delete_light:hover
{
    background: #333;
    color: #f99;
}