if (Ext.version == '3.0') { Ext.override(Ext.grid.GridView, { ensureVisible : function(row, col, hscroll) { var resolved = this.resolveCell(row, col, hscroll); if (!resolved || !resolved.row) { return; } var rowEl = resolved.row, cellEl = resolved.cell, c = this.scroller.dom, ctop = 0, p = rowEl, stop = this.el.dom; var p = rowEl, stop = this.el.dom; while (p && p != stop) { ctop += p.offsetTop; p = p.offsetParent; } ctop -= this.mainHd.dom.offsetHeight; var cbot = ctop + rowEl.offsetHeight; var ch = c.clientHeight; var stop = parseInt(c.scrollTop, 10); var sbot = stop + ch; if (ctop < stop) { c.scrollTop = ctop; } else if (cbot > sbot) { c.scrollTop = cbot - ch; } if (hscroll !== false) { var cleft = parseInt(cellEl.offsetLeft, 10); var cright = cleft + cellEl.offsetWidth; var sleft = parseInt(c.scrollLeft, 10); var sright = sleft + c.clientWidth; if (cleft < sleft) { c.scrollLeft = cleft; } else if (cright > sright) { c.scrollLeft = cright - c.clientWidth; } } return this.getResolvedXY(resolved); } }); } Ext.namespace('Ext.ux.maximgb.tg'); /** * This class shouldn't be created directly use NestedSetStore or * AdjacencyListStore instead. * * @abstract */ Ext.ux.maximgb.tg.AbstractTreeStore = Ext.extend(Ext.data.Store, { /** * @cfg {String} is_leaf_field_name Record leaf flag field name. */ leaf_field_name : 'Leaf', /** * Current page offset. * * @access private */ page_offset : 0, /** * Current active node. * * @access private */ active_node : null, /** * @constructor */ constructor : function(config) { Ext.ux.maximgb.tg.AbstractTreeStore.superclass.constructor.call(this, config); if (!this.paramNames.active_node) { this.paramNames.active_node = 'anode'; } this.addEvents( /** * @event beforeexpandnode Fires before node expand. Return * false to cancel operation. param {AbstractTreeStore} * this param {Record} record */ 'beforeexpandnode', /** * @event expandnode Fires after node expand. param * {AbstractTreeStore} this param {Record} record */ 'expandnode', /** * @event expandnodefailed Fires when expand node operation is * failed. param {AbstractTreeStore} this param {id} * Record id param {Record} Record, may be undefined */ 'expandnodefailed', /** * @event beforecollapsenode Fires before node collapse. Return * false to cancel operation. param {AbstractTreeStore} * this param {Record} record */ 'beforecollapsenode', /** * @event collapsenode Fires after node collapse. param * {AbstractTreeStore} this param {Record} record */ 'collapsenode', /** * @event beforeactivenodechange Fires before active node * change. Return false to cancel operation. param * {AbstractTreeStore} this param {Record} old active * node record param {Record} new active node record */ 'beforeactivenodechange', /** * @event activenodechange Fires after active node change. param * {AbstractTreeStore} this param {Record} old active * node record param {Record} new active node record */ 'activenodechange'); }, // Store methods. // ----------------------------------------------------------------------------------------------- /** * Removes record and all its descendants. * * @access public * @param {Record} * record Record to remove. */ remove : function(record) { // ----- Modification start if (record === this.active_node) { this.setActiveNode(null); } this.removeNodeDescendants(record); // ----- End of modification Ext.ux.maximgb.tg.AbstractTreeStore.superclass.remove .call(this, record); }, /** * Removes node descendants. * * @access private */ removeNodeDescendants : function(rc) { var i, len, children = this.getNodeChildren(rc); for (i = 0, len = children.length; i < len; i++) { this.remove(children[i]); } }, /** * Loads current active record data. */ load : function(options) { if (options) { if (options.params) { if (options.params[this.paramNames.active_node] === undefined) { options.params[this.paramNames.active_node] = this.active_node ? this.active_node.id : null; } } else { options.params = {}; options.params[this.paramNames.active_node] = this.active_node ? this.active_node.id : null; } } else { options = { params : {} }; options.params[this.paramNames.active_node] = this.active_node ? this.active_node.id : null; } if (options.params[this.paramNames.active_node] !== null) { options.add = true; } return Ext.ux.maximgb.tg.AbstractTreeStore.superclass.load.call(this, options); }, /** * Called as a callback by the Reader during load operation. * * @access private */ loadRecords : function(o, options, success) { if (!o || success === false) { if (success !== false) { this.fireEvent("load", this, [], options); } if (options.callback) { options.callback .call(options.scope || this, [], options, false); } return; } var r = o.records, t = o.totalRecords || r.length, page_offset = this .getPageOffsetFromOptions(options), loaded_node_id = this .getLoadedNodeIdFromOptions(options), loaded_node, i, len, record, idx, updated, self = this; if (!options || options.add !== true/* || loaded_node_id === null */) { if (this.pruneModifiedRecords) { this.modified = []; } for (var i = 0, len = r.length; i < len; i++) { r[i].join(this); } if (this.snapshot) { this.data = this.snapshot; delete this.snapshot; } this.data.clear(); this.data.addAll(r); this.page_offset = page_offset; this.totalLength = t; this.applySort(); this.fireEvent("datachanged", this); } else { if (loaded_node_id) { loaded_node = this.getById(loaded_node_id); } if (loaded_node) { this.setNodeChildrenOffset(loaded_node, page_offset); this.setNodeChildrenTotalCount(loaded_node, Math.max(t, r.length)); this.removeNodeDescendants(loaded_node); } this.suspendEvents(); updated = {}; for (i = 0, len = r.length; i < len; i++) { record = r[i]; idx = this.indexOfId(record.id); if (idx == -1) { updated[record.id] = false; } else { updated[record.id] = true; this.setNodeExpanded(record, this.isExpandedNode(this .getAt(idx))); } this.add(record); } this.applySort(); this.resumeEvents(); r.sort(function(r1, r2) { var idx1 = self.data.indexOf(r1), idx2 = self.data.indexOf(r2), r; if (idx1 > idx2) { r = 1; } else { r = -1; } return r; }); for (i = 0, len = r.length; i < len; i++) { record = r[i]; if (updated[record.id] == true) { this.fireEvent('update', this, record, Ext.data.Record.COMMIT); } else { this.fireEvent("add", this, [record], this.data .indexOf(record)); } } } this.fireEvent("load", this, r, options); if (options.callback) { options.callback.call(options.scope || this, r, options, true); } }, /** * Sort the Records. * * @access public */ sort : function(fieldName, dir) { if (this.remoteSort) { this.setActiveNode(null); if (this.lastOptions) { this.lastOptions.add = false; if (this.lastOptions.params) { this.lastOptions.params[this.paramNames.active_node] = null; } } } return Ext.ux.maximgb.tg.AbstractTreeStore.superclass.sort.call(this, fieldName, dir); }, /** * Applyes current sort method. * * @access private */ applySort : function() { if (this.sortInfo && !this.remoteSort) { var s = this.sortInfo, f = s.field; this.sortData(f, s.direction); } // ----- Modification start else { this.applyTreeSort(); } // ----- End of modification }, /** * Sorts data according to sort params and then applyes tree sorting. * * @access private */ sortData : function(f, direction) { direction = direction || 'ASC'; var st = this.fields.get(f).sortType; var fn = function(r1, r2) { var v1 = st(r1.data[f]), v2 = st(r2.data[f]); return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0); }; this.data.sort(direction, fn); if (this.snapshot && this.snapshot != this.data) { this.snapshot.sort(direction, fn); } // ----- Modification start this.applyTreeSort(); // ----- End of modification }, // Tree support methods. // ----------------------------------------------------------------------------------------------- /** * Sorts store data with respect to nodes parent-child relation. Every child * node will be positioned after its parent. * * @access public */ applyTreeSort : function() { var i, len, temp, rec, records = [], roots = this.getRootNodes(); // Sorting data for (i = 0, len = roots.length; i < len; i++) { rec = roots[i]; records.push(rec); this.collectNodeChildrenTreeSorted(records, rec); } if (records.length > 0) { this.data.clear(); this.data.addAll(records); } // Sorting the snapshot if one present. if (this.snapshot && this.snapshot !== this.data) { temp = this.data; this.data = this.snapshot; this.snapshot = null; this.applyTreeSort(); this.snapshot = this.data; this.data = temp; } }, /** * Recusively collects rec descendants and adds them to records[] array. * * @access private * @param {Record[]} * records * @param {Record} * rec */ collectNodeChildrenTreeSorted : function(records, rec) { var i, len, child, children = this.getNodeChildren(rec); for (i = 0, len = children.length; i < len; i++) { child = children[i]; records.push(child); this.collectNodeChildrenTreeSorted(records, child); } }, /** * Returns current active node. * * @access public * @return {Record} */ getActiveNode : function() { return this.active_node; }, /** * Sets active node. * * @access public * @param {Record} * rc Record to set active. */ setActiveNode : function(rc) { if (this.active_node !== rc) { if (rc) { if (this.data.indexOf(rc) != -1) { if (this.fireEvent('beforeactivenodechange', this, this.active_node, rc) !== false) { this.active_node = rc; this.fireEvent('activenodechange', this, this.active_node, rc); } } else { throw "Given record is not from the store."; } } else { if (this.fireEvent('beforeactivenodechange', this, this.active_node, rc) !== false) { this.active_node = rc; this.fireEvent('activenodechange', this, this.active_node, rc); } } } }, /** * Returns true if node is expanded. * * @access public * @param {Record} * rc */ isExpandedNode : function(rc) { return rc.ux_maximgb_tg_expanded === true; }, /** * Sets node expanded flag. * * @access private */ setNodeExpanded : function(rc, value) { rc.ux_maximgb_tg_expanded = value; }, /** * Returns true if node's ancestors are all expanded - node is visible. * * @access public * @param {Record} * rc */ isVisibleNode : function(rc) { var i, len, ancestors = this.getNodeAncestors(rc), result = true; for (i = 0, len = ancestors.length; i < len; i++) { result = result && this.isExpandedNode(ancestors[i]); if (!result) { break; } } return result; }, /** * Returns true if node is a leaf. * * @access public * @return {Boolean} */ isLeafNode : function(rc) { return rc.get(this.leaf_field_name) == true; }, /** * Returns true if node was loaded. * * @access public * @return {Boolean} */ isLoadedNode : function(rc) { var result; if (rc.ux_maximgb_tg_loaded !== undefined) { result = rc.ux_maximgb_tg_loaded; } else if (this.isLeafNode(rc) || this.hasChildNodes(rc)) { result = true; } else { result = false; } return result; }, /** * Sets node loaded state. * * @access private * @param {Record} * rc * @param {Boolean} * value */ setNodeLoaded : function(rc, value) { rc.ux_maximgb_tg_loaded = value; }, /** * Returns node's children offset. * * @access public * @param {Record} * rc * @return {Integer} */ getNodeChildrenOffset : function(rc) { return rc.ux_maximgb_tg_offset || 0; }, /** * Sets node's children offset. * * @access private * @param {Record} * rc * @parma {Integer} value */ setNodeChildrenOffset : function(rc, value) { rc.ux_maximgb_tg_offset = value; }, /** * Returns node's children total count * * @access public * @param {Record} * rc * @return {Integer} */ getNodeChildrenTotalCount : function(rc) { return rc.ux_maximgb_tg_total || 0; }, /** * Sets node's children total count. * * @access private * @param {Record} * rc * @param {Integer} * value */ setNodeChildrenTotalCount : function(rc, value) { rc.ux_maximgb_tg_total = value; }, /** * Collapses node. * * @access public * @param {Record} * rc * @param {Record} * rc Node to collapse. */ collapseNode : function(rc) { if (this.isExpandedNode(rc) && this.fireEvent('beforecollapsenode', this, rc) !== false) { this.setNodeExpanded(rc, false); this.fireEvent('collapsenode', this, rc); } }, /** * Expands node. * * @access public * @param {Record} * rc */ expandNode : function(rc) { var params; if (!this.isExpandedNode(rc) && this.fireEvent('beforeexpandnode', this, rc) !== false) { // If node is already loaded then expanding now. if (this.isLoadedNode(rc)) { this.setNodeExpanded(rc, true); this.fireEvent('expandnode', this, rc); } // If node isn't loaded yet then expanding after load. else { params = {}; params[this.paramNames.active_node] = rc.id; this.load({ add : true, params : params, callback : this.expandNodeCallback, scope : this }); } } }, /** * @access private */ expandNodeCallback : function(r, options, success) { var rc = this.getById(options.params[this.paramNames.active_node]); if (success && rc) { this.setNodeLoaded(rc, true); this.setNodeExpanded(rc, true); this.fireEvent('expandnode', this, rc); } else { this.fireEvent('expandnodefailed', this, options.params[this.paramNames.active_node], rc); } }, /** * Expands all nodes. * * @access public */ expandAll : function() { var r, i, len, records = this.data.getRange(); this.suspendEvents(); for (i = 0, len = records.length; i < len; i++) { r = records[i]; if (!this.isExpandedNode(r)) { this.expandNode(r); } } this.resumeEvents(); this.fireEvent('datachanged', this); }, /** * Collapses all nodes. * * @access public */ collapseAll : function() { var r, i, len, records = this.data.getRange(); this.suspendEvents(); for (i = 0, len = records.length; i < len; i++) { r = records[i]; if (this.isExpandedNode(r)) { this.collapseNode(r); } } this.resumeEvents(); this.fireEvent('datachanged', this); }, /** * Returns loaded node id from the load options. * * @access public */ getLoadedNodeIdFromOptions : function(options) { var result = null; if (options && options.params && options.params[this.paramNames.active_node]) { result = options.params[this.paramNames.active_node]; } return result; }, /** * Returns start offset from the load options. */ getPageOffsetFromOptions : function(options) { var result = 0; if (options && options.params && options.params[this.paramNames.start]) { result = parseInt(options.params[this.paramNames.start], 10); if (isNaN(result)) { result = 0; } } return result; }, // Public hasNextSiblingNode : function(rc) { return this.getNodeNextSibling(rc) !== null; }, // Public hasPrevSiblingNode : function(rc) { return this.getNodePrevSibling(rc) !== null; }, // Public hasChildNodes : function(rc) { return this.getNodeChildrenCount(rc) > 0; }, // Public getNodeAncestors : function(rc) { var ancestors = [], parent; parent = this.getNodeParent(rc); while (parent) { ancestors.push(parent); parent = this.getNodeParent(parent); } return ancestors; }, // Public getNodeChildrenCount : function(rc) { return this.getNodeChildren(rc).length; }, // Public getNodeNextSibling : function(rc) { var siblings, parent, index, result = null; parent = this.getNodeParent(rc); if (parent) { siblings = this.getNodeChildren(parent); } else { siblings = this.getRootNodes(); } index = siblings.indexOf(rc); if (index < siblings.length - 1) { result = siblings[index + 1]; } return result; }, // Public getNodePrevSibling : function(rc) { var siblings, parent, index, result = null; parent = this.getNodeParent(rc); if (parent) { siblings = this.getNodeChildren(parent); } else { siblings = this.getRootNodes(); } index = siblings.indexOf(rc); if (index > 0) { result = siblings[index - 1]; } return result; }, // Abstract tree support methods. // ----------------------------------------------------------------------------------------------- // Public - Abstract getRootNodes : function() { throw 'Abstract method call'; }, // Public - Abstract getNodeDepth : function(rc) { throw 'Abstract method call'; }, // Public - Abstract getNodeParent : function(rc) { throw 'Abstract method call'; }, // Public - Abstract getNodeChildren : function(rc) { throw 'Abstract method call'; }, // Public - Abstract addToNode : function(parent, child) { throw 'Abstract method call'; }, // Public - Abstract removeFromNode : function(parent, child) { throw 'Abstract method call'; }, // Paging support methods. // ----------------------------------------------------------------------------------------------- /** * Returns top level node page offset. * * @access public * @return {Integer} */ getPageOffset : function() { return this.page_offset; }, /** * Returns active node page offset. * * @access public * @return {Integer} */ getActiveNodePageOffset : function() { var result; if (this.active_node) { result = this.getNodeChildrenOffset(this.active_node); } else { result = this.getPageOffset(); } return result; }, /** * Returns active node children count. * * @access public * @return {Integer} */ getActiveNodeCount : function() { var result; if (this.active_node) { result = this.getNodeChildrenCount(this.active_node); } else { result = this.getRootNodes().length; } return result; }, /** * Returns active node total children count. * * @access public * @return {Integer} */ getActiveNodeTotalCount : function() { var result; if (this.active_node) { result = this.getNodeChildrenTotalCount(this.active_node); } else { result = this.getTotalCount(); } return result; } }); /** * Tree store for adjacency list tree representation. */ Ext.ux.maximgb.tg.AdjacencyListStore = Ext.extend( Ext.ux.maximgb.tg.AbstractTreeStore, { /** * @cfg {String} parent_id_field_name Record parent id field name. */ parent_id_field_name : 'ParentId', getRootNodes : function() { var i, len, result = [], records = this.data.getRange(); for (i = 0, len = records.length; i < len; i++) { if (records[i].get(this.parent_id_field_name) == null) { result.push(records[i]); } } return result; }, getNodeDepth : function(rc) { return this.getNodeAncestors(rc).length; }, getNodeParent : function(rc) { return this.getById(rc.get(this.parent_id_field_name)); }, getNodeChildren : function(rc) { var i, len, result = [], records = this.data.getRange(); for (i = 0, len = records.length; i < len; i++) { if (records[i].get(this.parent_id_field_name) == rc.id) { result.push(records[i]); } } return result; }, addToNode : function(parent, child) { child.set(this.parent_id_field_name, parent.id); this.addSorted(child); }, removeFromNode : function(parent, child) { this.remove(child); } }); Ext.reg('Ext.ux.maximgb.tg.AdjacencyListStore', Ext.ux.maximgb.tg.AdjacencyListStore); /** * Tree store for nested set tree representation. */ Ext.ux.maximgb.tg.NestedSetStore = Ext.extend( Ext.ux.maximgb.tg.AbstractTreeStore, { /** * @cfg {String} left_field_name Record NS-left bound field name. */ left_field_name : '_lft', /** * @cfg {String} right_field_name Record NS-right bound field name. */ right_field_name : '_rgt', /** * @cfg {String} level_field_name Record NS-level field name. */ level_field_name : 'Level', /** * @cfg {Number} root_node_level Root node level. */ root_node_level : 1, getRootNodes : function() { var i, len, result = [], records = this.data.getRange(); for (i = 0, len = records.length; i < len; i++) { if (records[i].get(this.level_field_name) == this.root_node_level) { result.push(records[i]); } } return result; }, getNodeDepth : function(rc) { return rc.get(this.level_field_name) - this.root_node_level; }, getNodeParent : function(rc) { var result = null, rec, records = this.data.getRange(), i, len, lft, r_lft, rgt, r_rgt, level, r_level; lft = rc.get(this.left_field_name); rgt = rc.get(this.right_field_name); level = rc.get(this.level_field_name); for (i = 0, len = records.length; i < len; i++) { rec = records[i]; r_lft = rec.get(this.left_field_name); r_rgt = rec.get(this.right_field_name); r_level = rec.get(this.level_field_name); if (r_level == level - 1 && r_lft < lft && r_rgt > rgt) { result = rec; break; } } return result; }, getNodeChildren : function(rc) { var lft, r_lft, rgt, r_rgt, level, r_level, records, rec, result = []; records = this.data.getRange(); lft = rc.get(this.left_field_name); rgt = rc.get(this.right_field_name); level = rc.get(this.level_field_name); for (i = 0, len = records.length; i < len; i++) { rec = records[i]; r_lft = rec.get(this.left_field_name); r_rgt = rec.get(this.right_field_name); r_level = rec.get(this.level_field_name); if (r_level == level + 1 && r_lft > lft && r_rgt < rgt) { result.push(rec); } } return result; } }); Ext.ux.maximgb.tg.GridView = Ext.extend(Ext.grid.GridView, { expanded_icon_class : 'ux-maximgb-tg-elbow-minus', last_expanded_icon_class : 'ux-maximgb-tg-elbow-end-minus', collapsed_icon_class : 'ux-maximgb-tg-elbow-plus', last_collapsed_icon_class : 'ux-maximgb-tg-elbow-end-plus', skip_width_update_class : 'ux-maximgb-tg-skip-width-update', // private - overriden initTemplates : function() { var ts = this.templates || {}; if (!ts.row) { ts.row = new Ext.Template( '
', '', '', '{cells}', (this.enableRowBody ? '' + '' + '' : ''), '', '
' + '
{body}
' + '
', '
'); } if (!ts.mastercell) { ts.mastercell = new Ext.Template( '', '
', // This is // for // editor to // place // itself // right '{treeui}', '
{value}
', '
', ''); } if (!ts.treeui) { ts.treeui = new Ext.Template( '
', '{elbow_line}', '
 
', '
'); } if (!ts.elbow_line) { ts.elbow_line = new Ext.Template('
 
'); } this.templates = ts; Ext.ux.maximgb.tg.GridView.superclass.initTemplates.call(this); }, // Private - Overriden doRender : function(cs, rs, ds, startRow, colCount, stripe) { var ts = this.templates, ct = ts.cell, rt = ts.row, last = colCount - 1; var tstyle = 'width:' + this.getTotalWidth() + ';'; // buffers var 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); var row_render_res = this.renderRow(r, rowIndex, colCount, ds, this.cm.getTotalWidth()); if (row_render_res === false) { 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 (Ext.isEmpty(p.value)) { p.value = " "; } if (this.markDirty && r.dirty && typeof r.modified[c.name] !== 'undefined') { p.css += ' x-grid3-dirty-cell'; } // ----- Modification start if (c.id == this.grid.master_column_id) { p.treeui = this.renderCellTreeUI(r, ds); ct = ts.mastercell; } else { ct = ts.cell; } // ----- End of modification cb[cb.length] = ct.apply(p); } } else { cb.push(row_render_res); } 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(""); // ----- Modification start if (!ds.isVisibleNode(r)) { rp.display_style = 'display: none;'; } else { rp.display_style = ''; } rp.level = ds.getNodeDepth(r); // ----- End of modification buf[buf.length] = rt.apply(rp); } return buf.join(""); }, renderCellTreeUI : function(record, store) { var tpl = this.templates.treeui, line_tpl = this.templates.elbow_line, tpl_data = {}, rec, parent, depth = level = store .getNodeDepth(record); tpl_data.wrap_width = (depth + 1) * 16; if (level > 0) { tpl_data.elbow_line = ''; rec = record; left = 0; while (level--) { parent = store.getNodeParent(rec); if (parent) { if (store.hasNextSiblingNode(parent)) { tpl_data.elbow_line = line_tpl.apply({ left : level * 16, cls : 'ux-maximgb-tg-elbow-line' }) + tpl_data.elbow_line; } else { tpl_data.elbow_line = line_tpl.apply({ left : level * 16, cls : 'ux-maximgb-tg-elbow-empty' }) + tpl_data.elbow_line; } } else { throw ["Tree inconsistency can't get level ", level + 1, " node(id=", rec.id, ") parent."].join(""); } rec = parent; } } if (store.isLeafNode(record)) { if (store.hasNextSiblingNode(record)) { tpl_data.cls = 'ux-maximgb-tg-elbow'; } else { tpl_data.cls = 'ux-maximgb-tg-elbow-end'; } } else { tpl_data.cls = 'ux-maximgb-tg-elbow-active '; if (store.isExpandedNode(record)) { if (store.hasNextSiblingNode(record)) { tpl_data.cls += this.expanded_icon_class; } else { tpl_data.cls += this.last_expanded_icon_class; } } else { if (store.hasNextSiblingNode(record)) { tpl_data.cls += this.collapsed_icon_class; } else { tpl_data.cls += this.last_collapsed_icon_class; } } } tpl_data.left = 1 + depth * 16; return tpl.apply(tpl_data); }, // Template method renderRow : function(record, index, col_count, ds, total_width) { return false; }, // private - overriden afterRender : function() { Ext.ux.maximgb.tg.GridView.superclass.afterRender.call(this); this.updateAllColumnWidths(); }, // private - overriden to support missing column td's case, if row is // rendered by renderRow() // method. updateAllColumnWidths : function() { var tw = this.getTotalWidth(), clen = this.cm.getColumnCount(), ws = [], len, i; for (i = 0; i < clen; i++) { ws[i] = this.getColumnWidth(i); } this.innerHd.firstChild.style.width = this.getOffsetWidth(); this.innerHd.firstChild.firstChild.style.width = tw; this.mainBody.dom.style.width = tw; for (i = 0; i < clen; i++) { var hd = this.getHeaderCell(i); hd.style.width = ws[i]; } var ns = this.getRows(), row, trow; for (i = 0, len = ns.length; i < len; i++) { row = ns[i]; row.style.width = tw; if (row.firstChild) { row.firstChild.style.width = tw; trow = row.firstChild.rows[0]; for (var j = 0; j < clen && j < trow.childNodes.length; j++) { if (!Ext.fly(trow.childNodes[j]) .hasClass(this.skip_width_update_class)) { trow.childNodes[j].style.width = ws[j]; } } } } this.onAllColumnWidthsUpdated(ws, tw); }, // private - overriden to support missing column td's case, if row is // rendered by renderRow() // method. updateColumnWidth : function(col, width) { var w = this.getColumnWidth(col); var tw = this.getTotalWidth(); this.innerHd.firstChild.style.width = this.getOffsetWidth(); this.innerHd.firstChild.firstChild.style.width = tw; this.mainBody.dom.style.width = tw; var hd = this.getHeaderCell(col); hd.style.width = w; var ns = this.getRows(), row; for (var i = 0, len = ns.length; i < len; i++) { row = ns[i]; row.style.width = tw; if (row.firstChild) { row.firstChild.style.width = tw; if (col < row.firstChild.rows[0].childNodes.length) { if (!Ext.fly(row.firstChild.rows[0].childNodes[col]) .hasClass(this.skip_width_update_class)) { row.firstChild.rows[0].childNodes[col].style.width = w; } } } } this.onColumnWidthUpdated(col, w, tw); }, // private - overriden to support missing column td's case, if row is // rendered by renderRow() // method. updateColumnHidden : function(col, hidden) { var tw = this.getTotalWidth(); this.innerHd.firstChild.style.width = this.getOffsetWidth(); this.innerHd.firstChild.firstChild.style.width = tw; this.mainBody.dom.style.width = tw; var display = hidden ? 'none' : ''; var hd = this.getHeaderCell(col); hd.style.display = display; var ns = this.getRows(), row, cell; for (var i = 0, len = ns.length; i < len; i++) { row = ns[i]; row.style.width = tw; if (row.firstChild) { row.firstChild.style.width = tw; if (col < row.firstChild.rows[0].childNodes.length) { if (!Ext.fly(row.firstChild.rows[0].childNodes[col]) .hasClass(this.skip_width_update_class)) { row.firstChild.rows[0].childNodes[col].style.display = display; } } } } this.onColumnHiddenUpdated(col, hidden, tw); delete this.lastViewWidth; // force recalc this.layout(); }, // private - overriden to skip hidden rows processing. processRows : function(startRow, skipStripe) { var processed_cnt = 0; if (this.ds.getCount() < 1) { return; } skipStripe = !this.grid.stripeRows; // skipStripe || // !this.grid.stripeRows; startRow = startRow || 0; var rows = this.getRows(); var processed_cnt = 0; Ext.each(rows, function(row, idx) { row.rowIndex = idx; row.className = row.className.replace(this.rowClsRe, ' '); if (row.style.display != 'none') { if (!skipStripe && ((processed_cnt + 1) % 2 === 0)) { row.className += ' x-grid3-row-alt'; } processed_cnt++; } }, this); Ext.fly(rows[0]).addClass(this.firstRowCls); Ext.fly(rows[rows.length - 1]).addClass(this.lastRowCls); }, ensureVisible : function(row, col, hscroll) { var ancestors, record = this.ds.getAt(row); if (!this.ds.isVisibleNode(record)) { ancestors = this.ds.getNodeAncestors(record); while (ancestors.length > 0) { record = ancestors.shift(); if (!this.ds.isExpandedNode(record)) { this.ds.expandNode(record); } } } return Ext.ux.maximgb.tg.GridView.superclass.ensureVisible.call(this, row, col, hscroll); }, // Private expandRow : function(record, skip_process) { var ds = this.ds, i, len, row, pmel, children, index, child_index; if (typeof record == 'number') { index = record; record = ds.getAt(index); } else { index = ds.indexOf(record); } skip_process = skip_process || false; row = this.getRow(index); pmel = Ext.fly(row).child('.ux-maximgb-tg-elbow-active'); if (pmel) { if (ds.hasNextSiblingNode(record)) { pmel.removeClass(this.collapsed_icon_class); pmel.removeClass(this.last_collapsed_icon_class); pmel.addClass(this.expanded_icon_class); } else { pmel.removeClass(this.collapsed_icon_class); pmel.removeClass(this.last_collapsed_icon_class); pmel.addClass(this.last_expanded_icon_class); } } if (ds.isVisibleNode(record)) { children = ds.getNodeChildren(record); for (i = 0, len = children.length; i < len; i++) { child_index = ds.indexOf(children[i]); row = this.getRow(child_index); row.style.display = 'block'; if (ds.isExpandedNode(children[i])) { this.expandRow(child_index, true); } } } if (!skip_process) { this.processRows(0); } // this.updateAllColumnWidths(); }, collapseRow : function(record, skip_process) { var ds = this.ds, i, len, children, row, index, child_index; if (typeof record == 'number') { index = record; record = ds.getAt(index); } else { index = ds.indexOf(record); } skip_process = skip_process || false; row = this.getRow(index); pmel = Ext.fly(row).child('.ux-maximgb-tg-elbow-active'); if (pmel) { if (ds.hasNextSiblingNode(record)) { pmel.removeClass(this.expanded_icon_class); pmel.removeClass(this.last_expanded_icon_class); pmel.addClass(this.collapsed_icon_class); } else { pmel.removeClass(this.expanded_icon_class); pmel.removeClass(this.last_expanded_icon_class); pmel.addClass(this.last_collapsed_icon_class); } } children = ds.getNodeChildren(record); for (i = 0, len = children.length; i < len; i++) { child_index = ds.indexOf(children[i]); row = this.getRow(child_index); if (row.style.display != 'none') { row.style.display = 'none'; this.collapseRow(child_index, true); } } if (!skip_process) { this.processRows(0); } // this.updateAllColumnWidths(); }, /** * @access private */ initData : function(ds, cm) { Ext.ux.maximgb.tg.GridView.superclass.initData.call(this, ds, cm); if (this.ds) { this.ds.un('expandnode', this.onStoreExpandNode, this); this.ds.un('collapsenode', this.onStoreCollapseNode, this); } if (ds) { ds.on('expandnode', this.onStoreExpandNode, this); ds.on('collapsenode', this.onStoreCollapseNode, this); } }, onLoad : function(store, records, options) { var ridx; if (options && options.params && (options.params[store.paramNames.active_node] === null || store .indexOfId(options.params[store.paramNames.active_node]) == -1)) { Ext.ux.maximgb.tg.GridView.superclass.onLoad.call(this, store, records, options); } }, onAdd : function(ds, records, index) { Ext.ux.maximgb.tg.GridView.superclass.onAdd.call(this, ds, records, index); if (this.mainWrap) { // this.updateAllColumnWidths(); this.processRows(0); } }, onRemove : function(ds, record, index, isUpdate) { Ext.ux.maximgb.tg.GridView.superclass.onRemove.call(this, ds, record, index, isUpdate); if (isUpdate !== true) { if (this.mainWrap) { // this.updateAllColumnWidths(); this.processRows(0); } } }, onUpdate : function(ds, record) { Ext.ux.maximgb.tg.GridView.superclass.onUpdate.call(this, ds, record); if (this.mainWrap) { // this.updateAllColumnWidths(); this.processRows(0); } }, onStoreExpandNode : function(store, rc) { this.expandRow(rc); }, onStoreCollapseNode : function(store, rc) { this.collapseRow(rc); } }); Ext.ux.maximgb.tg.GridPanel = Ext.extend(Ext.grid.GridPanel, { /** * @cfg {String|Integer} master_column_id Master column id. Master * column cells are nested. Master column cell values are used * to build breadcrumbs. */ master_column_id : 0, /** * @cfg {Stirng} TreeGrid panel custom class. */ tg_cls : 'ux-maximgb-tg-panel', // Private initComponent : function() { this.initComponentPreOverride(); Ext.ux.maximgb.tg.GridPanel.superclass.initComponent.call(this); this.getSelectionModel().on('selectionchange', this.onTreeGridSelectionChange, this); this.initComponentPostOverride(); }, initComponentPreOverride : Ext.emptyFn, initComponentPostOverride : Ext.emptyFn, // Private onRender : function(ct, position) { Ext.ux.maximgb.tg.GridPanel.superclass.onRender.call(this, ct, position); this.el.addClass(this.tg_cls); }, /** * Returns view instance. * * @access private * @return {GridView} */ getView : function() { if (!this.view) { this.view = new Ext.ux.maximgb.tg.GridView(this.viewConfig); } return this.view; }, /** * @access private */ onClick : function(e) { var target = e.getTarget(), view = this.getView(), row = view .findRowIndex(target), store = this.getStore(), sm = this .getSelectionModel(), record, record_id, do_default = true; // Row click if (row !== false) { if (Ext.fly(target).hasClass('ux-maximgb-tg-elbow-active')) { record = store.getAt(row); if (store.isExpandedNode(record)) { store.collapseNode(record); } else { store.expandNode(record); } do_default = false; } } if (do_default) { Ext.ux.maximgb.tg.GridPanel.superclass.onClick .call(this, e); } }, /** * @access private */ onMouseDown : function(e) { var target = e.getTarget(); if (!Ext.fly(target).hasClass('ux-maximgb-tg-elbow-active')) { Ext.ux.maximgb.tg.GridPanel.superclass.onMouseDown.call( this, e); } }, /** * @access private */ onTreeGridSelectionChange : function(sm, selection) { var record, ancestors, store = this.getStore(); // Row selection model if (sm.getSelected) { record = sm.getSelected(); store.setActiveNode(record); } // Cell selection model else if (sm.getSelectedCell && selection) { record = selection.record; store.setActiveNode(record); } // Ensuring that selected node is visible. if (record) { if (!store.isVisibleNode(record)) { ancestors = store.getNodeAncestors(record); while (ancestors.length > 0) { store.expandNode(ancestors.pop()); } } } } }); Ext.ux.maximgb.tg.EditorGridPanel = Ext.extend(Ext.grid.EditorGridPanel, { /** * @cfg {String|Integer} master_column_id Master column id. Master * column cells are nested. Master column cell values are used * to build breadcrumbs. */ master_column_id : 0, // Private initComponent : function() { this.initComponentPreOverride(); Ext.ux.maximgb.tg.EditorGridPanel.superclass.initComponent .call(this); this.getSelectionModel().on('selectionchange', this.onTreeGridSelectionChange, this); this.initComponentPostOverride(); }, initComponentPreOverride : Ext.emptyFn, initComponentPostOverride : Ext.emptyFn, // Private onRender : function(ct, position) { Ext.ux.maximgb.tg.EditorGridPanel.superclass.onRender.call( this, ct, position); this.el.addClass('ux-maximgb-tg-panel'); }, /** * Returns view instance. * * @access private * @return {GridView} */ getView : function() { if (!this.view) { this.view = new Ext.ux.maximgb.tg.GridView(this.viewConfig); } return this.view; }, /** * @access private */ onClick : function(e) { var target = e.getTarget(), view = this.getView(), row = view .findRowIndex(target), store = this.getStore(), sm = this .getSelectionModel(), record, record_id, do_default = true; // Row click if (row !== false) { if (Ext.fly(target).hasClass('ux-maximgb-tg-elbow-active')) { record = store.getAt(row); if (store.isExpandedNode(record)) { store.collapseNode(record); } else { store.expandNode(record); } do_default = false; } } if (do_default) { Ext.ux.maximgb.tg.EditorGridPanel.superclass.onClick.call( this, e); } }, /** * @access private */ onMouseDown : function(e) { var target = e.getTarget(); if (!Ext.fly(target).hasClass('ux-maximgb-tg-elbow-active')) { Ext.ux.maximgb.tg.EditorGridPanel.superclass.onMouseDown .call(this, e); } }, /** * @access private */ onTreeGridSelectionChange : function(sm, selection) { var record, ancestors, store = this.getStore(); // Row selection model if (sm.getSelected) { record = sm.getSelected(); store.setActiveNode(record); } // Cell selection model else if (sm.getSelectedCell && selection) { record = selection.record; store.setActiveNode(record); } // Ensuring that selected node is visible. if (record) { if (!store.isVisibleNode(record)) { ancestors = store.getNodeAncestors(record); while (ancestors.length > 0) { store.expandNode(ancestors.pop()); } } } } }); /** * Paging toolbar for work this AbstractTreeStore. */ Ext.ux.maximgb.tg.PagingToolbar = Ext.extend(Ext.PagingToolbar, { onRender : function(ct, position) { Ext.ux.maximgb.tg.PagingToolbar.superclass.onRender.call(this, ct, position); this.updateUI(); }, getPageData : function() { var total = 0, cursor = 0; if (this.store) { cursor = this.store.getActiveNodePageOffset(); total = this.store.getActiveNodeTotalCount(); } return { total : total, activePage : Math.ceil((cursor + this.pageSize) / this.pageSize), pages : total < this.pageSize ? 1 : Math.ceil(total / this.pageSize) }; }, updateInfo : function() { var count = 0, cursor = 0, total = 0, msg; if (this.displayItem) { if (this.store) { cursor = this.store.getActiveNodePageOffset(); count = this.store.getActiveNodeCount(); total = this.store.getActiveNodeTotalCount(); } msg = count == 0 ? this.emptyMsg : String.format( this.displayMsg, cursor + 1, cursor + count, total); this.displayItem.setText(msg); } }, updateUI : function() { var d = this.getPageData(), ap = d.activePage, ps = d.pages; this.afterTextItem.setText(String.format(this.afterPageText, d.pages)); this.inputItem.setValue(ap); this.first.setDisabled(ap == 1); this.prev.setDisabled(ap == 1); this.next.setDisabled(ap == ps); this.last.setDisabled(ap == ps); this.refresh.enable(); this.updateInfo(); }, bindStore : function(store, initial) { if (!initial && this.store) { this.store.un('activenodechange', this.onStoreActiveNodeChange, this); } if (store) { store.on('activenodechange', this.onStoreActiveNodeChange, this); } Ext.ux.maximgb.tg.PagingToolbar.superclass.bindStore.call(this, store, initial); }, beforeLoad : function(store, options) { var paramNames = this.getParams(); Ext.ux.maximgb.tg.PagingToolbar.superclass.beforeLoad.call( this, store, options); if (options && options.params) { if (options.params[paramNames.start] === undefined) { options.params[paramNames.start] = 0; } if (options.params[paramNames.limit] === undefined) { options.params[paramNames.limit] = this.pageSize; } } }, /** * Move to the first page, has the same effect as clicking the * 'first' button. */ moveFirst : function() { this.doLoad(0); }, /** * Move to the previous page, has the same effect as clicking the * 'previous' button. */ movePrevious : function() { var store = this.store, cursor = store ? store .getActiveNodePageOffset() : 0; this.doLoad(Math.max(0, cursor - this.pageSize)); }, /** * Move to the next page, has the same effect as clicking the 'next' * button. */ moveNext : function() { var store = this.store, cursor = store ? store .getActiveNodePageOffset() : 0; this.doLoad(cursor + this.pageSize); }, /** * Move to the last page, has the same effect as clicking the 'last' * button. */ moveLast : function() { var store = this.store, cursor = store ? store .getActiveNodePageOffset() : 0, total = store ? store .getActiveNodeTotalCount() : 0, extra = total % this.pageSize; this.doLoad(extra ? (total - extra) : total - this.pageSize); }, onStoreActiveNodeChange : function(store, old_rec, new_rec) { if (this.rendered) { this.updateUI(); } } }); Ext.reg('Ext.ux.maximgb.tg.GridPanel', Ext.ux.maximgb.tg.GridPanel); Ext.reg('Ext.ux.maximgb.tg.EditorGridPanel', Ext.ux.maximgb.tg.EditorGridPanel); Ext.reg('Ext.ux.maximgb.tg.PagingToolbar', Ext.ux.maximgb.tg.PagingToolbar);