app/assets/javascripts/ext_extensions/TreeGrid/TreeGrid.js
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 : '_is_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, prev_record, 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.setNodeLoaded(loaded_node, true);
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;
this.add(record);
}
else {
updated[record.id] = true;
prev_record = this.getAt(idx);
prev_record.reject();
prev_record.data = record.data;
r[i] = prev_record;
}
}
this.applySort();
this.resumeEvents();
r.sort(function(r1, r2) {
var idx1 = self.data.indexOf(r1),
idx2 = self.data.indexOf(r2),
result;
if (idx1 > idx2) {
result = 1;
}
else {
result = -1;
}
return result;
});
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.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 : '_parent',
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(
'<div class="x-grid3-row ux-maximgb-tg-level-{level} {alt}" style="{tstyle} {display_style}">',
'<table class="x-grid3-row-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
'<tbody>',
'<tr>{cells}</tr>',
(
this.enableRowBody ?
'<tr class="x-grid3-row-body-tr" style="{bodyStyle}">' +
'<td colspan="{cols}" class="x-grid3-body-cell" tabIndex="0" hidefocus="on">'+
'<div class="x-grid3-row-body">{body}</div>'+
'</td>'+
'</tr>'
:
''
),
'</tbody>',
'</table>',
'</div>'
);
}
if (!ts.mastercell) {
ts.mastercell = new Ext.Template(
'<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}" tabIndex="0" {cellAttr}>',
'<div class="ux-maximgb-tg-mastercell-wrap">', // This is for editor to place itself right
'{treeui}',
'<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
'</div>',
'</td>'
);
}
if (!ts.treeui) {
ts.treeui = new Ext.Template(
'<div class="ux-maximgb-tg-uiwrap" style="width: {wrap_width}px">',
'{elbow_line}',
'<div style="left: {left}px" class="{cls}"> </div>',
'</div>'
);
}
if (!ts.elbow_line) {
ts.elbow_line = new Ext.Template(
'<div style="left: {left}px" class="{cls}"> </div>'
);
}
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.call(c.scope, 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);