if( typeof( Form ) == 'undefined' ) { Form = {}; }
if( typeof( Form.Element ) == 'undefined' ) { Form.Element = {}; }

// class constructor
Form.Element.Resize = function( oProps )
{
    // class properties
    this.props = {};

    // defaults
    this.props.elementId = null; // mandatory!
    this.props.containerId = null;
    this.props.containerClass = 'resizable';
    this.props.startX = 0;
    this.props.startY = 0;
    this.props.minWidth = 1;
    this.props.minHeight = 1;
    this.props.maxWidth = 0;
    this.props.maxHeight = 0;
    this.props.stopResize = 1;
    this.props.resizeType = 'auto';

    // directions/corners resize activation flags
    this.props.resizeFlags = {
        n: 0,
        ne: 0,
        e: 0,
        se: 0,
        s: 0,
        sw: 0,
        w: 0,
        nw: 0
    };

    // overwrite properties with user defined ones
    for( var cProp in this.props )
    {
        if( oProps[cProp] != undefined )
        {
            this.props[cProp] = oProps[cProp];
        }
    }

    // DOM stuff
    this.dom = {};

    this._init();
}

// version
Form.Element.Resize.VERSION = '0.05';

// class methods
Form.Element.Resize.prototype = {

// _init: generates the table and puts the element in the middle cell
_init: function()
{
    var oFER = this;

    // element object
    var oElement = document.getElementById( this.props.elementId );

    var oElementCloned = oElement.cloneNode(true);

    // YAIEH-1 (Yet Another Internet Exploder Hack)
    // the selected index is not preserved in the cloned node
    // see: http://support.microsoft.com/kb/829907
    if( oElement.type == 'select-one' || oElement.type == 'select-multiple' )
    {
        oElementCloned.selectedIndex = oElement.selectedIndex;
    }

    this.dom.elementEl = oElementCloned;

    // set resize flags
    switch( this.props.resizeType )
    {
        // automatic depending on field type
        case 'auto':

            if( oElementCloned.type == 'text' || oElementCloned.type == 'select-one' )
            {
                this.props.resizeFlags['e'] = 1;
            }
            else if( oElementCloned.type == 'textarea' || oElementCloned.type == 'select-multiple' )
            {
                with( this.props.resizeFlags )
                {
                    e = 1;
                    se = 1;
                    s = 1;
                }
            }

            break;

        case 'horizontal':

            this.props.resizeFlags['e'] = 1;

            break;

        case 'vertical':

            this.props.resizeFlags['s'] = 1;

            break;

        default:

            var oCustomFlags = this.props.resizeType;

            if( typeof( oCustomFlags ) == 'object' )
            {
                for( var cFlag in this.props.resizeFlags )
                {
                    if( oCustomFlags[cFlag] != undefined )
                    {
                        this.props.resizeFlags[cFlag] = oCustomFlags[cFlag];
                    }
                }
            }
    }

    // original dimensions
    this.dom.elementOriginalWidth = oElement.offsetWidth;
    this.dom.elementOriginalHeight = oElement.offsetHeight;

    // event handling
    // double click inside the element restores its original dimensions
    this._addEvent(
        oElementCloned,
        'dblclick',
        function() { oFER._reset(); }
    );

    // mouse move while left button is pressed resizes the element
    // if shift key is pressed the resize is proportional (x = y)
    this._addEvent(
        document,
        'mousemove',
        function( e ) { oFER._resize( e ); }
    );

    // mouse up stop resize
    this._addEvent(
        document,
        'mouseup',
        function() { oFER.props.stopResize = 1; }
    );

    // create the <table> container element
    var oElementContainer = document.createElement( 'table' );

    // YAIEH-2 (Yet Another Internet Exploder Hack)
    // without this the table will not be shown
    // see: http://cf-bill.blogspot.com/2006/02/dom-gotchas.html
    var oTbody = document.createElement( 'tbody' );

    var aCursors = [
        [ 'nw','n','ne' ],
        [ 'w',null,'e' ],
        [ 'sw','s','se' ]
    ];

    for( var r = 0; r < 3; r++ )
    {
        var oTr = document.createElement( 'tr' );

        for( var c = 0; c < 3; c++ )
        {
            var oTd = document.createElement( 'td' );
            
            var cResizeType = aCursors[r][c];

            if( this.props.resizeFlags[cResizeType] )
            {
                oTd.className = cResizeType;
                oTd.style.cursor = cResizeType + '-resize';

                this._addEvent(
                    oTd,
                    'mousedown',
                    function( e ) { oFER._storeStartCoords( e ); }
                );
            }

            if( r == 1 && c == 1 )
            {
                oTd.appendChild( oElementCloned );
            }

            oTr.appendChild( oTd );
        }

        oTbody.appendChild( oTr );
    }

    oElementContainer.appendChild( oTbody );

    // set id, class attributes on container element if needed
    if( this.props.containerId )
    {
        oElementContainer.id = this.props.containerId;
    }

    if( this.props.containerClass )
    {
        oElementContainer.className = this.props.containerClass;
    }

    this.dom.containerEl = oElementContainer;

    oElement.parentNode.replaceChild( oElementContainer, oElement );
},

// _storeStartCoords: called when the mousedown event is fired on a border/corner stores the coordinates of the mouse pointer
_storeStartCoords: function( e )
{
    if( ! e && window.event )
    {
        e = window.event;
    }

    this.props.startX = e.clientX;
    this.props.startY = e.clientY;

    // store also original dimensions if not initialized
    // (it seems that in the _init() the offsetWidth / offsetHeight are 0, maybe because the document is not fully loaded?!)
    if( ! this.dom.elementOriginalWidth || ! this.dom.elementOriginalHeight )
    {
        this.dom.elementOriginalWidth = this.dom.elementEl.offsetWidth;
        this.dom.elementOriginalHeight = this.dom.elementEl.offsetHeight;
    }

    this.dom.elementCurrentWidth = this.dom.elementEl.offsetWidth;
    this.dom.elementCurrentHeight = this.dom.elementEl.offsetHeight;

    var oTd = ( e.target ) ? e.target
                           : ( e.srcElement ) ? e.srcElement
                                              : undefined;

    // this is for a Safari bug (see w3cschools)
    if( oTd && oTd.type == 3 )
    {
        oTd = oTd.parentNode;
    }

    this.props.currentResizeType = oTd.className;
    this.props.stopResize = 0;
},

// _resize: called when the mouse is moved, reads the pointer position and resizes the table and element dimensions depending on the start coordinates
_resize: function( e )
{
    // left mouse button must be pressed!
    if( this.props.stopResize ) { return true; }

    if( ! e && window.event )
    {
        e = window.event;
    }

    var lVResize = ( this.props.currentResizeType.match( /[n|s]/ ) ) ? 1 : 0;
    var lHResize = ( this.props.currentResizeType.match( /[e|w]/ ) ) ? 1 : 0;

    var nWidth = this.dom.elementCurrentWidth + ( e.clientX - this.props.startX );

    // proportional resize with SHIFT key pressed and both horizontal and vertical resize requested
    var nHeight = ( e.shiftKey && lVResize && lHResize )
                ? nWidth
                : this.dom.elementCurrentHeight + ( e.clientY - this.props.startY );

    // check if max dimensions are specified
    if( this.props.maxWidth > 0 && nWidth > this.props.maxWidth )
    {
        lHResize = 0;
    }

    if( this.props.maxHeight > 0 && nHeight > this.props.maxHeight )
    {
        lVResize = 0;
    }

    with( this.dom.containerEl.style )
    {
        if( lHResize && nWidth >= this.props.minWidth ) { width = nWidth + 'px'; }
        if( lVResize && nHeight >= this.props.minHeight ) { height = nHeight + 'px'; }
    }

    with( this.dom.elementEl.style )
    {
        if( lHResize && nWidth >= this.props.minWidth ) { width = nWidth + 'px'; }
        if( lVResize && nHeight >= this.props.minHeight ) { height = nHeight + 'px'; }
    }
},

// _reset: called when a double click event is fired, restores the original dimensions of the element
_reset: function()
{
    if( ! this.dom.elementOriginalWidth && ! this.dom.elementOriginalHeight ) { return true; }

    with( this.dom.containerEl.style )
    {
        width = this.dom.elementOriginalWidth + 'px';
        height = this.dom.elementOriginalHeight + 'px';
    }

    with( this.dom.elementEl.style )
    {

        width = this.dom.elementOriginalWidth + 'px';
        height = this.dom.elementOriginalHeight + 'px';
    }
},

// _addEvent: cross-browser event handler
_addEvent: function( oObj, cEvent, rFunction )
{
    if( oObj.addEventListener )
    {
        oObj.addEventListener( cEvent, rFunction, false );
        return true;
    }
    else if( oObj.attachEvent )
    {
        return oObj.attachEvent( 'on' + cEvent, rFunction );
    }
    else
    {
        return false;
    }
}

};