Ext.namespace('Zarafa.common.ui.grid');
/**
* @class Zarafa.common.ui.grid.GridView
* @extends Ext.grid.GridView
* @xtype zarafa.gridview
*
* WebApp specific GridView which contain extra features and bugfixes
* which could not be resolved by plugins or directly in extjs.
*/
Zarafa.common.ui.grid.GridView = Ext.extend(Ext.grid.GroupingView, {
/**
* @cfg {Boolean} disableScrollToTop {@link Ext.grid.GridView} by default scrolls to top when data is loaded in
* {@link Zarafa.core.data.MAPIStore MAPIStore}, but in our case {@link Zarafa.core.ContextModel ContextModel} handles
* selection of records based on settings. so this flag will disable default functionality of {@link Ext.grid.GridView}.
*/
disableScrollToTop : undefined,
/**
* @cfg {Boolean} isBuffering by default it was false which represent that no more rows are in buffer to
* insert in to {@link Zarafa.mail.ui.MailGrid mailgrid} and if it is true means there are some rows
* in buffer which are going to inser in {@link Zarafa.mail.ui.MailGrid mailgrid}, also it will not allow
* to fire {@link #livescrollstart} event till {@link #isBuffering} get false.
*/
isBuffering : false,
/**
* @constructor
* @param {Object} config Configuration object
*/
constructor : function(config)
{
Ext.applyIf(config, {
enableGrouping : true,
enableGroupingMenu : false,
groupTextTpl : '{text:htmlEncode} ({values.rs.length} {[ngettext("Item","Items", values.rs.length)]})',
disableScrollToTop : false,
deferEmptyText : true,
emptyText : '<div class="emptytext">' + _('There are no items to show in this list') + '</div>',
forceFit : true
});
this.addEvents(
/**
* @event beforelivescrollstart
* Fires when the scroll is being {@link #onScroll started}.
* @param {Zarafa.common.ui.grid.GridView} grid view The grid view which fired the event
* @param {HtmlElement} target The target of the event.
*/
'beforelivescrollstart',
/**
* @event livescrollstart
* Fires when the scroll is being {@link #onScroll started}.
* @param {Number} cursor the cursor contains the last index of record in grid.
*/
'livescrollstart',
/**
* @event beforesort
* Fires when the header of grid is being {@link #onHeaderClick clicked}.
* @param {Number} cursor the cursor contains the last index of record in grid.
*/
'beforesort'
);
Zarafa.common.ui.grid.GridView.superclass.constructor.call(this, config);
this.initEvents();
},
/**
* Initialize event handlers
* @private
*/
initEvents : function()
{
Zarafa.common.ui.grid.GridView.superclass.initEvents.call(this);
this.on('rowremoved', this.onRowRemoved, this);
},
/**
* Binds a new Store and ColumnModel to this GridView. Removes any listeners from the old objects (if present)
* and adds listeners to the new ones
* @param {Ext.data.Store} newStore The new Store instance
* @param {Ext.grid.ColumnModel} newColModel The new ColumnModel instance
* @private
*/
initData : function(newStore, newColModel)
{
if (this.ds) {
this.ds.un('exception', this.onException, this);
}
Zarafa.common.ui.grid.GridView.superclass.initData.apply(this, arguments);
if (this.ds) {
this.ds.on('exception', this.onException, this);
}
},
/**
* Event handler will be called when the {@link Zarafa.core.data.MAPIStore Store} has
* fired an exception event.
* @param {Ext.data.DataProxy} proxy The proxy which fired the event.
* No event handler may modify any properties inside the provided record.
* @param {String} type See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
* for description.
* @param {String} action See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
* for description.
* @param {Object} options See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
* for description.
* @param {Object} response See {@link Ext.data.DataProxy}.{@link Ext.data.DataProxy#exception exception}
* for description.
* @param {Zarafa.core.data.MAPIRecord} record The record which was subject of the request
* that encountered an exception.
* @param {String} error (Optional) Passed when a thrown JS Exception or JS Error is
* available.
* @private
*/
onException : function(proxy, type, action, options, response, arg)
{
if (options && options.actionType === 'list') {
this.mainBody.update('<div class="x-grid-empty"><div class="emptytext">' + response.error.info.display_message + '</div></div>');
}
},
/**
* Event handler is used to execute {@link #scrollToTop} method to scroll to first item after
* {@link Zarafa.core.data.MAPIStore MAPIStore} has completed loading all data, but we don't need this
* default behaviour and handle it differently through {@link Zarafa.core.ContextModel ContextModel},
* so to disable this functionality we have provided a config {@link #disableScrollToTop}.
* @param {Ext.data.Store} store The store which was loaded
* @param {Ext.data.Record[]} records The records which were loaded into the store
* @param {Object} options The options which were used to load the data
*/
onLoad : function(store, record, options)
{
// if grid view is destroyed then we shouldn't call this function.
// if reload/search is called than we don't want to scroll, and if load is called than we should scroll to top.
if (!Ext.isDefined(options.reload) && this.disableScrollToTop !== true && Ext.isDefined(this.scroller.dom) && options.actionType == 'list') {
Zarafa.common.ui.grid.GridView.superclass.onLoad.apply(this, arguments);
}
},
/**
* Event handler which is called when the data inside the store has changed.
* Because {@link #refresh} blocks {@link #applyEmptyText}, we have to call it
* here manually again.
* @private
*/
onDataChange : function()
{
Zarafa.common.ui.grid.GridView.superclass.onDataChange.apply(this, arguments);
this.applyEmptyText();
},
/**
* Displays the configured emptyText if there are currently no rows to display
* @private
*/
applyEmptyText : function()
{
// When we are reloading, do not apply the empty text, we do not
// want to confuse the user by indicating no items are found, while we in fact
// are still downloading the list of items.
if (!this.grid.store.isExecuting(Zarafa.core.Actions['list'])) {
Zarafa.common.ui.grid.GridView.superclass.applyEmptyText.call(this);
}
},
/**
* This is called internally, once, by this.render after the HTML elements are added to the grid element.
* This is always intended to be called after renderUI. Sets up listeners on the UI elements
* and sets up options like column menus, moving and resizing.
* This function is overridden to use a {@link Zarafa.common.ui.grid.GridDragZone} for the {@link #dragZone},
* rather then the default {@link Ext.grid.GridDragZone}.
* @private
*/
afterRenderUI : function()
{
Zarafa.common.ui.grid.GridView.superclass.afterRenderUI.apply(this, arguments);
if (this.dragZone) {
this.dragZone.destroy();
this.dragZone = new Zarafa.common.ui.grid.GridDragZone(this.grid, {
ddGroup : this.grid.ddGroup || 'GridDD'
});
}
},
/**
* Event handler which is triggered when scroll bar of an {@link Ext.grid.GridPanel Grid}
* scrolled. here it will fire {@link #livescrollstart} event, if {@link Ext.grid.GridPanel Grid}
* as rows and scroll bar is at the end of {@link Ext.grid.GridPanel Grid}.
* @param {Ext.EventObject} event The {@link Ext.EventObject} encapsulating the DOM event.
* @param {HtmlElement} target The target of the event.
* @param {Object} option The options configuration passed to the {@link #addListener} call.
*/
onScroll : function(event, target, option)
{
if(this.fireEvent('beforelivescrollstart', this, target) !== false) {
// chrome dose not support scrollTopMax so we have to find the scrollTopMax manually.
var scrollTopMax;
if(!Ext.isDefined(target.scrollTopMax)) {
scrollTopMax = target.scrollHeight - target.offsetHeight;
} else {
scrollTopMax = target.scrollTopMax;
}
var scrollState = scrollTopMax * 0.90;
if(scrollState < target.scrollTop && !this.isBuffering) {
var cursor = this.ds.getCount();
if(cursor !== this.ds.totalLength) {
this.fireEvent('livescrollstart', cursor);
}
}
}
},
/**
* Function was used to reset the scroller position to top if it is not. also it will
* set the focus to first line of grid.
*/
resetScroll : function()
{
if(this.getScrollState().top > 0) {
this.scrollToTop();
// it is required in case when user load all records in grid and switch the
// view of grid, then it will show the extra space in grid. once user click on grid
// it will resize grid and remove the extra space. it happens because focus element of grid
// will not resize when user switch the view so we have to set the focus on grid.
this.focusRow(1);
}
},
/**
* Called when the GridView has been rendered.
* This will check if the store has been loaded already, and apply
* the emptyText if needed.
* @private
*/
afterRender : function()
{
Zarafa.common.ui.grid.GridView.superclass.afterRender.apply(this, arguments);
// When deferEmptyText is false, the emptyText will only be applied after
// the store has been loaded (the 'load' event has been fired). However, when
// we arrive here, while the store has already been loaded, we would show nothing,
// since we missed the event.
var store = this.grid.store;
if (this.deferEmptyText === false && store) {
if (!store.lastOptions || !Ext.isEmpty(Object.keys(store.lastOptions))) {
this.applyEmptyText();
}
}
this.scroller.on('scroll', this.onScroll, this);
},
/**
* Called after a row has been removed for the GridView.
* This will check if the store has a next/previous row to select in the Grid
* @private
*/
onRowRemoved : function(view, rowIndex, record)
{
var sm = this.grid.getSelectionModel();
var itemCount = this.grid.store.getCount();
if (itemCount > 0) {
// check for the next item in store else select the previous item
if(rowIndex < itemCount) {
sm.selectRow(rowIndex);
} else {
sm.selectRow(rowIndex - 1);
}
} else {
sm.clearSelections();
// When the store is empty, sm.clearSelections will not
// fire any events to indicate that the selections have
// changed...
sm.fireEvent('selectionchange', sm);
}
},
* Event handler triggered when header of {@link Ext.grid.GridPanel Grid}
* was clicked for sort the data of {@link Ext.grid.GridPanel Grid}.
* also it will fire the {@link #beforesort} event.
* @param {Ext.grid.GridPanel} grid The grid on which the user clicked
* @param {Number} The index number of the header which clicked.
* @private
*/
onHeaderClick : function(grid, index)
{
if(this.fireEvent('beforesort', this) !== false) {
Zarafa.common.ui.grid.GridView.superclass.onHeaderClick.apply(this, arguments);
}
},
/**
* Event handler triggered when header menu item was clicked and the column show/hide submenu (if available).
* Performs sorting if the sorter buttons were clicked, otherwise hides/shows the column that was clicked.
* also it will fire the {@link #beforesort} event.
* @param {Ext.menu.Item} item The item which is being clicked
* @private
*/
handleHdMenuClick : function(item)
{
if(this.fireEvent('beforesort', this) !== false) {
Zarafa.common.ui.grid.GridView.superclass.handleHdMenuClick.apply(this, arguments);
}
},
/**
* Function used to insert the dummy row in grid and warp the loading mask on it.
* also it was {@link #isBuffering} set to true.
*
* @param {String} msg the message which load mask shows while loading.
*/
showGridRowLoadMask : function(msg)
{
this.isBuffering = true;
var height = this.getRow(1).offsetHeight;
var width = this.getTotalWidth();
// add dummy row at bottom of the grid.
var style = {styles : 'width :'+width+'px; height :'+height+'px'};
var tpl = new Ext.Template('<div id="dummy-row" style = "{styles}"> </div>');
var html = tpl.apply(style);
var dom = Ext.DomHelper.insertHtml('beforeEnd',this.mainBody.dom, html);
// wrap the loading mask on the dummy row.
Ext.get(dom).mask(msg, 'x-mask-loading x-mask-loading-row');
var el = Ext.get(dom).child('.ext-el-mask', true);
el.className += 'x-mask-row';
},
/**
* Function use to remove the dummy row which use to show the loading mask and
* {@link #isBuffering} set to false.
*/
removeGridRowLoadMask : function()
{
var rowMask = Ext.query('div#dummy-row', this.mainBody.dom)[0];
if(Ext.isDefined(rowMask)) {
Ext.get(rowMask).remove();
}
this.isBuffering = false;
}
});
Ext.reg('zarafa.gridview', Zarafa.common.ui.grid.GridView);