Ext.define('Ext.ux.grid.FiltersFeature', {
extend: 'Ext.grid.feature.Feature',
alias: 'feature.filters',
uses: [
'Ext.ux.grid.menu.ListMenu',
'Ext.ux.grid.menu.RangeMenu',
'Ext.ux.grid.filter.BooleanFilter',
'Ext.ux.grid.filter.DateFilter',
'Ext.ux.grid.filter.ListFilter',
'Ext.ux.grid.filter.NumericFilter',
'Ext.ux.grid.filter.StringFilter'
],
autoReload : true,
filterCls : 'ux-filtered-column',
local : false,
menuFilterText : 'Filters',
paramPrefix : 'filter',
showMenu : true,
stateId : undefined,
updateBuffer : 500,
hasFeatureEvent: false,
constructor : function (config) {
var me = this;
config = config || {};
Ext.apply(me, config);
me.deferredUpdate = Ext.create('Ext.util.DelayedTask', me.reload, me);
me.filters = me.createFiltersCollection();
me.filterConfigs = config.filters;
},
attachEvents: function() {
var me = this,
view = me.view,
headerCt = view.headerCt,
grid = me.getGridPanel();
me.bindStore(view.getStore(), true);
headerCt.on('menucreate', me.onMenuCreate, me);
view.on('refresh', me.onRefresh, me);
grid.on({
scope: me,
beforestaterestore: me.applyState,
beforestatesave: me.saveState,
beforedestroy: me.destroy
});
grid.filters = me;
grid.addEvents('filterupdate');
},
createFiltersCollection: function () {
return Ext.create('Ext.util.MixedCollection', false, function (o) {
return o ? o.dataIndex : null;
});
},
createFilters: function() {
var me = this,
hadFilters = me.filters.getCount(),
grid = me.getGridPanel(),
filters = me.createFiltersCollection(),
model = grid.store.model,
fields = model.prototype.fields,
field,
filter,
state;
if (hadFilters) {
state = {};
me.saveState(null, state);
}
function add (dataIndex, config, filterable) {
if (dataIndex && (filterable || config)) {
field = fields.get(dataIndex);
filter = {
dataIndex: dataIndex,
type: (field && field.type && field.type.type) || 'auto'
};
if (Ext.isObject(config)) {
Ext.apply(filter, config);
}
filters.replace(filter);
}
}
Ext.Array.each(me.filterConfigs, function (filterConfig) {
add(filterConfig.dataIndex, filterConfig);
});
Ext.Array.each(grid.columns, function (column) {
if (column.filterable === false) {
filters.removeAtKey(column.dataIndex);
} else {
add(column.dataIndex, column.filter, column.filterable);
}
});
me.removeAll();
if (filters.items) {
me.initializeFilters(filters.items);
}
if (hadFilters) {
me.applyState(null, state);
}
},
initializeFilters: function(filters) {
var me = this,
filtersLength = filters.length,
i, filter, FilterClass;
for (i = 0; i < filtersLength; i++) {
filter = filters[i];
if (filter) {
FilterClass = me.getFilterClass(filter.type);
filter = filter.menu ? filter : new FilterClass(filter);
me.filters.add(filter);
Ext.util.Observable.capture(filter, this.onStateChange, this);
}
}
},
onMenuCreate: function(headerCt, menu) {
var me = this;
me.createFilters();
menu.on('beforeshow', me.onMenuBeforeShow, me);
},
onMenuBeforeShow: function(menu) {
var me = this,
menuItem, filter;
if (me.showMenu) {
menuItem = me.menuItem;
if (!menuItem || menuItem.isDestroyed) {
me.createMenuItem(menu);
menuItem = me.menuItem;
}
filter = me.getMenuFilter();
if (filter) {
menuItem.setMenu(filter.menu, false);
menuItem.setChecked(filter.active);
menuItem.setDisabled(filter.disabled === true);
}
menuItem.setVisible(!!filter);
this.sep.setVisible(!!filter);
}
},
createMenuItem: function(menu) {
var me = this;
me.sep = menu.add('-');
me.menuItem = menu.add({
checked: false,
itemId: 'filters',
text: me.menuFilterText,
listeners: {
scope: me,
checkchange: me.onCheckChange,
beforecheckchange: me.onBeforeCheck
}
});
},
getGridPanel: function() {
return this.view.up('gridpanel');
},
applyState : function (grid, state) {
var me = this,
key, filter;
me.applyingState = true;
me.clearFilters();
if (state.filters) {
for (key in state.filters) {
if (state.filters.hasOwnProperty(key)) {
filter = me.filters.get(key);
if (filter) {
filter.setValue(state.filters[key]);
filter.setActive(true);
}
}
}
}
me.deferredUpdate.cancel();
if (me.local) {
me.reload();
}
delete me.applyingState;
delete state.filters;
},
saveState : function (grid, state) {
var filters = {};
this.filters.each(function (filter) {
if (filter.active) {
filters[filter.dataIndex] = filter.getValue();
}
});
return (state.filters = filters);
},
destroy : function () {
var me = this;
Ext.destroyMembers(me, 'menuItem', 'sep');
me.removeAll();
me.clearListeners();
},
removeAll : function () {
if(this.filters){
Ext.destroy.apply(Ext, this.filters.items);
this.filters.clear();
}
},
bindStore : function(store) {
var me = this;
if (me.store && me.storeListeners) {
me.store.un(me.storeListeners);
}
if (store) {
me.storeListeners = {
scope: me
};
if (me.local) {
me.storeListeners.load = me.onLoad;
} else {
me.storeListeners['before' + (store.buffered ? 'prefetch' : 'load')] = me.onBeforeLoad;
}
store.on(me.storeListeners);
} else {
delete me.storeListeners;
}
me.store = store;
},
getMenuFilter : function () {
var header = this.view.headerCt.getMenu().activeHeader;
return header ? this.filters.get(header.dataIndex) : null;
},
onCheckChange : function (item, value) {
this.getMenuFilter().setActive(value);
},
onBeforeCheck : function (check, value) {
return !value || this.getMenuFilter().isActivatable();
},
onStateChange : function (event, filter) {
if (event !== 'serialize') {
var me = this,
grid = me.getGridPanel();
if (filter == me.getMenuFilter()) {
me.menuItem.setChecked(filter.active, false);
}
if ((me.autoReload || me.local) && !me.applyingState) {
me.deferredUpdate.delay(me.updateBuffer);
}
me.updateColumnHeadings();
if (!me.applyingState) {
grid.saveState();
}
grid.fireEvent('filterupdate', me, filter);
}
},
onBeforeLoad : function (store, options) {
options.params = options.params || {};
this.cleanParams(options.params);
var params = this.buildQuery(this.getFilterData());
Ext.apply(options.params, params);
},
onLoad : function (store) {
store.filterBy(this.getRecordFilter());
},
onRefresh : function () {
this.updateColumnHeadings();
},
updateColumnHeadings : function () {
var me = this,
headerCt = me.view.headerCt;
if (headerCt) {
headerCt.items.each(function(header) {
var filter = me.getFilter(header.dataIndex);
header[filter && filter.active ? 'addCls' : 'removeCls'](me.filterCls);
});
}
},
reload : function () {
var me = this,
store = me.view.getStore();
if (me.local) {
store.clearFilter(true);
store.filterBy(me.getRecordFilter());
store.sort();
} else {
me.deferredUpdate.cancel();
if (store.buffered) {
store.pageMap.clear();
}
store.loadPage(1);
}
},
getRecordFilter : function () {
var f = [], len, i;
this.filters.each(function (filter) {
if (filter.active) {
f.push(filter);
}
});
len = f.length;
return function (record) {
for (i = 0; i < len; i++) {
if (!f[i].validateRecord(record)) {
return false;
}
}
return true;
};
},
addFilter : function (config) {
var me = this,
columns = me.getGridPanel().columns,
i, columnsLength, column, filtersLength, filter;
for (i = 0, columnsLength = columns.length; i < columnsLength; i++) {
column = columns[i];
if (column.dataIndex === config.dataIndex) {
column.filter = config;
}
}
if (me.view.headerCt.menu) {
me.createFilters();
} else {
me.view.headerCt.getMenu();
}
for (i = 0, filtersLength = me.filters.items.length; i < filtersLength; i++) {
filter = me.filters.items[i];
if (filter.dataIndex === config.dataIndex) {
return filter;
}
}
},
addFilters : function (filters) {
if (filters) {
var me = this,
i, filtersLength;
for (i = 0, filtersLength = filters.length; i < filtersLength; i++) {
me.addFilter(filters[i]);
}
}
},
getFilter : function (dataIndex) {
return this.filters.get(dataIndex);
},
clearFilters : function () {
this.filters.each(function (filter) {
filter.setActive(false);
});
},
getFilterData : function () {
var filters = [], i, len;
this.filters.each(function (f) {
if (f.active) {
var d = [].concat(f.serialize());
for (i = 0, len = d.length; i < len; i++) {
filters.push({
field: f.dataIndex,
data: d[i]
});
}
}
});
return filters;
},
buildQuery : function (filters) {
var p = {}, i, f, root, dataPrefix, key, tmp,
len = filters.length;
if (!this.encode){
for (i = 0; i < len; i++) {
f = filters[i];
root = [this.paramPrefix, '[', i, ']'].join('');
p[root + '[field]'] = f.field;
dataPrefix = root + '[data]';
for (key in f.data) {
p[[dataPrefix, '[', key, ']'].join('')] = f.data[key];
}
}
} else {
tmp = [];
for (i = 0; i < len; i++) {
f = filters[i];
tmp.push(Ext.apply(
{},
{field: f.field},
f.data
));
}
if (tmp.length > 0){
p[this.paramPrefix] = Ext.JSON.encode(tmp);
}
}
return p;
},
cleanParams : function (p) {
if (this.encode) {
delete p[this.paramPrefix];
} else {
var regex, key;
regex = new RegExp('^' + this.paramPrefix + '\[[0-9]+\]');
for (key in p) {
if (regex.test(key)) {
delete p[key];
}
}
}
},
getFilterClass : function (type) {
switch(type) {
case 'auto':
type = 'string';
break;
case 'int':
case 'float':
type = 'numeric';
break;
case 'bool':
type = 'boolean';
break;
}
return Ext.ClassManager.getByAlias('gridfilter.' + type);
}
});