// SlidingPanel 1.2.0

function SlidingPanel(fieldName, initialHeight)
{
    this.field = document.getElementById(fieldName);
    this.height = -1;
    this.callback = null;
    this.speed = 0;
    this.onExpanded = [];
    this.onCollapsed = [];

    if (initialHeight != -1)
    {
        this.height = initialHeight;
        this.field.style.display = initialHeight == 0 ? 'none' : '';
        this.field.style.height = this.height != 0 ? this.height + 'px' : '';
    }
}

SlidingPanel.prototype.getClientHeight = function(height)
{
    if (height != -1)
    {
        return height;
    }
    else
    {
        var prevDisplay = this.field.style.display;
        var prevHeight = this.field.style.height;

        this.field.style.height = '';
        this.field.style.display = '';
        var h = -1;
        if (this.field.clientHeight)
        {
            h = this.field.clientHeight;
        }
        else
        {
            h = this.field.scrollHeight;
        }
        this.field.style.display = prevDisplay;
        this.field.style.height = prevHeight;

        return h;
    }
}

SlidingPanel.prototype.setHeight = function(height)
{
    if (this.callback != null)
    {
        clearInterval(this.callback);
        this.callback = null;
    }

    // ensure we have most recent current height
    if (this.height == -1)
    {
        this.height = this.getClientHeight(this.height);
    }

    // start animation
    this.fixedHeight = height != -1;
    this.targetHeight = this.getClientHeight(height);
    this.speed = 1;
    this.callback = setInterval(Core.CreateDelegate(this, this.onCallback), 42);
}

SlidingPanel.prototype.onCallback = function()
{
    var isFinished = false;

    if (this.targetHeight < this.height)
    {
        // collapsing
        this.height -= this.speed;
        if (this.height <= this.targetHeight)
        {
            this.height = this.targetHeight;
            isFinished = true;
            for (var key in this.onCollapsed)
            {
                this.onCollapsed[key]();
            }
        }
    }
    else
    {
        // expanding
        this.height += this.speed;
        if (this.height >= this.targetHeight)
        {
            this.height = this.targetHeight;
            isFinished = true;
            for (var key in this.onExpanded)
            {
                this.onExpanded[key]();
            }
        }
    }

    if (isFinished)
    {
        clearInterval(this.callback);
        this.callback = null;
        if (!this.fixedHeight)
        {
            this.height = -1;
        }
    }

    this.field.style.display = this.height == 0 ? 'none' : '';
    this.field.style.height = this.height != -1 && this.heigth != 0 ? this.height + 'px' : '';

    // update speed
    if (Math.abs(this.targetHeight - this.height) > 20)
    {
        if (this.speed < 16)
        {
            this.speed *= 2;
        }
    }
    else
    {
        if (this.speed > 1)
        {
            this.speed /= 2;
        }
    }
}

SlidingPanel.prototype.toggle = function()
{
    this.setHeight(this.height == 0 ? -1 : 0);
}

// Flyout 1.0.0

function Flyout(contentElementId, initialSize)
{
    this.field = document.getElementById(contentElementId);
    this.size = initialSize;
    this.callback = null;

    this.field.style.display = this.size['x'] == 0 || this.size['y'] == 0 ? 'none' : '';
    this.field.style.width = this.size['x'] != -1 && this.size['x'] != 0 ? (this.size['x'] + 'px') : '';
    this.field.style.height = this.size['y'] != -1 && this.size['y'] != 0 ? (this.size['y'] + 'px') : '';
}

Flyout.prototype.getClientSize = function(size)
{
    if (size['x'] != -1 && size['y'] != -1)
    {
        return size;
    }
    else
    {
        var prevDisplay = this.field.style.display;
        var prevSize = [];
        prevSize['x'] = this.field.style.width;
        prevSize['y'] = this.field.style.height;

        this.field.style.width = size['x'] != -1 ? (size['x'] + 'px') : '';
        this.field.style.height = size['y'] != -1 ? (size['y'] + 'px') : '';
        this.field.style.display = '';

        var s = [];
        if (this.field.clientHeight)
        {
            s['x'] = this.field.clientWidth;
            s['y'] = this.field.clientHeight;
        }
        else
        {
            s['x'] = this.field.scrollWidth;
            s['y'] = this.field.scrollHeight;
        }

        this.field.style.display = prevDisplay;
        this.field.style.width = prevSize['x'];
        this.field.style.height = prevSize['y'];

        return s;
    }
}

Flyout.prototype.setSize = function(size)
{
    if (this.callback != null)
    {
        clearInterval(this.callback);
        this.callback = null;
    }

    // ensure we have most recent current size
    this.size = this.getClientSize(this.size);

    // start animation
    this.fixedWidth = size['x'] != -1;
    this.fixedHeight = size['y'] != -1;
    this.targetSize = this.getClientSize(size);
    this.speed = [];

    var dx = Math.abs(this.targetSize['x'] - this.size['x']);
    var dy = Math.abs(this.targetSize['y'] - this.size['y']);
    if (dx == 0)
    {
        this.speed['x'] = 0;
        this.speed['y'] = 1;
    }
    else if (dy == 0)
    {
        this.speed['x'] = 1;
        this.speed['y'] = 0;
    }
    else
    {
        this.speed['x'] = dx > dy ? (dx/dy) : 1;
        this.speed['y'] = dy > dx ? (dy/dx) : 1;
    }
    this.callback = setInterval(Core.CreateDelegate(this, this.onCallback), 42);
}

Flyout.prototype.onCallback = function()
{
    var isFinished = false;

    if (this.targetSize['x'] < this.size['x'] || this.targetSize['y'] < this.size['y'])
    {
        this.size['x'] = Math.max(this.size['x'] - this.speed['x'], this.targetSize['x']);
        this.size['y'] = Math.max(this.size['y'] - this.speed['y'], this.targetSize['y']);
        if (this.size['x'] <= this.targetSize['x'] && this.size['y'] <= this.targetSize['y'])
        {
            isFinished = true;
        }
    }
    else
    {
        this.size['x'] = Math.min(this.size['x'] + this.speed['x'], this.targetSize['x']);
        this.size['y'] = Math.min(this.size['y'] + this.speed['y'], this.targetSize['y']);
        if (this.size['x'] >= this.targetSize['x'] && this.size['y'] >= this.targetSize['y'])
        {
            isFinished = true;
        }
    }

    if (isFinished)
    {
        clearInterval(this.callback);
        this.callback = null;
        if (!this.fixedWidth)
        {
            this.size['x'] = -1;
        }
        if (!this.fixedHeight)
        {
            this.size['y'] = -1;
        }
    }

    this.field.style.display = this.size['x'] == 0 || this.size['y'] == 0 ? 'none' : '';
    this.field.style.width = this.size['x'] != -1 ? (this.size['x'] + 'px') : '';
    this.field.style.height = this.size['y'] != -1 ? (this.size['y'] + 'px') : '';

    // update speed
    if (Math.abs(this.targetSize['x'] - this.size['x']) > 20 || Math.abs(this.targetSize['y'] - this.size['y']) > 20)
    {
        if (this.speed['x'] < 16 || this.speed['y'] < 16)
        {
            this.speed['x'] *= 2;
            this.speed['y'] *= 2;
        }
    }
    else
    {
        if (this.speed['x'] > 1 || this.speed['y'] > 1)
        {
            this.speed['x'] /= 2;
            this.speed['y'] /= 2;
        }
    }
}


// TODO: move to separate file
function Core()
{
}

Core.CreateDelegate = function(instance, method)
{
    return function()
    {
        return method.apply(instance, arguments);
    }
}

