Upgrade to ExtJS 3.2.0 - Released 03/30/2010
[extjs.git] / examples / ux / ColumnHeaderGroup.js
index e9ad21b..a08c22d 100644 (file)
 /*!
- * Ext JS Library 3.1.1
- * Copyright(c) 2006-2010 Ext JS, LLC
+ * Ext JS Library 3.2.0
+ * Copyright(c) 2006-2010 Ext JS, Inc.
  * licensing@extjs.com
  * http://www.extjs.com/license
  */
-Ext.ns('Ext.ux.grid');\r
-\r
-Ext.ux.grid.ColumnHeaderGroup = Ext.extend(Ext.util.Observable, {\r
-\r
-    constructor: function(config){\r
-        this.config = config;\r
-    },\r
-\r
-    init: function(grid){\r
-        Ext.applyIf(grid.colModel, this.config);\r
-        Ext.apply(grid.getView(), this.viewConfig);\r
-    },\r
-\r
-    viewConfig: {\r
-        initTemplates: function(){\r
-            this.constructor.prototype.initTemplates.apply(this, arguments);\r
-            var ts = this.templates || {};\r
-            if(!ts.gcell){\r
-                ts.gcell = new Ext.XTemplate('<td class="x-grid3-hd x-grid3-gcell x-grid3-td-{id} ux-grid-hd-group-row-{row} {cls}" style="{style}">', '<div {tooltip} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '', '{value}</div></td>');\r
-            }\r
-            this.templates = ts;\r
-            this.hrowRe = new RegExp("ux-grid-hd-group-row-(\\d+)", "");\r
-        },\r
-\r
-        renderHeaders: function(){\r
-            var ts = this.templates, headers = [], cm = this.cm, rows = cm.rows, tstyle = 'width:' + this.getTotalWidth() + ';';\r
-\r
-            for(var row = 0, rlen = rows.length; row < rlen; row++){\r
-                var r = rows[row], cells = [];\r
-                for(var i = 0, gcol = 0, len = r.length; i < len; i++){\r
-                    var group = r[i];\r
-                    group.colspan = group.colspan || 1;\r
-                    var id = this.getColumnId(group.dataIndex ? cm.findColumnIndex(group.dataIndex) : gcol), gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);\r
-                    cells[i] = ts.gcell.apply({\r
-                        cls: 'ux-grid-hd-group-cell',\r
-                        id: id,\r
-                        row: row,\r
-                        style: 'width:' + gs.width + ';' + (gs.hidden ? 'display:none;' : '') + (group.align ? 'text-align:' + group.align + ';' : ''),\r
-                        tooltip: group.tooltip ? (Ext.QuickTips.isEnabled() ? 'ext:qtip' : 'title') + '="' + group.tooltip + '"' : '',\r
-                        istyle: group.align == 'right' ? 'padding-right:16px' : '',\r
-                        btn: this.grid.enableHdMenu && group.header,\r
-                        value: group.header || '&nbsp;'\r
-                    });\r
-                    gcol += group.colspan;\r
-                }\r
-                headers[row] = ts.header.apply({\r
-                    tstyle: tstyle,\r
-                    cells: cells.join('')\r
-                });\r
-            }\r
-            headers.push(this.constructor.prototype.renderHeaders.apply(this, arguments));\r
-            return headers.join('');\r
-        },\r
-\r
-        onColumnWidthUpdated: function(){\r
-            this.constructor.prototype.onColumnWidthUpdated.apply(this, arguments);\r
-            Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);\r
-        },\r
-\r
-        onAllColumnWidthsUpdated: function(){\r
-            this.constructor.prototype.onAllColumnWidthsUpdated.apply(this, arguments);\r
-            Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);\r
-        },\r
-\r
-        onColumnHiddenUpdated: function(){\r
-            this.constructor.prototype.onColumnHiddenUpdated.apply(this, arguments);\r
-            Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);\r
-        },\r
-\r
-        getHeaderCell: function(index){\r
-            return this.mainHd.query(this.cellSelector)[index];\r
-        },\r
-\r
-        findHeaderCell: function(el){\r
-            return el ? this.fly(el).findParent('td.x-grid3-hd', this.cellSelectorDepth) : false;\r
-        },\r
-\r
-        findHeaderIndex: function(el){\r
-            var cell = this.findHeaderCell(el);\r
-            return cell ? this.getCellIndex(cell) : false;\r
-        },\r
-\r
-        updateSortIcon: function(col, dir){\r
-            var sc = this.sortClasses, hds = this.mainHd.select(this.cellSelector).removeClass(sc);\r
-            hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);\r
-        },\r
-\r
-        handleHdDown: function(e, t){\r
-            var el = Ext.get(t);\r
-            if(el.hasClass('x-grid3-hd-btn')){\r
-                e.stopEvent();\r
-                var hd = this.findHeaderCell(t);\r
-                Ext.fly(hd).addClass('x-grid3-hd-menu-open');\r
-                var index = this.getCellIndex(hd);\r
-                this.hdCtxIndex = index;\r
-                var ms = this.hmenu.items, cm = this.cm;\r
-                ms.get('asc').setDisabled(!cm.isSortable(index));\r
-                ms.get('desc').setDisabled(!cm.isSortable(index));\r
-                this.hmenu.on('hide', function(){\r
-                    Ext.fly(hd).removeClass('x-grid3-hd-menu-open');\r
-                }, this, {\r
-                    single: true\r
-                });\r
-                this.hmenu.show(t, 'tl-bl?');\r
-            }else if(el.hasClass('ux-grid-hd-group-cell') || Ext.fly(t).up('.ux-grid-hd-group-cell')){\r
-                e.stopEvent();\r
-            }\r
-        },\r
-\r
-        handleHdMove: function(e, t){\r
-            var hd = this.findHeaderCell(this.activeHdRef);\r
-            if(hd && !this.headersDisabled && !Ext.fly(hd).hasClass('ux-grid-hd-group-cell')){\r
-                var hw = this.splitHandleWidth || 5, r = this.activeHdRegion, x = e.getPageX(), ss = hd.style, cur = '';\r
-                if(this.grid.enableColumnResize !== false){\r
-                    if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex - 1)){\r
-                        cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize\r
-                                                                                                // not\r
-                                                                                                // always\r
-                                                                                                // supported\r
-                    }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){\r
-                        cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';\r
-                    }\r
-                }\r
-                ss.cursor = cur;\r
-            }\r
-        },\r
-\r
-        handleHdOver: function(e, t){\r
-            var hd = this.findHeaderCell(t);\r
-            if(hd && !this.headersDisabled){\r
-                this.activeHdRef = t;\r
-                this.activeHdIndex = this.getCellIndex(hd);\r
-                var fly = this.fly(hd);\r
-                this.activeHdRegion = fly.getRegion();\r
-                if(!(this.cm.isMenuDisabled(this.activeHdIndex) || fly.hasClass('ux-grid-hd-group-cell'))){\r
-                    fly.addClass('x-grid3-hd-over');\r
-                    this.activeHdBtn = fly.child('.x-grid3-hd-btn');\r
-                    if(this.activeHdBtn){\r
-                        this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight - 1) + 'px';\r
-                    }\r
-                }\r
-            }\r
-        },\r
-\r
-        handleHdOut: function(e, t){\r
-            var hd = this.findHeaderCell(t);\r
-            if(hd && (!Ext.isIE || !e.within(hd, true))){\r
-                this.activeHdRef = null;\r
-                this.fly(hd).removeClass('x-grid3-hd-over');\r
-                hd.style.cursor = '';\r
-            }\r
-        },\r
-\r
-        handleHdMenuClick: function(item){\r
-            var index = this.hdCtxIndex, cm = this.cm, ds = this.ds, id = item.getItemId();\r
-            switch(id){\r
-                case 'asc':\r
-                    ds.sort(cm.getDataIndex(index), 'ASC');\r
-                    break;\r
-                case 'desc':\r
-                    ds.sort(cm.getDataIndex(index), 'DESC');\r
-                    break;\r
-                default:\r
-                    if(id.substr(0, 5) == 'group'){\r
-                        var i = id.split('-'), row = parseInt(i[1], 10), col = parseInt(i[2], 10), r = this.cm.rows[row], group, gcol = 0;\r
-                        for(var i = 0, len = r.length; i < len; i++){\r
-                            group = r[i];\r
-                            if(col >= gcol && col < gcol + group.colspan){\r
-                                break;\r
-                            }\r
-                            gcol += group.colspan;\r
-                        }\r
-                        if(item.checked){\r
-                            var max = cm.getColumnsBy(this.isHideableColumn, this).length;\r
-                            for(var i = gcol, len = gcol + group.colspan; i < len; i++){\r
-                                if(!cm.isHidden(i)){\r
-                                    max--;\r
-                                }\r
-                            }\r
-                            if(max < 1){\r
-                                this.onDenyColumnHide();\r
-                                return false;\r
-                            }\r
-                        }\r
-                        for(var i = gcol, len = gcol + group.colspan; i < len; i++){\r
-                            if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){\r
-                                cm.setHidden(i, item.checked);\r
-                            }\r
-                        }\r
-                    }else{\r
-                        index = cm.getIndexById(id.substr(4));\r
-                        if(index != -1){\r
-                            if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){\r
-                                this.onDenyColumnHide();\r
-                                return false;\r
-                            }\r
-                            cm.setHidden(index, item.checked);\r
-                        }\r
-                    }\r
-                    item.checked = !item.checked;\r
-                    if(item.menu){\r
-                        var updateChildren = function(menu){\r
-                            menu.items.each(function(childItem){\r
-                                if(!childItem.disabled){\r
-                                    childItem.setChecked(item.checked, false);\r
-                                    if(childItem.menu){\r
-                                        updateChildren(childItem.menu);\r
-                                    }\r
-                                }\r
-                            });\r
-                        }\r
-                        updateChildren(item.menu);\r
-                    }\r
-                    var parentMenu = item, parentItem;\r
-                    while(parentMenu = parentMenu.parentMenu){\r
-                        if(!parentMenu.parentMenu || !(parentItem = parentMenu.parentMenu.items.get(parentMenu.getItemId())) || !parentItem.setChecked){\r
-                            break;\r
-                        }\r
-                        var checked = parentMenu.items.findIndexBy(function(m){\r
-                            return m.checked;\r
-                        }) >= 0;\r
-                        parentItem.setChecked(checked, true);\r
-                    }\r
-                    item.checked = !item.checked;\r
-            }\r
-            return true;\r
-        },\r
-\r
-        beforeColMenuShow: function(){\r
-            var cm = this.cm, rows = this.cm.rows;\r
-            this.colMenu.removeAll();\r
-            for(var col = 0, clen = cm.getColumnCount(); col < clen; col++){\r
-                var menu = this.colMenu, title = cm.getColumnHeader(col), text = [];\r
-                if(cm.config[col].fixed !== true && cm.config[col].hideable !== false){\r
-                    for(var row = 0, rlen = rows.length; row < rlen; row++){\r
-                        var r = rows[row], group, gcol = 0;\r
-                        for(var i = 0, len = r.length; i < len; i++){\r
-                            group = r[i];\r
-                            if(col >= gcol && col < gcol + group.colspan){\r
-                                break;\r
-                            }\r
-                            gcol += group.colspan;\r
-                        }\r
-                        if(group && group.header){\r
-                            if(cm.hierarchicalColMenu){\r
-                                var gid = 'group-' + row + '-' + gcol;\r
-                                var item = menu.items.item(gid);\r
-                                var submenu = item ? item.menu : null;\r
-                                if(!submenu){\r
-                                    submenu = new Ext.menu.Menu({\r
-                                        itemId: gid\r
-                                    });\r
-                                    submenu.on("itemclick", this.handleHdMenuClick, this);\r
-                                    var checked = false, disabled = true;\r
-                                    for(var c = gcol, lc = gcol + group.colspan; c < lc; c++){\r
-                                        if(!cm.isHidden(c)){\r
-                                            checked = true;\r
-                                        }\r
-                                        if(cm.config[c].hideable !== false){\r
-                                            disabled = false;\r
-                                        }\r
-                                    }\r
-                                    menu.add({\r
-                                        itemId: gid,\r
-                                        text: group.header,\r
-                                        menu: submenu,\r
-                                        hideOnClick: false,\r
-                                        checked: checked,\r
-                                        disabled: disabled\r
-                                    });\r
-                                }\r
-                                menu = submenu;\r
-                            }else{\r
-                                text.push(group.header);\r
-                            }\r
-                        }\r
-                    }\r
-                    text.push(title);\r
-                    menu.add(new Ext.menu.CheckItem({\r
-                        itemId: "col-" + cm.getColumnId(col),\r
-                        text: text.join(' '),\r
-                        checked: !cm.isHidden(col),\r
-                        hideOnClick: false,\r
-                        disabled: cm.config[col].hideable === false\r
-                    }));\r
-                }\r
-            }\r
-        },\r
-\r
-        renderUI: function(){\r
-            this.constructor.prototype.renderUI.apply(this, arguments);\r
-            Ext.apply(this.columnDrop, Ext.ux.grid.ColumnHeaderGroup.prototype.columnDropConfig);\r
-            Ext.apply(this.splitZone, Ext.ux.grid.ColumnHeaderGroup.prototype.splitZoneConfig);\r
-        }\r
-    },\r
-\r
-    splitZoneConfig: {\r
-        allowHeaderDrag: function(e){\r
-            return !e.getTarget(null, null, true).hasClass('ux-grid-hd-group-cell');\r
-        }\r
-    },\r
-\r
-    columnDropConfig: {\r
-        getTargetFromEvent: function(e){\r
-            var t = Ext.lib.Event.getTarget(e);\r
-            return this.view.findHeaderCell(t);\r
-        },\r
-\r
-        positionIndicator: function(h, n, e){\r
-            var data = Ext.ux.grid.ColumnHeaderGroup.prototype.getDragDropData.call(this, h, n, e);\r
-            if(data === false){\r
-                return false;\r
-            }\r
-            var px = data.px + this.proxyOffsets[0];\r
-            this.proxyTop.setLeftTop(px, data.r.top + this.proxyOffsets[1]);\r
-            this.proxyTop.show();\r
-            this.proxyBottom.setLeftTop(px, data.r.bottom);\r
-            this.proxyBottom.show();\r
-            return data.pt;\r
-        },\r
-\r
-        onNodeDrop: function(n, dd, e, data){\r
-            var h = data.header;\r
-            if(h != n){\r
-                var d = Ext.ux.grid.ColumnHeaderGroup.prototype.getDragDropData.call(this, h, n, e);\r
-                if(d === false){\r
-                    return false;\r
-                }\r
-                var cm = this.grid.colModel, right = d.oldIndex < d.newIndex, rows = cm.rows;\r
-                for(var row = d.row, rlen = rows.length; row < rlen; row++){\r
-                    var r = rows[row], len = r.length, fromIx = 0, span = 1, toIx = len;\r
-                    for(var i = 0, gcol = 0; i < len; i++){\r
-                        var group = r[i];\r
-                        if(d.oldIndex >= gcol && d.oldIndex < gcol + group.colspan){\r
-                            fromIx = i;\r
-                        }\r
-                        if(d.oldIndex + d.colspan - 1 >= gcol && d.oldIndex + d.colspan - 1 < gcol + group.colspan){\r
-                            span = i - fromIx + 1;\r
-                        }\r
-                        if(d.newIndex >= gcol && d.newIndex < gcol + group.colspan){\r
-                            toIx = i;\r
-                        }\r
-                        gcol += group.colspan;\r
-                    }\r
-                    var groups = r.splice(fromIx, span);\r
-                    rows[row] = r.splice(0, toIx - (right ? span : 0)).concat(groups).concat(r);\r
-                }\r
-                for(var c = 0; c < d.colspan; c++){\r
-                    var oldIx = d.oldIndex + (right ? 0 : c), newIx = d.newIndex + (right ? -1 : c);\r
-                    cm.moveColumn(oldIx, newIx);\r
-                    this.grid.fireEvent("columnmove", oldIx, newIx);\r
-                }\r
-                return true;\r
-            }\r
-            return false;\r
-        }\r
-    },\r
-\r
-    getGroupStyle: function(group, gcol){\r
-        var width = 0, hidden = true;\r
-        for(var i = gcol, len = gcol + group.colspan; i < len; i++){\r
-            if(!this.cm.isHidden(i)){\r
-                var cw = this.cm.getColumnWidth(i);\r
-                if(typeof cw == 'number'){\r
-                    width += cw;\r
-                }\r
-                hidden = false;\r
-            }\r
-        }\r
-        return {\r
-            width: (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? width : Math.max(width - this.borderWidth, 0)) + 'px',\r
-            hidden: hidden\r
-        };\r
-    },\r
-\r
-    updateGroupStyles: function(col){\r
-        var tables = this.mainHd.query('.x-grid3-header-offset > table'), tw = this.getTotalWidth(), rows = this.cm.rows;\r
-        for(var row = 0; row < tables.length; row++){\r
-            tables[row].style.width = tw;\r
-            if(row < rows.length){\r
-                var cells = tables[row].firstChild.firstChild.childNodes;\r
-                for(var i = 0, gcol = 0; i < cells.length; i++){\r
-                    var group = rows[row][i];\r
-                    if((typeof col != 'number') || (col >= gcol && col < gcol + group.colspan)){\r
-                        var gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);\r
-                        cells[i].style.width = gs.width;\r
-                        cells[i].style.display = gs.hidden ? 'none' : '';\r
-                    }\r
-                    gcol += group.colspan;\r
-                }\r
-            }\r
-        }\r
-    },\r
-\r
-    getGroupRowIndex: function(el){\r
-        if(el){\r
-            var m = el.className.match(this.hrowRe);\r
-            if(m && m[1]){\r
-                return parseInt(m[1], 10);\r
-            }\r
-        }\r
-        return this.cm.rows.length;\r
-    },\r
-\r
-    getGroupSpan: function(row, col){\r
-        if(row < 0){\r
-            return {\r
-                col: 0,\r
-                colspan: this.cm.getColumnCount()\r
-            };\r
-        }\r
-        var r = this.cm.rows[row];\r
-        if(r){\r
-            for(var i = 0, gcol = 0, len = r.length; i < len; i++){\r
-                var group = r[i];\r
-                if(col >= gcol && col < gcol + group.colspan){\r
-                    return {\r
-                        col: gcol,\r
-                        colspan: group.colspan\r
-                    };\r
-                }\r
-                gcol += group.colspan;\r
-            }\r
-            return {\r
-                col: gcol,\r
-                colspan: 0\r
-            };\r
-        }\r
-        return {\r
-            col: col,\r
-            colspan: 1\r
-        };\r
-    },\r
-\r
-    getDragDropData: function(h, n, e){\r
-        if(h.parentNode != n.parentNode){\r
-            return false;\r
-        }\r
-        var cm = this.grid.colModel, x = Ext.lib.Event.getPageX(e), r = Ext.lib.Dom.getRegion(n.firstChild), px, pt;\r
-        if((r.right - x) <= (r.right - r.left) / 2){\r
-            px = r.right + this.view.borderWidth;\r
-            pt = "after";\r
-        }else{\r
-            px = r.left;\r
-            pt = "before";\r
-        }\r
-        var oldIndex = this.view.getCellIndex(h), newIndex = this.view.getCellIndex(n);\r
-        if(cm.isFixed(newIndex)){\r
-            return false;\r
-        }\r
-        var row = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupRowIndex.call(this.view, h),\r
-            oldGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row, oldIndex),\r
-            newGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row, newIndex),\r
-            oldIndex = oldGroup.col;\r
-            newIndex = newGroup.col + (pt == "after" ? newGroup.colspan : 0);\r
-        if(newIndex >= oldGroup.col && newIndex <= oldGroup.col + oldGroup.colspan){\r
-            return false;\r
-        }\r
-        var parentGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row - 1, oldIndex);\r
-        if(newIndex < parentGroup.col || newIndex > parentGroup.col + parentGroup.colspan){\r
-            return false;\r
-        }\r
-        return {\r
-            r: r,\r
-            px: px,\r
-            pt: pt,\r
-            row: row,\r
-            oldIndex: oldIndex,\r
-            newIndex: newIndex,\r
-            colspan: oldGroup.colspan\r
-        };\r
-    }\r
+Ext.ns('Ext.ux.grid');
+
+Ext.ux.grid.ColumnHeaderGroup = Ext.extend(Ext.util.Observable, {
+
+    constructor: function(config){
+        this.config = config;
+    },
+
+    init: function(grid){
+        Ext.applyIf(grid.colModel, this.config);
+        Ext.apply(grid.getView(), this.viewConfig);
+    },
+
+    viewConfig: {
+        initTemplates: function(){
+            this.constructor.prototype.initTemplates.apply(this, arguments);
+            var ts = this.templates || {};
+            if(!ts.gcell){
+                ts.gcell = new Ext.XTemplate('<td class="x-grid3-hd x-grid3-gcell x-grid3-td-{id} ux-grid-hd-group-row-{row} {cls}" style="{style}">', '<div {tooltip} class="x-grid3-hd-inner x-grid3-hd-{id}" unselectable="on" style="{istyle}">', this.grid.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '', '{value}</div></td>');
+            }
+            this.templates = ts;
+            this.hrowRe = new RegExp("ux-grid-hd-group-row-(\\d+)", "");
+        },
+
+        renderHeaders: function(){
+            var ts = this.templates, headers = [], cm = this.cm, rows = cm.rows, tstyle = 'width:' + this.getTotalWidth() + ';';
+
+            for(var row = 0, rlen = rows.length; row < rlen; row++){
+                var r = rows[row], cells = [];
+                for(var i = 0, gcol = 0, len = r.length; i < len; i++){
+                    var group = r[i];
+                    group.colspan = group.colspan || 1;
+                    var id = this.getColumnId(group.dataIndex ? cm.findColumnIndex(group.dataIndex) : gcol), gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);
+                    cells[i] = ts.gcell.apply({
+                        cls: 'ux-grid-hd-group-cell',
+                        id: id,
+                        row: row,
+                        style: 'width:' + gs.width + ';' + (gs.hidden ? 'display:none;' : '') + (group.align ? 'text-align:' + group.align + ';' : ''),
+                        tooltip: group.tooltip ? (Ext.QuickTips.isEnabled() ? 'ext:qtip' : 'title') + '="' + group.tooltip + '"' : '',
+                        istyle: group.align == 'right' ? 'padding-right:16px' : '',
+                        btn: this.grid.enableHdMenu && group.header,
+                        value: group.header || '&nbsp;'
+                    });
+                    gcol += group.colspan;
+                }
+                headers[row] = ts.header.apply({
+                    tstyle: tstyle,
+                    cells: cells.join('')
+                });
+            }
+            headers.push(this.constructor.prototype.renderHeaders.apply(this, arguments));
+            return headers.join('');
+        },
+
+        onColumnWidthUpdated: function(){
+            this.constructor.prototype.onColumnWidthUpdated.apply(this, arguments);
+            Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
+        },
+
+        onAllColumnWidthsUpdated: function(){
+            this.constructor.prototype.onAllColumnWidthsUpdated.apply(this, arguments);
+            Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
+        },
+
+        onColumnHiddenUpdated: function(){
+            this.constructor.prototype.onColumnHiddenUpdated.apply(this, arguments);
+            Ext.ux.grid.ColumnHeaderGroup.prototype.updateGroupStyles.call(this);
+        },
+
+        getHeaderCell: function(index){
+            return this.mainHd.query(this.cellSelector)[index];
+        },
+
+        findHeaderCell: function(el){
+            return el ? this.fly(el).findParent('td.x-grid3-hd', this.cellSelectorDepth) : false;
+        },
+
+        findHeaderIndex: function(el){
+            var cell = this.findHeaderCell(el);
+            return cell ? this.getCellIndex(cell) : false;
+        },
+
+        updateSortIcon: function(col, dir){
+            var sc = this.sortClasses, hds = this.mainHd.select(this.cellSelector).removeClass(sc);
+            hds.item(col).addClass(sc[dir == "DESC" ? 1 : 0]);
+        },
+
+        handleHdDown: function(e, t){
+            var el = Ext.get(t);
+            if(el.hasClass('x-grid3-hd-btn')){
+                e.stopEvent();
+                var hd = this.findHeaderCell(t);
+                Ext.fly(hd).addClass('x-grid3-hd-menu-open');
+                var index = this.getCellIndex(hd);
+                this.hdCtxIndex = index;
+                var ms = this.hmenu.items, cm = this.cm;
+                ms.get('asc').setDisabled(!cm.isSortable(index));
+                ms.get('desc').setDisabled(!cm.isSortable(index));
+                this.hmenu.on('hide', function(){
+                    Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
+                }, this, {
+                    single: true
+                });
+                this.hmenu.show(t, 'tl-bl?');
+            }else if(el.hasClass('ux-grid-hd-group-cell') || Ext.fly(t).up('.ux-grid-hd-group-cell')){
+                e.stopEvent();
+            }
+        },
+
+        handleHdMove: function(e, t){
+            var hd = this.findHeaderCell(this.activeHdRef);
+            if(hd && !this.headersDisabled && !Ext.fly(hd).hasClass('ux-grid-hd-group-cell')){
+                var hw = this.splitHandleWidth || 5, r = this.activeHdRegion, x = e.getPageX(), ss = hd.style, cur = '';
+                if(this.grid.enableColumnResize !== false){
+                    if(x - r.left <= hw && this.cm.isResizable(this.activeHdIndex - 1)){
+                        cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'e-resize' : 'col-resize'; // col-resize
+                                                                                                // not
+                                                                                                // always
+                                                                                                // supported
+                    }else if(r.right - x <= (!this.activeHdBtn ? hw : 2) && this.cm.isResizable(this.activeHdIndex)){
+                        cur = Ext.isAir ? 'move' : Ext.isWebKit ? 'w-resize' : 'col-resize';
+                    }
+                }
+                ss.cursor = cur;
+            }
+        },
+
+        handleHdOver: function(e, t){
+            var hd = this.findHeaderCell(t);
+            if(hd && !this.headersDisabled){
+                this.activeHdRef = t;
+                this.activeHdIndex = this.getCellIndex(hd);
+                var fly = this.fly(hd);
+                this.activeHdRegion = fly.getRegion();
+                if(!(this.cm.isMenuDisabled(this.activeHdIndex) || fly.hasClass('ux-grid-hd-group-cell'))){
+                    fly.addClass('x-grid3-hd-over');
+                    this.activeHdBtn = fly.child('.x-grid3-hd-btn');
+                    if(this.activeHdBtn){
+                        this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight - 1) + 'px';
+                    }
+                }
+            }
+        },
+
+        handleHdOut: function(e, t){
+            var hd = this.findHeaderCell(t);
+            if(hd && (!Ext.isIE || !e.within(hd, true))){
+                this.activeHdRef = null;
+                this.fly(hd).removeClass('x-grid3-hd-over');
+                hd.style.cursor = '';
+            }
+        },
+
+        handleHdMenuClick: function(item){
+            var index = this.hdCtxIndex, cm = this.cm, ds = this.ds, id = item.getItemId();
+            switch(id){
+                case 'asc':
+                    ds.sort(cm.getDataIndex(index), 'ASC');
+                    break;
+                case 'desc':
+                    ds.sort(cm.getDataIndex(index), 'DESC');
+                    break;
+                default:
+                    if(id.substr(0, 5) == 'group'){
+                        var i = id.split('-'), row = parseInt(i[1], 10), col = parseInt(i[2], 10), r = this.cm.rows[row], group, gcol = 0;
+                        for(var i = 0, len = r.length; i < len; i++){
+                            group = r[i];
+                            if(col >= gcol && col < gcol + group.colspan){
+                                break;
+                            }
+                            gcol += group.colspan;
+                        }
+                        if(item.checked){
+                            var max = cm.getColumnsBy(this.isHideableColumn, this).length;
+                            for(var i = gcol, len = gcol + group.colspan; i < len; i++){
+                                if(!cm.isHidden(i)){
+                                    max--;
+                                }
+                            }
+                            if(max < 1){
+                                this.onDenyColumnHide();
+                                return false;
+                            }
+                        }
+                        for(var i = gcol, len = gcol + group.colspan; i < len; i++){
+                            if(cm.config[i].fixed !== true && cm.config[i].hideable !== false){
+                                cm.setHidden(i, item.checked);
+                            }
+                        }
+                    }else{
+                        index = cm.getIndexById(id.substr(4));
+                        if(index != -1){
+                            if(item.checked && cm.getColumnsBy(this.isHideableColumn, this).length <= 1){
+                                this.onDenyColumnHide();
+                                return false;
+                            }
+                            cm.setHidden(index, item.checked);
+                        }
+                    }
+                    item.checked = !item.checked;
+                    if(item.menu){
+                        var updateChildren = function(menu){
+                            menu.items.each(function(childItem){
+                                if(!childItem.disabled){
+                                    childItem.setChecked(item.checked, false);
+                                    if(childItem.menu){
+                                        updateChildren(childItem.menu);
+                                    }
+                                }
+                            });
+                        }
+                        updateChildren(item.menu);
+                    }
+                    var parentMenu = item, parentItem;
+                    while(parentMenu = parentMenu.parentMenu){
+                        if(!parentMenu.parentMenu || !(parentItem = parentMenu.parentMenu.items.get(parentMenu.getItemId())) || !parentItem.setChecked){
+                            break;
+                        }
+                        var checked = parentMenu.items.findIndexBy(function(m){
+                            return m.checked;
+                        }) >= 0;
+                        parentItem.setChecked(checked, true);
+                    }
+                    item.checked = !item.checked;
+            }
+            return true;
+        },
+
+        beforeColMenuShow: function(){
+            var cm = this.cm, rows = this.cm.rows;
+            this.colMenu.removeAll();
+            for(var col = 0, clen = cm.getColumnCount(); col < clen; col++){
+                var menu = this.colMenu, title = cm.getColumnHeader(col), text = [];
+                if(cm.config[col].fixed !== true && cm.config[col].hideable !== false){
+                    for(var row = 0, rlen = rows.length; row < rlen; row++){
+                        var r = rows[row], group, gcol = 0;
+                        for(var i = 0, len = r.length; i < len; i++){
+                            group = r[i];
+                            if(col >= gcol && col < gcol + group.colspan){
+                                break;
+                            }
+                            gcol += group.colspan;
+                        }
+                        if(group && group.header){
+                            if(cm.hierarchicalColMenu){
+                                var gid = 'group-' + row + '-' + gcol;
+                                var item = menu.items.item(gid);
+                                var submenu = item ? item.menu : null;
+                                if(!submenu){
+                                    submenu = new Ext.menu.Menu({
+                                        itemId: gid
+                                    });
+                                    submenu.on("itemclick", this.handleHdMenuClick, this);
+                                    var checked = false, disabled = true;
+                                    for(var c = gcol, lc = gcol + group.colspan; c < lc; c++){
+                                        if(!cm.isHidden(c)){
+                                            checked = true;
+                                        }
+                                        if(cm.config[c].hideable !== false){
+                                            disabled = false;
+                                        }
+                                    }
+                                    menu.add({
+                                        itemId: gid,
+                                        text: group.header,
+                                        menu: submenu,
+                                        hideOnClick: false,
+                                        checked: checked,
+                                        disabled: disabled
+                                    });
+                                }
+                                menu = submenu;
+                            }else{
+                                text.push(group.header);
+                            }
+                        }
+                    }
+                    text.push(title);
+                    menu.add(new Ext.menu.CheckItem({
+                        itemId: "col-" + cm.getColumnId(col),
+                        text: text.join(' '),
+                        checked: !cm.isHidden(col),
+                        hideOnClick: false,
+                        disabled: cm.config[col].hideable === false
+                    }));
+                }
+            }
+        },
+
+        renderUI: function(){
+            this.constructor.prototype.renderUI.apply(this, arguments);
+            Ext.apply(this.columnDrop, Ext.ux.grid.ColumnHeaderGroup.prototype.columnDropConfig);
+            Ext.apply(this.splitZone, Ext.ux.grid.ColumnHeaderGroup.prototype.splitZoneConfig);
+        }
+    },
+
+    splitZoneConfig: {
+        allowHeaderDrag: function(e){
+            return !e.getTarget(null, null, true).hasClass('ux-grid-hd-group-cell');
+        }
+    },
+
+    columnDropConfig: {
+        getTargetFromEvent: function(e){
+            var t = Ext.lib.Event.getTarget(e);
+            return this.view.findHeaderCell(t);
+        },
+
+        positionIndicator: function(h, n, e){
+            var data = Ext.ux.grid.ColumnHeaderGroup.prototype.getDragDropData.call(this, h, n, e);
+            if(data === false){
+                return false;
+            }
+            var px = data.px + this.proxyOffsets[0];
+            this.proxyTop.setLeftTop(px, data.r.top + this.proxyOffsets[1]);
+            this.proxyTop.show();
+            this.proxyBottom.setLeftTop(px, data.r.bottom);
+            this.proxyBottom.show();
+            return data.pt;
+        },
+
+        onNodeDrop: function(n, dd, e, data){
+            var h = data.header;
+            if(h != n){
+                var d = Ext.ux.grid.ColumnHeaderGroup.prototype.getDragDropData.call(this, h, n, e);
+                if(d === false){
+                    return false;
+                }
+                var cm = this.grid.colModel, right = d.oldIndex < d.newIndex, rows = cm.rows;
+                for(var row = d.row, rlen = rows.length; row < rlen; row++){
+                    var r = rows[row], len = r.length, fromIx = 0, span = 1, toIx = len;
+                    for(var i = 0, gcol = 0; i < len; i++){
+                        var group = r[i];
+                        if(d.oldIndex >= gcol && d.oldIndex < gcol + group.colspan){
+                            fromIx = i;
+                        }
+                        if(d.oldIndex + d.colspan - 1 >= gcol && d.oldIndex + d.colspan - 1 < gcol + group.colspan){
+                            span = i - fromIx + 1;
+                        }
+                        if(d.newIndex >= gcol && d.newIndex < gcol + group.colspan){
+                            toIx = i;
+                        }
+                        gcol += group.colspan;
+                    }
+                    var groups = r.splice(fromIx, span);
+                    rows[row] = r.splice(0, toIx - (right ? span : 0)).concat(groups).concat(r);
+                }
+                for(var c = 0; c < d.colspan; c++){
+                    var oldIx = d.oldIndex + (right ? 0 : c), newIx = d.newIndex + (right ? -1 : c);
+                    cm.moveColumn(oldIx, newIx);
+                    this.grid.fireEvent("columnmove", oldIx, newIx);
+                }
+                return true;
+            }
+            return false;
+        }
+    },
+
+    getGroupStyle: function(group, gcol){
+        var width = 0, hidden = true;
+        for(var i = gcol, len = gcol + group.colspan; i < len; i++){
+            if(!this.cm.isHidden(i)){
+                var cw = this.cm.getColumnWidth(i);
+                if(typeof cw == 'number'){
+                    width += cw;
+                }
+                hidden = false;
+            }
+        }
+        return {
+            width: (Ext.isBorderBox || (Ext.isWebKit && !Ext.isSafari2) ? width : Math.max(width - this.borderWidth, 0)) + 'px',
+            hidden: hidden
+        };
+    },
+
+    updateGroupStyles: function(col){
+        var tables = this.mainHd.query('.x-grid3-header-offset > table'), tw = this.getTotalWidth(), rows = this.cm.rows;
+        for(var row = 0; row < tables.length; row++){
+            tables[row].style.width = tw;
+            if(row < rows.length){
+                var cells = tables[row].firstChild.firstChild.childNodes;
+                for(var i = 0, gcol = 0; i < cells.length; i++){
+                    var group = rows[row][i];
+                    if((typeof col != 'number') || (col >= gcol && col < gcol + group.colspan)){
+                        var gs = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupStyle.call(this, group, gcol);
+                        cells[i].style.width = gs.width;
+                        cells[i].style.display = gs.hidden ? 'none' : '';
+                    }
+                    gcol += group.colspan;
+                }
+            }
+        }
+    },
+
+    getGroupRowIndex: function(el){
+        if(el){
+            var m = el.className.match(this.hrowRe);
+            if(m && m[1]){
+                return parseInt(m[1], 10);
+            }
+        }
+        return this.cm.rows.length;
+    },
+
+    getGroupSpan: function(row, col){
+        if(row < 0){
+            return {
+                col: 0,
+                colspan: this.cm.getColumnCount()
+            };
+        }
+        var r = this.cm.rows[row];
+        if(r){
+            for(var i = 0, gcol = 0, len = r.length; i < len; i++){
+                var group = r[i];
+                if(col >= gcol && col < gcol + group.colspan){
+                    return {
+                        col: gcol,
+                        colspan: group.colspan
+                    };
+                }
+                gcol += group.colspan;
+            }
+            return {
+                col: gcol,
+                colspan: 0
+            };
+        }
+        return {
+            col: col,
+            colspan: 1
+        };
+    },
+
+    getDragDropData: function(h, n, e){
+        if(h.parentNode != n.parentNode){
+            return false;
+        }
+        var cm = this.grid.colModel, x = Ext.lib.Event.getPageX(e), r = Ext.lib.Dom.getRegion(n.firstChild), px, pt;
+        if((r.right - x) <= (r.right - r.left) / 2){
+            px = r.right + this.view.borderWidth;
+            pt = "after";
+        }else{
+            px = r.left;
+            pt = "before";
+        }
+        var oldIndex = this.view.getCellIndex(h), newIndex = this.view.getCellIndex(n);
+        if(cm.isFixed(newIndex)){
+            return false;
+        }
+        var row = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupRowIndex.call(this.view, h),
+            oldGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row, oldIndex),
+            newGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row, newIndex),
+            oldIndex = oldGroup.col;
+            newIndex = newGroup.col + (pt == "after" ? newGroup.colspan : 0);
+        if(newIndex >= oldGroup.col && newIndex <= oldGroup.col + oldGroup.colspan){
+            return false;
+        }
+        var parentGroup = Ext.ux.grid.ColumnHeaderGroup.prototype.getGroupSpan.call(this.view, row - 1, oldIndex);
+        if(newIndex < parentGroup.col || newIndex > parentGroup.col + parentGroup.colspan){
+            return false;
+        }
+        return {
+            r: r,
+            px: px,
+            pt: pt,
+            row: row,
+            oldIndex: oldIndex,
+            newIndex: newIndex,
+            colspan: oldGroup.colspan
+        };
+    }
 });
\ No newline at end of file