/*
SimplestNet 3 Menu js
Eric Grossi <egrossi@simplestnet.com>
15 apr 2006
Copyright 2004-2006 SimplestNet Inc.
See the enclosed file GPL for license information (GPL).  If you
did not receive this file, see http://www.gnu.org/copyleft/gpl.html.

Menu definition example

var mnMain = {
    'clkactive' : true,     // true to activate top menu by click. On true, top menu dont react to script and url definition.
    'orient'    : "h",      // h for horizontal, v for vertical. Default h.
    'frstyle'   : "",       // css style text, ex: margin-right: 1px; dont forget the last ;
    'items'     : [         // top menu items definition.
         // Top menu items definition.
        {
            'text' : "Top item 1",      // item text, can contain html tag.
            'offostyle' : "",           // css style text when mouse are outside item and menu is inactive, see 'clkactive'.
            'offistyle' : "",           // css style text when mouse are inside item and menu is inactive, see 'clkactive'.
            'outstyle'  : "",           // css style text when mouse are outside item and menu is active.
            'instyle'   : "",           // css style text when mouse are inside item and menu is active.
            'orient'    : "v",
            'yoffset'   : 3,            // Optional, submenu vertical offset in px.
            'xoffset'   : -5,           // Optional, submenu horizontal offset in px.
            'frstyle'   : "",
            'items'     : [
                 // Sub menu items definition.
                {
                    'text' : "Sub item 1-1",
                    'outstyle'  : "",
                    'instyle'   : "",
                    'script'    : "",           // javascript code to execute when clicking on item, called befor url.
                    'url'       : ""            // url to call when clicking on item.
                }...
            ]
        },
        {
            'text' : "Top item 2",
            'offostyle' : "",
            'offistyle' : "",
            'outstyle'  : "",
            'instyle'   : "",
            'orient'    : "v",
            'yoffset'   : 3,
            'xoffset'   : -10,
            'frstyle'   : "",
            'items'     : [
                 // Sub menu items definition.
                {
                    'text' : "Sub item 2-1",
                    'outstyle'  : "",
                    'instyle'   : "",
                    'script'    : "",
                    'url'       : ""
                },
                {
                    'text' : "Sub item 2-2",
                    'outstyle'  : "",
                    'instyle'   : "",
                    'orient'    : "v",
                    'yoffset'   : 3,
                    'xoffset'   : 2,
                    'frstyle'   : "",
                    'items'     : [
                         // Sub sub menu items definition.
                        {
                            'text' : "Sub item 2-1",
                            'outstyle'  : "",
                            'instyle'   : "",
                            'url'       : ""
                        }...
                    ]
                }...
            ]
        }
    ]
};


*/

/* Menu constructor */
var SimplestNetMenu = function(menudef, idcont) {
    // check our params
    if ( !(this.Parent = SimplestNetMenu.getId(idcont)) ) {
        alert("SimplestNetMenu: Invalid parent");
        return;
    }

    if ( typeof(menudef)!="object" || typeof(menudef['items'])!="object" ) {
        alert("SimplestNetMenu: Invalid menu definition");
        return;
    }

    // no duplicate menu for a container
    if ( this.Parent._sn_menu ) return;

    if ( menudef['clkactive'] ) this.ClkActive = true;

    // write our first frame
    this.Frame = SimplestNetMenu.writeFrame(this.Parent, null, this,  menudef);
    this.Parent._sn_menu = this;
    this.IsClkActive = false;

    // attach events
    SimplestNetMenu._addEvent(this.Frame, "mouseover", SimplestNetMenu._onmouseover);
    SimplestNetMenu._addEvent(this.Frame, "mouseout", SimplestNetMenu._onmouseout);
    SimplestNetMenu._addEvent(this.Frame, "click", SimplestNetMenu._onclick);

    /* outside menu event */
    if ( !document._sn_menu_outside ) {
        document._sn_menu_outside = true;
        SimplestNetMenu._addEvent(document, "click", SimplestNetMenu._mainonclick);
    }
}

/* Close all menu items */
SimplestNetMenu.prototype.Close = function() {
    if ( !this.Frame ) return;

    this.IsClkActive = false;

    for ( var i=0; i<this.Frame.Items.length; i++ )
        SimplestNetMenu.closeBranche(this.Frame.Items[i]);
}

/* write frame*/
SimplestNetMenu.writeFrame = function(parent, mnparent, menu, framedef) {
    var frame = document.createElement("div");

    frame.IsItem = false;
    frame.Parent = mnparent;
    frame.Menu = menu;
    frame.Orient = framedef['orient'] ? framedef['orient'].toLowerCase() : "h";
    frame.Style = (framedef['frstyle'] ? framedef['frstyle'] : "")+" float: left;";
    frame.OffStyle = (framedef['frstoff'] ? framedef['frstoff']+" float: left;" : frame.Style);
    frame.style.cssText = (!mnparent && menu.ClkActive && !menu.IsClkActive ? frame.OffStyle : frame.Style);
    frame.Items = [];

    // write items
    if ( framedef['items'] ) {
        for ( var i=0; i<framedef['items'].length; i++)
            SimplestNetMenu.writeItem(frame, framedef['items'][i]);
    }

    parent.appendChild(frame);
    return frame;
}

/* write 1 item inside frame */
SimplestNetMenu.writeItem = function(frame, itemdef) {
    var item = document.createElement("div");
    var orstyle = frame.Orient=="v" ? " clear: left;": "";

    frame.Items[frame.Items.length] = item;
    item.IsItem = true;
    item.Parent = frame;
    item.Menu = frame.Menu;
    item.Index = frame.Items.length-1;
    item.OrStyle = orstyle;
    item.SubMenu = false;
    item.SubMenuFrame = false;
    item.IsOn = false;
    item.url = null;
    item.script = null;
    item.OStyle = (itemdef['outstyle'] ? itemdef['outstyle'] : "")+" float: left;"+orstyle;
    item.IStyle = (itemdef['instyle'] ? itemdef['instyle'] : "")+" float: left;"+orstyle;
    item.OffOStyle = (itemdef['offostyle'] ? itemdef['offostyle']+" float: left;"+orstyle : item.OStyle);
    item.OffIStyle = (itemdef['offistyle'] ? itemdef['offistyle']+" float: left;"+orstyle : item.OStyle);
    item.COStyle = (!frame.Parent && frame.Menu.ClkActive && !frame.Menu.IsClkActive ? item.OffOStyle : item.OStyle);
    item.CIStyle = (!frame.Parent && frame.Menu.ClkActive && !frame.Menu.IsClkActive ? item.OffIStyle : item.IStyle);
    item.XOffset = itemdef['xoffset'] ? itemdef['xoffset'] : 0;
    item.YOffset = itemdef['yoffset'] ? itemdef['yoffset'] : 0;
    item.style.cssText = item.COStyle;

    if ( itemdef['text'] ) item.innerHTML = itemdef['text'];

    frame.appendChild(item);

    if ( itemdef['items'] ) {
        // create submenu connector
        var conn = document.createElement("div");
        conn.style.cssText = "position: absolute; top: 0px; left: 0px; visibility: hidden; "+
                             (item.XOffset>0 || item.YOffset>0 ? "background: url(noimage.png) repeat;": "");
        conn.IsItem = false;
        conn.Parent = item;
        conn.Menu = frame.Menu;
        item.SubMenu = conn;
        item.SubMenuFrame = SimplestNetMenu.writeFrame(conn, item, frame.Menu, itemdef);
        frame.appendChild(conn);
    } else {
        if ( SimplestNetMenu.is_ie ) {
            var conn = document.createElement("div");
            conn.style.cssText = "background-color: transparent; width: 1px; top: 0px; left: 0px; height: 10px;"+
                                 " position: absolute;";
            item.Conn = conn;
            frame.appendChild(conn);
        }

        if ( itemdef['url'] )
            item.url = itemdef['url'];

        if ( itemdef['script'] )
            item.script = itemdef['script'];
    }

    return item;
}

/* return true if both item objects are in same branche */
SimplestNetMenu.isInBranche = function(ob1, ob2) {
    var obr1, obr2;

    if ( !ob1 || !ob1.IsItem || !ob2 || !ob2.IsItem ) return false;

    // first branche
    do { obr1 = ob1; } while ( ob1 = ob1.Parent.Parent);

    // second branche
    do { obr2 = ob2; } while ( ob2 = ob2.Parent.Parent);

    return (obr1==obr2);
}

/* close all element in item branche */
SimplestNetMenu.closeBranche = function(ob) {
    if ( !ob || !ob.IsItem ) return;

    ob.style.cssText = ob.COStyle;

    if ( ob.SubMenu ) {
        for ( var i=0; i<ob.SubMenuFrame.Items.length; i++ )
            SimplestNetMenu.closeBranche(ob.SubMenuFrame.Items[i]);

        ob.SubMenu.style.visibility = "hidden";
    }

    ob.IsOn = false;
}

/* close all brother branches */
SimplestNetMenu.closeBrotherBranche = function(ob) {
    if ( !ob || !ob.IsItem ) return;

    var fr = ob.Parent;

    for ( var i=0; i<fr.Items.length; i++ ) {
        if ( i==ob.Index || (fr.Parent && !fr.Items[i].IsOn) ) continue;

        SimplestNetMenu.closeBranche(fr.Items[i]);
    }
}

/* onmouseover event */
SimplestNetMenu._onmouseover = function(ev) {
    var el = SimplestNetMenu._getTargetItem(ev), op = el;

    if ( !el ) return;

    if ( el.Menu.ClkActive && !el.Menu.IsClkActive ) {
        if ( !el.Parent.Parent ) el.style.cssText = el.CIStyle;

        return;
    }

    SimplestNetMenu.closeBrotherBranche(el);
    el.style.cssText = el.CIStyle;
    el.IsOn = true;

    if ( el.SubMenu ) {
        var r = SimplestNetMenu._getAbsolutePos(el);

        if ( "h"==el.Parent.Orient ) r.y += el.offsetHeight;
        if ( "v"==el.Parent.Orient ) r.x += el.offsetWidth;

        if ( el.XOffset>0 ) el.SubMenu.style.paddingLeft = el.XOffset+"px";
        if ( el.XOffset<0 ) r.x+=el.XOffset;
        if ( el.YOffset>0 ) el.SubMenu.style.paddingTop = el.YOffset+"px";
        if ( el.YOffset<0 ) r.y+=el.YOffset;

        if ( SimplestNetMenu.is_ie && el.offsetParent ) {
            r.x += el.offsetParent.clientLeft;
            r.y += el.offsetParent.clientTop;
        }

        el.SubMenu.style.top = r.y+"px";
        el.SubMenu.style.left = r.x+"px";
        el.SubMenu.style.visibility = "visible";
    } else if ( el.Conn ) {
        el.Conn.style.top = (el.offsetParent.offsetTop)+"px";
        el.Conn.style.left = (el.offsetParent.offsetLeft)+"px";
    }

    SimplestNetMenu._stopEvent(ev);
}

/* onmouseout event */
SimplestNetMenu._onmouseout = function(ev) {
    var el = SimplestNetMenu._getTargetItem(ev), op = el, from = SimplestNetMenu._getFromItem(ev), last = null;

    if ( !el ) return;

    if ( el.Menu.ClkActive && !el.Menu.IsClkActive ) {
        if ( !el.Parent.Parent ) el.style.cssText = el.COStyle;

        return;
    }

    if ( !from || (el!=from && el!=from.Parent.Parent ) ) {
        if ( !SimplestNetMenu.isInBranche(el, from) ) {
            if ( !el.Menu.ClkActive )
                while ( op = op.Parent.Parent ) last  = op;
            else
                while ( (op = op.Parent.Parent) && op.Parent.Parent ) last  = op;

            SimplestNetMenu.closeBranche(last);
        }

        if ( !el.Menu.ClkActive || el.Parent.Parent )
            SimplestNetMenu.closeBranche(el);
    }

    SimplestNetMenu._stopEvent(ev);
}

/* onclick event */
SimplestNetMenu._onclick = function(ev) {
    var el = SimplestNetMenu._getTargetItem(ev);

    if ( !el ) return;

    if ( document._sn_menu_active && el.Menu!=document._sn_menu_active )
        SimplestNetMenu._mainonclick(ev);

    if ( !el.Parent.Parent && el.Menu.ClkActive ) {
        for ( var i=0; i<el.Menu.Frame.Items.length; i++ ) {
            if ( el.Menu.IsClkActive ) {
                el.Menu.Frame.Items[i].COStyle = el.Menu.Frame.Items[i].OffOStyle;
                el.Menu.Frame.Items[i].CIStyle = el.Menu.Frame.Items[i].OffIStyle;
            } else {
                el.Menu.Frame.Items[i].COStyle = el.Menu.Frame.Items[i].OStyle;
                el.Menu.Frame.Items[i].CIStyle = el.Menu.Frame.Items[i].IStyle;
            }
        }

        if ( el.Menu.IsClkActive ) {
            el.Menu.Close();
            el.Menu.Frame.style.cssText = el.Menu.Frame.OffStyle;
            document._sn_menu_active = null;
        } else {
            el.Menu.IsClkActive = true;
            document._sn_menu_active = el.Menu;
            el.Menu.Frame.style.cssText = el.Menu.Frame.Style;
            SimplestNetMenu._onmouseover(ev);
        }
    } else {
        if ( el.script ) {
            el.Menu.Close();
            eval(el.script);
        }

        if ( el.url ) {
            el.Menu.Close();
            window.location=el.url;
        }
    }

    SimplestNetMenu._stopEvent(ev);
}

/* outside menu click event */
SimplestNetMenu._mainonclick = function(ev) {
    var el = SimplestNetMenu._getTargetItem(ev);

    if ( document._sn_menu_active && (!el || el.Menu!=document._sn_menu_active) ) {
        for ( var i=0; i<document._sn_menu_active.Frame.Items.length; i++ ) {
            document._sn_menu_active.Frame.Items[i].COStyle = document._sn_menu_active.Frame.Items[i].OffOStyle;
            document._sn_menu_active.Frame.Items[i].CIStyle = document._sn_menu_active.Frame.Items[i].OffIStyle;
        }

        document._sn_menu_active.Close();
        document._sn_menu_active.Frame.style.cssText = document._sn_menu_active.Frame.OffStyle;
        document._sn_menu_active = null;
    }
}

/* get absolute position of element */
SimplestNetMenu._getAbsolutePos = function(el) {
	var SL = 0, ST = 0;
	var is_div = /^div$/i.test(el.tagName);

	if ( is_div && el.scrollLeft )
		SL = el.scrollLeft;

	if ( is_div && el.scrollTop )
		ST = el.scrollTop;

	var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };

	if ( el.offsetParent && el.offsetParent.style.position !="absolute" && el.offsetParent.style.position !="relative" ) {
		var tmp = SimplestNetMenu._getAbsolutePos(el.offsetParent);
		r.x += tmp.x;
		r.y += tmp.y;
	}

	return r;
};

/* Find an object by id */
SimplestNetMenu.getId = function(id)
{
	if ( undefined!=id && typeof(id)=="string" ) return document.getElementById(id);

	return false;
}

/* detect a special case of "web browser" */
SimplestNetMenu.is_ie = ( /msie/i.test(navigator.userAgent) &&
		   !/opera/i.test(navigator.userAgent) );

SimplestNetMenu.is_ie5 = ( SimplestNetMenu.is_ie && /msie 5\.0/i.test(navigator.userAgent) );

/* detect Opera browser */
SimplestNetMenu.is_opera = /opera/i.test(navigator.userAgent);

/* detect KHTML-based browsers */
SimplestNetMenu.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);

/* event utils */
SimplestNetMenu._addEvent = function(el, evname, func) {
	if ( el.attachEvent ) { // IE
		el.attachEvent("on" + evname, func);
	} else if ( el.addEventListener ) { // Gecko / W3C
		el.addEventListener(evname, func, true);
	} else {
		el["on" + evname] = func;
	}
}

SimplestNetMenu._removeEvent = function(el, evname, func) {
	if ( el.detachEvent ) { // IE
		el.detachEvent("on" + evname, func);
	} else if ( el.removeEventListener ) { // Gecko / W3C
		el.removeEventListener(evname, func, true);
	} else {
		el["on" + evname] = null;
	}
}

SimplestNetMenu._stopEvent = function(ev) {
    if ( !ev && !(ev = window.event) ) return;

    if ( SimplestNetMenu.is_ie ) {
		ev.cancelBubble = true;
		ev.returnValue = false;
	} else {
		ev.preventDefault();
		ev.stopPropagation();
	}
}

SimplestNetMenu._getTargetElement = function(ev) {
	var f = null;

    if ( SimplestNetMenu.is_ie )
        f = ev.srcElement;
    else
        f = ev.target;

    return f;
}

SimplestNetMenu._getTargetItem = function(ev) {
    var el = SimplestNetMenu._getTargetElement(ev), res = null;

    if ( !el ) return null;

    do {
        if ( el.Parent ) res = el.Parent;
        if ( el.IsItem ) res = el;
    } while ( !res && (el = el.parentNode) );

    return res;
}

SimplestNetMenu._getFromItem = function(ev) {
	var f = null, res = null;

    if ( SimplestNetMenu.is_ie )
        f = ev.toElement;
    else
        f = ev.relatedTarget;

    if ( !f ) return null;

    do {
        if ( f.Parent ) res = f.Parent;
        if ( f.IsItem ) res = f;
    } while ( !res && (f = f.parentNode) );

    return res;
}