/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2010 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Matthias Biggeleben <matthias.biggeleben@open-xchange.com>
 * @ignore
 */

// DEVELOPMENT IN PROGRESS // SUBJECT TO PERMANENT CHANGE!


ox.gui.Menu = ox.gui.Container.extend({
/** @lends ox.gui.Menu.prototype */
    
    getClass: function() { return "ox.gui.Menu"; },
    
    /** 
     * The basic Menu class
     * @constructs
     * @extends ox.gui.Container
     * @param {String} id An optional unique id for the widget.
     */
    construct: function(id) {

        // call super class constructor (inherit from Container)
        this._super(id);
        
        // active?
        this.statusActive = false;
        
        // current menu item
        this.openHover = null;
    },
    
    /**
     * @private
     */
    getTopOfPropagation: function() {
        var current = this;
        while (current.parent && current.parent.openHover) {
            current = current.parent;
        }
        return current;
    },

    /**
     * @private
     */
    propagateOpenHover: function (item) {
        // local handler?
        var p = this.hoverParent || this.parent;
        if (p.openHover !== undefined) {
            // another item open?
            if (p.openHover && p.openHover != item) {
                p.openHover.closeHover();
            }
            // set open menu item
            p.openHover = item;
        } else if ($.isFunction(p.propagateOpenHover)) {
            // a parent widget will take care
            p.propagateOpenHover(item);
        }
    },
    
    closeHover: function () {
    },
    
    /**
     * @private
     */
    isParentActive: function() {
        // return (and inject) state
        return (this.parent.statusActive = !!this.parent.statusActive);
    }
});

//------------------------------------------------------------------------------

// define within a closure to manage private static vars

(function () {
    
    // radio buttons
    var radios = {};

    ox.gui.MenuItem = ox.gui.Menu.extend({
    /** @lends ox.gui.MenuItem.prototype */
        
        getClass: function() { return "ox.gui.MenuItem"; },
        
        /** 
         * The basic MenuItem class
         * @constructs
         * @extends ox.gui.Menu
         * @param {Object} options Options
         * @param {String} [options.id] Widget ID
         * @param {String} [options.title] Menu item title
         * @param {Array} [options.icons] Menu icons
         * @param {String} [options.pathPrefix] Optional non-theme prefix for icon paths
         * @param {Boolean} [options.big] Appear with a large icon
         * @param {function ()} [options.action] Default action
         * @param {Boolean} [options.enabled] Enabled/Disabled
         * @param {Array} [options.requires] Internal descriptions to enable/disable items
         * @param {String} [options.behavior] button/checkbox/radio
         * @param {String} [options.group] Group name/id for radio buttons
         */
        construct: function (options) {
            
            // options
            this.options = $.extend({
                id: undefined,
                title: noI18n(""),
                icons: [],
                pathPrefix: undefined,
                big: false,
                action: $.noop,
                enabled: true,
                requires: [],
                behavior: "button",
                group: "",
                widget: this,
                parts: "both" // text/icon/both
            }, options || {});
            
            // can be cloned
            this.clonable = true;
            
            // call super class constructor (inherit from Menu)
            this._super(this.options.id);
            
            // re-get id
            this.options.id = this.id;
    
            // set text, icon etc.
            this.text = this.options.title;
            this.icon = this.options.icons.length > 0 ? this.options.icons[0] : "";
            this.iconDisabled = this.options.icons.length > 1 ? this.options.icons[1] : "";
            this.bigIcon = !!this.options.big;
            this.statusEnabled = !!this.options.enabled;
            this.forceSmallIcon = false;
    
            // menu strip
            this.menuStrip = null;
            this.menuStripAlign = "right";
            
            // click callback
            this.onClickCallback = null;
            
            // show/hide timeouts
            this.timeoutShow = null;
            this.timeoutHide = null;
            
            // radio?
            if (this.options.behavior === "radio") {
                // get group
                var group = this.options.group + "";
                // add to observer
                if (radios[group] === undefined) {
                    radios[group] = [];
                }
                radios[group].push(this);
            }
        },
        
        i18nHandler: function () {
            this.paint();
        },
        
        nodeInit: function() {
            // styles
            this.addCSSClass("menu-item");
            //this.css({ cursor: "pointer" });
        },
        
        nodeListeners: function() {
           
            var fnOver = function (e) {
                if (this.isEnabled()) {
                    this.addCSSClass("menu-item-over");
                }
                if (this.isParentActive() && this.isEnabled()) {
                    this.activate();
                }
            };
            
            var fnDown = function(e) {
                // fix event
                e = jQuery.event.fix(e || window.event);
                // "hard" mousedown
                if (this.options.mousedown) {
                    this.options.mousedown(this.options, e);
                }
                if (this.isEnabled()) {
                    this.addCSSClass("menu-item-down");
                }
            };

            var fnUp = function (e) {
                if (this.isEnabled()) { this.removeCSSClass("menu-item-down"); }
            };
            
            var fnOut = function (e) {
                if (this.isEnabled()) { 
                    this.removeCSSClass("menu-item-down");
                }
                window.clearTimeout(this.showTimeout);
                this.removeCSSClass("menu-item-over");
            };
            
            var fnClick = function (e) {
                // fix event
                e = jQuery.event.fix(e || window.event);
                // is enabled?
                if (this.isEnabled()) {
                    // is radio or checkbox?
                    if (this.options.behavior === "radio") {
                        // check
                        this.setChecked(true);
                    } else  if (this.options.behavior === "checkbox") {
                        // toggle state
                        this.setChecked(!this.stateChecked);
                    }
                    // action taken
                    var action = false;
                    // has individual action?
                    if (this.options.action !== $.noop) {
                        // extend event
                        e.widget = this;
                        // action
                        action = this.options.action(e);
                    }
                    
                    // action taken?
                    if (action === true) {
                        // hide hovers
                        ox.desktop.hideHovers();
                    } else {
                        // activate
                        this.toggleActive();
                    }
                    // avoid bubbling
                    e.stopPropagation();
                }
            };
            
            if (ox.browser.IE) {
                // fix strange IE/jQuery bug
                // TODO: review this issue with more time
                this.dom.node.onmouseover = $.proxy(fnOver, this);
                this.dom.node.onmousedown = $.proxy(fnDown, this);
                this.dom.node.onmouseup = $.proxy(fnUp, this);
                this.dom.node.onmouseout = $.proxy(fnOut, this);
                this.dom.node.onclick = $.proxy(fnClick, this);
            } else {
                this.addListener("dom:mouseover", fnOver);
                this.addListener("dom:mousedown", fnDown);
                this.addListener("dom:mouseup", fnUp);
                this.addListener("dom:mouseout", fnOut);
                this.addListener("dom:click", fnClick);
            }
        },
        
        validate: function (force) {
            // render both the widget as well as the menuStrip
            if (this.menuStrip) {
                // copy flag
                this.menuStrip.requiresValidation = !!(this.requiresValidation || force);
                // validate menu strip
                this.menuStrip.validate();
            }
            // render item
            ox.gui.Widget.prototype.validateTree.call(this);
        },
        
        paintEnabled: function() {
            this.paintState();
        },
        
        paintDisabled: function() {
            this.paintState();
        },
        
        getPath: function(path) {
            var prefix = this.options.pathPrefix;
            return prefix === undefined ? ox.gui.themePath + path
                                        : urlify(path);
        },
        
        paint: function () {
            
            // enable i18n (on first paint)
            this.enableI18n();
            
            // get empty jQuery node
            var node = $(this.dom.node).empty();
            
            // remove keyboard shortcuts
            var text = (this.text || "").replace(/&/, "");
            
            // big icon?
            if (this.bigIcon && !this.forceSmallIcon) {
                
                // popup below the item
                this.menuStripAlign = "bottom";
                
                // center align
                node.css({ textAlign: "center" });
                
                // adjust line breaks
                var matches = text.match(/([^\s]+\s|[^\s]+$)/g);
                if (matches && matches.length > 1) {
                    var splitIndex = Math.ceil(matches.length/2);
                    var half1 = matches.slice(0, splitIndex);
                    var half2 = matches.slice(splitIndex);
                    text = half1.join("") + "\n" + half2.join("");
                } else {
                    text = text + "\n\u00a0";
                }
                
                // add icon?
                if (this.icon) {
                    var icon = $("<img/>", { src: this.getPath(this.icon), alt: "", title: text });
                    node.append(icon.addClass("menu-item-icon-large"));
                    this.dom.icon = icon[0];
                }
                
                // add text
                var escaped = text.replace(/</g, "&lt;").replace(/\s*\n\s*/g, "<br/>");
                var div = $("<div/>").html(escaped).appendTo(node);
                
                // arrow?
                if (this.hasChildren()) {
                    div.append(
                        $("<img/>", { src: ox.gui.themePath + "img/arrows/menu_arrow_down.gif", alt: "" }).
                            addClass("menu-item-arrow")
                    );
                }
                
            } else {
                
                // left align
                node.css({
                    textAlign: "left",
                    whiteSpace: "nowrap"
                });
                
                var parts = this.options.parts;
                var hasIcon = this.icon && (parts === "icon" || parts === "both");
                var hasText = parts === "text" || parts === "both";
                
                // add icon?
                if (hasIcon) {
                    var icon = $("<img/>", { src: this.getPath(this.icon), alt: "", title: text });
                    node.append(icon.addClass("menu-item-icon"));
                    this.dom.icon = icon[0];
                }
                
                // add text
                if (hasText) {
                    node.append(
                        $("<span/>").text(hasIcon ? "\u00a0" + "\u00a0" + text : text)
                    );
                }
                // arrow?
                if (this.hasChildren()) {
                    node.append(
                        $("<img/>", { src: ox.gui.themePath + "img/arrows/menu_arrow_right.gif", alt: "" }).
                            addClass("menu-item-arrow")
                    );
                }
            }
            // consider current state
            this.paintState();
        },
        
        /**
         * Toggle active status.
         */
        toggleActive: function() {
            if (this.statusActive) {
                this.deactivate();
            } else {
                this.activate();
            }
        },
        
        /**
         * Activates the menu.
         */
        activate: function() {
            this.statusActive = true;
            this.open();
        },
        
        /**
         * Deactivates the menu.
         */
        deactivate: function() {
            this.statusActive = false;
            this.close();
            // loop children
            this.each(function(child) {
                if (child.deactivate) {
                    child.deactivate();
                }
            });
        },
        
        closeHover: function () {
            this.deactivate();
        },
        
        /**
         * Defines the default callback (dom:click).
         * @param {Function} callback The callback function
         */
        onClick: function(callback) {
            this.onClickCallback = callback;
        },
        
        hide: function() {
            this._super();
            this.deactivate();
        },
        
        hideHovers: function () {
            this.deactivate();
        },
        
        /**
         * Opens the menu item's menu strip (if it has children)
         */
        open: function (delay) {
            // propagate
            this.propagateOpenHover(this);
            // has children?
            if (this.hasChildren()) {
                // strip initialized?
                if (!this.menuStrip) {
                    this.menuStrip = new ox.gui.MenuStrip();
                    this.menuStrip.parent = this;
                }
                if (!this.menuStrip.statusOpen) {
                    // adjust delay
                    delay = delay || 50;
                    // stop running timeout
                    if (this.showTimeout != null) {
                        window.clearTimeout(this.showTimeout);
                    }
                    // show strip
                    var self = this;
                    this.showTimeout = window.setTimeout(function() {
                        // open strip
                        self.menuStrip.open();
                    }, delay);
                }
            }
        },
        
        /**
         * Closes the menu item's menu strip.
         */
        close: function() {
            if (this.menuStrip) {
                this.menuStrip.close();
            }
        },
        
        /**
         * Set state: checked (for checkbox behavior)
         */
        setChecked: function (state) {
            // changed?
            if (state !== this.stateChecked) {
                // is radio to be checked?
                if (state === true && this.options.behavior === "radio") {
                    // disable other items
                    var i = 0, items = radios[this.options.group] || [], $l = items.length, item;
                    for (; i < $l; i++) {
                        item = items[i];
                        if (item !== this) {
                            item.setChecked(false);
                        }
                    }
                }
                // set new state
                this.stateChecked = state;
                // paint
                this.paintState();
            }
        },
        
        paintState: function () {
            
            // invisible?
            if (this.statusVisible === false) {
                // done
                return;
            }
            
            // get node
            var node = $(this.dom.node);
            
            // checked?
            if (this.stateChecked === true) {
                // add class (overwrites disabled)
                node.addClass("menu-item-checked");
                node.removeClass("menu-item-disabled");
                return;
            } else {
                node.removeClass("menu-item-checked");
            }
            
            // disabled?
            if (this.statusEnabled === false) {
                // add class
                node.addClass("menu-item-disabled");
            } else {
                node.removeClass("menu-item-disabled");
            }
        },
        
        /**
         * Sets the icon dynamically.
         * @param {String} icon The new URL of the icon, relative to a theme.
         */
        setIcon: function(icon) {
            this.icon = icon;
            if (this.dom.icon) this.dom.icon.src = this.getPath(icon);
        },
        
        /**
         * Sets the title dynamically.
         * @param {String} title The new title of the menu item.
         */
        setTitle: function(title) {
            this.options.title = title;
            this.text = title;
            this.paint();
        }
    });
    
}());

//------------------------------------------------------------------------------

ox.gui.MainMenu = ox.gui.Menu.extend({
/** @lends ox.gui.MainMenu.prototype */
    
    getClass: function() { return "ox.gui.MainMenu"; },
    
    /** 
     * A main menu
     * @constructs
     * @extends ox.gui.Menu
     * @param {String} id An optional unique id for the widget.
     */
    construct: function(id) {
        
        // call super class constructor (inherit from Menu)
        this._super(id);
        
        // set layout manager
        this.setLayout(new MenuBarLayout(this));
    },
    
    add: function(item) {
        // set align
        item.menuStripAlign = "bottom";
        // call super class method
        ox.gui.Container.prototype.add.call(this, item);
    }
});


//------------------------------------------------------------------------------

(function () {
    
    ox.gui.MenuStrip = ox.gui.Menu.extend({
    /** @lends ox.gui.MenuStrip.prototype */
        
        getClass: function() { return "ox.gui.MenuStrip"; },
        
        /** 
         * A menu strip
         * @constructs
         * @extends ox.gui.Menu
         * @param {String} id An optional unique id for the widget.
         */
        construct: function(id) {
            
            this.clonable = true;
            this.isHover = true;
            
            // call super class constructor
            this._super(id);
            
            // set layout manager
            this.setLayout(new ox.gui.MenuStripLayout(this));
            
            this.statusOpen = false;
            this.statusActive = true; // always active
            
            this.inDOM = false;
            
            // mouse event handlers
            var self = this;
            this.fnLocalEvent = $.proxy(function (e) {
                e.stopPropagation();
            }, this);
            this.fnGlobalEvent = $.proxy(function (e) {
                this.parent.deactivate();
                this.close(); // make sure it really closes in all cases
            }, this);
            
            var overlays = $();
            
            this.fixIFrames = function (flag) {
                // show?
                if (flag) {
                    // get all visible iframes
                    var iframes = $("iframe:visible"), i = 0, $i = iframes.length;
                    var iframe, offset, width, height, fix;
                    // loop
                    for (; i < $i; i++) {
                        // get
                        iframe = iframes.eq(i);
                        // get dimensions
                        offset = iframe.offset();
                        width = iframe.outerWidth();
                        height = iframe.outerHeight();
                        // add overlay
                        overlays = overlays.add($("<div/>").css({
                            position: "absolute",
                            top: offset.top,
                            left: offset.left,
                            width: width,
                            height: height,
                            zIndex: 5000,
                            backgroundColor: "white",
                            opacity: 0.001
                        }).text(" ").appendTo(document.body));
                    }
                } else {
                    // hide overlays
                    overlays.remove();
                    overlays = $();
                }
            };
        },
        
        nodeInit: function() {
            this.addCSSClass("menustrip");
        },
        
        nodeListeners: function() {
            // add listener
            this.addListener("dom:resize", window, function(e) {
                this.parent.deactivate();
            });
        },
        
        getChildren: function() { return this.parent.getChildren(); },
        
        /**
         * @private
         */
        setPosition: function() {
            // get parent
            var parent = this.parent;
            // get position of current menu
            var parenNode = $(parent.dom.node);
            var parentPos = parenNode.offset();
            var parentDim = {
                width: parenNode.outerWidth(true),
                height: parenNode.outerHeight(true)
            };
            // get menu dimensions
            var node = $(this.dom.node);
            var dim = {
                width: node.outerWidth(true) || 0,
                height: node.outerHeight(true) || 0
            };
            // get maximal positions
            var maxLeft = $(document).width() - dim.width - 10, maxTop = $(document).height() - dim.height - 10;
            // adjust position of menu strip
            var style = this.dom.node.style;
            switch (parent.menuStripAlign) {
                case "right":
                    style.left = Math.max(0, Math.min(parentPos.left + parentDim.width - 5, maxLeft)) + "px";
                    style.top  = Math.max(0, Math.min(parentPos.top, maxTop)) + "px";
                    break;
                case "bottom":
                    style.left = Math.max(0, Math.min(parentPos.left, maxLeft)) + "px";
                    style.top  = Math.max(0, Math.min(parentPos.top + parentDim.height, maxTop)) +"px";
                    break;
            }
            // be on top
            var p = parent.parent;
            if (p && p.menuStrip) {
                style.zIndex = (p.menuStrip.dom.node.style.zIndex || 10001) + 1;
            } else {
                style.zIndex = 10001;
            }
        },
        
        /**
         * Opens the menu strip
         */
        open: function (delay) {
            if (!this.statusOpen) {
                // set status
                this.statusOpen = true;
                // has children?
                if (this.hasChildren()) {
                    // in DOM?
                    if (!this.inDOM) {
                        // add to DOM
                        document.body.appendChild(this.dom.node);
                    }
                    // apply iframe fix
                    this.fixIFrames(true);
                    // set strip position?
                    this.setPosition();
                    this.validate(true);
                    // bind mouse event
                    $(this.dom.node).bind("click", this.fnLocalEvent);
                    $(document.body).bind("click", this.fnGlobalEvent);
                    // show strip
                    this.dom.node.style.display = "block";
                    this.dom.node.style.visibility = "visible";
                    // register
                    this.hasHover(true);
                }
            }
        },
        
        /**
         * Closes the menu strip
         */
        close: function () {
            this.statusOpen = false;
            // remove iframe fix
            this.fixIFrames(false);
            // unbind mouse event
            $(this.dom.node).unbind("click", this.fnLocalEvent);
            $(document.body).unbind("click", this.fnGlobalEvent);
            // hide
            this.dom.node.style.display = "none";
            this.dom.node.style.visibility = "hidden";
            this.hasHover(false);
        }
    });
    
}());

//------------------------------------------------------------------------------


ox.gui.ContextMenu = ox.gui.Menu.extend({
/** @lends ox.gui.ContextMenu.prototype */
    
    getClass: function() { return "ox.gui.ContextMenu"; },
    
    /** 
     * A context menu / right mouse button menu.
     * @constructs
     * @extends ox.gui.Menu
     * @param {String} id An optional unique id for the widget.
     */
    construct: function(id) {
        // call super class constructor (inherit from Menu)
        this._super(id);
    },
    
    paint: function() {
        this.dom.node.innerHTML = "[ContextMenu] " + this.id;
    }
});


//------------------------------------------------------------------------------

/** @ignore */
function MenuBarLayout(targetDOMNode) {
    this.targetDOMNode = targetDOMNode || null;
}

/** @ignore */
MenuBarLayout.prototype.doLayout = function(container) {
    // set target DOM node
    var targetDOMNode = this.targetDOMNode || container.dom.node;
    // clear
    targetDOMNode.innerHTML = "";
    var table = document.createElement("table");
    var tbody = document.createElement("tbody"); // IE needs an explicit
                                                    // table body
    var tr = document.createElement("tr");
    // loop children
    for(var i = 0; i < container.children.length; i++) {
        var child = container.children[i];
        // add to DOM & show
        var td = document.createElement("td");
        child.setParentDOMNode(td);
        child.validate();
        tr.appendChild(td);
    }
    tbody.appendChild(tr);
    table.appendChild(tbody);
    targetDOMNode.appendChild(table);
}


//------------------------------------------------------------------------------


ox.gui.MenuStripLayout = ox.gui.LayoutManager.extend({
/** @lends ox.gui.MenuStripLayout.prototype */
    
    getClass: function () { return "ox.gui.MenuStripLayout"; },
    
    /** 
     * A menu strip layout manager.
     * @extends ox.gui.LayoutManager
     * @constructs
     * @param {ox.gui.Container} The container widget that should be managed
     * by the layout manager.
     */
    construct: function (container) {
        this._super(container);
    },
    
    doLayout: function () {
        var target = this.container.dom.node;
        // loop children
        this.container.each(function (child) {
            child.validate();
            target.appendChild(child.dom.node);
        });
    }
});
