/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 *
 */
// #ifdef __AMLHBOX || __AMLVBOX || __INC_ALL

/**
 * A container that stacks its children vertically.
 * 
 * #### Example
 * 
 * ```xml, demo
 * <a:application xmlns:a="http://ajax.org/2005/aml">
 *   <!-- startcontent -->
 *   <a:vbox width="100">
 *       <a:button height="28" edge="5">Button 1</a:button>
 *       <a:button height="28" edge="5">Button 2</a:button>
 *       <a:button height="28" edge="5">Button 3</a:button>
 *   </a:vbox>
 *   <!-- endcontent -->
 * </a:application>
 * ```
 * 
 * @class apf.vbox
 * @layout
 * @define vbox 
 * 
 * 
 * @see element.hbox
 * 
 * @author      Ruben Daniels (ruben AT ajax DOT org)
 * @version     %I%, %G%
 * @since       0.9
 * @layout
 */
/**
 * A container that stacks its children horizontally.
 * 
 * #### Example
 * 
 * ```xml, demo
 * <a:application xmlns:a="http://ajax.org/2005/aml">
 *   <!-- startcontent -->
 *   <a:hbox height="29" width="300" lean="right" margin="5 0 0 0">
 *       <a:button width="100" edge="5">Button 1</a:button>
 *       <a:button width="100">Button 2</a:button>
 *       <a:button width="100">Button 3</a:button>
 *   </a:hbox>
 *   <!-- endcontent -->
 * </a:application>
 * ```
 * 
 * 
 * #### Remarks
 * 
 * Firefox has some issues:
 * 
 * 1. Sometimes it's necessary to put a fixed width to have it calculate the right
 * height value.
 * 2. Using flex="1" on non fixed height/width tree's will give unexpected results.
 * 
 *
 *
 * @class apf.hbox
 * @inherits apf.GuiElement
 * @define hbox
 * @layout
 * 
 * @see element.vbox
 * @author      Ruben Daniels (ruben AT ajax DOT org)
 * @version     %I%, %G%
 * @since       0.9
 */
apf.hbox = function(struct, tagName){
    this.$init(tagName || "hbox", apf.NODE_VISIBLE, struct);
};
apf.vbox = function(struct, tagName){
    this.$init(tagName || "vbox", apf.NODE_VISIBLE, struct);
};

(function(){
    this.minwidth    = 0;
    this.minheight   = 0;
    
    // *** Properties and Attributes *** //

    this.$focussable = false;
    this.$useLateDom = true; 
    this.$box        = true;
    this.$layout     = true;
    
    var MOZSTACK = "-moz-stack";
    var input    = {"INPUT":1, "SELECT":1, "TEXTAREA":1}

    /**
     * @attribute {String}  [padding=2]      Sets or gets the space between each element.
     */
    /**
     * @attribute {Boolean} reverse      Sets or gets whether the sequence of the elements is in reverse order.
     */
    /**
     * @attribute {String}  [edge="5,5,5,5"]         Sets or gets the space between the container and the elements, space seperated in pixels for each side. Similar to CSS in the sequence (_.i.e._. `top right bottom left`).
     * 
     * #### Example
     * 
     * ```xml
     *  <a:vbox edge="10 10 40 10" />
     * ```
     */
    // @todo Doc
    /**
     * @attribute {String} pack     
     *   
     *  Possible values include:
     * 
     *   - `"start"`:
     *   - `"center"`:
     *   - `"end"`:
     */
    /**
     * @attribute {Boolean} align
     *   
     *  Possible values include:
     * 
     *   - `"start"`:
     *   - `"center"`:
     *   - `"end"`:
     *   - `"stretch"`:
     */
    this.$booleanProperties["splitters"] = true;
    this.$supportedProperties.push("padding", "reverse", "edge", "pack", "align", "splitters");
    
    this.$propHandlers["padding"] = function(value){
        this.padding = parseInt(value);
        
        var node, nodes = this.childNodes, elms = [];
        for (var i = 0, l = nodes.length; i < l; i++) {
            if ((node = nodes[i]).nodeFunc == apf.NODE_VISIBLE 
              && node.$ext && node.visible !== false)
                elms.push(node);
        }
        
        if (!elms.length)
            return;

        for (var last, b, el, i = elms.length - 2; i >= 0; i--) {
            b = (el = elms[i]).margin && apf.getBox(el.margin) || [0,0,0,0];
            
            if ((!last || !last.$splitter) && !el.$splitter) {
                b[this.$vbox ? 2 : 1] += this.padding;

                if (!apf.hasFlexibleBox && i != 0 && this.align == "stretch" && this.$vbox)
                    b[0] += this.padding;
            }
            
            el.$ext.style.margin = b.join("px ") + "px";
            last = el;
        }
        b = (el = elms[elms.length - 1]).margin && apf.getBox(el.margin) || [0,0,0,0];
        el.$ext.style.margin = b.join("px ") + "px";
        
        if (!apf.hasFlexibleBox)
            this.$resize();
    }
    
    this.$propHandlers["reverse"]  = function(value){
        if (apf.hasFlexibleBox)
            this.$int.style[apf.CSSPREFIX + "BoxDirection"] = value ? "reverse" : "normal";
        else {
            //@todo
        }
    };
    
    this.$propHandlers["edge"]  = function(value){
        var el = !apf.hasFlexibleBox && this.$vbox ? this.$ext : this.$int;
        el.style.padding = (this.$edge = apf.getBox(value)).join("px ") + "px";
        
        if (!apf.hasFlexibleBox)
            this.$resize();
    };
    
    this.$propHandlers["pack"]  = function(value){
        if (apf.hasFlexibleBox)
            this.$int.style[apf.CSSPREFIX + "BoxPack"] = value || "start";
        else if (this.$amlLoaded) {
            if (this.$vbox) {
                this.$int.style.verticalAlign = value == "center" ? "middle" : (value == "end" ? "bottom" : "top");
            }    
            else {
                this.$int.style.textAlign = "";
                
                var nodes = this.childNodes;
                for (var i = 0, l = nodes.length; i < l; i++) {
                    if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || !node.$amlLoaded) //|| node.visible === false 
                        continue;

                    node.$ext.style.textAlign = apf.getStyle(node.$ext, "textAlign") || "left";
                }
                
                this.$int.style.textAlign = value == "center" ? "center" : (value == "end" ? "right" : "left");
            }
        }
    };
    
    //@todo change overflow when height/width changes depending on $vbox
    
    this.$propHandlers["align"] = function(value){
        if (apf.hasFlexibleBox) {
            this.$int.style[apf.CSSPREFIX + "BoxAlign"] = value || "stretch";
            
            if (apf.isGecko)
                this.$int.style.overflow = "visible";

            //@todo this should probably be reinstated
            var stretch = !value || value == "stretch";
            var nodes = this.childNodes;
            var size  = this.$vbox ? "width" : "height";
            
            var isInFixed = false, loopNode = this;
            while(!isInFixed && loopNode) {
                isInFixed = loopNode[size] || loopNode.anchors || (loopNode.$vbox ? loopNode.top && loopNode.bottom : loopNode.left && loopNode.right);
                if (!loopNode.flex)
                    break;
                loopNode = loopNode.parentNode || loopNode.$parentNode;
            }
            
            for (var i = 0, l = nodes.length; i < l; i++) {
                if (!(node = nodes[i]).$ext || node.$ext.nodeType != 1)
                    continue;

                //(this[size] || this.anchors || (this.$vbox ? this.top && this.bottom : this.left && this.right)
                if (stretch && !node[size]) //(node.$altExt || 
                    node.$ext.style[size] = apf.isGecko && (this.flex || node.flex) 
                        ? (isInFixed ? "1px" : "auto")
                        : (apf.isWebkit && input[node.$ext.tagName] 
                            ? "100%" 
                            : (false && apf.isWebkit && node[this.$vbox ? "minwidth" : "minheight"] && this.flex //nasty bug fix
                                ? "0px"
                                : "auto"));//(apf.isWebkit && node.flex && size == "height" ? "100%" : "auto"); // && (this.flex && node.flex)
                else if (node[size])
                    handlers["true"][size].call(node, node[size]);
            }
        }
        else if (this.$amlLoaded) {
            var stretch = !value || value == "stretch";
            
            if (!this.$vbox) {
                var nodes = this.childNodes;
                for (var i = 0, l = nodes.length; i < l; i++) {
                    if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || !node.$amlLoaded) //|| node.visible === false 
                        continue;
                    
                    node.$ext.style.verticalAlign = value == "center" ? "middle" : (value == "end" ? "bottom" : "top");
                }
            }
            else {
                var el = !apf.hasFlexibleBox && this.$vbox ? this.$ext : this.$int;
                el.style.textAlign = "";
                
                var node, nodes = this.childNodes;
                for (var i = 0, l = nodes.length; i < l; i++) {
                    if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || !node.$amlLoaded) //|| node.visible === false 
                        continue;

                    if (node.visible !== false) {
                        node.$ext.style.display   = value == "stretch" ? "block" : apf.INLINE;
                        node.$br.style.display    = value == "stretch" ? "none" : "";
                        
                        if (apf.needZoomForLayout)
                            node.$ext.style.zoom = 1;
                    }
                    node.$ext.style.textAlign = apf.getStyle(node.$ext, "textAlign") || "left";
                }
                
                el.style.textAlign = value == "center" ? "center" : (value == "end" ? "right" : "left");
            }
        }
    };
    
    function visibleHandler(e){
        //#ifdef __LAYOUT_ENABLE_SPLITTERS
        if (this.parentNode.splitters && !this.$splitter) {
            if (!e.value) {
                if (this.nextSibling && this.nextSibling.$splitter)
                    this.nextSibling.removeNode();
                else if (this.previousSibling && this.previousSibling.$splitter)
                    this.previousSibling.removeNode();
            }
            else {
                var isLast = isLastVisibleChild(this);
                if (!isLast) {
                    if (!this.nextSibling.$splitter && !this.nextSibling.nosplitter
                      && !isFirstVisibleChild(this) && !this.nosplitter) {
                        this.parentNode.insertBefore(
                            this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), 
                            this.nextSibling);
                    }
                }
                else if (this.previousSibling && !this.previousSibling.$splitter
                   && !this.previousSibling.nosplitter) {
                    this.parentNode.insertBefore(
                        this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), 
                        this);
                }
            }
        }
        //#endif
        
        //@todo this can be more optimized by calcing if it WAS the last vis child.
        if (this.parentNode.$propHandlers["padding"]) {// && isLastVisibleChild(this)) {
            this.parentNode.$propHandlers["padding"]
                .call(this.parentNode, this.parentNode.padding);
        }
        
        apf.layout.forceResize(this.parentNode.$int);
        
        if (apf.hasFlexibleBox) {
            if (this.$altExt)
                this.$altExt.style.display = e.value 
                    ? (apf.isGecko ? MOZSTACK : apf.CSSPREFIX2 + "-box") 
                    : "none";
            return;
        }
        
        if (e.value) {
            this.$ext.style.display    = this.parentNode.$vbox 
                && this.parentNode.align == "stretch" ? "block" : apf.INLINE;
            if (apf.needZoomForLayout)
                this.$ext.style.zoom = 1;
            if (this.$br)
                this.$br.style.display = this.parentNode.align == "stretch" ? "none" : "";
        }
        else {
            if (this.$br)
                this.$br.style.display = "none";
        }

        this.parentNode.$resize();
    }
    
    function resizeHandler(){
        if (!this.flex) {
            if (this.$isRszHandling || this.$lastSizeChild && 
              this.$lastSizeChild[0] == this.$ext.offsetWidth && 
              this.$lastSizeChild[1] == this.$ext.offsetHeight)
                return;
            
            /*if (this.$skipResizeOnce)
                delete this.$skipResizeOnce;
            else*/
                this.parentNode.$resize(true);
            
            this.$lastSizeChild = [this.$ext.offsetWidth, this.$ext.offsetHeight];
        }
    }
    
    var handlers = {
        //Handlers for flexible box layout
        "true" : {
            "optimize" : function(value){
                this.optimize = apf.isTrue(value);
            },
            
            "width" : function(value){
                //@todo this should check the largest and only allow that one
                //if (this.parentNode.$vbox && this.parentNode.align == "stretch")
                    //return;

                (this.$altExt || this.$ext).style.width = !apf.isNot(value) 
                    ? (parseFloat(value) == value 
                        ? value + "px"
                        : value)
                    : "";
            },
            
            "height" : function(value){
                //@todo this should check the largest and only allow that one
                //if (!this.parentNode.$vbox && this.parentNode.align == "stretch")
                    //return;

                (this.$altExt || this.$ext).style.height = !apf.isNot(value) 
                    ? (parseFloat(value) == value 
                        ? value + "px"
                        : value)
                    : (apf.isGecko && this.flex && this.parentNode.$vbox ? "auto" : "");
            },
            
            "margin" : function(value){
                var b = apf.getBox(value);
                if (!isLastVisibleChild(this))
                    b[this.parentNode.$vbox ? 2 : 1] += this.parentNode.padding;
                this.$ext.style.margin = b.join("px ") + "px";
            },
            
            "flex" : function(value){
                this.flex = value = parseInt(value);
                if (value) {
                    if (!this.optimize && !this.$altExt) {
                        this.$altExt = this.$ext.ownerDocument.createElement("div");
                        this.parentNode.$int.replaceChild(this.$altExt, this.$ext);
                        this.$altExt.appendChild(this.$ext);
                        this.$altExt.style[apf.CSSPREFIX + "BoxSizing"] = "border-box";
                        this.$altExt.style.display = apf.CSSPREFIX2 + "-box";
                        this.$altExt.style[apf.CSSPREFIX + "BoxOrient"] = "vertical";
                        this.$ext.style[apf.CSSPREFIX + "BoxFlex"]   = 1;
                        var size = this.parentNode.$vbox ? "height" : "width";
                        //var osize = this.parentNode.$vbox ? "width" : "height";
                        
                        if (apf.isWebkit) {
                            if (!this.preventforcezero)
                                this.$altExt.style[size] = "0px";
                        }
                        else if (apf.isGecko) {
                            this.$altExt.style[size] = "0px";
                            
                            //Possible hack to not do this for $box elements 
                            if (!this.$box)
                                this.$altExt.style.overflow = "hidden"; //Gecko
                            if (apf.getStyle(this.$ext, "overflow") == "visible")
                                this.$ext.style.overflow = "hidden"; //Gecko
                            this.$ext.style[size] = "1px";
                        
                            this.$altExt.style.minHeight = this.$ext.style.minHeight;
                            this.$altExt.style.maxHeight = this.$ext.style.maxHeight;
                            this.$altExt.style.minWidth = this.$ext.style.minWidth;
                            this.$altExt.style.maxWidth = this.$ext.style.maxWidth;
                        }
                    }
                    
                    (this.$altExt || this.$ext).style[apf.CSSPREFIX + "BoxFlex"] = parseInt(value) || 1;
                }
                else if (this.$altExt) {
                    this.parentNode.$int.replaceChild(this.$ext, this.$altExt);
                    this.$ext.style[apf.CSSPREFIX + "BoxFlex"] = "";
                    if (apf.isGecko)
                        this.$ext.style.overflow = "";
                    delete this.$altExt;
                }
            }
        },
        
        //Handlers for older browsers
        "false" : {
            "width" : function(value){
                //@todo this should check the largest and only allow that one
                //if (this.parentNode.$vbox && this.parentNode.align == "stretch")
                    //return;
              
                this.$ext.style.width = value
                    ? (parseFloat(value) == value 
                        ? Math.max(0, value - apf.getWidthDiff(this.$ext)) + "px"
                        : value)
                    : "";
            },
            
            "height" : function(value){
                //@todo this should check the largest and only allow that one
                //if (this.parentNode.localName == "hbox" && this.parentNode.align == "stretch")
                    //return;
      
                this.$ext.style.height = value 
                    ? (parseFloat(value) == value 
                        ? Math.max(0, value - apf.getHeightDiff(this.$ext)) + "px"
                        : value)
                    : "";
            },
            
            "margin" : function(value){
                var b = apf.getBox(value);
                if (this.padding) {
                    if (!isLastVisibleChild(this))
                        b[this.parentNode.$vbox ? 2 : 1] += this.padding;
                    if (this != this.parentNode.firstChild && this.parentNode.align == "stretch" && this.parentNode.$vbox) //@todo
                        b[0] += this.padding;
                }
                this.$ext.style.margin = b.join("px ") + "px";
            },
            
            "flex" : function(value){
                this.flex = parseInt(value);
                if (this.$amlLoaded)
                    this.parentNode.$resize(true);
            }
        }
    }
    
    function isFirstVisibleChild(amlNode){
        var firstChild = amlNode.parentNode.firstChild;
        while (firstChild && (firstChild.nodeFunc != apf.NODE_VISIBLE 
          || firstChild.visible === false 
          || firstChild.visible == 2 && apf.isFalse(firstChild.getAttribute("visible")))) {
            firstChild = firstChild.nextSibling;
        }
        
        return firstChild && firstChild == amlNode;
    }
    
    function isLastVisibleChild(amlNode){
        var lastChild = amlNode.parentNode.lastChild;
        while (lastChild && (lastChild.nodeFunc != apf.NODE_VISIBLE 
          || lastChild.visible === false 
          || lastChild.visible == 2 && apf.isFalse(lastChild.getAttribute("visible")))) {
            lastChild = lastChild.previousSibling;
        }
        
        return lastChild && lastChild == amlNode;
    }
    
    //@todo move this to enableTable, disableTable
    this.register = function(amlNode, insert){
        if (amlNode.$altExt) //@todo hack, need to re-arch layouting
            return;

        amlNode.$propHandlers["left"]   = 
        amlNode.$propHandlers["top"]    = 
        amlNode.$propHandlers["right"]  = 
        amlNode.$propHandlers["bottom"] = apf.K;

        var propHandlers = handlers[apf.hasFlexibleBox];
        for (var prop in propHandlers) {
            amlNode.$propHandlers[prop] = propHandlers[prop];
        }

        if (amlNode.nodeFunc == apf.NODE_VISIBLE) {
            if (apf.hasFlexibleBox) {
                //if (apf.isGecko && apf.getStyle(amlNode.$ext, "display") == "block")
                    //amlNode.$ext.style.display = MOZSTACK; //@todo visible toggle
                
                //input elements are not handled correctly by firefox and webkit
                if (amlNode.$ext.tagName == "INPUT" || apf.isWebkit && input[amlNode.$ext.tagName]) {
                    var doc = amlNode.$ext.ownerDocument;
                    amlNode.$altExt = doc.createElement("div");
                    amlNode.parentNode.$int.replaceChild(amlNode.$altExt, amlNode.$ext);
                    amlNode.$altExt.style[apf.CSSPREFIX + "BoxSizing"] = "border-box";
                    amlNode.$altExt.appendChild(amlNode.$ext);
                    
                    if (apf.isWebkit) {
                        var d = apf.getDiff(amlNode.$ext);
                        //amlNode.$altExt.style.padding = "0 " + d[0] + "px " + d[1] + "px 0";
                        amlNode.$altExt.style.height = "100%";
                        amlNode.$altExt.style.width = "0";
                        amlNode.$altExt.style.lineHeight = 0;
                        amlNode.$altExt.style.margin  = "-1px 0 0 0";
                        amlNode.$ext.style.width  = "100%";
                        amlNode.$ext.style.height  = "100%";
                        amlNode.$ext.style.top = "1px";
                        amlNode.$ext.style.position = "relative";
                    }
                    else {
                        amlNode.$altExt.style.display = apf.CSSPREFIX2 + "-box";
                        amlNode.$altExt.style[apf.CSSPREFIX + "BoxOrient"] = "horizontal";
                        amlNode.$altExt.style[apf.CSSPREFIX + "BoxAlign"]  = "stretch";
                        amlNode.$ext.style[apf.CSSPREFIX + "BoxFlex"] = 1;
                    }
                }
                else {
                    if (apf.getStyle(amlNode.$ext, "display") == "inline")
                        amlNode.$ext.style.display = "block"; //@todo undo
                    //This is nice for positioning elements in the context of an hbox/vbox
                    //if (apf.getStyle(amlNode.$ext, "position") == "absolute")
                        //amlNode.$ext.style.position = "relative"; //@todo undo
                }
                
                amlNode.$ext.style[apf.CSSPREFIX + "BoxSizing"] = "border-box";
            }
            else {
                if (this.$vbox) {
                    amlNode.$br = this.$int.insertBefore(amlNode.$ext.ownerDocument.createElement("br"), amlNode.$ext.nextSibling);
                    if (amlNode.visible === false)
                        amlNode.$br.style.display = "none";
                }
                else {
                    if (amlNode.visible !== false) {
                        amlNode.$ext.style.display = apf.INLINE;
                        if (apf.needZoomForLayout)
                            amlNode.$ext.style.zoom = 1;
                    }
                    this.$int.style.whiteSpace = "";
                    amlNode.$ext.style.whiteSpace = apf.getStyle(amlNode.$ext, "whiteSpace") || "normal";
                    this.$int.style.whiteSpace = "nowrap";
                }
                
                this.$int.style.fontSize = "0";
                if (!amlNode.$box) {
                    var fontSize = apf.getStyle(amlNode.$ext, "fontSize");
                    if (fontSize == "0px") {
                        amlNode.$ext.style.fontSize = "";
                        var pNode = this.$int.parentNode;
                        while(apf.getStyle(pNode, "fontSize") == "0px") {
                            pNode = pNode.parentNode;
                        }
                        fontSize = apf.getStyle(pNode, "fontSize");
                    }
                    amlNode.$ext.style.fontSize = fontSize;//apf.getStyle(amlNode.$ext, "fontSize") || "normal";
                }
                
                amlNode.addEventListener("resize", resizeHandler);
            }
            
            amlNode.addEventListener("prop.visible", visibleHandler);
    
            this.$noResize = true;
            
            if (amlNode.height)
                propHandlers.height.call(amlNode, amlNode.height);
            if (amlNode.width)
                propHandlers.width.call(amlNode, amlNode.width);
            if (amlNode.margin)
                propHandlers.margin.call(amlNode, amlNode.margin);
            if (amlNode.flex)
                propHandlers.flex.call(amlNode, amlNode.flex);    
                
            //Ie somehow sets the visible flags in between registration
            var isLast = isLastVisibleChild(amlNode); //apf.isIE ? this.lastChild == amlNode : 
            if (isLast || insert) {
                this.$propHandlers["padding"].call(this, this.padding);
                this.$propHandlers["align"].call(this, this.align);
                
                if (!apf.hasFlexibleBox)
                    this.$propHandlers["pack"].call(this, this.pack);
                    
                if (amlNode.visible !== false) //insert && - removed because for new nodes that are being attached to the tree insert is not set
                    visibleHandler.call(amlNode, {value: true});
                
                //@todo this needs more work
                if (insert && amlNode.previousSibling) {
                    var prev = amlNode.previousSibling;
                    while (prev && (prev.nodeType != 1 || prev.localName == "splitter"))
                        prev = prev.previousSibling;
                    if (prev)
                        visibleHandler.call(prev, {value: true});
                }
            }
            //#ifdef __LAYOUT_ENABLE_SPLITTERS
            else if (this.splitters && !amlNode.$splitter && amlNode.visible !== false && !amlNode.nosplitter) {
                if (amlNode.$ext.nextSibling != (amlNode.nextSibling 
                  && (amlNode.nextSibling.$altExt || amlNode.nextSibling.$ext))) {
                    var _self = this;
                    setTimeout(function(){
                        _self.insertBefore(
                            _self.ownerDocument.createElementNS(apf.ns.aml, "splitter"), 
                            amlNode.nextSibling);
                    });
                }
                else {
                    this.insertBefore(
                        this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), 
                        amlNode.nextSibling);
                }
            }
            //#endif
        
            delete this.$noResize;
            
            if (!apf.hasFlexibleBox && isLast)
                this.$resize();
        }
    }
    
    this.unregister = function(amlNode){
        if(!amlNode.$propHandlers)
            return;
        
        amlNode.$propHandlers["left"]   = 
        amlNode.$propHandlers["top"]    = 
        amlNode.$propHandlers["right"]  = 
        amlNode.$propHandlers["bottom"] = null;
        
        var propHandlers = handlers[apf.hasFlexibleBox];
        for (var prop in propHandlers) {
            delete amlNode.$propHandlers[prop];
        }
        
        //Clear css properties and set layout
        if (amlNode.nodeFunc == apf.NODE_VISIBLE) {
            if (amlNode.flex) {
                var flex = amlNode.flex;
                propHandlers.flex.call(amlNode, 0);
                amlNode.flex = flex;
            }
            
            if (apf.hasFlexibleBox) {
                amlNode.$ext.style[apf.CSSPREFIX + "BoxSizing"] = "";
                
                if (apf.isGecko) {
                    this.$int.style.overflow = "visible";
                
                    if (amlNode.$ext.style.display == "block")
                        amlNode.$ext.style.display = "";
                }
            }
            else {
                amlNode.$ext.style.verticalAlign = "";
                amlNode.$ext.style.textAlign = "";
                amlNode.$ext.style.whiteSpace = "";
                //amlNode.$ext.style[apf.CSSFLOAT] = "";
                
                if (amlNode.$br) {
                    amlNode.$br.parentNode.removeChild(amlNode.$br);
                    delete amlNode.$br;
                    //amlNode.$ext.style.fontSize = "";
                }
                
                amlNode.removeEventListener("resize", resizeHandler);
            }
            
            amlNode.removeEventListener("prop.visible", visibleHandler);
            
            amlNode.$ext.style.display = amlNode.visible ? "block" : "none";
            
            if (amlNode.margin)
                amlNode.$ext.style.margin = "";
            
            if (amlNode.width)
                amlNode.$ext.style.width = "";

            //#ifdef __LAYOUT_ENABLE_SPLITTERS
            if (this.splitters && !amlNode.$splitter) {
                if (amlNode.nextSibling && amlNode.nextSibling.$splitter)
                    amlNode.nextSibling.removeNode();
                if (isLastVisibleChild(amlNode) && amlNode.previousSibling 
                  && amlNode.previousSibling.$splitter)
                    amlNode.previousSibling.removeNode();
            }
            //#endif
        }
    }
    /*
         this.addEventListener("DOMNodeInsertedIntoDocument", function(e){
        this.register(this.parentNode);
    });
    */
    
    // *** DOM Hooks *** //
    
    this.addEventListener("DOMNodeRemoved", function(e){
        if (e.$doOnlyAdmin || e.currentTarget == this)
            return;

        if (e.relatedNode == this){
            this.unregister(e.currentTarget);
            //e.currentTarget.$setLayout();
        }
    });

    this.addEventListener("DOMNodeInserted", function(e){
        if (e.currentTarget == this) {
            if (this.visible)
                this.$ext.style.display = apf.CSSPREFIX2 + "-box"; //Webkit issue
            return;
        }
        
        if (e.currentTarget.nodeType != 1 
          || e.currentTarget.nodeFunc != apf.NODE_VISIBLE)
            return;

        if (e.relatedNode == this && !e.$isMoveWithinParent) {
            e.currentTarget.$setLayout(this.localName, true);
            
            if (e.currentTarget.$altExt) {
                
                return false;
            }
        }
    });

    function myVisibleHandler(e){
        if (e.value)
            this.$int.style.display = apf.CSSPREFIX2 + "-box";
    }
    
    function myHeightHandler(e){
        clearInterval(this.$heighttimer);
        if (e.value || this.align != "stretch") {
            delete this.$heighttimer;
        }
        else if (!this.$heighttimer) {
            var _self = this;
            this.$heighttimer = $setInterval(function(){
                if (_self.$amlDestroyed)
                    return;

                var nodes = _self.childNodes;
                for (var $int, i = 0, l = nodes.length; i < l; i++) {
                    if (!($int = (node = nodes[i]).$int || node.$container))
                        continue;

                    if (Math.min($int.scrollHeight, node["maxheight"] || 10000) > $int.offsetHeight)
                        return _self.$resize(true);
                }
                
                if (_self.flex)
                    clearInterval(this.$heighttimer);
            }, this.flex ? 1 : 500);
        }
    }
    
    this.$draw = function(){
        var doc = this.$pHtmlNode.ownerDocument;
        this.$ext = this.$pHtmlNode.appendChild(doc.createElement("div"));
        if (this.getAttribute("style"))
            this.$ext.setAttribute("style", this.getAttribute("style"));
        this.$ext.className = this.localName;

        this.$vbox = this.localName == "vbox";
        this.$int = apf.isGecko && !(this.parentNode 
          && "hbox|vbox".indexOf(this.parentNode.localName) > -1) 
          || !apf.hasFlexibleBox && this.$vbox //@todo reparenting for gecko needs some admin work
            ? this.$ext.appendChild(doc.createElement("div")) 
            : this.$ext;
        this.$ext.host = this;
        
        if (apf.isGecko && !(this.parentNode 
          && "hbox|vbox".indexOf(this.parentNode.localName) > -1)) {
            this.$int.style.width = "100%";
            this.$int.style.height = "100%";
        }
        else if (!apf.hasFlexibleBox && this.$vbox) {
            this.$int.style.display = apf.INLINE;
            if (apf.needZoomForLayout)
                this.$int.style.zoom = 1;
            this.$int.style.width   = "100%";
        }
        
        if (apf.hasFlexibleBox) {
            this.$display = "-" + apf.CSSPREFIX +"-box";
            
            this.$int.style.display = apf.CSSPREFIX2 + "-box";
            this.$int.style[apf.CSSPREFIX + "BoxOrient"] = this.localName == "hbox" ? "horizontal" : "vertical";
            if (apf.isGecko) //!webkit
                this.$int.style[apf.CSSPREFIX + "BoxSizing"] = "border-box";
            this.$int.style[apf.CSSPREFIX + "BoxAlign"]  = "stretch";
            
            this.addEventListener("prop.visible", myVisibleHandler);
        }
        else {
            if (!this.$vbox) {
                this.$int.style.whiteSpace = "nowrap";
                this.addEventListener("prop.height", myHeightHandler);
            }

            var spacer = (!apf.hasFlexibleBox && this.$vbox ? this.$ext : this.$int)
                            .appendChild(doc.createElement("strong"));
            spacer.style.height        = "100%";
            spacer.style.display       = apf.INLINE;
            if (apf.needZoomForLayout)
                spacer.style.zoom = 1;
            //spacer.style.marginLeft    = "-4px";
            spacer.style.verticalAlign = "middle";
            
            this.addEventListener("resize", this.$resize);
        }

        if (this.getAttribute("class")) 
            apf.setStyleClass(this.$ext, this.getAttribute("class"));
        
        this.$originalMin = [this.minwidth || 0,  this.minheight || 0];
    };
    
    this.$resize = function(force){
        if (!this.$amlLoaded || this.$noResize) // || apf.isIE7 && force !== true)
            return;

        //Protection for stretch re-resizing
        if (force !== true && this.$lastSize && 
          this.$lastSize[0] == this.$int.offsetWidth && 
          this.$lastSize[1] == this.$int.offsetHeight)
            return;
        
        if (!apf.window.vManager.check(this, this.$uniqueId, this.$resize))
            return;
        
        this.$noResize = true;
        this.$lastSize = [this.$int.offsetWidth, this.$int.offsetHeight];

        //this.$ext.style.border = "1px solid " + (["red", "green", "blue", "orange", "pink", "yellow"])[Math.round(Math.random() * 5)];
        
        /*if (this.$table.offsetWidth >= this.$ext.offsetWidth)
            this.$ext.style.minWidth = (this.minwidth = Math.max(0, this.$table.offsetWidth 
                - apf.getWidthDiff(this.$ext))) + "px";
        else {
            this.$ext.style.minWidth = "";
            this.minwidth = this.$originalMin[0];
        }

        if (this.$table.offsetHeight >= this.$ext.offsetHeight)
            this.$ext.style.minHeight = (this.minheight = Math.max(0, this.$table.offsetHeight 
                - apf.getHeightDiff(this.$ext))) + "px";
        else {
            this.$ext.style.minHeight = "";
            this.minheight = this.$originalMin[1];
        }*/
        
        //if (!this.$vbox) alert("here");

        var total    = 0;
        var size     = this.$vbox ? "width" : "height";
        var minsize  = this.$vbox ? "minWidth" : "minHeight";
        var osize    = this.$vbox ? "height" : "width";
        var scroll   = this.$vbox ? "scrollWidth"  : "scrollHeight";
        var offset   = this.$vbox ? "offsetWidth" : "offsetHeight";
        var ooffset  = this.$vbox ? "offsetHeight" : "offsetWidth";
        var getDiff  = this.$vbox ? "getWidthDiff" : "getHeightDiff";
        var ogetDiff = this.$vbox ? "getHeightDiff" : "getWidthDiff";
        var inner    = this.$vbox ? "getHtmlInnerWidth" : "getHtmlInnerHeight";
        var oinner   = this.$vbox ? "getHtmlInnerHeight" : "getHtmlInnerWidth";
        var borders  = this.$vbox ? "getVerBorders" : "getHorBorders";

        var nodes = this.childNodes, hNodes = [], fW = 0, max = 0;
        for (var node, i = 0; i < nodes.length; i++) {
            if ((node = nodes[i]).nodeFunc != apf.NODE_VISIBLE || node.visible === false || !node.$amlLoaded)
                continue;

            hNodes.push(node);
            if (!node[size]) {
                //if (!node.$skipResizeOnce) node.$skipResizeOnce = 1;
                //else node.$skipResizeOnce++;
                //node.$skipResizeOnce = 1
                //node.$ext.style[size] = ""; //@todo this is a sucky way of measuring
                var m = node.margin && apf.getBox(node.margin);
                if (m && this.$vbox) m.unshift();
                var mdiff = (m ? m[0] + m[2] : 0);
                max = Math.max(max, mdiff + Math.min(node.$ext[scroll] + apf[borders](node.$ext), node["max" + size] || 10000));
            }

            if (parseInt(node.flex))
                total += parseFloat(node.flex);
            else {
                var m = node.margin && apf.getBox(node.margin);
                if (m && !this.$vbox) m.shift();
                fW += node.$ext[ooffset] + (m ? m[0] + m[2] : 0); //this.padding + 
            }
        }
        if (!max && this[size]) {
            max = this[size] 
                //- (this.$vbox ? this.$edge[0] + this.$edge[2] : this.$edge[1] + this.$edge[3]);
                - apf[ogetDiff](this.$ext);
        }

        /*
             && (this[size] || this.flex)
        */
        if (this.align == "stretch") {
            //var hasSize = this[size] || this.flex;
            var l  = hNodes.length;
            var pH = max;//this.$int[offset] - apf[getDiff](this.$int);// - (2 * this.padding);
            for (var i = 0; i < l; i++) {
                node = hNodes[i];

                if (!node[size] && !this.$vbox || this.$vbox && input[node.$ext.tagName]) {
                    var m = node.margin && apf.getBox(node.margin);
                    if (m && this.$vbox) m.unshift();
                    var mdiff = (m ? m[0] + m[2] : 0);
                    
                    /*shouldClear = !this[size] && !this.flex && node.$ext.offsetHeight == (pH - mdiff);
                    if (shouldClear)
                        node.$ext.style[size] = "";
                    else
                        node.$ext.style[size] = Math.max(0, pH - apf[getDiff](node.$ext) - mdiff) + "px";
                    node.$setResizeHeight = !shouldClear;*/

                    //!this[size] && !this.flex
                    if (max && Math.min(node.$ext[scroll], node["max" + size] || 10000) != max)
                        node.$ext.style[size] = Math.max(0, max - apf[getDiff](node.$ext) - mdiff) + "px";
                    else
                        node.$ext.style[size] = "";
                    
                    /*node.$ext.style[size] = !this[size] && !this.flex && node.$ext.offsetHeight == pH - mdiff
                        ? ""
                        : Math.max(0, pH - apf[getDiff](node.$ext) - mdiff) + "px";*/
                }
            }
        }

        //Flexing
        if (total > 0) {
            if (this.$vbox)
                this.$int.style.height = "100%";
            this.$int.style.overflow = "hidden";
            
            var splitterCount = apf.n(this).children("a:splitter").length() * 2;
            
            var rW = this.$int[ooffset] - apf[ogetDiff](this.$int) - fW 
              - ((hNodes.length - 1 - splitterCount) * this.padding);// - (2 * this.edge);
            var lW = rW, done = 0;
            for (var i = 0, l = hNodes.length; i < l; i++) {
                if ((node = hNodes[i]).flex) {
                    var v = (i % 2 == 0 ? Math.floor : Math.ceil)((rW / total) * parseInt(node.flex));
                    done += parseInt(node.flex);
                    var m = node.margin && apf.getBox(node.margin);
                    if (m && !this.$vbox) m.shift();
                    node.$ext.style[osize] = Math.max(0, (done == total ? lW : v) - apf[ogetDiff](node.$ext) - (m ? m[0] + m[2] : 0)) + "px";
                    lW -= v;
                }
            }
        }
        else {
            if (this.$vbox)
                this.$int.style.height = "";
            this.$int.style.overflow = "";
        }
        
        this.$noResize = false;
        /*this.$noResize = true;
        var _self = this;
        setTimeout(function(){
            _self.$noResize = false;
        });*/
    }
    
    this.$loadAml = function(x){
        if (this.padding == undefined)
            this.padding = 0;
            //this.$propHandlers.padding.call(this, this.padding = 0);
        if (this.edge == undefined)
            this.$propHandlers.edge.call(this, this.edge = 0);
        if (this.pack == undefined)
            this.$propHandlers.pack.call(this, this.edge = "start");
        if (this.align == undefined)
            this.align = "stretch";
            //this.$propHandlers.align.call(this, this.align = "stretch");
        if (!apf.hasFlexibleBox && !this.$vbox && !this.height && this.align == "stretch")
            myHeightHandler.call(this, {});
    };
}).call(apf.vbox.prototype = new apf.GuiElement());

apf.hbox.prototype = apf.vbox.prototype;

apf.aml.setElement("hbox", apf.hbox);
apf.aml.setElement("vbox", apf.vbox);
// #endif
