function Color() {
  var hexdigits = '0123456789ABCDEF';
  
  var hexify_byte = function (value) {
    return (hexdigits.charAt(Math.floor(value / 16)) +
	    hexdigits.charAt(value % 16));
  };

  var color_for_hue = function(hf) {
    hf *= 6.0;
    var i = Math.floor(hf);
    var f = hf - i;
    var r, g, b;
    switch(i) {
    case 0: r = 1.0;      g = f;        b = 0.0;      break;
    case 1: r = 1.0 - f;  g = 1.0;      b = 0.0;      break;
    case 2: r = 0.0;      g = 1.0;      b = f;        break;
    case 3: r = 0.0;      g = 1.0 - f;  b = 1.0;      break;
    case 4: r = f;        g = 0.0;      b = 1.0 ;     break;
    case 5: r = 1.0;      g = 0.0;      b = 1.0 - f;  break;
    };
    return { r:r, g:g, b:b };
  };
 
  this.hex_to_rgb = function(hex) {
    hex = hex.replace(/#/g,'');
    var rgb = {r:0,g:0,b:0};
    if(hex.length == 3) {
      var r = hex.substring(0,1);
      var g = hex.substring(1,2);
      var b = hex.substring(2,3);
      rgb.r = parseInt(r+r,16);
      rgb.g = parseInt(g+g,16);
      rgb.b = parseInt(b+b,16);
    }
    else if(hex.length == 6) {
      rgb.r = parseInt(hex.substring(0,2),16);
      rgb.g = parseInt(hex.substring(2,4),16);
      rgb.b = parseInt(hex.substring(4,6),16);
    };
    return rgb;
  };
  
  this.hsv_to_rgb = function(hue,saturation,value) {
    value = 1.0 - value;
    var color = color_for_hue(hue);
    var opacity = 1.0 - (saturation * value);
    var rgb = {
      r: Math.floor(((color.r * (1.0 - opacity)) 
		     + (value * opacity))*255.0),
      g: Math.floor(((color.g * (1.0 - opacity)) 
		     + (value * opacity))*255.0),
      b: Math.floor(((color.b * (1.0 - opacity)) 
		     + (value * opacity))*255.0)
    };
    return rgb;
  };
  
  this.rgb_to_hsv = function(r, g, b) {
    r = r/255.0; g = g/255.0; b = b/255.0;
    var hsv = {};
    var min = (r < g) ? r : g;
    if(b < min) min = b;
    var max = (r > g) ? r : g;
    if(b > max) max = b;
    hsv.v = max;
    var delta = max - min;
    if(delta == 0.0) {
      hsv.s = 0.0;
      hsv.h = 0.0;
    }
    else {
      hsv.s = delta / max;
      var delta_r = (((max - r) / 6.0) + (delta / 2.0)) / delta;
      var delta_g = (((max - g) / 6.0) + (delta / 2.0)) / delta;
      var delta_b = (((max - b) / 6.0) + (delta / 2.0)) / delta;
      if(r == max) hsv.h = delta_b - delta_g;
      if(g == max) hsv.h = (1.0/3.0) + (delta_r - delta_b);
      if(b == max) hsv.h = (2.0/3.0) + (delta_g - delta_r);
      if(hsv.h < 0.0) hsv.h += 1.0;
      if(hsv.h > 1.0) hsv.h -= 1.0;
    };
    return hsv;
  };

  this.rgb_to_hex = function(r,g,b) {
    return (hexify_byte(r) +
	    hexify_byte(g) +
	    hexify_byte(b));
  };

  this.hsv_to_hex = function(hue,saturation,value) {
    var c = this.hsv_to_rgb(hue,saturation,value);
    return this.rgb_to_hex(c.r,c.g,c.b);
  };

  this.hex_to_hsv = function(hex) {
    var c = this.hex_to_rgb(hex);
    return this.rgb_to_hsv(c.r,c.g,c.b);
  };
};

