Ext.ns('Ext.ux.grid');

Ext.ux.grid.GridSummary = function(config) {
		Ext.apply(this, config);
};

Ext.extend(Ext.ux.grid.GridSummary, Ext.util.Observable, {
	init : function(grid) {
		this.grid = grid;
		this.cm = grid.getColumnModel();
		this.view = grid.getView();

		var v = this.view;

		// override GridView's onLayout() method
		v.onLayout = this.onLayout;

		v.afterMethod('render', this.refreshSummary, this);
		v.afterMethod('refresh', this.refreshSummary, this);
		v.afterMethod('syncScroll', this.syncSummaryScroll, this);
		v.afterMethod('onColumnWidthUpdated', this.doWidth, this);
		v.afterMethod('onAllColumnWidthsUpdated', this.doAllWidths, this);
		v.afterMethod('onColumnHiddenUpdated', this.doHidden, this);

		// update summary row on store's add/remove/clear/update events
		grid.store.on({
			add: this.refreshSummary,
			remove: this.refreshSummary,
			clear: this.refreshSummary,
			update: this.refreshSummary,
			scope: this
		});

		if (!this.rowTpl) {
			this.rowTpl = new Ext.Template(
				'<div class="x-grid3-summary-row x-grid3-gridsummary-row-offset">',
					'<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
						'<tbody><tr>{cells}</tr></tbody>',
					'</table>',
				'</div>'
			);
			this.rowTpl.disableFormats = true;
		}
		this.rowTpl.compile();

		if (!this.cellTpl) {
			this.cellTpl = new Ext.Template(
				'<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
					'<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on" {attr}>{value}</div>',
				"</td>"
			);
			this.cellTpl.disableFormats = true;
		}
		this.cellTpl.compile();
	},

	calculate : function(rs, cm) {
		var data = {}, cfg = cm.config;
		for (var i = 0, len = cfg.length; i < len; i++) { // loop through all columns in ColumnModel
			var cf = cfg[i], // get column's configuration
				cname = cf.dataIndex; // get column dataIndex

			// initialise grid summary row data for
			// the current column being worked on
			data[cname] = 0;

			if (cf.summaryType) {
				if (cf.summaryType == 'special') {
					data[cname] = GetSpecialSummary(cf, rs);
				} else {
					for (var j = 0, jlen = rs.length; j < jlen; j++) {
						var r = rs[j]; // get a single Record
						data[cname] = Ext.ux.grid.GridSummary.Calculations[cf.summaryType](r.get(cname), r, cname, data, j);
					}
				}
			}
		}

		return data;
	},

	onLayout : function(vw, vh) {
		if (Ext.type(vh) != 'number') { // handles grid's height:'auto' config
			return;
		}
		// note: this method is scoped to the GridView
		if (!this.grid.getGridEl().hasClass('x-grid-hide-gridsummary')) {
			// readjust gridview's height only if grid summary row is visible
			this.scroller.setHeight(vh - this.summary.getHeight());
		}
	},

	syncSummaryScroll : function() {
		var mb = this.view.scroller.dom;

		this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft;
		this.view.summaryWrap.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
	},

	doWidth : function(col, w, tw) {
		var s = this.view.summary.dom;

		s.firstChild.style.width = tw;
		s.firstChild.rows[0].childNodes[col].style.width = w;
	},

	doAllWidths : function(ws, tw) {
		var s = this.view.summary.dom, wlen = ws.length;

		s.firstChild.style.width = tw;

		var 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 s = this.view.summary.dom,
			display = hidden ? 'none' : '';

		s.firstChild.style.width = tw;
		s.firstChild.rows[0].childNodes[col].style.display = display;
	},

	renderSummary : function(o, cs, cm) {
		cs = cs || this.view.getColumnData();
		var cfg = cm.config,
			buf = [],
			last = cs.length - 1;

		for (var i = 0, len = cs.length; i < len; i++) {
			var c = cs[i], cf = cfg[i], p = {};

			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('')
		});
	},

	refreshSummary : function() {
		var g = this.grid, ds = g.store,
			cs = this.view.getColumnData(),
			cm = this.cm,
			rs = ds.getRange(),
			data = this.calculate(rs, cm),
			buf = this.renderSummary({data: data}, cs, cm);

		if (!this.view.summaryWrap) {
			this.view.summaryWrap = Ext.DomHelper.insertAfter(this.view.scroller, {
				tag: 'div',
				cls: 'x-grid3-gridsummary-row-inner'
			}, true);
		}
		this.view.summary = this.view.summaryWrap.update(buf).first();
	},

	toggleSummary : function(visible) { // true to display summary row
		var el = this.grid.getGridEl();

		if (el) {
			if (visible === undefined) {
				visible = el.hasClass('x-grid-hide-gridsummary');
			}
			el[visible ? 'removeClass' : 'addClass']('x-grid-hide-gridsummary');

			this.view.layout(); // readjust gridview height
		}
	},

	getSummaryNode : function() {
		return this.view.summary
	}
});
Ext.reg('gridsummary', Ext.ux.grid.GridSummary);

/*
 * all Calculation methods are called on each Record in the Store
 * with the following 5 parameters:
 *
 * v - cell value
 * record - reference to the current Record
 * colName - column name (i.e. the ColumnModel's dataIndex)
 * data - the cumulative data for the current column + summaryType up to the current Record
 * rowIdx - current row index
 */
Ext.ux.grid.GridSummary.Calculations = {
	sum : function(v, record, colName, data, rowIdx) {
		return data[colName] + Ext.num(v, 0);
	},

	count : function(v, record, colName, data, rowIdx) {
		return rowIdx + 1;
	},

	max : function(v, record, colName, data, rowIdx) {
		return Math.max(Ext.num(v, 0), data[colName]);
	},

	min : function(v, record, colName, data, rowIdx) {
		return Math.min(Ext.num(v, 0), data[colName]);
	},

	average : function(v, record, colName, data, rowIdx) {
		var t = data[colName] + Ext.num(v, 0), count = record.store.getCount();
		return rowIdx == count - 1 ? (t / count) : t;
	}
}





/*!
 * Ext JS Library 3.0+
 * Copyright(c) 2006-2009 Ext JS, LLC
 * licensing@extjs.com
 * http://www.extjs.com/license
 */
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:<pre><code>
summaryRenderer: function(v, params, data){
	return ((v === 0 || v > 1) ? '(' + v +' Tasks)' : '(1 Task)');
},
	 * </code></pre>
	 */
	/**
	 * @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(
				'<div class="x-grid3-summary-row" style="{tstyle}">',
				'<table class="x-grid3-summary-table" border="0" cellspacing="0" cellpadding="0" style="{tstyle}">',
					'<tbody><tr>{cells}</tr></tbody>',
				'</table></div>'
			);
			this.rowTpl.disableFormats = true;
		}
		this.rowTpl.compile();

		if(!this.cellTpl){
			this.cellTpl = new Ext.Template(
				'<td class="x-grid3-col x-grid3-cell x-grid3-td-{id} {css}" style="{style}">',
				'<div class="x-grid3-cell-inner x-grid3-col-{id}" unselectable="on">{value}</div>',
				"</td>"
			);
			this.cellTpl.disableFormats = true;
		}
		this.cellTpl.compile();
	},

	/**
	 * Toggle the display of the summary row on/off
	 * @param {Boolean} visible <tt>true</tt> to show the summary, <tt>false</tt> 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 = "&#160;";
			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 i = 0, len = cs.length; i < len; i++){
			c = cs[i];
			cf = cfg[i];

			if (cf.summaryType == "special") {
				data[c.name] = GetSpecialSummary(cf, rs);
			} else {
				for(var j = 0, jlen = rs.length; j < jlen; j++){
					r = rs[j];
					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('</div>', this.renderSummary({data: data}, cs), '</div>');
	},

	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.
	 * <pre><code>
grid.on('afteredit', function(){
	var groupValue = 'Ext Forms: Field Anchoring';
	summary.showSummaryMsg(groupValue, 'Updating Summary...');
});
	 * </code></pre>
	 * @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 = '<div class="x-grid3-summary-msg">' + msg + '</div>';
		}
	}
});

//backwards compat
Ext.grid.GroupSummary = Ext.ux.grid.GroupSummary;


/**
 * Calculation types for summary row:</p><div class="mdetail-params"><ul>
 * <li><b><tt>sum</tt></b> : <div class="sub-desc"></div></li>
 * <li><b><tt>count</tt></b> : <div class="sub-desc"></div></li>
 * <li><b><tt>max</tt></b> : <div class="sub-desc"></div></li>
 * <li><b><tt>min</tt></b> : <div class="sub-desc"></div></li>
 * <li><b><tt>average</tt></b> : <div class="sub-desc"></div></li>
 * </ul></div>
 * <p>Custom calculations may be implemented.  An example of
 * custom <code>summaryType=totalCost</code>:</p><pre><code>
// define a custom summary function
Ext.ux.grid.GroupSummary.Calculations['totalCost'] = function(v, record, field){
	return v + (record.data.estimate * record.data.rate);
};
 * </code></pre>
 * @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:
 * <pre><code>
{
	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
		}
	}
}
 * </code></pre>
 *
 */
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);
	},

	/**
	 * <pre><code>
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);
});
	 * </code></pre>
	 * @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;



function GetSpecialSummary(cf, rs)
{
	if (cf.dataIndex == "profitFactor") return SpecialSummary_ProfitFactor(rs);
	if (cf.dataIndex == "winpc") return SpecialSummary_PercentOf(rs, "winners", "trades");
	if (cf.dataIndex == "losepc") return SpecialSummary_PercentOf(rs, "losers", "trades");
	if (cf.dataIndex == "averagewin") return SpecialSummary_Divide(rs, "bankedProfit", "winners");
	if (cf.dataIndex == "averageloss") return SpecialSummary_Divide(rs, "bankedLoss", "losers");
	if (cf.dataIndex == "averagetrade") return SpecialSummary_Divide(rs, "netProfit", "trades");
	if (cf.dataIndex == "averagePipWin") return SpecialSummary_Divide(rs, "winningBankedPips", "winners");
	if (cf.dataIndex == "averagePipLoss") return SpecialSummary_Divide(rs, "losingBankedPips", "losers");
	if (cf.dataIndex == "averageBankedPips") return SpecialSummary_Divide(rs, "totalBankedPips", "trades");
	if (cf.dataIndex == "averageLotsTraded") return SpecialSummary_Divide(rs, "lotsTraded", "trades");
}

function SpecialSummary_ProfitFactor(rs)
{
	var t_gp = 0;
	var t_gl = 0;
	for (var j = 0, jlen = rs.length; j < jlen; j++) {
		var r = rs[j]; // get a single Record
		t_gp += r.get('bankedProfit');
		t_gl += r.get('bankedLoss');
	}
	if (t_gl != 0) {
		return t_gp / Math.abs(t_gl);
	}
}

function SpecialSummary_PercentOf(rs, field1, field2)
{
	var t_f1 = 0;
	var t_f2 = 0;
	for (var j = 0, jlen = rs.length; j < jlen; j++) {
		var r = rs[j]; // get a single Record
		t_f1 += r.get(field1);
		t_f2 += r.get(field2);
	}
	if (t_f2 != 0) {
		return t_f1 / t_f2 * 100;
	}
}

function SpecialSummary_Divide(rs, field1, field2)
{
	var t_f1 = 0;
	var t_f2 = 0;
	for (var j = 0, jlen = rs.length; j < jlen; j++) {
		var r = rs[j]; // get a single Record
		t_f1 += r.get(field1);
		t_f2 += r.get(field2);
	}
	if (t_f2 != 0) {
		return t_f1 / t_f2;
	}
}





/**
 * @class GetIt.GridPrinter
 * @author Ed Spencer (edward@domine.co.uk)
 * Helper class to easily print the contents of a grid. Will open a new window with a table where the first row
 * contains the headings from your column model, and with a row for each item in your grid's store. When formatted
 * with appropriate CSS it should look very similar to a default grid. If renderers are specified in your column
 * model, they will be used in creating the table. Override headerTpl and bodyTpl to change how the markup is generated
 * 
 * Usage:
 * 
 * var grid = new Ext.grid.GridPanel({
 *   colModel: //some column model,
 *   store   : //some store
 * });
 * 
 * Ext.ux.GridPrinter.print(grid);
 * 
 */
Ext.ns('Ext.ux.GridPrinter');
Ext.ux.GridPrinter = {
  /**
   * Prints the passed grid. Reflects on the grid's column model to build a table, and fills it using the store
   * @param {Ext.grid.GridPanel} grid The grid to print
   */
  print: function(grid) {

    //We generate an XTemplate here by using 2 intermediary XTemplates - one to create the header,
    //the other to create the body (see the escaped {} below)
    var columns = grid.getColumnModel().config;

    //build a useable array of store data for the XTemplate
    var data = [];
    grid.store.data.each(function(item) {
      var convertedData = [];

      //apply renderers from column model
      for (var key in item.data) {
        var value = item.data[key];
        
        Ext.each(columns, function(column) {
          if (column.dataIndex == key) {
            convertedData[key] = column.renderer ? column.renderer(value) : value;
          }
        }, this);
      }
      
      data.push(convertedData);
    });

    
    //use the headerTpl and bodyTpl XTemplates to create the main XTemplate below
    var headings = Ext.ux.GridPrinter.headerTpl.apply(columns);
    var body     = Ext.ux.GridPrinter.bodyTpl.apply(columns);

    
	var html = new Ext.XTemplate(
      '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
      '<html>',
        '<head>',
          '<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />',
          '<link href="' + Ext.ux.GridPrinter.stylesheetPath + '" rel="stylesheet" type="text/css" media="screen,print" />',
          '<title>' + glbUserId + ' - ' + grid.title + ' grid</title>',
        '</head>',
        '<body>',
          '<table>',
            headings,
            '<tpl for=".">',
              body,
            '</tpl>',
          '</table>',
        '</body>',
      '</html>'
    ).apply(data);
    
    //open up a new printing window, write to it, print it and close
    var win = window.open('', 'printgrid');
    win.document.write(html);
    win.focus();
    win.print();
    //win.close();
  },
  
  /**
   * @property stylesheetPath
   * @type String
   * The path at which the print stylesheet can be found (defaults to '/stylesheets/print.css')
   */
  stylesheetPath: glbSiteRoot + 'css/GridPrint.css',
  
  /**
   * @property headerTpl
   * @type Ext.XTemplate
   * The XTemplate used to create the headings row. By default this just uses <th> elements, override to provide your own
   */
  headerTpl:  new Ext.XTemplate(
    '<tr>',
      '<tpl for=".">',
        '<th>{header}</th>',
      '</tpl>',
    '</tr>'
  ),
   
   /**
    * @property bodyTpl
    * @type Ext.XTemplate
    * The XTemplate used to create each row. This is used inside the 'print' function to build another XTemplate, to which the data
    * are then applied (see the escaped dataIndex attribute here - this ends up as "{dataIndex}")
    */
  bodyTpl:  new Ext.XTemplate(
    '<tr>',
      '<tpl for=".">',
        '<td>\{{dataIndex}\}</td>',
      '</tpl>',
    '</tr>'
  )
};
