function xHex(n, digits, prefix)
{
  var p = '', n = Math.ceil(n);
  if (prefix) p = prefix;
  n = n.toString(16);
  for (var i=0; i < digits - n.length; ++i) {
    p += '0';
  }
  return p + n;
}

function xParseColor(c)
{
  var o = {};
  if (xStr(c)) {
    if (c.indexOf('rgb')!=-1) {
      var a = c.match(/(\d*)\s*,\s*(\d*)\s*,\s*(\d*)/);
      o.r = parseInt(a[1]) || 0;
      o.g = parseInt(a[2]) || 0;
      o.b = parseInt(a[3]) || 0;
      o.n = (o.r << 16) | (o.g << 8) | o.b;
    }
    else {
      pn(parseInt(c.substr(1), 16));
    }
  }
  else {
    pn(c);
  }
  o.s = xHex(o.n, 6, '#');
  return o;
  function pn(n) { // parse num
    o.n = n || 0;
    o.r = (o.n & 0xFF0000) >> 16;
    o.g = (o.n & 0xFF00) >> 8;
    o.b = o.n & 0xFF;
  }
}

function xRgbToHex(r, g, b)
{
  return xHex((r << 16) | (g << 8) | b, 6, '#');
}

function xAniRgb(e, p, c, t, a, oe)
{
  if (!(e=xGetElementById(e))) return;
  var c0 = xParseColor(xGetComputedStyle(e, p)); // start colors
  p = xCamelize(p);
  c = xParseColor(c); // target colors
  var d = { r: c.r - c0.r, g: c.g - c0.g, b: c.b - c0.b }; // color displacements
  var fq = 1 / t; // frequency
  if (a) fq *= (Math.PI / 2);
  var t0 = new Date().getTime(); // start time
  var tmr = setInterval(
    function() {
      var et = new Date().getTime() - t0; // elapsed time
      if (et < t) {
        var f = et * fq; // constant velocity
        if (a == 1) f = Math.sin(f); // sine acceleration
        else if (a == 2) f = 1 - Math.cos(f); // cosine acceleration
        f = Math.abs(f);
        e.style[p] = xRgbToHex( // instantaneous colors
          Math.round(f * d.r + c0.r),
          Math.round(f * d.g + c0.g),
          Math.round(f * d.b + c0.b));
      }
      else {
        clearInterval(tmr); // stop iterations
        e.style[p] = c.s; // target color
        if (typeof oe == 'function') oe(); // 'onEnd' handler
        else if (typeof oe == 'string') eval(oe);
      }
    }, 10 // timer interval
  );
}
