# Yahoo! UI Library

### layout 2.8.0r4

Yahoo! UI Library > layout > layout.js (source view)
Search:
Filters Show Private Show Protected Show Deprecated

    /**
     * @description <p>Provides a fixed layout containing, top, bottom, left, right and center layout units. It can be applied to either the body or an element.</p>
     * @namespace YAHOO.widget
     * @requires yahoo, dom, element, event
     * @module layout
     */
    (function() {
        var Dom = YAHOO.util.Dom,
            Event = YAHOO.util.Event,
            Lang = YAHOO.lang;

        /**
         * @constructor
         * @class Layout
         * @extends YAHOO.util.Element
         * @description <p>Provides a fixed layout containing, top, bottom, left, right and center layout units. It can be applied to either the body or an element.</p>
         * @param {String/HTMLElement} el The element to make contain a layout.
         * @param {Object} attrs Object liternal containing configuration parameters.
        */

        var Layout = function(el, config) {
            YAHOO.log('Creating the Layout Object', 'info', 'Layout');
            if (Lang.isObject(el) && !el.tagName) {
                config = el;
                el = null;
            }
            if (Lang.isString(el)) {
                if (Dom.get(el)) {
                    el = Dom.get(el);
                }
            }
            if (!el) {
                el = document.body;
            }

            var oConfig = {
                element: el,
                attributes: config || {}
            };

            Layout.superclass.constructor.call(this, oConfig.element, oConfig.attributes);
        };

        /**
        * @private
        * @static
        * @property _instances
        * @description Internal hash table for all layout instances
        * @type Object
        */
        Layout._instances = {};
        /**
        * @static
        * @method getLayoutById
        * @description Get's a layout object by the HTML id of the element associated with the Layout object.
        * @return {Object} The Layout Object
        */
        Layout.getLayoutById = function(id) {
            if (Layout._instances[id]) {
                return Layout._instances[id];
            }
            return false;
        };

        YAHOO.extend(Layout, YAHOO.util.Element, {
            /**
            * @property browser
            * @description A modified version of the YAHOO.env.ua object
            * @type Object
            */
            browser: function() {
                var b = YAHOO.env.ua;
                b.standardsMode = false;
                b.secure = false;
                return b;
            }(),
            /**
            * @private
            * @property _units
            * @description An object literal that contains a list of units in the layout
            * @type Object
            */
            _units: null,
            /**
            * @private
            * @property _rendered
            * @description Set to true when the layout is rendered
            * @type Boolean
            */
            _rendered: null,
            /**
            * @private
            * @property _zIndex
            * @description The zIndex to set all LayoutUnits to
            * @type Number
            */
            _zIndex: null,
            /**
            * @private
            * @property _sizes
            * @description A collection of the current sizes of all usable LayoutUnits to be used for calculations
            * @type Object
            */
            _sizes: null,
            /**
            * @private
            * @method _setBodySize
            * @param {Boolean} set If set to false, it will NOT set the size, just perform the calculations (used for collapsing units)
            * @description Used to set the body size of the layout, sets the height and width of the parent container
            */
            _setBodySize: function(set) {
                var h = 0, w = 0;
                set = ((set === false) ? false : true);

                if (this._isBody) {
                    h = Dom.getClientHeight();
                    w = Dom.getClientWidth();
                } else {
                    h = parseInt(this.getStyle('height'), 10);
                    w = parseInt(this.getStyle('width'), 10);
                    if (isNaN(w)) {
                        w = this.get('element').clientWidth;
                    }
                    if (isNaN(h)) {
                        h = this.get('element').clientHeight;
                    }
                }
                if (this.get('minWidth')) {
                    if (w < this.get('minWidth')) {
                        w = this.get('minWidth');
                    }
                }
                if (this.get('minHeight')) {
                    if (h < this.get('minHeight')) {
                        h = this.get('minHeight');
                    }
                }
                if (set) {
                    if (h < 0) {
                        h = 0;
                    }
                    if (w < 0) {
                        w = 0;
                    }
                    Dom.setStyle(this._doc, 'height', h + 'px');
                    Dom.setStyle(this._doc, 'width', w + 'px');
                }
                this._sizes.doc = { h: h, w: w };
                YAHOO.log('Setting Body height and width: (' + h + ',' + w + ')', 'info', 'Layout');
                this._setSides(set);
            },
            /**
            * @private
            * @method _setSides
            * @param {Boolean} set If set to false, it will NOT set the size, just perform the calculations (used for collapsing units)
            * @description Used to set the size and position of the left, right, top and bottom units
            */
            _setSides: function(set) {
                YAHOO.log('Setting side units', 'info', 'Layout');
                var h1 = ((this._units.top) ? this._units.top.get('height') : 0),
                    h2 = ((this._units.bottom) ? this._units.bottom.get('height') : 0),
                    h = this._sizes.doc.h,
                    w = this._sizes.doc.w;
                set = ((set === false) ? false : true);

                this._sizes.top = {
                    h: h1, w: ((this._units.top) ? w : 0),
                    t: 0
                };
                this._sizes.bottom = {
                    h: h2, w: ((this._units.bottom) ? w : 0)
                };

                var newH = (h - (h1 + h2));

                this._sizes.left = {
                    h: newH, w: ((this._units.left) ? this._units.left.get('width') : 0)
                };
                this._sizes.right = {
                    h: newH, w: ((this._units.right) ? this._units.right.get('width') : 0),
                    l: ((this._units.right) ? (w - this._units.right.get('width')) : 0),
                    t: ((this._units.top) ? this._sizes.top.h : 0)
                };

                if (this._units.right && set) {
                    this._units.right.set('top', this._sizes.right.t);
                    if (!this._units.right._collapsing) {
                        this._units.right.set('left', this._sizes.right.l);
                    }
                    this._units.right.set('height', this._sizes.right.h, true);
                }
                if (this._units.left) {
                    this._sizes.left.l = 0;
                    if (this._units.top) {
                        this._sizes.left.t = this._sizes.top.h;
                    } else {
                        this._sizes.left.t = 0;
                    }
                    if (set) {
                        this._units.left.set('top', this._sizes.left.t);
                        this._units.left.set('height', this._sizes.left.h, true);
                        this._units.left.set('left', 0);
                    }
                }
                if (this._units.bottom) {
                    this._sizes.bottom.t = this._sizes.top.h + this._sizes.left.h;
                    if (set) {
                        this._units.bottom.set('top', this._sizes.bottom.t);
                        this._units.bottom.set('width', this._sizes.bottom.w, true);
                    }
                }
                if (this._units.top) {
                    if (set) {
                        this._units.top.set('width', this._sizes.top.w, true);
                    }
                }
                YAHOO.log('Setting sizes: (' + Lang.dump(this._sizes) + ')', 'info', 'Layout');
                this._setCenter(set);
            },
            /**
            * @private
            * @method _setCenter
            * @param {Boolean} set If set to false, it will NOT set the size, just perform the calculations (used for collapsing units)
            * @description Used to set the size and position of the center unit
            */
            _setCenter: function(set) {
                set = ((set === false) ? false : true);
                var h = this._sizes.left.h;
                var w = (this._sizes.doc.w - (this._sizes.left.w + this._sizes.right.w));
                if (set) {
                    this._units.center.set('height', h, true);
                    this._units.center.set('width', w, true);
                    this._units.center.set('top', this._sizes.top.h);
                    this._units.center.set('left', this._sizes.left.w);
                }
                this._sizes.center = { h: h, w: w, t: this._sizes.top.h, l: this._sizes.left.w };
                YAHOO.log('Setting Center size to: (' + h + ', ' + w + ')', 'info', 'Layout');
            },
            /**
            * @method getSizes
            * @description Get a reference to the internal Layout Unit sizes object used to build the layout wireframe
            * @return {Object} An object of the layout unit sizes
            */
            getSizes: function() {
                return this._sizes;
            },
            /**
            * @method getUnitById
            * @param {String} id The HTML element id of the unit
            * @description Get the LayoutUnit by it's HTML id
            * @return {<a href="YAHOO.widget.LayoutUnit.html">YAHOO.widget.LayoutUnit</a>} The LayoutUnit instance
            */
            getUnitById: function(id) {
                return YAHOO.widget.LayoutUnit.getLayoutUnitById(id);
            },
            /**
            * @method getUnitByPosition
            * @param {String} pos The position of the unit in this layout
            * @description Get the LayoutUnit by it's position in this layout
            * @return {<a href="YAHOO.widget.LayoutUnit.html">YAHOO.widget.LayoutUnit</a>} The LayoutUnit instance
            */
            getUnitByPosition: function(pos) {
                if (pos) {
                    pos = pos.toLowerCase();
                    if (this._units[pos]) {
                        return this._units[pos];
                    }
                    return false;
                }
                return false;
            },
            /**
            * @method removeUnit
            * @param {Object} unit The LayoutUnit that you want to remove
            * @description Remove the unit from this layout and resize the layout.
            */
            removeUnit: function(unit) {
                delete this._units[unit.get('position')];
                this.resize();
            },
            /**
            * @method addUnit
            * @param {Object} cfg The config for the LayoutUnit that you want to add
            * @description Add a unit to this layout and if the layout is rendered, resize the layout.
            * @return {<a href="YAHOO.widget.LayoutUnit.html">YAHOO.widget.LayoutUnit</a>} The LayoutUnit instance
            */
            addUnit: function(cfg) {
                if (!cfg.position) {
                    YAHOO.log('No position property passed', 'error', 'Layout');
                    return false;
                }
                if (this._units[cfg.position]) {
                    YAHOO.log('Position already exists', 'error', 'Layout');
                    return false;
                }
                YAHOO.log('Adding Unit at position: ' + cfg.position, 'info', 'Layout');
                var element = null,
                    el = null;

                if (cfg.id) {
                    if (Dom.get(cfg.id)) {
                        element = Dom.get(cfg.id);
                        delete cfg.id;

                    }
                }
                if (cfg.element) {
                    element = cfg.element;
                }

                if (!el) {
                    el = document.createElement('div');
                    var id = Dom.generateId();
                    el.id = id;
                }

                if (!element) {
                    element = document.createElement('div');
                }
                Dom.addClass(element, 'yui-layout-wrap');
                if (this.browser.ie && !this.browser.standardsMode) {
                    el.style.zoom = 1;
                    element.style.zoom = 1;
                }

                if (el.firstChild) {
                    el.insertBefore(element, el.firstChild);
                } else {
                    el.appendChild(element);
                }
                this._doc.appendChild(el);

                var h = false, w = false;

                if (cfg.height) {
                    h = parseInt(cfg.height, 10);
                }
                if (cfg.width) {
                    w = parseInt(cfg.width, 10);
                }
                var unitConfig = {};
                YAHOO.lang.augmentObject(unitConfig, cfg); // break obj ref

                unitConfig.parent = this;
                unitConfig.wrap = element;
                unitConfig.height = h;
                unitConfig.width = w;

                var unit = new YAHOO.widget.LayoutUnit(el, unitConfig);

                unit.on('heightChange', this.resize, { unit: unit }, this);
                unit.on('widthChange', this.resize, { unit: unit }, this);
                unit.on('gutterChange', this.resize, { unit: unit }, this);
                this._units[cfg.position] = unit;

                if (this._rendered) {
                    this.resize();
                }

                return unit;
            },
            /**
            * @private
            * @method _createUnits
            * @description Private method to create units from the config that was passed in.
            */
            _createUnits: function() {
                var units = this.get('units');
                for (var i in units) {
                    if (Lang.hasOwnProperty(units, i)) {
                        this.addUnit(units[i]);
                    }
                }
            },
            /**
            * @method resize
            * @param Boolean/Event set If set to false, it will NOT set the size, just perform the calculations (used for collapsing units). This can also have an attribute event passed to it.
            * @description Starts the chain of resize routines that will resize all the units.
            * @return {<a href="YAHOO.widget.Layout.html">YAHOO.widget.Layout</a>} The Layout instance
            */
            resize: function(set, info) {
                /*
                * Fixes bug #2528175
                * If the event comes from an attribute and the value hasn't changed, don't process it.
                */
                var ev = set;
                if (ev && ev.prevValue && ev.newValue) {
                    if (ev.prevValue == ev.newValue) {
                        if (info) {
                            if (info.unit) {
                                if (!info.unit.get('animate')) {
                                    set = false;
                                }
                            }
                        }
                    }
                }
                set = ((set === false) ? false : true);
                if (set) {
                    var retVal = this.fireEvent('beforeResize');
                    if (retVal === false) {
                        set = false;
                    }
                    if (this.browser.ie) {
                        if (this._isBody) {
                            Dom.removeClass(document.documentElement, 'yui-layout');
                            Dom.addClass(document.documentElement, 'yui-layout');
                        } else {
                            this.removeClass('yui-layout');
                            this.addClass('yui-layout');
                        }
                    }
                }
                this._setBodySize(set);
                if (set) {
                    this.fireEvent('resize', { target: this, sizes: this._sizes, event: ev });
                }
                return this;
            },
            /**
            * @private
            * @method _setupBodyElements
            * @description Sets up the main doc element when using the body as the main element.
            */
            _setupBodyElements: function() {
                this._doc = Dom.get('layout-doc');
                if (!this._doc) {
                    this._doc = document.createElement('div');
                    this._doc.id = 'layout-doc';
                    if (document.body.firstChild) {
                        document.body.insertBefore(this._doc, document.body.firstChild);
                    } else {
                        document.body.appendChild(this._doc);
                    }
                }
                this._createUnits();
                this._setBodySize();
                Event.on(window, 'resize', this.resize, this, true);
                Dom.addClass(this._doc, 'yui-layout-doc');
            },
            /**
            * @private
            * @method _setupElements
            * @description Sets up the main doc element when not using the body as the main element.
            */
            _setupElements: function() {
                this._doc = this.getElementsByClassName('yui-layout-doc')[0];
                if (!this._doc) {
                    this._doc = document.createElement('div');
                    this.get('element').appendChild(this._doc);
                }
                this._createUnits();
                this._setBodySize();
                Dom.addClass(this._doc, 'yui-layout-doc');
            },
            /**
            * @private
            * @property _isBody
            * @description Flag to determine if we are using the body as the root element.
            * @type Boolean
            */
            _isBody: null,
            /**
            * @private
            * @property _doc
            * @description Reference to the root element
            * @type HTMLElement
            */
            _doc: null,
            /**
            * @private
            * @method init
            * @description The Layout class' initialization method
            */
            init: function(p_oElement, p_oAttributes) {
                YAHOO.log('init', 'info', 'Layout');

                this._zIndex = 0;

                Layout.superclass.init.call(this, p_oElement, p_oAttributes);

                if (this.get('parent')) {
                    this._zIndex = this.get('parent')._zIndex + 10;
                }

                this._sizes = {};
                this._units = {};

                var id = p_oElement;
                if (!Lang.isString(id)) {
                    id = Dom.generateId(id);
                }
                Layout._instances[id] = this;
            },
            /**
            * @method render
            * @description This method starts the render process, applying classnames and creating elements
            * @return {<a href="YAHOO.widget.Layout.html">YAHOO.widget.Layout</a>} The Layout instance
            */
            render: function() {
                YAHOO.log('Render', 'info', 'Layout');
                this._stamp();
                var el = this.get('element');
                if (el && el.tagName && (el.tagName.toLowerCase() == 'body')) {
                    this._isBody = true;
                    Dom.addClass(document.body, 'yui-layout');
                    if (Dom.hasClass(document.body, 'yui-skin-sam')) {
                        //Move the class up so we can have a css chain
                        Dom.addClass(document.documentElement, 'yui-skin-sam');
                        Dom.removeClass(document.body, 'yui-skin-sam');
                    }
                    this._setupBodyElements();
                } else {
                    this._isBody = false;
                    this.addClass('yui-layout');
                    this._setupElements();
                }
                this.resize();
                this._rendered = true;
                this.fireEvent('render');

                return this;
            },
            /**
            * @private
            * @method _stamp
            * @description Stamps the root node with a secure classname for ease of use. Also sets the this.browser.standardsMode variable.
            */
            _stamp: function() {
                if (document.compatMode == 'CSS1Compat') {
                    this.browser.standardsMode = true;
                }
                if (window.location.href.toLowerCase().indexOf("https") === 0) {
                    Dom.addClass(document.documentElement, 'secure');
                    this.browser.secure = true;
                }
            },
            /**
            * @private
            * @method initAttributes
            * @description Processes the config
            */
            initAttributes: function(attr) {
                Layout.superclass.initAttributes.call(this, attr);
                /**
                * @attribute units
                * @description An array of config definitions for the LayoutUnits to add to this layout
                * @type Array
                */
                this.setAttributeConfig('units', {
                    writeOnce: true,
                    validator: YAHOO.lang.isArray,
                    value: attr.units || []
                });

                /**
                * @attribute minHeight
                * @description The minimum height in pixels
                * @type Number
                */
                this.setAttributeConfig('minHeight', {
                    value: attr.minHeight || false,
                    validator: YAHOO.lang.isNumber
                });

                /**
                * @attribute minWidth
                * @description The minimum width in pixels
                * @type Number
                */
                this.setAttributeConfig('minWidth', {
                    value: attr.minWidth || false,
                    validator: YAHOO.lang.isNumber
                });

                /**
                * @attribute height
                * @description The height in pixels
                * @type Number
                */
                this.setAttributeConfig('height', {
                    value: attr.height || false,
                    validator: YAHOO.lang.isNumber,
                    method: function(h) {
                        if (h < 0) {
                            h = 0;
                        }
                        this.setStyle('height', h + 'px');
                    }
                });

                /**
                * @attribute width
                * @description The width in pixels
                * @type Number
                */
                this.setAttributeConfig('width', {
                    value: attr.width || false,
                    validator: YAHOO.lang.isNumber,
                    method: function(w) {
                        if (w < 0) {
                            w = 0;
                        }
                        this.setStyle('width', w + 'px');
                    }
                });

                /**
                * @attribute parent
                * @description If this layout is to be used as a child of another Layout instance, this config will bind the resize events together.
                * @type Object YAHOO.widget.Layout
                */
                this.setAttributeConfig('parent', {
                    writeOnce: true,
                    value: attr.parent || false,
                    method: function(p) {
                        if (p) {
                            p.on('resize', this.resize, this, true);
                        }
                    }
                });
            },
            /**
            * @method destroy
            * @description Removes this layout from the page and destroys all units that it contains. This will destroy all data inside the layout and it's children.
            */
            destroy: function() {
                var par = this.get('parent');
                if (par) {
                    par.removeListener('resize', this.resize, this, true);
                }
                Event.removeListener(window, 'resize', this.resize, this, true);

                this.unsubscribeAll();
                for (var u in this._units) {
                    if (Lang.hasOwnProperty(this._units, u)) {
                        if (this._units[u]) {
                            this._units[u].destroy(true);
                        }
                    }
                }

                Event.purgeElement(this.get('element'));
                this.get('parentNode').removeChild(this.get('element'));

                delete YAHOO.widget.Layout._instances[this.get('id')];
                //Brutal Object Destroy
                for (var i in this) {
                    if (Lang.hasOwnProperty(this, i)) {
                        this[i] = null;
                        delete this[i];
                    }
                }

                if (par) {
                    par.resize();
                }
            },
            /**
            * @method toString
            * @description Returns a string representing the Layout.
            * @return {String}
            */
            toString: function() {
                if (this.get) {
                    return 'Layout #' + this.get('id');
                }
                return 'Layout';
            }
        });
        /**
        * @event resize
        * @description Fired when this.resize is called
        * @type YAHOO.util.CustomEvent
        */
        /**
        * @event startResize
        * @description Fired when the Resize Utility for a Unit fires it's startResize Event.
        * @type YAHOO.util.CustomEvent
        */
        /**
        * @event beforeResize
        * @description Fires at the beginning of the resize method. If you return false, the resize is cancelled.
        * @type YAHOO.util.CustomEvent
        */
        /**
        * @event render
        * @description Fired after the render method completes.
        * @type YAHOO.util.CustomEvent
        */

        YAHOO.widget.Layout = Layout;
    })();

Copyright © 2009 Yahoo! Inc. All rights reserved.