var CROSSHAIRS_LOCATION = 'files/selector.gif';
var HUE_SLIDER_LOCATION = 'files/hue.png';
var HUE_SLIDER_ARROWS_LOCATION = 'files/arrows.png';
var SAT_VAL_SQUARE_LOCATION = 'files/bg.png';
var DATA_FILE = 'files/data.js';
var RGB_MAX=255;
/* These are the increments, used to generate the Macys reduced color pallet. */
var RED_STEP=8;
var GREEN_STEP=8;
var BLUE_STEP=16;
var HEX_RE=new RegExp("^[0-9A-F]{6}$");


function hexToRgb(hex_string, default_, decimal)
{
    if (default_ == undefined)
    {
        default_ = null;
    }

    if (hex_string.substr(0, 1) == '#')
    {
        hex_string = hex_string.substr(1);
    }
    
    var r;
    var g;
    var b;
    if (hex_string.length == 3)
    {
        r = hex_string.substr(0, 1);
        r += r;
        g = hex_string.substr(1, 1);
        g += g;
        b = hex_string.substr(2, 1);
        b += b;
    }
    else if (hex_string.length == 6)
    {
        r = hex_string.substr(0, 2);
        g = hex_string.substr(2, 2);
        b = hex_string.substr(4, 2);
    }
    else
    {
        return default_;
    }
    
    r = parseInt(r, 16);
    g = parseInt(g, 16);
    b = parseInt(b, 16);
    if (isNaN(r) || isNaN(g) || isNaN(b))
    {
        return default_;
    }
    else if (decimal) //JC: added decimal output
    {
        return {r: r, g: g, b: b};
    }
    else
    {
        return {r: r / 255, g: g / 255, b: b / 255};
    }
}

function rgbToHex(r, g, b, includeHash,decimal)
{
    if (decimal!=1) {
      r = Math.round(r * 255);
      g = Math.round(g * 255);
      b = Math.round(b * 255);
    }
    if (includeHash == undefined)
    {
        includeHash = false;
    }
    
    r = r.toString(16);
    if (r.length == 1)
    {
        r = '0' + r;
    }
    g = g.toString(16);
    if (g.length == 1)
    {
        g = '0' + g;
    }
    b = b.toString(16);
    if (b.length == 1)
    {
        b = '0' + b;
    }
    return ((includeHash ? '#' : '') + r + g + b).toUpperCase();
}

var arVersion = navigator.appVersion.split("MSIE");
var version = parseFloat(arVersion[1]);

function fixPNG(myImage)
{
    if ((version >= 5.5) && (version < 7) && (document.body.filters)) 
    {
        var node = document.createElement('span');
        node.id = myImage.id;
        node.className = myImage.className;
        node.title = myImage.title;
        node.style.cssText = myImage.style.cssText;
        node.style.setAttribute('filter', "progid:DXImageTransform.Microsoft.AlphaImageLoader"
                                        + "(src=\'" + myImage.src + "\', sizingMethod='scale')");
        node.style.fontSize = '0';
        node.style.width = myImage.width.toString() + 'px';
        node.style.height = myImage.height.toString() + 'px';
        node.style.display = 'inline-block';
        return node;
    }
    else
    {
        return myImage.cloneNode(false);
    }
}

function trackDrag(node, handler)
{
    function fixCoords(x, y)
    {
        var nodePageCoords = pageCoords(node);
        x = (x - nodePageCoords.x) + document.documentElement.scrollLeft;
        y = (y - nodePageCoords.y) + document.documentElement.scrollTop;
        if (x < 0) x = 0;
        if (y < 0) y = 0;
        if (x > node.offsetWidth - 1) x = node.offsetWidth - 1;
        if (y > node.offsetHeight - 1) y = node.offsetHeight - 1;
        return {x: x, y: y};
    }
    function mouseDown(ev)
    {
        var coords = fixCoords(ev.clientX, ev.clientY);
        var lastX = coords.x;
        var lastY = coords.y;
        handler(coords.x, coords.y);

        function moveHandler(ev)
        {
            var coords = fixCoords(ev.clientX, ev.clientY);
            if (coords.x != lastX || coords.y != lastY)
            {
                lastX = coords.x;
                lastY = coords.y;
                handler(coords.x, coords.y);
            }
        }
        function upHandler(ev)
        {
            RemoveEventListener(document, 'mouseup', upHandler);
            RemoveEventListener(document, 'mousemove', moveHandler);
            AddEventListener(node, 'mousedown', mouseDown);
        }
        AddEventListener(document, 'mouseup', upHandler);
        AddEventListener(document, 'mousemove', moveHandler);
        RemoveEventListener(node, 'mousedown', mouseDown);
        if (ev.preventDefault) ev.preventDefault();
    }
    AddEventListener(node, 'mousedown', mouseDown);
    node.onmousedown = function(e) { return false; };
    node.onselectstart = function(e) { return false; };
    node.ondragstart = function(e) { return false; };
}

var eventListeners = [];

function findEventListener(node, event, handler)
{
    var i;
    for (i in eventListeners)
    {
        if (eventListeners[i].node == node && eventListeners[i].event == event
         && eventListeners[i].handler == handler)
        {
            return i;
        }
    }
    return null;
}
function AddEventListener(node, event, handler)
{
    if (findEventListener(node, event, handler) != null)
    {
        return;
    }

    if (!node.addEventListener)
    {
        node.attachEvent('on' + event, handler);
    }
    else
    {
        node.addEventListener(event, handler, false);
    }

    eventListeners.push({node: node, event: event, handler: handler});
}

function removeEventListenerIndex(index)
{
    var eventListener = eventListeners[index];
    delete eventListeners[index];
    
    if (!eventListener.node.removeEventListener)
    {
        eventListener.node.detachEvent('on' + eventListener.event,
                                       eventListener.handler);
    }
    else
    {
        eventListener.node.removeEventListener(eventListener.event,
                                               eventListener.handler, false);
    }
}

function RemoveEventListener(node, event, handler)
{
    removeEventListenerIndex(findEventListener(node, event, handler));
}

function cleanupEventListeners()
{
    var i;
    for (i = eventListeners.length; i > 0; i--)
    {
        if (eventListeners[i] != undefined)
        {
            removeEventListenerIndex(i);
        }
    }
}
AddEventListener(window, 'unload', cleanupEventListeners);

// This copyright statement applies to the following two functions,
// which are taken from MochiKit.
//
// Copyright 2505 Bob Ippolito <bob@redivi.com>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject
// to the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

function hsvToRgb(hue, saturation, value)
{
    var red;
    var green;
    var blue;
    if (value == 0.0)
    {
        red = 0;
        green = 0;
        blue = 0;
    }
    else
    {
        var i = Math.floor(hue * 6);
        var f = (hue * 6) - i;
        var p = value * (1 - saturation);
        var q = value * (1 - (saturation * f));
        var t = value * (1 - (saturation * (1 - f)));
        switch (i)
        {
            case 1: red = q; green = value; blue = p; break;
            case 2: red = p; green = value; blue = t; break;
            case 3: red = p; green = q; blue = value; break;
            case 4: red = t; green = p; blue = value; break;
            case 5: red = value; green = p; blue = q; break;
            case 6: // fall through
            case 0: red = value; green = t; blue = p; break;
        }
    }
    return {r: red, g: green, b: blue};
}

function rgbToHsv(red, green, blue)
{
    var max = Math.max(Math.max(red, green), blue);
    var min = Math.min(Math.min(red, green), blue);
    var hue;
    var saturation;
    var value = max;
    if (min == max)
    {
        hue = 0;
        saturation = 0;
    }
    else
    {
        var delta = (max - min);
        saturation = delta / max;
        if (red == max)
        {
            hue = (green - blue) / delta;
        }
        else if (green == max)
        {
            hue = 2 + ((blue - red) / delta);
        }
        else
        {
            hue = 4 + ((red - green) / delta);
        }
        hue /= 6;
        if (hue < 0)
        {
            hue += 1;
        }
        if (hue > 1)
        {
            hue -= 1;
        }
    }
    return {
        h: hue,
        s: saturation,
        v: value
    };
}

function pageCoords(node)
{
    var x = node.offsetLeft;
    var y = node.offsetTop;
    var parent = node.offsetParent;
    while (parent != null)
    {
        x += parent.offsetLeft;
        y += parent.offsetTop;
        parent = parent.offsetParent;
    }
    return {x: x, y: y};
}

var huePositionImg = document.createElement('img');
huePositionImg.galleryImg = false;
huePositionImg.width = 38;
huePositionImg.height = 11;
huePositionImg.src = HUE_SLIDER_ARROWS_LOCATION;
huePositionImg.style.position = 'absolute';

var hueSelectorImg1 = document.createElement('img');
hueSelectorImg1.galleryImg = false;
hueSelectorImg1.width = 30;
hueSelectorImg1.height = 250;
hueSelectorImg1.src = HUE_SLIDER_LOCATION;
hueSelectorImg1.style.display = 'block';

var satValImg = document.createElement('img');
satValImg.galleryImg = false;
satValImg.width = 250;
satValImg.height = 250;
satValImg.src = SAT_VAL_SQUARE_LOCATION;
satValImg.style.display = 'block';

var crossHairsImg = document.createElement('img');
crossHairsImg.galleryImg = false;
crossHairsImg.width = 13;
crossHairsImg.height = 13;
crossHairsImg.src = CROSSHAIRS_LOCATION;
crossHairsImg.style.position = 'absolute';

function makeColorSelector()
{
    var rgb, hsv;
    var changeCount=0;

    function colorChanged()
    {
      var hex = rgbToHex(rgb.r, rgb.g, rgb.b, 0);
      var hueRgb = hsvToRgb(hsv.h, 1, 1);
      var hueHex = rgbToHex(hueRgb.r, hueRgb.g, hueRgb.b, 1);
      /* Reset error state becuase a color was chosen */
      if (UI.isError===1) {
        UI.hexColorInput.toggleError(0);
        UI.swatchIdInput.toggleError(0);
      }
      /* Realtime Hook for legal color lookup */
      validColors.getColorMatch(hex);

      satValDiv.style.background = hueHex;
      crossHairs.style.left = ((hsv.v*249)-10) + 'px';
      crossHairs.style.top = parseInt(((1-hsv.s)*249)-10) + 'px'; /* parseInt fix for errors caused by  */
      huePos.style.top = parseInt((hsv.h*249)-5) + 'px';          /* non-whole value for px (1.4323232) */
    }
    function rgbChanged(X)
    {
      hsv = rgbToHsv(rgb.r, rgb.g, rgb.b);
      colorChanged(X);
    }
    function hsvChanged()
    {
      rgb = hsvToRgb(hsv.h, hsv.s, hsv.v);
      colorChanged();
    }
    var satValDiv = document.createElement('div');
    satValDiv.style.position = 'relative';
    satValDiv.style.width = '250px';
    satValDiv.style.height = '250px';
    
    var newSatValImg = fixPNG(satValImg);
    satValDiv.appendChild(newSatValImg);
    var crossHairs = crossHairsImg.cloneNode(false);
    satValDiv.appendChild(crossHairs);
    function satValDragged(x, y)
    {
      hsv.s = 1-(y/249);
      hsv.v = (x/249);
      hsvChanged();
    }
    trackDrag(satValDiv, satValDragged)
    UI.colorSelector.appendChild(satValDiv);

    var hueDiv = document.createElement('div');
    hueDiv.style.position = 'absolute';
    hueDiv.style.left = '280px';
    hueDiv.style.top = '0px';
    hueDiv.style.width = '30px';
    hueDiv.style.height = '250px';
    var huePos = fixPNG(huePositionImg);

    hueDiv.appendChild(hueSelectorImg1.cloneNode(false));
    hueDiv.appendChild(huePos);
    function hueDragged(x, y)
    {
      hsv.h = y/249;
      hsvChanged();
    }
    trackDrag(hueDiv, hueDragged);
    UI.colorSelector.appendChild(hueDiv);
        
    function hexColorInputChanged()
    {
      var input = UI.hexColorInput;
      if (input.validateHex()) {
        rgb = hexToRgb(input.value, {r: 0, g: 0, b: 0});
        rgbChanged();
      }else {
        input.toggleError("ERROR: The hex color you entered was invalid. Please choose another.");
      }
    }
    function swatchIdInputChanged()
    {
      var input = UI.swatchIdInput;
      if (validColors.isSwatch(input.value)) {
        rgb = hexToRgb(validColors.getColorFromId(input.value), {r: 0, g: 0, b: 0});
        rgbChanged();
      }else{
        input.toggleError("ERROR: The swatch ID you entered was invalid. Choose another id or color.");
      }
    }
    AddEventListener(UI.hexColorInput,       'change', hexColorInputChanged);
    AddEventListener(UI.hexColorInputButton, 'click',  hexColorInputChanged);
    AddEventListener(UI.swatchIdInput,       'change', swatchIdInputChanged);
    AddEventListener(UI.swatchIdInputButton, 'click',  swatchIdInputChanged);
    /* Force IE to respond to enter key so "onchange" event is fired. */
    if (document.all) {
      AddEventListener(UI.hexColorInput,     'keypress', checkEnter);
      AddEventListener(UI.swatchIdInput,     'keypress', checkEnter);
    }
    /* Attach methods to the inputs. */
    UI.hexColorInput.validateHex = UI.validateHex;
    UI.hexColorInput.toggleError = UI.toggleError;
    UI.swatchIdInput.toggleError = UI.toggleError;
    
    hexColorInputChanged();
}

/*
    CODE BELOW ADDED BY JONATHAN CROSS 2008-04-22
    http://www.JonathanCross.com
    All code is subject to the above licenses.
*/
function UserInterface() {
  var Super = UserInterface.prototype;
  this.isError=0;
  this.init = function() {
    /* Browser check */
    var isIEMac = ((navigator.userAgent.indexOf("Mac") != -1) && document.all);
    var isDOM = document.createElement && document.getElementsByTagName;
    if (! isDOM || isIEMac) return false;
    /* Now we can continue for IE6+, Firefox, Safari and modern Opera. */
    Super.isDebug=0;
    var pieces=new Array('R','G','B','hexColorInput','hexColorInputButton','colorSelector','matchedColor','swatchIdInput','swatchIdInputButton','status');
    for (var i=0;i<pieces.length;i++) {
      var obj=pieces[i];
      eval("Super."+obj+" = document.getElementById(obj);");
      eval("Super."+obj+".parentClass = Super;");
    }
    Super.status.innerHTML="Loading...";
    if (Super.isDebug) {
      Super.debug = document.getElementById('debug');
      Super.debug.style.visibility="visible";
      Super.debug.innetHTML="Debug Mode.";
    }
    /* Prepare the RGB displays for fast DOM nodeValue insertion. */
    Super.R.appendChild(document.createTextNode(''));
    Super.G.appendChild(document.createTextNode(''));
    Super.B.appendChild(document.createTextNode(''));
    if (!document.all) {
      Super.hexColorInputButton.style.height="15px";
      Super.swatchIdInputButton.style.height="15px";
    }else{
      /* Adding a href allows CSS :hover effect to work in IE6 */
      Super.hexColorInputButton.setAttribute("href","javascript:;");
      Super.swatchIdInputButton.setAttribute("href","javascript:;");
    }
    /* This inserts a BR after last button to fix crazy float clearing problem in IE6.  There is no insertAfter() DOM method. */
    Super.swatchIdInputButton.parentNode.insertBefore(document.createElement("br"),Super.swatchIdInputButton.nextSibling);
    /* Get the color database */
    loadColors();
  }
  /* Call-back function indicating database download is complete. */
  this.dataLoaded = function() {
    this.setTime(0);
    this.setStatus((validColors.length)+" colors loaded in "+this.getTime()+" seconds.");
    makeColorSelector();
    Super.colorSelector.style.visibility="visible";
    setTimeout("UI.setStatus('&nbsp;')",8000);
  }
  /* Status message */
  Super.setStatus = function(message) {
    Super.status.innerHTML=message;
  }
  /* Timer timestamp */
  this.setTime = function(start) {
    var d = new Date();
    var t = d.getTime()
    if (start) this.startLoad = t;
    else this.endLoad = t;
  }
  /* Timer */
  this.getTime = function() {
    return (this.endLoad - this.startLoad)/1000;
  }
  /* Force uppercase letters for hex value input and validate against RE */
  this.validateHex = function() {
    this.value = this.value.toUpperCase();
    return this.value.match(HEX_RE);
  }
  /* swatchIdInput error state handeling */
  this.toggleError = function(message) {
    if (message) {
      this.className="error";
      UI.status.className="error";
      UI.setStatus(message);
      UI.isError=1;
    }else{
      this.className=undefined;
      UI.status.className=undefined;
      UI.setStatus('&nbsp;');
      UI.isError=0;
    }
  }
  /* Update UI components with new color */
  this.update = function(color) {
    var match = validColors.getColor(color);
    Super.matchedColor.style.background = '#'+color;
    Super.hexColorInput.value = color;
    Super.swatchIdInput.value = match.id;
    Super.R.firstChild.nodeValue = match.rgb.r;
    Super.G.firstChild.nodeValue = match.rgb.g;
    Super.B.firstChild.nodeValue = match.rgb.b;
  }
}
function ColorHash() {
  /* Note:
     There seems to be a basic limit to the number of items javascript will allow in the constructor.
     40K is too much... 25K works fine.  If you hit the limit, simply append extra items.
  */
  this.length = 0;
  this.imageIds = new Array();
  this.items = new Array();
  for (var i = 0; i < arguments.length; i += 2) {
    if (typeof(arguments[i + 1]) != 'undefined') {
      var hex=arguments[i].toUpperCase();
      var imageId=arguments[i + 1];
      this.items[hex] = new ValidColor(hex,imageId);
      this.imageIds[imageId] = hex;
      if (i%9000==0) UI.status.firstChild.nodeValue+=".";
      this.length++;
    }
  }
  /* Returns a validColor object from a given hex color */
  this.getColor = function(hex) {
    return this.items[hex];
  }
  /* Returns a hex color from a given swatch ID */
  this.getColorFromId = function(id) {
    return this.imageIds[id];
  }
  /* Send it a hex value to see if there is a swatch */
  this.isColor = function(hex) {
    return typeof(this.items[hex]) != 'undefined';
  }
  /* Send it a swatch id number to see if there is a color */
  this.isSwatch = function(id) {
    return typeof(this.imageIds[id]) != 'undefined';
  }
  /* Get closest rounded hex value from database. */
  this.getColorMatch = function(hex) {
    if (! validColors.isColor(hex)) {
      hex=this.roundHex(hex);
    }
    UI.update(hex);
    return hex;
  }
  /* return a the "valid hex value" which is closest to hexIn */
  this.roundHex = function(hexIn) {
    var rgb=hexToRgb(hexIn,null,1);
    var R=this.roundRGB(rgb.r,RED_STEP);
    var G=this.roundRGB(rgb.g,GREEN_STEP);
    var B=this.roundRGB(rgb.b,BLUE_STEP); 
    var hex=rgbToHex(R,G,B,0,1);
    return hex;
  }
  /* Round to the nearest legal RGB value from a hex number */
  this.roundRGB = function(val,step) {
    var half=Math.round(step/2);
    var remainder=(val % step);
    if (remainder===0) {
      return val;
    }else{
      return (remainder>half)?Math.min(val+(step-remainder),RGB_MAX):Math.max(val-remainder,0);
    }
  }
}
/* Load external color hash. */
function loadColors() {
  UI.setTime(1);
  var head = document.getElementsByTagName('head')[0];
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = DATA_FILE;
  head.appendChild(script);
}
/* ValidColor class */
function ValidColor(hex,id) {
  this.rgb = hexToRgb(hex,null,1);
  this.id = id;
}
/* IE 6 enter key support */
function checkEnter(e){
  if(e.keyCode == 13) {
    if (typeof(e.target)=='undefined') {
      e.target = e.srcElement;
    }
    e.target.blur();
    return false;
  }else{
    return true;
  }
}
/* Initialize the UI object */
var UI = new UserInterface();
AddEventListener(window, 'load',  UI.init);



