/*!
 * Ext JS Library 3.2.0
 * Copyright(c) 2006-2010 Ext JS, Inc.
 * licensing@extjs.com
 * http://www.extjs.com/license
 */
Ext.ns('Ext.ux.grid');

/**
 * @class Ext.ux.grid.RowExpander
 * @extends Ext.util.Observable
 * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables
 * a second row body which expands/contracts. The expand/contract behavior is configurable to react
 * on clicking of the column, double click of the row, and/or hitting enter while a row is selected.
 *
 * @ptype rowexpander
 */
Ext.ux.grid.RowExpander = Ext.extend(Ext.util.Observable, {
 /**
 * @cfg {Boolean} expandOnEnter
 * <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter
 * key is pressed (defaults to <tt>true</tt>).
 */
 expandOnEnter : true,
 /**
 * @cfg {Boolean} expandOnDblClick
 * <tt>true</tt> to toggle a row between expanded/collapsed when double clicked
 * (defaults to <tt>true</tt>).
 */
 expandOnDblClick : true,

 header : '',
 width : 20,
 sortable : false,
 fixed : true,
 hideable: false,
 menuDisabled : true,
 dataIndex : '',
 id : 'expander',
 lazyRender : true,
 enableCaching : true,

 constructor: function(config){
 Ext.apply(this, config);

 this.addEvents({
 /**
 * @event beforeexpand
 * Fires before the row expands. Have the listener return false to prevent the row from expanding.
 * @param {Object} this RowExpander object.
 * @param {Object} Ext.data.Record Record for the selected row.
 * @param {Object} body body element for the secondary row.
 * @param {Number} rowIndex The current row index.
 */
 beforeexpand: true,
 /**
 * @event expand
 * Fires after the row expands.
 * @param {Object} this RowExpander object.
 * @param {Object} Ext.data.Record Record for the selected row.
 * @param {Object} body body element for the secondary row.
 * @param {Number} rowIndex The current row index.
 */
 expand: true,
 /**
 * @event beforecollapse
 * Fires before the row collapses. Have the listener return false to prevent the row from collapsing.
 * @param {Object} this RowExpander object.
 * @param {Object} Ext.data.Record Record for the selected row.
 * @param {Object} body body element for the secondary row.
 * @param {Number} rowIndex The current row index.
 */
 beforecollapse: true,
 /**
 * @event collapse
 * Fires after the row collapses.
 * @param {Object} this RowExpander object.
 * @param {Object} Ext.data.Record Record for the selected row.
 * @param {Object} body body element for the secondary row.
 * @param {Number} rowIndex The current row index.
 */
 collapse: true
 });

 Ext.ux.grid.RowExpander.superclass.constructor.call(this);

 if(this.tpl){
 if(typeof this.tpl == 'string'){
 this.tpl = new Ext.Template(this.tpl);
 }
 this.tpl.compile();
 }

 this.state = {};
 this.bodyContent = {};
 },

 getRowClass : function(record, rowIndex, p, ds){
 p.cols = p.cols-1;
 var content = this.bodyContent[record.id];
 if(!content && !this.lazyRender){
 content = this.getBodyContent(record, rowIndex);
 }
 if(content){
 p.body = content;
 }
 return this.state[record.id] ? 'x-grid3-row-expanded' : 'x-grid3-row-collapsed';
 },

 init : function(grid){
 this.grid = grid;

 var view = grid.getView();
 view.getRowClass = this.getRowClass.createDelegate(this);

 view.enableRowBody = true;


 grid.on('render', this.onRender, this);
 grid.on('destroy', this.onDestroy, this);
 },

 // @private
 onRender: function() {
 var grid = this.grid;
 var mainBody = grid.getView().mainBody;
 mainBody.on('mousedown', this.onMouseDown, this, {delegate: '.x-grid3-row-expander'});
 if (this.expandOnEnter) {
 this.keyNav = new Ext.KeyNav(this.grid.getGridEl(), {
 'enter' : this.onEnter,
 scope: this
 });
 }
 if (this.expandOnDblClick) {
 grid.on('rowdblclick', this.onRowDblClick, this);
 }
 },

 // @private
 onDestroy: function() {
 if(this.keyNav){
 this.keyNav.disable();
 delete this.keyNav;
 }
 /*
 * A majority of the time, the plugin will be destroyed along with the grid,
 * which means the mainBody won't be available. On the off chance that the plugin
 * isn't destroyed with the grid, take care of removing the listener.
 */
 var mainBody = this.grid.getView().mainBody;
 if(mainBody){
 mainBody.un('mousedown', this.onMouseDown, this);
 }
 },
 // @private
 onRowDblClick: function(grid, rowIdx, e) {
 this.toggleRow(rowIdx);
 },

 onEnter: function(e) {
 var g = this.grid;
 var sm = g.getSelectionModel();
 var sels = sm.getSelections();
 for (var i = 0, len = sels.length; i < len; i++) {
 var rowIdx = g.getStore().indexOf(sels[i]);
 this.toggleRow(rowIdx);
 }
 },

 getBodyContent : function(record, index){
 if(!this.enableCaching){
 return this.tpl.apply(record.data);
 }
 var content = this.bodyContent[record.id];
 if(!content){
 content = this.tpl.apply(record.data);
 this.bodyContent[record.id] = content;
 }
 return content;
 },

 onMouseDown : function(e, t){
 e.stopEvent();
 var row = e.getTarget('.x-grid3-row');
 this.toggleRow(row);
 },

 renderer : function(v, p, record){
 p.cellAttr = 'rowspan="2"';
 return '<div class="x-grid3-row-expander">&#160;</div>';
 },

 beforeExpand : function(record, body, rowIndex){
 if(this.fireEvent('beforeexpand', this, record, body, rowIndex) !== false){
 if(this.tpl && this.lazyRender){
 body.innerHTML = this.getBodyContent(record, rowIndex);
 }
 return true;
 }else{
 return false;
 }
 },

 toggleRow : function(row){
 if(typeof row == 'number'){
 row = this.grid.view.getRow(row);
 }
 this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
 },

 expandRow : function(row){
 if(typeof row == 'number'){
 row = this.grid.view.getRow(row);
 }
 var record = this.grid.store.getAt(row.rowIndex);
 var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
 if(this.beforeExpand(record, body, row.rowIndex)){
 this.state[record.id] = true;
 Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
 this.fireEvent('expand', this, record, body, row.rowIndex);
 }
 },

 collapseRow : function(row){
 if(typeof row == 'number'){
 row = this.grid.view.getRow(row);
 }
 var record = this.grid.store.getAt(row.rowIndex);
 var body = Ext.fly(row).child('tr:nth(1) div.x-grid3-row-body', true);
 if(this.fireEvent('beforecollapse', this, record, body, row.rowIndex) !== false){
 this.state[record.id] = false;
 Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
 this.fireEvent('collapse', this, record, body, row.rowIndex);
 }
 }
});

Ext.preg('rowexpander', Ext.ux.grid.RowExpander);

//backwards compat
Ext.grid.RowExpander = Ext.ux.grid.RowExpander;

