var CFX = new function() {
var granularity = 50;
return {

getPosX: function(obj) {
var curleft = 0;
if (obj.offsetParent)
{
while (obj.offsetParent)
{
curleft += obj.offsetLeft
obj = obj.offsetParent;
}
}
else if (obj.x)
curleft += obj.x;
return curleft;
},

getPosY: function(obj) {
var curtop = 0;
if (obj.offsetParent)
{
while (obj.offsetParent)
{
curtop += obj.offsetTop
obj = obj.offsetParent;
}
}
else if (obj.y)
curtop += obj.y;
return curtop;
},

distance: function(x1, y1, x2, y2) {
return Math.sqrt( Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2) );
},

Mover: function(el, startX, startY, endX, endY, millisec, params) {
var posX = startX;
var posY = startY;
var offX = (endX - startX) / (millisec / granularity);
var offY = (endY - startY) / (millisec / granularity);
var aFrames = new Array();

if (params) {
if (params.onComplete && params.onComplete.call) {
var onComplete = params.onComplete.bind(this);
}
if (params.onCancel && params.onCancel.call) {
var onCancel = params.onCancel.bind(this);
}
if (params.onFrame && params.onFrame.call) {
var onFrame = params.onFrame.bind(this);
}
}

el.style.position = 'absolute';
el.style.left = startX + 'px';
el.style.top = startY + 'px';

this.element = function() {
return el;
}

var move = function() {
if (aFrames.length == 0) {
return;
}
if (onFrame) {
onFrame();
}
var pos = aFrames.shift();
el.style.left = Math.floor(pos.x) + 'px';
el.style.top = Math.floor(pos.y) + 'px';
if (aFrames.length == 0) {
if (onComplete) {
onComplete();
}
}
}

// Initialize the animation frames
for (var i = 0; i < Math.floor(millisec / granularity) - 1; i++) {
aFrames.push({x: posX, y: posY});
posX += offX;
posY += offY;
setTimeout(move, granularity * i);
}
// Add the last frame
aFrames.push({x: endX, y: endY});
setTimeout(move, granularity * i);

this.cancel = function() {
if (aFrames.length > 0) {
aFrames = new Array();
if (onCancel) {
onCancel();
}
}
}
},

// Fader constructor to change the opacity of elements

Fader: function(el, startOpac, endOpac, millisec, params) {
var timer = 0;
var curOpac = startOpac;
var offOpac = (endOpac - startOpac) / (millisec / granularity);
var aFrames = new Array();

this.element = function() {
return el;
}

// Handle additional params, if any
// Valid params are onComplete and onCancel functions
if (params) {
if (params.onComplete && params.onComplete.call) {
var onComplete = params.onComplete.bind(this);
}
if (params.onCancel && params.onCancel.call) {
var onCancel = params.onCancel.bind(this);
}
if (params.onFrame && params.onFrame.call) {
var onFrame = params.onFrame.bind(this);
}
}

var fade = function() {
if (aFrames.length == 0) {
return;
}
if (onFrame) {
onFrame();
}
var val = aFrames.shift();
el.opacity.set(val);
if (aFrames.length == 0) {
if (onComplete) {
onComplete();
}
}
}

// Starting opacity
el.opacity.set(startOpac);

// Initialize the animation frames
for (var i = 0; i < (millisec / granularity) - 1; i++) {
//setTimeout(this.fade, i * 100);
aFrames.push(curOpac);
curOpac += offOpac;
setTimeout(fade, granularity * i);
}
// Add the last frame
aFrames.push(endOpac);
setTimeout(fade, granularity * i);

this.cancel = function() {
if (aFrames.length > 0) {
aFrames = new Array();
if (onCancel) {
onCancel();
}
}
}
},

Animator: function(el, prop, unit, startVal, endVal, millisec, params) {
var curVal = startVal;
var offVal = (endVal - startVal) / (millisec / granularity);
var aFrames = new Array();

this.element = function() {
return el;
}

if (params) {
if (params.onComplete && params.onComplete.call) {
var onComplete = params.onComplete.bind(this);
}
if (params.onCancel && params.onCancel.call) {
var onCancel = params.onCancel.bind(this);
}
if (params.onFrame && params.onFrame.call) {
var onFrame = params.onFrame.bind(this);
}
}

// Initialize the animation frames
for (var i = 0; i < (millisec / granularity) - 1; i++) {
//setTimeout(this.fade, i * 100);
aFrames.push(curVal);
curVal += offVal;
setTimeout(animate, granularity * i);
}
// Add the last frame
aFrames.push(endVal);
setTimeout(animate, granularity * i);

var animate = function() {
if (aFrames.length == 0) {
return;
}
if (onFrame) {
onFrame();
}
var val = aFrames.shift();
el.style[prop] = val + unit;
if (aFrames.length == 0) {
if (onComplete) {
onComplete();
}
}

}

this.cancel = function() {
if (aFrames.length > 0) {
aFrames = new Array();
if (onCancel) {
onCancel();
}
}
}
}

};
}

function $CFX(i) {
if (!i.tagName) { var element = document.getElementById(i); } else { var element = i; }
$(element);
if (!element.opacity) element.opacity = {
set: function(opac) { element.style.opacity = (opac / 100); element.style.MozOpacity = (opac / 100); element.style.KhtmlOpacity = (opac / 100); element.style.filter = "alpha(opacity=" + opac + ")"; },
fade: function(startOpac, endOpac, millisec, params) { return new CFX.Fader(element, startOpac, endOpac, millisec, params); }
};
if (!element.position) element.position = {
set: function(x, y) { element.style.position = 'absolute'; element.style.left = x + 'px'; element.style.top = y + 'px'; },
getX: function() { return CFX.getPosX(element); },
getY: function() { return CFX.getPosY(element); },
move: function(sX, sY, eX, eY, millisec, params) { return new CFX.Mover(element, sX, sY, eX, eY, millisec, params); },
moveTo: function(x, y, millisec, params) { return new CFX.Mover(element, element.position.getX(), element.position.getY(), x, y, millisec, params); }
};
if (!element.animate) element.animate = function(prop, unit, startVal, endVal, millisec, params) { return new CFX.Animator(element, prop, unit, startVal, endVal, millisec, params); }
return element;
}
