Upgrade to ExtJS 3.2.0 - Released 03/30/2010
[extjs.git] / examples / ux / LockingGridView.js
index 0015561..39673dc 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.LockingGridView = Ext.extend(Ext.grid.GridView, {\r
-    lockText : 'Lock',\r
-    unlockText : 'Unlock',\r
-    rowBorderWidth : 1,\r
-    lockedBorderWidth : 1,\r
-    /*\r
-     * This option ensures that height between the rows is synchronized\r
-     * between the locked and unlocked sides. This option only needs to be used\r
-     * when the row heights isn't predictable.\r
-     */\r
-    syncHeights: false,\r
-    initTemplates : function(){\r
-        var ts = this.templates || {};\r
-        if(!ts.master){\r
-            ts.master = new Ext.Template(\r
-                '<div class="x-grid3" hidefocus="true">',\r
-                    '<div class="x-grid3-locked">',\r
-                        '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{lstyle}">{lockedHeader}</div></div><div class="x-clear"></div></div>',\r
-                        '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{lstyle}">{lockedBody}</div><div class="x-grid3-scroll-spacer"></div></div>',\r
-                    '</div>',\r
-                    '<div class="x-grid3-viewport x-grid3-unlocked">',\r
-                        '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',\r
-                        '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',\r
-                    '</div>',\r
-                    '<div class="x-grid3-resize-marker">&#160;</div>',\r
-                    '<div class="x-grid3-resize-proxy">&#160;</div>',\r
-                '</div>'\r
-            );\r
-        }\r
-        this.templates = ts;\r
-        Ext.ux.grid.LockingGridView.superclass.initTemplates.call(this);\r
-    },\r
-    getEditorParent : function(ed){\r
-        return this.el.dom;\r
-    },\r
-    initElements : function(){\r
-        var E = Ext.Element;\r
-        var el = this.grid.getGridEl().dom.firstChild;\r
-        var cs = el.childNodes;\r
-        this.el = new E(el);\r
-        this.lockedWrap = new E(cs[0]);\r
-        this.lockedHd = new E(this.lockedWrap.dom.firstChild);\r
-        this.lockedInnerHd = this.lockedHd.dom.firstChild;\r
-        this.lockedScroller = new E(this.lockedWrap.dom.childNodes[1]);\r
-        this.lockedBody = new E(this.lockedScroller.dom.firstChild);\r
-        this.mainWrap = new E(cs[1]);\r
-        this.mainHd = new E(this.mainWrap.dom.firstChild);\r
-        if(this.grid.hideHeaders){\r
-            this.lockedHd.setDisplayed(false);\r
-            this.mainHd.setDisplayed(false);\r
-        }\r
-        this.innerHd = this.mainHd.dom.firstChild;\r
-        this.scroller = new E(this.mainWrap.dom.childNodes[1]);\r
-        if(this.forceFit){\r
-            this.scroller.setStyle('overflow-x', 'hidden');\r
-        }\r
-        this.mainBody = new E(this.scroller.dom.firstChild);\r
-        this.focusEl = new E(this.scroller.dom.childNodes[1]);\r
-        this.focusEl.swallowEvent('click', true);\r
-        this.resizeMarker = new E(cs[2]);\r
-        this.resizeProxy = new E(cs[3]);\r
-    },\r
-    \r
-    getLockedRows : function(){\r
-        return this.hasRows() ? this.lockedBody.dom.childNodes : [];\r
-    },\r
-    \r
-    getLockedRow : function(row){\r
-        return this.getLockedRows()[row];\r
-    },\r
-    \r
-    getCell : function(row, col){\r
-        var llen = this.cm.getLockedCount();\r
-        if(col < llen){\r
-            return this.getLockedRow(row).getElementsByTagName('td')[col];\r
-        }\r
-        return Ext.ux.grid.LockingGridView.superclass.getCell.call(this, row, col - llen);\r
-    },\r
-    \r
-    getHeaderCell : function(index){\r
-        var llen = this.cm.getLockedCount();\r
-        if(index < llen){\r
-            return this.lockedHd.dom.getElementsByTagName('td')[index];\r
-        }\r
-        return Ext.ux.grid.LockingGridView.superclass.getHeaderCell.call(this, index - llen);\r
-    },\r
-    \r
-    addRowClass : function(row, cls){\r
-        var r = this.getLockedRow(row);\r
-        if(r){\r
-            this.fly(r).addClass(cls);\r
-        }\r
-        Ext.ux.grid.LockingGridView.superclass.addRowClass.call(this, row, cls);\r
-    },\r
-    \r
-    removeRowClass : function(row, cls){\r
-        var r = this.getLockedRow(row);\r
-        if(r){\r
-            this.fly(r).removeClass(cls);\r
-        }\r
-        Ext.ux.grid.LockingGridView.superclass.removeRowClass.call(this, row, cls);\r
-    },\r
-    \r
-    removeRow : function(row) {\r
-        Ext.removeNode(this.getLockedRow(row));\r
-        Ext.ux.grid.LockingGridView.superclass.removeRow.call(this, row);\r
-    },\r
-    \r
-    removeRows : function(firstRow, lastRow){\r
-        var bd = this.lockedBody.dom;\r
-        for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){\r
-            Ext.removeNode(bd.childNodes[firstRow]);\r
-        }\r
-        Ext.ux.grid.LockingGridView.superclass.removeRows.call(this, firstRow, lastRow);\r
-    },\r
-    \r
-    syncScroll : function(e){\r
-        var mb = this.scroller.dom;\r
-        this.lockedScroller.dom.scrollTop = mb.scrollTop;\r
-        Ext.ux.grid.LockingGridView.superclass.syncScroll.call(this, e);\r
-    },\r
-    \r
-    updateSortIcon : function(col, dir){\r
-        var sc = this.sortClasses,\r
-            lhds = this.lockedHd.select('td').removeClass(sc),\r
-            hds = this.mainHd.select('td').removeClass(sc),\r
-            llen = this.cm.getLockedCount(),\r
-            cls = sc[dir == 'DESC' ? 1 : 0];\r
-        if(col < llen){\r
-            lhds.item(col).addClass(cls);\r
-        }else{\r
-            hds.item(col - llen).addClass(cls);\r
-        }\r
-    },\r
-    \r
-    updateAllColumnWidths : function(){\r
-        var tw = this.getTotalWidth(),\r
-            clen = this.cm.getColumnCount(),\r
-            lw = this.getLockedWidth(),\r
-            llen = this.cm.getLockedCount(),\r
-            ws = [], len, i;\r
-        this.updateLockedWidth();\r
-        for(i = 0; i < clen; i++){\r
-            ws[i] = this.getColumnWidth(i);\r
-            var hd = this.getHeaderCell(i);\r
-            hd.style.width = ws[i];\r
-        }\r
-        var lns = this.getLockedRows(), ns = this.getRows(), row, trow, j;\r
-        for(i = 0, len = ns.length; i < len; i++){\r
-            row = lns[i];\r
-            row.style.width = lw;\r
-            if(row.firstChild){\r
-                row.firstChild.style.width = lw;\r
-                trow = row.firstChild.rows[0];\r
-                for (j = 0; j < llen; j++) {\r
-                   trow.childNodes[j].style.width = ws[j];\r
-                }\r
-            }\r
-            row = ns[i];\r
-            row.style.width = tw;\r
-            if(row.firstChild){\r
-                row.firstChild.style.width = tw;\r
-                trow = row.firstChild.rows[0];\r
-                for (j = llen; j < clen; j++) {\r
-                   trow.childNodes[j - llen].style.width = ws[j];\r
-                }\r
-            }\r
-        }\r
-        this.onAllColumnWidthsUpdated(ws, tw);\r
-        this.syncHeaderHeight();\r
-    },\r
-    \r
-    updateColumnWidth : function(col, width){\r
-        var w = this.getColumnWidth(col),\r
-            llen = this.cm.getLockedCount(),\r
-            ns, rw, c, row;\r
-        this.updateLockedWidth();\r
-        if(col < llen){\r
-            ns = this.getLockedRows();\r
-            rw = this.getLockedWidth();\r
-            c = col;\r
-        }else{\r
-            ns = this.getRows();\r
-            rw = this.getTotalWidth();\r
-            c = col - llen;\r
-        }\r
-        var hd = this.getHeaderCell(col);\r
-        hd.style.width = w;\r
-        for(var i = 0, len = ns.length; i < len; i++){\r
-            row = ns[i];\r
-            row.style.width = rw;\r
-            if(row.firstChild){\r
-                row.firstChild.style.width = rw;\r
-                row.firstChild.rows[0].childNodes[c].style.width = w;\r
-            }\r
-        }\r
-        this.onColumnWidthUpdated(col, w, this.getTotalWidth());\r
-        this.syncHeaderHeight();\r
-    },\r
-    \r
-    updateColumnHidden : function(col, hidden){\r
-        var llen = this.cm.getLockedCount(),\r
-            ns, rw, c, row,\r
-            display = hidden ? 'none' : '';\r
-        this.updateLockedWidth();\r
-        if(col < llen){\r
-            ns = this.getLockedRows();\r
-            rw = this.getLockedWidth();\r
-            c = col;\r
-        }else{\r
-            ns = this.getRows();\r
-            rw = this.getTotalWidth();\r
-            c = col - llen;\r
-        }\r
-        var hd = this.getHeaderCell(col);\r
-        hd.style.display = display;\r
-        for(var i = 0, len = ns.length; i < len; i++){\r
-            row = ns[i];\r
-            row.style.width = rw;\r
-            if(row.firstChild){\r
-                row.firstChild.style.width = rw;\r
-                row.firstChild.rows[0].childNodes[c].style.display = display;\r
-            }\r
-        }\r
-        this.onColumnHiddenUpdated(col, hidden, this.getTotalWidth());\r
-        delete this.lastViewWidth;\r
-        this.layout();\r
-    },\r
-    \r
-    doRender : function(cs, rs, ds, startRow, colCount, stripe){\r
-        var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1,\r
-            tstyle = 'width:'+this.getTotalWidth()+';',\r
-            lstyle = 'width:'+this.getLockedWidth()+';',\r
-            buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r;\r
-        for(var j = 0, len = rs.length; j < len; j++){\r
-            r = rs[j]; cb = []; lcb = [];\r
-            var rowIndex = (j+startRow);\r
-            for(var i = 0; i < colCount; i++){\r
-                c = cs[i];\r
-                p.id = c.id;\r
-                p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +\r
-                    (this.cm.config[i].cellCls ? ' ' + this.cm.config[i].cellCls : '');\r
-                p.attr = p.cellAttr = '';\r
-                p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);\r
-                p.style = c.style;\r
-                if(Ext.isEmpty(p.value)){\r
-                    p.value = '&#160;';\r
-                }\r
-                if(this.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])){\r
-                    p.css += ' x-grid3-dirty-cell';\r
-                }\r
-                if(c.locked){\r
-                    lcb[lcb.length] = ct.apply(p);\r
-                }else{\r
-                    cb[cb.length] = ct.apply(p);\r
-                }\r
-            }\r
-            var alt = [];\r
-            if(stripe && ((rowIndex+1) % 2 === 0)){\r
-                alt[0] = 'x-grid3-row-alt';\r
-            }\r
-            if(r.dirty){\r
-                alt[1] = ' x-grid3-dirty-row';\r
-            }\r
-            rp.cols = colCount;\r
-            if(this.getRowClass){\r
-                alt[2] = this.getRowClass(r, rowIndex, rp, ds);\r
-            }\r
-            rp.alt = alt.join(' ');\r
-            rp.cells = cb.join('');\r
-            rp.tstyle = tstyle;\r
-            buf[buf.length] = rt.apply(rp);\r
-            rp.cells = lcb.join('');\r
-            rp.tstyle = lstyle;\r
-            lbuf[lbuf.length] = rt.apply(rp);\r
-        }\r
-        return [buf.join(''), lbuf.join('')];\r
-    },\r
-    processRows : function(startRow, skipStripe){\r
-        if(!this.ds || this.ds.getCount() < 1){\r
-            return;\r
-        }\r
-        var rows = this.getRows(),\r
-            lrows = this.getLockedRows(),\r
-            row, lrow;\r
-        skipStripe = skipStripe || !this.grid.stripeRows;\r
-        startRow = startRow || 0;\r
-        for(var i = 0, len = rows.length; i < len; ++i){\r
-            row = rows[i];\r
-            lrow = lrows[i];\r
-            row.rowIndex = i;\r
-            lrow.rowIndex = i;\r
-            if(!skipStripe){\r
-                row.className = row.className.replace(this.rowClsRe, ' ');\r
-                lrow.className = lrow.className.replace(this.rowClsRe, ' ');\r
-                if ((i + 1) % 2 === 0){\r
-                    row.className += ' x-grid3-row-alt';\r
-                    lrow.className += ' x-grid3-row-alt';\r
-                }\r
-            }\r
-            if(this.syncHeights){\r
-                var el1 = Ext.get(row),\r
-                    el2 = Ext.get(lrow),\r
-                    h1 = el1.getHeight(),\r
-                    h2 = el2.getHeight();\r
-                \r
-                if(h1 > h2){\r
-                    el2.setHeight(h1);    \r
-                }else if(h2 > h1){\r
-                    el1.setHeight(h2);\r
-                }\r
-            }\r
-        }\r
-        if(startRow === 0){\r
-            Ext.fly(rows[0]).addClass(this.firstRowCls);\r
-            Ext.fly(lrows[0]).addClass(this.firstRowCls);\r
-        }\r
-        Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);\r
-        Ext.fly(lrows[lrows.length - 1]).addClass(this.lastRowCls);\r
-    },\r
-    \r
-    afterRender : function(){\r
-        if(!this.ds || !this.cm){\r
-            return;\r
-        }\r
-        var bd = this.renderRows() || ['&#160;', '&#160;'];\r
-        this.mainBody.dom.innerHTML = bd[0];\r
-        this.lockedBody.dom.innerHTML = bd[1];\r
-        this.processRows(0, true);\r
-        if(this.deferEmptyText !== true){\r
-            this.applyEmptyText();\r
-        }\r
-    },\r
-    \r
-    renderUI : function(){\r
-        var header = this.renderHeaders();\r
-        var body = this.templates.body.apply({rows:'&#160;'});\r
-        var html = this.templates.master.apply({\r
-            body: body,\r
-            header: header[0],\r
-            ostyle: 'width:'+this.getOffsetWidth()+';',\r
-            bstyle: 'width:'+this.getTotalWidth()+';',\r
-            lockedBody: body,\r
-            lockedHeader: header[1],\r
-            lstyle: 'width:'+this.getLockedWidth()+';'\r
-        });\r
-        var g = this.grid;\r
-        g.getGridEl().dom.innerHTML = html;\r
-        this.initElements();\r
-        Ext.fly(this.innerHd).on('click', this.handleHdDown, this);\r
-        Ext.fly(this.lockedInnerHd).on('click', this.handleHdDown, this);\r
-        this.mainHd.on({\r
-            scope: this,\r
-            mouseover: this.handleHdOver,\r
-            mouseout: this.handleHdOut,\r
-            mousemove: this.handleHdMove\r
-        });\r
-        this.lockedHd.on({\r
-            scope: this,\r
-            mouseover: this.handleHdOver,\r
-            mouseout: this.handleHdOut,\r
-            mousemove: this.handleHdMove\r
-        });\r
-        this.scroller.on('scroll', this.syncScroll,  this);\r
-        if(g.enableColumnResize !== false){\r
-            this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);\r
-            this.splitZone.setOuterHandleElId(Ext.id(this.lockedHd.dom));\r
-            this.splitZone.setOuterHandleElId(Ext.id(this.mainHd.dom));\r
-        }\r
-        if(g.enableColumnMove){\r
-            this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);\r
-            this.columnDrag.setOuterHandleElId(Ext.id(this.lockedInnerHd));\r
-            this.columnDrag.setOuterHandleElId(Ext.id(this.innerHd));\r
-            this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);\r
-        }\r
-        if(g.enableHdMenu !== false){\r
-            this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});\r
-            this.hmenu.add(\r
-                {itemId: 'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},\r
-                {itemId: 'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}\r
-            );\r
-            if(this.grid.enableColLock !== false){\r
-                this.hmenu.add('-',\r
-                    {itemId: 'lock', text: this.lockText, cls: 'xg-hmenu-lock'},\r
-                    {itemId: 'unlock', text: this.unlockText, cls: 'xg-hmenu-unlock'}\r
-                );\r
-            }\r
-            if(g.enableColumnHide !== false){\r
-                this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});\r
-                this.colMenu.on({\r
-                    scope: this,\r
-                    beforeshow: this.beforeColMenuShow,\r
-                    itemclick: this.handleHdMenuClick\r
-                });\r
-                this.hmenu.add('-', {\r
-                    itemId:'columns',\r
-                    hideOnClick: false,\r
-                    text: this.columnsText,\r
-                    menu: this.colMenu,\r
-                    iconCls: 'x-cols-icon'\r
-                });\r
-            }\r
-            this.hmenu.on('itemclick', this.handleHdMenuClick, this);\r
-        }\r
-        if(g.trackMouseOver){\r
-            this.mainBody.on({\r
-                scope: this,\r
-                mouseover: this.onRowOver,\r
-                mouseout: this.onRowOut\r
-            });\r
-            this.lockedBody.on({\r
-                scope: this,\r
-                mouseover: this.onRowOver,\r
-                mouseout: this.onRowOut\r
-            });\r
-        }\r
-        \r
-        if(g.enableDragDrop || g.enableDrag){\r
-            this.dragZone = new Ext.grid.GridDragZone(g, {\r
-                ddGroup : g.ddGroup || 'GridDD'\r
-            });\r
-        }\r
-        this.updateHeaderSortState();\r
-    },\r
-    \r
-    layout : function(){\r
-        if(!this.mainBody){\r
-            return;\r
-        }\r
-        var g = this.grid;\r
-        var c = g.getGridEl();\r
-        var csize = c.getSize(true);\r
-        var vw = csize.width;\r
-        if(!g.hideHeaders && (vw < 20 || csize.height < 20)){\r
-            return;\r
-        }\r
-        this.syncHeaderHeight();\r
-        if(g.autoHeight){\r
-            this.scroller.dom.style.overflow = 'visible';\r
-            this.lockedScroller.dom.style.overflow = 'visible';\r
-            if(Ext.isWebKit){\r
-                this.scroller.dom.style.position = 'static';\r
-                this.lockedScroller.dom.style.position = 'static';\r
-            }\r
-        }else{\r
-            this.el.setSize(csize.width, csize.height);\r
-            var hdHeight = this.mainHd.getHeight();\r
-            var vh = csize.height - (hdHeight);\r
-        }\r
-        this.updateLockedWidth();\r
-        if(this.forceFit){\r
-            if(this.lastViewWidth != vw){\r
-                this.fitColumns(false, false);\r
-                this.lastViewWidth = vw;\r
-            }\r
-        }else {\r
-            this.autoExpand();\r
-            this.syncHeaderScroll();\r
-        }\r
-        this.onLayout(vw, vh);\r
-    },\r
-    \r
-    getOffsetWidth : function() {\r
-        return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth() + this.getScrollOffset()) + 'px';\r
-    },\r
-    \r
-    renderHeaders : function(){\r
-        var cm = this.cm,\r
-            ts = this.templates,\r
-            ct = ts.hcell,\r
-            cb = [], lcb = [],\r
-            p = {},\r
-            len = cm.getColumnCount(),\r
-            last = len - 1;\r
-        for(var i = 0; i < len; i++){\r
-            p.id = cm.getColumnId(i);\r
-            p.value = cm.getColumnHeader(i) || '';\r
-            p.style = this.getColumnStyle(i, true);\r
-            p.tooltip = this.getColumnTooltip(i);\r
-            p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +\r
-                (cm.config[i].headerCls ? ' ' + cm.config[i].headerCls : '');\r
-            if(cm.config[i].align == 'right'){\r
-                p.istyle = 'padding-right:16px';\r
-            } else {\r
-                delete p.istyle;\r
-            }\r
-            if(cm.isLocked(i)){\r
-                lcb[lcb.length] = ct.apply(p);\r
-            }else{\r
-                cb[cb.length] = ct.apply(p);\r
-            }\r
-        }\r
-        return [ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'}),\r
-                ts.header.apply({cells: lcb.join(''), tstyle:'width:'+this.getLockedWidth()+';'})];\r
-    },\r
-    \r
-    updateHeaders : function(){\r
-        var hd = this.renderHeaders();\r
-        this.innerHd.firstChild.innerHTML = hd[0];\r
-        this.innerHd.firstChild.style.width = this.getOffsetWidth();\r
-        this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();\r
-        this.lockedInnerHd.firstChild.innerHTML = hd[1];\r
-        var lw = this.getLockedWidth();\r
-        this.lockedInnerHd.firstChild.style.width = lw;\r
-        this.lockedInnerHd.firstChild.firstChild.style.width = lw;\r
-    },\r
-    \r
-    getResolvedXY : function(resolved){\r
-        if(!resolved){\r
-            return null;\r
-        }\r
-        var c = resolved.cell, r = resolved.row;\r
-        return c ? Ext.fly(c).getXY() : [this.scroller.getX(), Ext.fly(r).getY()];\r
-    },\r
-    \r
-    syncFocusEl : function(row, col, hscroll){\r
-        Ext.ux.grid.LockingGridView.superclass.syncFocusEl.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);\r
-    },\r
-    \r
-    ensureVisible : function(row, col, hscroll){\r
-        return Ext.ux.grid.LockingGridView.superclass.ensureVisible.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);\r
-    },\r
-    \r
-    insertRows : function(dm, firstRow, lastRow, isUpdate){\r
-        var last = dm.getCount() - 1;\r
-        if(!isUpdate && firstRow === 0 && lastRow >= last){\r
-            this.refresh();\r
-        }else{\r
-            if(!isUpdate){\r
-                this.fireEvent('beforerowsinserted', this, firstRow, lastRow);\r
-            }\r
-            var html = this.renderRows(firstRow, lastRow),\r
-                before = this.getRow(firstRow);\r
-            if(before){\r
-                if(firstRow === 0){\r
-                    this.removeRowClass(0, this.firstRowCls);\r
-                }\r
-                Ext.DomHelper.insertHtml('beforeBegin', before, html[0]);\r
-                before = this.getLockedRow(firstRow);\r
-                Ext.DomHelper.insertHtml('beforeBegin', before, html[1]);\r
-            }else{\r
-                this.removeRowClass(last - 1, this.lastRowCls);\r
-                Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html[0]);\r
-                Ext.DomHelper.insertHtml('beforeEnd', this.lockedBody.dom, html[1]);\r
-            }\r
-            if(!isUpdate){\r
-                this.fireEvent('rowsinserted', this, firstRow, lastRow);\r
-                this.processRows(firstRow);\r
-            }else if(firstRow === 0 || firstRow >= last){\r
-                this.addRowClass(firstRow, firstRow === 0 ? this.firstRowCls : this.lastRowCls);\r
-            }\r
-        }\r
-        this.syncFocusEl(firstRow);\r
-    },\r
-    \r
-    getColumnStyle : function(col, isHeader){\r
-        var style = !isHeader ? this.cm.config[col].cellStyle || this.cm.config[col].css || '' : this.cm.config[col].headerStyle || '';\r
-        style += 'width:'+this.getColumnWidth(col)+';';\r
-        if(this.cm.isHidden(col)){\r
-            style += 'display:none;';\r
-        }\r
-        var align = this.cm.config[col].align;\r
-        if(align){\r
-            style += 'text-align:'+align+';';\r
-        }\r
-        return style;\r
-    },\r
-    \r
-    getLockedWidth : function() {\r
-        return this.cm.getTotalLockedWidth() + 'px';\r
-    },\r
-    \r
-    getTotalWidth : function() {\r
-        return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth()) + 'px';\r
-    },\r
-    \r
-    getColumnData : function(){\r
-        var cs = [], cm = this.cm, colCount = cm.getColumnCount();\r
-        for(var i = 0; i < colCount; i++){\r
-            var name = cm.getDataIndex(i);\r
-            cs[i] = {\r
-                name : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),\r
-                renderer : cm.getRenderer(i),\r
-                id : cm.getColumnId(i),\r
-                style : this.getColumnStyle(i),\r
-                locked : cm.isLocked(i)\r
-            };\r
-        }\r
-        return cs;\r
-    },\r
-    \r
-    renderBody : function(){\r
-        var markup = this.renderRows() || ['&#160;', '&#160;'];\r
-        return [this.templates.body.apply({rows: markup[0]}), this.templates.body.apply({rows: markup[1]})];\r
-    },\r
-    \r
-    refreshRow : function(record){\r
-        Ext.ux.grid.LockingGridView.superclass.refreshRow.call(this, record);\r
-        var index = Ext.isNumber(record) ? record : this.ds.indexOf(record);\r
-        this.getLockedRow(index).rowIndex = index;\r
-    },\r
-    \r
-    refresh : function(headersToo){\r
-        this.fireEvent('beforerefresh', this);\r
-        this.grid.stopEditing(true);\r
-        var result = this.renderBody();\r
-        this.mainBody.update(result[0]).setWidth(this.getTotalWidth());\r
-        this.lockedBody.update(result[1]).setWidth(this.getLockedWidth());\r
-        if(headersToo === true){\r
-            this.updateHeaders();\r
-            this.updateHeaderSortState();\r
-        }\r
-        this.processRows(0, true);\r
-        this.layout();\r
-        this.applyEmptyText();\r
-        this.fireEvent('refresh', this);\r
-    },\r
-    \r
-    onDenyColumnLock : function(){\r
-\r
-    },\r
-    \r
-    initData : function(ds, cm){\r
-        if(this.cm){\r
-            this.cm.un('columnlockchange', this.onColumnLock, this);\r
-        }\r
-        Ext.ux.grid.LockingGridView.superclass.initData.call(this, ds, cm);\r
-        if(this.cm){\r
-            this.cm.on('columnlockchange', this.onColumnLock, this);\r
-        }\r
-    },\r
-    \r
-    onColumnLock : function(){\r
-        this.refresh(true);\r
-    },\r
-    \r
-    handleHdMenuClick : function(item){\r
-        var index = this.hdCtxIndex,\r
-            cm = this.cm,\r
-            id = item.getItemId(),\r
-            llen = cm.getLockedCount();\r
-        switch(id){\r
-            case 'lock':\r
-                if(cm.getColumnCount(true) <= llen + 1){\r
-                    this.onDenyColumnLock();\r
-                    return;\r
-                }\r
-                if(llen != index){\r
-                    cm.setLocked(index, true, true);\r
-                    cm.moveColumn(index, llen);\r
-                    this.grid.fireEvent('columnmove', index, llen);\r
-                }else{\r
-                    cm.setLocked(index, true);\r
-                }\r
-            break;\r
-            case 'unlock':\r
-                if(llen - 1 != index){\r
-                    cm.setLocked(index, false, true);\r
-                    cm.moveColumn(index, llen - 1);\r
-                    this.grid.fireEvent('columnmove', index, llen - 1);\r
-                }else{\r
-                    cm.setLocked(index, false);\r
-                }\r
-            break;\r
-            default:\r
-                return Ext.ux.grid.LockingGridView.superclass.handleHdMenuClick.call(this, item);\r
-        }\r
-        return true;\r
-    },\r
-    \r
-    handleHdDown : function(e, t){\r
-        Ext.ux.grid.LockingGridView.superclass.handleHdDown.call(this, e, t);\r
-        if(this.grid.enableColLock !== false){\r
-            if(Ext.fly(t).hasClass('x-grid3-hd-btn')){\r
-                var hd = this.findHeaderCell(t),\r
-                    index = this.getCellIndex(hd),\r
-                    ms = this.hmenu.items, cm = this.cm;\r
-                ms.get('lock').setDisabled(cm.isLocked(index));\r
-                ms.get('unlock').setDisabled(!cm.isLocked(index));\r
-            }\r
-        }\r
-    },\r
-    \r
-    syncHeaderHeight: function(){\r
-        this.innerHd.firstChild.firstChild.style.height = 'auto';\r
-        this.lockedInnerHd.firstChild.firstChild.style.height = 'auto';\r
-        var hd = this.innerHd.firstChild.firstChild.offsetHeight,\r
-            lhd = this.lockedInnerHd.firstChild.firstChild.offsetHeight,\r
-            height = (lhd > hd ? lhd : hd) + 'px';\r
-        this.innerHd.firstChild.firstChild.style.height = height;\r
-        this.lockedInnerHd.firstChild.firstChild.style.height = height;\r
-    },\r
-    \r
-    updateLockedWidth: function(){\r
-        var lw = this.cm.getTotalLockedWidth(),\r
-            tw = this.cm.getTotalWidth() - lw,\r
-            csize = this.grid.getGridEl().getSize(true),\r
-            lp = Ext.isBorderBox ? 0 : this.lockedBorderWidth,\r
-            rp = Ext.isBorderBox ? 0 : this.rowBorderWidth,\r
-            vw = (csize.width - lw - lp - rp) + 'px',\r
-            so = this.getScrollOffset();\r
-        if(!this.grid.autoHeight){\r
-            var vh = (csize.height - this.mainHd.getHeight()) + 'px';\r
-            this.lockedScroller.dom.style.height = vh;\r
-            this.scroller.dom.style.height = vh;\r
-        }\r
-        this.lockedWrap.dom.style.width = (lw + rp) + 'px';\r
-        this.scroller.dom.style.width = vw;\r
-        this.mainWrap.dom.style.left = (lw + lp + rp) + 'px';\r
-        if(this.innerHd){\r
-            this.lockedInnerHd.firstChild.style.width = lw + 'px';\r
-            this.lockedInnerHd.firstChild.firstChild.style.width = lw + 'px';\r
-            this.innerHd.style.width = vw;\r
-            this.innerHd.firstChild.style.width = (tw + rp + so) + 'px';\r
-            this.innerHd.firstChild.firstChild.style.width = tw + 'px';\r
-        }\r
-        if(this.mainBody){\r
-            this.lockedBody.dom.style.width = (lw + rp) + 'px';\r
-            this.mainBody.dom.style.width = (tw + rp) + 'px';\r
-        }\r
-    }\r
-});\r
-\r
-Ext.ux.grid.LockingColumnModel = Ext.extend(Ext.grid.ColumnModel, {\r
-    isLocked : function(colIndex){\r
-        return this.config[colIndex].locked === true;\r
-    },\r
-    \r
-    setLocked : function(colIndex, value, suppressEvent){\r
-        if(this.isLocked(colIndex) == value){\r
-            return;\r
-        }\r
-        this.config[colIndex].locked = value;\r
-        if(!suppressEvent){\r
-            this.fireEvent('columnlockchange', this, colIndex, value);\r
-        }\r
-    },\r
-    \r
-    getTotalLockedWidth : function(){\r
-        var totalWidth = 0;\r
-        for(var i = 0, len = this.config.length; i < len; i++){\r
-            if(this.isLocked(i) && !this.isHidden(i)){\r
-                totalWidth += this.getColumnWidth(i);\r
-            }\r
-        }\r
-        return totalWidth;\r
-    },\r
-    \r
-    getLockedCount : function(){\r
-        for(var i = 0, len = this.config.length; i < len; i++){\r
-            if(!this.isLocked(i)){\r
-                return i;\r
-            }\r
-        }\r
-    },\r
-    \r
-    moveColumn : function(oldIndex, newIndex){\r
-        if(oldIndex < newIndex && this.isLocked(oldIndex) && !this.isLocked(newIndex)){\r
-            this.setLocked(oldIndex, false, true);\r
-        }else if(oldIndex > newIndex && !this.isLocked(oldIndex) && this.isLocked(newIndex)){\r
-            this.setLocked(oldIndex, true, true);\r
-        }\r
-        Ext.ux.grid.LockingColumnModel.superclass.moveColumn.apply(this, arguments);\r
-    }\r
-});\r
+Ext.ns('Ext.ux.grid');
+
+Ext.ux.grid.LockingGridView = Ext.extend(Ext.grid.GridView, {
+    lockText : 'Lock',
+    unlockText : 'Unlock',
+    rowBorderWidth : 1,
+    lockedBorderWidth : 1,
+    
+    /*
+     * This option ensures that height between the rows is synchronized
+     * between the locked and unlocked sides. This option only needs to be used
+     * when the row heights aren't predictable.
+     */
+    syncHeights: false,
+    
+    initTemplates : function(){
+        var ts = this.templates || {};
+        
+        if (!ts.master) {
+            ts.master = new Ext.Template(
+                '<div class="x-grid3" hidefocus="true">',
+                    '<div class="x-grid3-locked">',
+                        '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{lstyle}">{lockedHeader}</div></div><div class="x-clear"></div></div>',
+                        '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{lstyle}">{lockedBody}</div><div class="x-grid3-scroll-spacer"></div></div>',
+                    '</div>',
+                    '<div class="x-grid3-viewport x-grid3-unlocked">',
+                        '<div class="x-grid3-header"><div class="x-grid3-header-inner"><div class="x-grid3-header-offset" style="{ostyle}">{header}</div></div><div class="x-clear"></div></div>',
+                        '<div class="x-grid3-scroller"><div class="x-grid3-body" style="{bstyle}">{body}</div><a href="#" class="x-grid3-focus" tabIndex="-1"></a></div>',
+                    '</div>',
+                    '<div class="x-grid3-resize-marker">&#160;</div>',
+                    '<div class="x-grid3-resize-proxy">&#160;</div>',
+                '</div>'
+            );
+        }
+        
+        this.templates = ts;
+        
+        Ext.ux.grid.LockingGridView.superclass.initTemplates.call(this);
+    },
+    
+    getEditorParent : function(ed){
+        return this.el.dom;
+    },
+    
+    initElements : function(){
+        var E  = Ext.Element,
+            el = this.grid.getGridEl().dom.firstChild,
+            cs = el.childNodes;
+            
+        this.el             = new E(el);
+        this.lockedWrap     = new E(cs[0]);
+        this.lockedHd       = new E(this.lockedWrap.dom.firstChild);
+        this.lockedInnerHd  = this.lockedHd.dom.firstChild;
+        this.lockedScroller = new E(this.lockedWrap.dom.childNodes[1]);
+        this.lockedBody     = new E(this.lockedScroller.dom.firstChild);
+        this.mainWrap       = new E(cs[1]);
+        this.mainHd         = new E(this.mainWrap.dom.firstChild);
+        
+        if (this.grid.hideHeaders) {
+            this.lockedHd.setDisplayed(false);
+            this.mainHd.setDisplayed(false);
+        }
+        
+        this.innerHd  = this.mainHd.dom.firstChild;
+        this.scroller = new E(this.mainWrap.dom.childNodes[1]);
+        
+        if(this.forceFit){
+            this.scroller.setStyle('overflow-x', 'hidden');
+        }
+        
+        this.mainBody     = new E(this.scroller.dom.firstChild);
+        this.focusEl      = new E(this.scroller.dom.childNodes[1]);
+        this.resizeMarker = new E(cs[2]);
+        this.resizeProxy  = new E(cs[3]);
+        
+        this.focusEl.swallowEvent('click', true);
+    },
+    
+    getLockedRows : function(){
+        return this.hasRows() ? this.lockedBody.dom.childNodes : [];
+    },
+    
+    getLockedRow : function(row){
+        return this.getLockedRows()[row];
+    },
+    
+    getCell : function(row, col){
+        var llen = this.cm.getLockedCount();
+        if(col < llen){
+            return this.getLockedRow(row).getElementsByTagName('td')[col];
+        }
+        return Ext.ux.grid.LockingGridView.superclass.getCell.call(this, row, col - llen);
+    },
+    
+    getHeaderCell : function(index){
+        var llen = this.cm.getLockedCount();
+        if(index < llen){
+            return this.lockedHd.dom.getElementsByTagName('td')[index];
+        }
+        return Ext.ux.grid.LockingGridView.superclass.getHeaderCell.call(this, index - llen);
+    },
+    
+    addRowClass : function(row, cls){
+        var r = this.getLockedRow(row);
+        if(r){
+            this.fly(r).addClass(cls);
+        }
+        Ext.ux.grid.LockingGridView.superclass.addRowClass.call(this, row, cls);
+    },
+    
+    removeRowClass : function(row, cls){
+        var r = this.getLockedRow(row);
+        if(r){
+            this.fly(r).removeClass(cls);
+        }
+        Ext.ux.grid.LockingGridView.superclass.removeRowClass.call(this, row, cls);
+    },
+    
+    removeRow : function(row) {
+        Ext.removeNode(this.getLockedRow(row));
+        Ext.ux.grid.LockingGridView.superclass.removeRow.call(this, row);
+    },
+    
+    removeRows : function(firstRow, lastRow){
+        var bd = this.lockedBody.dom;
+        for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
+            Ext.removeNode(bd.childNodes[firstRow]);
+        }
+        Ext.ux.grid.LockingGridView.superclass.removeRows.call(this, firstRow, lastRow);
+    },
+    
+    syncScroll : function(e){
+        var mb = this.scroller.dom;
+        this.lockedScroller.dom.scrollTop = mb.scrollTop;
+        Ext.ux.grid.LockingGridView.superclass.syncScroll.call(this, e);
+    },
+    
+    updateSortIcon : function(col, dir){
+        var sc = this.sortClasses,
+            lhds = this.lockedHd.select('td').removeClass(sc),
+            hds = this.mainHd.select('td').removeClass(sc),
+            llen = this.cm.getLockedCount(),
+            cls = sc[dir == 'DESC' ? 1 : 0];
+        if(col < llen){
+            lhds.item(col).addClass(cls);
+        }else{
+            hds.item(col - llen).addClass(cls);
+        }
+    },
+    
+    updateAllColumnWidths : function(){
+        var tw = this.getTotalWidth(),
+            clen = this.cm.getColumnCount(),
+            lw = this.getLockedWidth(),
+            llen = this.cm.getLockedCount(),
+            ws = [], len, i;
+        this.updateLockedWidth();
+        for(i = 0; i < clen; i++){
+            ws[i] = this.getColumnWidth(i);
+            var hd = this.getHeaderCell(i);
+            hd.style.width = ws[i];
+        }
+        var lns = this.getLockedRows(), ns = this.getRows(), row, trow, j;
+        for(i = 0, len = ns.length; i < len; i++){
+            row = lns[i];
+            row.style.width = lw;
+            if(row.firstChild){
+                row.firstChild.style.width = lw;
+                trow = row.firstChild.rows[0];
+                for (j = 0; j < llen; j++) {
+                   trow.childNodes[j].style.width = ws[j];
+                }
+            }
+            row = ns[i];
+            row.style.width = tw;
+            if(row.firstChild){
+                row.firstChild.style.width = tw;
+                trow = row.firstChild.rows[0];
+                for (j = llen; j < clen; j++) {
+                   trow.childNodes[j - llen].style.width = ws[j];
+                }
+            }
+        }
+        this.onAllColumnWidthsUpdated(ws, tw);
+        this.syncHeaderHeight();
+    },
+    
+    updateColumnWidth : function(col, width){
+        var w = this.getColumnWidth(col),
+            llen = this.cm.getLockedCount(),
+            ns, rw, c, row;
+        this.updateLockedWidth();
+        if(col < llen){
+            ns = this.getLockedRows();
+            rw = this.getLockedWidth();
+            c = col;
+        }else{
+            ns = this.getRows();
+            rw = this.getTotalWidth();
+            c = col - llen;
+        }
+        var hd = this.getHeaderCell(col);
+        hd.style.width = w;
+        for(var i = 0, len = ns.length; i < len; i++){
+            row = ns[i];
+            row.style.width = rw;
+            if(row.firstChild){
+                row.firstChild.style.width = rw;
+                row.firstChild.rows[0].childNodes[c].style.width = w;
+            }
+        }
+        this.onColumnWidthUpdated(col, w, this.getTotalWidth());
+        this.syncHeaderHeight();
+    },
+    
+    updateColumnHidden : function(col, hidden){
+        var llen = this.cm.getLockedCount(),
+            ns, rw, c, row,
+            display = hidden ? 'none' : '';
+        this.updateLockedWidth();
+        if(col < llen){
+            ns = this.getLockedRows();
+            rw = this.getLockedWidth();
+            c = col;
+        }else{
+            ns = this.getRows();
+            rw = this.getTotalWidth();
+            c = col - llen;
+        }
+        var hd = this.getHeaderCell(col);
+        hd.style.display = display;
+        for(var i = 0, len = ns.length; i < len; i++){
+            row = ns[i];
+            row.style.width = rw;
+            if(row.firstChild){
+                row.firstChild.style.width = rw;
+                row.firstChild.rows[0].childNodes[c].style.display = display;
+            }
+        }
+        this.onColumnHiddenUpdated(col, hidden, this.getTotalWidth());
+        delete this.lastViewWidth;
+        this.layout();
+    },
+    
+    doRender : function(cs, rs, ds, startRow, colCount, stripe){
+        var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount-1,
+            tstyle = 'width:'+this.getTotalWidth()+';',
+            lstyle = 'width:'+this.getLockedWidth()+';',
+            buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r;
+        for(var j = 0, len = rs.length; j < len; j++){
+            r = rs[j]; cb = []; lcb = [];
+            var rowIndex = (j+startRow);
+            for(var i = 0; i < colCount; i++){
+                c = cs[i];
+                p.id = c.id;
+                p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +
+                    (this.cm.config[i].cellCls ? ' ' + this.cm.config[i].cellCls : '');
+                p.attr = p.cellAttr = '';
+                p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
+                p.style = c.style;
+                if(Ext.isEmpty(p.value)){
+                    p.value = '&#160;';
+                }
+                if(this.markDirty && r.dirty && Ext.isDefined(r.modified[c.name])){
+                    p.css += ' x-grid3-dirty-cell';
+                }
+                if(c.locked){
+                    lcb[lcb.length] = ct.apply(p);
+                }else{
+                    cb[cb.length] = ct.apply(p);
+                }
+            }
+            var alt = [];
+            if(stripe && ((rowIndex+1) % 2 === 0)){
+                alt[0] = 'x-grid3-row-alt';
+            }
+            if(r.dirty){
+                alt[1] = ' x-grid3-dirty-row';
+            }
+            rp.cols = colCount;
+            if(this.getRowClass){
+                alt[2] = this.getRowClass(r, rowIndex, rp, ds);
+            }
+            rp.alt = alt.join(' ');
+            rp.cells = cb.join('');
+            rp.tstyle = tstyle;
+            buf[buf.length] = rt.apply(rp);
+            rp.cells = lcb.join('');
+            rp.tstyle = lstyle;
+            lbuf[lbuf.length] = rt.apply(rp);
+        }
+        return [buf.join(''), lbuf.join('')];
+    },
+    processRows : function(startRow, skipStripe){
+        if(!this.ds || this.ds.getCount() < 1){
+            return;
+        }
+        var rows = this.getRows(),
+            lrows = this.getLockedRows(),
+            row, lrow;
+        skipStripe = skipStripe || !this.grid.stripeRows;
+        startRow = startRow || 0;
+        for(var i = 0, len = rows.length; i < len; ++i){
+            row = rows[i];
+            lrow = lrows[i];
+            row.rowIndex = i;
+            lrow.rowIndex = i;
+            if(!skipStripe){
+                row.className = row.className.replace(this.rowClsRe, ' ');
+                lrow.className = lrow.className.replace(this.rowClsRe, ' ');
+                if ((i + 1) % 2 === 0){
+                    row.className += ' x-grid3-row-alt';
+                    lrow.className += ' x-grid3-row-alt';
+                }
+            }
+            if(this.syncHeights){
+                var el1 = Ext.get(row),
+                    el2 = Ext.get(lrow),
+                    h1 = el1.getHeight(),
+                    h2 = el2.getHeight();
+                
+                if(h1 > h2){
+                    el2.setHeight(h1);    
+                }else if(h2 > h1){
+                    el1.setHeight(h2);
+                }
+            }
+        }
+        if(startRow === 0){
+            Ext.fly(rows[0]).addClass(this.firstRowCls);
+            Ext.fly(lrows[0]).addClass(this.firstRowCls);
+        }
+        Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls);
+        Ext.fly(lrows[lrows.length - 1]).addClass(this.lastRowCls);
+    },
+    
+    afterRender : function(){
+        if(!this.ds || !this.cm){
+            return;
+        }
+        var bd = this.renderRows() || ['&#160;', '&#160;'];
+        this.mainBody.dom.innerHTML = bd[0];
+        this.lockedBody.dom.innerHTML = bd[1];
+        this.processRows(0, true);
+        if(this.deferEmptyText !== true){
+            this.applyEmptyText();
+        }
+    },
+    
+    renderUI : function(){
+        var header = this.renderHeaders();
+        var body = this.templates.body.apply({rows:'&#160;'});
+        var html = this.templates.master.apply({
+            body: body,
+            header: header[0],
+            ostyle: 'width:'+this.getOffsetWidth()+';',
+            bstyle: 'width:'+this.getTotalWidth()+';',
+            lockedBody: body,
+            lockedHeader: header[1],
+            lstyle: 'width:'+this.getLockedWidth()+';'
+        });
+        var g = this.grid;
+        g.getGridEl().dom.innerHTML = html;
+        this.initElements();
+        Ext.fly(this.innerHd).on('click', this.handleHdDown, this);
+        Ext.fly(this.lockedInnerHd).on('click', this.handleHdDown, this);
+        this.mainHd.on({
+            scope: this,
+            mouseover: this.handleHdOver,
+            mouseout: this.handleHdOut,
+            mousemove: this.handleHdMove
+        });
+        this.lockedHd.on({
+            scope: this,
+            mouseover: this.handleHdOver,
+            mouseout: this.handleHdOut,
+            mousemove: this.handleHdMove
+        });
+        this.scroller.on('scroll', this.syncScroll,  this);
+        if(g.enableColumnResize !== false){
+            this.splitZone = new Ext.grid.GridView.SplitDragZone(g, this.mainHd.dom);
+            this.splitZone.setOuterHandleElId(Ext.id(this.lockedHd.dom));
+            this.splitZone.setOuterHandleElId(Ext.id(this.mainHd.dom));
+        }
+        if(g.enableColumnMove){
+            this.columnDrag = new Ext.grid.GridView.ColumnDragZone(g, this.innerHd);
+            this.columnDrag.setOuterHandleElId(Ext.id(this.lockedInnerHd));
+            this.columnDrag.setOuterHandleElId(Ext.id(this.innerHd));
+            this.columnDrop = new Ext.grid.HeaderDropZone(g, this.mainHd.dom);
+        }
+        if(g.enableHdMenu !== false){
+            this.hmenu = new Ext.menu.Menu({id: g.id + '-hctx'});
+            this.hmenu.add(
+                {itemId: 'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'},
+                {itemId: 'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'}
+            );
+            if(this.grid.enableColLock !== false){
+                this.hmenu.add('-',
+                    {itemId: 'lock', text: this.lockText, cls: 'xg-hmenu-lock'},
+                    {itemId: 'unlock', text: this.unlockText, cls: 'xg-hmenu-unlock'}
+                );
+            }
+            if(g.enableColumnHide !== false){
+                this.colMenu = new Ext.menu.Menu({id:g.id + '-hcols-menu'});
+                this.colMenu.on({
+                    scope: this,
+                    beforeshow: this.beforeColMenuShow,
+                    itemclick: this.handleHdMenuClick
+                });
+                this.hmenu.add('-', {
+                    itemId:'columns',
+                    hideOnClick: false,
+                    text: this.columnsText,
+                    menu: this.colMenu,
+                    iconCls: 'x-cols-icon'
+                });
+            }
+            this.hmenu.on('itemclick', this.handleHdMenuClick, this);
+        }
+        if(g.trackMouseOver){
+            this.mainBody.on({
+                scope: this,
+                mouseover: this.onRowOver,
+                mouseout: this.onRowOut
+            });
+            this.lockedBody.on({
+                scope: this,
+                mouseover: this.onRowOver,
+                mouseout: this.onRowOut
+            });
+        }
+        
+        if(g.enableDragDrop || g.enableDrag){
+            this.dragZone = new Ext.grid.GridDragZone(g, {
+                ddGroup : g.ddGroup || 'GridDD'
+            });
+        }
+        this.updateHeaderSortState();
+    },
+    
+    layout : function(){
+        if(!this.mainBody){
+            return;
+        }
+        var g = this.grid;
+        var c = g.getGridEl();
+        var csize = c.getSize(true);
+        var vw = csize.width;
+        if(!g.hideHeaders && (vw < 20 || csize.height < 20)){
+            return;
+        }
+        this.syncHeaderHeight();
+        if(g.autoHeight){
+            this.scroller.dom.style.overflow = 'visible';
+            this.lockedScroller.dom.style.overflow = 'visible';
+            if(Ext.isWebKit){
+                this.scroller.dom.style.position = 'static';
+                this.lockedScroller.dom.style.position = 'static';
+            }
+        }else{
+            this.el.setSize(csize.width, csize.height);
+            var hdHeight = this.mainHd.getHeight();
+            var vh = csize.height - (hdHeight);
+        }
+        this.updateLockedWidth();
+        if(this.forceFit){
+            if(this.lastViewWidth != vw){
+                this.fitColumns(false, false);
+                this.lastViewWidth = vw;
+            }
+        }else {
+            this.autoExpand();
+            this.syncHeaderScroll();
+        }
+        this.onLayout(vw, vh);
+    },
+    
+    getOffsetWidth : function() {
+        return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth() + this.getScrollOffset()) + 'px';
+    },
+    
+    renderHeaders : function(){
+        var cm = this.cm,
+            ts = this.templates,
+            ct = ts.hcell,
+            cb = [], lcb = [],
+            p = {},
+            len = cm.getColumnCount(),
+            last = len - 1;
+        for(var i = 0; i < len; i++){
+            p.id = cm.getColumnId(i);
+            p.value = cm.getColumnHeader(i) || '';
+            p.style = this.getColumnStyle(i, true);
+            p.tooltip = this.getColumnTooltip(i);
+            p.css = (i === 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : '')) +
+                (cm.config[i].headerCls ? ' ' + cm.config[i].headerCls : '');
+            if(cm.config[i].align == 'right'){
+                p.istyle = 'padding-right:16px';
+            } else {
+                delete p.istyle;
+            }
+            if(cm.isLocked(i)){
+                lcb[lcb.length] = ct.apply(p);
+            }else{
+                cb[cb.length] = ct.apply(p);
+            }
+        }
+        return [ts.header.apply({cells: cb.join(''), tstyle:'width:'+this.getTotalWidth()+';'}),
+                ts.header.apply({cells: lcb.join(''), tstyle:'width:'+this.getLockedWidth()+';'})];
+    },
+    
+    updateHeaders : function(){
+        var hd = this.renderHeaders();
+        this.innerHd.firstChild.innerHTML = hd[0];
+        this.innerHd.firstChild.style.width = this.getOffsetWidth();
+        this.innerHd.firstChild.firstChild.style.width = this.getTotalWidth();
+        this.lockedInnerHd.firstChild.innerHTML = hd[1];
+        var lw = this.getLockedWidth();
+        this.lockedInnerHd.firstChild.style.width = lw;
+        this.lockedInnerHd.firstChild.firstChild.style.width = lw;
+    },
+    
+    getResolvedXY : function(resolved){
+        if(!resolved){
+            return null;
+        }
+        var c = resolved.cell, r = resolved.row;
+        return c ? Ext.fly(c).getXY() : [this.scroller.getX(), Ext.fly(r).getY()];
+    },
+    
+    syncFocusEl : function(row, col, hscroll){
+        Ext.ux.grid.LockingGridView.superclass.syncFocusEl.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);
+    },
+    
+    ensureVisible : function(row, col, hscroll){
+        return Ext.ux.grid.LockingGridView.superclass.ensureVisible.call(this, row, col, col < this.cm.getLockedCount() ? false : hscroll);
+    },
+    
+    insertRows : function(dm, firstRow, lastRow, isUpdate){
+        var last = dm.getCount() - 1;
+        if(!isUpdate && firstRow === 0 && lastRow >= last){
+            this.refresh();
+        }else{
+            if(!isUpdate){
+                this.fireEvent('beforerowsinserted', this, firstRow, lastRow);
+            }
+            var html = this.renderRows(firstRow, lastRow),
+                before = this.getRow(firstRow);
+            if(before){
+                if(firstRow === 0){
+                    this.removeRowClass(0, this.firstRowCls);
+                }
+                Ext.DomHelper.insertHtml('beforeBegin', before, html[0]);
+                before = this.getLockedRow(firstRow);
+                Ext.DomHelper.insertHtml('beforeBegin', before, html[1]);
+            }else{
+                this.removeRowClass(last - 1, this.lastRowCls);
+                Ext.DomHelper.insertHtml('beforeEnd', this.mainBody.dom, html[0]);
+                Ext.DomHelper.insertHtml('beforeEnd', this.lockedBody.dom, html[1]);
+            }
+            if(!isUpdate){
+                this.fireEvent('rowsinserted', this, firstRow, lastRow);
+                this.processRows(firstRow);
+            }else if(firstRow === 0 || firstRow >= last){
+                this.addRowClass(firstRow, firstRow === 0 ? this.firstRowCls : this.lastRowCls);
+            }
+        }
+        this.syncFocusEl(firstRow);
+    },
+    
+    getColumnStyle : function(col, isHeader){
+        var style = !isHeader ? this.cm.config[col].cellStyle || this.cm.config[col].css || '' : this.cm.config[col].headerStyle || '';
+        style += 'width:'+this.getColumnWidth(col)+';';
+        if(this.cm.isHidden(col)){
+            style += 'display:none;';
+        }
+        var align = this.cm.config[col].align;
+        if(align){
+            style += 'text-align:'+align+';';
+        }
+        return style;
+    },
+    
+    getLockedWidth : function() {
+        return this.cm.getTotalLockedWidth() + 'px';
+    },
+    
+    getTotalWidth : function() {
+        return (this.cm.getTotalWidth() - this.cm.getTotalLockedWidth()) + 'px';
+    },
+    
+    getColumnData : function(){
+        var cs = [], cm = this.cm, colCount = cm.getColumnCount();
+        for(var i = 0; i < colCount; i++){
+            var name = cm.getDataIndex(i);
+            cs[i] = {
+                name : (!Ext.isDefined(name) ? this.ds.fields.get(i).name : name),
+                renderer : cm.getRenderer(i),
+                id : cm.getColumnId(i),
+                style : this.getColumnStyle(i),
+                locked : cm.isLocked(i)
+            };
+        }
+        return cs;
+    },
+    
+    renderBody : function(){
+        var markup = this.renderRows() || ['&#160;', '&#160;'];
+        return [this.templates.body.apply({rows: markup[0]}), this.templates.body.apply({rows: markup[1]})];
+    },
+    
+    refreshRow : function(record){
+        Ext.ux.grid.LockingGridView.superclass.refreshRow.call(this, record);
+        var index = Ext.isNumber(record) ? record : this.ds.indexOf(record);
+        this.getLockedRow(index).rowIndex = index;
+    },
+    
+    refresh : function(headersToo){
+        this.fireEvent('beforerefresh', this);
+        this.grid.stopEditing(true);
+        var result = this.renderBody();
+        this.mainBody.update(result[0]).setWidth(this.getTotalWidth());
+        this.lockedBody.update(result[1]).setWidth(this.getLockedWidth());
+        if(headersToo === true){
+            this.updateHeaders();
+            this.updateHeaderSortState();
+        }
+        this.processRows(0, true);
+        this.layout();
+        this.applyEmptyText();
+        this.fireEvent('refresh', this);
+    },
+    
+    onDenyColumnLock : function(){
+
+    },
+    
+    initData : function(ds, cm){
+        if(this.cm){
+            this.cm.un('columnlockchange', this.onColumnLock, this);
+        }
+        Ext.ux.grid.LockingGridView.superclass.initData.call(this, ds, cm);
+        if(this.cm){
+            this.cm.on('columnlockchange', this.onColumnLock, this);
+        }
+    },
+    
+    onColumnLock : function(){
+        this.refresh(true);
+    },
+    
+    handleHdMenuClick : function(item){
+        var index = this.hdCtxIndex,
+            cm = this.cm,
+            id = item.getItemId(),
+            llen = cm.getLockedCount();
+        switch(id){
+            case 'lock':
+                if(cm.getColumnCount(true) <= llen + 1){
+                    this.onDenyColumnLock();
+                    return;
+                }
+                if(llen != index){
+                    cm.setLocked(index, true, true);
+                    cm.moveColumn(index, llen);
+                    this.grid.fireEvent('columnmove', index, llen);
+                }else{
+                    cm.setLocked(index, true);
+                }
+            break;
+            case 'unlock':
+                if(llen - 1 != index){
+                    cm.setLocked(index, false, true);
+                    cm.moveColumn(index, llen - 1);
+                    this.grid.fireEvent('columnmove', index, llen - 1);
+                }else{
+                    cm.setLocked(index, false);
+                }
+            break;
+            default:
+                return Ext.ux.grid.LockingGridView.superclass.handleHdMenuClick.call(this, item);
+        }
+        return true;
+    },
+    
+    handleHdDown : function(e, t){
+        Ext.ux.grid.LockingGridView.superclass.handleHdDown.call(this, e, t);
+        if(this.grid.enableColLock !== false){
+            if(Ext.fly(t).hasClass('x-grid3-hd-btn')){
+                var hd = this.findHeaderCell(t),
+                    index = this.getCellIndex(hd),
+                    ms = this.hmenu.items, cm = this.cm;
+                ms.get('lock').setDisabled(cm.isLocked(index));
+                ms.get('unlock').setDisabled(!cm.isLocked(index));
+            }
+        }
+    },
+    
+    syncHeaderHeight: function(){
+        this.innerHd.firstChild.firstChild.style.height = 'auto';
+        this.lockedInnerHd.firstChild.firstChild.style.height = 'auto';
+        var hd = this.innerHd.firstChild.firstChild.offsetHeight,
+            lhd = this.lockedInnerHd.firstChild.firstChild.offsetHeight,
+            height = (lhd > hd ? lhd : hd) + 'px';
+        this.innerHd.firstChild.firstChild.style.height = height;
+        this.lockedInnerHd.firstChild.firstChild.style.height = height;
+    },
+    
+    updateLockedWidth: function(){
+        var lw = this.cm.getTotalLockedWidth(),
+            tw = this.cm.getTotalWidth() - lw,
+            csize = this.grid.getGridEl().getSize(true),
+            lp = Ext.isBorderBox ? 0 : this.lockedBorderWidth,
+            rp = Ext.isBorderBox ? 0 : this.rowBorderWidth,
+            vw = (csize.width - lw - lp - rp) + 'px',
+            so = this.getScrollOffset();
+        if(!this.grid.autoHeight){
+            var vh = (csize.height - this.mainHd.getHeight()) + 'px';
+            this.lockedScroller.dom.style.height = vh;
+            this.scroller.dom.style.height = vh;
+        }
+        this.lockedWrap.dom.style.width = (lw + rp) + 'px';
+        this.scroller.dom.style.width = vw;
+        this.mainWrap.dom.style.left = (lw + lp + rp) + 'px';
+        if(this.innerHd){
+            this.lockedInnerHd.firstChild.style.width = lw + 'px';
+            this.lockedInnerHd.firstChild.firstChild.style.width = lw + 'px';
+            this.innerHd.style.width = vw;
+            this.innerHd.firstChild.style.width = (tw + rp + so) + 'px';
+            this.innerHd.firstChild.firstChild.style.width = tw + 'px';
+        }
+        if(this.mainBody){
+            this.lockedBody.dom.style.width = (lw + rp) + 'px';
+            this.mainBody.dom.style.width = (tw + rp) + 'px';
+        }
+    }
+});
+
+Ext.ux.grid.LockingColumnModel = Ext.extend(Ext.grid.ColumnModel, {
+    /**
+     * Returns true if the given column index is currently locked
+     * @param {Number} colIndex The column index
+     * @return {Boolean} True if the column is locked
+     */
+    isLocked : function(colIndex){
+        return this.config[colIndex].locked === true;
+    },
+    
+    /**
+     * Locks or unlocks a given column
+     * @param {Number} colIndex The column index
+     * @param {Boolean} value True to lock, false to unlock
+     * @param {Boolean} suppressEvent Pass false to cause the columnlockchange event not to fire
+     */
+    setLocked : function(colIndex, value, suppressEvent){
+        if (this.isLocked(colIndex) == value) {
+            return;
+        }
+        this.config[colIndex].locked = value;
+        if (!suppressEvent) {
+            this.fireEvent('columnlockchange', this, colIndex, value);
+        }
+    },
+    
+    /**
+     * Returns the total width of all locked columns
+     * @return {Number} The width of all locked columns
+     */
+    getTotalLockedWidth : function(){
+        var totalWidth = 0;
+        for (var i = 0, len = this.config.length; i < len; i++) {
+            if (this.isLocked(i) && !this.isHidden(i)) {
+                totalWidth += this.getColumnWidth(i);
+            }
+        }
+        
+        return totalWidth;
+    },
+    
+    /**
+     * Returns the total number of locked columns
+     * @return {Number} The number of locked columns
+     */
+    getLockedCount : function() {
+        var len = this.config.length;
+        
+        for (var i = 0; i < len; i++) {
+            if (!this.isLocked(i)) {
+                return i;
+            }
+        }
+        
+        //if we get to this point all of the columns are locked so we return the total
+        return len;
+    },
+    
+    /**
+     * Moves a column from one position to another
+     * @param {Number} oldIndex The current column index
+     * @param {Number} newIndex The destination column index
+     */
+    moveColumn : function(oldIndex, newIndex){
+        var oldLocked = this.isLocked(oldIndex),
+            newLocked = this.isLocked(newIndex);
+        
+        if (oldIndex < newIndex && oldLocked && !newLocked) {
+            this.setLocked(oldIndex, false, true);
+        } else if (oldIndex > newIndex && !oldLocked && newLocked) {
+            this.setLocked(oldIndex, true, true);
+        }
+        
+        Ext.ux.grid.LockingColumnModel.superclass.moveColumn.apply(this, arguments);
+    }
+});