API: datatable ColumnSet.js (YUI Library)

# Yahoo! UI Library

### DataTable Widget 2.9.0

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

    /****************************************************************************/
    /****************************************************************************/
    /****************************************************************************/

    var Dom = YAHOO.util.Dom;

    /**
     * The ColumnSet class defines and manages a DataTable's Columns,
     * including nested hierarchies and access to individual Column instances.
     *
     * @namespace YAHOO.widget
     * @class ColumnSet
     * @uses YAHOO.util.EventProvider
     * @constructor
     * @param aDefinitions {Object[]} Array of object literals that define cells in
     * the THEAD.
     */
    YAHOO.widget.ColumnSet = function(aDefinitions) {
        this._sId = Dom.generateId(null, "yui-cs"); // "yui-cs" + YAHOO.widget.ColumnSet._nCount;

        // First clone the defs
        aDefinitions = YAHOO.widget.DataTable._cloneObject(aDefinitions);
        this._init(aDefinitions);

        YAHOO.widget.ColumnSet._nCount++;
        YAHOO.log("ColumnSet initialized", "info", this.toString());
    };

    /////////////////////////////////////////////////////////////////////////////
    //
    // Private member variables
    //
    /////////////////////////////////////////////////////////////////////////////

    /**
     * Internal class variable to index multiple ColumnSet instances.
     *
     * @property ColumnSet._nCount
     * @type Number
     * @private
     * @static
     */
    YAHOO.widget.ColumnSet._nCount = 0;

    YAHOO.widget.ColumnSet.prototype = {
        /**
         * Unique instance name.
         *
         * @property _sId
         * @type String
         * @private
         */
        _sId : null,

        /**
         * Array of object literal Column definitions passed to the constructor.
         *
         * @property _aDefinitions
         * @type Object[]
         * @private
         */
        _aDefinitions : null,

        /////////////////////////////////////////////////////////////////////////////
        //
        // Public member variables
        //
        /////////////////////////////////////////////////////////////////////////////

        /**
         * Top-down tree representation of Column hierarchy.
         *
         * @property tree
         * @type YAHOO.widget.Column[]
         */
        tree : null,

        /**
         * Flattened representation of all Columns.
         *
         * @property flat
         * @type YAHOO.widget.Column[]
         * @default []
         */
        flat : null,

        /**
         * Array of Columns that map one-to-one to a table column.
         *
         * @property keys
         * @type YAHOO.widget.Column[]
         * @default []
         */
        keys : null,

        /**
         * ID index of nested parent hierarchies for HEADERS accessibility attribute.
         *
         * @property headers
         * @type String[]
         * @default []
         */
        headers : null,

        /////////////////////////////////////////////////////////////////////////////
        //
        // Private methods
        //
        /////////////////////////////////////////////////////////////////////////////

        /**
         * Initializes ColumnSet instance with data from Column definitions.
         *
         * @method _init
         * @param aDefinitions {Object[]} Array of object literals that define cells in
         * the THEAD .
         * @private
         */

        _init : function(aDefinitions) {
            // DOM tree representation of all Columns
            var tree = [];
            // Flat representation of all Columns
            var flat = [];
            // Flat representation of only Columns that are meant to display data
            var keys = [];
            // Array of HEADERS attribute values for all keys in the "keys" array
            var headers = [];

            // Tracks current node list depth being tracked
            var nodeDepth = -1;

            // Internal recursive function to define Column instances
            var parseColumns = function(nodeList, parent) {
                // One level down
                nodeDepth++;

                // Create corresponding tree node if not already there for this depth
                if(!tree[nodeDepth]) {
                    tree[nodeDepth] = [];
                }

                // Parse each node at this depth for attributes and any children
                for(var j=0; j<nodeList.length; j++) {
                    var currentNode = nodeList[j];

                    // Instantiate a new Column for each node
                    var oColumn = new YAHOO.widget.Column(currentNode);

                    // Cross-reference Column ID back to the original object literal definition
                    currentNode.yuiColumnId = oColumn._sId;

                    // Add the new Column to the flat list
                    flat.push(oColumn);

                    // Assign its parent as an attribute, if applicable
                    if(parent) {
                        oColumn._oParent = parent;
                    }

                    // The Column has descendants
                    if(YAHOO.lang.isArray(currentNode.children)) {
                        oColumn.children = currentNode.children;

                        // Determine COLSPAN value for this Column
                        var terminalChildNodes = 0;
                        var countTerminalChildNodes = function(ancestor) {
                            var descendants = ancestor.children;
                            // Drill down each branch and count terminal nodes
                            for(var k=0; k<descendants.length; k++) {
                                // Keep drilling down
                                if(YAHOO.lang.isArray(descendants[k].children)) {
                                    countTerminalChildNodes(descendants[k]);
                                }
                                // Reached branch terminus
                                else {
                                    terminalChildNodes++;
                                }
                            }
                        };
                        countTerminalChildNodes(currentNode);
                        oColumn._nColspan = terminalChildNodes;

                        // Cascade certain properties to children if not defined on their own
                        var currentChildren = currentNode.children;
                        for(var k=0; k<currentChildren.length; k++) {
                            var child = currentChildren[k];
                            if(oColumn.className && (child.className === undefined)) {
                                child.className = oColumn.className;
                            }
                            if(oColumn.editor && (child.editor === undefined)) {
                                child.editor = oColumn.editor;
                            }
                            //TODO: Deprecated
                            if(oColumn.editorOptions && (child.editorOptions === undefined)) {
                                child.editorOptions = oColumn.editorOptions;
                            }
                            if(oColumn.formatter && (child.formatter === undefined)) {
                                child.formatter = oColumn.formatter;
                            }
                            if(oColumn.resizeable && (child.resizeable === undefined)) {
                                child.resizeable = oColumn.resizeable;
                            }
                            if(oColumn.sortable && (child.sortable === undefined)) {
                                child.sortable = oColumn.sortable;
                            }
                            if(oColumn.hidden) {
                                child.hidden = true;
                            }
                            if(oColumn.width && (child.width === undefined)) {
                                child.width = oColumn.width;
                            }
                            if(oColumn.minWidth && (child.minWidth === undefined)) {
                                child.minWidth = oColumn.minWidth;
                            }
                            if(oColumn.maxAutoWidth && (child.maxAutoWidth === undefined)) {
                                child.maxAutoWidth = oColumn.maxAutoWidth;
                            }
                            // Backward compatibility
                            if(oColumn.type && (child.type === undefined)) {
                                child.type = oColumn.type;
                            }
                            if(oColumn.type && !oColumn.formatter) {
                                YAHOO.log("The property type has been" +
                                " deprecated in favor of formatter", "warn", oColumn.toString());
                                oColumn.formatter = oColumn.type;
                            }
                            if(oColumn.text && !YAHOO.lang.isValue(oColumn.label)) {
                                YAHOO.log("The property text has been" +
                                " deprecated in favor of label", "warn", oColumn.toString());
                                oColumn.label = oColumn.text;
                            }
                            if(oColumn.parser) {
                                YAHOO.log("The property parser is no longer supported",
                                "warn", this.toString());
                            }
                            if(oColumn.sortOptions && ((oColumn.sortOptions.ascFunction) ||
                                    (oColumn.sortOptions.descFunction))) {
                                YAHOO.log("The properties sortOptions.ascFunction and " +
                                " sortOptions.descFunction have been deprecated in favor " +
                                " of sortOptions.sortFunction", "warn", oColumn.toString());
                            }
                        }

                        // The children themselves must also be parsed for Column instances
                        if(!tree[nodeDepth+1]) {
                            tree[nodeDepth+1] = [];
                        }
                        parseColumns(currentChildren, oColumn);
                    }
                    // This Column does not have any children
                    else {
                        oColumn._nKeyIndex = keys.length;
                        oColumn._nColspan = 1;
                        keys.push(oColumn);
                    }

                    // Add the Column to the top-down tree
                    tree[nodeDepth].push(oColumn);
                }
                nodeDepth--;
            };

            // Parse out Column instances from the array of object literals
            if(YAHOO.lang.isArray(aDefinitions)) {
                parseColumns(aDefinitions);

                // Store the array
                this._aDefinitions = aDefinitions;
            }
            else {
                YAHOO.log("Could not initialize ColumnSet due to invalid definitions","error");
                return null;
            }

            var i;

            // Determine ROWSPAN value for each Column in the tree
            var parseTreeForRowspan = function(tree) {
                var maxRowDepth = 1;
                var currentRow;
                var currentColumn;

                // Calculate the max depth of descendants for this row
                var countMaxRowDepth = function(row, tmpRowDepth) {
                    tmpRowDepth = tmpRowDepth || 1;

                    for(var n=0; n<row.length; n++) {
                        var col = row[n];
                        // Column has children, so keep counting
                        if(YAHOO.lang.isArray(col.children)) {
                            tmpRowDepth++;
                            countMaxRowDepth(col.children, tmpRowDepth);
                            tmpRowDepth--;
                        }
                        // No children, is it the max depth?
                        else {
                            if(tmpRowDepth > maxRowDepth) {
                                maxRowDepth = tmpRowDepth;
                            }
                        }

                    }
                };

                // Count max row depth for each row
                for(var m=0; m<tree.length; m++) {
                    currentRow = tree[m];
                    countMaxRowDepth(currentRow);

                    // Assign the right ROWSPAN values to each Column in the row
                    for(var p=0; p<currentRow.length; p++) {
                        currentColumn = currentRow[p];
                        if(!YAHOO.lang.isArray(currentColumn.children)) {
                            currentColumn._nRowspan = maxRowDepth;
                        }
                        else {
                            currentColumn._nRowspan = 1;
                        }
                    }

                    // Reset counter for next row
                    maxRowDepth = 1;
                }
            };
            parseTreeForRowspan(tree);

            // Store tree index values
            for(i=0; i<tree[0].length; i++) {
                tree[0][i]._nTreeIndex = i;
            }

            // Store header relationships in an array for HEADERS attribute
            var recurseAncestorsForHeaders = function(i, oColumn) {
                headers[i].push(oColumn.getSanitizedKey());
                if(oColumn._oParent) {
                    recurseAncestorsForHeaders(i, oColumn._oParent);
                }
            };
            for(i=0; i<keys.length; i++) {
                headers[i] = [];
                recurseAncestorsForHeaders(i, keys[i]);
                headers[i] = headers[i].reverse();
            }

            // Save to the ColumnSet instance
            this.tree = tree;
            this.flat = flat;
            this.keys = keys;
            this.headers = headers;
        },

        /////////////////////////////////////////////////////////////////////////////
        //
        // Public methods
        //
        /////////////////////////////////////////////////////////////////////////////

        /**
         * Returns unique name of the ColumnSet instance.
         *
         * @method getId
         * @return {String} Unique name of the ColumnSet instance.
         */

        getId : function() {
            return this._sId;
        },

        /**
         * ColumnSet instance name, for logging.
         *
         * @method toString
         * @return {String} Unique name of the ColumnSet instance.
         */

        toString : function() {
            return "ColumnSet instance " + this._sId;
        },

        /**
         * Public accessor to the definitions array.
         *
         * @method getDefinitions
         * @return {Object[]} Array of object literal Column definitions.
         */

        getDefinitions : function() {
            var aDefinitions = this._aDefinitions;

            // Internal recursive function to define Column instances
            var parseColumns = function(nodeList, oSelf) {
                // Parse each node at this depth for attributes and any children
                for(var j=0; j<nodeList.length; j++) {
                    var currentNode = nodeList[j];

                    // Get the Column for each node
                    var oColumn = oSelf.getColumnById(currentNode.yuiColumnId);

                    if(oColumn) {
                        // Update the current values
                        var oDefinition = oColumn.getDefinition();
                        for(var name in oDefinition) {
                            if(YAHOO.lang.hasOwnProperty(oDefinition, name)) {
                                currentNode[name] = oDefinition[name];
                            }
                        }
                    }

                    // The Column has descendants
                    if(YAHOO.lang.isArray(currentNode.children)) {
                        // The children themselves must also be parsed for Column instances
                        parseColumns(currentNode.children, oSelf);
                    }
                }
            };

            parseColumns(aDefinitions, this);
            this._aDefinitions = aDefinitions;
            return aDefinitions;
        },

        /**
         * Returns Column instance with given ID.
         *
         * @method getColumnById
         * @param column {String} Column ID.
         * @return {YAHOO.widget.Column} Column instance.
         */

        getColumnById : function(column) {
            if(YAHOO.lang.isString(column)) {
                var allColumns = this.flat;
                for(var i=allColumns.length-1; i>-1; i--) {
                    if(allColumns[i]._sId === column) {
                        return allColumns[i];
                    }
                }
            }
            return null;
        },

        /**
         * Returns Column instance with given key or ColumnSet key index.
         *
         * @method getColumn
         * @param column {String | Number} Column key or ColumnSet key index.
         * @return {YAHOO.widget.Column} Column instance.
         */

        getColumn : function(column) {
            if(YAHOO.lang.isNumber(column) && this.keys[column]) {
                return this.keys[column];
            }
            else if(YAHOO.lang.isString(column)) {
                var allColumns = this.flat;
                var aColumns = [];
                for(var i=0; i<allColumns.length; i++) {
                    if(allColumns[i].key === column) {
                        aColumns.push(allColumns[i]);
                    }
                }
                if(aColumns.length === 1) {
                    return aColumns[0];
                }
                else if(aColumns.length > 1) {
                    return aColumns;
                }
            }
            return null;
        },

        /**
         * Public accessor returns array of given Column's desendants (if any), including itself.
         *
         * @method getDescendants
         * @parem {YAHOO.widget.Column} Column instance.
         * @return {Array} Array including the Column itself and all descendants (if any).
         */
        getDescendants : function(oColumn) {
            var oSelf = this;
            var allDescendants = [];
            var i;

            // Recursive function to loop thru all children
            var parse = function(oParent) {
                allDescendants.push(oParent);
                // This Column has children
                if(oParent.children) {
                    for(i=0; i<oParent.children.length; i++) {
                        parse(oSelf.getColumn(oParent.children[i].key));
                    }
                }
            };
            parse(oColumn);

            return allDescendants;
        }
    };

    /****************************************************************************/
    /****************************************************************************/
    /****************************************************************************/

    /**
     * The Column class defines and manages attributes of DataTable Columns
     *
     * @namespace YAHOO.widget
     * @class Column
     * @constructor
     * @param oConfigs {Object} Object literal of definitions.
     */
    YAHOO.widget.Column = function(oConfigs) {
        this._sId = Dom.generateId(null, "yui-col"); // "yui-col" + YAHOO.widget.Column._nCount;

        // Object literal defines Column attributes
        if(oConfigs && YAHOO.lang.isObject(oConfigs)) {
            for(var sConfig in oConfigs) {
                if(sConfig) {
                    this[sConfig] = oConfigs[sConfig];
                }
            }
        }

        // Assign a key if not found
        if(!YAHOO.lang.isValue(this.key)) {
            this.key = Dom.generateId(null, "yui-dt-col"); //"yui-dt-col" + YAHOO.widget.Column._nCount;
        }

        // Assign a field if not found, defaults to key
        if(!YAHOO.lang.isValue(this.field)) {
            this.field = this.key;
        }

        // Increment counter
        YAHOO.widget.Column._nCount++;

        // Backward compatibility
        if(this.width && !YAHOO.lang.isNumber(this.width)) {
            this.width = null;
            YAHOO.log("The Column property width must be a number", "warn", this.toString());
        }
        if(this.editor && YAHOO.lang.isString(this.editor)) {
            this.editor = new YAHOO.widget.CellEditor(this.editor, this.editorOptions);
            YAHOO.log("The Column property editor must be an instance of YAHOO.widget.CellEditor", "warn", this.toString());
        }
    };

    /////////////////////////////////////////////////////////////////////////////
    //
    // Private member variables
    //
    /////////////////////////////////////////////////////////////////////////////

    YAHOO.lang.augmentObject(YAHOO.widget.Column, {
        /**
         * Internal class variable to index multiple Column instances.
         *
         * @property Column._nCount
         * @type Number
         * @private
         * @static
         */
        _nCount : 0,

        formatCheckbox : function(elCell, oRecord, oColumn, oData) {
            YAHOO.log("The method YAHOO.widget.Column.formatCheckbox() has been" +
            " deprecated in favor of YAHOO.widget.DataTable.formatCheckbox()", "warn",
            "YAHOO.widget.Column.formatCheckbox");
            YAHOO.widget.DataTable.formatCheckbox(elCell, oRecord, oColumn, oData);
        },

        formatCurrency : function(elCell, oRecord, oColumn, oData) {
            YAHOO.log("The method YAHOO.widget.Column.formatCurrency() has been" +
            " deprecated in favor of YAHOO.widget.DataTable.formatCurrency()", "warn",
            "YAHOO.widget.Column.formatCurrency");
            YAHOO.widget.DataTable.formatCurrency(elCell, oRecord, oColumn, oData);
        },

        formatDate : function(elCell, oRecord, oColumn, oData) {
            YAHOO.log("The method YAHOO.widget.Column.formatDate() has been" +
            " deprecated in favor of YAHOO.widget.DataTable.formatDate()", "warn",
            "YAHOO.widget.Column.formatDate");
            YAHOO.widget.DataTable.formatDate(elCell, oRecord, oColumn, oData);
        },

        formatEmail : function(elCell, oRecord, oColumn, oData) {
            YAHOO.log("The method YAHOO.widget.Column.formatEmail() has been" +
            " deprecated in favor of YAHOO.widget.DataTable.formatEmail()", "warn",
            "YAHOO.widget.Column.formatEmail");
            YAHOO.widget.DataTable.formatEmail(elCell, oRecord, oColumn, oData);
        },

        formatLink : function(elCell, oRecord, oColumn, oData) {
            YAHOO.log("The method YAHOO.widget.Column.formatLink() has been" +
            " deprecated in favor of YAHOO.widget.DataTable.formatLink()", "warn",
            "YAHOO.widget.Column.formatLink");
            YAHOO.widget.DataTable.formatLink(elCell, oRecord, oColumn, oData);
        },

        formatNumber : function(elCell, oRecord, oColumn, oData) {
            YAHOO.log("The method YAHOO.widget.Column.formatNumber() has been" +
            " deprecated in favor of YAHOO.widget.DataTable.formatNumber()", "warn",
            "YAHOO.widget.Column.formatNumber");
            YAHOO.widget.DataTable.formatNumber(elCell, oRecord, oColumn, oData);
        },

        formatSelect : function(elCell, oRecord, oColumn, oData) {
            YAHOO.log("The method YAHOO.widget.Column.formatSelect() has been" +
            " deprecated in favor of YAHOO.widget.DataTable.formatDropdown()", "warn",
            "YAHOO.widget.Column.formatSelect");
            YAHOO.widget.DataTable.formatDropdown(elCell, oRecord, oColumn, oData);
        }
    });

    YAHOO.widget.Column.prototype = {
        /**
         * Unique String identifier assigned at instantiation.
         *
         * @property _sId
         * @type String
         * @private
         */
        _sId : null,

        /**
         * Reference to Column's current position index within its ColumnSet's keys
         * array, if applicable. This property only applies to non-nested and bottom-
         * level child Columns.
         *
         * @property _nKeyIndex
         * @type Number
         * @private
         */
        _nKeyIndex : null,

        /**
         * Reference to Column's current position index within its ColumnSet's tree
         * array, if applicable. This property only applies to non-nested and top-
         * level parent Columns.
         *
         * @property _nTreeIndex
         * @type Number
         * @private
         */
        _nTreeIndex : null,

        /**
         * Number of table cells the Column spans.
         *
         * @property _nColspan
         * @type Number
         * @private
         */
        _nColspan : 1,

        /**
         * Number of table rows the Column spans.
         *
         * @property _nRowspan
         * @type Number
         * @private
         */
        _nRowspan : 1,

        /**
         * Column's parent Column instance, or null.
         *
         * @property _oParent
         * @type YAHOO.widget.Column
         * @private
         */
        _oParent : null,

        /**
         * The DOM reference to the associated TH element.
         *
         * @property _elTh
         * @type HTMLElement
         * @private
         */
        _elTh : null,

        /**
         * The DOM reference to the associated TH element's liner DIV element.
         *
         * @property _elThLiner
         * @type HTMLElement
         * @private
         */
        _elThLiner : null,

        /**
         * The DOM reference to the associated TH element's label SPAN element.
         *
         * @property _elThLabel
         * @type HTMLElement
         * @private
         */
        _elThLabel : null,

        /**
         * The DOM reference to the associated resizerelement (if any).
         *
         * @property _elResizer
         * @type HTMLElement
         * @private
         */
        _elResizer : null,

        /**
         * Internal width tracker.
         *
         * @property _nWidth
         * @type Number
         * @private
         */
        _nWidth : null,

        /**
         * For unreg() purposes, a reference to the Column's DragDrop instance.
         *
         * @property _dd
         * @type YAHOO.util.DragDrop
         * @private
         */
        _dd : null,

        /**
         * For unreg() purposes, a reference to the Column resizer's DragDrop instance.
         *
         * @property _ddResizer
         * @type YAHOO.util.DragDrop
         * @private
         */
        _ddResizer : null,

        /////////////////////////////////////////////////////////////////////////////
        //
        // Public member variables
        //
        /////////////////////////////////////////////////////////////////////////////

        /**
         * Unique name, required. If "label" property is not provided, the "key"
         * value will be treated as markup and inserted into the DOM as innerHTML.
         *
         * @property key
         * @type String|HTML
         */
        key : null,

        /**
         * Associated database field, or null.
         *
         * @property field
         * @type String
         */
        field : null,

        /**
         * Value displayed as Column header in the TH element. String value is
         * treated as markup and inserted into the DOM as innerHTML.
         *
         * @property label
         * @type HTML
         */
        label : null,

        /**
         * Column head cell ABBR for accessibility.
         *
         * @property abbr
         * @type String
         */
        abbr : null,

        /**
         * Array of object literals that define children (nested headers) of a Column.
         *
         * @property children
         * @type Object[]
         */
        children : null,

        /**
         * Column width (in pixels).
         *
         * @property width
         * @type Number
         */
        width : null,

        /**
         * Minimum Column width (in pixels).
         *
         * @property minWidth
         * @type Number
         * @default null
         */
        minWidth : null,

        /**
         * When a width is not defined for a Column, maxAutoWidth defines an upper
         * limit that the Column should be auto-sized to. If resizeable is enabled,
         * users may still resize to a greater width. Most useful for Columns intended
         * to hold long unbroken, unwrapped Strings, such as URLs, to prevent very
         * wide Columns from disrupting visual readability by inducing truncation.
         *
         * @property maxAutoWidth
         * @type Number
         * @default null
         */
        maxAutoWidth : null,

        /**
         * True if Column is in hidden state.
         *
         * @property hidden
         * @type Boolean
         * @default false
         */
        hidden : false,

        /**
         * True if Column is in selected state.
         *
         * @property selected
         * @type Boolean
         * @default false
         */
        selected : false,

        /**
         * Custom CSS class or array of classes to be applied to every cell in the Column.
         *
         * @property className
         * @type String || String[]
         */
        className : null,

        /**
         * Cell formatter function, or a shortcut pointer to a function in the
         * DataTable.Formatter object. The function, called from the DataTable's
         * formatCell method, renders markup into the cell liner
         * element and accepts the following arguments:
         * <dl>
         *    <dt>elLiner</dt>
         *    <dd>The element to write innerHTML to.</dd>
         *    <dt>oRecord</dt>
         *    <dd>The associated Record for the row.</dd>
         *    <dt>oColumn</dt>
         *    <dd>The Column instance for the cell.</dd>
         *    <dt>oData</dt>
         *    <dd>The data value for the cell.</dd>
         * </dl>
         *
         * @property formatter
         * @type String || HTMLFunction
         */
        formatter : null,

        /**
         * Config passed to YAHOO.util.Number.format() by the 'currency' Column formatter.
         *
         * @property currencyOptions
         * @type Object
         * @default null
         */
        currencyOptions : null,

        /**
         * Config passed to YAHOO.util.Date.format() by the 'date' Column formatter.
         *
         * @property dateOptions
         * @type Object
         * @default null
         */
        dateOptions : null,

        /**
         * Array of dropdown values for formatter:"dropdown" cases. Can either be a
         * simple array (e.g., ["Alabama","Alaska","Arizona","Arkansas"]) or a an
         * array of objects (e.g., [{label:"Alabama", value:"AL"},
         * {label:"Alaska", value:"AK"}, {label:"Arizona", value:"AZ"},
         * {label:"Arkansas", value:"AR"}]). String values are treated as markup and
         * inserted into the DOM as innerHTML.
         *
         * @property dropdownOptions
         * @type HTML[] | Object[]
         */
        dropdownOptions : null,

        /**
         * A CellEditor instance, otherwise Column is not editable.
         *
         * @property editor
         * @type YAHOO.widget.CellEditor
         */
        editor : null,

        /**
         * True if Column is resizeable, false otherwise. The Drag & Drop Utility is
         * required to enable this feature. Only bottom-level and non-nested Columns are
         * resizeble.
         *
         * @property resizeable
         * @type Boolean
         * @default false
         */
        resizeable : false,

        /**
         * True if Column is sortable, false otherwise.
         *
         * @property sortable
         * @type Boolean
         * @default false
         */
        sortable : false,

        /**
         * @property sortOptions.defaultOrder
         * @deprecated Use sortOptions.defaultDir.
         */
        /**
         * Default sort direction for Column: YAHOO.widget.DataTable.CLASS_ASC or YAHOO.widget.DataTable.CLASS_DESC.
         *
         * @property sortOptions.defaultDir
         * @type String
         * @default null
         */
        /**
         * Custom field to sort on.
         *
         * @property sortOptions.field
         * @type String
         * @default null
         */
        /**
         * Custom sort handler. Signature: sortFunction(a, b, desc, field) where field is the sortOptions.field value
         *
         * @property sortOptions.sortFunction
         * @type Function
         * @default null
         */
        sortOptions : null,

        /////////////////////////////////////////////////////////////////////////////
        //
        // Public methods
        //
        /////////////////////////////////////////////////////////////////////////////

        /**
         * Returns unique ID string.
         *
         * @method getId
         * @return {String} Unique ID string.
         */
        getId : function() {
            return this._sId;
        },

        /**
         * Column instance name, for logging.
         *
         * @method toString
         * @return {String} Column's unique name.
         */
        toString : function() {
            return "Column instance " + this._sId;
        },

        /**
         * Returns object literal definition.
         *
         * @method getDefinition
         * @return {Object} Object literal definition.
         */
        getDefinition : function() {
            var oDefinition = {};

            // Update the definition
            oDefinition.abbr = this.abbr;
            oDefinition.className = this.className;
            oDefinition.editor = this.editor;
            oDefinition.editorOptions = this.editorOptions; //TODO: deprecated
            oDefinition.field = this.field;
            oDefinition.formatter = this.formatter;
            oDefinition.hidden = this.hidden;
            oDefinition.key = this.key;
            oDefinition.label = this.label;
            oDefinition.minWidth = this.minWidth;
            oDefinition.maxAutoWidth = this.maxAutoWidth;
            oDefinition.resizeable = this.resizeable;
            oDefinition.selected = this.selected;
            oDefinition.sortable = this.sortable;
            oDefinition.sortOptions = this.sortOptions;
            oDefinition.width = this.width;

            // Bug 2529147
            oDefinition._calculatedWidth = this._calculatedWidth;

            return oDefinition;
        },

        /**
         * Returns unique Column key.
         *
         * @method getKey
         * @return {String} Column key.
         */
        getKey : function() {
            return this.key;
        },

        /**
         * Returns field.
         *
         * @method getField
         * @return {String} Column field.
         */
        getField : function() {
            return this.field;
        },

        /**
         * Returns Column key which has been sanitized for DOM (class and ID) usage
         * starts with letter, contains only letters, numbers, hyphen, or period.
         *
         * @method getSanitizedKey
         * @return {String} Sanitized Column key.
         */
        getSanitizedKey : function() {
            return this.getKey().replace(/[^\w\-]/g,"");
        },

        /**
         * Public accessor returns Column's current position index within its
         * ColumnSet's keys array, if applicable. Only non-nested and bottom-level
         * child Columns will return a value.
         *
         * @method getKeyIndex
         * @return {Number} Position index, or null.
         */
        getKeyIndex : function() {
            return this._nKeyIndex;
        },

        /**
         * Public accessor returns Column's current position index within its
         * ColumnSet's tree array, if applicable. Only non-nested and top-level parent
         * Columns will return a value;
         *
         * @method getTreeIndex
         * @return {Number} Position index, or null.
         */
        getTreeIndex : function() {
            return this._nTreeIndex;
        },

        /**
         * Public accessor returns Column's parent instance if any, or null otherwise.
         *
         * @method getParent
         * @return {YAHOO.widget.Column} Column's parent instance.
         */
        getParent : function() {
            return this._oParent;
        },

        /**
         * Public accessor returns Column's calculated COLSPAN value.
         *
         * @method getColspan
         * @return {Number} Column's COLSPAN value.
         */
        getColspan : function() {
            return this._nColspan;
        },
        // Backward compatibility
        getColSpan : function() {
            YAHOO.log("The method getColSpan() has been" +
            " deprecated in favor of getColspan()", "warn", this.toString());
            return this.getColspan();
        },

        /**
         * Public accessor returns Column's calculated ROWSPAN value.
         *
         * @method getRowspan
         * @return {Number} Column's ROWSPAN value.
         */
        getRowspan : function() {
            return this._nRowspan;
        },

        /**
         * Returns DOM reference to the key TH element.
         *
         * @method getThEl
         * @return {HTMLElement} TH element.
         */
        getThEl : function() {
            return this._elTh;
        },

        /**
         * Returns DOM reference to the TH's liner DIV element. Introduced since
         * resizeable Columns may have an extra resizer liner, making the DIV liner
         * not reliably the TH element's first child.
         *
         * @method getThLInerEl
         * @return {HTMLElement} TH element.
         */
        getThLinerEl : function() {
            return this._elThLiner;
        },

        /**
         * Returns DOM reference to the resizer element, or null.
         *
         * @method getResizerEl
         * @return {HTMLElement} DIV element.
         */
        getResizerEl : function() {
            return this._elResizer;
        },

        // Backward compatibility
        /**
         * @method getColEl
         * @deprecated Use getThEl
         */
        getColEl : function() {
            YAHOO.log("The method getColEl() has been" +
            " deprecated in favor of getThEl()", "warn",
            this.toString());
            return this.getThEl();
        },
        getIndex : function() {
            YAHOO.log("The method getIndex() has been" +
            " deprecated in favor of getKeyIndex()", "warn",
            this.toString());
            return this.getKeyIndex();
        },
        format : function() {
            YAHOO.log("The method format() has been deprecated in favor of the " +
            "DataTable method formatCell()", "error", this.toString());
        }
    };

    /****************************************************************************/
    /****************************************************************************/
    /****************************************************************************/

    /**
     * Sort static utility to support Column sorting.
     *
     * @namespace YAHOO.util
     * @class Sort
     * @static
     */
    YAHOO.util.Sort = {
        /////////////////////////////////////////////////////////////////////////////
        //
        // Public methods
        //
        /////////////////////////////////////////////////////////////////////////////

        /**
         * Comparator function for simple case-insensitive string sorting.
         *
         * @method compare
         * @param a {Object} First sort argument.
         * @param b {Object} Second sort argument.
         * @param desc {Boolean} True if sort direction is descending, false if
         * sort direction is ascending.
         * @return {Boolean} Return -1 when a < b. Return 0 when a = b.
         * Return 1 when a > b.
         */
        compare: function(a, b, desc) {
            if((a === null) || (typeof a == "undefined")) {
                if((b === null) || (typeof b == "undefined")) {
                    return 0;
                }
                else {
                    return 1;
                }
            }
            else if((b === null) || (typeof b == "undefined")) {
                return -1;
            }

            if(a.constructor == String) {
                a = a.toLowerCase();
            }
            if(b.constructor == String) {
                b = b.toLowerCase();
            }
            if(a < b) {
                return (desc) ? 1 : -1;
            }
            else if (a > b) {
                return (desc) ? -1 : 1;
            }
            else {
                return 0;
            }
        }
    };

    /****************************************************************************/
    /****************************************************************************/
    /****************************************************************************/

    /**
     * ColumnDD subclasses DragDrop to support rearrangeable Columns.
     *
     * @namespace YAHOO.util
     * @class ColumnDD
     * @extends YAHOO.util.DDProxy
     * @constructor
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
     * @param oColumn {YAHOO.widget.Column} Column instance.
     * @param elTh {HTMLElement} TH element reference.
     * @param elTarget {HTMLElement} Drag target element.
     */
    YAHOO.widget.ColumnDD = function(oDataTable, oColumn, elTh, elTarget) {
        if(oDataTable && oColumn && elTh && elTarget) {
            this.datatable = oDataTable;
            this.table = oDataTable.getTableEl();
            this.column = oColumn;
            this.headCell = elTh;
            this.pointer = elTarget;
            this.newIndex = null;
            this.init(elTh);
            this.initFrame(); // Needed for DDProxy
            this.invalidHandleTypes = {};

            // Set top/bottom padding to account for children of nested columns
            this.setPadding(10, 0, (this.datatable.getTheadEl().offsetHeight + 10) , 0);

            YAHOO.util.Event.on(window, 'resize', function() {
                this.initConstraints();
            }, this, true);
        }
        else {
            YAHOO.log("Column dragdrop could not be created","warn",oDataTable.toString());
        }
    };

    if(YAHOO.util.DDProxy) {
        YAHOO.extend(YAHOO.widget.ColumnDD, YAHOO.util.DDProxy, {
            initConstraints: function() {
                //Get the top, right, bottom and left positions
                var region = YAHOO.util.Dom.getRegion(this.table),
                    //Get the element we are working on
                    el = this.getEl(),
                    //Get the xy position of it
                    xy = YAHOO.util.Dom.getXY(el),
                    //Get the width and height
                    width = parseInt(YAHOO.util.Dom.getStyle(el, 'width'), 10),
                    height = parseInt(YAHOO.util.Dom.getStyle(el, 'height'), 10),
                    //Set left to x minus left
                    left = ((xy[0] - region.left) + 15), //Buffer of 15px
                    //Set right to right minus x minus width
                    right = ((region.right - xy[0] - width) + 15);

                //Set the constraints based on the above calculations
                this.setXConstraint(left, right);
                this.setYConstraint(10, 10);
            },
            _resizeProxy: function() {
                YAHOO.widget.ColumnDD.superclass._resizeProxy.apply(this, arguments);
                var dragEl = this.getDragEl(),
                    el = this.getEl();

                YAHOO.util.Dom.setStyle(this.pointer, 'height', (this.table.parentNode.offsetHeight + 10) + 'px');
                YAHOO.util.Dom.setStyle(this.pointer, 'display', 'block');
                var xy = YAHOO.util.Dom.getXY(el);
                YAHOO.util.Dom.setXY(this.pointer, [xy[0], (xy[1] - 5)]);

                YAHOO.util.Dom.setStyle(dragEl, 'height', this.datatable.getContainerEl().offsetHeight + "px");
                YAHOO.util.Dom.setStyle(dragEl, 'width', (parseInt(YAHOO.util.Dom.getStyle(dragEl, 'width'),10) + 4) + 'px');
                YAHOO.util.Dom.setXY(this.dragEl, xy);
            },
            onMouseDown: function() {
                    this.initConstraints();
                    this.resetConstraints();
            },
            clickValidator: function(e) {
                if(!this.column.hidden) {
                    var target = YAHOO.util.Event.getTarget(e);
                    return ( this.isValidHandleChild(target) &&
                                (this.id == this.handleElId ||
                                    this.DDM.handleWasClicked(target, this.id)) );
                }
            },
            onDragOver: function(ev, id) {
                // Validate target as a Column
                var target = this.datatable.getColumn(id);
                if(target) {
                    // Validate target as a top-level parent
                    var targetIndex = target.getTreeIndex();
                    while((targetIndex === null) && target.getParent()) {
                        target = target.getParent();
                        targetIndex = target.getTreeIndex();
                    }
                    if(targetIndex !== null) {
                        // Are we placing to left or right of target?
                        var elTarget = target.getThEl();
                        var newIndex = targetIndex;
                        var mouseX = YAHOO.util.Event.getPageX(ev),
                            targetX = YAHOO.util.Dom.getX(elTarget),
                            midX = targetX + ((YAHOO.util.Dom.get(elTarget).offsetWidth)/2),
                            currentIndex =  this.column.getTreeIndex();

                        if (mouseX < midX) {
                           YAHOO.util.Dom.setX(this.pointer, targetX);
                        } else {
                            var targetWidth = parseInt(elTarget.offsetWidth, 10);
                            YAHOO.util.Dom.setX(this.pointer, (targetX + targetWidth));
                            newIndex++;
                        }
                        if (targetIndex > currentIndex) {
                            newIndex--;
                        }
                        if(newIndex < 0) {
                            newIndex = 0;
                        }
                        else if(newIndex > this.datatable.getColumnSet().tree[0].length) {
                            newIndex = this.datatable.getColumnSet().tree[0].length;
                        }
                        this.newIndex = newIndex;
                    }
                }
            },
            onDragDrop: function() {
                this.datatable.reorderColumn(this.column, this.newIndex);
            },
            endDrag: function() {
                this.newIndex = null;
                YAHOO.util.Dom.setStyle(this.pointer, 'display', 'none');
            }
        });
    }

    /****************************************************************************/
    /****************************************************************************/
    /****************************************************************************/

    /**
     * ColumnResizer subclasses DragDrop to support resizeable Columns.
     *
     * @namespace YAHOO.util
     * @class ColumnResizer
     * @extends YAHOO.util.DDProxy
     * @constructor
     * @param oDataTable {YAHOO.widget.DataTable} DataTable instance.
     * @param oColumn {YAHOO.widget.Column} Column instance.
     * @param elTh {HTMLElement} TH element reference.
     * @param sHandleElId {String} DOM ID of the handle element that causes the resize.
     * @param elProxy {HTMLElement} Resizer proxy element.
     */
    YAHOO.util.ColumnResizer = function(oDataTable, oColumn, elTh, sHandleId, elProxy) {
        if(oDataTable && oColumn && elTh && sHandleId) {
            this.datatable = oDataTable;
            this.column = oColumn;
            this.headCell = elTh;
            this.headCellLiner = oColumn.getThLinerEl();
            this.resizerLiner = elTh.firstChild;
            this.init(sHandleId, sHandleId, {dragOnly:true, dragElId: elProxy.id});
            this.initFrame(); // Needed for proxy
            this.resetResizerEl(); // Needed when rowspan > 0

            // Set right padding for bug 1858462
            this.setPadding(0, 1, 0, 0);
        }
        else {
            YAHOO.log("Column resizer could not be created","warn",oDataTable.toString());
        }
    };

    if(YAHOO.util.DD) {
        YAHOO.extend(YAHOO.util.ColumnResizer, YAHOO.util.DDProxy, {
            /////////////////////////////////////////////////////////////////////////////
            //
            // Public methods
            //
            /////////////////////////////////////////////////////////////////////////////
            /**
             * Resets resizer element.
             *
             * @method resetResizerEl
             */
            resetResizerEl : function() {
                var resizerStyle = YAHOO.util.Dom.get(this.handleElId).style;
                resizerStyle.left = "auto";
                resizerStyle.right = 0;
                resizerStyle.top = "auto";
                resizerStyle.bottom = 0;
                resizerStyle.height = this.headCell.offsetHeight+"px";
            },

            /////////////////////////////////////////////////////////////////////////////
            //
            // Public DOM event handlers
            //
            /////////////////////////////////////////////////////////////////////////////

            /**
             * Handles mouseup events on the Column resizer.
             *
             * @method onMouseUp
             * @param e {string} The mouseup event
             */
            onMouseUp : function(e) {
                // Reset height of all resizer els in case TH's have changed height
                var allKeys = this.datatable.getColumnSet().keys,
                    col;
                for(var i=0, len=allKeys.length; i<len; i++) {
                    col = allKeys[i];
                    if(col._ddResizer) {
                        col._ddResizer.resetResizerEl();
                    }
                }
                this.resetResizerEl();

                var el = this.headCellLiner;
                var newWidth = el.offsetWidth -
                    (parseInt(YAHOO.util.Dom.getStyle(el,"paddingLeft"),10)|0) -
                    (parseInt(YAHOO.util.Dom.getStyle(el,"paddingRight"),10)|0);

                this.datatable.fireEvent("columnResizeEvent", {column:this.column,target:this.headCell,width:newWidth});
            },

            /**
             * Handles mousedown events on the Column resizer.
             *
             * @method onMouseDown
             * @param e {string} The mousedown event
             */
            onMouseDown : function(e) {
                this.startWidth = this.headCellLiner.offsetWidth;
                this.startX = YAHOO.util.Event.getXY(e)[0];
                this.nLinerPadding = (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingLeft"),10)|0) +
                        (parseInt(YAHOO.util.Dom.getStyle(this.headCellLiner,"paddingRight"),10)|0);
            },

            /**
             * Custom clickValidator to ensure Column is not in hidden state.
             *
             * @method clickValidator
             * @param {Event} e
             * @private
             */
            clickValidator : function(e) {
                if(!this.column.hidden) {
                    var target = YAHOO.util.Event.getTarget(e);
                    return ( this.isValidHandleChild(target) &&
                                (this.id == this.handleElId ||
                                    this.DDM.handleWasClicked(target, this.id)) );
                }
            },

            /**
             * Handles start drag on the Column resizer.
             *
             * @method startDrag
             * @param e {string} The drag event
             */
            startDrag : function() {
                // Shrinks height of all resizer els to not hold open TH els
                var allKeys = this.datatable.getColumnSet().keys,
                    thisKey = this.column.getKeyIndex(),
                    col;
                for(var i=0, len=allKeys.length; i<len; i++) {
                    col = allKeys[i];
                    if(col._ddResizer) {
                        YAHOO.util.Dom.get(col._ddResizer.handleElId).style.height = "1em";
                    }
                }
            },

            /**
             * Handles drag events on the Column resizer.
             *
             * @method onDrag
             * @param e {string} The drag event
             */
            onDrag : function(e) {
                var newX = YAHOO.util.Event.getXY(e)[0];
                if(newX > YAHOO.util.Dom.getX(this.headCellLiner)) {
                    var offsetX = newX - this.startX;
                    var newWidth = this.startWidth + offsetX - this.nLinerPadding;
                    if(newWidth > 0) {
                        this.datatable.setColumnWidth(this.column, newWidth);
                    }
                }
            }
        });
    }

    /////////////////////////////////////////////////////////////////////////////
    //
    // Deprecated
    //
    /////////////////////////////////////////////////////////////////////////////

    /**
     * @property editorOptions
     * @deprecated Pass configs directly to CellEditor constructor.
     */

Copyright © 2011 Yahoo! Inc. All rights reserved.