/** This file is part of the equanda project, http://equanda.org */

/**
 * add trim() function in String class
 */
String.prototype.trim = function()
{
    return this.replace( /^\s+|\s+$/g, "" );
};

/**
 * get window height (browser independent)
 */
function equandaWindowHeight()
{
    var height = 0;
    if( typeof ( window.innerWidth ) == 'number' )
    {
        // Non-IE
        height = window.innerHeight;
    }
    else if( document.documentElement
            && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) )
    {
        // IE 6+ in 'standards compliant mode'
        height = document.documentElement.clientHeight;
    }
    else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) )
    {
        // IE 4 compatible
        height = document.body.clientHeight;
    }
    return height;
}

/**
 * get window width (browser independent)
 */
function equandaWindowWidth()
{
    var width = 0;
    if( typeof ( window.innerWidth ) == 'number' )
    {
        // Non-IE
        width = window.innerWidth;
    }
    else if( document.documentElement
            && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) )
    {
        // IE 6+ in 'standards compliant mode'
        width = document.documentElement.clientWidth;
    }
    else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) )
    {
        // IE 4 compatible
        width = document.body.clientWidth;
    }
    return width;
}

/**
 * Convert case, use as onkeyup="eqCC( this, 2 );"
 * 
 * @param element - element (usually this)
 * @param caseConversion - case conversion type. 0 for no conversion, 1 for all lower case, 2 for all upper case
 * @return text after case conversion
 */
function eqCC( element, caseConversion )
{
    if( element.value.length == 0 )
        return "";
    var start = element.selectionStart;
    var end = element.selectionEnd;
    if( caseConversion == 2 )
        element.value = element.value.toUpperCase();
    if( caseConversion == 1 )
        element.value = element.value.toLowerCase();
    if( start != null && end != null )
    {
        element.selectionStart = start;
        element.selectionEnd = end;
    }
    return element.value;
}

function eqAxSelR( linkUrlStart, linkUrlEnd, checkbox, objectId )
{
    new Ajax.Request( linkUrlStart + ( checkbox.checked ? "true" : "false" ) + "/" + objectId + linkUrlEnd, {
        method :'post',
        onFailure : function( t )
        {
            alert( 'Error communication with the server: ' + t.responseText.stripTags() );
        },
        onException : function( t, exception )
        {
            alert( 'Error communication with the server: ' + exception.stripTags() );
        },
        onSuccess : function( t )
        {

        }.bind( this )
    } );
}

/**
 * Url encode a string to ensure it can be passed to tapestry5
 * @param string
 */
function eqUrlEnc( string )
{
    string = string.replace(/\r\n/g,"\n");
    var res = "";
    for (var n = 0; n < string.length; n++)
    {
        var c = string.charCodeAt( n );
        if ( '$' == string.charAt( n ) )
        {
            res += '$$';
        }
        else if ( eqInRange( c, "AZ" ) || eqInRange( c, "az" ) || eqInRange( c, "09" ) || eqInRange( c, ".." ) )
        {
            res += string.charAt( n )
        }
        else
        {
            var tmp = c.toString(16);
            while ( tmp.length < 4 ) tmp = "0" + tmp;
            res += '$' + tmp;
        }
    }
    return res;
}

function eqInRange( code, range )
{
    return code >= range.charCodeAt( 0 ) &&  code <= range.charCodeAt( 1 );
}

/**
 * Zone update for text field
 * @param event
 * @param urlStart
 * @param urlEnd
 */
function eqTfzu( event, urlStart, urlEnd )
{
    Event.stop( event );
    var element = event.element();
    var zoneObject = Tapestry.findZoneManager(element);
    if ( !zoneObject ) return;
    var val = element.value;
    if ( '' == val ) val = ' ';
    zoneObject.updateFromURL( urlStart + eqUrlEnc( val ) + urlEnd );
}

/**
 * Switch to edit mode in the edit component.
 * Different effects are used for IE vs rest of world to avoid problems.
 * @param clientId
 * @param page
 */
function eqFFedit( clientId, page, link )
{
    if ( link ) new Ajax.Request( link );
    $( clientId + 'te' + page ).style.display = 'none';
    $( clientId + 'ts' + page ).style.display = 'block';
	if ( Prototype.Browser.IE )
    {
        Effect.Fade( $( clientId + 'show' + page ) );
        Effect.Appear( $( clientId + 'edit' + page ) );
    } else {
        Effect.SlideUp( $( clientId + 'show' + page ) );
        Effect.SlideDown( $( clientId + 'edit' + page ) );
    }
}

/**
 * Switch to show/display mode in the edit component.
 * Different effects are used for IE vs rest of world to avoid problems.
 * @param clientId
 * @param page
 */
function eqFFshow( clientId, page, link )
{
    if ( link ) new Ajax.Request( link );
    $( clientId + 'te' + page ).style.display = 'block';
    $( clientId + 'ts' + page ).style.display = 'none';
    if ( Prototype.Browser.IE )
    {
        Effect.Fade( $( clientId + 'edit' + page ) );
        Effect.Appear( $( clientId + 'show' + page ) );
    } else {
        Effect.SlideUp( $( clientId + 'edit' + page ) );
        Effect.SlideDown( $( clientId + 'show' + page ) );
    }
}

/* for accordion component */

var EQUANDA_ACCORDION_SKIP = "eqTravSkip";

/* equandaAccordion change pane */
function eqAcc( el, indx )
{
    el = $( el );
    if ( el.tagName == "A" ) el = $( el.up() );
    var accordionId = el.parentNode.id;
    if ( !el.hasClassName( accordionId + "_atit" ) )
    {
        $$( '.' + accordionId + '_atit').each( function deactivate( object )
        {
            object.next().style.display = "none";
            object.removeClassName( accordionId + '_atit' );
            object.down().removeClassName( EQUANDA_ACCORDION_SKIP );
        } );
        el.addClassName( accordionId + '_atit' );
        el.down().addClassName( EQUANDA_ACCORDION_SKIP );
        el.next().style.display = "block";
    }
    document.cookie = "active_index_" + accordionId + "=" + ( indx + 1 ) + "; path=/";
}

/* for formactionlink component */

function eqFal( url )
{
    $( 'eqFalT' ).value = url;
    $( 'eqFalT' ).form.submit();
}

/* for formtraversal component */

// assure first item in form gets focus

function eqFtFocus()
{
    var form=document.forms[0];
    if (form!=null)
    {
        var i, tr, td, elem, tbody;
        for (i=0; i<form.elements.length; i++)
        {
            elem = $( form.elements[i] );
            if (((elem.tagName=="INPUT" ) && ((elem.type=="text") || (elem.type=="checkbox"))) || (elem.tagName=="SELECT"))
            {
                if ((elem.style.display!="none") && !(elem.disabled) && (elem.className!="nodisplay"))
                {
                    td=elem.parentNode;
                    tr=td.parentNode;
                    tbody=tr.parentNode;
                    if ((td.style.display!="none") && (td.className!="displayNone") && (tr.style.display!="none") && (tr.className!="displayNone") && (tbody.style.display!="none") && (tbody.className!="displayNone"))
                    {
                        if (elem.tagName=="INPUT") elem.select();
                        else elem.focus();
                        break;
                    }
                 }
            }
        }
    }
}

// form traversal

var EQUANDA_FT_DEFAULT_SUBMIT = "defaultSubmit";
var EQUANDA_FT_VISCHECK = 25; // check so many parents to be visible
var EQUANDA_FT_SKIP = "eqTravSkip";
var EQUANDA_FT_INVOKELINK_FORWARD = "eqTravFw";
var EQUANDA_FT_INVOKELINK_BACKWARD = "eqTravBw";
var EQUANDA_FT_MOVELINK = "eqTravMove";
var EQUANDA_FT_ENTEROVERRIDE = "eqTravEnter";

function eqFtInit()
{
    document.onkeypress = eqFtKeyHandler;
}

function eqFtKeyHandler( evnt )
{
    var key;
    if (!evnt) evnt = window.event;
    if (evnt.keyCode) key = evnt.keyCode; else if (evnt.which) key = evnt.which;
    if ( key == 10 ) key = 13; // assure \n and \r are treated the same (for IE)
    var input = $( evnt.target ? evnt.target : window.event.srcElement );
    if ( key == 9 || key == 13 )
    {
        // special enter handling
        if ( key == 13 )
        {
            // ctrl-enter should invoke default submit (if any)
            if ( evnt.ctrlKey )
            {
                var submit = $$("."+EQUANDA_FT_DEFAULT_SUBMIT);
                if (submit.length>0)
                {
                    eqFtClick( submit[0] );
                    return false;
                }
            }

            // shift-enter should work as usual (needed for textarea)
            if ( evnt.shiftKey ) return true;

            // enter should work as normal when overridden
            if ( input.hasClassName( EQUANDA_FT_ENTEROVERRIDE ) ) return true;

            // enter on default submit should just submit
            if ( input.hasClassName( EQUANDA_FT_DEFAULT_SUBMIT ) )
            {
                eqFtClick( input );
                return false;
            }
        }

        var direction = 1;
        if ( key != 13 && evnt.shiftKey ) direction = -1;

        while ( 1 == 1 )
        {
            input = eqFtTraverse( input, direction );
            if ( input.hasClassName( EQUANDA_FT_SKIP ) ) continue;
            if ( direction > 0 && input.hasClassName( EQUANDA_FT_INVOKELINK_FORWARD ) ) eqFtClick( input );
            if ( direction < 0 && input.hasClassName( EQUANDA_FT_INVOKELINK_BACKWARD ) ) eqFtClick( input );
            if ( input.hasClassName( EQUANDA_FT_MOVELINK ) )
            {
                var attr = input.getAttribute( 'href' );
                if ( attr && attr.charAt(0)=='#') attr=attr.substring(1);
                input=$(attr);
            }

            if ( eqFtIsFocusable( input ) && eqFtIsVisible( input ) )
            {
                input.focus();
                return false;
            }
        }
    }
    else
    {
        if ( key == 32 ) eqFtClick( input );
    }
    return true;
}

function eqFtIsVisible( element )
{
    // need to move up the tree as getStyle doew not check parent style (which is reasonable enough)
    var depth = EQUANDA_FT_VISCHECK;
    while ( depth > 0 && element != null && element.tagName != "BODY" )
    {
        if ( element.getStyle('display')=='none' || element.hasClassName( 'nodisplay' ) ) return false;
        element = element.up();
    }
    return true;
}

function eqFtIsFocusable( element )
{
    if ( element.disabled ) return false;
    if ( element.tagName == "A" && element.href ) return true;
    if ( element.tagName == "INPUT" && element.type != "hidden" ) return true;
    if ( element.tagName == "TEXTAREA" && element.type != "hidden" ) return true;
    if ( element.tagName == "SELECT" ) return true;
    if ( element.tagName == "BUTTON" ) return true;
    return false;
}

function eqFtTraverse( element, direction )
{
    var next;
    if ( direction > 0 )
    {
        next = element.down();
        if (!next) next = element.next();
        while ( !next && element && element.tagName != "BODY" )
        {
            element = element.up();
            next = element.next();
        }
        if ( !next ) next = $$( 'body' )[0];
    }
    else
    {
        next = element.previous();
        if ( next ) next = eqFtLastChild( next );
        if ( !next ) next = element.up();
	    if ( next.tagName == "BODY" ) next = eqFtLastChild( next );
    }
    return next;
}

function eqFtLastChild( element )
{
    while ( element.descendants().length > 0 )
    {
        var desc = element.descendants();
        element = desc[ desc.length - 1 ];
    }
    return $( element );
}

function eqFtClick( element )
{
    if ( element.click )
    {
        element.click();
    }
    else if ( element.href )
    {
        if ( !element.onclick )
        {
            window.location.href = element.href;
        }
        else
        {
            if ( element.onclick() )
            {
                window.location.href = element.href;
            }
        }
    }
}

/* for jspager component */

// Object to store all pager configurations
var eqJsplC = new Object();

/**
 * Shows selected page of the paged loop component
 *
 * @param pageIdx - selected page index
 * @param config - configuration object which contains all required data for JS pager
 */
function eqShwPg( pageIdx, config )
{
    if( config == undefined )
    {
        return;
    }

    var rows = $$( "." + config.rowClass );

    var arr = $$( "." + config.containerClass );
    if( arr.length == 0 )
    {
        return;
    }

    var container = arr[0];

    if( pageIdx )
    {
        config.currentPage = pageIdx;
    }

    if( !config.currentPage )
    {
        config.currentPage = 1;
    }

    var availableRows = config.rowCount - config.additionalRowCount + config.addedRowCount;

    config.maxPages = Math.floor( ( ( availableRows - 1 ) / config.rowsPerPage ) + 1 );
    if( config.maxPages > 0 && config.currentPage > config.maxPages )
    {
        config.currentPage = config.maxPages;
    }

    var startIndex = config.rowsPerPage * ( config.currentPage - 1 );
    var endIndex = startIndex + config.rowsPerPage - 1;

    for( var i = 0; i < rows.length; i++ )
    {
        if( i >= startIndex && i <= endIndex && i < config.rowCount - config.additionalRowCount + config.addedRowCount )
        {
            rows[i].show();
        }
        else
        {
            rows[i].hide();
        }
    }

    $( config.currentPageFieldName ).value = "" + config.currentPage;

    container.hide();

    container.innerHTML = "";

    if( config.maxPages < 2 )
    {
        return;
    }

    config.lastIndex = 0;

    for( var i = 1; i <= 2; i++ )
    {
        equandaWritePageLink( container, i, config, config.name );
    }

    var low = config.currentPage - config.range;
    var high = config.currentPage + config.range;

    if( low < 1 )
    {
        low = 1;
        high = 2 * config.range + 1;
    }
    else
    {
        if( high > config.maxPages )
        {
            high = config.maxPages;
            low = high - 2 * config.range;
        }
    }

    for( var i = low; i <= high; i++ )
    {
        equandaWritePageLink( container, i, config, config.name );
    }

    for( var i = config.maxPages - 1; i <= config.maxPages; i++ )
    {
        equandaWritePageLink( container, i, config, config.name );
    }

    container.show();
}

/**
 * Writes page links
 *
 * @param element - parent element
 * @param pageIndex - selected page
 * @param config - configuration object
 */
function equandaWritePageLink( element, pageIndex, config )
{
    if( pageIndex < 1 || pageIndex > config.maxPages )
        return;

    if( pageIndex <= config.lastIndex )
        return;

    if( pageIndex != config.lastIndex + 1 )
    {
        element.appendChild( document.createTextNode( "..." ) );
    }

    config.lastIndex = pageIndex;

    if( pageIndex == config.currentPage )
    {
        var span = document.createElement( "span" );
        span.className = "eqplc";
        span.innerHTML = "" + pageIndex;
        element.appendChild( span );
        return;
    }

    var anchor = document.createElement( "a" );
    anchor.href = "javascript:;";
    anchor.onclick = function()
    {
        eqShwPg( pageIndex, config );
    };
    anchor.title = "Goto Page " + pageIndex;
    anchor.innerHTML = "" + pageIndex;
    element.appendChild( anchor );
}

/**
 * Adds new row to paged loop
 *
 * @param config - configuration object
 */
function eqJsplA( config )
{
    if( !config || config.additionalRowCount == 0 || config.rowCount == 0 )
    {
        return;
    }

    if( config.addedRowCount < config.additionalRowCount )
    {
        config.addedRowCount = config.addedRowCount + 1;
    }

    eqChAL( config );

    $( config.addedRowFieldName ).value = "" + config.addedRowCount;

    eqShwPg( config.currentPage, config );
}

/**
 * Shows or hides links depends on count of added rows
 *
 * @param config - configuration object
 */
function eqChAL( config )
{
    var addLinks = $$( "." + config.addLinkClass );
    var addBlocks = $$( "." + config.addBlockClass );
    if( addLinks.length == 0 || addBlocks.length == 0 )
    {
        return;
    }
    if( config.additionalRowCount == 0 || config.addedRowCount == config.additionalRowCount )
    {
        addLinks[0].hide();
        addBlocks[0].show();
    }
    else
    {
        addLinks[0].show();
        addBlocks[0].hide();
    }
}

/**
 * Marks row as deleted
 *
 * @param anchor - clicked link
 * @param hiddenId - id of hidden component with deleted rows
 * @param index - deleted row index
 * @param rowContentClass - css class of the row
 * @param revertAnchorId - revert anchor id
 */
function eqMarkDel( anchor, hiddenId, index, rowContentClass, revertAnchorId )
{
    if( hiddenId && $( hiddenId ) != null && index >= 0 )
    {
        var hidden = $( hiddenId );

        var arrValues = eqStr2IntArr( hidden.value );

        arrValues.push( index );
        arrValues = arrValues.uniq();

        hidden.value = arrValues.join( "," );

        var rows = $$( "." + rowContentClass );
        if( rows.length > 0 )
        {
            rows[0].style.backgroundColor = "red";
            rows[0].style.padding = "2px";
        }

        var revertLink = $( revertAnchorId );
        if( revertLink != null )
        {
            revertLink.style.display = "block";
        }
        anchor.style.display = "none";
    }
}

/**
 * Removes row index from list of deleted rows
 *
 * @param anchor - clicked link
 * @param hiddenId - id of hidden component with deleted rows
 * @param index - deleted row index
 * @param rowContentClass - css class of the row
 * @param deleteAnchorId - delete anchor id
 */
function eqRevDel( anchor, hiddenId, index, rowContentClass, deleteAnchorId )
{
    if( hiddenId && $( hiddenId ) != null && index >= 0 )
    {
        var hidden = $( hiddenId );

        var arrValues = eqStr2IntArr( hidden.value );

        arrValues = arrValues.without( index );

        hidden.value = arrValues.join( "," );

        var rows = $$( "." + rowContentClass );
        if( rows.length > 0 )
        {
            rows[0].style.backgroundColor = "white";
            rows[0].style.padding = "0";
        }
    }

    var deleteLink = $( deleteAnchorId );
    if( deleteLink != null )
    {
        deleteLink.style.display = "block";
    }
    anchor.style.display = "none";
}

/**
 * Splits string to array of integers
 *
 * @param str - string to split
 * @param delimiter - value delimiter
 * @return new array of integers
 */
function eqStr2IntArr( str, delimiter )
{
    var arr = new Array();
    if( str )
    {
        var strs = str.split( delimiter && delimiter.length > 0 ? delimiter : "," );
        for( var i = 0; i < strs.length; i++ )
        {
            if( strs[i] && strs[i].length > 0 )
            {
                var num = parseInt( strs[i] );
                if( !isNaN( num ) )
                {
                    arr.push( num );
                }
            }
        }
    }
    arr = arr.uniq();
    return arr;
}

/* for tabs component */

var EQUANDA_TABS_ACTIVE = "eqTabsA";
var EQUANDA_TABS_TRAVERSE = "eqTravSkip";
var EQUANDA_TABS_ALL = "eqTabsAll";

function eqTabsCl( title, content, usingCookies )
{
    var tabsId = title.up().up().up().readAttribute( "id" );
    var titleId = title.readAttribute( "id" );
    var indexstr = titleId.substr( titleId.length - 1);
    var index = parseInt( indexstr );
    if ( !title.hasClassName( EQUANDA_TABS_ACTIVE ) )
    {
        title.addClassName( EQUANDA_TABS_ACTIVE );
        title.addClassName( EQUANDA_TABS_TRAVERSE );
        title.up().siblings().each( function rem( el ) {
            el.down().removeClassName( EQUANDA_TABS_ACTIVE ).removeClassName( EQUANDA_TABS_TRAVERSE );
        } );
        content.style.display = "block";
        content.siblings().each( function rem( el ) {
            if ( !el.hasClassName( EQUANDA_TABS_ALL ) ) el.style.display = "none";
        } );
        if (usingCookies)
        {
            document.cookie = "active_index_" + tabsId + "=" + ( index + 1) + "; path=/";
        }
    }
    return false;
}

/* for treetable component */

function eqTTtoggleRows( elm )
{
    var rows = document.getElementsByTagName("TR");
    elm.className = "folderclose";
    var newDisplay = "none";
    var thisID = elm.parentNode.parentNode.parentNode.id + "-";
    // Are we expanding or contracting? If the first child is hidden, we expand
    for ( var i = 0 ; i < rows.length ; i++ )
    {
        var r = rows[i];
        if ( eqTTmatchStart(r.id, thisID, true) )
        {
            if ( r.style.display == "none" )
            {
                if ( document.all ) newDisplay = "block"; //IE4+ specific code
                else newDisplay = "table-row"; //Netscape and Mozilla
                elm.className = "folderopen";
            }
            break;
        }
    }

    // When expanding, only expand one level.  Collapse all descendants.
    var matchDirectChildrenOnly = (newDisplay != "none");

    for ( var j = 0 ; j < rows.length ; j++ )
    {
        var s = rows[j];
        if ( eqTTmatchStart(s.id, thisID, matchDirectChildrenOnly) )
        {
            s.style.display = newDisplay;
            var cell = s.getElementsByTagName("TD")[0];
            var tier = cell.getElementsByTagName("DIV")[0];
            var folder = tier.getElementsByTagName("A")[0];
            if ( folder.getAttribute("onclick") != null )
            {
                folder.className = "folderclose";
            }
        }
    }
}

function eqTTtoggle( elm )
{
    var folder = elm.getElementsByTagName("A")[0];
    eqTTtoggleRows(folder);
}

function eqTTmatchStart( target, pattern, matchDirectChildrenOnly )
{
    var pos = target.indexOf(pattern);
    if ( pos != 0 ) return false;
    if ( !matchDirectChildrenOnly ) return true;
    if ( target.slice(pos + pattern.length, target.length).indexOf("-") >= 0 ) return false;
    return true;
}

/* for truncate component */

var disappearDelay = 250; // Delay before hiding of popup
var verticalOffset = 0; // Vertical popup offset
var isIE = document.all; // IE check

/* Gets offset of the element with full text from the short text element */
function equandaGetPositionOffset( what, offsettype ){
	var totaloffset = ( offsettype == "left" ) ? what.offsetLeft : what.offsetTop;
	var parentEl = what.offsetParent;
	while( parentEl != null ){
		totaloffset = ( offsettype == "left" ) ? totaloffset + parentEl.offsetLeft : totaloffset + parentEl.offsetTop;
		parentEl = parentEl.offsetParent;
	}
	return totaloffset;
}

/* Shows or hides element */
function equandaShowHide( obj, e ){
	textObject.style.left = textObject.style.top = "-500px";
	if( e.type == "mouseover" ){
		obj.visibility = "visible";
	}
}

/* Checks for compatibility  and returns suitable document's body */
function equandaCompatibleObject(){
	return ( document.compatMode && document.compatMode != "BackCompat") ? document.documentElement : document.body;
}

/* Gets browser's edge offset */
function equandaClearBrowserEdge( obj, targetEdge ){
	if( targetEdge == "rightedge" ){
		edgeoffsetx = 0;
		var windowedge = isIE && !window.opera ? equandaCompatibleObject().scrollLeft + equandaCompatibleObject().clientWidth - 15 : window.pageXOffset + window.innerWidth - 15;
		textObject.contentmeasure = textObject.offsetWidth;
		if( windowedge - textObject.x < textObject.contentmeasure ){
			edgeoffsetx = textObject.contentmeasure - obj.offsetWidth;
		}
		return edgeoffsetx;
	} else {
		edgeoffsety = 0;
		var topedge = isIE && !window.opera ? equandaCompatibleObject().scrollTop : window.pageYOffset;
		var windowedge = isIE && !window.opera ? equandaCompatibleObject().scrollTop + equandaCompatibleObject().clientHeight - 15 : window.pageYOffset+window.innerHeight - 18;
		textObject.contentmeasure = textObject.offsetHeight;
		if( windowedge - textObject.y < textObject.contentmeasure ){
			edgeoffsety = textObject.contentmeasure + obj.offsetHeight + ( verticalOffset * 2 );
		}
		return edgeoffsety;
	}
}

/* Shows element with full text */
function equandaShowText( obj, e ){
	if( window.event ){
		event.cancelBubble = true;
	} else if( e.stopPropagation ){
		e.stopPropagation();
	}
	if( typeof textObject != "undefined" ){
		 textObject.style.visibility = "hidden";
	}
	equandaClearHide();
	textObject = document.getElementById( obj.getAttribute( "id" ) + "_text" );
	equandaShowHide( textObject.style, e );
	textObject.x = equandaGetPositionOffset( obj, "left" );
	textObject.y = equandaGetPositionOffset( obj, "top" ) + verticalOffset;
	textObject.style.left = textObject.x - equandaClearBrowserEdge( obj, "rightedge" ) + "px";
	textObject.style.top = textObject.y - equandaClearBrowserEdge( obj, "bottomedge" ) + obj.offsetHeight + "px";
}

/* Hides popup with delay */
function equandaHideWithDelay(){
	delayhide = setTimeout( "textObject.style.visibility = 'hidden'; textObject.style.left = 0;", disappearDelay );
}

/* Clears timeout object */
function equandaClearHide(){
	if( typeof delayhide != "undefined" ){
		clearTimeout( delayhide );
	}
}

/* Initializes mouse's events on element with truncated text */
function eqTruncInit( id )
{
	var e = document.getElementById( id );
	if( e != null ){
		e.onmouseover = function(e){
			var ev = window.event ? window.event : e;
			equandaShowText( this, ev );
		}
		e.onmouseout = equandaHideWithDelay;
	}
}

/* TextAreaAutoExpander */

/**
 * @name : Textarea Expander With Animation
 * @originalAuthor : Swapnil Sarwe
 * @original : http://swapnilsarwe.phpnet.us/text-area-auto-xpander.html
 * @description : Based on and inspired by James Padolsey's jQuery autoResize
 *              The basic idea is to have textarea expand as the user types in
 *              the content, so that the user never has the scroll bar appear
 *              for the textarea.
 * @see : http://james.padolsey.com/javascript/jquery-plugin-autoresize/
 *
 * original modified to automatically apply directly on all textareas (Joachim)
 * and to assure it works on hidden textareas
 */
function equandaXpander(options) {
	makeDuplicate = function(el) {
		var newEl = el.cloneNode(true);
		newEl.removeAttribute('id');
		newEl.removeAttribute('name');
		newEl.style['marginLeft'] = "-9999px";
		newEl.style['display'] = "block";
		return newEl;
	};
	insertAfter = function(target, bullet) {
		target.nextSibling ? target.parentNode.insertBefore(bullet,
				target.nextSibling) : target.parentNode.appendChild(bullet);
	};

	var lastHeight = null;
	activateResize = function(evt) {
		evt = evt || window.event;
		resize( this );
		if (evt.preventDefault) {
			evt.preventDefault();
		} else {
			evt.returnValue = false;
		}
	};
	resize = function(el) {
		var cloned = el.clone;
		cloned.value = el.value;
		if (!lastHeight) {
			lastHeight = el.clientHeight;
		} else {
			lastHeight = cloned.scrollHeight;
		}
		var newHeight = cloned.scrollHeight + options.xtraSpace;
		if (newHeight == el.clientHeight)
			return;
        el = $( el );
        el.setStyle("height:"+newHeight+"px;");
	};
	$$('textarea').each( function activate( object )
    {
        if ( object.style['marginLeft'] != "-9999px" )
        {
            object.style['overflowY'] = 'hidden';
            var dupA = makeDuplicate(object);
            object.clone = dupA;
            $$('body')[0].insert( dupA );

            object.observe( 'keyup', activateResize );
            resize( object );
        }
    } );
}