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;
       
this.cm = grid.getColumnModel();
       
this.view = grid.getView();

       
var v = this.view;
        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.cm.config;

       
var 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.cm.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){
       
var gs = this.view.getGroups(), s;
       
for(var i = 0, len = gs.length; 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){
       
var gs = this.view.getGroups(), s, cells, wlen = ws.length;
       
for(var i = 0, len = gs.length; i < len; i++){
            s
= gs[i].childNodes[2];
            s
.style.width = tw;
            s
.firstChild.style.width = tw;
            cells
= s.firstChild.rows[0].childNodes;
           
for(var j = 0; j < wlen; j++){
                cells
[j].style.width = ws[j];
           
}
       
}
   
},

    doHidden
: function(col, hidden, tw){
       
var gs = this.view.getGroups(), s, display = hidden ? 'none' : '';
       
for(var i = 0, len = gs.length; 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;
       
}
   
},

   
// 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 = document.getElementById(gid);
       
if(!g){
           
return false;
       
}
       
var rs = [];
       
this.grid.store.each(function(r){
           
if(r._groupId == gid){
                rs
[rs.length] = r;
           
}
       
});
       
var cs = this.view.getColumnData();
       
var data = this.calculate(rs, cs);
       
var markup = this.renderSummary({data: data}, cs);

       
var 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);
       
var node = this.getSummaryNode(gid);
       
if(node){
            node
.innerHTML = '
' + msg + '
';
       
}
   
}
});

//backwards compat
Ext.grid.GroupSummary = Ext.ux.grid.GroupSummary;


/**
 * Calculation types for summary row:


 *

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();
       
var gvalue = rs[0].data[gcol];
       
var 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.store.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 json = this.grid.store.reader.jsonData;
       
if(json && json.summaryData){
           
return json.summaryData[groupValue];
       
}
       
return null;
   
}
});

//backwards compat
Ext.grid.HybridSummary = Ext.ux.grid.HybridSummary;