
var CalidasoftMenu = function(id, posRefId, style, updateURL, updateParam) {
    
    //id is element which is displayed
    //posRefEl is element used to trigger menu
    //style is 'menu' or 'popup' . 
    //  Menus appear near trigger element and are hidden one mouse moves 
    //  off menu and trigger
    //  Popups are centered and remain on screen

    this.triggerLeave = function(event) {
        this.cancelAllEffects();
        this.hide();
    }

    this.registerEventHandlers = function() {

        if (this.style == 'menu' || this.style == 'hover') {

            this.el.observe('mouseleave', function(event) {
                //this can get triggered on auto complete drop down on forms
                //this seems to give a relatedTarget of null
                target = event.relatedTarget || event.toElement;
                if (target == undefined || target == this.posRefEl) {
                    return;
                }
                this.cancelAllEffects();
                this.hide();
            }.bindAsEventListener(this))

            this.el.observe('mouseenter', function(event) {
                this.cancelAllEffects();
                this.show(0.25);
            }.bindAsEventListener(this))

        }

        if (this.posRefEl != null) {

            //register handler for triggering menu
            if (this.style == 'menu' || 
                this.style == 'popup' || 
                this.style == 'popup_menu') {
                this.posRefEl.observe('click', function(event) {
                    this.toggle();
                }.bindAsEventListener(this))
            } 
            else if (this.style == 'hover') {
                this.posRefEl.observe('mouseenter', function(event) {
                    this.toggle();
                }.bindAsEventListener(this))
            }
        }
    }

    this.setPosRef = function(posRefEl) {

        if ( this.posRefEl == posRefEl ) {
            return;
        }

        if (this.posRefEl != null) {
            this.posRefEl.stopObserving('mouseleave');
        }

        this.posRefEl = posRefEl;

        if ( (this.style == 'menu' || this.style == 'hover') 
              && this.posRefEl != null ) {
            this.posRefEl.observe('mouseleave', 
                              this.triggerLeave.bindAsEventListener(this));
        }
    }

    this.setPosRefAndToggle = function(posRefEl, updateParam) {

        transition = false;
        if (posRefEl != this.posRefEl) {
            transition = true;
        }

        this.setPosRef(posRefEl);

        this.updateParam = updateParam;
        this.toggle(transition);
    }

    this.setPosRefAndShow = function(posRefEl, updateParam) {

        this.cancelAllEffects();

        if ( this.el.visible() &&
             this.posRefEl == posRefEl ) {
            return;
        }

        this.setPosRef(posRefEl);

        this.updateParam = updateParam;
        this.updateAndShow();
    }

    this.cancelAllEffects = function() {
        var queue = Effect.Queues.get(this.effectScope);
        queue.each(function(effect) { effect.cancel(); });
    }

    this.toggle = function(transition) {

        if (transition == null) transition = false;

        if (!this.el.visible() || transition)
        {
            if (transition && this.el.visible()) {
                this.hideNow();
            }
            this.updateAndShow();
        } else {
            this.hide();
        }
    }

    //function for doing Ajax updates 
    this.doAjaxUpdate = function() {
        new Ajax.Updater(this.el, this.updateURL, {
            method : 'post',
            parameters : this.updateParam,
            onComplete : function() {
                if (this.updateParam != null) {
                    if (this.updateParam.postUpdate != null) {
                        this.updateParam.postUpdate(this.el, this.updateParam);
                    }
                }
                this.setPosition();
            }.bind(this)
        });
    }


    this.updateAndShow = function() {

        if (this.updateParam != null && 
            this.updateParam.preDisplay != null) {
            this.updateParam.preDisplay(this.el, this.updateParam);
        }

        // set position of menu and update element
        if (this.updateURL != null) {
            this.el.update('<p style="color: #404040; background-color: #f0f0f0; border: 2px #404040 solid; font-size: 16px; padding: 4px">Loading &hellip;</p>');
            if (this.style == 'hover') { 
                this.delayedShow(0.25);
                //delay update very slightly in case mouse just passing over
                clearTimeout(this.ajaxUpdateTimeoutHandler);
                this.ajaxUpdateTimeoutHandler = 
                            setTimeout(this.doAjaxUpdate.bind(this), 100);
            } else {
                //make appear last longer if need to load data
                this.show(1.0);
                this.doAjaxUpdate();
            }
        } else {
            if (this.style == 'hover') { 
                this.delayedShow(0.25);
            } else {
                this.show(0.25);
            }
        }
    }

    this.setPosition = function() {
        if (this.style == 'menu' || 
            this.style == 'popup_menu' ||
            this.style == 'hover') {
            this.reposition();
        } else {
            this.center();
        }
    }

    /*
     * Center element on screen 
     */
    this.center = function() {

        dim = this.el.getDimensions()
        vdim = document.viewport.getDimensions()
        offsets = document.viewport.getScrollOffsets()
        left = ( (vdim.width - dim.width) / 2 ) + offsets.left;
        top = ( (vdim.height - dim.height) / 2 ) + offsets.top;
        this.el.style.left = left + "px"
        this.el.style.top = top + "px"
    }


    /*
     * Reposition element to try to keep it on the screen
     */
    this.reposition = function() {

        if (this.posRefEl == null) {
            this.el.style.left = "0px";
            this.el.style.top = "0px";
        }

        offsets = document.viewport.getScrollOffsets()
        vdim = document.viewport.getDimensions()
        posRefPos = this.posRefEl.cumulativeOffset();

        windowTop = offsets.top
        windowBottom = vdim.height + offsets.top

        posRefTop = posRefPos.top;
        posRefBottom = posRefPos.top + this.posRefEl.getHeight();

        var top, left;

        if ( (posRefTop - windowTop) > (windowBottom - posRefBottom) ) {
            top = posRefTop - this.el.getHeight();
        } else {
            top = posRefBottom;
        }

        w = this.el.getWidth();
        left = posRefPos.left;
        diff = vdim.width - (left + w - offsets.left);
        if ( diff < 0 )
        {
            left = left + diff;
        }

        this.el.style.left = left + "px";
        this.el.style.top = top + "px";
    }

    this.hide = function() {
        clearTimeout(this.hoverTimeoutHandle);
        new Effect.Fade(this.el, { duration : 0.25, queue : { position: 'end', scope : this.effectScope} })
    }

    this.hideNow = function() {
        clearTimeout(this.hoverTimeoutHandle);
        this.el.hide();
    }

    this.show = function(dur) {
        new Effect.Appear(this.el, { duration : dur, queue : { position: 'end', scope : this.effectScope} })
        this.setPosition();
    }

    this.delayedShow = function(dur) {
        clearTimeout(this.hoverTimeoutHandle);
        this.hoverTimeoutHandle = 
                    setTimeout(this.show.bind(this, dur), 500);
    }

    // Constructor code
    this.effectScope = id+"_calidasoftmenu";
    this.el = $(id);
    if (style != null && style != "") {
        this.style = style;
    } else {
        this.style = 'menu';
    }
    this.updateURL = updateURL;
    this.updateParam = updateParam;
    this.hoverTimeoutHandle = null;
    this.ajaxUpdateTimeoutHandler = null;

    //register required event handlers
    this.posRefEl = null;
    this.setPosRef($(posRefId));
    this.registerEventHandlers();

    this.el.style.position = "absolute";

}

