// Delay

function delay(id, f, t)
{
 if (delay.delays[id]) clearTimeout(delay.delays[id]);
 delay.delays[id] = setTimeout(function() { delete delay.delays[id]; f(); }, t);
}

delay.cancel = function(id)
{
 if (delay.delays[id])
 {
  clearTimeout(delay.delays[id]);
  delete delay.delays[id];
 }
}

delay.delays = {};

// Fades

var fade_time_quantum = 10;

function interpolate(from, to, position)
{
 if (position <= 0) return from;
 else if (position >= 1) return to;
 else return from + (to - from) * (Math.sin(Math.PI * position / 2));
 // linear: from + (to - from) * position
}

// Opacity

function get_opacity(element)
{
 var opacity = 1;
 if (typeof document.body.style.opacity == "string")
  opacity = parseFloat(element.style.opacity);
 else if (typeof document.body.filters == "object" && navigator.appVersion.match(/MSIE ([\d.]+);/)[1]>=5.5)
 {
  var filter = element.filters["DXImageTransform.Microsoft.alpha"] || element.filters.alpha;
  opacity = filter ? parseInt(filter.opacity) / 100 : 1;
 }
 return isNaN(opacity) ? 1 : opacity;
}

function set_opacity(element, opacity)
{
 var result = false;
 if (typeof document.body.style.opacity == "string")
 {
  element.style.opacity = opacity;
  result = true;
 }
 else if (typeof document.body.filters == "object" && navigator.appVersion.match(/MSIE ([\d.]+);/)[1]>=5.5)
 {
  opacity = Math.round(opacity * 100);
  var filter = element.filters["DXImageTransform.Microsoft.alpha"] || element.filters.alpha;
  if (filter) filter.opacity = opacity;
  else element.style.filter += "progid:DXImageTransform.Microsoft.Alpha(opacity=" + opacity + ")";
  result = true;
 }
 return result;
}

function fade_opacity(element, to, time)
{
 if (!element.id)
  element.id = "ID" + Math.ceil(Math.random() * 1000000000);
 if (fade_opacity.fades[element.id])
  clearInterval(fade_opacity.fades[element.id]);
 var from = get_opacity(element),
  date = new Date(), start = date.getTime(),
  oncomplete = arguments.length > 3 ? arguments[3] : null;
 fade_opacity.fades[element.id] = setInterval(
  function()
  {
   var date = new Date(), dt = date.getTime() - start, opacity;
   if (dt < time)
    opacity = Math.round(interpolate(from, to, dt / time) * 100) / 100;
   else
   {
    opacity = to;
    clearInterval(fade_opacity.fades[element.id]);
    delete fade_opacity.fades[element.id];
    if (oncomplete) oncomplete(element);
   }
   set_opacity(element, opacity);
  },
  fade_time_quantum
 )
}

fade_opacity.stop = function(element)
{
 if (fade_opacity.fades[element.id])
 {
  clearInterval(fade_opacity.fades[element.id]);
  delete fade_opacity.fades[element.id];
 }
 if (arguments.length > 1)
  set_opacity(element, arguments[1]);
}

fade_opacity.fades = {};

// Position

function get_position(element)
{
 return { left: element.offsetLeft, top: element.offsetTop }
}

function set_position(element, left, top)
{
 if (typeof left == "number") element.style.left = left + "px";
 else if (typeof left == "string") element.style.left = left;
 if (typeof top == "number") element.style.top = top + "px";
 else if (typeof top == "string") element.style.top = top;
}

function fade_position(element, to_left, to_top, time)
{
 if (!element.id)
  element.id = "ID" + Math.ceil(Math.random() * 1000000000);
 if (fade_position.fades[element.id])
  clearInterval(fade_position.fades[element.id]);
 var from = get_position(element),
  date = new Date(), start = date.getTime(),
  oncomplete = arguments.length > 4 ? arguments[4] : null;
 fade_position.fades[element.id] = setInterval(
  function()
  {
   var date = new Date(), dt = date.getTime() - start, left, top;
   left = typeof to_left == "number" ? Math.round(interpolate(from.left, to_left, dt / time)) : false;
   top = typeof to_top == "number" ? Math.round(interpolate(from.top, to_top, dt / time)) : false;
   if (dt >= time)
   {
    clearInterval(fade_position.fades[element.id]);
    delete fade_position.fades[element.id];
    if (oncomplete) oncomplete(element);
   }
   set_position(element, left, top);
  },
  fade_time_quantum
 )
}

fade_position.stop = function(element)
{
 if (fade_position.fades[element.id])
 {
  clearInterval(fade_position.fades[element.id]);
  delete fade_position.fades[element.id];
 }
 if (arguments.length > 2)
  set_position(element, arguments[1], arguments[2]);
}

fade_position.fades = {};

// Size

function get_size(element)
{
 return { width: element.offsetWidth, height: element.offsetHeight }
}

function set_size(element, width, height)
{
 if (typeof width == "number") element.style.width = width + "px";
 else if (typeof width == "string") element.style.width = width;
 if (typeof height == "number") element.style.height = height + "px";
 else if (typeof height == "string") element.style.height = height;
}

function fade_size(element, to_width, to_height, time)
{
 if (!element.id)
  element.id = "ID" + Math.ceil(Math.random() * 1000000000);
 if (fade_size.fades[element.id])
  clearInterval(fade_size.fades[element.id]);
 var from = get_size(element),
  date = new Date(), start = date.getTime(),
  oncomplete = arguments.length > 4 ? arguments[4] : null;
 fade_size.fades[element.id] = setInterval(
  function()
  {
   var date = new Date(), dt = date.getTime() - start, width, height;
   width = typeof to_width == "number" ? Math.round(interpolate(from.width, to_width, dt / time)) : false;
   height = typeof to_height == "number" ? Math.round(interpolate(from.height, to_height, dt / time)) : false;
   if (dt >= time)
   {
    clearInterval(fade_size.fades[element.id]);
    delete fade_size.fades[element.id];
    if (oncomplete) oncomplete(element);
   }
   set_size(element, width, height);
  },
  fade_time_quantum
 )
}

fade_size.stop = function(element)
{
 if (fade_size.fades[element.id])
 {
  clearInterval(fade_size.fades[element.id]);
  delete fade_size.fades[element.id];
 }
 if (arguments.length > 2)
  set_size(element, arguments[1], arguments[2]);
}

fade_size.fades = {};

// Scroll

function get_scroll(element)
{
 return { left: element.scrollLeft, top: element.scrollTop }
}

function set_scroll(element, left, top)
{
 if (typeof left == "number") element.scrollLeft = left;
 if (typeof top == "number") element.scrollTop = top;
}

function fade_scroll(element, to_left, to_top, time)
{
 if (!element.id)
  element.id = "ID" + Math.ceil(Math.random() * 1000000000);
 if (fade_scroll.fades[element.id])
  clearInterval(fade_scroll.fades[element.id]);
 var from = get_scroll(element),
  date = new Date(), start = date.getTime(),
  oncomplete = arguments.length > 4 ? arguments[4] : null;
 fade_scroll.fades[element.id] = setInterval(
  function()
  {
   var date = new Date(), dt = date.getTime() - start, left, top;
   left = typeof to_left == "number" ? Math.round(interpolate(from.left, to_left, dt / time)) : false;
   top = typeof to_top == "number" ? Math.round(interpolate(from.top, to_top, dt / time)) : false;
   if (dt >= time)
   {
    clearInterval(fade_scroll.fades[element.id]);
    delete fade_scroll.fades[element.id];
    if (oncomplete) oncomplete(element);
   }
   set_scroll(element, left, top);
  },
  fade_time_quantum
 )
}

fade_scroll.stop = function(element)
{
 if (fade_scroll.fades[element.id])
 {
  clearInterval(fade_scroll.fades[element.id]);
  delete fade_scroll.fades[element.id];
 }
 if (arguments.length > 2)
  set_scroll(element, arguments[1], arguments[2]);
}

fade_scroll.fades = {};

// Clip

function get_clip(element)
{
 var c = element.style.clip,
  result = { top: 0, right: element.offsetWidth, bottom: element.offsetHeight, left: 0 };
 if (c)
 {
  var s = c.indexOf("(") + 1, e = c.indexOf(")");
  if (s > 0 && e > s)
  {
   c = c.substring(s, e);
   c = c.split(", ");
   if (c.length == 4)
   {
    for (var i = 0; i < 4; i++)
    {
     c[i] = parseInt(c[i]);
     if (isNaN(c[i])) c[i] = 0;
    }
    result = { top: c[0], right: c[1], bottom: c[2], left: c[3] };
   }
  }
 }
 return result;
}

function set_clip(element, top, right, bottom, left)
{
 element.style.clip = "rect(" + (top ? top + "px" : "auto") + ", " + (right ? right + "px" : "auto") + ", " + (bottom ? bottom + "px" : "auto") + ", " + (left ? left + "px" : "auto") + ")";
}

function fade_clip(element, to_top, to_right, to_bottom, to_left, time)
{
 if (!element.id)
  element.id = "ID" + Math.ceil(Math.random() * 1000000000);
 if (fade_clip.fades[element.id])
  clearInterval(fade_clip.fades[element.id]);
 var from = get_clip(element),
  date = new Date(), start = date.getTime(),
  oncomplete = arguments.length > 6 ? arguments[6] : null;
 fade_clip.fades[element.id] = setInterval(
  function()
  {
   var date = new Date(), dt = date.getTime() - start, top, right, bottom, left;
   top = typeof to_top == "number" ? Math.round(interpolate(from.top, to_top, dt / time)) : false;
   right = typeof to_right == "number" ? Math.round(interpolate(from.right, to_right, dt / time)) : false;
   bottom = typeof to_bottom == "number" ? Math.round(interpolate(from.bottom, to_bottom, dt / time)) : false;
   left = typeof to_left == "number" ? Math.round(interpolate(from.left, to_left, dt / time)) : false;
   if (dt >= time)
   {
    clearInterval(fade_clip.fades[element.id]);
    delete fade_clip.fades[element.id];
    if (oncomplete) oncomplete(element);
   }
   set_clip(element, top, right, bottom, left);
  },
  fade_time_quantum
 )
}

fade_clip.stop = function(element)
{
 if (fade_clip.fades[element.id])
 {
  clearInterval(fade_clip.fades[element.id]);
  delete fade_clip.fades[element.id];
 }
 if (arguments.length > 2)
  set_clip(element, arguments[1], arguments[2], arguments[3], arguments[4]);
}

fade_clip.fades = {};

// Window

function get_window_size()
{
 var width, height;
 if (self.innerHeight)
 {
  width = self.innerWidth;
  height = self.innerHeight;
 } else
 if (document.documentElement && document.documentElement.clientHeight)
 {
  width = document.documentElement.clientWidth;
  height = document.documentElement.clientHeight;
 } else
 if (document.body)
 {
  width = document.body.clientWidth;
  height = document.body.clientHeight;
 }
 return { width: width, height: height };
}

function get_window_scroll()
{
 var left, top;
 if (document.documentElement && document.documentElement.scrollTop)
 {
  left = document.documentElement.scrollLeft;
  top = document.documentElement.scrollTop;
 } else
 if (document.body)
 {
  left = document.body.scrollLeft;
  top = document.body.scrollTop;
 }
 return { left: left, top: top };
}

function center_window(element)
{
 var window_size = get_window_size(), window_scroll = get_window_scroll(), element_size = get_size(element);
 set_position(element,
  window_scroll.left + Math.round((window_size.width - element_size.width) / 2),
  window_scroll.top + Math.round((window_size.height - element_size.height) / 2));
}