
// shorter notation ****************************************************************************************

function gfSetAttributes(object, attributes)
{
  for (attribute in attributes)
  {
    object[attribute] = attributes[attribute];
  }
}

function gfAlertAttributes(object)
{
    var s;
    for (i in object)
    {
        s += i + "=" + object[i] + "; ";
    }
    alert(s);
}

// unique identifier generation ****************************************************************************************

var gfGenerateId_value = 0;
function gfGenerateId()
{
    return "id_" + (++gfGenerateId_value);
}

// loading ****************************************************************************************
 
// loads page 'url' and calls 'callbackDone(newdoc)' when done or 'callbackFailed()' 
// when failed after 'timeout' seconds
function gfLoadPage(url, callbackDone, callbackFailed, timeout)
{
    var div = document.createElement('div');
    var iframe = document.createElement('iframe');
    var id = gfGenerateId();
    
    gfSetAttributes(div, {id:id, callbackDone:callbackDone, callbackFailed:callbackFailed, timeout:timeout, timePassed:0 });
    gfSetAttributes(div.style, {position:'absolute', top:'0px', left: '0px'});
    gfSetAttributes(iframe, {src:url, width:0, height:0 });
    
    div.appendChild(iframe);
    document.body.appendChild(div);

    setTimeout("gfLoadPageChecker('"+id+"')", 100);
}
function gfLoadPageChecker(id)
{
    var div = gfGet(id);
    var iframe = div.firstChild;
    var doc = gfGetIFrameDocument(iframe);
        
    if (doc)
    {
        div.callbackDone(doc);
        document.body.removeChild(div);
    }
    else
        {
        div.timePassed += 0.1;
        if (div.timePassed < div.timeout)
            setTimeout("gfLoadPageChecker('"+id+"')", 100);
        else
        {
            div.callbackFailed();
            document.body.removeChild(div);
        }
    }
}

// time ****************************************************************************************

gfMonthIndex2Text_values = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
function gfMonthIndex2Text(index)
{
    return gfMonthIndex2Text_values[index-1];
}

// scheduling functions ****************************************************************************************

var gfSchedule_values = new Array();
// schedules 'func' to be executed in 'delay' seconds
function gfSchedule(func, delay)
{
    var id = gfGenerateId();
    gfSchedule_values[id] = func;
    setTimeout("gfScheduleHelper('"+id+"')", delay*1000);
}
function gfScheduleHelper(id)
{
    // execute scheduled function
    gfSchedule_values[id]();
    // remove function
    gfSchedule_values[id] = void 0;
}

// time ****************************************************************************************

function gfGetTimeStamp()
{
    return new Date().getTime();
}
// my own concept: the part of the total duration that has passed; a number in [0..1]
function gfGetDurationAlpha(timeStamp, duration)
{
    return ( (gfGetTimeStamp() - timeStamp) /1000) /  duration;
}

// math ****************************************************************************************

function gfConstrain(value, firstValue, secondValue)
{
    if (firstValue < secondValue)
    {
        if (value < firstValue) return firstValue;
        if (value > secondValue) return secondValue;
    }
    else
    {
        if (value < secondValue) return secondValue;
        if (value > firstValue) return firstValue;
    }
    return value;
}

function gfGetRandom(begin, end)
{
    return begin + Math.random() * (end - begin);
}

function gfGetAngleFromDeltaXY(dx, dy)
{
    var angle;
    
    if (dx == 0)
    {
        if (dy < 0) angle = -90; else angle = 90;
    }
    else    
    {
        angle = rad2degrees * Math.atan(dy / dx);
        if (dx < 0) angle += 180;
    }
    
    return angle;
}

// graphics ****************************************************************************************

var deg2radians = Math.PI * 2 / 360;
var rad2degrees = 360 / (Math.PI * 2);

function gfCenter(element, container)
{
    // todo: check!
    if (!container) container = document.body;

    var x = (gfGetBoxWidth(container) - gfGetBoxWidth(element)) / 2;
    var y = (gfGetBoxHeight(container) - gfGetBoxHeight(element)) / 2;
    gfSetBorderPos(element, x, y);
}

// returns the element's matrix filter. Creates one if necessary.
function gfGetMatrixFilter(element)
{
    var filter = element.filters["DXImageTransform.Microsoft.Matrix"];
    if (!filter) filter = element.style.filter = "progid:DXImageTransform.Microsoft.Matrix(FilterType='bilinear',SizingMethod='auto expand')";
    // FilterType='nearest neighbor' SizingMethod='clip to original'

    return filter;
}

// rotates element degrees around its center X axis
// element should have fixed dimensions (width=; height=); and it's containing element too.
function gfRotateX(element, degrees)
{
    if (isIE55()) gfRotateX = 
        function(element, degrees)
        {
            var rad = degrees * deg2radians ;
            var filter = gfGetMatrixFilter(element);
            var costheta = Math.cos(rad);
            filter.M22 = costheta;

            gfCenter(element, element.parentNode);
        }
    else return false;
    
    gfRotateX(element, degrees);
}
// rotates element degrees around its center X axis
function gfRotateY(element, degrees)
{
    if (isIE55()) gfRotateY = 
        function(element, degrees)
        {
            var rad = degrees * deg2radians ;
            var filter = gfGetMatrixFilter(element);
            var costheta = Math.cos(rad);
            filter.M11 = costheta;
            
            gfCenter(element, element.parentNode);
        }
    else return false;
    
    gfRotateY(element, degrees);
}
// rotates element degrees around its center Z axis
function gfRotateZ(element, degrees)
{
    if (isIE55()) gfRotateZ = 
        function(element, degrees)
        {
            var rad = degrees * deg2radians;
            var filter = gfGetMatrixFilter(element);
            var costheta = Math.cos(rad);
            var sintheta = Math.sin(rad);
            
            filter.M11 = costheta;
            filter.M12 = -sintheta;
            filter.M21 = sintheta;
            filter.M22 = costheta;
        
            gfCenter(element, element.parentNode);
        }
    else return false;
    
    gfRotateZ(element, degrees);
}

// drag and drop ****************************************************************************************

var gfDragElement;
var gfOldOnmousemove;
var gfOldOnmouseup;
var gfPrevMouseX;
var gfPrevMouseY;
var gfOldZIndex;

function gfStartDrag(evt)
{
    // reroute messages
    gfOldOnmousemove = document.onmousemove;
    gfOldOnmouseup = document.onmouseup;
    document.onmousemove = gfDrag;
    document.onmouseup = gfDrop;

    // store drag parameters
    gfPrevMouseX = gfGetMouseX(evt);
    gfPrevMouseY = gfGetMouseY(evt);
    
    evt = (evt) ? evt : window.event;

    gfDragElement = ((evt.srcElement) ? evt.srcElement : evt.target);

    // lift element
    gfOldZIndex = gfDragElement.style.zIndex;
    gfDragElement.style.zIndex = 10000;
}
function gfDrag(evt)
{
    var mouseX = gfGetMouseX(evt);
    var mouseY = gfGetMouseY(evt);
    
    var currentX = gfGetPositionedLeft(gfDragElement) + (mouseX - gfPrevMouseX);
    var currentY = gfGetPositionedTop(gfDragElement) + (mouseY - gfPrevMouseY);
    gfSetBorderPos(gfDragElement, currentX, currentY);
    
    gfPrevMouseX = mouseX;
    gfPrevMouseY = mouseY;
    
    evt = (evt) ? evt : event;
    evt.cancelBubble = true;
    //evt.returnValue = false; // Opera?
    //if (evt.preventDefault)    evt.preventDefault();
    //if (evt.stopPropagation) preventDefaultAction();
    return false;
}
function gfDrop()
{
    document.onmousemove = gfOldOnmousemove;
    document.onmouseup = gfOldOnmouseup;
    
    // drop to previous level
	gfDragElement.style.zIndex = gfOldZIndex;
}