define(function(require, module, exports) {
return function(apf) {
var $setTimeout  = setTimeout;
var $setInterval = setInterval;


/**
 * This abstraction is using for resizing block elements. Resizing is allowed
 * with square elements in vertical, horizontal or both planes. Symmetric
 * resizing is possible with SHIFT button.
 * 
 * @private
 * @default_private
 * @constructor
 * 
 * @author      Lukasz Lipinski
 * @version     %I%, %G%
 * @since       1.0
 * 
 */

apf.resize = function() {
    /** 
     *     {Boolean} scalex       resizing in horizontal plane, default is true
     *         Possible values:
     *         true   resizing in horizontal plane is allowed
     *         false  resizing in horizontal plane is not allowed
     *     {Boolean} scaley       resizing in vertical plane, default is true
     *         Possible values:
     *         true   resizing in vertical plane is allowed
     *         false  resizing in vertical plane is not allowed
     *     {Boolean} scaleratio   resizing in horizontal or vertical plane only is not allowed. Resizing in two dimensions plane at the same time is allowed.
     *         Possible values:
     *         true   resizing in two dimensions plane at the same time is allowed
     *         false  Resizing in two dimensions plane at the same time is not allowed
     *     {Number}  dwidth       the minimal horizontal size of Block element, default is 56 pixels
     *     {Number}  dheight      the minimal vertical size of Block element, default is 56 pixels
     */
    this.scales = {
        scalex: false,
        scaley: false,
        scaleratio: false,
        dwidth: 0,
        dheight: 0,
        snap: false,
        gridW: 48,
        gridH: 48
    };

    /*
     * html representation of resized block element
     */
    this.htmlElement;

    /**
     * store object representations of inputs elements
     */
    var squares = [];

    this.init = function() {
        squares = [
            new apf.resize.square("top",    "left",   this),
            new apf.resize.square("top",    "middle", this),
            new apf.resize.square("top",    "right",  this),
            new apf.resize.square("middle", "left",   this),
            new apf.resize.square("middle", "right",  this),
            new apf.resize.square("bottom", "left",   this),
            new apf.resize.square("bottom", "middle", this),
            new apf.resize.square("bottom", "right",  this)];
    };
    
    /**
     * Links block element with resize feature
     * 
     * @param {HTMLElement}   oHtml    html representation of block element
     * @param {Object}        scales   blocks scale settings
     */
    this.grab = function(oHtml, scales) {
        this.htmlElement = oHtml;
        this.scales = scales;

        if (!squares.length)
            this.init();
        this.show();
    };

    /**
     * Hides all block squares
     */
    this.hide = function() {
        for (var i = 0, l = squares.length; i < l; i++) {
            squares[i].visible = false;
            squares[i].repaint();
        }
    };

    /**
     * Shows all block squares
     */
    this.show = function() {
        var sx = this.scales.scalex;
        var sy = this.scales.scaley;
        var sr = this.scales.scaleratio;

        for (var i = 0, l = squares.length, s; i < l; i++) {
            s = squares[i];
            s.visible = sx && sy
                ? true
                : (sy && !sx
                    ? (s.posX == "middle"
                        ? true
                        : false)
                    : (sx && !sy
                        ? (s.posY == "middle"
                            ? true
                            : false)
                        : (sr
                            ? ((s.posY == "top" || s.posY == "bottom")
                              && s.posX !== "middle"
                                ? true
                                : false)
                            : false)));
            
            s.repaint();
        }
    };

    /**
     * Destroys all block squares
     */
    this.destroy = function(){
        for (var i = 0; i < squares.length; i++) {
            squares[i].destroy();
        }
    };
};

/*
 * Creates html and object representation for square element. Square is used for
 * resizing block elements.
 * 
 * @param {String}   posY        square vertical align relative to resized block element
 *     Possible values:
 *     top      square is on top of resized block element
 *     middle   square is in the middle of the resized block element
 *     bottom   square is on the bottom of resized block element
 * @param {String}   posX        square vertical align relative to resized block element
 *     Possible values:
 *     left     square is on the left of resized block element
 *     middle   square is in the middle of the resized block element
 *     right    square is on the right of resized block element
 * @param {Object}   objResize   object of resize class
 * @constructor
 */
apf.resize.square = function(posY, posX, objResize) {
    /*
     * Square visibility
     */
    this.visible = true;
    /*
     * square vertical align relative to resized block element
     */
    this.posX = posX;
    /*
     * square vertical align relative to resized block element
     */
    this.posY = posY;

    var margin = 0;
    var _self = this;

    /*
     * html represenation of square element
     */
    this.htmlElement = objResize.htmlElement.parentNode.appendChild(document.createElement('div'));
    apf.setStyleClass(this.htmlElement, "square");

    /*
     * Repaints square
     */
    this.repaint = function() {
        if (this.visible) {
            var block = objResize.htmlElement;
            this.htmlElement.style.display = "block";

            var bw = parseInt(block.style.width) + apf.getDiff(block)[0];
            var bh = parseInt(block.style.height) + apf.getDiff(block)[1];
            var bt = parseInt(block.style.top);
            var bl = parseInt(block.style.left);

            var sw = this.htmlElement.offsetWidth;
            var sh = this.htmlElement.offsetHeight;

            var t = posY == "top"
                ? bt - margin - sh
                : posY == "middle"
                    ? bt + bh/2 - sh/2
                    : bt + bh + margin;
            var l = posX == "left"
                ? bl - margin - sw
                : posX == "middle"
                    ? bl + bw/2 - sw/2
                    : bl + bw + margin;

            var c = (posY == "middle" 
                ? "w-resize"
                : (posX == "middle"
                     ? "n-resize"
                     : (posY + posX == "topleft"
                       || posY + posX == "bottomright") 
                         ? "nw-resize" 
                         : "ne-resize"));

            this.htmlElement.style.top = (t - 1) + "px";
            this.htmlElement.style.left = (l - 1) + "px";
            this.htmlElement.style.cursor = c;
        }
        else {
            //IE bug
            var sw = this.htmlElement.offsetWidth;
            this.htmlElement.style.display = 'none';
        }
    };

    this.destroy = function(){
        apf.destroyHtmlNode(this.htmlElement);
    };

    /* Events */
    this.htmlElement.onmouseover = function(e) {
        apf.setStyleClass(_self.htmlElement, "squareHover");
    };

    this.htmlElement.onmouseout = function(e) {
        apf.setStyleClass(_self.htmlElement, "", ["squareHover"]);
    };

    this.htmlElement.onmousedown = function(e) {
        e = (e || event);

        var block = objResize.htmlElement,

            sx = e.clientX,
            sy = e.clientY,

            pt = block.parentNode.offsetTop,
            pl = block.parentNode.offsetLeft,

            dw = objResize.scales.dwidth,
            dh = objResize.scales.dheight,
            
            snap = objResize.scales.snap,
            gridH = objResize.scales.gridH,
            gridW = objResize.scales.gridW,

            objBlock = apf.flow.isBlock(block),
            r = objBlock.other.ratio,

            posX = _self.posX,
            posY = _self.posY,

            width, height, top, left, dx, dy,
            prev_w, prev_h,

            l = parseInt(block.style.left),
            t = parseInt(block.style.top),
            w = parseInt(block.style.width),
            h = parseInt(block.style.height),
            resized = false;
            
        objResize.onresizedone(w, h, t, l);

        if (e.preventDefault) {
            e.preventDefault();
        }

        document.onmousemove = function(e) {
            e = (e || event);

            dx = e.clientX - sx;
            dy = e.clientY - sy;
            var shiftKey = e.shiftKey,
                proportion = r;

            if (shiftKey) {
                if (posX == "right" && posY == "bottom") {
                    width = w + dx;
                    height = width/proportion;
                    left = l;
                    top = t;
                }
                else if (posX == "right" && posY == "top") {
                    width = w + dx;
                    height = width/proportion;
                    left = l;
                    top = t - dx/proportion;
                }
                else if (posX == "left" && posY == "bottom") {
                    width = w - dx;
                    height = width/proportion;
                    left = l + dx;
                    top = t;
                }
                else if (posX == "left" && posY == "top") {
                    width = w - dx;
                    height = width/proportion;
                    left = l + dx;
                    top = t + dx/proportion;
                }

                /* Keep minimal size */
                if (width >= dw && height >= dh) {
                    width = prev_w = Math.max(dw, width);
                    height = prev_h = Math.max(dh, height);
                }
                else {
                    width = prev_w;
                    height = prev_h;
                    return false;
                }
            }
            else {
                width = posX == "right"
                    ? w + dx
                    : (posX == "left"
                        ? w - dx
                        : w);
                height = posY == "bottom"
                    ? h + dy
                    : (posY == "top"
                        ? h - dy
                        : h);
                left = posX == "right"
                    ? l
                    : (posX == "left"
                        ? Math.min(l + w - dw, l + dx)
                        : l);
                top = posY == "bottom"
                    ? t
                    : (posY == "top"
                        ? Math.min(t + h - dh, t + dy)
                        : t);

                /* Keep minimal size */
                width = Math.max(dw, width);
                height = Math.max(dh, height);
            }

            if (snap) {
                left = Math.floor(left / gridW) * gridW;
                top = Math.floor(top / gridH) * gridH;
                width = Math.ceil(width / gridW) * gridW;
                height = Math.ceil(height / gridH) * gridH;
            }

            if (objResize.onresize) {
                objResize.onresize(block, top, left, width, height);
            }

            objResize.show();
            
            resized = true;
        };

        document.onmouseup = function(e) {
            document.onmousemove = null;
            if (objResize.onresizedone && resized) {
                objResize.onresizedone(width, height, top, left);
                objBlock.other.ratio = width / height;
                resized = false;
            }
        };
    };
};




/**
 * 
 * A container that stacks two children vertically. 
 * 
 * Programatically, this is identical to a regular [[vbox]], except that it can
 * only accept two children, and uses absolute positioning. Because of this, there
 * is more work required to construct AML that matches a regular `<a:vbox>`; however,
 * the performance improvements in using a `<a:vsplitbox>` are massive.
 *
 * @class apf.vsplitbox
 * @define vsplitbox
 * @layout
 *
 * @inheritDoc apf.hsplitbox
 *
 * @author      Ruben Daniels (ruben AT ajax DOT org)
 * @version     %I%, %G%
 * @since       3.0
 * @see element.hsplitbox
 */
/**
 *
 * A container that stacks two children horizontally.
 * 
 * Programatically, this is identical to a regular [[apf.hbox]], except that it can
 * only accept two children, and uses absolute positioning. Because of this, there
 * is more work required to construct AML that matches a regular `<a:hbox>`; however,
 * the performance improvements in using a `<a:hsplitbox>` are massive.
 *
 * @class apf.hsplitbox
 * @define hsplitbox 
 * @layout
 * @inherits apf.GuiElement
 *
 * @author      Ruben Daniels (ruben AT ajax DOT org)
 * @version     %I%, %G%
 * @since       3.0\
 * @see element.vsplitbox
 */
apf.hsplitbox = function(struct, tagName) {
    this.$init(tagName || "hsplitbox", apf.NODE_VISIBLE, struct);
};
apf.vsplitbox = function(struct, tagName) {
    this.$init(tagName || "vsplitbox", apf.NODE_VISIBLE, struct);
};

(function(){
    this.minwidth = 0;
    this.minheight = 0;
    
    this.padding = 0;
    this.edge = 0;
    this.$edge = [0,0,0,0];
    
    // *** Properties and Attributes *** //

    this.$focussable = false;
    this.$useLateDom = true; 
    this.$box = true;
    this.$layout = true;
    
    /**
     * @attribute {String}  [padding="2"]      Sets or gets the space between each element.
     */
    /**
     * @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 of `top right bottom left`.
     */
    this.$booleanProperties["splitter"] = true;
    this.$supportedProperties.push("padding", "edge", "splitter");
    
    this.$propHandlers["padding"] = function(value) {
        this.padding = parseInt(value);
        
        if (!this.$amlLoaded)
            return;
            
        if (this.$handle)
            this.$handle.$ext.style[this.$vbox ? "height" : "width"] = value + "px";
        
        var firstChild = this.getFirstChild();
        var lastChild = this.getSecondChild();
        
        if (this.$vbox) {
            //Two flex children
            if (this.flexChild2) {
                if (firstChild.height) {
                    
                    // This is not needed because with bottom: xx% it already works
                    
                    // if (String(firstChild.height).indexOf("%") == -1) {
                        lastChild.$ext.style.marginTop = firstChild.visible
                            ? value + "px" // + apf.getHeightDiff(firstChild.$ext)
                            : 0;
                    // }
                }
                else {
                    firstChild.$ext.style.marginBottom = lastChild.visible
                        ? (value 
                            + apf.getHeightDiff(lastChild.$ext)) + "px"
                        : 0;
                }
            }
            else if (this.fixedChild && this.fixedChild.visible) {
                //One flex child (first)
                if (this.flexChild1 == firstChild) {
                    if (this.fixedChild.visible) {
                        this.flexChild1.$ext.style.bottom = 
                            (parseInt(this.fixedChild.height) + value + this.$edge[2]) + "px";
                    }
                }
                    
                //One flex child (last)
                else if (lastChild && this.flexChild1 == lastChild) {
                    this.flexChild1.$ext.style.top = 
                        (parseInt(this.fixedChild.height) + value + this.$edge[2]) + "px";
                }
            }
        }
        else {
            //Two flex children
            if (this.flexChild2) {
                if (firstChild.width) {
                    lastChild.$ext.style.marginLeft = 
                        (value 
                            + apf.getWidthDiff(firstChild.$ext)
                            + apf.getMargin(firstChild.$ext)[0]) + "px";
                }
                else {
                    firstChild.$ext.style.marginRight = 
                        (value + (lastChild
                            ? apf.getWidthDiff(lastChild.$ext)
                                + apf.getMargin(lastChild.$ext)[0]
                            : 0)) + "px";
                }
            }
            else if (this.fixedChild && this.fixedChild.visible) {
                //One flex child (first)
                if (this.flexChild1 == firstChild) {
                    this.flexChild1.$ext.style.right =   
                        (parseInt(this.fixedChild.width) + value + this.$edge[1]) + "px";
                }
                    
                //One flex child (last)
                else if (lastChild && this.flexChild1 == lastChild) {
                    this.flexChild1.$ext.style.left = 
                        (parseInt(this.fixedChild.width) + value + this.$edge[3]) + "px";
                }
            }
        }
    }
    
    this.$propHandlers["splitter"] = function(value) {
        if (value) {
            if (this.$handle)
                this.$handle.show();
            else {
                this.$handle = this.insertBefore(
                    this.ownerDocument.createElementNS(apf.ns.aml, "splitter"), 
                    this.lastChild);
            }
        }
        else {
            this.$handle && this.$handle.hide();//destroy(true, true);
        }
    }
    
    this.$propHandlers["edge"] = function(value, setSize) {
        this.$edge = apf.getBox(value);
        
        if (!this.$amlLoaded)
            return;
        
        var fNode = this.getFirstVisibleChild();
        if (!fNode) {
            this.hide();
            return false;
        }
        fNode.$ext.style.left = (this.$edge[3] + fNode.$margin[3]) + "px";
        fNode.$ext.style.top = (this.$edge[0] + fNode.$margin[0]) + "px";
        if (this.$vbox)
            fNode.$ext.style.right = (this.$edge[1] + fNode.$margin[1]) + "px";
        else
            fNode.$ext.style.bottom = (this.$edge[2] + fNode.$margin[2]) + "px";
        
        var lNode = this.getSecondVisibleChild();
        if (lNode && lNode.visible && lNode.$ext) {
            lNode.$ext.style.right = (this.$edge[1] + lNode.$margin[1]) + "px";
            lNode.$ext.style.bottom = (this.$edge[2] + lNode.$margin[2]) + "px";
            if (this.$vbox) {
                var isPercentage;
                
                lNode.$ext.style.left = (this.$edge[3] + lNode.$margin[3]) + "px";
                if (fNode.height || fNode.height === 0) {
                    isPercentage = String(fNode.height).indexOf("%") > -1;
                    lNode.$ext.style.top = isPercentage 
                        ? fNode.height 
                        : ((parseInt(fNode.height) + this.padding 
                            + this.$edge[0] + fNode.$margin[0]) + "px");
                    if (isPercentage) {
                        fNode.$ext.style.height = "";
                        fNode.$ext.style.bottom = (100 - parseFloat(fNode.height)) + "%";
                        lNode.$ext.style.height = "";
                    }
                    
                    if (this.$handle) {
                        this.$handle.$ext.style.top = isPercentage
                            ? fNode.height 
                            : ((parseInt(fNode.height) + this.$edge[0]) + "px");
//                        this.$handle.$ext.style.marginTop = isPercentage
//                            ? this.padding + "px"
//                            : "0";
                    }
                }
                else {
                    isPercentage = String(lNode.height).indexOf("%") > -1;
                    lNode.$ext.style.top = "";
                    fNode.$ext.style.bottom = isPercentage 
                        ? lNode.height 
                        : ((parseInt(lNode.height) + this.padding 
                            + this.$edge[2] + lNode.$margin[2]) + "px");
                    if (isPercentage) {
                        lNode.$ext.style.height = "";
                        lNode.$ext.style.top = (100 - parseFloat(lNode.height)) + "%";
                    }
                    
                    if (this.$handle) {
                        this.$handle.$ext.style.bottom = isPercentage
                            ? lNode.height 
                            : ((parseInt(lNode.height) + this.$edge[0]) + "px");
//                        this.$handle.$ext.style.marginBottom = isPercentage
//                            ? this.padding + "px"
//                            : "0";
                    }
                }
                
                if (this.$handle) {
                    this.$handle.$ext.style.left = this.$edge[3] + "px";
                    this.$handle.$ext.style.right = this.$edge[1] + "px";
                }
            }
            else {
                lNode.$ext.style.top = this.$edge[0] + lNode.$margin[0] + "px";
                
                if (fNode.width || fNode.width === 0) {
                    var isPercentage = String(fNode.width).indexOf("%") > -1;
                    lNode.$ext.style.left = isPercentage
                        ? fNode.width 
                        : ((parseInt(fNode.width) + this.padding 
                            + this.$edge[3] + fNode.$margin[3]) + "px");
                    if (isPercentage) {
                        fNode.$ext.style.width = "";
                        fNode.$ext.style.right = (100 - parseFloat(fNode.width)) + "%";
                    }
                    
                    if (this.$handle) {
                        this.$handle.$ext.style.left = isPercentage
                            ? fNode.width 
                            : ((parseInt(fNode.width) + this.$edge[3]) + "px");
//                        this.$handle.$ext.style.marginLeft = isPercentage
//                            ? this.padding + "px"
//                            : "0";
                    }
                }
                else {
                    var isPercentage = String(lNode.width).indexOf("%") > -1;
                    lNode.$ext.style.left = "";
                    fNode.$ext.style.right = isPercentage
                        ? lNode.width 
                        : ((parseInt(lNode.width) + this.padding 
                            + this.$edge[1] + lNode.$margin[1]) + "px");
                    if (isPercentage) {
                        lNode.$ext.style.width = "";
                        lNode.$ext.style.left = (100 - parseFloat(lNode.width)) + "%";
                    }
                    
                    if (this.$handle) {
                        this.$handle.$ext.style.right = isPercentage
                            ? lNode.width 
                            : ((parseInt(lNode.width) + this.$edge[3]) + "px");
//                        this.$handle.$ext.style.marginRight = isPercentage
//                            ? this.padding + "px"
//                            : "0";
                    }
                }
                
                if (this.$handle) {
                    this.$handle.$ext.style.top = this.$edge[0] + "px";
                    this.$handle.$ext.style.bottom = this.$edge[2] + "px";
                }
            }
            
            if (this.$handle)
                this.$handle.$ext.style.position = "absolute";
        }
        else {
            if (!this.$vbox) {
                fNode.$ext.style.right = (this.$edge[1] + fNode.$margin[1]) + "px";
                fNode.$ext.style.width = "";
            }
            else {
                fNode.$ext.style.bottom = (this.$edge[2] + fNode.$margin[2]) + "px";
                fNode.$ext.style.height = "";
            }
            
            if (this.$handle)
                this.$handle.hide();
        }
        
        if (setSize === true) {
            var size = this.$vbox ? "height" : "width";
            fNode.$propHandlers[size].call(fNode, fNode[size]);
        }
    };
    
    this.getFirstChild = function(startNode) {
        var node = startNode || this.firstChild;
        while (node && node.$splitter) {
            node = node.nextSibling;
        }
        return node || false;
    }
    this.getSecondChild = function(){
        var node = this.getFirstChild();
        if (!node)
            return false;
        return node.nextSibling && this.getFirstChild(node.nextSibling);
    }
    
    this.getFirstVisibleChild = function(startNode) {
        var node = startNode || this.firstChild;
        while (node && (!node.visible || node.$splitter)) {
            node = node.nextSibling;
        }
        if (node && node.visible)
            return node;
        return false;
    }
    
    this.getSecondVisibleChild = function(){
        var node = this.getFirstVisibleChild();
        if (!node)
            return false;
        return node.nextSibling && this.getFirstVisibleChild(node.nextSibling);
    }
    
    function visibleHandler(e) {
        if (this.parentNode.$handle) {
            if (!e.value || this.parentNode.childNodes.length < 3)
                this.parentNode.$handle.hide();
            else
                this.parentNode.$handle.show();
        }
        
        if (e.value && !this.parentNode.visible)
            this.parentNode.show();
        
        this.parentNode.$propHandlers.edge
            .call(this.parentNode, this.parentNode.edge, true);
        
        // apf.layout.forceResize(this.parentNode.$int);
        
        //Change margin
        this.parentNode.$propHandlers.padding
            .call(this.parentNode, this.parentNode.padding)
    }
    
    var handlers = {
        "width" : function(value, isLast) {
            //@todo this should check the largest and only allow that one
            //if (this.parentNode.$vbox && this.parentNode.align == "stretch")
                //return;
            
            //@todo change fixedChild flexChild1 and flexChild2 based on this

            this.$ext.style.width = !apf.isNot(value) 
                ? (parseFloat(value) == value 
                    ? (value - apf.getWidthDiff(this.$ext)) + "px"
                    : value)
                : "";
            
            //This can be optimized
            if (this.$amlLoaded && isLast !== false)
                this.parentNode.$propHandlers["edge"].call(this.parentNode, 
                    this.parentNode.edge);
        },
        
        "height" : function(value, isLast) {
            //@todo this should check the largest and only allow that one
            //if (!this.parentNode.$vbox && this.parentNode.align == "stretch")
                //return;

            //@todo change fixedChild flexChild1 and flexChild2 based on this

            this.$ext.style.height = !apf.isNot(value) 
                ? (parseFloat(value) == value 
                    ? (value - apf.getHeightDiff(this.$ext)) + "px"
                    : value)
                : "";
            
            //This can be optimized
            if (this.$amlLoaded && isLast !== false)
                this.parentNode.$propHandlers["edge"].call(this.parentNode, 
                    this.parentNode.edge);
        },
        
        "margin" : function(value, isLast) {
            this.$margin = apf.getBox(value);
            
            //This can be optimized
            if (this.$amlLoaded && isLast !== false)
                this.parentNode.$propHandlers["edge"].call(this.parentNode, this.parentNode.edge);
        }
    }
    
    this.register = function(amlNode, insert) {
        if (amlNode.$splitter || amlNode.nodeFunc != apf.NODE_VISIBLE)
            return;

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

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

        var fNode = this.getFirstChild();
        var sNode = fNode && this.getSecondChild();
        var prop = this.$vbox ? "height" : "width";
        
        this.flexChild1 = this.flexChild2 = this.fixedChild = null;
        
        if (!fNode[prop] || ~String(fNode[prop]).indexOf("%"))
            this.flexChild1 = fNode;
        else
            this.fixedChild = fNode;
            
        if (sNode) {
            if (!sNode[prop] || ~String(sNode[prop]).indexOf("%"))
                this[this.flexChild1 ? "flexChild2" : "flexChild1"] = sNode;
            else
                this.fixedChild = sNode;
        }
            
        // if (this.flexChild1 && this.flexChild1 == amlNode){ }
        // else if (this.$vbox) {
        //     if (!amlNode.height || String(amlNode.height).indexOf("%") > -1)
        //         this[!this.flexChild1 ? "flexChild1" : "flexChild2"] = amlNode;
        //     else
        //         this.fixedChild = amlNode;
        // }
        // else {
        //     if (!amlNode.width || String(amlNode.width).indexOf("%") > -1)
        //         this[!this.flexChild1 ? "flexChild1" : "flexChild2"] = amlNode;
        //     else
        //         this.fixedChild = amlNode;
        // }

        amlNode.addEventListener("prop.visible", visibleHandler);
        amlNode.$ext.style.position = "absolute";
        amlNode.$ext.style.margin = "";

        if (amlNode.height)
            handlers.height.call(amlNode, amlNode.height, false);
        if (amlNode.width)
            handlers.width.call(amlNode, amlNode.width, false);
        if (amlNode.margin)
            handlers.margin.call(amlNode, amlNode.margin, false);
            
        var isLast = this.lastChild.$amlLoaded || this.lastChild == amlNode;
        if (isLast) {
            this.$propHandlers["padding"].call(this, this.padding);
            this.$propHandlers["edge"].call(this, this.edge, 
                !amlNode[this.$vbox ? "height" : "width"]);
        }
        
        if (this.$handle && this.childNodes.length > 2)
            this.$handle.show();
    }
    
    this.unregister = function(amlNode) {
        if (amlNode.$splitter || amlNode.nodeFunc != apf.NODE_VISIBLE)
            return;
        
        delete amlNode.$margin;
        
        amlNode.$propHandlers["left"] = 
        amlNode.$propHandlers["top"] = 
        amlNode.$propHandlers["right"] = 
        amlNode.$propHandlers["bottom"] = null;
        
        for (var prop in handlers) {
            delete amlNode.$propHandlers[prop];
        }
        
        if (this.fixedChild == amlNode)
            delete this.fixedChild;
        else if (this.flexChild1 == amlNode)
            delete this.flexChild1;
        else if (this.flexChild2 == amlNode)
            delete this.flexChild2;
        
        //Clear css properties and set layout
        amlNode.removeEventListener("prop.visible", visibleHandler);
        amlNode.$ext.style.display = amlNode.visible ? "block" : "none";
        
        if (amlNode.width)
            amlNode.$ext.style.width = "";
        if (amlNode.height)
            amlNode.$ext.style.height = "";
        amlNode.$ext.style.position = 
        amlNode.$ext.style.margin = 
        amlNode.$ext.style.left = 
        amlNode.$ext.style.top = 
        amlNode.$ext.style.right = 
        amlNode.$ext.style.bottom = "";
        
        if (this.$handle)
            this.$handle.hide();
    }
    
    // *** 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) {
            return;
        }
        
        if (e.currentTarget.nodeType != 1 
          || e.currentTarget.nodeFunc != apf.NODE_VISIBLE)
            return;
        
//        if (this.$handle) {
//            var _self = this;
//            setTimeout(function(){
//                if (_self.$handle.nextSibling != _self.lastChild)
//                    _self.insertBefore(_self.$handle, _self.lastChild);
//            });
//        }

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

    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 == "vsplitbox";
        this.$int = this.$ext;
        this.$ext.host = this;
        
        if (this.getAttribute("class")) 
            apf.setStyleClass(this.$ext, this.getAttribute("class"));
    };
    
    this.$loadAml = function(x) {
    };
}).call(apf.vsplitbox.prototype = new apf.GuiElement());

apf.hsplitbox.prototype = apf.vsplitbox.prototype;

apf.aml.setElement("hsplitbox", apf.hsplitbox);
apf.aml.setElement("vsplitbox", apf.vsplitbox);


/**
 * @constructor
 * @private
 */
apf.splitter = function(struct, tagName) {
    this.$init(tagName || "splitter", apf.NODE_VISIBLE, struct);
};

(function() {
    this.minwidth = 0;
    this.minheight = 0;
    
    this.$scale = 0; // 0 both, 1 left/top, 2 right/bottom 
    
    this.$focussable = false; // This object can get the focus
    this.$splitter = true;
    
    this.$booleanProperties["realtime"] = true;
    
    this.$propHandlers["realtime"] = function(value) {
        this.$setStyleClass(this.$ext, value && (this.$baseCSSname + "Realtime") || "", 
            [this.$baseCSSname + "Realtime"]);
    };
    
    this.$propHandlers["scale"] = function(value) {
        this.$scale = value == "left" || value == "top"
            ? 1 : (value == "right" || "bottom " 
                ? 2 : 0);
    };
    
    this.$propHandlers["parent"] = function(value) {
        this.$parent = typeof value == "object" ? value : self[value];
    };
    
    this.$propHandlers["type"] = function(value) {
        this.$setStyleClass(this.$ext, value,
            [value == "horizontal" ? "vertical" : "horizontal"]);
        
        if (value == "vertical")
            this.$setStyleClass(this.$ext, "w-resize", ["n-resize"]);
        else
            this.$setStyleClass(this.$ext, "n-resize", ["w-resize"]);

        //Optimize this to not recalc for certain cases
        if (value == "horizontal") {
            this.$info = {
                pos: "top",
                opos: "left",
                size: "width",
                osize: "height",
                offsetPos: "offsetTop",
                offsetSize: "offsetHeight",
                oOffsetPos: "offsetLeft",
                oOffsetSize: "offsetWidth",
                clientPos: "clientY",
                d1          : 1,
                d2          : 0,
                x1          : 0,
                x2          : 2
            };
        }
        else {
            this.$info = {
                pos: "left",
                opos: "top",
                size: "height",
                osize: "width",
                offsetPos: "offsetLeft",
                offsetSize: "offsetWidth",
                oOffsetPos: "offsetTop",
                oOffsetSize: "offsetHeight",
                clientPos: "clientX",
                d1          : 0,
                d2          : 1,
                x1          : 3,
                x2          : 1
            };
        }
    };
    
    this.addEventListener("DOMNodeInserted", function(e) {
        if (e.currentTarget != this)
            return;
        
        /*if (e.$oldParent) {
            e.$oldParent.removeEventListener("DOMNodeInserted", this.$siblingChange);
            e.$oldParent.removeEventListener("DOMNodeRemoved", this.$siblingChange);
        }*/
        
        this.init && this.init();
    });
    
    /*this.$siblingChange = function(e) {
        //if (e.currentTarget
        
        //this.init();
    }*/
    
    this.$draw = function(){
        //Build Main Skin
        this.$ext = this.$getExternal();

        var template = "vbox|hbox".indexOf(this.parentNode.localName) > -1
            ? "box" : "splitbox";
            
        apf.extend(this, apf.splitter.templates[template]);
        this.decorate();
    };
        
    this.$loadAml = function(x) {
        if (this.realtime !== false)
            this.$propHandlers.realtime.call(this, this.realtime = true);
    };
}).call(apf.splitter.prototype = new apf.Presentation());

apf.splitter.templates = {
    box: {
        update: function(newPos, finalPass) {
            with (this.$info) {
                //var pos = Math.ceil(apf.getAbsolutePosition(this.$ext, this.parentNode.$int)[d1] - posPrev[d1]);
                var max = this.$previous 
                    ? this.$previous.$ext[offsetSize] + this.$next.$ext[offsetSize]
                    : (this.parentNode).getWidth();
                var method = finalPass ? "setAttribute" : "setProperty";
                if (apf.hasFlexibleBox)
                    newPos -= this.$previous ? apf.getAbsolutePosition(this.$previous.$ext, this.parentNode.$int)[d1] : 0;
    
                //Both flex
                if (this.$previous && this.$next && (this.$previous.flex || this.$previous.flex === 0) && (this.$next.flex || this.$next.flex === 0)) {
                    if (!finalPass && !this.realtime) 
                        newPos -= this.$ext[offsetSize];
    
                    //var totalFlex = this.$previous.flex + this.$next.flex - (finalPass && !this.realtime ? this.parentNode.padding : 0);
                    if (!this.$scale || this.$scale == 1)
                        this.$previous[method]("flex", newPos);
                    if (!this.$scale || this.$scale == 2)
                        this.$next[method]("flex", this.$totalFlex - newPos);
                }
                //Fixed
                else {
                    if (this.$next && !this.$next.flex && (!this.$scale || this.$scale == 2))
                        this.$next[method](osize, max - newPos);
                    if (this.$previous && !this.$previous.flex && (!this.$scale || this.$scale == 1))
                        this.$previous[method](osize, newPos);
                }
            }
    
            if (apf.hasSingleResizeEvent)
                apf.layout.forceResize(this.$ext.parentNode);
        },
        
        $setSiblings: function(){
            this.$previous = this.previousSibling;
            while (this.$previous && (this.$previous.nodeType != 1 
              || this.$previous.visible === false 
              || this.$previous.nodeFunc != apf.NODE_VISIBLE))
                this.$previous = this.$previous.previousSibling;
            this.$next = this.nextSibling;
            while (this.$next && (this.$next.nodeType != 1 
              || this.$next.visible === false 
              || this.$next.nodeFunc != apf.NODE_VISIBLE))
                this.$next = this.$next.nextSibling;
        },
        
        init: function(size, refNode, oItem) {
            //this.parentNode.addEventListener("DOMNodeInserted", this.$siblingChange);
            //this.parentNode.addEventListener("DOMNodeRemoved", this.$siblingChange);
            
            this.$setSiblings();
            
            this.$thickness = null;
            if (this.parentNode && this.parentNode.$box) {
                this.setProperty("type", this.parentNode.localName == "vbox" 
                    ? "horizontal" 
                    : "vertical");
                this.$thickness = parseInt(this.parentNode.padding);
            }
            
            if (!this.$previous || !this.$next)
                return this;
            
            with (this.$info) {
                var diff = apf.getDiff(this.$ext);
                if (!this.parentNode.$box) {
                    var iSize = Math.max(
                        this.$previous.$ext[offsetSize], this.$next.$ext[offsetSize]);
                    this.$ext.style[size] = (iSize - diff[d1]) + "px";
                }
    
                var iThick = this[osize] = this.$thickness 
                    || (this.$next[oOffsetPos] - this.$previous[oOffsetPos] 
                        - this.$previous[oOffsetSize]);
    
                this.$ext.style[osize] = (iThick - diff[d2]) + "px";
            }
            
            return this;
        },
        
        decorate: function(){
            var _self = this;
            this.$ext.onmousedown = function(e) {
                if (!e)
                    e = event;
                
                if (_self.dispatchEvent("dragstart") === false)
                    return;
                
                apf.dragMode = true; //prevent selection
                
                _self.$setSiblings();
    
                var changedPosition, pHtml = _self.parentNode.$int, diff = 0;
                if ("absolute|fixed|relative".indexOf(apf.getStyle(pHtml, "position")) == -1) {
                    pHtml.style.position = "relative";
                    changedPosition = true;
                }
    
                _self.$totalFlex = 0;
                with (_self.$info) {
                    if (_self.$parent) {
                        if (!_self.$previous) {
                            var posNext = apf.getAbsolutePosition(_self.$next.$ext, _self.parentNode.$int);
                            var wd = _self.$parent.getWidth();
                            
                            if (_self.$scale == 2) {
                                var max = posNext[d1] + _self.$next.$ext[offsetSize] - this[offsetSize];
                                diff = (_self.parentNode.$int[offsetSize] - max);
                                var min = max - wd - diff;
                            }
                        }
                        else if (!_self.$next) {
                            //@todo
                        }
                    }
                    else {
                        if (_self.$previous) {
                            var posPrev = apf.getAbsolutePosition(_self.$previous.$ext, _self.parentNode.$int);
                            var min = _self.$scale 
                                ? 0 
                                : (posPrev[d1] || 0) + (parseInt(_self.$previous.minwidth) || 0);
                        }
                        if (_self.$next) {
                            var posNext = apf.getAbsolutePosition(_self.$next.$ext, _self.parentNode.$int);
                            var max = posNext[d1] + _self.$next.$ext[offsetSize] 
                                - this[offsetSize] - (parseInt(_self.$next.minwidth) || 0);
                        }
                    }
                    
                    //Set flex to pixel sizes
                    if (_self.$previous && _self.$next) {
                        if ((_self.$previous.flex || _self.$previous.flex === 0) 
                          && (_self.$next.flex || _self.$next.flex === 0)) {
                            var set = [], nodes = _self.parentNode.childNodes, padding = 0;
                            for (var node, i = 0, l = nodes.length; i < l; i++) {
                                if ((node = nodes[i]).visible === false 
                                  || node.nodeFunc != apf.NODE_VISIBLE || node.$splitter)
                                    continue;
                                
                                if (node.flex)
                                    set.push(node, node.$ext[offsetSize] 
                                        + (apf.hasFlexibleBox && !_self.realtime && node == _self.$previous 
                                            ? 2 * _self.parentNode.padding : 0));
                            }
                            for (var i = 0, l = set.length; i < l; i+=2) {
                                set[i].setAttribute("flex", set[i+1]);
                            }
                        }
                        
                        _self.$totalFlex += _self.$next.flex + _self.$previous.flex;
                    }
                    
                    var startPos, startOffset;
                    if (apf.hasFlexibleBox) {
                        var coords = apf.getAbsolutePosition(this);
                        startPos = e[clientPos] - coords[d1];
    
                        if (!_self.realtime) {
                            if (_self.$previous.flex && !_self.$next.flex) {
                                var mBox = apf.getBox(_self.$next.margin);
                                mBox[x1] = _self.parentNode.padding;
                                _self.$next.$ext.style.margin = mBox.join("px ") + "px";
                            }
                            else {
                                var mBox = apf.getBox(_self.$previous.margin);
                                mBox[x2] = _self.parentNode.padding;
                                _self.$previous.$ext.style.margin = mBox.join("px ") + "px";
                            }
                            
                            var diff = apf.getDiff(this);
                            this.style.left = coords[0] + "px";
                            this.style.top = coords[1] + "px"; //(apf.getHtmlTop(this) - Math.ceil(this.offsetHeight/2))
                            this.style.width = (this.offsetWidth - diff[0]) + "px";
                            this.style.height = (this.offsetHeight - diff[1]) + "px";
                            this.style.position = "absolute";
                        }
                    }
                    else {
                        var coords = apf.getAbsolutePosition(this.offsetParent);
                        startOffset = apf.getAbsolutePosition(_self.$previous.$ext)[d1];
                        startPos = e[clientPos] - coords[d1];
                        
                        if (!_self.realtime) {
                            this.style.left = "0px";
                            this.style.top = "0px";
                            this.style.position = "relative";
                        }
                        min = -1000; //@todo
                    }
                }
                
                //e.returnValue = false;
                //e.cancelBubble = true;
                //apf.stopEvent(e);
                
                
                apf.plane.show(this);
                
    
                _self.$setStyleClass(this, _self.$baseCSSname + "Moving");
                
                _self.$setStyleClass(document.body,
                    _self.type == "vertical" ? "w-resize" : "n-resize",
                    [_self.type == "vertical" ? "n-resize" : "w-resize"]);
                
                //@todo convert to proper way
                document.onmouseup = function(e) {
                    if (!e) e = event;
                    
                    with (_self.$info) {
                        var newPos;
                        if (e[clientPos] >= 0) {
                            var coords = apf.getAbsolutePosition(_self.$ext.offsetParent);
                            newPos = (Math.min(max, Math.max(min, (e[clientPos] - coords[d1]) - 
                                (apf.hasFlexibleBox ? startPos : startOffset)))) + diff;
                        }
                    }
    
                    _self.$setStyleClass(_self.$ext, "", [_self.$baseCSSname + "Moving"]);
                    _self.$setStyleClass(document.body, "", ["n-resize", "w-resize"]);
                    
                    if (changedPosition)
                        pHtml.style.position = "";
                    
                    if (apf.hasFlexibleBox && !_self.realtime)
                        (_self.$previous.flex && !_self.$next.flex
                          ? _self.$next : _self.$previous).$ext.style.margin 
                            = apf.getBox(_self.$previous.margin).join("px ") + "px";
                    
                    if (newPos)
                        _self.update(newPos, true);
                    
                    
                    apf.plane.hide();
                    
                    
                    if (!_self.realtime) {
                        _self.$ext.style.left = "";
                        _self.$ext.style.top = "";
                        _self.$ext.style[_self.$info.size] = "";
                        _self.$ext.style.position = "";
                    }
                    
                    _self.dispatchEvent("dragdrop");
                    
                    document.onmouseup = 
                    document.onmousemove = null;
                    
                    apf.dragMode = false; //return to default selection policy
                };
                
                //@todo convert to proper way
                document.onmousemove = function(e) {
                    if (!e) e = event;
            
                    with (_self.$info) {
                        var newPos;
                        if (e[clientPos] >= 0) {
                            var coords = apf.getAbsolutePosition(_self.$ext.offsetParent);
                            newPos = (Math.min(max, Math.max(min, (e[clientPos] - coords[d1]) - 
                                (apf.hasFlexibleBox || !_self.realtime ? startPos : startOffset)))) + diff;
    
                            if (_self.realtime)
                                _self.update(newPos);
                            else {
                                _self.$ext.style[pos] = newPos + "px";
                            }
                        }
                    }
                    
                    apf.stopEvent(e);
                    //e.returnValue = false;
                    //e.cancelBubble = true;
                    
                    _self.dispatchEvent("dragmove");
                };
            }
            
            apf.queue.add("splitter" + this.$uniqueId, function(){
                _self.init();
            });
        }
    },
    
    splitbox: {
        update: function(newPos, finalPass) {
            this[this.parentNode.$vbox ? "updateV" : "updateH"](newPos, finalPass);
        },
        
        updateV: function(newPos, finalPass) {
            var method = finalPass ? "setAttribute" : "setProperty";
            
            var pNode = this.$parent || this.parentNode;
            var firstChild = pNode.firstChild;
            if (firstChild.localName == "splitter")
                firstChild = firstChild.nextSibling;
            if (pNode.fixedChild) {
                if (pNode.fixedChild == pNode.firstChild) {
                    pNode.fixedChild[method]("height", newPos - pNode.$edge[0]);
                }
                else {
                    pNode.fixedChild[method]("height", 
                        apf.getHtmlInnerHeight(pNode.$int) - newPos 
                        - pNode.padding - pNode.$edge[1]);
                }
            }
            else if (firstChild.height) {
                var total = apf.getHtmlInnerHeight(pNode.$int);
                firstChild[method]("height", 
                    ((newPos - pNode.$edge[0])/total*100) + "%");
            }
            else {
                var total = apf.getHtmlInnerHeight(pNode.$int) ;
                pNode.lastChild[method]("height", 
                    ((total - newPos - pNode.$edge[2] - pNode.padding)/total*100) + "%");
            }
    
            apf.dispatchEvent("splitter.resize", { splitter: this, final: finalPass });
        },
        
        updateH: function(newPos, finalPass) {
            var method = finalPass ? "setAttribute" : "setProperty";

            var pNode = this.$parent || this.parentNode;
            var firstChild = pNode.firstChild;
            if (firstChild.localName == "splitter")
                firstChild = firstChild.nextSibling;
            if (pNode.fixedChild) {
                if (pNode.fixedChild == pNode.firstChild) {
                    pNode.fixedChild[method]("width", newPos - pNode.$edge[3]);
                }
                else {
                    pNode.fixedChild[method]("width", 
                        apf.getHtmlInnerWidth(pNode.$int) - newPos 
                        - pNode.padding - pNode.$edge[2]);
                }
            }
            else if (firstChild.width) {
                var total = apf.getHtmlInnerWidth(pNode.$int);
                firstChild[method]("width", 
                    ((newPos - pNode.$edge[3])/total*100) + "%");
            }
            else {
                var total = apf.getHtmlInnerWidth(pNode.$int) ;
                pNode.lastChild[method]("width", 
                    ((total - newPos - pNode.$edge[1] - pNode.padding)/total*100) + "%");
            }
    
            apf.dispatchEvent("splitter.resize", { splitter: this, final: finalPass });
        },
        
        $setSiblings: function(){
            this.$previous = this.parentNode.firstChild;
            this.$next = this.parentNode.lastChild;
        },
        
        decorate: function(){
            var _self = this;
            
            if (this.parentNode && this.parentNode.$box) {
                this.setProperty("type", this.parentNode.$vbox
                    ? "horizontal" 
                    : "vertical");
            }
            
            this.$ext.onmousedown = function(e) {
                if (!e)
                    e = event;

                if (_self.dispatchEvent("dragstart") === false)
                    return;

                apf.dragMode = true; //prevent selection
                
                _self.$setSiblings();

                var pNode = _self.$parent || _self.parentNode;
                var firstChild = pNode.firstChild.$splitter ? pNode.childNodes[1] : pNode.firstChild;
                if (pNode.$vbox) {
                    var min = parseInt(firstChild.minheight) + pNode.$edge[0];
                    var max = apf.getHtmlInnerHeight(pNode.$ext) - pNode.lastChild.minheight 
                        - pNode.$edge[2] - pNode.padding;
                    var offset = _self.$ext.getBoundingClientRect().top - e.clientY;
                }
                else {
                    var min = parseInt(firstChild.minwidth) + pNode.$edge[3];
                    var max = apf.getHtmlInnerWidth(pNode.$ext) - pNode.lastChild.minwidth 
                        - pNode.$edge[1] - pNode.padding;
                    var offset = _self.$ext.getBoundingClientRect().left - e.clientX;
                }
                
                function update(e, final) {
                    if (!_self.$ext.offsetParent)
                        return;
                    
                    var newPos, coords;
                    if (pNode.$vbox) {
                        if (e.clientY >= 0) {
                            coords = apf.getAbsolutePosition(_self.$parent ? _self.$parent.$ext : _self.$ext.offsetParent);
                            newPos = Math.min(max, Math.max(min, (e.clientY - coords[1] - offset)));
                        }
                    }
                    else {
                        if (e.clientX >= 0) {
                            coords = apf.getAbsolutePosition(_self.$parent ? _self.$parent.$ext : _self.$ext.offsetParent);
                            newPos = Math.min(max, Math.max(min, (e.clientX - coords[0] - offset)));
                        }
                    }
                    
                    if (!newPos) return;
                    
                    if (_self.realtime || final)
                        _self.update(newPos, final);
                    else {
                        _self.$ext.style[pNode.$vbox ? "top" : "left"] = newPos + "px";
                    }
                }
    
                
                apf.plane.show(this);
                
    
                _self.$setStyleClass(this, _self.$baseCSSname + "Moving");
                
                _self.$setStyleClass(document.body,
                    _self.type == "vertical" ? "w-resize" : "n-resize",
                    [_self.type == "vertical" ? "n-resize" : "w-resize"]);
                
                //@todo convert to proper way
                document.onmouseup = function(e) {
                    if (!e) e = event;
                    
                    _self.$setStyleClass(_self.$ext, "", [_self.$baseCSSname + "Moving"]);
                    _self.$setStyleClass(document.body, "", ["n-resize", "w-resize"]);
                    
                    update(e, true);
                    
                    
                    apf.plane.hide();
                    
                    // Return all pointer events to iframes
                    var frames = document.getElementsByTagName("iframe");
                    for (var i = 0; i < frames.length; i++)
                        frames[i].style.pointerEvents = "";
                    
                    if (!_self.realtime) {
                        _self.$ext.style.left = "";
                        _self.$ext.style.top = "";
                        _self.$ext.style[_self.$info.size] = "";
                        _self.$ext.style.position = "";
                    }
                    
                    _self.dispatchEvent("dragdrop");
                    
                    document.onmouseup = 
                    document.onmousemove = null;
                    
                    apf.dragMode = false; //return to default selection policy
                };
                
                //@todo convert to proper way
                document.onmousemove = function(e) {
                    if (!e) e = event;
            
                    update(e);
                    
                    apf.stopEvent(e);
                    //e.returnValue = false;
                    //e.cancelBubble = true;
                    
                    _self.dispatchEvent("dragmove");
                };
            }
        }
    }
};

apf.aml.setElement("splitter", apf.splitter);



};
});