if (filter == this.getMenuFilter()) {
this.menu.setChecked(filter.active, false);
}
if ((this.autoReload || this.local) && !this.applyingState) {
this.deferredUpdate.delay(this.updateBuffer);
}
this.updateColumnHeadings();
if (!this.applyingState) {
this.grid.saveState();
}
this.grid.fireEvent('filterupdate', this, filter);
},
/**
* @private
* Handler for store's beforeload event when configured for remote filtering
* @param {Object} store
* @param {Object} options
*/
onBeforeLoad: function(store, options) {
options.params = options.params || {};
this.cleanParams(options.params);
var params = this.buildQuery(this.getFilterData());
Ext.apply(options.params, params);
},
/**
* @private
* Handler for store's load event when configured for local filtering
* @param {Object} store
* @param {Object} options
*/
onLoad: function(store, options) {
store.filterBy(this.getRecordFilter());
},
/**
* @private
* Handler called when the grid's view is refreshed
*/
onRefresh: function() {
this.updateColumnHeadings();
},
/**
* Update the styles for the header row based on the active filters
*/
updateColumnHeadings: function() {
var view = this.grid.getView(),
hds, i, len, filter;
if (view.mainHd) {
hds = view.mainHd.select('td').removeClass(this.filterCls);
for (i = 0, len = view.cm.config.length; i < len; i++) {
filter = this.getFilter(view.cm.config[i].dataIndex);
if (filter && filter.active) {
hds.item(i).addClass(this.filterCls);
}
}
}
},
/** @private */
reload: function() {
if (this.local) {
this.grid.store.clearFilter(true);
this.grid.store.filterBy(this.getRecordFilter());
} else {
var start,
store = this.grid.store;
this.deferredUpdate.cancel();
if (this.toolbar) {
start = store.paramNames.start;
if (store.lastOptions && store.lastOptions.params && store.lastOptions.params[start]) {
store.lastOptions.params[start] = 0;
}
}
store.reload();
}
},
/**
* Method factory that generates a record validator for the filters active at the time
* of invokation.
* @private
*/
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;
};
},
/**
* Adds a filter to the collection and observes it for state change.
* @param {Object/Ext.ux.grid.filter.Filter} config A filter configuration or a filter object.
* @return {Ext.ux.grid.filter.Filter} The existing or newly created filter object.
*/
addFilter: function(config) {
var Cls = this.getFilterClass(config.type),
filter = config.menu ? config : (new Cls(config));
this.filters.add(filter);
/**
* Adds filters to the collection.
* @param {Array/Ext.grid.ColumnModel} filters Either an Array of
* filter configuration objects or an Ext.grid.ColumnModel. The columns
* of a passed Ext.grid.ColumnModel will be examined for a <code>filter</code>
* property and, if present, will be used as the filter configuration object.
*/
addFilters: function(filters) {
if (filters) {
var i, len, filter, cm = false, dI;
if (filters instanceof Ext.grid.ColumnModel) {
filters = filters.config;
cm = true;
}
for (i = 0, len = filters.length; i < len; i++) {
filter = false;
if (cm) {
dI = filters[i].dataIndex;
filter = filters[i].filter || filters[i].filterable;
if (filter) {
filter = (filter === true) ? {} : filter;
Ext.apply(filter, { dataIndex: dI });
// filter type is specified in order of preference:
// filter type specified in config
// type specified in store's field's type config
filter.type = filter.type || this.store.fields.get(dI).type;
}
} else {
filter = filters[i];
}
// if filter config found add filter for the column
if (filter) {
this.addFilter(filter);
}
}
}
},
/**
* Returns a filter for the given dataIndex, if one exists.
* @param {String} dataIndex The dataIndex of the desired filter object.
* @return {Ext.ux.grid.filter.Filter}
*/
getFilter: function(dataIndex) {
return this.filters.get(dataIndex);
},
/**
* Turns all filters off. This does not clear the configuration information
* (see {@link #removeAll}).
*/
clearFilters: function() {
this.filters.each(function(filter) {
filter.setActive(false);
});
},
/**
* Returns an Array of the currently active filters.
* @return {Array} filters Array of the currently active filters.
*/
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 = {};
var arr = new Array();
for (var i = 0, len = filters.length; i < len; i++) {
var f = filters[i];
var child = new Array();
child.push(f.field);
for (var key in f.data) {
child.push(f.data[key]);
}
arr.push(child.join('|'));
}
p[this.paramPrefix] = arr.join('$');
return p;
},
cleanParams: function(p) {
// if encoding just delete the property
if (this.encode) {
delete p[this.paramPrefix];
// otherwise scrub the object of filter data
} 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) {
// map the supported Ext.data.Field type values into a supported filter
switch (type) {
case 'auto':
type = 'string';
break;
case 'int':
case 'float':
type = 'numeric';
break;
}
return Ext.ux.grid.filter[type.substr(0, 1).toUpperCase() + type.substr(1) + 'Filter'];
}
});
this.bindStore(this.grid.getStore(), true);
// assumes no filters were passed in the constructor, so try and use ones from the colModel
if (this.filters.getCount() == 0) {
this.addFilters(this.grid.getColumnModel());
}
/**
* Remove all filters, permanently destroying them.
*/
removeAll: function() {
if (this.filters) {
Ext.destroy.apply(Ext, this.filters.items);
// remove all items from the collection
this.filters.clear();
}
},
/**
* Changes the data store bound to this view and refreshes it.
* @param {Store} store The store to bind to this view
*/
bindStore: function(store, initial) {
if (!initial && this.store) {
if (this.local) {
store.un('load', this.onLoad, this);
} else {
store.un('beforeload', this.onBeforeLoad, this);
}
}
if (store) {
if (this.local) {
store.on('load', this.onLoad, this);
} else {
store.on('beforeload', this.onBeforeLoad, this);
}
}
this.store = store;
},
/**
* @private
* Handler called when the grid reconfigure event fires
*/
onReconfigure: function() {
this.bindStore(this.grid.getStore());
this.store.clearFilter();
this.removeAll();
this.addFilters(this.grid.getColumnModel());
this.updateColumnHeadings();
},
createMenu: function() {
var view = this.grid.getView(),
hmenu = view.hmenu;
/**
* @private
* Get the filter menu from the filters MixedCollection based on the clicked header
*/
getMenuFilter: function() {
var view = this.grid.getView();
if (!view || view.hdCtxIndex === undefined) {
return null;
}
return this.filters.get(
view.cm.config[view.hdCtxIndex].dataIndex
);
},
/**
* @private
* Handler called by the grid's hmenu beforeshow event
*/
onMenu: function(filterMenu) {
var filter = this.getMenuFilter();
if (filter) {
/*
TODO: lazy rendering
if (!filter.menu) {
filter.menu = filter.createMenu();
}
*/
this.menu.menu = filter.menu;
this.menu.setChecked(filter.active, false);
// disable the menu if filter.disabled explicitly set to true
this.menu.setDisabled(filter.disabled === true);
}
/**
* @private
* Handler for all events on filters.
* @param {String} event Event name
* @param {Object} filter Standard signature of the event before the event is fired
*/
onStateChange: function(event, filter) {
if (event === 'serialize') {
return;
}