/**
* This base class is used to handle data preparation (e.g., sorting, filtering and
* group summary).
*/
Ext.define('Ext.ux.ajax.DataSimlet', function() {
function makeSortFn(def, cmp) {
var order = def.direction,
sign = (order && order.toUpperCase() === 'DESC') ? -1 : 1;
return function(leftRec, rightRec) {
var lhs = leftRec[def.property],
rhs = rightRec[def.property],
c = (lhs < rhs) ? -1 : ((rhs < lhs) ? 1 : 0);
if (c || !cmp) {
return c * sign;
}
return cmp(leftRec, rightRec);
};
}
function makeSortFns(defs, cmp) {
var sortFn, i;
for (sortFn = cmp, i = defs && defs.length; i;) {
sortFn = makeSortFn(defs[--i], sortFn);
}
return sortFn;
}
return {
extend: 'Ext.ux.ajax.Simlet',
buildNodes: function(node, path) {
var me = this,
nodeData = {
data: []
},
len = node.length,
children, i, child, name;
me.nodes[path] = nodeData;
for (i = 0; i < len; ++i) {
nodeData.data.push(child = node[i]);
name = child.text || child.title;
child.id = path ? path + '/' + name : name;
children = child.children;
if (!(child.leaf = !children)) {
delete child.children;
me.buildNodes(children, child.id);
}
}
},
deleteRecord: function(pos) {
if (this.data && typeof this.data !== 'function') {
Ext.Array.removeAt(this.data, pos);
}
},
fixTree: function(ctx, tree) {
var me = this,
node = ctx.params.node,
nodes;
if (!(nodes = me.nodes)) {
me.nodes = nodes = {};
me.buildNodes(tree, '');
}
node = nodes[node];
if (node) {
if (me.node) {
me.node.sortedData = me.sortedData;
me.node.currentOrder = me.currentOrder;
}
me.node = node;
me.data = node.data;
me.sortedData = node.sortedData;
me.currentOrder = node.currentOrder;
}
else {
me.data = null;
}
},
getData: function(ctx) {
var me = this,
params = ctx.params,
order = (params.filter || '') + (params.group || '') + '-' + (params.sort || '') +
'-' + (params.dir || ''),
tree = me.tree,
dynamicData, data, fields, sortFn, filters;
if (tree) {
me.fixTree(ctx, tree);
}
data = me.data;
if (typeof data === 'function') {
dynamicData = true;
data = data.call(this, ctx);
}
// If order is '--' then it means we had no order passed, due to the string concat above
if (!data || order === '--') {
return data || [];
}
if (!dynamicData && order === me.currentOrder) {
return me.sortedData;
}
ctx.filterSpec = params.filter && Ext.decode(params.filter);
ctx.groupSpec = params.group && Ext.decode(params.group);
fields = params.sort;
if (params.dir) {
fields = [{ direction: params.dir, property: fields }];
}
else if (params.sort) {
fields = Ext.decode(params.sort);
}
else {
fields = null;
}
if (ctx.filterSpec) {
filters = new Ext.util.FilterCollection();
filters.add(this.processFilters(ctx.filterSpec));
data = Ext.Array.filter(data, filters.getFilterFn());
}
sortFn = makeSortFns((ctx.sortSpec = fields));
if (ctx.groupSpec) {
sortFn = makeSortFns([ctx.groupSpec], sortFn);
}
// If a straight Ajax request, data may not be an array.
// If an Array, preserve 'physical' order of raw data...
data = Ext.isArray(data) ? data.slice(0) : data;
if (sortFn) {
Ext.Array.sort(data, sortFn);
}
me.sortedData = data;
me.currentOrder = order;
return data;
},
processFilters: Ext.identityFn,
getPage: function(ctx, data) {
var ret = data,
length = data.length,
start = ctx.params.start || 0,
end = ctx.params.limit ? Math.min(length, start + ctx.params.limit) : length;
if (start || end < length) {
ret = ret.slice(start, end);
}
return ret;
},
getGroupSummary: function(groupField, rows, ctx) {
return rows[0];
},
getSummary: function(ctx, data, page) {
var me = this,
groupField = ctx.groupSpec.property,
accum,
todo = {},
summary = [],
fieldValue,
lastFieldValue;
Ext.each(page, function(rec) {
fieldValue = rec[groupField];
todo[fieldValue] = true;
});
function flush() {
if (accum) {
summary.push(me.getGroupSummary(groupField, accum, ctx));
accum = null;
}
}
// data is ordered primarily by the groupField, so one pass can pick up all
// the summaries one at a time.
Ext.each(data, function(rec) {
fieldValue = rec[groupField];
if (lastFieldValue !== fieldValue) {
flush();
lastFieldValue = fieldValue;
}
if (!todo[fieldValue]) {
// if we have even 1 summary, we have summarized all that we need
// (again because data and page are ordered by groupField)
return !summary.length;
}
if (accum) {
accum.push(rec);
}
else {
accum = [rec];
}
return true;
});
flush(); // make sure that last pesky summary goes...
return summary;
}
};
}());