X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/examples/ux/ux-all-debug.js?ds=inline diff --git a/examples/ux/ux-all-debug.js b/examples/ux/ux-all-debug.js deleted file mode 100644 index 8f957b8f..00000000 --- a/examples/ux/ux-all-debug.js +++ /dev/null @@ -1,10604 +0,0 @@ -/*! - * Ext JS Library 3.3.1 - * Copyright(c) 2006-2010 Sencha Inc. - * licensing@sencha.com - * http://www.sencha.com/license - */ -Ext.ns('Ext.ux.grid'); - -/** - * @class Ext.ux.grid.BufferView - * @extends Ext.grid.GridView - * A custom GridView which renders rows on an as-needed basis. - */ -Ext.ux.grid.BufferView = Ext.extend(Ext.grid.GridView, { - /** - * @cfg {Number} rowHeight - * The height of a row in the grid. - */ - rowHeight: 19, - - /** - * @cfg {Number} borderHeight - * The combined height of border-top and border-bottom of a row. - */ - borderHeight: 2, - - /** - * @cfg {Boolean/Number} scrollDelay - * The number of milliseconds before rendering rows out of the visible - * viewing area. Defaults to 100. Rows will render immediately with a config - * of false. - */ - scrollDelay: 100, - - /** - * @cfg {Number} cacheSize - * The number of rows to look forward and backwards from the currently viewable - * area. The cache applies only to rows that have been rendered already. - */ - cacheSize: 20, - - /** - * @cfg {Number} cleanDelay - * The number of milliseconds to buffer cleaning of extra rows not in the - * cache. - */ - cleanDelay: 500, - - initTemplates : function(){ - Ext.ux.grid.BufferView.superclass.initTemplates.call(this); - var ts = this.templates; - // empty div to act as a place holder for a row - ts.rowHolder = new Ext.Template( - '
' - ); - ts.rowHolder.disableFormats = true; - ts.rowHolder.compile(); - - ts.rowBody = new Ext.Template( - '', - '{cells}', - (this.enableRowBody ? '' : ''), - '
{body}
' - ); - ts.rowBody.disableFormats = true; - ts.rowBody.compile(); - }, - - getStyleRowHeight : function(){ - return Ext.isBorderBox ? (this.rowHeight + this.borderHeight) : this.rowHeight; - }, - - getCalculatedRowHeight : function(){ - return this.rowHeight + this.borderHeight; - }, - - getVisibleRowCount : function(){ - var rh = this.getCalculatedRowHeight(), - visibleHeight = this.scroller.dom.clientHeight; - return (visibleHeight < 1) ? 0 : Math.ceil(visibleHeight / rh); - }, - - getVisibleRows: function(){ - var count = this.getVisibleRowCount(), - sc = this.scroller.dom.scrollTop, - start = (sc === 0 ? 0 : Math.floor(sc/this.getCalculatedRowHeight())-1); - return { - first: Math.max(start, 0), - last: Math.min(start + count + 2, this.ds.getCount()-1) - }; - }, - - doRender : function(cs, rs, ds, startRow, colCount, stripe, onlyBody){ - var ts = this.templates, - ct = ts.cell, - rt = ts.row, - rb = ts.rowBody, - last = colCount-1, - rh = this.getStyleRowHeight(), - vr = this.getVisibleRows(), - tstyle = 'width:'+this.getTotalWidth()+';height:'+rh+'px;', - // buffers - buf = [], - cb, - c, - p = {}, - rp = {tstyle: tstyle}, - r; - for (var j = 0, len = rs.length; j < len; j++) { - r = rs[j]; cb = []; - var rowIndex = (j+startRow), - visible = rowIndex >= vr.first && rowIndex <= vr.last; - if (visible) { - 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 ' : ''); - p.attr = p.cellAttr = ""; - p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds); - p.style = c.style; - if (p.value === undefined || p.value === "") { - p.value = " "; - } - if (r.dirty && typeof r.modified[c.name] !== 'undefined') { - p.css += ' x-grid3-dirty-cell'; - } - 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(""); - buf[buf.length] = !visible ? ts.rowHolder.apply(rp) : (onlyBody ? rb.apply(rp) : rt.apply(rp)); - } - return buf.join(""); - }, - - isRowRendered: function(index){ - var row = this.getRow(index); - return row && row.childNodes.length > 0; - }, - - syncScroll: function(){ - Ext.ux.grid.BufferView.superclass.syncScroll.apply(this, arguments); - this.update(); - }, - - // a (optionally) buffered method to update contents of gridview - update: function(){ - if (this.scrollDelay) { - if (!this.renderTask) { - this.renderTask = new Ext.util.DelayedTask(this.doUpdate, this); - } - this.renderTask.delay(this.scrollDelay); - }else{ - this.doUpdate(); - } - }, - - onRemove : function(ds, record, index, isUpdate){ - Ext.ux.grid.BufferView.superclass.onRemove.apply(this, arguments); - if(isUpdate !== true){ - this.update(); - } - }, - - doUpdate: function(){ - if (this.getVisibleRowCount() > 0) { - var g = this.grid, - cm = g.colModel, - ds = g.store, - cs = this.getColumnData(), - vr = this.getVisibleRows(), - row; - for (var i = vr.first; i <= vr.last; i++) { - // if row is NOT rendered and is visible, render it - if(!this.isRowRendered(i) && (row = this.getRow(i))){ - var html = this.doRender(cs, [ds.getAt(i)], ds, i, cm.getColumnCount(), g.stripeRows, true); - row.innerHTML = html; - } - } - this.clean(); - } - }, - - // a buffered method to clean rows - clean : function(){ - if(!this.cleanTask){ - this.cleanTask = new Ext.util.DelayedTask(this.doClean, this); - } - this.cleanTask.delay(this.cleanDelay); - }, - - doClean: function(){ - if (this.getVisibleRowCount() > 0) { - var vr = this.getVisibleRows(); - vr.first -= this.cacheSize; - vr.last += this.cacheSize; - - var i = 0, rows = this.getRows(); - // if first is less than 0, all rows have been rendered - // so lets clean the end... - if(vr.first <= 0){ - i = vr.last + 1; - } - for(var len = this.ds.getCount(); i < len; i++){ - // if current row is outside of first and last and - // has content, update the innerHTML to nothing - if ((i < vr.first || i > vr.last) && rows[i].innerHTML) { - rows[i].innerHTML = ''; - } - } - } - }, - - removeTask: function(name){ - var task = this[name]; - if(task && task.cancel){ - task.cancel(); - this[name] = null; - } - }, - - destroy : function(){ - this.removeTask('cleanTask'); - this.removeTask('renderTask'); - Ext.ux.grid.BufferView.superclass.destroy.call(this); - }, - - layout: function(){ - Ext.ux.grid.BufferView.superclass.layout.call(this); - this.update(); - } -});// We are adding these custom layouts to a namespace that does not -// exist by default in Ext, so we have to add the namespace first: -Ext.ns('Ext.ux.layout'); - -/** - * @class Ext.ux.layout.CenterLayout - * @extends Ext.layout.FitLayout - *

This is a very simple layout style used to center contents within a container. This layout works within - * nested containers and can also be used as expected as a Viewport layout to center the page layout.

- *

As a subclass of FitLayout, CenterLayout expects to have a single child panel of the container that uses - * the layout. The layout does not require any config options, although the child panel contained within the - * layout must provide a fixed or percentage width. The child panel's height will fit to the container by - * default, but you can specify autoHeight:true to allow it to autosize based on its content height. - * Example usage:

- *

-// The content panel is centered in the container
-var p = new Ext.Panel({
-    title: 'Center Layout',
-    layout: 'ux.center',
-    items: [{
-        title: 'Centered Content',
-        width: '75%',
-        html: 'Some content'
-    }]
-});
-
-// If you leave the title blank and specify no border
-// you'll create a non-visual, structural panel just
-// for centering the contents in the main container.
-var p = new Ext.Panel({
-    layout: 'ux.center',
-    border: false,
-    items: [{
-        title: 'Centered Content',
-        width: 300,
-        autoHeight: true,
-        html: 'Some content'
-    }]
-});
-
- */ -Ext.ux.layout.CenterLayout = Ext.extend(Ext.layout.FitLayout, { - // private - setItemSize : function(item, size){ - this.container.addClass('ux-layout-center'); - item.addClass('ux-layout-center-item'); - if(item && size.height > 0){ - if(item.width){ - size.width = item.width; - } - item.setSize(size); - } - } -}); - -Ext.Container.LAYOUTS['ux.center'] = Ext.ux.layout.CenterLayout; -Ext.ns('Ext.ux.grid'); - -/** - * @class Ext.ux.grid.CheckColumn - * @extends Ext.grid.Column - *

A Column subclass which renders a checkbox in each column cell which toggles the truthiness of the associated data field on click.

- *

Note. As of ExtJS 3.3 this no longer has to be configured as a plugin of the GridPanel.

- *

Example usage:

- *

-var cm = new Ext.grid.ColumnModel([{
-       header: 'Foo',
-       ...
-    },{
-       xtype: 'checkcolumn',
-       header: 'Indoor?',
-       dataIndex: 'indoor',
-       width: 55
-    }
-]);
-
-// create the grid
-var grid = new Ext.grid.EditorGridPanel({
-    ...
-    colModel: cm,
-    ...
-});
- * 
- * In addition to toggling a Boolean value within the record data, this - * class toggles a css class between 'x-grid3-check-col' and - * 'x-grid3-check-col-on' to alter the background image used for - * a column. - */ -Ext.ux.grid.CheckColumn = Ext.extend(Ext.grid.Column, { - - /** - * @private - * Process and refire events routed from the GridView's processEvent method. - */ - processEvent : function(name, e, grid, rowIndex, colIndex){ - if (name == 'mousedown') { - var record = grid.store.getAt(rowIndex); - record.set(this.dataIndex, !record.data[this.dataIndex]); - return false; // Cancel row selection. - } else { - return Ext.grid.ActionColumn.superclass.processEvent.apply(this, arguments); - } - }, - - renderer : function(v, p, record){ - p.css += ' x-grid3-check-col-td'; - return String.format('
 
', v ? '-on' : ''); - }, - - // Deprecate use as a plugin. Remove in 4.0 - init: Ext.emptyFn -}); - -// register ptype. Deprecate. Remove in 4.0 -Ext.preg('checkcolumn', Ext.ux.grid.CheckColumn); - -// backwards compat. Remove in 4.0 -Ext.grid.CheckColumn = Ext.ux.grid.CheckColumn; - -// register Column xtype -Ext.grid.Column.types.checkcolumn = Ext.ux.grid.CheckColumn;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('', '
', this.grid.enableHdMenu ? '' : '', '{value}
'); - } - 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 || ' ' - }); - 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, 6) == '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 if(id.substr(0, 4) == 'col-'){ - 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); - } - } - if(id.substr(0, 6) == 'group-' || id.substr(0, 4) == 'col-'){ - 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, - item = menu.items ? menu.getComponent(gid) : null, - 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 - })); - } - } - }, - - afterRenderUI: function(){ - this.constructor.prototype.afterRenderUI.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 - }; - } -});Ext.ns('Ext.ux.tree'); - -/** - * @class Ext.ux.tree.ColumnTree - * @extends Ext.tree.TreePanel - * - * @xtype columntree - */ -Ext.ux.tree.ColumnTree = Ext.extend(Ext.tree.TreePanel, { - lines : false, - borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell - cls : 'x-column-tree', - - onRender : function(){ - Ext.tree.ColumnTree.superclass.onRender.apply(this, arguments); - this.headers = this.header.createChild({cls:'x-tree-headers'}); - - var cols = this.columns, c; - var totalWidth = 0; - var scrollOffset = 19; // similar to Ext.grid.GridView default - - for(var i = 0, len = cols.length; i < len; i++){ - c = cols[i]; - totalWidth += c.width; - this.headers.createChild({ - cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''), - cn: { - cls:'x-tree-hd-text', - html: c.header - }, - style:'width:'+(c.width-this.borderWidth)+'px;' - }); - } - this.headers.createChild({cls:'x-clear'}); - // prevent floats from wrapping when clipped - this.headers.setWidth(totalWidth+scrollOffset); - this.innerCt.setWidth(totalWidth); - } -}); - -Ext.reg('columntree', Ext.ux.tree.ColumnTree); - -//backwards compat -Ext.tree.ColumnTree = Ext.ux.tree.ColumnTree; - - -/** - * @class Ext.ux.tree.ColumnNodeUI - * @extends Ext.tree.TreeNodeUI - */ -Ext.ux.tree.ColumnNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { - focus: Ext.emptyFn, // prevent odd scrolling behavior - - renderElements : function(n, a, targetNode, bulkRender){ - this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : ''; - - var t = n.getOwnerTree(); - var cols = t.columns; - var bw = t.borderWidth; - var c = cols[0]; - - var buf = [ - '
  • ', - '"]; - for(var i = 1, len = cols.length; i < len; i++){ - c = cols[i]; - - buf.push('
    ', - '
    ',(c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]),"
    ", - "
    "); - } - buf.push( - '
    ', - '', - "
  • "); - - if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){ - this.wrap = Ext.DomHelper.insertHtml("beforeBegin", - n.nextSibling.ui.getEl(), buf.join("")); - }else{ - this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join("")); - } - - this.elNode = this.wrap.childNodes[0]; - this.ctNode = this.wrap.childNodes[1]; - var cs = this.elNode.firstChild.childNodes; - this.indentNode = cs[0]; - this.ecNode = cs[1]; - this.iconNode = cs[2]; - this.anchor = cs[3]; - this.textNode = cs[3].firstChild; - } -}); - -//backwards compat -Ext.tree.ColumnNodeUI = Ext.ux.tree.ColumnNodeUI; -/** - * @class Ext.DataView.LabelEditor - * @extends Ext.Editor - * - */ -Ext.DataView.LabelEditor = Ext.extend(Ext.Editor, { - alignment: "tl-tl", - hideEl : false, - cls: "x-small-editor", - shim: false, - completeOnEnter: true, - cancelOnEsc: true, - labelSelector: 'span.x-editable', - - constructor: function(cfg, field){ - Ext.DataView.LabelEditor.superclass.constructor.call(this, - field || new Ext.form.TextField({ - allowBlank: false, - growMin:90, - growMax:240, - grow:true, - selectOnFocus:true - }), cfg - ); - }, - - init : function(view){ - this.view = view; - view.on('render', this.initEditor, this); - this.on('complete', this.onSave, this); - }, - - initEditor : function(){ - this.view.on({ - scope: this, - containerclick: this.doBlur, - click: this.doBlur - }); - this.view.getEl().on('mousedown', this.onMouseDown, this, {delegate: this.labelSelector}); - }, - - doBlur: function(){ - if(this.editing){ - this.field.blur(); - } - }, - - onMouseDown : function(e, target){ - if(!e.ctrlKey && !e.shiftKey){ - var item = this.view.findItemFromChild(target); - e.stopEvent(); - var record = this.view.store.getAt(this.view.indexOf(item)); - this.startEdit(target, record.data[this.dataIndex]); - this.activeRecord = record; - }else{ - e.preventDefault(); - } - }, - - onSave : function(ed, value){ - this.activeRecord.set(this.dataIndex, value); - } -}); - - -Ext.DataView.DragSelector = function(cfg){ - cfg = cfg || {}; - var view, proxy, tracker; - var rs, bodyRegion, dragRegion = new Ext.lib.Region(0,0,0,0); - var dragSafe = cfg.dragSafe === true; - - this.init = function(dataView){ - view = dataView; - view.on('render', onRender); - }; - - function fillRegions(){ - rs = []; - view.all.each(function(el){ - rs[rs.length] = el.getRegion(); - }); - bodyRegion = view.el.getRegion(); - } - - function cancelClick(){ - return false; - } - - function onBeforeStart(e){ - return !dragSafe || e.target == view.el.dom; - } - - function onStart(e){ - view.on('containerclick', cancelClick, view, {single:true}); - if(!proxy){ - proxy = view.el.createChild({cls:'x-view-selector'}); - }else{ - if(proxy.dom.parentNode !== view.el.dom){ - view.el.dom.appendChild(proxy.dom); - } - proxy.setDisplayed('block'); - } - fillRegions(); - view.clearSelections(); - } - - function onDrag(e){ - var startXY = tracker.startXY; - var xy = tracker.getXY(); - - var x = Math.min(startXY[0], xy[0]); - var y = Math.min(startXY[1], xy[1]); - var w = Math.abs(startXY[0] - xy[0]); - var h = Math.abs(startXY[1] - xy[1]); - - dragRegion.left = x; - dragRegion.top = y; - dragRegion.right = x+w; - dragRegion.bottom = y+h; - - dragRegion.constrainTo(bodyRegion); - proxy.setRegion(dragRegion); - - for(var i = 0, len = rs.length; i < len; i++){ - var r = rs[i], sel = dragRegion.intersect(r); - if(sel && !r.selected){ - r.selected = true; - view.select(i, true); - }else if(!sel && r.selected){ - r.selected = false; - view.deselect(i); - } - } - } - - function onEnd(e){ - if (!Ext.isIE) { - view.un('containerclick', cancelClick, view); - } - if(proxy){ - proxy.setDisplayed(false); - } - } - - function onRender(view){ - tracker = new Ext.dd.DragTracker({ - onBeforeStart: onBeforeStart, - onStart: onStart, - onDrag: onDrag, - onEnd: onEnd - }); - tracker.initEl(view.el); - } -};Ext.ns('Ext.ux.form'); - -/** - * @class Ext.ux.form.FileUploadField - * @extends Ext.form.TextField - * Creates a file upload field. - * @xtype fileuploadfield - */ -Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField, { - /** - * @cfg {String} buttonText The button text to display on the upload button (defaults to - * 'Browse...'). Note that if you supply a value for {@link #buttonCfg}, the buttonCfg.text - * value will be used instead if available. - */ - buttonText: 'Browse...', - /** - * @cfg {Boolean} buttonOnly True to display the file upload field as a button with no visible - * text field (defaults to false). If true, all inherited TextField members will still be available. - */ - buttonOnly: false, - /** - * @cfg {Number} buttonOffset The number of pixels of space reserved between the button and the text field - * (defaults to 3). Note that this only applies if {@link #buttonOnly} = false. - */ - buttonOffset: 3, - /** - * @cfg {Object} buttonCfg A standard {@link Ext.Button} config object. - */ - - // private - readOnly: true, - - /** - * @hide - * @method autoSize - */ - autoSize: Ext.emptyFn, - - // private - initComponent: function(){ - Ext.ux.form.FileUploadField.superclass.initComponent.call(this); - - this.addEvents( - /** - * @event fileselected - * Fires when the underlying file input field's value has changed from the user - * selecting a new file from the system file selection dialog. - * @param {Ext.ux.form.FileUploadField} this - * @param {String} value The file value returned by the underlying file input field - */ - 'fileselected' - ); - }, - - // private - onRender : function(ct, position){ - Ext.ux.form.FileUploadField.superclass.onRender.call(this, ct, position); - - this.wrap = this.el.wrap({cls:'x-form-field-wrap x-form-file-wrap'}); - this.el.addClass('x-form-file-text'); - this.el.dom.removeAttribute('name'); - this.createFileInput(); - - var btnCfg = Ext.applyIf(this.buttonCfg || {}, { - text: this.buttonText - }); - this.button = new Ext.Button(Ext.apply(btnCfg, { - renderTo: this.wrap, - cls: 'x-form-file-btn' + (btnCfg.iconCls ? ' x-btn-icon' : '') - })); - - if(this.buttonOnly){ - this.el.hide(); - this.wrap.setWidth(this.button.getEl().getWidth()); - } - - this.bindListeners(); - this.resizeEl = this.positionEl = this.wrap; - }, - - bindListeners: function(){ - this.fileInput.on({ - scope: this, - mouseenter: function() { - this.button.addClass(['x-btn-over','x-btn-focus']) - }, - mouseleave: function(){ - this.button.removeClass(['x-btn-over','x-btn-focus','x-btn-click']) - }, - mousedown: function(){ - this.button.addClass('x-btn-click') - }, - mouseup: function(){ - this.button.removeClass(['x-btn-over','x-btn-focus','x-btn-click']) - }, - change: function(){ - var v = this.fileInput.dom.value; - this.setValue(v); - this.fireEvent('fileselected', this, v); - } - }); - }, - - createFileInput : function() { - this.fileInput = this.wrap.createChild({ - id: this.getFileInputId(), - name: this.name||this.getId(), - cls: 'x-form-file', - tag: 'input', - type: 'file', - size: 1 - }); - }, - - reset : function(){ - this.fileInput.remove(); - this.createFileInput(); - this.bindListeners(); - Ext.ux.form.FileUploadField.superclass.reset.call(this); - }, - - // private - getFileInputId: function(){ - return this.id + '-file'; - }, - - // private - onResize : function(w, h){ - Ext.ux.form.FileUploadField.superclass.onResize.call(this, w, h); - - this.wrap.setWidth(w); - - if(!this.buttonOnly){ - var w = this.wrap.getWidth() - this.button.getEl().getWidth() - this.buttonOffset; - this.el.setWidth(w); - } - }, - - // private - onDestroy: function(){ - Ext.ux.form.FileUploadField.superclass.onDestroy.call(this); - Ext.destroy(this.fileInput, this.button, this.wrap); - }, - - onDisable: function(){ - Ext.ux.form.FileUploadField.superclass.onDisable.call(this); - this.doDisable(true); - }, - - onEnable: function(){ - Ext.ux.form.FileUploadField.superclass.onEnable.call(this); - this.doDisable(false); - - }, - - // private - doDisable: function(disabled){ - this.fileInput.dom.disabled = disabled; - this.button.setDisabled(disabled); - }, - - - // private - preFocus : Ext.emptyFn, - - // private - alignErrorIcon : function(){ - this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]); - } - -}); - -Ext.reg('fileuploadfield', Ext.ux.form.FileUploadField); - -// backwards compat -Ext.form.FileUploadField = Ext.ux.form.FileUploadField; -/** - * @class Ext.ux.GMapPanel - * @extends Ext.Panel - * @author Shea Frederick - */ -Ext.ux.GMapPanel = Ext.extend(Ext.Panel, { - initComponent : function(){ - - var defConfig = { - plain: true, - zoomLevel: 3, - yaw: 180, - pitch: 0, - zoom: 0, - gmapType: 'map', - border: false - }; - - Ext.applyIf(this,defConfig); - - Ext.ux.GMapPanel.superclass.initComponent.call(this); - - }, - afterRender : function(){ - - var wh = this.ownerCt.getSize(); - Ext.applyIf(this, wh); - - Ext.ux.GMapPanel.superclass.afterRender.call(this); - - if (this.gmapType === 'map'){ - this.gmap = new GMap2(this.body.dom); - } - - if (this.gmapType === 'panorama'){ - this.gmap = new GStreetviewPanorama(this.body.dom); - } - - if (typeof this.addControl == 'object' && this.gmapType === 'map') { - this.gmap.addControl(this.addControl); - } - - if (typeof this.setCenter === 'object') { - if (typeof this.setCenter.geoCodeAddr === 'string'){ - this.geoCodeLookup(this.setCenter.geoCodeAddr); - }else{ - if (this.gmapType === 'map'){ - var point = new GLatLng(this.setCenter.lat,this.setCenter.lng); - this.gmap.setCenter(point, this.zoomLevel); - } - if (typeof this.setCenter.marker === 'object' && typeof point === 'object'){ - this.addMarker(point,this.setCenter.marker,this.setCenter.marker.clear); - } - } - if (this.gmapType === 'panorama'){ - this.gmap.setLocationAndPOV(new GLatLng(this.setCenter.lat,this.setCenter.lng), {yaw: this.yaw, pitch: this.pitch, zoom: this.zoom}); - } - } - - GEvent.bind(this.gmap, 'load', this, function(){ - this.onMapReady(); - }); - - }, - onMapReady : function(){ - this.addMarkers(this.markers); - this.addMapControls(); - this.addOptions(); - }, - onResize : function(w, h){ - - if (typeof this.getMap() == 'object') { - this.gmap.checkResize(); - } - - Ext.ux.GMapPanel.superclass.onResize.call(this, w, h); - - }, - setSize : function(width, height, animate){ - - if (typeof this.getMap() == 'object') { - this.gmap.checkResize(); - } - - Ext.ux.GMapPanel.superclass.setSize.call(this, width, height, animate); - - }, - getMap : function(){ - - return this.gmap; - - }, - getCenter : function(){ - - return this.getMap().getCenter(); - - }, - getCenterLatLng : function(){ - - var ll = this.getCenter(); - return {lat: ll.lat(), lng: ll.lng()}; - - }, - addMarkers : function(markers) { - - if (Ext.isArray(markers)){ - for (var i = 0; i < markers.length; i++) { - var mkr_point = new GLatLng(markers[i].lat,markers[i].lng); - this.addMarker(mkr_point,markers[i].marker,false,markers[i].setCenter, markers[i].listeners); - } - } - - }, - addMarker : function(point, marker, clear, center, listeners){ - - Ext.applyIf(marker,G_DEFAULT_ICON); - - if (clear === true){ - this.getMap().clearOverlays(); - } - if (center === true) { - this.getMap().setCenter(point, this.zoomLevel); - } - - var mark = new GMarker(point,marker); - if (typeof listeners === 'object'){ - for (evt in listeners) { - GEvent.bind(mark, evt, this, listeners[evt]); - } - } - this.getMap().addOverlay(mark); - - }, - addMapControls : function(){ - - if (this.gmapType === 'map') { - if (Ext.isArray(this.mapControls)) { - for(i=0;i
    Level '+accuracy+' Accuracy (8 = Exact Match, 1 = Vague Match)'); - }else{ - point = new GLatLng(place.Point.coordinates[1], place.Point.coordinates[0]); - if (typeof this.setCenter.marker === 'object' && typeof point === 'object'){ - this.addMarker(point,this.setCenter.marker,this.setCenter.marker.clear,true, this.setCenter.listeners); - } - } - } - } - - } - -}); - -Ext.reg('gmappanel', Ext.ux.GMapPanel); Ext.namespace('Ext.ux.grid'); - -/** - * @class Ext.ux.grid.GridFilters - * @extends Ext.util.Observable - *

    GridFilter is a plugin (ptype='gridfilters') for grids that - * allow for a slightly more robust representation of filtering than what is - * provided by the default store.

    - *

    Filtering is adjusted by the user using the grid's column header menu - * (this menu can be disabled through configuration). Through this menu users - * can configure, enable, and disable filters for each column.

    - *

    Features:

    - *
    - *

    Example usage:

    - *
    
    -var store = new Ext.data.GroupingStore({
    -    ...
    -});
    -
    -var filters = new Ext.ux.grid.GridFilters({
    -    autoReload: false, //don't reload automatically
    -    local: true, //only filter locally
    -    // filters may be configured through the plugin,
    -    // or in the column definition within the column model configuration
    -    filters: [{
    -        type: 'numeric',
    -        dataIndex: 'id'
    -    }, {
    -        type: 'string',
    -        dataIndex: 'name'
    -    }, {
    -        type: 'numeric',
    -        dataIndex: 'price'
    -    }, {
    -        type: 'date',
    -        dataIndex: 'dateAdded'
    -    }, {
    -        type: 'list',
    -        dataIndex: 'size',
    -        options: ['extra small', 'small', 'medium', 'large', 'extra large'],
    -        phpMode: true
    -    }, {
    -        type: 'boolean',
    -        dataIndex: 'visible'
    -    }]
    -});
    -var cm = new Ext.grid.ColumnModel([{
    -    ...
    -}]);
    -
    -var grid = new Ext.grid.GridPanel({
    -     ds: store,
    -     cm: cm,
    -     view: new Ext.grid.GroupingView(),
    -     plugins: [filters],
    -     height: 400,
    -     width: 700,
    -     bbar: new Ext.PagingToolbar({
    -         store: store,
    -         pageSize: 15,
    -         plugins: [filters] //reset page to page 1 if filters change
    -     })
    - });
    -
    -store.load({params: {start: 0, limit: 15}});
    -
    -// a filters property is added to the grid
    -grid.filters
    - * 
    - */ -Ext.ux.grid.GridFilters = Ext.extend(Ext.util.Observable, { - /** - * @cfg {Boolean} autoReload - * Defaults to true, reloading the datasource when a filter change happens. - * Set this to false to prevent the datastore from being reloaded if there - * are changes to the filters. See {@link updateBuffer}. - */ - autoReload : true, - /** - * @cfg {Boolean} encode - * Specify true for {@link #buildQuery} to use Ext.util.JSON.encode to - * encode the filter query parameter sent with a remote request. - * Defaults to false. - */ - /** - * @cfg {Array} filters - * An Array of filters config objects. Refer to each filter type class for - * configuration details specific to each filter type. Filters for Strings, - * Numeric Ranges, Date Ranges, Lists, and Boolean are the standard filters - * available. - */ - /** - * @cfg {String} filterCls - * The css class to be applied to column headers with active filters. - * Defaults to 'ux-filterd-column'. - */ - filterCls : 'ux-filtered-column', - /** - * @cfg {Boolean} local - * true to use Ext.data.Store filter functions (local filtering) - * instead of the default (false) server side filtering. - */ - local : false, - /** - * @cfg {String} menuFilterText - * defaults to 'Filters'. - */ - menuFilterText : 'Filters', - /** - * @cfg {String} paramPrefix - * The url parameter prefix for the filters. - * Defaults to 'filter'. - */ - paramPrefix : 'filter', - /** - * @cfg {Boolean} showMenu - * Defaults to true, including a filter submenu in the default header menu. - */ - showMenu : true, - /** - * @cfg {String} stateId - * Name of the value to be used to store state information. - */ - stateId : undefined, - /** - * @cfg {Integer} updateBuffer - * Number of milliseconds to defer store updates since the last filter change. - */ - updateBuffer : 500, - - /** @private */ - constructor : function (config) { - config = config || {}; - this.deferredUpdate = new Ext.util.DelayedTask(this.reload, this); - this.filters = new Ext.util.MixedCollection(); - this.filters.getKey = function (o) { - return o ? o.dataIndex : null; - }; - this.addFilters(config.filters); - delete config.filters; - Ext.apply(this, config); - }, - - /** @private */ - init : function (grid) { - if (grid instanceof Ext.grid.GridPanel) { - this.grid = grid; - - this.bindStore(this.grid.getStore(), true); - // assumes no filters were passed in the constructor, so try and use ones from the colModel - if(this.filters.getCount() == 0){ - this.addFilters(this.grid.getColumnModel()); - } - - this.grid.filters = this; - - this.grid.addEvents({'filterupdate': true}); - - grid.on({ - scope: this, - beforestaterestore: this.applyState, - beforestatesave: this.saveState, - beforedestroy: this.destroy, - reconfigure: this.onReconfigure - }); - - if (grid.rendered){ - this.onRender(); - } else { - grid.on({ - scope: this, - single: true, - render: this.onRender - }); - } - - } else if (grid instanceof Ext.PagingToolbar) { - this.toolbar = grid; - } - }, - - /** - * @private - * Handler for the grid's beforestaterestore event (fires before the state of the - * grid is restored). - * @param {Object} grid The grid object - * @param {Object} state The hash of state values returned from the StateProvider. - */ - applyState : function (grid, state) { - var key, filter; - this.applyingState = true; - this.clearFilters(); - if (state.filters) { - for (key in state.filters) { - filter = this.filters.get(key); - if (filter) { - filter.setValue(state.filters[key]); - filter.setActive(true); - } - } - } - this.deferredUpdate.cancel(); - if (this.local) { - this.reload(); - } - delete this.applyingState; - delete state.filters; - }, - - /** - * Saves the state of all active filters - * @param {Object} grid - * @param {Object} state - * @return {Boolean} - */ - saveState : function (grid, state) { - var filters = {}; - this.filters.each(function (filter) { - if (filter.active) { - filters[filter.dataIndex] = filter.getValue(); - } - }); - return (state.filters = filters); - }, - - /** - * @private - * Handler called when the grid is rendered - */ - onRender : function () { - this.grid.getView().on('refresh', this.onRefresh, this); - this.createMenu(); - }, - - /** - * @private - * Handler called by the grid 'beforedestroy' event - */ - destroy : function () { - this.removeAll(); - this.purgeListeners(); - - if(this.filterMenu){ - Ext.menu.MenuMgr.unregister(this.filterMenu); - this.filterMenu.destroy(); - this.filterMenu = this.menu.menu = null; - } - }, - - /** - * Remove all filters, permanently destroying them. - */ - removeAll : function () { - if(this.filters){ - Ext.destroy.apply(Ext, this.filters.items); - // remove all items from the collection - this.filters.clear(); - } - }, - - - /** - * Changes the data store bound to this view and refreshes it. - * @param {Store} store The store to bind to this view - */ - bindStore : function(store, initial){ - if(!initial && this.store){ - if (this.local) { - store.un('load', this.onLoad, this); - } else { - store.un('beforeload', this.onBeforeLoad, this); - } - } - if(store){ - if (this.local) { - store.on('load', this.onLoad, this); - } else { - store.on('beforeload', this.onBeforeLoad, this); - } - } - this.store = store; - }, - - /** - * @private - * Handler called when the grid reconfigure event fires - */ - onReconfigure : function () { - this.bindStore(this.grid.getStore()); - this.store.clearFilter(); - this.removeAll(); - this.addFilters(this.grid.getColumnModel()); - this.updateColumnHeadings(); - }, - - createMenu : function () { - var view = this.grid.getView(), - hmenu = view.hmenu; - - if (this.showMenu && hmenu) { - - this.sep = hmenu.addSeparator(); - this.filterMenu = new Ext.menu.Menu({ - id: this.grid.id + '-filters-menu' - }); - this.menu = hmenu.add({ - checked: false, - itemId: 'filters', - text: this.menuFilterText, - menu: this.filterMenu - }); - - this.menu.on({ - scope: this, - checkchange: this.onCheckChange, - beforecheckchange: this.onBeforeCheck - }); - hmenu.on('beforeshow', this.onMenu, this); - } - this.updateColumnHeadings(); - }, - - /** - * @private - * Get the filter menu from the filters MixedCollection based on the clicked header - */ - getMenuFilter : function () { - var view = this.grid.getView(); - if (!view || view.hdCtxIndex === undefined) { - return null; - } - return this.filters.get( - view.cm.config[view.hdCtxIndex].dataIndex - ); - }, - - /** - * @private - * Handler called by the grid's hmenu beforeshow event - */ - onMenu : function (filterMenu) { - var filter = this.getMenuFilter(); - - if (filter) { -/* -TODO: lazy rendering - if (!filter.menu) { - filter.menu = filter.createMenu(); - } -*/ - this.menu.menu = filter.menu; - this.menu.setChecked(filter.active, false); - // disable the menu if filter.disabled explicitly set to true - this.menu.setDisabled(filter.disabled === true); - } - - this.menu.setVisible(filter !== undefined); - this.sep.setVisible(filter !== undefined); - }, - - /** @private */ - onCheckChange : function (item, value) { - this.getMenuFilter().setActive(value); - }, - - /** @private */ - onBeforeCheck : function (check, value) { - return !value || this.getMenuFilter().isActivatable(); - }, - - /** - * @private - * Handler for all events on filters. - * @param {String} event Event name - * @param {Object} filter Standard signature of the event before the event is fired - */ - onStateChange : function (event, filter) { - if (event === 'serialize') { - return; - } - - if (filter == this.getMenuFilter()) { - this.menu.setChecked(filter.active, false); - } - - if ((this.autoReload || this.local) && !this.applyingState) { - this.deferredUpdate.delay(this.updateBuffer); - } - this.updateColumnHeadings(); - - if (!this.applyingState) { - this.grid.saveState(); - } - this.grid.fireEvent('filterupdate', this, filter); - }, - - /** - * @private - * Handler for store's beforeload event when configured for remote filtering - * @param {Object} store - * @param {Object} options - */ - onBeforeLoad : function (store, options) { - options.params = options.params || {}; - this.cleanParams(options.params); - var params = this.buildQuery(this.getFilterData()); - Ext.apply(options.params, params); - }, - - /** - * @private - * Handler for store's load event when configured for local filtering - * @param {Object} store - * @param {Object} options - */ - onLoad : function (store, options) { - store.filterBy(this.getRecordFilter()); - }, - - /** - * @private - * Handler called when the grid's view is refreshed - */ - onRefresh : function () { - this.updateColumnHeadings(); - }, - - /** - * Update the styles for the header row based on the active filters - */ - updateColumnHeadings : function () { - var view = this.grid.getView(), - i, len, filter; - if (view.mainHd) { - for (i = 0, len = view.cm.config.length; i < len; i++) { - filter = this.getFilter(view.cm.config[i].dataIndex); - Ext.fly(view.getHeaderCell(i))[filter && filter.active ? 'addClass' : 'removeClass'](this.filterCls); - } - } - }, - - /** @private */ - reload : function () { - if (this.local) { - this.grid.store.clearFilter(true); - this.grid.store.filterBy(this.getRecordFilter()); - } else { - var start, - store = this.grid.store; - this.deferredUpdate.cancel(); - if (this.toolbar) { - start = store.paramNames.start; - if (store.lastOptions && store.lastOptions.params && store.lastOptions.params[start]) { - store.lastOptions.params[start] = 0; - } - } - store.reload(); - } - }, - - /** - * Method factory that generates a record validator for the filters active at the time - * of invokation. - * @private - */ - getRecordFilter : function () { - var f = [], len, i; - this.filters.each(function (filter) { - if (filter.active) { - f.push(filter); - } - }); - - len = f.length; - return function (record) { - for (i = 0; i < len; i++) { - if (!f[i].validateRecord(record)) { - return false; - } - } - return true; - }; - }, - - /** - * Adds a filter to the collection and observes it for state change. - * @param {Object/Ext.ux.grid.filter.Filter} config A filter configuration or a filter object. - * @return {Ext.ux.grid.filter.Filter} The existing or newly created filter object. - */ - addFilter : function (config) { - var Cls = this.getFilterClass(config.type), - filter = config.menu ? config : (new Cls(config)); - this.filters.add(filter); - - Ext.util.Observable.capture(filter, this.onStateChange, this); - return filter; - }, - - /** - * Adds filters to the collection. - * @param {Array/Ext.grid.ColumnModel} filters Either an Array of - * filter configuration objects or an Ext.grid.ColumnModel. The columns - * of a passed Ext.grid.ColumnModel will be examined for a filter - * property and, if present, will be used as the filter configuration object. - */ - addFilters : function (filters) { - if (filters) { - var i, len, filter, cm = false, dI; - if (filters instanceof Ext.grid.ColumnModel) { - filters = filters.config; - cm = true; - } - for (i = 0, len = filters.length; i < len; i++) { - filter = false; - if (cm) { - dI = filters[i].dataIndex; - filter = filters[i].filter || filters[i].filterable; - if (filter){ - filter = (filter === true) ? {} : filter; - Ext.apply(filter, {dataIndex:dI}); - // filter type is specified in order of preference: - // filter type specified in config - // type specified in store's field's type config - filter.type = filter.type || this.store.fields.get(dI).type.type; - } - } else { - filter = filters[i]; - } - // if filter config found add filter for the column - if (filter) { - this.addFilter(filter); - } - } - } - }, - - /** - * Returns a filter for the given dataIndex, if one exists. - * @param {String} dataIndex The dataIndex of the desired filter object. - * @return {Ext.ux.grid.filter.Filter} - */ - getFilter : function (dataIndex) { - return this.filters.get(dataIndex); - }, - - /** - * Turns all filters off. This does not clear the configuration information - * (see {@link #removeAll}). - */ - clearFilters : function () { - this.filters.each(function (filter) { - filter.setActive(false); - }); - }, - - /** - * Returns an Array of the currently active filters. - * @return {Array} filters Array of the currently active filters. - */ - getFilterData : function () { - var filters = [], i, len; - - this.filters.each(function (f) { - if (f.active) { - var d = [].concat(f.serialize()); - for (i = 0, len = d.length; i < len; i++) { - filters.push({ - field: f.dataIndex, - data: d[i] - }); - } - } - }); - return filters; - }, - - /** - * Function to take the active filters data and build it into a query. - * The format of the query depends on the {@link #encode} - * configuration: - *
    - * Override this method to customize the format of the filter query for remote requests. - * @param {Array} filters A collection of objects representing active filters and their configuration. - * Each element will take the form of {field: dataIndex, data: filterConf}. dataIndex is not assured - * to be unique as any one filter may be a composite of more basic filters for the same dataIndex. - * @return {Object} Query keys and values - */ - buildQuery : function (filters) { - var p = {}, i, f, root, dataPrefix, key, tmp, - len = filters.length; - - if (!this.encode){ - for (i = 0; i < len; i++) { - f = filters[i]; - root = [this.paramPrefix, '[', i, ']'].join(''); - p[root + '[field]'] = f.field; - - dataPrefix = root + '[data]'; - for (key in f.data) { - p[[dataPrefix, '[', key, ']'].join('')] = f.data[key]; - } - } - } else { - tmp = []; - for (i = 0; i < len; i++) { - f = filters[i]; - tmp.push(Ext.apply( - {}, - {field: f.field}, - f.data - )); - } - // only build if there is active filter - if (tmp.length > 0){ - p[this.paramPrefix] = Ext.util.JSON.encode(tmp); - } - } - return p; - }, - - /** - * Removes filter related query parameters from the provided object. - * @param {Object} p Query parameters that may contain filter related fields. - */ - cleanParams : function (p) { - // if encoding just delete the property - if (this.encode) { - delete p[this.paramPrefix]; - // otherwise scrub the object of filter data - } else { - var regex, key; - regex = new RegExp('^' + this.paramPrefix + '\[[0-9]+\]'); - for (key in p) { - if (regex.test(key)) { - delete p[key]; - } - } - } - }, - - /** - * Function for locating filter classes, overwrite this with your favorite - * loader to provide dynamic filter loading. - * @param {String} type The type of filter to load ('Filter' is automatically - * appended to the passed type; eg, 'string' becomes 'StringFilter'). - * @return {Class} The Ext.ux.grid.filter.Class - */ - getFilterClass : function (type) { - // map the supported Ext.data.Field type values into a supported filter - switch(type) { - case 'auto': - type = 'string'; - break; - case 'int': - case 'float': - type = 'numeric'; - break; - case 'bool': - type = 'boolean'; - break; - } - return Ext.ux.grid.filter[type.substr(0, 1).toUpperCase() + type.substr(1) + 'Filter']; - } -}); - -// register ptype -Ext.preg('gridfilters', Ext.ux.grid.GridFilters); -Ext.namespace('Ext.ux.grid.filter'); - -/** - * @class Ext.ux.grid.filter.Filter - * @extends Ext.util.Observable - * Abstract base class for filter implementations. - */ -Ext.ux.grid.filter.Filter = Ext.extend(Ext.util.Observable, { - /** - * @cfg {Boolean} active - * Indicates the initial status of the filter (defaults to false). - */ - active : false, - /** - * True if this filter is active. Use setActive() to alter after configuration. - * @type Boolean - * @property active - */ - /** - * @cfg {String} dataIndex - * The {@link Ext.data.Store} dataIndex of the field this filter represents. - * The dataIndex does not actually have to exist in the store. - */ - dataIndex : null, - /** - * The filter configuration menu that will be installed into the filter submenu of a column menu. - * @type Ext.menu.Menu - * @property - */ - menu : null, - /** - * @cfg {Number} updateBuffer - * Number of milliseconds to wait after user interaction to fire an update. Only supported - * by filters: 'list', 'numeric', and 'string'. Defaults to 500. - */ - updateBuffer : 500, - - constructor : function (config) { - Ext.apply(this, config); - - this.addEvents( - /** - * @event activate - * Fires when an inactive filter becomes active - * @param {Ext.ux.grid.filter.Filter} this - */ - 'activate', - /** - * @event deactivate - * Fires when an active filter becomes inactive - * @param {Ext.ux.grid.filter.Filter} this - */ - 'deactivate', - /** - * @event serialize - * Fires after the serialization process. Use this to attach additional parameters to serialization - * data before it is encoded and sent to the server. - * @param {Array/Object} data A map or collection of maps representing the current filter configuration. - * @param {Ext.ux.grid.filter.Filter} filter The filter being serialized. - */ - 'serialize', - /** - * @event update - * Fires when a filter configuration has changed - * @param {Ext.ux.grid.filter.Filter} this The filter object. - */ - 'update' - ); - Ext.ux.grid.filter.Filter.superclass.constructor.call(this); - - this.menu = new Ext.menu.Menu(); - this.init(config); - if(config && config.value){ - this.setValue(config.value); - this.setActive(config.active !== false, true); - delete config.value; - } - }, - - /** - * Destroys this filter by purging any event listeners, and removing any menus. - */ - destroy : function(){ - if (this.menu){ - this.menu.destroy(); - } - this.purgeListeners(); - }, - - /** - * Template method to be implemented by all subclasses that is to - * initialize the filter and install required menu items. - * Defaults to Ext.emptyFn. - */ - init : Ext.emptyFn, - - /** - * Template method to be implemented by all subclasses that is to - * get and return the value of the filter. - * Defaults to Ext.emptyFn. - * @return {Object} The 'serialized' form of this filter - * @methodOf Ext.ux.grid.filter.Filter - */ - getValue : Ext.emptyFn, - - /** - * Template method to be implemented by all subclasses that is to - * set the value of the filter and fire the 'update' event. - * Defaults to Ext.emptyFn. - * @param {Object} data The value to set the filter - * @methodOf Ext.ux.grid.filter.Filter - */ - setValue : Ext.emptyFn, - - /** - * Template method to be implemented by all subclasses that is to - * return true if the filter has enough configuration information to be activated. - * Defaults to return true. - * @return {Boolean} - */ - isActivatable : function(){ - return true; - }, - - /** - * Template method to be implemented by all subclasses that is to - * get and return serialized filter data for transmission to the server. - * Defaults to Ext.emptyFn. - */ - getSerialArgs : Ext.emptyFn, - - /** - * Template method to be implemented by all subclasses that is to - * validates the provided Ext.data.Record against the filters configuration. - * Defaults to return true. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function(){ - return true; - }, - - /** - * Returns the serialized filter data for transmission to the server - * and fires the 'serialize' event. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - * @methodOf Ext.ux.grid.filter.Filter - */ - serialize : function(){ - var args = this.getSerialArgs(); - this.fireEvent('serialize', args, this); - return args; - }, - - /** @private */ - fireUpdate : function(){ - if (this.active) { - this.fireEvent('update', this); - } - this.setActive(this.isActivatable()); - }, - - /** - * Sets the status of the filter and fires the appropriate events. - * @param {Boolean} active The new filter state. - * @param {Boolean} suppressEvent True to prevent events from being fired. - * @methodOf Ext.ux.grid.filter.Filter - */ - setActive : function(active, suppressEvent){ - if(this.active != active){ - this.active = active; - if (suppressEvent !== true) { - this.fireEvent(active ? 'activate' : 'deactivate', this); - } - } - } -});/** - * @class Ext.ux.grid.filter.BooleanFilter - * @extends Ext.ux.grid.filter.Filter - * Boolean filters use unique radio group IDs (so you can have more than one!) - *

    Example Usage:

    - *
        
    -var filters = new Ext.ux.grid.GridFilters({
    -    ...
    -    filters: [{
    -        // required configs
    -        type: 'boolean',
    -        dataIndex: 'visible'
    -
    -        // optional configs
    -        defaultValue: null, // leave unselected (false selected by default)
    -        yesText: 'Yes',     // default
    -        noText: 'No'        // default
    -    }]
    -});
    - * 
    - */ -Ext.ux.grid.filter.BooleanFilter = Ext.extend(Ext.ux.grid.filter.Filter, { - /** - * @cfg {Boolean} defaultValue - * Set this to null if you do not want either option to be checked by default. Defaults to false. - */ - defaultValue : false, - /** - * @cfg {String} yesText - * Defaults to 'Yes'. - */ - yesText : 'Yes', - /** - * @cfg {String} noText - * Defaults to 'No'. - */ - noText : 'No', - - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - init : function (config) { - var gId = Ext.id(); - this.options = [ - new Ext.menu.CheckItem({text: this.yesText, group: gId, checked: this.defaultValue === true}), - new Ext.menu.CheckItem({text: this.noText, group: gId, checked: this.defaultValue === false})]; - - this.menu.add(this.options[0], this.options[1]); - - for(var i=0; iExample Usage:

    - *
        
    -var filters = new Ext.ux.grid.GridFilters({
    -    ...
    -    filters: [{
    -        // required configs
    -        type: 'date',
    -        dataIndex: 'dateAdded',
    -        
    -        // optional configs
    -        dateFormat: 'm/d/Y',  // default
    -        beforeText: 'Before', // default
    -        afterText: 'After',   // default
    -        onText: 'On',         // default
    -        pickerOpts: {
    -            // any DateMenu configs
    -        },
    -
    -        active: true // default is false
    -    }]
    -});
    - * 
    - */ -Ext.ux.grid.filter.DateFilter = Ext.extend(Ext.ux.grid.filter.Filter, { - /** - * @cfg {String} afterText - * Defaults to 'After'. - */ - afterText : 'After', - /** - * @cfg {String} beforeText - * Defaults to 'Before'. - */ - beforeText : 'Before', - /** - * @cfg {Object} compareMap - * Map for assigning the comparison values used in serialization. - */ - compareMap : { - before: 'lt', - after: 'gt', - on: 'eq' - }, - /** - * @cfg {String} dateFormat - * The date format to return when using getValue. - * Defaults to 'm/d/Y'. - */ - dateFormat : 'm/d/Y', - - /** - * @cfg {Date} maxDate - * Allowable date as passed to the Ext.DatePicker - * Defaults to undefined. - */ - /** - * @cfg {Date} minDate - * Allowable date as passed to the Ext.DatePicker - * Defaults to undefined. - */ - /** - * @cfg {Array} menuItems - * The items to be shown in this menu - * Defaults to:
    -     * menuItems : ['before', 'after', '-', 'on'],
    -     * 
    - */ - menuItems : ['before', 'after', '-', 'on'], - - /** - * @cfg {Object} menuItemCfgs - * Default configuration options for each menu item - */ - menuItemCfgs : { - selectOnFocus: true, - width: 125 - }, - - /** - * @cfg {String} onText - * Defaults to 'On'. - */ - onText : 'On', - - /** - * @cfg {Object} pickerOpts - * Configuration options for the date picker associated with each field. - */ - pickerOpts : {}, - - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - init : function (config) { - var menuCfg, i, len, item, cfg, Cls; - - menuCfg = Ext.apply(this.pickerOpts, { - minDate: this.minDate, - maxDate: this.maxDate, - format: this.dateFormat, - listeners: { - scope: this, - select: this.onMenuSelect - } - }); - - this.fields = {}; - for (i = 0, len = this.menuItems.length; i < len; i++) { - item = this.menuItems[i]; - if (item !== '-') { - cfg = { - itemId: 'range-' + item, - text: this[item + 'Text'], - menu: new Ext.menu.DateMenu( - Ext.apply(menuCfg, { - itemId: item - }) - ), - listeners: { - scope: this, - checkchange: this.onCheckChange - } - }; - Cls = Ext.menu.CheckItem; - item = this.fields[item] = new Cls(cfg); - } - //this.add(item); - this.menu.add(item); - } - }, - - onCheckChange : function () { - this.setActive(this.isActivatable()); - this.fireEvent('update', this); - }, - - /** - * @private - * Handler method called when there is a keyup event on an input - * item of this menu. - */ - onInputKeyUp : function (field, e) { - var k = e.getKey(); - if (k == e.RETURN && field.isValid()) { - e.stopEvent(); - this.menu.hide(true); - return; - } - }, - - /** - * Handler for when the menu for a field fires the 'select' event - * @param {Object} date - * @param {Object} menuItem - * @param {Object} value - * @param {Object} picker - */ - onMenuSelect : function (menuItem, value, picker) { - var fields = this.fields, - field = this.fields[menuItem.itemId]; - - field.setChecked(true); - - if (field == fields.on) { - fields.before.setChecked(false, true); - fields.after.setChecked(false, true); - } else { - fields.on.setChecked(false, true); - if (field == fields.after && fields.before.menu.picker.value < value) { - fields.before.setChecked(false, true); - } else if (field == fields.before && fields.after.menu.picker.value > value) { - fields.after.setChecked(false, true); - } - } - this.fireEvent('update', this); - }, - - /** - * @private - * Template method that is to get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - var key, result = {}; - for (key in this.fields) { - if (this.fields[key].checked) { - result[key] = this.fields[key].menu.picker.getValue(); - } - } - return result; - }, - - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter - * @param {Boolean} preserve true to preserve the checked status - * of the other fields. Defaults to false, unchecking the - * other fields - */ - setValue : function (value, preserve) { - var key; - for (key in this.fields) { - if(value[key]){ - this.fields[key].menu.picker.setValue(value[key]); - this.fields[key].setChecked(true); - } else if (!preserve) { - this.fields[key].setChecked(false); - } - } - this.fireEvent('update', this); - }, - - /** - * @private - * Template method that is to return true if the filter - * has enough configuration information to be activated. - * @return {Boolean} - */ - isActivatable : function () { - var key; - for (key in this.fields) { - if (this.fields[key].checked) { - return true; - } - } - return false; - }, - - /** - * @private - * Template method that is to get and return serialized filter data for - * transmission to the server. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - */ - getSerialArgs : function () { - var args = []; - for (var key in this.fields) { - if(this.fields[key].checked){ - args.push({ - type: 'date', - comparison: this.compareMap[key], - value: this.getFieldValue(key).format(this.dateFormat) - }); - } - } - return args; - }, - - /** - * Get and return the date menu picker value - * @param {String} item The field identifier ('before', 'after', 'on') - * @return {Date} Gets the current selected value of the date field - */ - getFieldValue : function(item){ - return this.fields[item].menu.picker.getValue(); - }, - - /** - * Gets the menu picker associated with the passed field - * @param {String} item The field identifier ('before', 'after', 'on') - * @return {Object} The menu picker - */ - getPicker : function(item){ - return this.fields[item].menu.picker; - }, - - /** - * Template method that is to validate the provided Ext.data.Record - * against the filters configuration. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function (record) { - var key, - pickerValue, - val = record.get(this.dataIndex); - - if(!Ext.isDate(val)){ - return false; - } - val = val.clearTime(true).getTime(); - - for (key in this.fields) { - if (this.fields[key].checked) { - pickerValue = this.getFieldValue(key).clearTime(true).getTime(); - if (key == 'before' && pickerValue <= val) { - return false; - } - if (key == 'after' && pickerValue >= val) { - return false; - } - if (key == 'on' && pickerValue != val) { - return false; - } - } - } - return true; - } -});/** - * @class Ext.ux.grid.filter.ListFilter - * @extends Ext.ux.grid.filter.Filter - *

    List filters are able to be preloaded/backed by an Ext.data.Store to load - * their options the first time they are shown. ListFilter utilizes the - * {@link Ext.ux.menu.ListMenu} component.

    - *

    Although not shown here, this class accepts all configuration options - * for {@link Ext.ux.menu.ListMenu}.

    - * - *

    Example Usage:

    - *
        
    -var filters = new Ext.ux.grid.GridFilters({
    -    ...
    -    filters: [{
    -        type: 'list',
    -        dataIndex: 'size',
    -        phpMode: true,
    -        // options will be used as data to implicitly creates an ArrayStore
    -        options: ['extra small', 'small', 'medium', 'large', 'extra large']
    -    }]
    -});
    - * 
    - * - */ -Ext.ux.grid.filter.ListFilter = Ext.extend(Ext.ux.grid.filter.Filter, { - - /** - * @cfg {Array} options - *

    data to be used to implicitly create a data store - * to back this list when the data source is local. If the - * data for the list is remote, use the {@link #store} - * config instead.

    - *

    Each item within the provided array may be in one of the - * following formats:

    - *
      - *
    • Array : - *
      
      -options: [
      -    [11, 'extra small'], 
      -    [18, 'small'],
      -    [22, 'medium'],
      -    [35, 'large'],
      -    [44, 'extra large']
      -]
      -     * 
      - *
    • - *
    • Object : - *
      
      -labelField: 'name', // override default of 'text'
      -options: [
      -    {id: 11, name:'extra small'}, 
      -    {id: 18, name:'small'}, 
      -    {id: 22, name:'medium'}, 
      -    {id: 35, name:'large'}, 
      -    {id: 44, name:'extra large'} 
      -]
      -     * 
      - *
    • - *
    • String : - *
      
      -     * options: ['extra small', 'small', 'medium', 'large', 'extra large']
      -     * 
      - *
    • - */ - /** - * @cfg {Boolean} phpMode - *

      Adjust the format of this filter. Defaults to false.

      - *

      When GridFilters @cfg encode = false (default):

      - *
      
      -// phpMode == false (default):
      -filter[0][data][type] list
      -filter[0][data][value] value1
      -filter[0][data][value] value2
      -filter[0][field] prod 
      -
      -// phpMode == true:
      -filter[0][data][type] list
      -filter[0][data][value] value1, value2
      -filter[0][field] prod 
      -     * 
      - * When GridFilters @cfg encode = true: - *
      
      -// phpMode == false (default):
      -filter : [{"type":"list","value":["small","medium"],"field":"size"}]
      -
      -// phpMode == true:
      -filter : [{"type":"list","value":"small,medium","field":"size"}]
      -     * 
      - */ - phpMode : false, - /** - * @cfg {Ext.data.Store} store - * The {@link Ext.data.Store} this list should use as its data source - * when the data source is remote. If the data for the list - * is local, use the {@link #options} config instead. - */ - - /** - * @private - * Template method that is to initialize the filter and install required menu items. - * @param {Object} config - */ - init : function (config) { - this.dt = new Ext.util.DelayedTask(this.fireUpdate, this); - - // if a menu already existed, do clean up first - if (this.menu){ - this.menu.destroy(); - } - this.menu = new Ext.ux.menu.ListMenu(config); - this.menu.on('checkchange', this.onCheckChange, this); - }, - - /** - * @private - * Template method that is to get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - return this.menu.getSelected(); - }, - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter - */ - setValue : function (value) { - this.menu.setSelected(value); - this.fireEvent('update', this); - }, - - /** - * @private - * Template method that is to return true if the filter - * has enough configuration information to be activated. - * @return {Boolean} - */ - isActivatable : function () { - return this.getValue().length > 0; - }, - - /** - * @private - * Template method that is to get and return serialized filter data for - * transmission to the server. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - */ - getSerialArgs : function () { - var args = {type: 'list', value: this.phpMode ? this.getValue().join(',') : this.getValue()}; - return args; - }, - - /** @private */ - onCheckChange : function(){ - this.dt.delay(this.updateBuffer); - }, - - - /** - * Template method that is to validate the provided Ext.data.Record - * against the filters configuration. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function (record) { - return this.getValue().indexOf(record.get(this.dataIndex)) > -1; - } -});/** - * @class Ext.ux.grid.filter.NumericFilter - * @extends Ext.ux.grid.filter.Filter - * Filters using an Ext.ux.menu.RangeMenu. - *

      Example Usage:

      - *
          
      -var filters = new Ext.ux.grid.GridFilters({
      -    ...
      -    filters: [{
      -        type: 'numeric',
      -        dataIndex: 'price'
      -    }]
      -});
      - * 
      - */ -Ext.ux.grid.filter.NumericFilter = Ext.extend(Ext.ux.grid.filter.Filter, { - - /** - * @cfg {Object} fieldCls - * The Class to use to construct each field item within this menu - * Defaults to:
      -     * fieldCls : Ext.form.NumberField
      -     * 
      - */ - fieldCls : Ext.form.NumberField, - /** - * @cfg {Object} fieldCfg - * The default configuration options for any field item unless superseded - * by the {@link #fields} configuration. - * Defaults to:
      -     * fieldCfg : {}
      -     * 
      - * Example usage: - *
      
      -fieldCfg : {
      -    width: 150,
      -},
      -     * 
      - */ - /** - * @cfg {Object} fields - * The field items may be configured individually - * Defaults to undefined. - * Example usage: - *
      
      -fields : {
      -    gt: { // override fieldCfg options
      -        width: 200,
      -        fieldCls: Ext.ux.form.CustomNumberField // to override default {@link #fieldCls}
      -    }
      -},
      -     * 
      - */ - /** - * @cfg {Object} iconCls - * The iconCls to be applied to each comparator field item. - * Defaults to:
      -iconCls : {
      -    gt : 'ux-rangemenu-gt',
      -    lt : 'ux-rangemenu-lt',
      -    eq : 'ux-rangemenu-eq'
      -}
      -     * 
      - */ - iconCls : { - gt : 'ux-rangemenu-gt', - lt : 'ux-rangemenu-lt', - eq : 'ux-rangemenu-eq' - }, - - /** - * @cfg {Object} menuItemCfgs - * Default configuration options for each menu item - * Defaults to:
      -menuItemCfgs : {
      -    emptyText: 'Enter Filter Text...',
      -    selectOnFocus: true,
      -    width: 125
      -}
      -     * 
      - */ - menuItemCfgs : { - emptyText: 'Enter Filter Text...', - selectOnFocus: true, - width: 125 - }, - - /** - * @cfg {Array} menuItems - * The items to be shown in this menu. Items are added to the menu - * according to their position within this array. Defaults to:
      -     * menuItems : ['lt','gt','-','eq']
      -     * 
      - */ - menuItems : ['lt', 'gt', '-', 'eq'], - - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - init : function (config) { - // if a menu already existed, do clean up first - if (this.menu){ - this.menu.destroy(); - } - this.menu = new Ext.ux.menu.RangeMenu(Ext.apply(config, { - // pass along filter configs to the menu - fieldCfg : this.fieldCfg || {}, - fieldCls : this.fieldCls, - fields : this.fields || {}, - iconCls: this.iconCls, - menuItemCfgs: this.menuItemCfgs, - menuItems: this.menuItems, - updateBuffer: this.updateBuffer - })); - // relay the event fired by the menu - this.menu.on('update', this.fireUpdate, this); - }, - - /** - * @private - * Template method that is to get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - return this.menu.getValue(); - }, - - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter - */ - setValue : function (value) { - this.menu.setValue(value); - }, - - /** - * @private - * Template method that is to return true if the filter - * has enough configuration information to be activated. - * @return {Boolean} - */ - isActivatable : function () { - var values = this.getValue(); - for (key in values) { - if (values[key] !== undefined) { - return true; - } - } - return false; - }, - - /** - * @private - * Template method that is to get and return serialized filter data for - * transmission to the server. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - */ - getSerialArgs : function () { - var key, - args = [], - values = this.menu.getValue(); - for (key in values) { - args.push({ - type: 'numeric', - comparison: key, - value: values[key] - }); - } - return args; - }, - - /** - * Template method that is to validate the provided Ext.data.Record - * against the filters configuration. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function (record) { - var val = record.get(this.dataIndex), - values = this.getValue(); - if (values.eq !== undefined && val != values.eq) { - return false; - } - if (values.lt !== undefined && val >= values.lt) { - return false; - } - if (values.gt !== undefined && val <= values.gt) { - return false; - } - return true; - } -});/** - * @class Ext.ux.grid.filter.StringFilter - * @extends Ext.ux.grid.filter.Filter - * Filter by a configurable Ext.form.TextField - *

      Example Usage:

      - *
          
      -var filters = new Ext.ux.grid.GridFilters({
      -    ...
      -    filters: [{
      -        // required configs
      -        type: 'string',
      -        dataIndex: 'name',
      -        
      -        // optional configs
      -        value: 'foo',
      -        active: true, // default is false
      -        iconCls: 'ux-gridfilter-text-icon' // default
      -        // any Ext.form.TextField configs accepted
      -    }]
      -});
      - * 
      - */ -Ext.ux.grid.filter.StringFilter = Ext.extend(Ext.ux.grid.filter.Filter, { - - /** - * @cfg {String} iconCls - * The iconCls to be applied to the menu item. - * Defaults to 'ux-gridfilter-text-icon'. - */ - iconCls : 'ux-gridfilter-text-icon', - - emptyText: 'Enter Filter Text...', - selectOnFocus: true, - width: 125, - - /** - * @private - * Template method that is to initialize the filter and install required menu items. - */ - init : function (config) { - Ext.applyIf(config, { - enableKeyEvents: true, - iconCls: this.iconCls, - listeners: { - scope: this, - keyup: this.onInputKeyUp - } - }); - - this.inputItem = new Ext.form.TextField(config); - this.menu.add(this.inputItem); - this.updateTask = new Ext.util.DelayedTask(this.fireUpdate, this); - }, - - /** - * @private - * Template method that is to get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - return this.inputItem.getValue(); - }, - - /** - * @private - * Template method that is to set the value of the filter. - * @param {Object} value The value to set the filter - */ - setValue : function (value) { - this.inputItem.setValue(value); - this.fireEvent('update', this); - }, - - /** - * @private - * Template method that is to return true if the filter - * has enough configuration information to be activated. - * @return {Boolean} - */ - isActivatable : function () { - return this.inputItem.getValue().length > 0; - }, - - /** - * @private - * Template method that is to get and return serialized filter data for - * transmission to the server. - * @return {Object/Array} An object or collection of objects containing - * key value pairs representing the current configuration of the filter. - */ - getSerialArgs : function () { - return {type: 'string', value: this.getValue()}; - }, - - /** - * Template method that is to validate the provided Ext.data.Record - * against the filters configuration. - * @param {Ext.data.Record} record The record to validate - * @return {Boolean} true if the record is valid within the bounds - * of the filter, false otherwise. - */ - validateRecord : function (record) { - var val = record.get(this.dataIndex); - - if(typeof val != 'string') { - return (this.getValue().length === 0); - } - - return val.toLowerCase().indexOf(this.getValue().toLowerCase()) > -1; - }, - - /** - * @private - * Handler method called when there is a keyup event on this.inputItem - */ - onInputKeyUp : function (field, e) { - var k = e.getKey(); - if (k == e.RETURN && field.isValid()) { - e.stopEvent(); - this.menu.hide(true); - return; - } - // restart the timer - this.updateTask.delay(this.updateBuffer); - } -}); -Ext.namespace('Ext.ux.menu'); - -/** - * @class Ext.ux.menu.ListMenu - * @extends Ext.menu.Menu - * This is a supporting class for {@link Ext.ux.grid.filter.ListFilter}. - * Although not listed as configuration options for this class, this class - * also accepts all configuration options from {@link Ext.ux.grid.filter.ListFilter}. - */ -Ext.ux.menu.ListMenu = Ext.extend(Ext.menu.Menu, { - /** - * @cfg {String} labelField - * Defaults to 'text'. - */ - labelField : 'text', - /** - * @cfg {String} paramPrefix - * Defaults to 'Loading...'. - */ - loadingText : 'Loading...', - /** - * @cfg {Boolean} loadOnShow - * Defaults to true. - */ - loadOnShow : true, - /** - * @cfg {Boolean} single - * Specify true to group all items in this list into a single-select - * radio button group. Defaults to false. - */ - single : false, - - constructor : function (cfg) { - this.selected = []; - this.addEvents( - /** - * @event checkchange - * Fires when there is a change in checked items from this list - * @param {Object} item Ext.menu.CheckItem - * @param {Object} checked The checked value that was set - */ - 'checkchange' - ); - - Ext.ux.menu.ListMenu.superclass.constructor.call(this, cfg = cfg || {}); - - if(!cfg.store && cfg.options){ - var options = []; - for(var i=0, len=cfg.options.length; i -1, - hideOnClick: false}); - - item.itemId = records[i].id; - item.on('checkchange', this.checkChange, this); - - this.add(item); - } - - this.loaded = true; - - if (visible) { - this.show(); - } - this.fireEvent('load', this, records); - }, - - /** - * Get the selected items. - * @return {Array} selected - */ - getSelected : function () { - return this.selected; - }, - - /** @private */ - setSelected : function (value) { - value = this.selected = [].concat(value); - - if (this.loaded) { - this.items.each(function(item){ - item.setChecked(false, true); - for (var i = 0, len = value.length; i < len; i++) { - if (item.itemId == value[i]) { - item.setChecked(true, true); - } - } - }, this); - } - }, - - /** - * Handler for the 'checkchange' event from an check item in this menu - * @param {Object} item Ext.menu.CheckItem - * @param {Object} checked The checked value that was set - */ - checkChange : function (item, checked) { - var value = []; - this.items.each(function(item){ - if (item.checked) { - value.push(item.itemId); - } - },this); - this.selected = value; - - this.fireEvent('checkchange', item, checked); - } -});Ext.ns('Ext.ux.menu'); - -/** - * @class Ext.ux.menu.RangeMenu - * @extends Ext.menu.Menu - * Custom implementation of Ext.menu.Menu that has preconfigured - * items for gt, lt, eq. - *

      Example Usage:

      - *
          
      -
      - * 
      - */ -Ext.ux.menu.RangeMenu = Ext.extend(Ext.menu.Menu, { - - constructor : function (config) { - - Ext.ux.menu.RangeMenu.superclass.constructor.call(this, config); - - this.addEvents( - /** - * @event update - * Fires when a filter configuration has changed - * @param {Ext.ux.grid.filter.Filter} this The filter object. - */ - 'update' - ); - - this.updateTask = new Ext.util.DelayedTask(this.fireUpdate, this); - - var i, len, item, cfg, Cls; - - for (i = 0, len = this.menuItems.length; i < len; i++) { - item = this.menuItems[i]; - if (item !== '-') { - // defaults - cfg = { - itemId: 'range-' + item, - enableKeyEvents: true, - iconCls: this.iconCls[item] || 'no-icon', - listeners: { - scope: this, - keyup: this.onInputKeyUp - } - }; - Ext.apply( - cfg, - // custom configs - Ext.applyIf(this.fields[item] || {}, this.fieldCfg[item]), - // configurable defaults - this.menuItemCfgs - ); - Cls = cfg.fieldCls || this.fieldCls; - item = this.fields[item] = new Cls(cfg); - } - this.add(item); - } - }, - - /** - * @private - * called by this.updateTask - */ - fireUpdate : function () { - this.fireEvent('update', this); - }, - - /** - * Get and return the value of the filter. - * @return {String} The value of this filter - */ - getValue : function () { - var result = {}, key, field; - for (key in this.fields) { - field = this.fields[key]; - if (field.isValid() && String(field.getValue()).length > 0) { - result[key] = field.getValue(); - } - } - return result; - }, - - /** - * Set the value of this menu and fires the 'update' event. - * @param {Object} data The data to assign to this menu - */ - setValue : function (data) { - var key; - for (key in this.fields) { - this.fields[key].setValue(data[key] !== undefined ? data[key] : ''); - } - this.fireEvent('update', this); - }, - - /** - * @private - * Handler method called when there is a keyup event on an input - * item of this menu. - */ - onInputKeyUp : function (field, e) { - var k = e.getKey(); - if (k == e.RETURN && field.isValid()) { - e.stopEvent(); - this.hide(true); - return; - } - - if (field == this.fields.eq) { - if (this.fields.gt) { - this.fields.gt.setValue(null); - } - if (this.fields.lt) { - this.fields.lt.setValue(null); - } - } - else { - this.fields.eq.setValue(null); - } - - // restart the timer - this.updateTask.delay(this.updateBuffer); - } -}); -Ext.ns('Ext.ux.grid'); - -/** - * @class Ext.ux.grid.GroupSummary - * @extends Ext.util.Observable - * A GridPanel plugin that enables dynamic column calculations and a dynamically - * updated grouped summary row. - */ -Ext.ux.grid.GroupSummary = Ext.extend(Ext.util.Observable, { - /** - * @cfg {Function} summaryRenderer Renderer example:
      
      -summaryRenderer: function(v, params, data){
      -    return ((v === 0 || v > 1) ? '(' + v +' Tasks)' : '(1 Task)');
      -},
      -     * 
      - */ - /** - * @cfg {String} summaryType (Optional) The type of - * calculation to be used for the column. For options available see - * {@link #Calculations}. - */ - - constructor : function(config){ - Ext.apply(this, config); - Ext.ux.grid.GroupSummary.superclass.constructor.call(this); - }, - init : function(grid){ - this.grid = grid; - var v = this.view = grid.getView(); - v.doGroupEnd = this.doGroupEnd.createDelegate(this); - - v.afterMethod('onColumnWidthUpdated', this.doWidth, this); - v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this); - v.afterMethod('onColumnHiddenUpdated', this.doHidden, this); - v.afterMethod('onUpdate', this.doUpdate, this); - v.afterMethod('onRemove', this.doRemove, this); - - if(!this.rowTpl){ - this.rowTpl = new Ext.Template( - '
      ', - '', - '{cells}', - '
      ' - ); - this.rowTpl.disableFormats = true; - } - this.rowTpl.compile(); - - if(!this.cellTpl){ - this.cellTpl = new Ext.Template( - '', - '
      {value}
      ', - "" - ); - this.cellTpl.disableFormats = true; - } - this.cellTpl.compile(); - }, - - /** - * Toggle the display of the summary row on/off - * @param {Boolean} visible true to show the summary, false to hide the summary. - */ - toggleSummaries : function(visible){ - var el = this.grid.getGridEl(); - if(el){ - if(visible === undefined){ - visible = el.hasClass('x-grid-hide-summary'); - } - el[visible ? 'removeClass' : 'addClass']('x-grid-hide-summary'); - } - }, - - renderSummary : function(o, cs){ - cs = cs || this.view.getColumnData(); - var cfg = this.grid.getColumnModel().config, - buf = [], c, p = {}, cf, last = cs.length-1; - for(var i = 0, len = cs.length; i < len; i++){ - c = cs[i]; - cf = cfg[i]; - p.id = c.id; - p.style = c.style; - p.css = i == 0 ? 'x-grid3-cell-first ' : (i == last ? 'x-grid3-cell-last ' : ''); - if(cf.summaryType || cf.summaryRenderer){ - p.value = (cf.summaryRenderer || c.renderer)(o.data[c.name], p, o); - }else{ - p.value = ''; - } - if(p.value == undefined || p.value === "") p.value = " "; - buf[buf.length] = this.cellTpl.apply(p); - } - - return this.rowTpl.apply({ - tstyle: 'width:'+this.view.getTotalWidth()+';', - cells: buf.join('') - }); - }, - - /** - * @private - * @param {Object} rs - * @param {Object} cs - */ - calculate : function(rs, cs){ - var data = {}, r, c, cfg = this.grid.getColumnModel().config, cf; - for(var j = 0, jlen = rs.length; j < jlen; j++){ - r = rs[j]; - for(var i = 0, len = cs.length; i < len; i++){ - c = cs[i]; - cf = cfg[i]; - if(cf.summaryType){ - data[c.name] = Ext.ux.grid.GroupSummary.Calculations[cf.summaryType](data[c.name] || 0, r, c.name, data); - } - } - } - return data; - }, - - doGroupEnd : function(buf, g, cs, ds, colCount){ - var data = this.calculate(g.rs, cs); - buf.push('
    ', this.renderSummary({data: data}, cs), ''); - }, - - doWidth : function(col, w, tw){ - if(!this.isGrouped()){ - return; - } - var gs = this.view.getGroups(), - len = gs.length, - i = 0, - s; - for(; i < len; ++i){ - s = gs[i].childNodes[2]; - s.style.width = tw; - s.firstChild.style.width = tw; - s.firstChild.rows[0].childNodes[col].style.width = w; - } - }, - - doAllWidths : function(ws, tw){ - if(!this.isGrouped()){ - return; - } - var gs = this.view.getGroups(), - len = gs.length, - i = 0, - j, - s, - cells, - wlen = ws.length; - - for(; i < len; i++){ - s = gs[i].childNodes[2]; - s.style.width = tw; - s.firstChild.style.width = tw; - cells = s.firstChild.rows[0].childNodes; - for(j = 0; j < wlen; j++){ - cells[j].style.width = ws[j]; - } - } - }, - - doHidden : function(col, hidden, tw){ - if(!this.isGrouped()){ - return; - } - var gs = this.view.getGroups(), - len = gs.length, - i = 0, - s, - display = hidden ? 'none' : ''; - for(; i < len; i++){ - s = gs[i].childNodes[2]; - s.style.width = tw; - s.firstChild.style.width = tw; - s.firstChild.rows[0].childNodes[col].style.display = display; - } - }, - - isGrouped : function(){ - return !Ext.isEmpty(this.grid.getStore().groupField); - }, - - // Note: requires that all (or the first) record in the - // group share the same group value. Returns false if the group - // could not be found. - refreshSummary : function(groupValue){ - return this.refreshSummaryById(this.view.getGroupId(groupValue)); - }, - - getSummaryNode : function(gid){ - var g = Ext.fly(gid, '_gsummary'); - if(g){ - return g.down('.x-grid3-summary-row', true); - } - return null; - }, - - refreshSummaryById : function(gid){ - var g = Ext.getDom(gid); - if(!g){ - return false; - } - var rs = []; - this.grid.getStore().each(function(r){ - if(r._groupId == gid){ - rs[rs.length] = r; - } - }); - var cs = this.view.getColumnData(), - data = this.calculate(rs, cs), - markup = this.renderSummary({data: data}, cs), - existing = this.getSummaryNode(gid); - - if(existing){ - g.removeChild(existing); - } - Ext.DomHelper.append(g, markup); - return true; - }, - - doUpdate : function(ds, record){ - this.refreshSummaryById(record._groupId); - }, - - doRemove : function(ds, record, index, isUpdate){ - if(!isUpdate){ - this.refreshSummaryById(record._groupId); - } - }, - - /** - * Show a message in the summary row. - *
    
    -grid.on('afteredit', function(){
    -    var groupValue = 'Ext Forms: Field Anchoring';
    -    summary.showSummaryMsg(groupValue, 'Updating Summary...');
    -});
    -     * 
    - * @param {String} groupValue - * @param {String} msg Text to use as innerHTML for the summary row. - */ - showSummaryMsg : function(groupValue, msg){ - var gid = this.view.getGroupId(groupValue), - node = this.getSummaryNode(gid); - if(node){ - node.innerHTML = '
    ' + msg + '
    '; - } - } -}); - -//backwards compat -Ext.grid.GroupSummary = Ext.ux.grid.GroupSummary; - - -/** - * Calculation types for summary row:

      - *
    • sum :
    • - *
    • count :
    • - *
    • max :
    • - *
    • min :
    • - *
    • average :
    • - *
    - *

    Custom calculations may be implemented. An example of - * custom summaryType=totalCost:

    
    -// define a custom summary function
    -Ext.ux.grid.GroupSummary.Calculations['totalCost'] = function(v, record, field){
    -    return v + (record.data.estimate * record.data.rate);
    -};
    - * 
    - * @property Calculations - */ - -Ext.ux.grid.GroupSummary.Calculations = { - 'sum' : function(v, record, field){ - return v + (record.data[field]||0); - }, - - 'count' : function(v, record, field, data){ - return data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1); - }, - - 'max' : function(v, record, field, data){ - var v = record.data[field]; - var max = data[field+'max'] === undefined ? (data[field+'max'] = v) : data[field+'max']; - return v > max ? (data[field+'max'] = v) : max; - }, - - 'min' : function(v, record, field, data){ - var v = record.data[field]; - var min = data[field+'min'] === undefined ? (data[field+'min'] = v) : data[field+'min']; - return v < min ? (data[field+'min'] = v) : min; - }, - - 'average' : function(v, record, field, data){ - var c = data[field+'count'] ? ++data[field+'count'] : (data[field+'count'] = 1); - var t = (data[field+'total'] = ((data[field+'total']||0) + (record.data[field]||0))); - return t === 0 ? 0 : t / c; - } -}; -Ext.grid.GroupSummary.Calculations = Ext.ux.grid.GroupSummary.Calculations; - -/** - * @class Ext.ux.grid.HybridSummary - * @extends Ext.ux.grid.GroupSummary - * Adds capability to specify the summary data for the group via json as illustrated here: - *
    
    -{
    -    data: [
    -        {
    -            projectId: 100,     project: 'House',
    -            taskId:    112, description: 'Paint',
    -            estimate:    6,        rate:     150,
    -            due:'06/24/2007'
    -        },
    -        ...
    -    ],
    -
    -    summaryData: {
    -        'House': {
    -            description: 14, estimate: 9,
    -                   rate: 99, due: new Date(2009, 6, 29),
    -                   cost: 999
    -        }
    -    }
    -}
    - * 
    - * - */ -Ext.ux.grid.HybridSummary = Ext.extend(Ext.ux.grid.GroupSummary, { - /** - * @private - * @param {Object} rs - * @param {Object} cs - */ - calculate : function(rs, cs){ - var gcol = this.view.getGroupField(), - gvalue = rs[0].data[gcol], - gdata = this.getSummaryData(gvalue); - return gdata || Ext.ux.grid.HybridSummary.superclass.calculate.call(this, rs, cs); - }, - - /** - *
    
    -grid.on('afteredit', function(){
    -    var groupValue = 'Ext Forms: Field Anchoring';
    -    summary.showSummaryMsg(groupValue, 'Updating Summary...');
    -    setTimeout(function(){ // simulate server call
    -        // HybridSummary class implements updateSummaryData
    -        summary.updateSummaryData(groupValue,
    -            // create data object based on configured dataIndex
    -            {description: 22, estimate: 888, rate: 888, due: new Date(), cost: 8});
    -    }, 2000);
    -});
    -     * 
    - * @param {String} groupValue - * @param {Object} data data object - * @param {Boolean} skipRefresh (Optional) Defaults to false - */ - updateSummaryData : function(groupValue, data, skipRefresh){ - var json = this.grid.getStore().reader.jsonData; - if(!json.summaryData){ - json.summaryData = {}; - } - json.summaryData[groupValue] = data; - if(!skipRefresh){ - this.refreshSummary(groupValue); - } - }, - - /** - * Returns the summaryData for the specified groupValue or null. - * @param {String} groupValue - * @return {Object} summaryData - */ - getSummaryData : function(groupValue){ - var reader = this.grid.getStore().reader, - json = reader.jsonData, - fields = reader.recordType.prototype.fields, - v; - - if(json && json.summaryData){ - v = json.summaryData[groupValue]; - if(v){ - return reader.extractValues(v, fields.items, fields.length); - } - } - return null; - } -}); - -//backwards compat -Ext.grid.HybridSummary = Ext.ux.grid.HybridSummary; -Ext.ux.GroupTab = Ext.extend(Ext.Container, { - mainItem: 0, - - expanded: true, - - deferredRender: true, - - activeTab: null, - - idDelimiter: '__', - - headerAsText: false, - - frame: false, - - hideBorders: true, - - initComponent: function(config){ - Ext.apply(this, config); - this.frame = false; - - Ext.ux.GroupTab.superclass.initComponent.call(this); - - this.addEvents('activate', 'deactivate', 'changemainitem', 'beforetabchange', 'tabchange'); - - this.setLayout(new Ext.layout.CardLayout({ - deferredRender: this.deferredRender - })); - - if (!this.stack) { - this.stack = Ext.TabPanel.AccessStack(); - } - - this.initItems(); - - this.on('beforerender', function(){ - this.groupEl = this.ownerCt.getGroupEl(this); - }, this); - - this.on('add', this.onAdd, this, { - target: this - }); - this.on('remove', this.onRemove, this, { - target: this - }); - - if (this.mainItem !== undefined) { - var item = (typeof this.mainItem == 'object') ? this.mainItem : this.items.get(this.mainItem); - delete this.mainItem; - this.setMainItem(item); - } - }, - - /** - * Sets the specified tab as the active tab. This method fires the {@link #beforetabchange} event which - * can return false to cancel the tab change. - * @param {String/Panel} tab The id or tab Panel to activate - */ - setActiveTab : function(item){ - item = this.getComponent(item); - if(!item){ - return false; - } - if(!this.rendered){ - this.activeTab = item; - return true; - } - if(this.activeTab != item && this.fireEvent('beforetabchange', this, item, this.activeTab) !== false){ - if(this.activeTab && this.activeTab != this.mainItem){ - var oldEl = this.getTabEl(this.activeTab); - if(oldEl){ - Ext.fly(oldEl).removeClass('x-grouptabs-strip-active'); - } - } - var el = this.getTabEl(item); - Ext.fly(el).addClass('x-grouptabs-strip-active'); - this.activeTab = item; - this.stack.add(item); - - this.layout.setActiveItem(item); - if(this.layoutOnTabChange && item.doLayout){ - item.doLayout(); - } - if(this.scrolling){ - this.scrollToTab(item, this.animScroll); - } - - this.fireEvent('tabchange', this, item); - return true; - } - return false; - }, - - getTabEl: function(item){ - if (item == this.mainItem) { - return this.groupEl; - } - return Ext.TabPanel.prototype.getTabEl.call(this, item); - }, - - onRender: function(ct, position){ - Ext.ux.GroupTab.superclass.onRender.call(this, ct, position); - - this.strip = Ext.fly(this.groupEl).createChild({ - tag: 'ul', - cls: 'x-grouptabs-sub' - }); - - this.tooltip = new Ext.ToolTip({ - target: this.groupEl, - delegate: 'a.x-grouptabs-text', - trackMouse: true, - renderTo: document.body, - listeners: { - beforeshow: function(tip) { - var item = (tip.triggerElement.parentNode === this.mainItem.tabEl) - ? this.mainItem - : this.findById(tip.triggerElement.parentNode.id.split(this.idDelimiter)[1]); - - if(!item.tabTip) { - return false; - } - tip.body.dom.innerHTML = item.tabTip; - }, - scope: this - } - }); - - if (!this.itemTpl) { - var tt = new Ext.Template('
  • ', '{text}', '
  • '); - tt.disableFormats = true; - tt.compile(); - Ext.ux.GroupTab.prototype.itemTpl = tt; - } - - this.items.each(this.initTab, this); - }, - - afterRender: function(){ - Ext.ux.GroupTab.superclass.afterRender.call(this); - - if (this.activeTab !== undefined) { - var item = (typeof this.activeTab == 'object') ? this.activeTab : this.items.get(this.activeTab); - delete this.activeTab; - this.setActiveTab(item); - } - }, - - // private - initTab: function(item, index){ - var before = this.strip.dom.childNodes[index]; - var p = Ext.TabPanel.prototype.getTemplateArgs.call(this, item); - - if (item === this.mainItem) { - item.tabEl = this.groupEl; - p.cls += ' x-grouptabs-main-item'; - } - - var el = before ? this.itemTpl.insertBefore(before, p) : this.itemTpl.append(this.strip, p); - - item.tabEl = item.tabEl || el; - - item.on('disable', this.onItemDisabled, this); - item.on('enable', this.onItemEnabled, this); - item.on('titlechange', this.onItemTitleChanged, this); - item.on('iconchange', this.onItemIconChanged, this); - item.on('beforeshow', this.onBeforeShowItem, this); - }, - - setMainItem: function(item){ - item = this.getComponent(item); - if (!item || this.fireEvent('changemainitem', this, item, this.mainItem) === false) { - return; - } - - this.mainItem = item; - }, - - getMainItem: function(){ - return this.mainItem || null; - }, - - // private - onBeforeShowItem: function(item){ - if (item != this.activeTab) { - this.setActiveTab(item); - return false; - } - }, - - // private - onAdd: function(gt, item, index){ - if (this.rendered) { - this.initTab.call(this, item, index); - } - }, - - // private - onRemove: function(tp, item){ - Ext.destroy(Ext.get(this.getTabEl(item))); - this.stack.remove(item); - item.un('disable', this.onItemDisabled, this); - item.un('enable', this.onItemEnabled, this); - item.un('titlechange', this.onItemTitleChanged, this); - item.un('iconchange', this.onItemIconChanged, this); - item.un('beforeshow', this.onBeforeShowItem, this); - if (item == this.activeTab) { - var next = this.stack.next(); - if (next) { - this.setActiveTab(next); - } - else if (this.items.getCount() > 0) { - this.setActiveTab(0); - } - else { - this.activeTab = null; - } - } - }, - - // private - onBeforeAdd: function(item){ - var existing = item.events ? (this.items.containsKey(item.getItemId()) ? item : null) : this.items.get(item); - if (existing) { - this.setActiveTab(item); - return false; - } - Ext.TabPanel.superclass.onBeforeAdd.apply(this, arguments); - var es = item.elements; - item.elements = es ? es.replace(',header', '') : es; - item.border = (item.border === true); - }, - - // private - onItemDisabled: Ext.TabPanel.prototype.onItemDisabled, - onItemEnabled: Ext.TabPanel.prototype.onItemEnabled, - - // private - onItemTitleChanged: function(item){ - var el = this.getTabEl(item); - if (el) { - Ext.fly(el).child('a.x-grouptabs-text', true).innerHTML = item.title; - } - }, - - //private - onItemIconChanged: function(item, iconCls, oldCls){ - var el = this.getTabEl(item); - if (el) { - Ext.fly(el).child('a.x-grouptabs-text').replaceClass(oldCls, iconCls); - } - }, - - beforeDestroy: function(){ - Ext.TabPanel.prototype.beforeDestroy.call(this); - this.tooltip.destroy(); - } -}); - -Ext.reg('grouptab', Ext.ux.GroupTab); -Ext.ns('Ext.ux'); - -Ext.ux.GroupTabPanel = Ext.extend(Ext.TabPanel, { - tabPosition: 'left', - - alternateColor: false, - - alternateCls: 'x-grouptabs-panel-alt', - - defaultType: 'grouptab', - - deferredRender: false, - - activeGroup : null, - - initComponent: function(){ - Ext.ux.GroupTabPanel.superclass.initComponent.call(this); - - this.addEvents( - 'beforegroupchange', - 'groupchange' - ); - this.elements = 'body,header'; - this.stripTarget = 'header'; - - this.tabPosition = this.tabPosition == 'right' ? 'right' : 'left'; - - this.addClass('x-grouptabs-panel'); - - if (this.tabStyle && this.tabStyle != '') { - this.addClass('x-grouptabs-panel-' + this.tabStyle); - } - - if (this.alternateColor) { - this.addClass(this.alternateCls); - } - - this.on('beforeadd', function(gtp, item, index){ - this.initGroup(item, index); - }); - this.items.each(function(item){ - item.on('tabchange',function(item){ - this.fireEvent('tabchange', this, item.activeTab); - }, this); - },this); - }, - - initEvents : function() { - this.mon(this.strip, 'mousedown', this.onStripMouseDown, this); - }, - - onRender: function(ct, position){ - Ext.TabPanel.superclass.onRender.call(this, ct, position); - if(this.plain){ - var pos = this.tabPosition == 'top' ? 'header' : 'footer'; - this[pos].addClass('x-tab-panel-'+pos+'-plain'); - } - - var st = this[this.stripTarget]; - - this.stripWrap = st.createChild({cls:'x-tab-strip-wrap ', cn:{ - tag:'ul', cls:'x-grouptabs-strip x-grouptabs-tab-strip-'+this.tabPosition}}); - - var beforeEl = (this.tabPosition=='bottom' ? this.stripWrap : null); - this.strip = new Ext.Element(this.stripWrap.dom.firstChild); - - this.header.addClass('x-grouptabs-panel-header'); - this.bwrap.addClass('x-grouptabs-bwrap'); - this.body.addClass('x-tab-panel-body-'+this.tabPosition + ' x-grouptabs-panel-body'); - - if (!this.groupTpl) { - var tt = new Ext.Template( - '
  • ', - '', - '', - '{text}', - '
  • ' - ); - tt.disableFormats = true; - tt.compile(); - Ext.ux.GroupTabPanel.prototype.groupTpl = tt; - } - this.items.each(this.initGroup, this); - }, - - afterRender: function(){ - Ext.ux.GroupTabPanel.superclass.afterRender.call(this); - - this.tabJoint = Ext.fly(this.body.dom.parentNode).createChild({ - cls: 'x-tab-joint' - }); - - this.addClass('x-tab-panel-' + this.tabPosition); - this.header.setWidth(this.tabWidth); - - if (this.activeGroup !== undefined) { - var group = (typeof this.activeGroup == 'object') ? this.activeGroup : this.items.get(this.activeGroup); - delete this.activeGroup; - this.setActiveGroup(group); - group.setActiveTab(group.getMainItem()); - } - }, - - getGroupEl : Ext.TabPanel.prototype.getTabEl, - - // private - findTargets: function(e){ - var item = null, - itemEl = e.getTarget('li', this.strip); - if (itemEl) { - item = this.findById(itemEl.id.split(this.idDelimiter)[1]); - if (item.disabled) { - return { - expand: null, - item: null, - el: null - }; - } - } - return { - expand: e.getTarget('.x-grouptabs-expand', this.strip), - isGroup: !e.getTarget('ul.x-grouptabs-sub', this.strip), - item: item, - el: itemEl - }; - }, - - // private - onStripMouseDown: function(e){ - if (e.button != 0) { - return; - } - e.preventDefault(); - var t = this.findTargets(e); - if (t.expand) { - this.toggleGroup(t.el); - } - else if (t.item) { - if(t.isGroup) { - t.item.setActiveTab(t.item.getMainItem()); - } - else { - t.item.ownerCt.setActiveTab(t.item); - } - } - }, - - expandGroup: function(groupEl){ - if(groupEl.isXType) { - groupEl = this.getGroupEl(groupEl); - } - Ext.fly(groupEl).addClass('x-grouptabs-expanded'); - this.syncTabJoint(); - }, - - toggleGroup: function(groupEl){ - if(groupEl.isXType) { - groupEl = this.getGroupEl(groupEl); - } - Ext.fly(groupEl).toggleClass('x-grouptabs-expanded'); - this.syncTabJoint(); - }, - - collapseGroup: function(groupEl){ - if(groupEl.isXType) { - groupEl = this.getGroupEl(groupEl); - } - Ext.fly(groupEl).removeClass('x-grouptabs-expanded'); - this.syncTabJoint(); - }, - - syncTabJoint: function(groupEl){ - if (!this.tabJoint) { - return; - } - - groupEl = groupEl || this.getGroupEl(this.activeGroup); - if(groupEl) { - this.tabJoint.setHeight(Ext.fly(groupEl).getHeight() - 2); - - var y = Ext.isGecko2 ? 0 : 1; - if (this.tabPosition == 'left'){ - this.tabJoint.alignTo(groupEl, 'tl-tr', [-2,y]); - } - else { - this.tabJoint.alignTo(groupEl, 'tr-tl', [1,y]); - } - } - else { - this.tabJoint.hide(); - } - }, - - getActiveTab : function() { - if(!this.activeGroup) return null; - return this.activeGroup.getTabEl(this.activeGroup.activeTab) || null; - }, - - onResize: function(){ - Ext.ux.GroupTabPanel.superclass.onResize.apply(this, arguments); - this.syncTabJoint(); - }, - - createCorner: function(el, pos){ - return Ext.fly(el).createChild({ - cls: 'x-grouptabs-corner x-grouptabs-corner-' + pos - }); - }, - - initGroup: function(group, index){ - var before = this.strip.dom.childNodes[index], - p = this.getTemplateArgs(group); - if (index === 0) { - p.cls += ' x-tab-first'; - } - p.cls += ' x-grouptabs-main'; - p.text = group.getMainItem().title; - - var el = before ? this.groupTpl.insertBefore(before, p) : this.groupTpl.append(this.strip, p), - tl = this.createCorner(el, 'top-' + this.tabPosition), - bl = this.createCorner(el, 'bottom-' + this.tabPosition); - - group.tabEl = el; - if (group.expanded) { - this.expandGroup(el); - } - - if (Ext.isIE6 || (Ext.isIE && !Ext.isStrict)){ - bl.setLeft('-10px'); - bl.setBottom('-5px'); - tl.setLeft('-10px'); - tl.setTop('-5px'); - } - - this.mon(group, { - scope: this, - changemainitem: this.onGroupChangeMainItem, - beforetabchange: this.onGroupBeforeTabChange - }); - }, - - setActiveGroup : function(group) { - group = this.getComponent(group); - if(!group){ - return false; - } - if(!this.rendered){ - this.activeGroup = group; - return true; - } - if(this.activeGroup != group && this.fireEvent('beforegroupchange', this, group, this.activeGroup) !== false){ - if(this.activeGroup){ - this.activeGroup.activeTab = null; - var oldEl = this.getGroupEl(this.activeGroup); - if(oldEl){ - Ext.fly(oldEl).removeClass('x-grouptabs-strip-active'); - } - } - - var groupEl = this.getGroupEl(group); - Ext.fly(groupEl).addClass('x-grouptabs-strip-active'); - - this.activeGroup = group; - this.stack.add(group); - - this.layout.setActiveItem(group); - this.syncTabJoint(groupEl); - - this.fireEvent('groupchange', this, group); - return true; - } - return false; - }, - - onGroupBeforeTabChange: function(group, newTab, oldTab){ - if(group !== this.activeGroup || newTab !== oldTab) { - this.strip.select('.x-grouptabs-sub > li.x-grouptabs-strip-active', true).removeClass('x-grouptabs-strip-active'); - } - this.expandGroup(this.getGroupEl(group)); - if(group !== this.activeGroup) { - return this.setActiveGroup(group); - } - }, - - getFrameHeight: function(){ - var h = this.el.getFrameWidth('tb'); - h += (this.tbar ? this.tbar.getHeight() : 0) + - (this.bbar ? this.bbar.getHeight() : 0); - - return h; - }, - - adjustBodyWidth: function(w){ - return w - this.tabWidth; - } -}); - -Ext.reg('grouptabpanel', Ext.ux.GroupTabPanel); -/* - * Note that this control will most likely remain as an example, and not as a core Ext form - * control. However, the API will be changing in a future release and so should not yet be - * treated as a final, stable API at this time. - */ - -/** - * @class Ext.ux.form.ItemSelector - * @extends Ext.form.Field - * A control that allows selection of between two Ext.ux.form.MultiSelect controls. - * - * @history - * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams) - * - * @constructor - * Create a new ItemSelector - * @param {Object} config Configuration options - * @xtype itemselector - */ -Ext.ux.form.ItemSelector = Ext.extend(Ext.form.Field, { - hideNavIcons:false, - imagePath:"", - iconUp:"up2.gif", - iconDown:"down2.gif", - iconLeft:"left2.gif", - iconRight:"right2.gif", - iconTop:"top2.gif", - iconBottom:"bottom2.gif", - drawUpIcon:true, - drawDownIcon:true, - drawLeftIcon:true, - drawRightIcon:true, - drawTopIcon:true, - drawBotIcon:true, - delimiter:',', - bodyStyle:null, - border:false, - defaultAutoCreate:{tag: "div"}, - /** - * @cfg {Array} multiselects An array of {@link Ext.ux.form.MultiSelect} config objects, with at least all required parameters (e.g., store) - */ - multiselects:null, - - initComponent: function(){ - Ext.ux.form.ItemSelector.superclass.initComponent.call(this); - this.addEvents({ - 'rowdblclick' : true, - 'change' : true - }); - }, - - onRender: function(ct, position){ - Ext.ux.form.ItemSelector.superclass.onRender.call(this, ct, position); - - // Internal default configuration for both multiselects - var msConfig = [{ - legend: 'Available', - draggable: true, - droppable: true, - width: 100, - height: 100 - },{ - legend: 'Selected', - droppable: true, - draggable: true, - width: 100, - height: 100 - }]; - - this.fromMultiselect = new Ext.ux.form.MultiSelect(Ext.applyIf(this.multiselects[0], msConfig[0])); - this.fromMultiselect.on('dblclick', this.onRowDblClick, this); - - this.toMultiselect = new Ext.ux.form.MultiSelect(Ext.applyIf(this.multiselects[1], msConfig[1])); - this.toMultiselect.on('dblclick', this.onRowDblClick, this); - - var p = new Ext.Panel({ - bodyStyle:this.bodyStyle, - border:this.border, - layout:"table", - layoutConfig:{columns:3} - }); - - p.add(this.fromMultiselect); - var icons = new Ext.Panel({header:false}); - p.add(icons); - p.add(this.toMultiselect); - p.render(this.el); - icons.el.down('.'+icons.bwrapCls).remove(); - - // ICON HELL!!! - if (this.imagePath!="" && this.imagePath.charAt(this.imagePath.length-1)!="/") - this.imagePath+="/"; - this.iconUp = this.imagePath + (this.iconUp || 'up2.gif'); - this.iconDown = this.imagePath + (this.iconDown || 'down2.gif'); - this.iconLeft = this.imagePath + (this.iconLeft || 'left2.gif'); - this.iconRight = this.imagePath + (this.iconRight || 'right2.gif'); - this.iconTop = this.imagePath + (this.iconTop || 'top2.gif'); - this.iconBottom = this.imagePath + (this.iconBottom || 'bottom2.gif'); - var el=icons.getEl(); - this.toTopIcon = el.createChild({tag:'img', src:this.iconTop, style:{cursor:'pointer', margin:'2px'}}); - el.createChild({tag: 'br'}); - this.upIcon = el.createChild({tag:'img', src:this.iconUp, style:{cursor:'pointer', margin:'2px'}}); - el.createChild({tag: 'br'}); - this.addIcon = el.createChild({tag:'img', src:this.iconRight, style:{cursor:'pointer', margin:'2px'}}); - el.createChild({tag: 'br'}); - this.removeIcon = el.createChild({tag:'img', src:this.iconLeft, style:{cursor:'pointer', margin:'2px'}}); - el.createChild({tag: 'br'}); - this.downIcon = el.createChild({tag:'img', src:this.iconDown, style:{cursor:'pointer', margin:'2px'}}); - el.createChild({tag: 'br'}); - this.toBottomIcon = el.createChild({tag:'img', src:this.iconBottom, style:{cursor:'pointer', margin:'2px'}}); - this.toTopIcon.on('click', this.toTop, this); - this.upIcon.on('click', this.up, this); - this.downIcon.on('click', this.down, this); - this.toBottomIcon.on('click', this.toBottom, this); - this.addIcon.on('click', this.fromTo, this); - this.removeIcon.on('click', this.toFrom, this); - if (!this.drawUpIcon || this.hideNavIcons) { this.upIcon.dom.style.display='none'; } - if (!this.drawDownIcon || this.hideNavIcons) { this.downIcon.dom.style.display='none'; } - if (!this.drawLeftIcon || this.hideNavIcons) { this.addIcon.dom.style.display='none'; } - if (!this.drawRightIcon || this.hideNavIcons) { this.removeIcon.dom.style.display='none'; } - if (!this.drawTopIcon || this.hideNavIcons) { this.toTopIcon.dom.style.display='none'; } - if (!this.drawBotIcon || this.hideNavIcons) { this.toBottomIcon.dom.style.display='none'; } - - var tb = p.body.first(); - this.el.setWidth(p.body.first().getWidth()); - p.body.removeClass(); - - this.hiddenName = this.name; - var hiddenTag = {tag: "input", type: "hidden", value: "", name: this.name}; - this.hiddenField = this.el.createChild(hiddenTag); - }, - - doLayout: function(){ - if(this.rendered){ - this.fromMultiselect.fs.doLayout(); - this.toMultiselect.fs.doLayout(); - } - }, - - afterRender: function(){ - Ext.ux.form.ItemSelector.superclass.afterRender.call(this); - - this.toStore = this.toMultiselect.store; - this.toStore.on('add', this.valueChanged, this); - this.toStore.on('remove', this.valueChanged, this); - this.toStore.on('load', this.valueChanged, this); - this.valueChanged(this.toStore); - }, - - toTop : function() { - var selectionsArray = this.toMultiselect.view.getSelectedIndexes(); - var records = []; - if (selectionsArray.length > 0) { - selectionsArray.sort(); - for (var i=0; i-1; i--) { - record = records[i]; - this.toMultiselect.view.store.remove(record); - this.toMultiselect.view.store.insert(0, record); - selectionsArray.push(((records.length - 1) - i)); - } - } - this.toMultiselect.view.refresh(); - this.toMultiselect.view.select(selectionsArray); - }, - - toBottom : function() { - var selectionsArray = this.toMultiselect.view.getSelectedIndexes(); - var records = []; - if (selectionsArray.length > 0) { - selectionsArray.sort(); - for (var i=0; i 0) { - for (var i=0; i= 0) { - this.toMultiselect.view.store.remove(record); - this.toMultiselect.view.store.insert(selectionsArray[i] - 1, record); - newSelectionsArray.push(selectionsArray[i] - 1); - } - } - this.toMultiselect.view.refresh(); - this.toMultiselect.view.select(newSelectionsArray); - } - }, - - down : function() { - var record = null; - var selectionsArray = this.toMultiselect.view.getSelectedIndexes(); - selectionsArray.sort(); - selectionsArray.reverse(); - var newSelectionsArray = []; - if (selectionsArray.length > 0) { - for (var i=0; i 0) { - for (var i=0; i 0) { - for (var i=0; i', - '
    ', - '
    {lockedHeader}
    ', - '
    {lockedBody}
    ', - '
    ', - '
    ', - '
    {header}
    ', - '
    {body}
    ', - '
    ', - '
     
    ', - '
     
    ', - '' - ); - } - - this.templates = ts; - - Ext.ux.grid.LockingGridView.superclass.initTemplates.call(this); - }, - - getEditorParent : function(ed){ - return this.el.dom; - }, - - initElements : function(){ - var el = Ext.get(this.grid.getGridEl().dom.firstChild), - lockedWrap = el.child('div.x-grid3-locked'), - lockedHd = lockedWrap.child('div.x-grid3-header'), - lockedScroller = lockedWrap.child('div.x-grid3-scroller'), - mainWrap = el.child('div.x-grid3-viewport'), - mainHd = mainWrap.child('div.x-grid3-header'), - scroller = mainWrap.child('div.x-grid3-scroller'); - - if (this.grid.hideHeaders) { - lockedHd.setDisplayed(false); - mainHd.setDisplayed(false); - } - - if(this.forceFit){ - scroller.setStyle('overflow-x', 'hidden'); - } - - Ext.apply(this, { - el : el, - mainWrap: mainWrap, - mainHd : mainHd, - innerHd : mainHd.dom.firstChild, - scroller: scroller, - mainBody: scroller.child('div.x-grid3-body'), - focusEl : scroller.child('a'), - resizeMarker: el.child('div.x-grid3-resize-marker'), - resizeProxy : el.child('div.x-grid3-resize-proxy'), - lockedWrap: lockedWrap, - lockedHd: lockedHd, - lockedScroller: lockedScroller, - lockedBody: lockedScroller.child('div.x-grid3-body'), - lockedInnerHd: lockedHd.child('div.x-grid3-header-inner', true) - }); - - 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 lockedLen = this.cm.getLockedCount(); - if(col < lockedLen){ - return this.getLockedRow(row).getElementsByTagName('td')[col]; - } - return Ext.ux.grid.LockingGridView.superclass.getCell.call(this, row, col - lockedLen); - }, - - getHeaderCell : function(index){ - var lockedLen = this.cm.getLockedCount(); - if(index < lockedLen){ - return this.lockedHd.dom.getElementsByTagName('td')[index]; - } - return Ext.ux.grid.LockingGridView.superclass.getHeaderCell.call(this, index - lockedLen); - }, - - addRowClass : function(row, cls){ - var lockedRow = this.getLockedRow(row); - if(lockedRow){ - this.fly(lockedRow).addClass(cls); - } - Ext.ux.grid.LockingGridView.superclass.addRowClass.call(this, row, cls); - }, - - removeRowClass : function(row, cls){ - var lockedRow = this.getLockedRow(row); - if(lockedRow){ - this.fly(lockedRow).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 lockedBody = this.lockedBody.dom, - rowIndex = firstRow; - for(; rowIndex <= lastRow; rowIndex++){ - Ext.removeNode(lockedBody.childNodes[firstRow]); - } - Ext.ux.grid.LockingGridView.superclass.removeRows.call(this, firstRow, lastRow); - }, - - syncScroll : function(e){ - this.lockedScroller.dom.scrollTop = this.scroller.dom.scrollTop; - Ext.ux.grid.LockingGridView.superclass.syncScroll.call(this, e); - }, - - updateSortIcon : function(col, dir){ - var sortClasses = this.sortClasses, - lockedHeaders = this.lockedHd.select('td').removeClass(sortClasses), - headers = this.mainHd.select('td').removeClass(sortClasses), - lockedLen = this.cm.getLockedCount(), - cls = sortClasses[dir == 'DESC' ? 1 : 0]; - - if(col < lockedLen){ - lockedHeaders.item(col).addClass(cls); - }else{ - headers.item(col - lockedLen).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 = ' '; - } - 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'; - } - } - this.syncRowHeights(row, lrow); - } - 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); - }, - - syncRowHeights: function(row1, row2){ - if(this.syncHeights){ - var el1 = Ext.get(row1), - el2 = Ext.get(row2), - h1 = el1.getHeight(), - h2 = el2.getHeight(); - - if(h1 > h2){ - el2.setHeight(h1); - }else if(h2 > h1){ - el1.setHeight(h2); - } - } - }, - - afterRender : function(){ - if(!this.ds || !this.cm){ - return; - } - var bd = this.renderRows() || [' ', ' ']; - this.mainBody.dom.innerHTML = bd[0]; - this.lockedBody.dom.innerHTML = bd[1]; - this.processRows(0, true); - if(this.deferEmptyText !== true){ - this.applyEmptyText(); - } - this.grid.fireEvent('viewready', this.grid); - }, - - renderUI : function(){ - var templates = this.templates, - header = this.renderHeaders(), - body = templates.body.apply({rows:' '}); - - return templates.masterTpl.apply({ - body : body, - header: header[0], - ostyle: 'width:' + this.getOffsetWidth() + ';', - bstyle: 'width:' + this.getTotalWidth() + ';', - lockedBody: body, - lockedHeader: header[1], - lstyle: 'width:'+this.getLockedWidth()+';' - }); - }, - - afterRenderUI: function(){ - var g = this.grid; - 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() || [' ', ' ']; - return [this.templates.body.apply({rows: markup[0]}), this.templates.body.apply({rows: markup[1]})]; - }, - - refreshRow: function(record){ - var store = this.ds, - colCount = this.cm.getColumnCount(), - columns = this.getColumnData(), - last = colCount - 1, - cls = ['x-grid3-row'], - rowParams = { - tstyle: String.format("width: {0};", this.getTotalWidth()) - }, - lockedRowParams = { - tstyle: String.format("width: {0};", this.getLockedWidth()) - }, - colBuffer = [], - lockedColBuffer = [], - cellTpl = this.templates.cell, - rowIndex, - row, - lockedRow, - column, - meta, - css, - i; - - if (Ext.isNumber(record)) { - rowIndex = record; - record = store.getAt(rowIndex); - } else { - rowIndex = store.indexOf(record); - } - - if (!record || rowIndex < 0) { - return; - } - - for (i = 0; i < colCount; i++) { - column = columns[i]; - - if (i == 0) { - css = 'x-grid3-cell-first'; - } else { - css = (i == last) ? 'x-grid3-cell-last ' : ''; - } - - meta = { - id: column.id, - style: column.style, - css: css, - attr: "", - cellAttr: "" - }; - - meta.value = column.renderer.call(column.scope, record.data[column.name], meta, record, rowIndex, i, store); - - if (Ext.isEmpty(meta.value)) { - meta.value = ' '; - } - - if (this.markDirty && record.dirty && typeof record.modified[column.name] != 'undefined') { - meta.css += ' x-grid3-dirty-cell'; - } - - if (column.locked) { - lockedColBuffer[i] = cellTpl.apply(meta); - } else { - colBuffer[i] = cellTpl.apply(meta); - } - } - - row = this.getRow(rowIndex); - row.className = ''; - lockedRow = this.getLockedRow(rowIndex); - lockedRow.className = ''; - - if (this.grid.stripeRows && ((rowIndex + 1) % 2 === 0)) { - cls.push('x-grid3-row-alt'); - } - - if (this.getRowClass) { - rowParams.cols = colCount; - cls.push(this.getRowClass(record, rowIndex, rowParams, store)); - } - - // Unlocked rows - this.fly(row).addClass(cls).setStyle(rowParams.tstyle); - rowParams.cells = colBuffer.join(""); - row.innerHTML = this.templates.rowInner.apply(rowParams); - - // Locked rows - this.fly(lockedRow).addClass(cls).setStyle(lockedRowParams.tstyle); - lockedRowParams.cells = lockedColBuffer.join(""); - lockedRow.innerHTML = this.templates.rowInner.apply(lockedRowParams); - lockedRow.rowIndex = rowIndex; - this.syncRowHeights(row, lockedRow); - this.fireEvent('rowupdated', this, rowIndex, record); - }, - - 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 undefined; - } - cm.setLocked(index, true); - if(llen != index){ - cm.moveColumn(index, llen); - this.grid.fireEvent('columnmove', index, llen); - } - 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(){ - var hrow = Ext.fly(this.innerHd).child('tr', true), - lhrow = Ext.fly(this.lockedInnerHd).child('tr', true); - - hrow.style.height = 'auto'; - lhrow.style.height = 'auto'; - var hd = hrow.offsetHeight, - lhd = lhrow.offsetHeight, - height = Math.max(lhd, hd) + 'px'; - - hrow.style.height = height; - lhrow.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); - } -});Ext.ns('Ext.ux.form'); - -/** - * @class Ext.ux.form.MultiSelect - * @extends Ext.form.Field - * A control that allows selection and form submission of multiple list items. - * - * @history - * 2008-06-19 bpm Original code contributed by Toby Stuart (with contributions from Robert Williams) - * 2008-06-19 bpm Docs and demo code clean up - * - * @constructor - * Create a new MultiSelect - * @param {Object} config Configuration options - * @xtype multiselect - */ -Ext.ux.form.MultiSelect = Ext.extend(Ext.form.Field, { - /** - * @cfg {String} legend Wraps the object with a fieldset and specified legend. - */ - /** - * @cfg {Ext.ListView} view The {@link Ext.ListView} used to render the multiselect list. - */ - /** - * @cfg {String/Array} dragGroup The ddgroup name(s) for the MultiSelect DragZone (defaults to undefined). - */ - /** - * @cfg {String/Array} dropGroup The ddgroup name(s) for the MultiSelect DropZone (defaults to undefined). - */ - /** - * @cfg {Boolean} ddReorder Whether the items in the MultiSelect list are drag/drop reorderable (defaults to false). - */ - ddReorder:false, - /** - * @cfg {Object/Array} tbar The top toolbar of the control. This can be a {@link Ext.Toolbar} object, a - * toolbar config, or an array of buttons/button configs to be added to the toolbar. - */ - /** - * @cfg {String} appendOnly True if the list should only allow append drops when drag/drop is enabled - * (use for lists which are sorted, defaults to false). - */ - appendOnly:false, - /** - * @cfg {Number} width Width in pixels of the control (defaults to 100). - */ - width:100, - /** - * @cfg {Number} height Height in pixels of the control (defaults to 100). - */ - height:100, - /** - * @cfg {String/Number} displayField Name/Index of the desired display field in the dataset (defaults to 0). - */ - displayField:0, - /** - * @cfg {String/Number} valueField Name/Index of the desired value field in the dataset (defaults to 1). - */ - valueField:1, - /** - * @cfg {Boolean} allowBlank False to require at least one item in the list to be selected, true to allow no - * selection (defaults to true). - */ - allowBlank:true, - /** - * @cfg {Number} minSelections Minimum number of selections allowed (defaults to 0). - */ - minSelections:0, - /** - * @cfg {Number} maxSelections Maximum number of selections allowed (defaults to Number.MAX_VALUE). - */ - maxSelections:Number.MAX_VALUE, - /** - * @cfg {String} blankText Default text displayed when the control contains no items (defaults to the same value as - * {@link Ext.form.TextField#blankText}. - */ - blankText:Ext.form.TextField.prototype.blankText, - /** - * @cfg {String} minSelectionsText Validation message displayed when {@link #minSelections} is not met (defaults to 'Minimum {0} - * item(s) required'). The {0} token will be replaced by the value of {@link #minSelections}. - */ - minSelectionsText:'Minimum {0} item(s) required', - /** - * @cfg {String} maxSelectionsText Validation message displayed when {@link #maxSelections} is not met (defaults to 'Maximum {0} - * item(s) allowed'). The {0} token will be replaced by the value of {@link #maxSelections}. - */ - maxSelectionsText:'Maximum {0} item(s) allowed', - /** - * @cfg {String} delimiter The string used to delimit between items when set or returned as a string of values - * (defaults to ','). - */ - delimiter:',', - /** - * @cfg {Ext.data.Store/Array} store The data source to which this MultiSelect is bound (defaults to undefined). - * Acceptable values for this property are: - *
      - *
    • any {@link Ext.data.Store Store} subclass
    • - *
    • an Array : Arrays will be converted to a {@link Ext.data.ArrayStore} internally. - *
        - *
      • 1-dimensional array : (e.g., ['Foo','Bar'])
        - * A 1-dimensional array will automatically be expanded (each array item will be the combo - * {@link #valueField value} and {@link #displayField text})
      • - *
      • 2-dimensional array : (e.g., [['f','Foo'],['b','Bar']])
        - * For a multi-dimensional array, the value in index 0 of each item will be assumed to be the combo - * {@link #valueField value}, while the value at index 1 is assumed to be the combo {@link #displayField text}. - *
    - */ - - // private - defaultAutoCreate : {tag: "div"}, - - // private - initComponent: function(){ - Ext.ux.form.MultiSelect.superclass.initComponent.call(this); - - if(Ext.isArray(this.store)){ - if (Ext.isArray(this.store[0])){ - this.store = new Ext.data.ArrayStore({ - fields: ['value','text'], - data: this.store - }); - this.valueField = 'value'; - }else{ - this.store = new Ext.data.ArrayStore({ - fields: ['text'], - data: this.store, - expandData: true - }); - this.valueField = 'text'; - } - this.displayField = 'text'; - } else { - this.store = Ext.StoreMgr.lookup(this.store); - } - - this.addEvents({ - 'dblclick' : true, - 'click' : true, - 'change' : true, - 'drop' : true - }); - }, - - // private - onRender: function(ct, position){ - Ext.ux.form.MultiSelect.superclass.onRender.call(this, ct, position); - - var fs = this.fs = new Ext.form.FieldSet({ - renderTo: this.el, - title: this.legend, - height: this.height, - width: this.width, - style: "padding:0;", - tbar: this.tbar - }); - fs.body.addClass('ux-mselect'); - - this.view = new Ext.ListView({ - multiSelect: true, - store: this.store, - columns: [{ header: 'Value', width: 1, dataIndex: this.displayField }], - hideHeaders: true - }); - - fs.add(this.view); - - this.view.on('click', this.onViewClick, this); - this.view.on('beforeclick', this.onViewBeforeClick, this); - this.view.on('dblclick', this.onViewDblClick, this); - - this.hiddenName = this.name || Ext.id(); - var hiddenTag = { tag: "input", type: "hidden", value: "", name: this.hiddenName }; - this.hiddenField = this.el.createChild(hiddenTag); - this.hiddenField.dom.disabled = this.hiddenName != this.name; - fs.doLayout(); - }, - - // private - afterRender: function(){ - Ext.ux.form.MultiSelect.superclass.afterRender.call(this); - - if (this.ddReorder && !this.dragGroup && !this.dropGroup){ - this.dragGroup = this.dropGroup = 'MultiselectDD-' + Ext.id(); - } - - if (this.draggable || this.dragGroup){ - this.dragZone = new Ext.ux.form.MultiSelect.DragZone(this, { - ddGroup: this.dragGroup - }); - } - if (this.droppable || this.dropGroup){ - this.dropZone = new Ext.ux.form.MultiSelect.DropZone(this, { - ddGroup: this.dropGroup - }); - } - }, - - // private - onViewClick: function(vw, index, node, e) { - this.fireEvent('change', this, this.getValue(), this.hiddenField.dom.value); - this.hiddenField.dom.value = this.getValue(); - this.fireEvent('click', this, e); - this.validate(); - }, - - // private - onViewBeforeClick: function(vw, index, node, e) { - if (this.disabled || this.readOnly) { - return false; - } - }, - - // private - onViewDblClick : function(vw, index, node, e) { - return this.fireEvent('dblclick', vw, index, node, e); - }, - - /** - * Returns an array of data values for the selected items in the list. The values will be separated - * by {@link #delimiter}. - * @return {Array} value An array of string data values - */ - getValue: function(valueField){ - var returnArray = []; - var selectionsArray = this.view.getSelectedIndexes(); - if (selectionsArray.length == 0) {return '';} - for (var i=0; i this.maxSelections) { - this.markInvalid(String.format(this.maxSelectionsText, this.maxSelections)); - return false; - } - return true; - }, - - // inherit docs - disable: function(){ - this.disabled = true; - this.hiddenField.dom.disabled = true; - this.fs.disable(); - }, - - // inherit docs - enable: function(){ - this.disabled = false; - this.hiddenField.dom.disabled = false; - this.fs.enable(); - }, - - // inherit docs - destroy: function(){ - Ext.destroy(this.fs, this.dragZone, this.dropZone); - Ext.ux.form.MultiSelect.superclass.destroy.call(this); - } -}); - - -Ext.reg('multiselect', Ext.ux.form.MultiSelect); - -//backwards compat -Ext.ux.Multiselect = Ext.ux.form.MultiSelect; - - -Ext.ux.form.MultiSelect.DragZone = function(ms, config){ - this.ms = ms; - this.view = ms.view; - var ddGroup = config.ddGroup || 'MultiselectDD'; - var dd; - if (Ext.isArray(ddGroup)){ - dd = ddGroup.shift(); - } else { - dd = ddGroup; - ddGroup = null; - } - Ext.ux.form.MultiSelect.DragZone.superclass.constructor.call(this, this.ms.fs.body, { containerScroll: true, ddGroup: dd }); - this.setDraggable(ddGroup); -}; - -Ext.extend(Ext.ux.form.MultiSelect.DragZone, Ext.dd.DragZone, { - onInitDrag : function(x, y){ - var el = Ext.get(this.dragData.ddel.cloneNode(true)); - this.proxy.update(el.dom); - el.setWidth(el.child('em').getWidth()); - this.onStartDrag(x, y); - return true; - }, - - // private - collectSelection: function(data) { - data.repairXY = Ext.fly(this.view.getSelectedNodes()[0]).getXY(); - var i = 0; - this.view.store.each(function(rec){ - if (this.view.isSelected(i)) { - var n = this.view.getNode(i); - var dragNode = n.cloneNode(true); - dragNode.id = Ext.id(); - data.ddel.appendChild(dragNode); - data.records.push(this.view.store.getAt(i)); - data.viewNodes.push(n); - } - i++; - }, this); - }, - - // override - onEndDrag: function(data, e) { - var d = Ext.get(this.dragData.ddel); - if (d && d.hasClass("multi-proxy")) { - d.remove(); - } - }, - - // override - getDragData: function(e){ - var target = this.view.findItemFromChild(e.getTarget()); - if(target) { - if (!this.view.isSelected(target) && !e.ctrlKey && !e.shiftKey) { - this.view.select(target); - this.ms.setValue(this.ms.getValue()); - } - if (this.view.getSelectionCount() == 0 || e.ctrlKey || e.shiftKey) return false; - var dragData = { - sourceView: this.view, - viewNodes: [], - records: [] - }; - if (this.view.getSelectionCount() == 1) { - var i = this.view.getSelectedIndexes()[0]; - var n = this.view.getNode(i); - dragData.viewNodes.push(dragData.ddel = n); - dragData.records.push(this.view.store.getAt(i)); - dragData.repairXY = Ext.fly(n).getXY(); - } else { - dragData.ddel = document.createElement('div'); - dragData.ddel.className = 'multi-proxy'; - this.collectSelection(dragData); - } - return dragData; - } - return false; - }, - - // override the default repairXY. - getRepairXY : function(e){ - return this.dragData.repairXY; - }, - - // private - setDraggable: function(ddGroup){ - if (!ddGroup) return; - if (Ext.isArray(ddGroup)) { - Ext.each(ddGroup, this.setDraggable, this); - return; - } - this.addToGroup(ddGroup); - } -}); - -Ext.ux.form.MultiSelect.DropZone = function(ms, config){ - this.ms = ms; - this.view = ms.view; - var ddGroup = config.ddGroup || 'MultiselectDD'; - var dd; - if (Ext.isArray(ddGroup)){ - dd = ddGroup.shift(); - } else { - dd = ddGroup; - ddGroup = null; - } - Ext.ux.form.MultiSelect.DropZone.superclass.constructor.call(this, this.ms.fs.body, { containerScroll: true, ddGroup: dd }); - this.setDroppable(ddGroup); -}; - -Ext.extend(Ext.ux.form.MultiSelect.DropZone, Ext.dd.DropZone, { - /** - * Part of the Ext.dd.DropZone interface. If no target node is found, the - * whole Element becomes the target, and this causes the drop gesture to append. - */ - getTargetFromEvent : function(e) { - var target = e.getTarget(); - return target; - }, - - // private - getDropPoint : function(e, n, dd){ - if (n == this.ms.fs.body.dom) { return "below"; } - var t = Ext.lib.Dom.getY(n), b = t + n.offsetHeight; - var c = t + (b - t) / 2; - var y = Ext.lib.Event.getPageY(e); - if(y <= c) { - return "above"; - }else{ - return "below"; - } - }, - - // private - isValidDropPoint: function(pt, n, data) { - if (!data.viewNodes || (data.viewNodes.length != 1)) { - return true; - } - var d = data.viewNodes[0]; - if (d == n) { - return false; - } - if ((pt == "below") && (n.nextSibling == d)) { - return false; - } - if ((pt == "above") && (n.previousSibling == d)) { - return false; - } - return true; - }, - - // override - onNodeEnter : function(n, dd, e, data){ - return false; - }, - - // override - onNodeOver : function(n, dd, e, data){ - var dragElClass = this.dropNotAllowed; - var pt = this.getDropPoint(e, n, dd); - if (this.isValidDropPoint(pt, n, data)) { - if (this.ms.appendOnly) { - return "x-tree-drop-ok-below"; - } - - // set the insert point style on the target node - if (pt) { - var targetElClass; - if (pt == "above"){ - dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above"; - targetElClass = "x-view-drag-insert-above"; - } else { - dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below"; - targetElClass = "x-view-drag-insert-below"; - } - if (this.lastInsertClass != targetElClass){ - Ext.fly(n).replaceClass(this.lastInsertClass, targetElClass); - this.lastInsertClass = targetElClass; - } - } - } - return dragElClass; - }, - - // private - onNodeOut : function(n, dd, e, data){ - this.removeDropIndicators(n); - }, - - // private - onNodeDrop : function(n, dd, e, data){ - if (this.ms.fireEvent("drop", this, n, dd, e, data) === false) { - return false; - } - var pt = this.getDropPoint(e, n, dd); - if (n != this.ms.fs.body.dom) - n = this.view.findItemFromChild(n); - - if(this.ms.appendOnly) { - insertAt = this.view.store.getCount(); - } else { - insertAt = n == this.ms.fs.body.dom ? this.view.store.getCount() - 1 : this.view.indexOf(n); - if (pt == "below") { - insertAt++; - } - } - - var dir = false; - - // Validate if dragging within the same MultiSelect - if (data.sourceView == this.view) { - // If the first element to be inserted below is the target node, remove it - if (pt == "below") { - if (data.viewNodes[0] == n) { - data.viewNodes.shift(); - } - } else { // If the last element to be inserted above is the target node, remove it - if (data.viewNodes[data.viewNodes.length - 1] == n) { - data.viewNodes.pop(); - } - } - - // Nothing to drop... - if (!data.viewNodes.length) { - return false; - } - - // If we are moving DOWN, then because a store.remove() takes place first, - // the insertAt must be decremented. - if (insertAt > this.view.store.indexOf(data.records[0])) { - dir = 'down'; - insertAt--; - } - } - - for (var i = 0; i < data.records.length; i++) { - var r = data.records[i]; - if (data.sourceView) { - data.sourceView.store.remove(r); - } - this.view.store.insert(dir == 'down' ? insertAt : insertAt++, r); - var si = this.view.store.sortInfo; - if(si){ - this.view.store.sort(si.field, si.direction); - } - } - return true; - }, - - // private - removeDropIndicators : function(n){ - if(n){ - Ext.fly(n).removeClass([ - "x-view-drag-insert-above", - "x-view-drag-insert-left", - "x-view-drag-insert-right", - "x-view-drag-insert-below"]); - this.lastInsertClass = "_noclass"; - } - }, - - // private - setDroppable: function(ddGroup){ - if (!ddGroup) return; - if (Ext.isArray(ddGroup)) { - Ext.each(ddGroup, this.setDroppable, this); - return; - } - this.addToGroup(ddGroup); - } -}); - -/* Fix for Opera, which does not seem to include the map function on Array's */ -if (!Array.prototype.map) { - Array.prototype.map = function(fun){ - var len = this.length; - if (typeof fun != 'function') { - throw new TypeError(); - } - var res = new Array(len); - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - if (i in this) { - res[i] = fun.call(thisp, this[i], i, this); - } - } - return res; - }; -} - -Ext.ns('Ext.ux.data'); - -/** - * @class Ext.ux.data.PagingMemoryProxy - * @extends Ext.data.MemoryProxy - *

    Paging Memory Proxy, allows to use paging grid with in memory dataset

    - */ -Ext.ux.data.PagingMemoryProxy = Ext.extend(Ext.data.MemoryProxy, { - constructor : function(data){ - Ext.ux.data.PagingMemoryProxy.superclass.constructor.call(this); - this.data = data; - }, - doRequest : function(action, rs, params, reader, callback, scope, options){ - params = params || - {}; - var result; - try { - result = reader.readRecords(this.data); - } - catch (e) { - this.fireEvent('loadexception', this, options, null, e); - callback.call(scope, null, options, false); - return; - } - - // filtering - if (params.filter !== undefined) { - result.records = result.records.filter(function(el){ - if (typeof(el) == 'object') { - var att = params.filterCol || 0; - return String(el.data[att]).match(params.filter) ? true : false; - } - else { - return String(el).match(params.filter) ? true : false; - } - }); - result.totalRecords = result.records.length; - } - - // sorting - if (params.sort !== undefined) { - // use integer as params.sort to specify column, since arrays are not named - // params.sort=0; would also match a array without columns - var dir = String(params.dir).toUpperCase() == 'DESC' ? -1 : 1; - var fn = function(v1, v2){ - return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); - }; - result.records.sort(function(a, b){ - var v = 0; - if (typeof(a) == 'object') { - v = fn(a.data[params.sort], b.data[params.sort]) * dir; - } - else { - v = fn(a, b) * dir; - } - if (v == 0) { - v = (a.index < b.index ? -1 : 1); - } - return v; - }); - } - // paging (use undefined cause start can also be 0 (thus false)) - if (params.start !== undefined && params.limit !== undefined) { - result.records = result.records.slice(params.start, params.start + params.limit); - } - callback.call(scope, result, options, true); - } -}); - -//backwards compat. -Ext.data.PagingMemoryProxy = Ext.ux.data.PagingMemoryProxy; -Ext.ux.PanelResizer = Ext.extend(Ext.util.Observable, { - minHeight: 0, - maxHeight:10000000, - - constructor: function(config){ - Ext.apply(this, config); - this.events = {}; - Ext.ux.PanelResizer.superclass.constructor.call(this, config); - }, - - init : function(p){ - this.panel = p; - - if(this.panel.elements.indexOf('footer')==-1){ - p.elements += ',footer'; - } - p.on('render', this.onRender, this); - }, - - onRender : function(p){ - this.handle = p.footer.createChild({cls:'x-panel-resize'}); - - this.tracker = new Ext.dd.DragTracker({ - onStart: this.onDragStart.createDelegate(this), - onDrag: this.onDrag.createDelegate(this), - onEnd: this.onDragEnd.createDelegate(this), - tolerance: 3, - autoStart: 300 - }); - this.tracker.initEl(this.handle); - p.on('beforedestroy', this.tracker.destroy, this.tracker); - }, - - // private - onDragStart: function(e){ - this.dragging = true; - this.startHeight = this.panel.el.getHeight(); - this.fireEvent('dragstart', this, e); - }, - - // private - onDrag: function(e){ - this.panel.setHeight((this.startHeight-this.tracker.getOffset()[1]).constrain(this.minHeight, this.maxHeight)); - this.fireEvent('drag', this, e); - }, - - // private - onDragEnd: function(e){ - this.dragging = false; - this.fireEvent('dragend', this, e); - } -}); -Ext.preg('panelresizer', Ext.ux.PanelResizer);Ext.ux.Portal = Ext.extend(Ext.Panel, { - layout : 'column', - autoScroll : true, - cls : 'x-portal', - defaultType : 'portalcolumn', - - initComponent : function(){ - Ext.ux.Portal.superclass.initComponent.call(this); - this.addEvents({ - validatedrop:true, - beforedragover:true, - dragover:true, - beforedrop:true, - drop:true - }); - }, - - initEvents : function(){ - Ext.ux.Portal.superclass.initEvents.call(this); - this.dd = new Ext.ux.Portal.DropZone(this, this.dropConfig); - }, - - beforeDestroy : function() { - if(this.dd){ - this.dd.unreg(); - } - Ext.ux.Portal.superclass.beforeDestroy.call(this); - } -}); - -Ext.reg('portal', Ext.ux.Portal); - -Ext.ux.Portal.DropZone = Ext.extend(Ext.dd.DropTarget, { - - constructor : function(portal, cfg){ - this.portal = portal; - Ext.dd.ScrollManager.register(portal.body); - Ext.ux.Portal.DropZone.superclass.constructor.call(this, portal.bwrap.dom, cfg); - portal.body.ddScrollConfig = this.ddScrollConfig; - }, - - ddScrollConfig : { - vthresh: 50, - hthresh: -1, - animate: true, - increment: 200 - }, - - createEvent : function(dd, e, data, col, c, pos){ - return { - portal: this.portal, - panel: data.panel, - columnIndex: col, - column: c, - position: pos, - data: data, - source: dd, - rawEvent: e, - status: this.dropAllowed - }; - }, - - notifyOver : function(dd, e, data){ - var xy = e.getXY(), portal = this.portal, px = dd.proxy; - - // case column widths - if(!this.grid){ - this.grid = this.getGrid(); - } - - // handle case scroll where scrollbars appear during drag - var cw = portal.body.dom.clientWidth; - if(!this.lastCW){ - this.lastCW = cw; - }else if(this.lastCW != cw){ - this.lastCW = cw; - portal.doLayout(); - this.grid = this.getGrid(); - } - - // determine column - var col = 0, xs = this.grid.columnX, cmatch = false; - for(var len = xs.length; col < len; col++){ - if(xy[0] < (xs[col].x + xs[col].w)){ - cmatch = true; - break; - } - } - // no match, fix last index - if(!cmatch){ - col--; - } - - // find insert position - var p, match = false, pos = 0, - c = portal.items.itemAt(col), - items = c.items.items, overSelf = false; - - for(var len = items.length; pos < len; pos++){ - p = items[pos]; - var h = p.el.getHeight(); - if(h === 0){ - overSelf = true; - } - else if((p.el.getY()+(h/2)) > xy[1]){ - match = true; - break; - } - } - - pos = (match && p ? pos : c.items.getCount()) + (overSelf ? -1 : 0); - var overEvent = this.createEvent(dd, e, data, col, c, pos); - - if(portal.fireEvent('validatedrop', overEvent) !== false && - portal.fireEvent('beforedragover', overEvent) !== false){ - - // make sure proxy width is fluid - px.getProxy().setWidth('auto'); - - if(p){ - px.moveProxy(p.el.dom.parentNode, match ? p.el.dom : null); - }else{ - px.moveProxy(c.el.dom, null); - } - - this.lastPos = {c: c, col: col, p: overSelf || (match && p) ? pos : false}; - this.scrollPos = portal.body.getScroll(); - - portal.fireEvent('dragover', overEvent); - - return overEvent.status; - }else{ - return overEvent.status; - } - - }, - - notifyOut : function(){ - delete this.grid; - }, - - notifyDrop : function(dd, e, data){ - delete this.grid; - if(!this.lastPos){ - return; - } - var c = this.lastPos.c, - col = this.lastPos.col, - pos = this.lastPos.p, - panel = dd.panel, - dropEvent = this.createEvent(dd, e, data, col, c, - pos !== false ? pos : c.items.getCount()); - - if(this.portal.fireEvent('validatedrop', dropEvent) !== false && - this.portal.fireEvent('beforedrop', dropEvent) !== false){ - - dd.proxy.getProxy().remove(); - panel.el.dom.parentNode.removeChild(dd.panel.el.dom); - - if(pos !== false){ - c.insert(pos, panel); - }else{ - c.add(panel); - } - - c.doLayout(); - - this.portal.fireEvent('drop', dropEvent); - - // scroll position is lost on drop, fix it - var st = this.scrollPos.top; - if(st){ - var d = this.portal.body.dom; - setTimeout(function(){ - d.scrollTop = st; - }, 10); - } - - } - delete this.lastPos; - }, - - // internal cache of body and column coords - getGrid : function(){ - var box = this.portal.bwrap.getBox(); - box.columnX = []; - this.portal.items.each(function(c){ - box.columnX.push({x: c.el.getX(), w: c.el.getWidth()}); - }); - return box; - }, - - // unregister the dropzone from ScrollManager - unreg: function() { - Ext.dd.ScrollManager.unregister(this.portal.body); - Ext.ux.Portal.DropZone.superclass.unreg.call(this); - } -}); -Ext.ux.PortalColumn = Ext.extend(Ext.Container, { - layout : 'anchor', - //autoEl : 'div',//already defined by Ext.Component - defaultType : 'portlet', - cls : 'x-portal-column' -}); - -Ext.reg('portalcolumn', Ext.ux.PortalColumn); -Ext.ux.Portlet = Ext.extend(Ext.Panel, { - anchor : '100%', - frame : true, - collapsible : true, - draggable : true, - cls : 'x-portlet' -}); - -Ext.reg('portlet', Ext.ux.Portlet); -/** -* @class Ext.ux.ProgressBarPager -* @extends Object -* Plugin (ptype = 'tabclosemenu') for displaying a progressbar inside of a paging toolbar instead of plain text -* -* @ptype progressbarpager -* @constructor -* Create a new ItemSelector -* @param {Object} config Configuration options -* @xtype itemselector -*/ -Ext.ux.ProgressBarPager = Ext.extend(Object, { - /** - * @cfg {Integer} progBarWidth - *

    The default progress bar width. Default is 225.

    - */ - progBarWidth : 225, - /** - * @cfg {String} defaultText - *

    The text to display while the store is loading. Default is 'Loading...'

    - */ - defaultText : 'Loading...', - /** - * @cfg {Object} defaultAnimCfg - *

    A {@link Ext.Fx Ext.Fx} configuration object. Default is { duration : 1, easing : 'bounceOut' }.

    - */ - defaultAnimCfg : { - duration : 1, - easing : 'bounceOut' - }, - constructor : function(config) { - if (config) { - Ext.apply(this, config); - } - }, - //public - init : function (parent) { - - if(parent.displayInfo){ - this.parent = parent; - var ind = parent.items.indexOf(parent.displayItem); - parent.remove(parent.displayItem, true); - this.progressBar = new Ext.ProgressBar({ - text : this.defaultText, - width : this.progBarWidth, - animate : this.defaultAnimCfg - }); - - parent.displayItem = this.progressBar; - - parent.add(parent.displayItem); - parent.doLayout(); - Ext.apply(parent, this.parentOverrides); - - this.progressBar.on('render', function(pb) { - pb.mon(pb.getEl().applyStyles('cursor:pointer'), 'click', this.handleProgressBarClick, this); - }, this, {single: true}); - - } - - }, - // private - // This method handles the click for the progress bar - handleProgressBarClick : function(e){ - var parent = this.parent, - displayItem = parent.displayItem, - box = this.progressBar.getBox(), - xy = e.getXY(), - position = xy[0]-box.x, - pages = Math.ceil(parent.store.getTotalCount()/parent.pageSize), - newpage = Math.ceil(position/(displayItem.width/pages)); - - parent.changePage(newpage); - }, - - // private, overriddes - parentOverrides : { - // private - // This method updates the information via the progress bar. - updateInfo : function(){ - if(this.displayItem){ - var count = this.store.getCount(), - pgData = this.getPageData(), - pageNum = this.readPage(pgData), - msg = count == 0 ? - this.emptyMsg : - String.format( - this.displayMsg, - this.cursor+1, this.cursor+count, this.store.getTotalCount() - ); - - pageNum = pgData.activePage; ; - - var pct = pageNum / pgData.pages; - - this.displayItem.updateProgress(pct, msg, this.animate || this.defaultAnimConfig); - } - } - } -}); -Ext.preg('progressbarpager', Ext.ux.ProgressBarPager); - -Ext.ns('Ext.ux.grid'); - -/** - * @class Ext.ux.grid.RowEditor - * @extends Ext.Panel - * Plugin (ptype = 'roweditor') that adds the ability to rapidly edit full rows in a grid. - * A validation mode may be enabled which uses AnchorTips to notify the user of all - * validation errors at once. - * - * @ptype roweditor - */ -Ext.ux.grid.RowEditor = Ext.extend(Ext.Panel, { - floating: true, - shadow: false, - layout: 'hbox', - cls: 'x-small-editor', - buttonAlign: 'center', - baseCls: 'x-row-editor', - elements: 'header,footer,body', - frameWidth: 5, - buttonPad: 3, - clicksToEdit: 'auto', - monitorValid: true, - focusDelay: 250, - errorSummary: true, - - saveText: 'Save', - cancelText: 'Cancel', - commitChangesText: 'You need to commit or cancel your changes', - errorText: 'Errors', - - defaults: { - normalWidth: true - }, - - initComponent: function(){ - Ext.ux.grid.RowEditor.superclass.initComponent.call(this); - this.addEvents( - /** - * @event beforeedit - * Fired before the row editor is activated. - * If the listener returns false the editor will not be activated. - * @param {Ext.ux.grid.RowEditor} roweditor This object - * @param {Number} rowIndex The rowIndex of the row just edited - */ - 'beforeedit', - /** - * @event canceledit - * Fired when the editor is cancelled. - * @param {Ext.ux.grid.RowEditor} roweditor This object - * @param {Boolean} forced True if the cancel button is pressed, false is the editor was invalid. - */ - 'canceledit', - /** - * @event validateedit - * Fired after a row is edited and passes validation. - * If the listener returns false changes to the record will not be set. - * @param {Ext.ux.grid.RowEditor} roweditor This object - * @param {Object} changes Object with changes made to the record. - * @param {Ext.data.Record} r The Record that was edited. - * @param {Number} rowIndex The rowIndex of the row just edited - */ - 'validateedit', - /** - * @event afteredit - * Fired after a row is edited and passes validation. This event is fired - * after the store's update event is fired with this edit. - * @param {Ext.ux.grid.RowEditor} roweditor This object - * @param {Object} changes Object with changes made to the record. - * @param {Ext.data.Record} r The Record that was edited. - * @param {Number} rowIndex The rowIndex of the row just edited - */ - 'afteredit' - ); - }, - - init: function(grid){ - this.grid = grid; - this.ownerCt = grid; - if(this.clicksToEdit === 2){ - grid.on('rowdblclick', this.onRowDblClick, this); - }else{ - grid.on('rowclick', this.onRowClick, this); - if(Ext.isIE){ - grid.on('rowdblclick', this.onRowDblClick, this); - } - } - - // stopEditing without saving when a record is removed from Store. - grid.getStore().on('remove', function() { - this.stopEditing(false); - },this); - - grid.on({ - scope: this, - keydown: this.onGridKey, - columnresize: this.verifyLayout, - columnmove: this.refreshFields, - reconfigure: this.refreshFields, - beforedestroy : this.beforedestroy, - destroy : this.destroy, - bodyscroll: { - buffer: 250, - fn: this.positionButtons - } - }); - grid.getColumnModel().on('hiddenchange', this.verifyLayout, this, {delay:1}); - grid.getView().on('refresh', this.stopEditing.createDelegate(this, [])); - }, - - beforedestroy: function() { - this.stopMonitoring(); - this.grid.getStore().un('remove', this.onStoreRemove, this); - this.stopEditing(false); - Ext.destroy(this.btns, this.tooltip); - }, - - refreshFields: function(){ - this.initFields(); - this.verifyLayout(); - }, - - isDirty: function(){ - var dirty; - this.items.each(function(f){ - if(String(this.values[f.id]) !== String(f.getValue())){ - dirty = true; - return false; - } - }, this); - return dirty; - }, - - startEditing: function(rowIndex, doFocus){ - if(this.editing && this.isDirty()){ - this.showTooltip(this.commitChangesText); - return; - } - if(Ext.isObject(rowIndex)){ - rowIndex = this.grid.getStore().indexOf(rowIndex); - } - if(this.fireEvent('beforeedit', this, rowIndex) !== false){ - this.editing = true; - var g = this.grid, view = g.getView(), - row = view.getRow(rowIndex), - record = g.store.getAt(rowIndex); - - this.record = record; - this.rowIndex = rowIndex; - this.values = {}; - if(!this.rendered){ - this.render(view.getEditorParent()); - } - var w = Ext.fly(row).getWidth(); - this.setSize(w); - if(!this.initialized){ - this.initFields(); - } - var cm = g.getColumnModel(), fields = this.items.items, f, val; - for(var i = 0, len = cm.getColumnCount(); i < len; i++){ - val = this.preEditValue(record, cm.getDataIndex(i)); - f = fields[i]; - f.setValue(val); - this.values[f.id] = Ext.isEmpty(val) ? '' : val; - } - this.verifyLayout(true); - if(!this.isVisible()){ - this.setPagePosition(Ext.fly(row).getXY()); - } else{ - this.el.setXY(Ext.fly(row).getXY(), {duration:0.15}); - } - if(!this.isVisible()){ - this.show().doLayout(); - } - if(doFocus !== false){ - this.doFocus.defer(this.focusDelay, this); - } - } - }, - - stopEditing : function(saveChanges){ - this.editing = false; - if(!this.isVisible()){ - return; - } - if(saveChanges === false || !this.isValid()){ - this.hide(); - this.fireEvent('canceledit', this, saveChanges === false); - return; - } - var changes = {}, - r = this.record, - hasChange = false, - cm = this.grid.colModel, - fields = this.items.items; - for(var i = 0, len = cm.getColumnCount(); i < len; i++){ - if(!cm.isHidden(i)){ - var dindex = cm.getDataIndex(i); - if(!Ext.isEmpty(dindex)){ - var oldValue = r.data[dindex], - value = this.postEditValue(fields[i].getValue(), oldValue, r, dindex); - if(String(oldValue) !== String(value)){ - changes[dindex] = value; - hasChange = true; - } - } - } - } - if(hasChange && this.fireEvent('validateedit', this, changes, r, this.rowIndex) !== false){ - r.beginEdit(); - Ext.iterate(changes, function(name, value){ - r.set(name, value); - }); - r.endEdit(); - this.fireEvent('afteredit', this, changes, r, this.rowIndex); - } - this.hide(); - }, - - verifyLayout: function(force){ - if(this.el && (this.isVisible() || force === true)){ - var row = this.grid.getView().getRow(this.rowIndex); - this.setSize(Ext.fly(row).getWidth(), Ext.isIE ? Ext.fly(row).getHeight() + 9 : undefined); - var cm = this.grid.colModel, fields = this.items.items; - for(var i = 0, len = cm.getColumnCount(); i < len; i++){ - if(!cm.isHidden(i)){ - var adjust = 0; - if(i === (len - 1)){ - adjust += 3; // outer padding - } else{ - adjust += 1; - } - fields[i].show(); - fields[i].setWidth(cm.getColumnWidth(i) - adjust); - } else{ - fields[i].hide(); - } - } - this.doLayout(); - this.positionButtons(); - } - }, - - slideHide : function(){ - this.hide(); - }, - - initFields: function(){ - var cm = this.grid.getColumnModel(), pm = Ext.layout.ContainerLayout.prototype.parseMargins; - this.removeAll(false); - for(var i = 0, len = cm.getColumnCount(); i < len; i++){ - var c = cm.getColumnAt(i), - ed = c.getEditor(); - if(!ed){ - ed = c.displayEditor || new Ext.form.DisplayField(); - } - if(i == 0){ - ed.margins = pm('0 1 2 1'); - } else if(i == len - 1){ - ed.margins = pm('0 0 2 1'); - } else{ - if (Ext.isIE) { - ed.margins = pm('0 0 2 0'); - } - else { - ed.margins = pm('0 1 2 0'); - } - } - ed.setWidth(cm.getColumnWidth(i)); - ed.column = c; - if(ed.ownerCt !== this){ - ed.on('focus', this.ensureVisible, this); - ed.on('specialkey', this.onKey, this); - } - this.insert(i, ed); - } - this.initialized = true; - }, - - onKey: function(f, e){ - if(e.getKey() === e.ENTER){ - this.stopEditing(true); - e.stopPropagation(); - } - }, - - onGridKey: function(e){ - if(e.getKey() === e.ENTER && !this.isVisible()){ - var r = this.grid.getSelectionModel().getSelected(); - if(r){ - var index = this.grid.store.indexOf(r); - this.startEditing(index); - e.stopPropagation(); - } - } - }, - - ensureVisible: function(editor){ - if(this.isVisible()){ - this.grid.getView().ensureVisible(this.rowIndex, this.grid.colModel.getIndexById(editor.column.id), true); - } - }, - - onRowClick: function(g, rowIndex, e){ - if(this.clicksToEdit == 'auto'){ - var li = this.lastClickIndex; - this.lastClickIndex = rowIndex; - if(li != rowIndex && !this.isVisible()){ - return; - } - } - this.startEditing(rowIndex, false); - this.doFocus.defer(this.focusDelay, this, [e.getPoint()]); - }, - - onRowDblClick: function(g, rowIndex, e){ - this.startEditing(rowIndex, false); - this.doFocus.defer(this.focusDelay, this, [e.getPoint()]); - }, - - onRender: function(){ - Ext.ux.grid.RowEditor.superclass.onRender.apply(this, arguments); - this.el.swallowEvent(['keydown', 'keyup', 'keypress']); - this.btns = new Ext.Panel({ - baseCls: 'x-plain', - cls: 'x-btns', - elements:'body', - layout: 'table', - width: (this.minButtonWidth * 2) + (this.frameWidth * 2) + (this.buttonPad * 4), // width must be specified for IE - items: [{ - ref: 'saveBtn', - itemId: 'saveBtn', - xtype: 'button', - text: this.saveText, - width: this.minButtonWidth, - handler: this.stopEditing.createDelegate(this, [true]) - }, { - xtype: 'button', - text: this.cancelText, - width: this.minButtonWidth, - handler: this.stopEditing.createDelegate(this, [false]) - }] - }); - this.btns.render(this.bwrap); - }, - - afterRender: function(){ - Ext.ux.grid.RowEditor.superclass.afterRender.apply(this, arguments); - this.positionButtons(); - if(this.monitorValid){ - this.startMonitoring(); - } - }, - - onShow: function(){ - if(this.monitorValid){ - this.startMonitoring(); - } - Ext.ux.grid.RowEditor.superclass.onShow.apply(this, arguments); - }, - - onHide: function(){ - Ext.ux.grid.RowEditor.superclass.onHide.apply(this, arguments); - this.stopMonitoring(); - this.grid.getView().focusRow(this.rowIndex); - }, - - positionButtons: function(){ - if(this.btns){ - var g = this.grid, - h = this.el.dom.clientHeight, - view = g.getView(), - scroll = view.scroller.dom.scrollLeft, - bw = this.btns.getWidth(), - width = Math.min(g.getWidth(), g.getColumnModel().getTotalWidth()); - - this.btns.el.shift({left: (width/2)-(bw/2)+scroll, top: h - 2, stopFx: true, duration:0.2}); - } - }, - - // private - preEditValue : function(r, field){ - var value = r.data[field]; - return this.autoEncode && typeof value === 'string' ? Ext.util.Format.htmlDecode(value) : value; - }, - - // private - postEditValue : function(value, originalValue, r, field){ - return this.autoEncode && typeof value == 'string' ? Ext.util.Format.htmlEncode(value) : value; - }, - - doFocus: function(pt){ - if(this.isVisible()){ - var index = 0, - cm = this.grid.getColumnModel(), - c; - if(pt){ - index = this.getTargetColumnIndex(pt); - } - for(var i = index||0, len = cm.getColumnCount(); i < len; i++){ - c = cm.getColumnAt(i); - if(!c.hidden && c.getEditor()){ - c.getEditor().focus(); - break; - } - } - } - }, - - getTargetColumnIndex: function(pt){ - var grid = this.grid, - v = grid.view, - x = pt.left, - cms = grid.colModel.config, - i = 0, - match = false; - for(var len = cms.length, c; c = cms[i]; i++){ - if(!c.hidden){ - if(Ext.fly(v.getHeaderCell(i)).getRegion().right >= x){ - match = i; - break; - } - } - } - return match; - }, - - startMonitoring : function(){ - if(!this.bound && this.monitorValid){ - this.bound = true; - Ext.TaskMgr.start({ - run : this.bindHandler, - interval : this.monitorPoll || 200, - scope: this - }); - } - }, - - stopMonitoring : function(){ - this.bound = false; - if(this.tooltip){ - this.tooltip.hide(); - } - }, - - isValid: function(){ - var valid = true; - this.items.each(function(f){ - if(!f.isValid(true)){ - valid = false; - return false; - } - }); - return valid; - }, - - // private - bindHandler : function(){ - if(!this.bound){ - return false; // stops binding - } - var valid = this.isValid(); - if(!valid && this.errorSummary){ - this.showTooltip(this.getErrorText().join('')); - } - this.btns.saveBtn.setDisabled(!valid); - this.fireEvent('validation', this, valid); - }, - - lastVisibleColumn : function() { - var i = this.items.getCount() - 1, - c; - for(; i >= 0; i--) { - c = this.items.items[i]; - if (!c.hidden) { - return c; - } - } - }, - - showTooltip: function(msg){ - var t = this.tooltip; - if(!t){ - t = this.tooltip = new Ext.ToolTip({ - maxWidth: 600, - cls: 'errorTip', - width: 300, - title: this.errorText, - autoHide: false, - anchor: 'left', - anchorToTarget: true, - mouseOffset: [40,0] - }); - } - var v = this.grid.getView(), - top = parseInt(this.el.dom.style.top, 10), - scroll = v.scroller.dom.scrollTop, - h = this.el.getHeight(); - - if(top + h >= scroll){ - t.initTarget(this.lastVisibleColumn().getEl()); - if(!t.rendered){ - t.show(); - t.hide(); - } - t.body.update(msg); - t.doAutoWidth(20); - t.show(); - }else if(t.rendered){ - t.hide(); - } - }, - - getErrorText: function(){ - var data = ['
      ']; - this.items.each(function(f){ - if(!f.isValid(true)){ - data.push('
    • ', f.getActiveError(), '
    • '); - } - }); - data.push('
    '); - return data; - } -}); -Ext.preg('roweditor', Ext.ux.grid.RowEditor); -Ext.ns('Ext.ux.grid'); - -/** - * @class Ext.ux.grid.RowExpander - * @extends Ext.util.Observable - * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables - * a second row body which expands/contracts. The expand/contract behavior is configurable to react - * on clicking of the column, double click of the row, and/or hitting enter while a row is selected. - * - * @ptype rowexpander - */ -Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, { - /** - * @cfg {Boolean} expandOnEnter - * true to toggle selected row(s) between expanded/collapsed when the enter - * key is pressed (defaults to true). - */ - expandOnEnter : true, - /** - * @cfg {Boolean} expandOnDblClick - * true to toggle a row between expanded/collapsed when double clicked - * (defaults to true). - */ - expandOnDblClick : true, - - header : '', - width : 20, - sortable : false, - fixed : true, - hideable: false, - menuDisabled : true, - dataIndex : '', - id : 'expander', - lazyRender : true, - enableCaching : true, - - constructor: function(config){ - Ext.apply(this, config); - - this.addEvents({ - /** - * @event beforeexpand - * Fires before the row expands. Have the listener return false to prevent the row from expanding. - * @param {Object} this RowExpander object. - * @param {Object} Ext.data.Record Record for the selected row. - * @param {Object} body body element for the secondary row. - * @param {Number} rowIndex The current row index. - */ - beforeexpand: true, - /** - * @event expand - * Fires after the row expands. - * @param {Object} this RowExpander object. - * @param {Object} Ext.data.Record Record for the selected row. - * @param {Object} body body element for the secondary row. - * @param {Number} rowIndex The current row index. - */ - expand: true, - /** - * @event beforecollapse - * Fires before the row collapses. Have the listener return false to prevent the row from collapsing. - * @param {Object} this RowExpander object. - * @param {Object} Ext.data.Record Record for the selected row. - * @param {Object} body body element for the secondary row. - * @param {Number} rowIndex The current row index. - */ - beforecollapse: true, - /** - * @event collapse - * Fires after the row collapses. - * @param {Object} this RowExpander object. - * @param {Object} Ext.data.Record Record for the selected row. - * @param {Object} body body element for the secondary row. - * @param {Number} rowIndex The current row index. - */ - collapse: true - }); - - Ext.ux.grid.RowExpander.superclass.constructor.call(this); - - if(this.tpl){ - if(typeof this.tpl == 'string'){ - this.tpl = new Ext.Template(this.tpl); - } - this.tpl.compile(); - } - - this.state = {}; - this.bodyContent = {}; - }, - - getRowClass : function(record, rowIndex, p, ds){ - p.cols = p.cols-1; - var content = this.bodyContent[record.id]; - if(!content && !this.lazyRender){ - content = this.getBodyContent(record, rowIndex); - } - if(content){ - p.body = content; - } - return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed'; - }, - - init : function(grid){ - this.grid = grid; - - var view = grid.getView(); - view.getRowClass = this.getRowClass.createDelegate(this); - - view.enableRowBody = true; - - - grid.on('render', this.onRender, this); - grid.on('destroy', this.onDestroy, this); - }, - - // @private - onRender: function() { - var grid = this.grid; - var mainBody = grid.getView().mainBody; - mainBody.on('mousedown', this.onMouseDown, this, {delegate: '.x-grid3-row-expander'}); - if (this.expandOnEnter) { - this.keyNav = new Ext.KeyNav(this.grid.getGridEl(), { - 'enter' : this.onEnter, - scope: this - }); - } - if (this.expandOnDblClick) { - grid.on('rowdblclick', this.onRowDblClick, this); - } - }, - - // @private - onDestroy: function() { - if(this.keyNav){ - this.keyNav.disable(); - delete this.keyNav; - } - /* - * A majority of the time, the plugin will be destroyed along with the grid, - * which means the mainBody won't be available. On the off chance that the plugin - * isn't destroyed with the grid, take care of removing the listener. - */ - var mainBody = this.grid.getView().mainBody; - if(mainBody){ - mainBody.un('mousedown', this.onMouseDown, this); - } - }, - // @private - onRowDblClick: function(grid, rowIdx, e) { - this.toggleRow(rowIdx); - }, - - onEnter: function(e) { - var g = this.grid; - var sm = g.getSelectionModel(); - var sels = sm.getSelections(); - for (var i = 0, len = sels.length; i < len; i++) { - var rowIdx = g.getStore().indexOf(sels[i]); - this.toggleRow(rowIdx); - } - }, - - getBodyContent : function(record, index){ - if(!this.enableCaching){ - return this.tpl.apply(record.data); - } - var content = this.bodyContent[record.id]; - if(!content){ - content = this.tpl.apply(record.data); - this.bodyContent[record.id] = content; - } - return content; - }, - - onMouseDown : function(e, t){ - e.stopEvent(); - var row = e.getTarget('.x-grid3-row'); - this.toggleRow(row); - }, - - renderer : function(v, p, record){ - p.cellAttr = 'rowspan="2"'; - return '
     
    '; - }, - - beforeExpand : function(record, body, rowIndex){ - if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){ - if(this.tpl && this.lazyRender){ - body.innerHTML = this.getBodyContent(record, rowIndex); - } - return true; - }else{ - return false; - } - }, - - toggleRow : function(row){ - if(typeof row == 'number'){ - row = this.grid.view.getRow(row); - } - this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row); - }, - - expandRow : function(row){ - if(typeof row == 'number'){ - row = this.grid.view.getRow(row); - } - var record = this.grid.store.getAt(row.rowIndex); - var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row); - if(this.beforeExpand(record, body, row.rowIndex)){ - this.state[record.id] = true; - Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded'); - this.fireEvent('expand', this, record, body, row.rowIndex); - } - }, - - collapseRow : function(row){ - if(typeof row == 'number'){ - row = this.grid.view.getRow(row); - } - var record = this.grid.store.getAt(row.rowIndex); - var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true); - if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){ - this.state[record.id] = false; - Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed'); - this.fireEvent('collapse', this, record, body, row.rowIndex); - } - } -}); - -Ext.preg('rowexpander', Ext.ux.grid.RowExpander); - -//backwards compat -Ext.grid.RowExpander = Ext.ux.grid.RowExpander;// We are adding these custom layouts to a namespace that does not -// exist by default in Ext, so we have to add the namespace first: -Ext.ns('Ext.ux.layout'); - -/** - * @class Ext.ux.layout.RowLayout - * @extends Ext.layout.ContainerLayout - *

    This is the layout style of choice for creating structural layouts in a multi-row format where the height of - * each row can be specified as a percentage or fixed height. Row widths can also be fixed, percentage or auto. - * This class is intended to be extended or created via the layout:'ux.row' {@link Ext.Container#layout} config, - * and should generally not need to be created directly via the new keyword.

    - *

    RowLayout does not have any direct config options (other than inherited ones), but it does support a - * specific config property of rowHeight that can be included in the config of any panel added to it. The - * layout will use the rowHeight (if present) or height of each panel during layout to determine how to size each panel. - * If height or rowHeight is not specified for a given panel, its height will default to the panel's height (or auto).

    - *

    The height property is always evaluated as pixels, and must be a number greater than or equal to 1. - * The rowHeight property is always evaluated as a percentage, and must be a decimal value greater than 0 and - * less than 1 (e.g., .25).

    - *

    The basic rules for specifying row heights are pretty simple. The logic makes two passes through the - * set of contained panels. During the first layout pass, all panels that either have a fixed height or none - * specified (auto) are skipped, but their heights are subtracted from the overall container height. During the second - * pass, all panels with rowHeights are assigned pixel heights in proportion to their percentages based on - * the total remaining container height. In other words, percentage height panels are designed to fill the space - * left over by all the fixed-height and/or auto-height panels. Because of this, while you can specify any number of rows - * with different percentages, the rowHeights must always add up to 1 (or 100%) when added together, otherwise your - * layout may not render as expected. Example usage:

    - *
    
    -// All rows are percentages -- they must add up to 1
    -var p = new Ext.Panel({
    -    title: 'Row Layout - Percentage Only',
    -    layout:'ux.row',
    -    items: [{
    -        title: 'Row 1',
    -        rowHeight: .25
    -    },{
    -        title: 'Row 2',
    -        rowHeight: .6
    -    },{
    -        title: 'Row 3',
    -        rowHeight: .15
    -    }]
    -});
    -
    -// Mix of height and rowHeight -- all rowHeight values must add
    -// up to 1. The first row will take up exactly 120px, and the last two
    -// rows will fill the remaining container height.
    -var p = new Ext.Panel({
    -    title: 'Row Layout - Mixed',
    -    layout:'ux.row',
    -    items: [{
    -        title: 'Row 1',
    -        height: 120,
    -        // standard panel widths are still supported too:
    -        width: '50%' // or 200
    -    },{
    -        title: 'Row 2',
    -        rowHeight: .8,
    -        width: 300
    -    },{
    -        title: 'Row 3',
    -        rowHeight: .2
    -    }]
    -});
    -
    - */ -Ext.ux.layout.RowLayout = Ext.extend(Ext.layout.ContainerLayout, { - // private - monitorResize:true, - - type: 'row', - - // private - allowContainerRemove: false, - - // private - isValidParent : function(c, target){ - return this.innerCt && c.getPositionEl().dom.parentNode == this.innerCt.dom; - }, - - getLayoutTargetSize : function() { - var target = this.container.getLayoutTarget(), ret; - if (target) { - ret = target.getViewSize(); - - // IE in strict mode will return a height of 0 on the 1st pass of getViewSize. - // Use getStyleSize to verify the 0 height, the adjustment pass will then work properly - // with getViewSize - if (Ext.isIE && Ext.isStrict && ret.height == 0){ - ret = target.getStyleSize(); - } - - ret.width -= target.getPadding('lr'); - ret.height -= target.getPadding('tb'); - } - return ret; - }, - - renderAll : function(ct, target) { - if(!this.innerCt){ - // the innerCt prevents wrapping and shuffling while - // the container is resizing - this.innerCt = target.createChild({cls:'x-column-inner'}); - this.innerCt.createChild({cls:'x-clear'}); - } - Ext.layout.ColumnLayout.superclass.renderAll.call(this, ct, this.innerCt); - }, - - // private - onLayout : function(ct, target){ - var rs = ct.items.items, - len = rs.length, - r, - m, - i, - margins = []; - - this.renderAll(ct, target); - - var size = this.getLayoutTargetSize(); - - if(size.width < 1 && size.height < 1){ // display none? - return; - } - - var h = size.height, - ph = h; - - this.innerCt.setSize({height:h}); - - // some rows can be percentages while others are fixed - // so we need to make 2 passes - - for(i = 0; i < len; i++){ - r = rs[i]; - m = r.getPositionEl().getMargins('tb'); - margins[i] = m; - if(!r.rowHeight){ - ph -= (r.getHeight() + m); - } - } - - ph = ph < 0 ? 0 : ph; - - for(i = 0; i < len; i++){ - r = rs[i]; - m = margins[i]; - if(r.rowHeight){ - r.setSize({height: Math.floor(r.rowHeight*ph) - m}); - } - } - - // Browsers differ as to when they account for scrollbars. We need to re-measure to see if the scrollbar - // spaces were accounted for properly. If not, re-layout. - if (Ext.isIE) { - if (i = target.getStyle('overflow') && i != 'hidden' && !this.adjustmentPass) { - var ts = this.getLayoutTargetSize(); - if (ts.width != size.width){ - this.adjustmentPass = true; - this.onLayout(ct, target); - } - } - } - delete this.adjustmentPass; - } - - /** - * @property activeItem - * @hide - */ -}); - -Ext.Container.LAYOUTS['ux.row'] = Ext.ux.layout.RowLayout; -Ext.ns('Ext.ux.form'); - -Ext.ux.form.SearchField = Ext.extend(Ext.form.TwinTriggerField, { - initComponent : function(){ - Ext.ux.form.SearchField.superclass.initComponent.call(this); - this.on('specialkey', function(f, e){ - if(e.getKey() == e.ENTER){ - this.onTrigger2Click(); - } - }, this); - }, - - validationEvent:false, - validateOnBlur:false, - trigger1Class:'x-form-clear-trigger', - trigger2Class:'x-form-search-trigger', - hideTrigger1:true, - width:180, - hasSearch : false, - paramName : 'query', - - onTrigger1Click : function(){ - if(this.hasSearch){ - this.el.dom.value = ''; - var o = {start: 0}; - this.store.baseParams = this.store.baseParams || {}; - this.store.baseParams[this.paramName] = ''; - this.store.reload({params:o}); - this.triggers[0].hide(); - this.hasSearch = false; - } - }, - - onTrigger2Click : function(){ - var v = this.getRawValue(); - if(v.length < 1){ - this.onTrigger1Click(); - return; - } - var o = {start: 0}; - this.store.baseParams = this.store.baseParams || {}; - this.store.baseParams[this.paramName] = v; - this.store.reload({params:o}); - this.hasSearch = true; - this.triggers[0].show(); - } -});Ext.ns('Ext.ux.form'); - -/** - * @class Ext.ux.form.SelectBox - * @extends Ext.form.ComboBox - *

    Makes a ComboBox more closely mimic an HTML SELECT. Supports clicking and dragging - * through the list, with item selection occurring when the mouse button is released. - * When used will automatically set {@link #editable} to false and call {@link Ext.Element#unselectable} - * on inner elements. Re-enabling editable after calling this will NOT work.

    - * @author Corey Gilmore http://extjs.com/forum/showthread.php?t=6392 - * @history 2007-07-08 jvs - * Slight mods for Ext 2.0 - * @xtype selectbox - */ -Ext.ux.form.SelectBox = Ext.extend(Ext.form.ComboBox, { - constructor: function(config){ - this.searchResetDelay = 1000; - config = config || {}; - config = Ext.apply(config || {}, { - editable: false, - forceSelection: true, - rowHeight: false, - lastSearchTerm: false, - triggerAction: 'all', - mode: 'local' - }); - - Ext.ux.form.SelectBox.superclass.constructor.apply(this, arguments); - - this.lastSelectedIndex = this.selectedIndex || 0; - }, - - initEvents : function(){ - Ext.ux.form.SelectBox.superclass.initEvents.apply(this, arguments); - // you need to use keypress to capture upper/lower case and shift+key, but it doesn't work in IE - this.el.on('keydown', this.keySearch, this, true); - this.cshTask = new Ext.util.DelayedTask(this.clearSearchHistory, this); - }, - - keySearch : function(e, target, options) { - var raw = e.getKey(); - var key = String.fromCharCode(raw); - var startIndex = 0; - - if( !this.store.getCount() ) { - return; - } - - switch(raw) { - case Ext.EventObject.HOME: - e.stopEvent(); - this.selectFirst(); - return; - - case Ext.EventObject.END: - e.stopEvent(); - this.selectLast(); - return; - - case Ext.EventObject.PAGEDOWN: - this.selectNextPage(); - e.stopEvent(); - return; - - case Ext.EventObject.PAGEUP: - this.selectPrevPage(); - e.stopEvent(); - return; - } - - // skip special keys other than the shift key - if( (e.hasModifier() && !e.shiftKey) || e.isNavKeyPress() || e.isSpecialKey() ) { - return; - } - if( this.lastSearchTerm == key ) { - startIndex = this.lastSelectedIndex; - } - this.search(this.displayField, key, startIndex); - this.cshTask.delay(this.searchResetDelay); - }, - - onRender : function(ct, position) { - this.store.on('load', this.calcRowsPerPage, this); - Ext.ux.form.SelectBox.superclass.onRender.apply(this, arguments); - if( this.mode == 'local' ) { - this.initList(); - this.calcRowsPerPage(); - } - }, - - onSelect : function(record, index, skipCollapse){ - if(this.fireEvent('beforeselect', this, record, index) !== false){ - this.setValue(record.data[this.valueField || this.displayField]); - if( !skipCollapse ) { - this.collapse(); - } - this.lastSelectedIndex = index + 1; - this.fireEvent('select', this, record, index); - } - }, - - afterRender : function() { - Ext.ux.form.SelectBox.superclass.afterRender.apply(this, arguments); - if(Ext.isWebKit) { - this.el.swallowEvent('mousedown', true); - } - this.el.unselectable(); - this.innerList.unselectable(); - this.trigger.unselectable(); - this.innerList.on('mouseup', function(e, target, options) { - if( target.id && target.id == this.innerList.id ) { - return; - } - this.onViewClick(); - }, this); - - this.innerList.on('mouseover', function(e, target, options) { - if( target.id && target.id == this.innerList.id ) { - return; - } - this.lastSelectedIndex = this.view.getSelectedIndexes()[0] + 1; - this.cshTask.delay(this.searchResetDelay); - }, this); - - this.trigger.un('click', this.onTriggerClick, this); - this.trigger.on('mousedown', function(e, target, options) { - e.preventDefault(); - this.onTriggerClick(); - }, this); - - this.on('collapse', function(e, target, options) { - Ext.getDoc().un('mouseup', this.collapseIf, this); - }, this, true); - - this.on('expand', function(e, target, options) { - Ext.getDoc().on('mouseup', this.collapseIf, this); - }, this, true); - }, - - clearSearchHistory : function() { - this.lastSelectedIndex = 0; - this.lastSearchTerm = false; - }, - - selectFirst : function() { - this.focusAndSelect(this.store.data.first()); - }, - - selectLast : function() { - this.focusAndSelect(this.store.data.last()); - }, - - selectPrevPage : function() { - if( !this.rowHeight ) { - return; - } - var index = Math.max(this.selectedIndex-this.rowsPerPage, 0); - this.focusAndSelect(this.store.getAt(index)); - }, - - selectNextPage : function() { - if( !this.rowHeight ) { - return; - } - var index = Math.min(this.selectedIndex+this.rowsPerPage, this.store.getCount() - 1); - this.focusAndSelect(this.store.getAt(index)); - }, - - search : function(field, value, startIndex) { - field = field || this.displayField; - this.lastSearchTerm = value; - var index = this.store.find.apply(this.store, arguments); - if( index !== -1 ) { - this.focusAndSelect(index); - } - }, - - focusAndSelect : function(record) { - var index = Ext.isNumber(record) ? record : this.store.indexOf(record); - this.select(index, this.isExpanded()); - this.onSelect(this.store.getAt(index), index, this.isExpanded()); - }, - - calcRowsPerPage : function() { - if( this.store.getCount() ) { - this.rowHeight = Ext.fly(this.view.getNode(0)).getHeight(); - this.rowsPerPage = this.maxHeight / this.rowHeight; - } else { - this.rowHeight = false; - } - } - -}); - -Ext.reg('selectbox', Ext.ux.form.SelectBox); - -//backwards compat -Ext.ux.SelectBox = Ext.ux.form.SelectBox; -/** - * Plugin for PagingToolbar which replaces the textfield input with a slider - */ -Ext.ux.SlidingPager = Ext.extend(Object, { - init : function(pbar){ - var idx = pbar.items.indexOf(pbar.inputItem); - Ext.each(pbar.items.getRange(idx - 2, idx + 2), function(c){ - c.hide(); - }); - var slider = new Ext.Slider({ - width: 114, - minValue: 1, - maxValue: 1, - plugins: new Ext.slider.Tip({ - getText : function(thumb) { - return String.format('Page {0} of {1}', thumb.value, thumb.slider.maxValue); - } - }), - listeners: { - changecomplete: function(s, v){ - pbar.changePage(v); - } - } - }); - pbar.insert(idx + 1, slider); - pbar.on({ - change: function(pb, data){ - slider.setMaxValue(data.pages); - slider.setValue(data.activePage); - } - }); - } -});Ext.ns('Ext.ux.form'); - -/** - * @class Ext.ux.form.SpinnerField - * @extends Ext.form.NumberField - * Creates a field utilizing Ext.ux.Spinner - * @xtype spinnerfield - */ -Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, { - actionMode: 'wrap', - deferHeight: true, - autoSize: Ext.emptyFn, - onBlur: Ext.emptyFn, - adjustSize: Ext.BoxComponent.prototype.adjustSize, - - constructor: function(config) { - var spinnerConfig = Ext.copyTo({}, config, 'incrementValue,alternateIncrementValue,accelerate,defaultValue,triggerClass,splitterClass'); - - var spl = this.spinner = new Ext.ux.Spinner(spinnerConfig); - - var plugins = config.plugins - ? (Ext.isArray(config.plugins) - ? config.plugins.push(spl) - : [config.plugins, spl]) - : spl; - - Ext.ux.form.SpinnerField.superclass.constructor.call(this, Ext.apply(config, {plugins: plugins})); - }, - - // private - getResizeEl: function(){ - return this.wrap; - }, - - // private - getPositionEl: function(){ - return this.wrap; - }, - - // private - alignErrorIcon: function(){ - if (this.wrap) { - this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]); - } - }, - - validateBlur: function(){ - return true; - } -}); - -Ext.reg('spinnerfield', Ext.ux.form.SpinnerField); - -//backwards compat -Ext.form.SpinnerField = Ext.ux.form.SpinnerField; -/** - * @class Ext.ux.Spinner - * @extends Ext.util.Observable - * Creates a Spinner control utilized by Ext.ux.form.SpinnerField - */ -Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { - incrementValue: 1, - alternateIncrementValue: 5, - triggerClass: 'x-form-spinner-trigger', - splitterClass: 'x-form-spinner-splitter', - alternateKey: Ext.EventObject.shiftKey, - defaultValue: 0, - accelerate: false, - - constructor: function(config){ - Ext.ux.Spinner.superclass.constructor.call(this, config); - Ext.apply(this, config); - this.mimicing = false; - }, - - init: function(field){ - this.field = field; - - field.afterMethod('onRender', this.doRender, this); - field.afterMethod('onEnable', this.doEnable, this); - field.afterMethod('onDisable', this.doDisable, this); - field.afterMethod('afterRender', this.doAfterRender, this); - field.afterMethod('onResize', this.doResize, this); - field.afterMethod('onFocus', this.doFocus, this); - field.beforeMethod('onDestroy', this.doDestroy, this); - }, - - doRender: function(ct, position){ - var el = this.el = this.field.getEl(); - var f = this.field; - - if (!f.wrap) { - f.wrap = this.wrap = el.wrap({ - cls: "x-form-field-wrap" - }); - } - else { - this.wrap = f.wrap.addClass('x-form-field-wrap'); - } - - this.trigger = this.wrap.createChild({ - tag: "img", - src: Ext.BLANK_IMAGE_URL, - cls: "x-form-trigger " + this.triggerClass - }); - - if (!f.width) { - this.wrap.setWidth(el.getWidth() + this.trigger.getWidth()); - } - - this.splitter = this.wrap.createChild({ - tag: 'div', - cls: this.splitterClass, - style: 'width:13px; height:2px;' - }); - this.splitter.setRight((Ext.isIE) ? 1 : 2).setTop(10).show(); - - this.proxy = this.trigger.createProxy('', this.splitter, true); - this.proxy.addClass("x-form-spinner-proxy"); - this.proxy.setStyle('left', '0px'); - this.proxy.setSize(14, 1); - this.proxy.hide(); - this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", { - dragElId: this.proxy.id - }); - - this.initTrigger(); - this.initSpinner(); - }, - - doAfterRender: function(){ - var y; - if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) { - this.el.position(); - this.el.setY(y); - } - }, - - doEnable: function(){ - if (this.wrap) { - this.disabled = false; - this.wrap.removeClass(this.field.disabledClass); - } - }, - - doDisable: function(){ - if (this.wrap) { - this.disabled = true; - this.wrap.addClass(this.field.disabledClass); - this.el.removeClass(this.field.disabledClass); - } - }, - - doResize: function(w, h){ - if (typeof w == 'number') { - this.el.setWidth(w - this.trigger.getWidth()); - } - this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth()); - }, - - doFocus: function(){ - if (!this.mimicing) { - this.wrap.addClass('x-trigger-wrap-focus'); - this.mimicing = true; - Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, { - delay: 10 - }); - this.el.on('keydown', this.checkTab, this); - } - }, - - // private - checkTab: function(e){ - if (e.getKey() == e.TAB) { - this.triggerBlur(); - } - }, - - // private - mimicBlur: function(e){ - if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) { - this.triggerBlur(); - } - }, - - // private - triggerBlur: function(){ - this.mimicing = false; - Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this); - this.el.un("keydown", this.checkTab, this); - this.field.beforeBlur(); - this.wrap.removeClass('x-trigger-wrap-focus'); - this.field.onBlur.call(this.field); - }, - - initTrigger: function(){ - this.trigger.addClassOnOver('x-form-trigger-over'); - this.trigger.addClassOnClick('x-form-trigger-click'); - }, - - initSpinner: function(){ - this.field.addEvents({ - 'spin': true, - 'spinup': true, - 'spindown': true - }); - - this.keyNav = new Ext.KeyNav(this.el, { - "up": function(e){ - e.preventDefault(); - this.onSpinUp(); - }, - - "down": function(e){ - e.preventDefault(); - this.onSpinDown(); - }, - - "pageUp": function(e){ - e.preventDefault(); - this.onSpinUpAlternate(); - }, - - "pageDown": function(e){ - e.preventDefault(); - this.onSpinDownAlternate(); - }, - - scope: this - }); - - this.repeater = new Ext.util.ClickRepeater(this.trigger, { - accelerate: this.accelerate - }); - this.field.mon(this.repeater, "click", this.onTriggerClick, this, { - preventDefault: true - }); - - this.field.mon(this.trigger, { - mouseover: this.onMouseOver, - mouseout: this.onMouseOut, - mousemove: this.onMouseMove, - mousedown: this.onMouseDown, - mouseup: this.onMouseUp, - scope: this, - preventDefault: true - }); - - this.field.mon(this.wrap, "mousewheel", this.handleMouseWheel, this); - - this.dd.setXConstraint(0, 0, 10) - this.dd.setYConstraint(1500, 1500, 10); - this.dd.endDrag = this.endDrag.createDelegate(this); - this.dd.startDrag = this.startDrag.createDelegate(this); - this.dd.onDrag = this.onDrag.createDelegate(this); - }, - - onMouseOver: function(){ - if (this.disabled) { - return; - } - var middle = this.getMiddle(); - this.tmpHoverClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown'; - this.trigger.addClass(this.tmpHoverClass); - }, - - //private - onMouseOut: function(){ - this.trigger.removeClass(this.tmpHoverClass); - }, - - //private - onMouseMove: function(){ - if (this.disabled) { - return; - } - var middle = this.getMiddle(); - if (((Ext.EventObject.getPageY() > middle) && this.tmpHoverClass == "x-form-spinner-overup") || - ((Ext.EventObject.getPageY() < middle) && this.tmpHoverClass == "x-form-spinner-overdown")) { - } - }, - - //private - onMouseDown: function(){ - if (this.disabled) { - return; - } - var middle = this.getMiddle(); - this.tmpClickClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown'; - this.trigger.addClass(this.tmpClickClass); - }, - - //private - onMouseUp: function(){ - this.trigger.removeClass(this.tmpClickClass); - }, - - //private - onTriggerClick: function(){ - if (this.disabled || this.el.dom.readOnly) { - return; - } - var middle = this.getMiddle(); - var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down'; - this['onSpin' + ud](); - }, - - //private - getMiddle: function(){ - var t = this.trigger.getTop(); - var h = this.trigger.getHeight(); - var middle = t + (h / 2); - return middle; - }, - - //private - //checks if control is allowed to spin - isSpinnable: function(){ - if (this.disabled || this.el.dom.readOnly) { - Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly - return false; - } - return true; - }, - - handleMouseWheel: function(e){ - //disable scrolling when not focused - if (this.wrap.hasClass('x-trigger-wrap-focus') == false) { - return; - } - - var delta = e.getWheelDelta(); - if (delta > 0) { - this.onSpinUp(); - e.stopEvent(); - } - else - if (delta < 0) { - this.onSpinDown(); - e.stopEvent(); - } - }, - - //private - startDrag: function(){ - this.proxy.show(); - this._previousY = Ext.fly(this.dd.getDragEl()).getTop(); - }, - - //private - endDrag: function(){ - this.proxy.hide(); - }, - - //private - onDrag: function(){ - if (this.disabled) { - return; - } - var y = Ext.fly(this.dd.getDragEl()).getTop(); - var ud = ''; - - if (this._previousY > y) { - ud = 'Up'; - } //up - if (this._previousY < y) { - ud = 'Down'; - } //down - if (ud != '') { - this['onSpin' + ud](); - } - - this._previousY = y; - }, - - //private - onSpinUp: function(){ - if (this.isSpinnable() == false) { - return; - } - if (Ext.EventObject.shiftKey == true) { - this.onSpinUpAlternate(); - return; - } - else { - this.spin(false, false); - } - this.field.fireEvent("spin", this); - this.field.fireEvent("spinup", this); - }, - - //private - onSpinDown: function(){ - if (this.isSpinnable() == false) { - return; - } - if (Ext.EventObject.shiftKey == true) { - this.onSpinDownAlternate(); - return; - } - else { - this.spin(true, false); - } - this.field.fireEvent("spin", this); - this.field.fireEvent("spindown", this); - }, - - //private - onSpinUpAlternate: function(){ - if (this.isSpinnable() == false) { - return; - } - this.spin(false, true); - this.field.fireEvent("spin", this); - this.field.fireEvent("spinup", this); - }, - - //private - onSpinDownAlternate: function(){ - if (this.isSpinnable() == false) { - return; - } - this.spin(true, true); - this.field.fireEvent("spin", this); - this.field.fireEvent("spindown", this); - }, - - spin: function(down, alternate){ - var v = parseFloat(this.field.getValue()); - var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue; - (down == true) ? v -= incr : v += incr; - - v = (isNaN(v)) ? this.defaultValue : v; - v = this.fixBoundries(v); - this.field.setRawValue(v); - }, - - fixBoundries: function(value){ - var v = value; - - if (this.field.minValue != undefined && v < this.field.minValue) { - v = this.field.minValue; - } - if (this.field.maxValue != undefined && v > this.field.maxValue) { - v = this.field.maxValue; - } - - return this.fixPrecision(v); - }, - - // private - fixPrecision: function(value){ - var nan = isNaN(value); - if (!this.field.allowDecimals || this.field.decimalPrecision == -1 || nan || !value) { - return nan ? '' : value; - } - return parseFloat(parseFloat(value).toFixed(this.field.decimalPrecision)); - }, - - doDestroy: function(){ - if (this.trigger) { - this.trigger.remove(); - } - if (this.wrap) { - this.wrap.remove(); - delete this.field.wrap; - } - - if (this.splitter) { - this.splitter.remove(); - } - - if (this.dd) { - this.dd.unreg(); - this.dd = null; - } - - if (this.proxy) { - this.proxy.remove(); - } - - if (this.repeater) { - this.repeater.purgeListeners(); - } - if (this.mimicing){ - Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this); - } - } -}); - -//backwards compat -Ext.form.Spinner = Ext.ux.Spinner;Ext.ux.Spotlight = function(config){ - Ext.apply(this, config); -} -Ext.ux.Spotlight.prototype = { - active : false, - animate : true, - duration: .25, - easing:'easeNone', - - // private - animated : false, - - createElements : function(){ - var bd = Ext.getBody(); - - this.right = bd.createChild({cls:'x-spotlight'}); - this.left = bd.createChild({cls:'x-spotlight'}); - this.top = bd.createChild({cls:'x-spotlight'}); - this.bottom = bd.createChild({cls:'x-spotlight'}); - - this.all = new Ext.CompositeElement([this.right, this.left, this.top, this.bottom]); - }, - - show : function(el, callback, scope){ - if(this.animated){ - this.show.defer(50, this, [el, callback, scope]); - return; - } - this.el = Ext.get(el); - if(!this.right){ - this.createElements(); - } - if(!this.active){ - this.all.setDisplayed(''); - this.applyBounds(true, false); - this.active = true; - Ext.EventManager.onWindowResize(this.syncSize, this); - this.applyBounds(false, this.animate, false, callback, scope); - }else{ - this.applyBounds(false, false, false, callback, scope); // all these booleans look hideous - } - }, - - hide : function(callback, scope){ - if(this.animated){ - this.hide.defer(50, this, [callback, scope]); - return; - } - Ext.EventManager.removeResizeListener(this.syncSize, this); - this.applyBounds(true, this.animate, true, callback, scope); - }, - - doHide : function(){ - this.active = false; - this.all.setDisplayed(false); - }, - - syncSize : function(){ - this.applyBounds(false, false); - }, - - applyBounds : function(basePts, anim, doHide, callback, scope){ - - var rg = this.el.getRegion(); - - var dw = Ext.lib.Dom.getViewWidth(true); - var dh = Ext.lib.Dom.getViewHeight(true); - - var c = 0, cb = false; - if(anim){ - cb = { - callback: function(){ - c++; - if(c == 4){ - this.animated = false; - if(doHide){ - this.doHide(); - } - Ext.callback(callback, scope, [this]); - } - }, - scope: this, - duration: this.duration, - easing: this.easing - }; - this.animated = true; - } - - this.right.setBounds( - rg.right, - basePts ? dh : rg.top, - dw - rg.right, - basePts ? 0 : (dh - rg.top), - cb); - - this.left.setBounds( - 0, - 0, - rg.left, - basePts ? 0 : rg.bottom, - cb); - - this.top.setBounds( - basePts ? dw : rg.left, - 0, - basePts ? 0 : dw - rg.left, - rg.top, - cb); - - this.bottom.setBounds( - 0, - rg.bottom, - basePts ? 0 : rg.right, - dh - rg.bottom, - cb); - - if(!anim){ - if(doHide){ - this.doHide(); - } - if(callback){ - Ext.callback(callback, scope, [this]); - } - } - }, - - destroy : function(){ - this.doHide(); - Ext.destroy( - this.right, - this.left, - this.top, - this.bottom); - delete this.el; - delete this.all; - } -}; - -//backwards compat -Ext.Spotlight = Ext.ux.Spotlight;/** - * @class Ext.ux.StatusBar - *

    Basic status bar component that can be used as the bottom toolbar of any {@link Ext.Panel}. In addition to - * supporting the standard {@link Ext.Toolbar} interface for adding buttons, menus and other items, the StatusBar - * provides a greedy status element that can be aligned to either side and has convenient methods for setting the - * status text and icon. You can also indicate that something is processing using the {@link #showBusy} method.

    - *
    
    -new Ext.Panel({
    -    title: 'StatusBar',
    -    // etc.
    -    bbar: new Ext.ux.StatusBar({
    -        id: 'my-status',
    -
    -        // defaults to use when the status is cleared:
    -        defaultText: 'Default status text',
    -        defaultIconCls: 'default-icon',
    -
    -        // values to set initially:
    -        text: 'Ready',
    -        iconCls: 'ready-icon',
    -
    -        // any standard Toolbar items:
    -        items: [{
    -            text: 'A Button'
    -        }, '-', 'Plain Text']
    -    })
    -});
    -
    -// Update the status bar later in code:
    -var sb = Ext.getCmp('my-status');
    -sb.setStatus({
    -    text: 'OK',
    -    iconCls: 'ok-icon',
    -    clear: true // auto-clear after a set interval
    -});
    -
    -// Set the status bar to show that something is processing:
    -sb.showBusy();
    -
    -// processing....
    -
    -sb.clearStatus(); // once completeed
    -
    - * @extends Ext.Toolbar - * @constructor - * Creates a new StatusBar - * @param {Object/Array} config A config object - */ -Ext.ux.StatusBar = Ext.extend(Ext.Toolbar, { - /** - * @cfg {String} statusAlign - * The alignment of the status element within the overall StatusBar layout. When the StatusBar is rendered, - * it creates an internal div containing the status text and icon. Any additional Toolbar items added in the - * StatusBar's {@link #items} config, or added via {@link #add} or any of the supported add* methods, will be - * rendered, in added order, to the opposite side. The status element is greedy, so it will automatically - * expand to take up all sapce left over by any other items. Example usage: - *
    
    -// Create a left-aligned status bar containing a button,
    -// separator and text item that will be right-aligned (default):
    -new Ext.Panel({
    -    title: 'StatusBar',
    -    // etc.
    -    bbar: new Ext.ux.StatusBar({
    -        defaultText: 'Default status text',
    -        id: 'status-id',
    -        items: [{
    -            text: 'A Button'
    -        }, '-', 'Plain Text']
    -    })
    -});
    -
    -// By adding the statusAlign config, this will create the
    -// exact same toolbar, except the status and toolbar item
    -// layout will be reversed from the previous example:
    -new Ext.Panel({
    -    title: 'StatusBar',
    -    // etc.
    -    bbar: new Ext.ux.StatusBar({
    -        defaultText: 'Default status text',
    -        id: 'status-id',
    -        statusAlign: 'right',
    -        items: [{
    -            text: 'A Button'
    -        }, '-', 'Plain Text']
    -    })
    -});
    -
    - */ - /** - * @cfg {String} defaultText - * The default {@link #text} value. This will be used anytime the status bar is cleared with the - * useDefaults:true option (defaults to ''). - */ - /** - * @cfg {String} defaultIconCls - * The default {@link #iconCls} value (see the iconCls docs for additional details about customizing the icon). - * This will be used anytime the status bar is cleared with the useDefaults:true option (defaults to ''). - */ - /** - * @cfg {String} text - * A string that will be initially set as the status message. This string - * will be set as innerHTML (html tags are accepted) for the toolbar item. - * If not specified, the value set for {@link #defaultText} - * will be used. - */ - /** - * @cfg {String} iconCls - * A CSS class that will be initially set as the status bar icon and is - * expected to provide a background image (defaults to ''). - * Example usage:
    
    -// Example CSS rule:
    -.x-statusbar .x-status-custom {
    -    padding-left: 25px;
    -    background: transparent url(images/custom-icon.gif) no-repeat 3px 2px;
    -}
    -
    -// Setting a default icon:
    -var sb = new Ext.ux.StatusBar({
    -    defaultIconCls: 'x-status-custom'
    -});
    -
    -// Changing the icon:
    -sb.setStatus({
    -    text: 'New status',
    -    iconCls: 'x-status-custom'
    -});
    -
    - */ - - /** - * @cfg {String} cls - * The base class applied to the containing element for this component on render (defaults to 'x-statusbar') - */ - cls : 'x-statusbar', - /** - * @cfg {String} busyIconCls - * The default {@link #iconCls} applied when calling - * {@link #showBusy} (defaults to 'x-status-busy'). - * It can be overridden at any time by passing the iconCls - * argument into {@link #showBusy}. - */ - busyIconCls : 'x-status-busy', - /** - * @cfg {String} busyText - * The default {@link #text} applied when calling - * {@link #showBusy} (defaults to 'Loading...'). - * It can be overridden at any time by passing the text - * argument into {@link #showBusy}. - */ - busyText : 'Loading...', - /** - * @cfg {Number} autoClear - * The number of milliseconds to wait after setting the status via - * {@link #setStatus} before automatically clearing the status - * text and icon (defaults to 5000). Note that this only applies - * when passing the clear argument to {@link #setStatus} - * since that is the only way to defer clearing the status. This can - * be overridden by specifying a different wait value in - * {@link #setStatus}. Calls to {@link #clearStatus} - * always clear the status bar immediately and ignore this value. - */ - autoClear : 5000, - - /** - * @cfg {String} emptyText - * The text string to use if no text has been set. Defaults to - * ' '). If there are no other items in the toolbar using - * an empty string ('') for this value would end up in the toolbar - * height collapsing since the empty string will not maintain the toolbar - * height. Use '' if the toolbar should collapse in height - * vertically when no text is specified and there are no other items in - * the toolbar. - */ - emptyText : ' ', - - // private - activeThreadId : 0, - - // private - initComponent : function(){ - if(this.statusAlign=='right'){ - this.cls += ' x-status-right'; - } - Ext.ux.StatusBar.superclass.initComponent.call(this); - }, - - // private - afterRender : function(){ - Ext.ux.StatusBar.superclass.afterRender.call(this); - - var right = this.statusAlign == 'right'; - this.currIconCls = this.iconCls || this.defaultIconCls; - this.statusEl = new Ext.Toolbar.TextItem({ - cls: 'x-status-text ' + (this.currIconCls || ''), - text: this.text || this.defaultText || '' - }); - - if(right){ - this.add('->'); - this.add(this.statusEl); - }else{ - this.insert(0, this.statusEl); - this.insert(1, '->'); - } - this.doLayout(); - }, - - /** - * Sets the status {@link #text} and/or {@link #iconCls}. Also supports automatically clearing the - * status that was set after a specified interval. - * @param {Object/String} config A config object specifying what status to set, or a string assumed - * to be the status text (and all other options are defaulted as explained below). A config - * object containing any or all of the following properties can be passed:
      - *
    • text {String} : (optional) The status text to display. If not specified, any current - * status text will remain unchanged.
    • - *
    • iconCls {String} : (optional) The CSS class used to customize the status icon (see - * {@link #iconCls} for details). If not specified, any current iconCls will remain unchanged.
    • - *
    • clear {Boolean/Number/Object} : (optional) Allows you to set an internal callback that will - * automatically clear the status text and iconCls after a specified amount of time has passed. If clear is not - * specified, the new status will not be auto-cleared and will stay until updated again or cleared using - * {@link #clearStatus}. If true is passed, the status will be cleared using {@link #autoClear}, - * {@link #defaultText} and {@link #defaultIconCls} via a fade out animation. If a numeric value is passed, - * it will be used as the callback interval (in milliseconds), overriding the {@link #autoClear} value. - * All other options will be defaulted as with the boolean option. To customize any other options, - * you can pass an object in the format:
        - *
      • wait {Number} : (optional) The number of milliseconds to wait before clearing - * (defaults to {@link #autoClear}).
      • - *
      • anim {Number} : (optional) False to clear the status immediately once the callback - * executes (defaults to true which fades the status out).
      • - *
      • useDefaults {Number} : (optional) False to completely clear the status text and iconCls - * (defaults to true which uses {@link #defaultText} and {@link #defaultIconCls}).
      • - *
    - * Example usage:
    
    -// Simple call to update the text
    -statusBar.setStatus('New status');
    -
    -// Set the status and icon, auto-clearing with default options:
    -statusBar.setStatus({
    -    text: 'New status',
    -    iconCls: 'x-status-custom',
    -    clear: true
    -});
    -
    -// Auto-clear with custom options:
    -statusBar.setStatus({
    -    text: 'New status',
    -    iconCls: 'x-status-custom',
    -    clear: {
    -        wait: 8000,
    -        anim: false,
    -        useDefaults: false
    -    }
    -});
    -
    - * @return {Ext.ux.StatusBar} this - */ - setStatus : function(o){ - o = o || {}; - - if(typeof o == 'string'){ - o = {text:o}; - } - if(o.text !== undefined){ - this.setText(o.text); - } - if(o.iconCls !== undefined){ - this.setIcon(o.iconCls); - } - - if(o.clear){ - var c = o.clear, - wait = this.autoClear, - defaults = {useDefaults: true, anim: true}; - - if(typeof c == 'object'){ - c = Ext.applyIf(c, defaults); - if(c.wait){ - wait = c.wait; - } - }else if(typeof c == 'number'){ - wait = c; - c = defaults; - }else if(typeof c == 'boolean'){ - c = defaults; - } - - c.threadId = this.activeThreadId; - this.clearStatus.defer(wait, this, [c]); - } - return this; - }, - - /** - * Clears the status {@link #text} and {@link #iconCls}. Also supports clearing via an optional fade out animation. - * @param {Object} config (optional) A config object containing any or all of the following properties. If this - * object is not specified the status will be cleared using the defaults below:
      - *
    • anim {Boolean} : (optional) True to clear the status by fading out the status element (defaults - * to false which clears immediately).
    • - *
    • useDefaults {Boolean} : (optional) True to reset the text and icon using {@link #defaultText} and - * {@link #defaultIconCls} (defaults to false which sets the text to '' and removes any existing icon class).
    • - *
    - * @return {Ext.ux.StatusBar} this - */ - clearStatus : function(o){ - o = o || {}; - - if(o.threadId && o.threadId !== this.activeThreadId){ - // this means the current call was made internally, but a newer - // thread has set a message since this call was deferred. Since - // we don't want to overwrite a newer message just ignore. - return this; - } - - var text = o.useDefaults ? this.defaultText : this.emptyText, - iconCls = o.useDefaults ? (this.defaultIconCls ? this.defaultIconCls : '') : ''; - - if(o.anim){ - // animate the statusEl Ext.Element - this.statusEl.el.fadeOut({ - remove: false, - useDisplay: true, - scope: this, - callback: function(){ - this.setStatus({ - text: text, - iconCls: iconCls - }); - - this.statusEl.el.show(); - } - }); - }else{ - // hide/show the el to avoid jumpy text or icon - this.statusEl.hide(); - this.setStatus({ - text: text, - iconCls: iconCls - }); - this.statusEl.show(); - } - return this; - }, - - /** - * Convenience method for setting the status text directly. For more flexible options see {@link #setStatus}. - * @param {String} text (optional) The text to set (defaults to '') - * @return {Ext.ux.StatusBar} this - */ - setText : function(text){ - this.activeThreadId++; - this.text = text || ''; - if(this.rendered){ - this.statusEl.setText(this.text); - } - return this; - }, - - /** - * Returns the current status text. - * @return {String} The status text - */ - getText : function(){ - return this.text; - }, - - /** - * Convenience method for setting the status icon directly. For more flexible options see {@link #setStatus}. - * See {@link #iconCls} for complete details about customizing the icon. - * @param {String} iconCls (optional) The icon class to set (defaults to '', and any current icon class is removed) - * @return {Ext.ux.StatusBar} this - */ - setIcon : function(cls){ - this.activeThreadId++; - cls = cls || ''; - - if(this.rendered){ - if(this.currIconCls){ - this.statusEl.removeClass(this.currIconCls); - this.currIconCls = null; - } - if(cls.length > 0){ - this.statusEl.addClass(cls); - this.currIconCls = cls; - } - }else{ - this.currIconCls = cls; - } - return this; - }, - - /** - * Convenience method for setting the status text and icon to special values that are pre-configured to indicate - * a "busy" state, usually for loading or processing activities. - * @param {Object/String} config (optional) A config object in the same format supported by {@link #setStatus}, or a - * string to use as the status text (in which case all other options for setStatus will be defaulted). Use the - * text and/or iconCls properties on the config to override the default {@link #busyText} - * and {@link #busyIconCls} settings. If the config argument is not specified, {@link #busyText} and - * {@link #busyIconCls} will be used in conjunction with all of the default options for {@link #setStatus}. - * @return {Ext.ux.StatusBar} this - */ - showBusy : function(o){ - if(typeof o == 'string'){ - o = {text:o}; - } - o = Ext.applyIf(o || {}, { - text: this.busyText, - iconCls: this.busyIconCls - }); - return this.setStatus(o); - } -}); -Ext.reg('statusbar', Ext.ux.StatusBar); -/** - * @class Ext.ux.TabCloseMenu - * @extends Object - * Plugin (ptype = 'tabclosemenu') for adding a close context menu to tabs. Note that the menu respects - * the closable configuration on the tab. As such, commands like remove others and remove all will not - * remove items that are not closable. - * - * @constructor - * @param {Object} config The configuration options - * @ptype tabclosemenu - */ -Ext.ux.TabCloseMenu = Ext.extend(Object, { - /** - * @cfg {String} closeTabText - * The text for closing the current tab. Defaults to 'Close Tab'. - */ - closeTabText: 'Close Tab', - - /** - * @cfg {String} closeOtherTabsText - * The text for closing all tabs except the current one. Defaults to 'Close Other Tabs'. - */ - closeOtherTabsText: 'Close Other Tabs', - - /** - * @cfg {Boolean} showCloseAll - * Indicates whether to show the 'Close All' option. Defaults to true. - */ - showCloseAll: true, - - /** - * @cfg {String} closeAllTabsText - *

    The text for closing all tabs. Defaults to 'Close All Tabs'. - */ - closeAllTabsText: 'Close All Tabs', - - constructor : function(config){ - Ext.apply(this, config || {}); - }, - - //public - init : function(tabs){ - this.tabs = tabs; - tabs.on({ - scope: this, - contextmenu: this.onContextMenu, - destroy: this.destroy - }); - }, - - destroy : function(){ - Ext.destroy(this.menu); - delete this.menu; - delete this.tabs; - delete this.active; - }, - - // private - onContextMenu : function(tabs, item, e){ - this.active = item; - var m = this.createMenu(), - disableAll = true, - disableOthers = true, - closeAll = m.getComponent('closeall'); - - m.getComponent('close').setDisabled(!item.closable); - tabs.items.each(function(){ - if(this.closable){ - disableAll = false; - if(this != item){ - disableOthers = false; - return false; - } - } - }); - m.getComponent('closeothers').setDisabled(disableOthers); - if(closeAll){ - closeAll.setDisabled(disableAll); - } - - e.stopEvent(); - m.showAt(e.getPoint()); - }, - - createMenu : function(){ - if(!this.menu){ - var items = [{ - itemId: 'close', - text: this.closeTabText, - scope: this, - handler: this.onClose - }]; - if(this.showCloseAll){ - items.push('-'); - } - items.push({ - itemId: 'closeothers', - text: this.closeOtherTabsText, - scope: this, - handler: this.onCloseOthers - }); - if(this.showCloseAll){ - items.push({ - itemId: 'closeall', - text: this.closeAllTabsText, - scope: this, - handler: this.onCloseAll - }); - } - this.menu = new Ext.menu.Menu({ - items: items - }); - } - return this.menu; - }, - - onClose : function(){ - this.tabs.remove(this.active); - }, - - onCloseOthers : function(){ - this.doClose(true); - }, - - onCloseAll : function(){ - this.doClose(false); - }, - - doClose : function(excludeActive){ - var items = []; - this.tabs.items.each(function(item){ - if(item.closable){ - if(!excludeActive || item != this.active){ - items.push(item); - } - } - }, this); - Ext.each(items, function(item){ - this.tabs.remove(item); - }, this); - } -}); - -Ext.preg('tabclosemenu', Ext.ux.TabCloseMenu);Ext.ns('Ext.ux.grid'); - -/** - * @class Ext.ux.grid.TableGrid - * @extends Ext.grid.GridPanel - * A Grid which creates itself from an existing HTML table element. - * @history - * 2007-03-01 Original version by Nige "Animal" White - * 2007-03-10 jvs Slightly refactored to reuse existing classes * @constructor - * @param {String/HTMLElement/Ext.Element} table The table element from which this grid will be created - - * The table MUST have some type of size defined for the grid to fill. The container will be - * automatically set to position relative if it isn't already. - * @param {Object} config A config object that sets properties on this grid and has two additional (optional) - * properties: fields and columns which allow for customizing data fields and columns for this grid. - */ -Ext.ux.grid.TableGrid = function(table, config){ - config = config || - {}; - Ext.apply(this, config); - var cf = config.fields || [], ch = config.columns || []; - table = Ext.get(table); - - var ct = table.insertSibling(); - - var fields = [], cols = []; - var headers = table.query("thead th"); - for (var i = 0, h; h = headers[i]; i++) { - var text = h.innerHTML; - var name = 'tcol-' + i; - - fields.push(Ext.applyIf(cf[i] || - {}, { - name: name, - mapping: 'td:nth(' + (i + 1) + ')/@innerHTML' - })); - - cols.push(Ext.applyIf(ch[i] || - {}, { - 'header': text, - 'dataIndex': name, - 'width': h.offsetWidth, - 'tooltip': h.title, - 'sortable': true - })); - } - - var ds = new Ext.data.Store({ - reader: new Ext.data.XmlReader({ - record: 'tbody tr' - }, fields) - }); - - ds.loadData(table.dom); - - var cm = new Ext.grid.ColumnModel(cols); - - if (config.width || config.height) { - ct.setSize(config.width || 'auto', config.height || 'auto'); - } - else { - ct.setWidth(table.getWidth()); - } - - if (config.remove !== false) { - table.remove(); - } - - Ext.applyIf(this, { - 'ds': ds, - 'cm': cm, - 'sm': new Ext.grid.RowSelectionModel(), - autoHeight: true, - autoWidth: false - }); - Ext.ux.grid.TableGrid.superclass.constructor.call(this, ct, {}); -}; - -Ext.extend(Ext.ux.grid.TableGrid, Ext.grid.GridPanel); - -//backwards compat -Ext.grid.TableGrid = Ext.ux.grid.TableGrid; -Ext.ns('Ext.ux'); -/** - * @class Ext.ux.TabScrollerMenu - * @extends Object - * Plugin (ptype = 'tabscrollermenu') for adding a tab scroller menu to tabs. - * @constructor - * @param {Object} config Configuration options - * @ptype tabscrollermenu - */ -Ext.ux.TabScrollerMenu = Ext.extend(Object, { - /** - * @cfg {Number} pageSize How many items to allow per submenu. - */ - pageSize : 10, - /** - * @cfg {Number} maxText How long should the title of each {@link Ext.menu.Item} be. - */ - maxText : 15, - /** - * @cfg {String} menuPrefixText Text to prefix the submenus. - */ - menuPrefixText : 'Items', - constructor : function(config) { - config = config || {}; - Ext.apply(this, config); - }, - //private - init : function(tabPanel) { - Ext.apply(tabPanel, this.parentOverrides); - - tabPanel.tabScrollerMenu = this; - var thisRef = this; - - tabPanel.on({ - render : { - scope : tabPanel, - single : true, - fn : function() { - var newFn = tabPanel.createScrollers.createSequence(thisRef.createPanelsMenu, this); - tabPanel.createScrollers = newFn; - } - } - }); - }, - // private && sequeneced - createPanelsMenu : function() { - var h = this.stripWrap.dom.offsetHeight; - - //move the right menu item to the left 18px - var rtScrBtn = this.header.dom.firstChild; - Ext.fly(rtScrBtn).applyStyles({ - right : '18px' - }); - - var stripWrap = Ext.get(this.strip.dom.parentNode); - stripWrap.applyStyles({ - 'margin-right' : '36px' - }); - - // Add the new righthand menu - var scrollMenu = this.header.insertFirst({ - cls:'x-tab-tabmenu-right' - }); - scrollMenu.setHeight(h); - scrollMenu.addClassOnOver('x-tab-tabmenu-over'); - scrollMenu.on('click', this.showTabsMenu, this); - - this.scrollLeft.show = this.scrollLeft.show.createSequence(function() { - scrollMenu.show(); - }); - - this.scrollLeft.hide = this.scrollLeft.hide.createSequence(function() { - scrollMenu.hide(); - }); - - }, - /** - * Returns an the current page size (this.pageSize); - * @return {Number} this.pageSize The current page size. - */ - getPageSize : function() { - return this.pageSize; - }, - /** - * Sets the number of menu items per submenu "page size". - * @param {Number} pageSize The page size - */ - setPageSize : function(pageSize) { - this.pageSize = pageSize; - }, - /** - * Returns the current maxText length; - * @return {Number} this.maxText The current max text length. - */ - getMaxText : function() { - return this.maxText; - }, - /** - * Sets the maximum text size for each menu item. - * @param {Number} t The max text per each menu item. - */ - setMaxText : function(t) { - this.maxText = t; - }, - /** - * Returns the current menu prefix text String.; - * @return {String} this.menuPrefixText The current menu prefix text. - */ - getMenuPrefixText : function() { - return this.menuPrefixText; - }, - /** - * Sets the menu prefix text String. - * @param {String} t The menu prefix text. - */ - setMenuPrefixText : function(t) { - this.menuPrefixText = t; - }, - // private && applied to the tab panel itself. - parentOverrides : { - // all execute within the scope of the tab panel - // private - showTabsMenu : function(e) { - if (this.tabsMenu) { - this.tabsMenu.destroy(); - this.un('destroy', this.tabsMenu.destroy, this.tabsMenu); - this.tabsMenu = null; - } - this.tabsMenu = new Ext.menu.Menu(); - this.on('destroy', this.tabsMenu.destroy, this.tabsMenu); - - this.generateTabMenuItems(); - - var target = Ext.get(e.getTarget()); - var xy = target.getXY(); -// - //Y param + 24 pixels - xy[1] += 24; - - this.tabsMenu.showAt(xy); - }, - // private - generateTabMenuItems : function() { - var curActive = this.getActiveTab(); - var totalItems = this.items.getCount(); - var pageSize = this.tabScrollerMenu.getPageSize(); - - - if (totalItems > pageSize) { - var numSubMenus = Math.floor(totalItems / pageSize); - var remainder = totalItems % pageSize; - - // Loop through all of the items and create submenus in chunks of 10 - for (var i = 0 ; i < numSubMenus; i++) { - var curPage = (i + 1) * pageSize; - var menuItems = []; - - - for (var x = 0; x < pageSize; x++) { - index = x + curPage - pageSize; - var item = this.items.get(index); - menuItems.push(this.autoGenMenuItem(item)); - } - - this.tabsMenu.add({ - text : this.tabScrollerMenu.getMenuPrefixText() + ' ' + (curPage - pageSize + 1) + ' - ' + curPage, - menu : menuItems - }); - - } - // remaining items - if (remainder > 0) { - var start = numSubMenus * pageSize; - menuItems = []; - for (var i = start ; i < totalItems; i ++ ) { - var item = this.items.get(i); - menuItems.push(this.autoGenMenuItem(item)); - } - - this.tabsMenu.add({ - text : this.tabScrollerMenu.menuPrefixText + ' ' + (start + 1) + ' - ' + (start + menuItems.length), - menu : menuItems - }); - - } - } - else { - this.items.each(function(item) { - if (item.id != curActive.id && !item.hidden) { - this.tabsMenu.add(this.autoGenMenuItem(item)); - } - }, this); - } - }, - // private - autoGenMenuItem : function(item) { - var maxText = this.tabScrollerMenu.getMaxText(); - var text = Ext.util.Format.ellipsis(item.title, maxText); - - return { - text : text, - handler : this.showTabFromMenu, - scope : this, - disabled : item.disabled, - tabToShow : item, - iconCls : item.iconCls - } - - }, - // private - showTabFromMenu : function(menuItem) { - this.setActiveTab(menuItem.tabToShow); - } - } -}); - -Ext.reg('tabscrollermenu', Ext.ux.TabScrollerMenu); -Ext.ns('Ext.ux.tree'); - -/** - * @class Ext.ux.tree.XmlTreeLoader - * @extends Ext.tree.TreeLoader - *

    A TreeLoader that can convert an XML document into a hierarchy of {@link Ext.tree.TreeNode}s. - * Any text value included as a text node in the XML will be added to the parent node as an attribute - * called innerText. Also, the tag name of each XML node will be added to the tree node as - * an attribute called tagName.

    - *

    By default, this class expects that your source XML will provide the necessary attributes on each - * node as expected by the {@link Ext.tree.TreePanel} to display and load properly. However, you can - * provide your own custom processing of node attributes by overriding the {@link #processNode} method - * and modifying the attributes as needed before they are used to create the associated TreeNode.

    - * @constructor - * Creates a new XmlTreeloader. - * @param {Object} config A config object containing config properties. - */ -Ext.ux.tree.XmlTreeLoader = Ext.extend(Ext.tree.TreeLoader, { - /** - * @property XML_NODE_ELEMENT - * XML element node (value 1, read-only) - * @type Number - */ - XML_NODE_ELEMENT : 1, - /** - * @property XML_NODE_TEXT - * XML text node (value 3, read-only) - * @type Number - */ - XML_NODE_TEXT : 3, - - // private override - processResponse : function(response, node, callback){ - var xmlData = response.responseXML, - root = xmlData.documentElement || xmlData; - - try{ - node.beginUpdate(); - node.appendChild(this.parseXml(root)); - node.endUpdate(); - - this.runCallback(callback, scope || node, [node]); - }catch(e){ - this.handleFailure(response); - } - }, - - // private - parseXml : function(node) { - var nodes = []; - Ext.each(node.childNodes, function(n){ - if(n.nodeType == this.XML_NODE_ELEMENT){ - var treeNode = this.createNode(n); - if(n.childNodes.length > 0){ - var child = this.parseXml(n); - if(typeof child == 'string'){ - treeNode.attributes.innerText = child; - }else{ - treeNode.appendChild(child); - } - } - nodes.push(treeNode); - } - else if(n.nodeType == this.XML_NODE_TEXT){ - var text = n.nodeValue.trim(); - if(text.length > 0){ - return nodes = text; - } - } - }, this); - - return nodes; - }, - - // private override - createNode : function(node){ - var attr = { - tagName: node.tagName - }; - - Ext.each(node.attributes, function(a){ - attr[a.nodeName] = a.nodeValue; - }); - - this.processAttributes(attr); - - return Ext.ux.tree.XmlTreeLoader.superclass.createNode.call(this, attr); - }, - - /* - * Template method intended to be overridden by subclasses that need to provide - * custom attribute processing prior to the creation of each TreeNode. This method - * will be passed a config object containing existing TreeNode attribute name/value - * pairs which can be modified as needed directly (no need to return the object). - */ - processAttributes: Ext.emptyFn -}); - -//backwards compat -Ext.ux.XmlTreeLoader = Ext.ux.tree.XmlTreeLoader; -/** - * @class Ext.ux.ValidationStatus - * A {@link Ext.StatusBar} plugin that provides automatic error notification when the - * associated form contains validation errors. - * @extends Ext.Component - * @constructor - * Creates a new ValiationStatus plugin - * @param {Object} config A config object - */ -Ext.ux.ValidationStatus = Ext.extend(Ext.Component, { - /** - * @cfg {String} errorIconCls - * The {@link #iconCls} value to be applied to the status message when there is a - * validation error. Defaults to 'x-status-error'. - */ - errorIconCls : 'x-status-error', - /** - * @cfg {String} errorListCls - * The css class to be used for the error list when there are validation errors. - * Defaults to 'x-status-error-list'. - */ - errorListCls : 'x-status-error-list', - /** - * @cfg {String} validIconCls - * The {@link #iconCls} value to be applied to the status message when the form - * validates. Defaults to 'x-status-valid'. - */ - validIconCls : 'x-status-valid', - - /** - * @cfg {String} showText - * The {@link #text} value to be applied when there is a form validation error. - * Defaults to 'The form has errors (click for details...)'. - */ - showText : 'The form has errors (click for details...)', - /** - * @cfg {String} showText - * The {@link #text} value to display when the error list is displayed. - * Defaults to 'Click again to hide the error list'. - */ - hideText : 'Click again to hide the error list', - /** - * @cfg {String} submitText - * The {@link #text} value to be applied when the form is being submitted. - * Defaults to 'Saving...'. - */ - submitText : 'Saving...', - - // private - init : function(sb){ - sb.on('render', function(){ - this.statusBar = sb; - this.monitor = true; - this.errors = new Ext.util.MixedCollection(); - this.listAlign = (sb.statusAlign=='right' ? 'br-tr?' : 'bl-tl?'); - - if(this.form){ - this.form = Ext.getCmp(this.form).getForm(); - this.startMonitoring(); - this.form.on('beforeaction', function(f, action){ - if(action.type == 'submit'){ - // Ignore monitoring while submitting otherwise the field validation - // events cause the status message to reset too early - this.monitor = false; - } - }, this); - var startMonitor = function(){ - this.monitor = true; - }; - this.form.on('actioncomplete', startMonitor, this); - this.form.on('actionfailed', startMonitor, this); - } - }, this, {single:true}); - sb.on({ - scope: this, - afterlayout:{ - single: true, - fn: function(){ - // Grab the statusEl after the first layout. - sb.statusEl.getEl().on('click', this.onStatusClick, this, {buffer:200}); - } - }, - beforedestroy:{ - single: true, - fn: this.onDestroy - } - }); - }, - - // private - startMonitoring : function(){ - this.form.items.each(function(f){ - f.on('invalid', this.onFieldValidation, this); - f.on('valid', this.onFieldValidation, this); - }, this); - }, - - // private - stopMonitoring : function(){ - this.form.items.each(function(f){ - f.un('invalid', this.onFieldValidation, this); - f.un('valid', this.onFieldValidation, this); - }, this); - }, - - // private - onDestroy : function(){ - this.stopMonitoring(); - this.statusBar.statusEl.un('click', this.onStatusClick, this); - Ext.ux.ValidationStatus.superclass.onDestroy.call(this); - }, - - // private - onFieldValidation : function(f, msg){ - if(!this.monitor){ - return false; - } - if(msg){ - this.errors.add(f.id, {field:f, msg:msg}); - }else{ - this.errors.removeKey(f.id); - } - this.updateErrorList(); - if(this.errors.getCount() > 0){ - if(this.statusBar.getText() != this.showText){ - this.statusBar.setStatus({text:this.showText, iconCls:this.errorIconCls}); - } - }else{ - this.statusBar.clearStatus().setIcon(this.validIconCls); - } - }, - - // private - updateErrorList : function(){ - if(this.errors.getCount() > 0){ - var msg = '
      '; - this.errors.each(function(err){ - msg += ('
    • ' + err.msg + '
    • '); - }, this); - this.getMsgEl().update(msg+'
    '); - }else{ - this.getMsgEl().update(''); - } - }, - - // private - getMsgEl : function(){ - if(!this.msgEl){ - this.msgEl = Ext.DomHelper.append(Ext.getBody(), { - cls: this.errorListCls+' x-hide-offsets' - }, true); - - this.msgEl.on('click', function(e){ - var t = e.getTarget('li', 10, true); - if(t){ - Ext.getCmp(t.id.split('x-err-')[1]).focus(); - this.hideErrors(); - } - }, this, {stopEvent:true}); // prevent anchor click navigation - } - return this.msgEl; - }, - - // private - showErrors : function(){ - this.updateErrorList(); - this.getMsgEl().alignTo(this.statusBar.getEl(), this.listAlign).slideIn('b', {duration:0.3, easing:'easeOut'}); - this.statusBar.setText(this.hideText); - this.form.getEl().on('click', this.hideErrors, this, {single:true}); // hide if the user clicks directly into the form - }, - - // private - hideErrors : function(){ - var el = this.getMsgEl(); - if(el.isVisible()){ - el.slideOut('b', {duration:0.2, easing:'easeIn'}); - this.statusBar.setText(this.showText); - } - this.form.getEl().un('click', this.hideErrors, this); - }, - - // private - onStatusClick : function(){ - if(this.getMsgEl().isVisible()){ - this.hideErrors(); - }else if(this.errors.getCount() > 0){ - this.showErrors(); - } - } -});(function() { - Ext.override(Ext.list.Column, { - init : function() { - var types = Ext.data.Types, - st = this.sortType; - - if(this.type){ - if(Ext.isString(this.type)){ - this.type = Ext.data.Types[this.type.toUpperCase()] || types.AUTO; - } - }else{ - this.type = types.AUTO; - } - - // named sortTypes are supported, here we look them up - if(Ext.isString(st)){ - this.sortType = Ext.data.SortTypes[st]; - }else if(Ext.isEmpty(st)){ - this.sortType = this.type.sortType; - } - } - }); - - Ext.tree.Column = Ext.extend(Ext.list.Column, {}); - Ext.tree.NumberColumn = Ext.extend(Ext.list.NumberColumn, {}); - Ext.tree.DateColumn = Ext.extend(Ext.list.DateColumn, {}); - Ext.tree.BooleanColumn = Ext.extend(Ext.list.BooleanColumn, {}); - - Ext.reg('tgcolumn', Ext.tree.Column); - Ext.reg('tgnumbercolumn', Ext.tree.NumberColumn); - Ext.reg('tgdatecolumn', Ext.tree.DateColumn); - Ext.reg('tgbooleancolumn', Ext.tree.BooleanColumn); -})(); -/** - * @class Ext.ux.tree.TreeGridNodeUI - * @extends Ext.tree.TreeNodeUI - */ -Ext.ux.tree.TreeGridNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { - isTreeGridNodeUI: true, - - renderElements : function(n, a, targetNode, bulkRender){ - var t = n.getOwnerTree(), - cols = t.columns, - c = cols[0], - i, buf, len; - - this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : ''; - - buf = [ - '', - '', - '', - '', this.indentMarkup, "", - '', - '', - '', - '', (c.tpl ? c.tpl.apply(a) : a[c.dataIndex] || c.text), '', - '' - ]; - - for(i = 1, len = cols.length; i < len; i++){ - c = cols[i]; - buf.push( - '', - '
    ', - (c.tpl ? c.tpl.apply(a) : a[c.dataIndex]), - '
    ', - '' - ); - } - - buf.push( - '', - '' - ); - for(i = 0, len = cols.length; i'); - } - buf.push(''); - - if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){ - this.wrap = Ext.DomHelper.insertHtml("beforeBegin", n.nextSibling.ui.getEl(), buf.join('')); - }else{ - this.wrap = Ext.DomHelper.insertHtml("beforeEnd", targetNode, buf.join('')); - } - - this.elNode = this.wrap.childNodes[0]; - this.ctNode = this.wrap.childNodes[1].firstChild.firstChild; - var cs = this.elNode.firstChild.childNodes; - this.indentNode = cs[0]; - this.ecNode = cs[1]; - this.iconNode = cs[2]; - this.anchor = cs[3]; - this.textNode = cs[3].firstChild; - }, - - // private - animExpand : function(cb){ - this.ctNode.style.display = ""; - Ext.ux.tree.TreeGridNodeUI.superclass.animExpand.call(this, cb); - } -}); - -Ext.ux.tree.TreeGridRootNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { - isTreeGridNodeUI: true, - - // private - render : function(){ - if(!this.rendered){ - this.wrap = this.ctNode = this.node.ownerTree.innerCt.dom; - this.node.expanded = true; - } - - if(Ext.isWebKit) { - // weird table-layout: fixed issue in webkit - var ct = this.ctNode; - ct.style.tableLayout = null; - (function() { - ct.style.tableLayout = 'fixed'; - }).defer(1); - } - }, - - destroy : function(){ - if(this.elNode){ - Ext.dd.Registry.unregister(this.elNode.id); - } - delete this.node; - }, - - collapse : Ext.emptyFn, - expand : Ext.emptyFn -});/** - * @class Ext.tree.ColumnResizer - * @extends Ext.util.Observable - */ -Ext.tree.ColumnResizer = Ext.extend(Ext.util.Observable, { - /** - * @cfg {Number} minWidth The minimum width the column can be dragged to. - * Defaults to 14. - */ - minWidth: 14, - - constructor: function(config){ - Ext.apply(this, config); - Ext.tree.ColumnResizer.superclass.constructor.call(this); - }, - - init : function(tree){ - this.tree = tree; - tree.on('render', this.initEvents, this); - }, - - initEvents : function(tree){ - tree.mon(tree.innerHd, 'mousemove', this.handleHdMove, this); - this.tracker = new Ext.dd.DragTracker({ - onBeforeStart: this.onBeforeStart.createDelegate(this), - onStart: this.onStart.createDelegate(this), - onDrag: this.onDrag.createDelegate(this), - onEnd: this.onEnd.createDelegate(this), - tolerance: 3, - autoStart: 300 - }); - this.tracker.initEl(tree.innerHd); - tree.on('beforedestroy', this.tracker.destroy, this.tracker); - }, - - handleHdMove : function(e, t){ - var hw = 5, - x = e.getPageX(), - hd = e.getTarget('.x-treegrid-hd', 3, true); - - if(hd){ - var r = hd.getRegion(), - ss = hd.dom.style, - pn = hd.dom.parentNode; - - if(x - r.left <= hw && hd.dom !== pn.firstChild) { - var ps = hd.dom.previousSibling; - while(ps && Ext.fly(ps).hasClass('x-treegrid-hd-hidden')) { - ps = ps.previousSibling; - } - if(ps) { - this.activeHd = Ext.get(ps); - ss.cursor = Ext.isWebKit ? 'e-resize' : 'col-resize'; - } - } else if(r.right - x <= hw) { - var ns = hd.dom; - while(ns && Ext.fly(ns).hasClass('x-treegrid-hd-hidden')) { - ns = ns.previousSibling; - } - if(ns) { - this.activeHd = Ext.get(ns); - ss.cursor = Ext.isWebKit ? 'w-resize' : 'col-resize'; - } - } else{ - delete this.activeHd; - ss.cursor = ''; - } - } - }, - - onBeforeStart : function(e){ - this.dragHd = this.activeHd; - return !!this.dragHd; - }, - - onStart : function(e){ - this.dragHeadersDisabled = this.tree.headersDisabled; - this.tree.headersDisabled = true; - this.proxy = this.tree.body.createChild({cls:'x-treegrid-resizer'}); - this.proxy.setHeight(this.tree.body.getHeight()); - - var x = this.tracker.getXY()[0]; - - this.hdX = this.dragHd.getX(); - this.hdIndex = this.tree.findHeaderIndex(this.dragHd); - - this.proxy.setX(this.hdX); - this.proxy.setWidth(x-this.hdX); - - this.maxWidth = this.tree.outerCt.getWidth() - this.tree.innerBody.translatePoints(this.hdX).left; - }, - - onDrag : function(e){ - var cursorX = this.tracker.getXY()[0]; - this.proxy.setWidth((cursorX-this.hdX).constrain(this.minWidth, this.maxWidth)); - }, - - onEnd : function(e){ - var nw = this.proxy.getWidth(), - tree = this.tree, - disabled = this.dragHeadersDisabled; - - this.proxy.remove(); - delete this.dragHd; - - tree.columns[this.hdIndex].width = nw; - tree.updateColumnWidths(); - - setTimeout(function(){ - tree.headersDisabled = disabled; - }, 100); - } -});Ext.ns('Ext.ux.tree'); - -/** - * @class Ext.ux.tree.TreeGridSorter - * @extends Ext.tree.TreeSorter - * Provides sorting of nodes in a {@link Ext.ux.tree.TreeGrid}. The TreeGridSorter automatically monitors events on the - * associated TreeGrid that might affect the tree's sort order (beforechildrenrendered, append, insert and textchange). - * Example usage:
    - *
    
    - new Ext.ux.tree.TreeGridSorter(myTreeGrid, {
    -     folderSort: true,
    -     dir: "desc",
    -     sortType: function(node) {
    -         // sort by a custom, typed attribute:
    -         return parseInt(node.id, 10);
    -     }
    - });
    - 
    - * @constructor - * @param {TreeGrid} tree - * @param {Object} config - */ -Ext.ux.tree.TreeGridSorter = Ext.extend(Ext.tree.TreeSorter, { - /** - * @cfg {Array} sortClasses The CSS classes applied to a header when it is sorted. (defaults to ['sort-asc', 'sort-desc']) - */ - sortClasses : ['sort-asc', 'sort-desc'], - /** - * @cfg {String} sortAscText The text displayed in the 'Sort Ascending' menu item (defaults to 'Sort Ascending') - */ - sortAscText : 'Sort Ascending', - /** - * @cfg {String} sortDescText The text displayed in the 'Sort Descending' menu item (defaults to 'Sort Descending') - */ - sortDescText : 'Sort Descending', - - constructor : function(tree, config) { - if(!Ext.isObject(config)) { - config = { - property: tree.columns[0].dataIndex || 'text', - folderSort: true - } - } - - Ext.ux.tree.TreeGridSorter.superclass.constructor.apply(this, arguments); - - this.tree = tree; - tree.on('headerclick', this.onHeaderClick, this); - tree.ddAppendOnly = true; - - var me = this; - this.defaultSortFn = function(n1, n2){ - - var desc = me.dir && me.dir.toLowerCase() == 'desc', - prop = me.property || 'text', - sortType = me.sortType, - caseSensitive = me.caseSensitive === true, - leafAttr = me.leafAttr || 'leaf', - attr1 = n1.attributes, - attr2 = n2.attributes; - - if(me.folderSort){ - if(attr1[leafAttr] && !attr2[leafAttr]){ - return 1; - } - if(!attr1[leafAttr] && attr2[leafAttr]){ - return -1; - } - } - var prop1 = attr1[prop], - prop2 = attr2[prop], - v1 = sortType ? sortType(prop1) : (caseSensitive ? prop1 : prop1.toUpperCase()); - v2 = sortType ? sortType(prop2) : (caseSensitive ? prop2 : prop2.toUpperCase()); - - if(v1 < v2){ - return desc ? +1 : -1; - }else if(v1 > v2){ - return desc ? -1 : +1; - }else{ - return 0; - } - }; - - tree.on('afterrender', this.onAfterTreeRender, this, {single: true}); - tree.on('headermenuclick', this.onHeaderMenuClick, this); - }, - - onAfterTreeRender : function() { - if(this.tree.hmenu){ - this.tree.hmenu.insert(0, - {itemId:'asc', text: this.sortAscText, cls: 'xg-hmenu-sort-asc'}, - {itemId:'desc', text: this.sortDescText, cls: 'xg-hmenu-sort-desc'} - ); - } - this.updateSortIcon(0, 'asc'); - }, - - onHeaderMenuClick : function(c, id, index) { - if(id === 'asc' || id === 'desc') { - this.onHeaderClick(c, null, index); - return false; - } - }, - - onHeaderClick : function(c, el, i) { - if(c && !this.tree.headersDisabled){ - var me = this; - - me.property = c.dataIndex; - me.dir = c.dir = (c.dir === 'desc' ? 'asc' : 'desc'); - me.sortType = c.sortType; - me.caseSensitive === Ext.isBoolean(c.caseSensitive) ? c.caseSensitive : this.caseSensitive; - me.sortFn = c.sortFn || this.defaultSortFn; - - this.tree.root.cascade(function(n) { - if(!n.isLeaf()) { - me.updateSort(me.tree, n); - } - }); - - this.updateSortIcon(i, c.dir); - } - }, - - // private - updateSortIcon : function(col, dir){ - var sc = this.sortClasses, - hds = this.tree.innerHd.select('td').removeClass(sc); - hds.item(col).addClass(sc[dir == 'desc' ? 1 : 0]); - } -});/** - * @class Ext.ux.tree.TreeGridLoader - * @extends Ext.tree.TreeLoader - */ -Ext.ux.tree.TreeGridLoader = Ext.extend(Ext.tree.TreeLoader, { - createNode : function(attr) { - if (!attr.uiProvider) { - attr.uiProvider = Ext.ux.tree.TreeGridNodeUI; - } - return Ext.tree.TreeLoader.prototype.createNode.call(this, attr); - } -});/** - * @class Ext.ux.tree.TreeGrid - * @extends Ext.tree.TreePanel - * - * @xtype treegrid - */ -Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, { - rootVisible : false, - useArrows : true, - lines : false, - borderWidth : Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell - cls : 'x-treegrid', - - columnResize : true, - enableSort : true, - reserveScrollOffset : true, - enableHdMenu : true, - - columnsText : 'Columns', - - initComponent : function() { - if(!this.root) { - this.root = new Ext.tree.AsyncTreeNode({text: 'Root'}); - } - - // initialize the loader - var l = this.loader; - if(!l){ - l = new Ext.ux.tree.TreeGridLoader({ - dataUrl: this.dataUrl, - requestMethod: this.requestMethod, - store: this.store - }); - }else if(Ext.isObject(l) && !l.load){ - l = new Ext.ux.tree.TreeGridLoader(l); - } - this.loader = l; - - Ext.ux.tree.TreeGrid.superclass.initComponent.call(this); - - this.initColumns(); - - if(this.enableSort) { - this.treeGridSorter = new Ext.ux.tree.TreeGridSorter(this, this.enableSort); - } - - if(this.columnResize){ - this.colResizer = new Ext.tree.ColumnResizer(this.columnResize); - this.colResizer.init(this); - } - - var c = this.columns; - if(!this.internalTpl){ - this.internalTpl = new Ext.XTemplate( - '
    ', - '
    ', - '
    ', - '', - '', - '', - '', - '', - '
    ', - '
    ', - this.enableHdMenu ? '' : '', - '{header}', - '
    ', - '
    ', - '
    ', - '
    ', - '
    ', - '
    ', - '
    ' - ); - } - - if(!this.colgroupTpl) { - this.colgroupTpl = new Ext.XTemplate( - '' - ); - } - }, - - initColumns : function() { - var cs = this.columns, - len = cs.length, - columns = [], - i, c; - - for(i = 0; i < len; i++){ - c = cs[i]; - if(!c.isColumn) { - c.xtype = c.xtype ? (/^tg/.test(c.xtype) ? c.xtype : 'tg' + c.xtype) : 'tgcolumn'; - c = Ext.create(c); - } - c.init(this); - columns.push(c); - - if(this.enableSort !== false && c.sortable !== false) { - c.sortable = true; - this.enableSort = true; - } - } - - this.columns = columns; - }, - - onRender : function(){ - Ext.tree.TreePanel.superclass.onRender.apply(this, arguments); - - this.el.addClass('x-treegrid'); - - this.outerCt = this.body.createChild({ - cls:'x-tree-root-ct x-treegrid-ct ' + (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines') - }); - - this.internalTpl.overwrite(this.outerCt, {columns: this.columns}); - - this.mainHd = Ext.get(this.outerCt.dom.firstChild); - this.innerHd = Ext.get(this.mainHd.dom.firstChild); - this.innerBody = Ext.get(this.outerCt.dom.lastChild); - this.innerCt = Ext.get(this.innerBody.dom.firstChild); - - this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns}); - - if(this.hideHeaders){ - this.el.child('.x-grid3-header').setDisplayed('none'); - } - else if(this.enableHdMenu !== false){ - this.hmenu = new Ext.menu.Menu({id: this.id + '-hctx'}); - if(this.enableColumnHide !== false){ - this.colMenu = new Ext.menu.Menu({id: this.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); - } - }, - - setRootNode : function(node){ - node.attributes.uiProvider = Ext.ux.tree.TreeGridRootNodeUI; - node = Ext.ux.tree.TreeGrid.superclass.setRootNode.call(this, node); - if(this.innerCt) { - this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns}); - } - return node; - }, - - clearInnerCt : function(){ - if(Ext.isIE){ - var dom = this.innerCt.dom; - while(dom.firstChild){ - dom.removeChild(dom.firstChild); - } - }else{ - Ext.ux.tree.TreeGrid.superclass.clearInnerCt.call(this); - } - }, - - initEvents : function() { - Ext.ux.tree.TreeGrid.superclass.initEvents.apply(this, arguments); - - this.mon(this.innerBody, 'scroll', this.syncScroll, this); - this.mon(this.innerHd, 'click', this.handleHdDown, this); - this.mon(this.mainHd, { - scope: this, - mouseover: this.handleHdOver, - mouseout: this.handleHdOut - }); - }, - - onResize : function(w, h) { - Ext.ux.tree.TreeGrid.superclass.onResize.apply(this, arguments); - - var bd = this.innerBody.dom; - var hd = this.innerHd.dom; - - if(!bd){ - return; - } - - if(Ext.isNumber(h)){ - bd.style.height = this.body.getHeight(true) - hd.offsetHeight + 'px'; - } - - if(Ext.isNumber(w)){ - var sw = Ext.num(this.scrollOffset, Ext.getScrollBarWidth()); - if(this.reserveScrollOffset || ((bd.offsetWidth - bd.clientWidth) > 10)){ - this.setScrollOffset(sw); - }else{ - var me = this; - setTimeout(function(){ - me.setScrollOffset(bd.offsetWidth - bd.clientWidth > 10 ? sw : 0); - }, 10); - } - } - }, - - updateColumnWidths : function() { - var cols = this.columns, - colCount = cols.length, - groups = this.outerCt.query('colgroup'), - groupCount = groups.length, - c, g, i, j; - - for(i = 0; i 0 && this.columns[index]) { - this.setColumnVisible(index, !item.checked); - } - } - - return true; - }, - - setColumnVisible : function(index, visible) { - this.columns[index].hidden = !visible; - this.updateColumnWidths(); - }, - - /** - * Scrolls the grid to the top - */ - scrollToTop : function(){ - this.innerBody.dom.scrollTop = 0; - this.innerBody.dom.scrollLeft = 0; - }, - - // private - syncScroll : function(){ - this.syncHeaderScroll(); - var mb = this.innerBody.dom; - this.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop); - }, - - // private - syncHeaderScroll : function(){ - var mb = this.innerBody.dom; - this.innerHd.dom.scrollLeft = mb.scrollLeft; - this.innerHd.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore) - }, - - registerNode : function(n) { - Ext.ux.tree.TreeGrid.superclass.registerNode.call(this, n); - if(!n.uiProvider && !n.isRoot && !n.ui.isTreeGridNodeUI) { - n.ui = new Ext.ux.tree.TreeGridNodeUI(n); - } - } -}); - -Ext.reg('treegrid', Ext.ux.tree.TreeGrid); \ No newline at end of file