334 lines
12 KiB
HTML
334 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
<title>The source code</title>
|
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
|
<style type="text/css">
|
|
.highlight { display: block; background-color: #ddd; }
|
|
</style>
|
|
<script type="text/javascript">
|
|
function highlight() {
|
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
|
}
|
|
</script>
|
|
</head>
|
|
<body onload="prettyPrint(); highlight();">
|
|
<pre class="prettyprint lang-js"><span id='Ext-grid-PivotAxis'>/**
|
|
</span> * @class Ext.grid.PivotAxis
|
|
* @extends Ext.Component
|
|
* <p>PivotAxis is a class that supports a {@link Ext.grid.PivotGrid}. Each PivotGrid contains two PivotAxis instances - the left
|
|
* axis and the top axis. Each PivotAxis defines an ordered set of dimensions, each of which should correspond to a field in a
|
|
* Store's Record (see {@link Ext.grid.PivotGrid} documentation for further explanation).</p>
|
|
* <p>Developers should have little interaction with the PivotAxis instances directly as most of their management is performed by
|
|
* the PivotGrid. An exception is the dynamic reconfiguration of axes at run time - to achieve this we use PivotAxis's
|
|
* {@link #setDimensions} function and refresh the grid:</p>
|
|
<pre><code>
|
|
var pivotGrid = new Ext.grid.PivotGrid({
|
|
//some PivotGrid config here
|
|
});
|
|
|
|
//change the left axis dimensions
|
|
pivotGrid.leftAxis.setDimensions([
|
|
{
|
|
dataIndex: 'person',
|
|
direction: 'DESC',
|
|
width : 100
|
|
},
|
|
{
|
|
dataIndex: 'product',
|
|
direction: 'ASC',
|
|
width : 80
|
|
}
|
|
]);
|
|
|
|
pivotGrid.view.refresh(true);
|
|
</code></pre>
|
|
* This clears the previous dimensions on the axis and redraws the grid with the new dimensions.
|
|
*/
|
|
Ext.grid.PivotAxis = Ext.extend(Ext.Component, {
|
|
<span id='Ext-grid-PivotAxis-cfg-orientation'> /**
|
|
</span> * @cfg {String} orientation One of 'vertical' or 'horizontal'. Defaults to horizontal
|
|
*/
|
|
orientation: 'horizontal',
|
|
|
|
<span id='Ext-grid-PivotAxis-cfg-defaultHeaderWidth'> /**
|
|
</span> * @cfg {Number} defaultHeaderWidth The width to render each row header that does not have a width specified via
|
|
{@link #getRowGroupHeaders}. Defaults to 80.
|
|
*/
|
|
defaultHeaderWidth: 80,
|
|
|
|
<span id='Ext-grid-PivotAxis-cfg-paddingWidth'> /**
|
|
</span> * @private
|
|
* @cfg {Number} paddingWidth The amount of padding used by each cell.
|
|
* TODO: From 4.x onwards this can be removed as it won't be needed. For now it is used to account for the differences between
|
|
* the content box and border box measurement models
|
|
*/
|
|
paddingWidth: 7,
|
|
|
|
<span id='Ext-grid-PivotAxis-method-setDimensions'> /**
|
|
</span> * Updates the dimensions used by this axis
|
|
* @param {Array} dimensions The new dimensions
|
|
*/
|
|
setDimensions: function(dimensions) {
|
|
this.dimensions = dimensions;
|
|
},
|
|
|
|
<span id='Ext-grid-PivotAxis-method-onRender'> /**
|
|
</span> * @private
|
|
* Builds the html table that contains the dimensions for this axis. This branches internally between vertical
|
|
* and horizontal orientations because the table structure is slightly different in each case
|
|
*/
|
|
onRender: function(ct, position) {
|
|
var rows = this.orientation == 'horizontal'
|
|
? this.renderHorizontalRows()
|
|
: this.renderVerticalRows();
|
|
|
|
this.el = Ext.DomHelper.overwrite(ct.dom, {tag: 'table', cn: rows}, true);
|
|
},
|
|
|
|
<span id='Ext-grid-PivotAxis-method-renderHorizontalRows'> /**
|
|
</span> * @private
|
|
* Specialised renderer for horizontal oriented axes
|
|
* @return {Object} The HTML Domspec for a horizontal oriented axis
|
|
*/
|
|
renderHorizontalRows: function() {
|
|
var headers = this.buildHeaders(),
|
|
rowCount = headers.length,
|
|
rows = [],
|
|
cells, cols, colCount, i, j;
|
|
|
|
for (i = 0; i < rowCount; i++) {
|
|
cells = [];
|
|
cols = headers[i].items;
|
|
colCount = cols.length;
|
|
|
|
for (j = 0; j < colCount; j++) {
|
|
cells.push({
|
|
tag: 'td',
|
|
html: cols[j].header,
|
|
colspan: cols[j].span
|
|
});
|
|
}
|
|
|
|
rows[i] = {
|
|
tag: 'tr',
|
|
cn: cells
|
|
};
|
|
}
|
|
|
|
return rows;
|
|
},
|
|
|
|
<span id='Ext-grid-PivotAxis-method-renderVerticalRows'> /**
|
|
</span> * @private
|
|
* Specialised renderer for vertical oriented axes
|
|
* @return {Object} The HTML Domspec for a vertical oriented axis
|
|
*/
|
|
renderVerticalRows: function() {
|
|
var headers = this.buildHeaders(),
|
|
colCount = headers.length,
|
|
rowCells = [],
|
|
rows = [],
|
|
rowCount, col, row, colWidth, i, j;
|
|
|
|
for (i = 0; i < colCount; i++) {
|
|
col = headers[i];
|
|
colWidth = col.width || 80;
|
|
rowCount = col.items.length;
|
|
|
|
for (j = 0; j < rowCount; j++) {
|
|
row = col.items[j];
|
|
|
|
rowCells[row.start] = rowCells[row.start] || [];
|
|
rowCells[row.start].push({
|
|
tag : 'td',
|
|
html : row.header,
|
|
rowspan: row.span,
|
|
width : Ext.isBorderBox ? colWidth : colWidth - this.paddingWidth
|
|
});
|
|
}
|
|
}
|
|
|
|
rowCount = rowCells.length;
|
|
for (i = 0; i < rowCount; i++) {
|
|
rows[i] = {
|
|
tag: 'tr',
|
|
cn : rowCells[i]
|
|
};
|
|
}
|
|
|
|
return rows;
|
|
},
|
|
|
|
<span id='Ext-grid-PivotAxis-method-getTuples'> /**
|
|
</span> * @private
|
|
* Returns the set of all unique tuples based on the bound store and dimension definitions.
|
|
* Internally we construct a new, temporary store to make use of the multi-sort capabilities of Store. In
|
|
* 4.x this functionality should have been moved to MixedCollection so this step should not be needed.
|
|
* @return {Array} All unique tuples
|
|
*/
|
|
getTuples: function() {
|
|
var newStore = new Ext.data.Store({});
|
|
|
|
newStore.data = this.store.data.clone();
|
|
newStore.fields = this.store.fields;
|
|
|
|
var sorters = [],
|
|
dimensions = this.dimensions,
|
|
length = dimensions.length,
|
|
i;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
sorters.push({
|
|
field : dimensions[i].dataIndex,
|
|
direction: dimensions[i].direction || 'ASC'
|
|
});
|
|
}
|
|
|
|
newStore.sort(sorters);
|
|
|
|
var records = newStore.data.items,
|
|
hashes = [],
|
|
tuples = [],
|
|
recData, hash, info, data, key;
|
|
|
|
length = records.length;
|
|
|
|
for (i = 0; i < length; i++) {
|
|
info = this.getRecordInfo(records[i]);
|
|
data = info.data;
|
|
hash = "";
|
|
|
|
for (key in data) {
|
|
hash += data[key] + '---';
|
|
}
|
|
|
|
if (hashes.indexOf(hash) == -1) {
|
|
hashes.push(hash);
|
|
tuples.push(info);
|
|
}
|
|
}
|
|
|
|
newStore.destroy();
|
|
|
|
return tuples;
|
|
},
|
|
|
|
<span id='Ext-grid-PivotAxis-method-getRecordInfo'> /**
|
|
</span> * @private
|
|
*/
|
|
getRecordInfo: function(record) {
|
|
var dimensions = this.dimensions,
|
|
length = dimensions.length,
|
|
data = {},
|
|
dimension, dataIndex, i;
|
|
|
|
//get an object containing just the data we are interested in based on the configured dimensions
|
|
for (i = 0; i < length; i++) {
|
|
dimension = dimensions[i];
|
|
dataIndex = dimension.dataIndex;
|
|
|
|
data[dataIndex] = record.get(dataIndex);
|
|
}
|
|
|
|
//creates a specialised matcher function for a given tuple. The returned function will return
|
|
//true if the record passed to it matches the dataIndex values of each dimension in this axis
|
|
var createMatcherFunction = function(data) {
|
|
return function(record) {
|
|
for (var dataIndex in data) {
|
|
if (record.get(dataIndex) != data[dataIndex]) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
};
|
|
};
|
|
|
|
return {
|
|
data: data,
|
|
matcher: createMatcherFunction(data)
|
|
};
|
|
},
|
|
|
|
<span id='Ext-grid-PivotAxis-method-buildHeaders'> /**
|
|
</span> * @private
|
|
* Uses the calculated set of tuples to build an array of headers that can be rendered into a table using rowspan or
|
|
* colspan. Basically this takes the set of tuples and spans any cells that run into one another, so if we had dimensions
|
|
* of Person and Product and several tuples containing different Products for the same Person, those Products would be
|
|
* spanned.
|
|
* @return {Array} The headers
|
|
*/
|
|
buildHeaders: function() {
|
|
var tuples = this.getTuples(),
|
|
rowCount = tuples.length,
|
|
dimensions = this.dimensions,
|
|
dimension,
|
|
colCount = dimensions.length,
|
|
headers = [],
|
|
tuple, rows, currentHeader, previousHeader, span, start, isLast, changed, i, j;
|
|
|
|
for (i = 0; i < colCount; i++) {
|
|
dimension = dimensions[i];
|
|
rows = [];
|
|
span = 0;
|
|
start = 0;
|
|
|
|
for (j = 0; j < rowCount; j++) {
|
|
tuple = tuples[j];
|
|
isLast = j == (rowCount - 1);
|
|
currentHeader = tuple.data[dimension.dataIndex];
|
|
|
|
/*
|
|
* 'changed' indicates that we need to create a new cell. This should be true whenever the cell
|
|
* above (previousHeader) is different from this cell, or when the cell on the previous dimension
|
|
* changed (e.g. if the current dimension is Product and the previous was Person, we need to start
|
|
* a new cell if Product is the same but Person changed, so we check the previous dimension and tuple)
|
|
*/
|
|
changed = previousHeader != undefined && previousHeader != currentHeader;
|
|
if (i > 0 && j > 0) {
|
|
changed = changed || tuple.data[dimensions[i-1].dataIndex] != tuples[j-1].data[dimensions[i-1].dataIndex];
|
|
}
|
|
|
|
if (changed) {
|
|
rows.push({
|
|
header: previousHeader,
|
|
span : span,
|
|
start : start
|
|
});
|
|
|
|
start += span;
|
|
span = 0;
|
|
}
|
|
|
|
if (isLast) {
|
|
rows.push({
|
|
header: currentHeader,
|
|
span : span + 1,
|
|
start : start
|
|
});
|
|
|
|
start += span;
|
|
span = 0;
|
|
}
|
|
|
|
previousHeader = currentHeader;
|
|
span++;
|
|
}
|
|
|
|
headers.push({
|
|
items: rows,
|
|
width: dimension.width || this.defaultHeaderWidth
|
|
});
|
|
|
|
previousHeader = undefined;
|
|
}
|
|
|
|
return headers;
|
|
}
|
|
});
|
|
</pre>
|
|
</body>
|
|
</html>
|