UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

574 lines (488 loc) • 20.6 kB
"use strict"; var deferredUtils = require("../../core/utils/deferred"), when = deferredUtils.when, Deferred = deferredUtils.Deferred, dataUtils = require("../../data/utils"), dataQuery = require("../../data/query"), dateSerialization = require("../../core/utils/date_serialization"), DataSourceModule = require("../../data/data_source/data_source"), CustomStore = require("../../data/custom_store"), dataCoreUtils = require("../../core/utils/data"), Class = require("../../core/class"), commonUtils = require("../../core/utils/common"), typeUtils = require("../../core/utils/type"), each = require("../../core/utils/iterator").each, pivotGridUtils = require("./ui.pivot_grid.utils"), getFiltersByPath = pivotGridUtils.getFiltersByPath, setFieldProperty = pivotGridUtils.setFieldProperty, ArrayStore = require("../../data/array_store"); exports.LocalStore = Class.inherit(function () { var DATE_INTERVAL_SELECTORS = { 'year': function year(date) { return date && date.getFullYear(); }, 'quarter': function quarter(date) { return date && Math.floor(date.getMonth() / 3) + 1; }, 'month': function month(date) { return date && date.getMonth() + 1; }, 'day': function day(date) { return date && date.getDate(); }, 'dayOfWeek': function dayOfWeek(date) { return date && date.getDay(); } }; function getDataSelector(dataField) { return dataField.indexOf(".") !== -1 ? dataCoreUtils.compileGetter(dataField) : function (data) { return data[dataField]; }; } function getDateValue(dataSelector) { return function (data) { var value = dataSelector(data); if (value && !(value instanceof Date)) { value = dateSerialization.deserializeDate(value); } return value; }; } function prepareFields(fields) { each(fields || [], function (_, field) { var fieldSelector, intervalSelector, dataField = field.dataField, groupInterval, levels = field.levels, dataSelector; if (!field.selector) { if (!dataField) { dataSelector = function dataSelector(data) { return data; }; } else { dataSelector = getDataSelector(dataField); } if (levels) { prepareFields(levels); } if (field.dataType === 'date') { intervalSelector = DATE_INTERVAL_SELECTORS[field.groupInterval]; var valueSelector = getDateValue(dataSelector); fieldSelector = function fieldSelector(data) { var value = valueSelector(data); return intervalSelector ? intervalSelector(value) : value; }; } else if (field.dataType === 'number') { groupInterval = typeUtils.isNumeric(field.groupInterval) && field.groupInterval > 0 && field.groupInterval; fieldSelector = function fieldSelector(data) { var value = dataSelector(data); if (typeUtils.isString(value)) { value = Number(value); } return groupInterval ? Math.floor(value / groupInterval) * groupInterval : value; }; } else { fieldSelector = dataSelector; } pivotGridUtils.setDefaultFieldValueFormatting(field); setFieldProperty(field, "selector", fieldSelector); } }); } var addHierarchyItem = function addHierarchyItem(value, hierarchyItems, pathHash, childrenHash) { var hierarchyItem = childrenHash[pathHash]; if (!hierarchyItem) { hierarchyItem = { value: value, index: childrenHash.length++ }; childrenHash[pathHash] = hierarchyItem; hierarchyItems.push(hierarchyItem); } return hierarchyItem; }; function fillHierarchyItemIndexesCore(indexes, options, children, expandIndex, pathHash) { var dimension = options.dimensions[expandIndex], expandedPathsHash = options.expandedPathsHash, dimensionValue, hierarchyItem; if (dimension) { dimensionValue = dimension.selector(options.data); pathHash = pathHash !== undefined ? pathHash + "." + dimensionValue : dimensionValue + ""; hierarchyItem = addHierarchyItem(dimensionValue, children, pathHash, options.childrenHash); indexes.push(hierarchyItem.index); if (expandedPathsHash && expandedPathsHash[pathHash] || dimension.expanded) { if (!hierarchyItem.children) { hierarchyItem.children = []; } fillHierarchyItemIndexesCore(indexes, options, hierarchyItem.children, expandIndex + 1, pathHash); } } } function generateHierarchyItems(data, loadOptions, headers, headerName) { var result = [0], expandIndex = loadOptions.headerName === headerName ? loadOptions.path.length : 0, expandedPaths = headerName === "rows" ? loadOptions.rowExpandedPaths : loadOptions.columnExpandedPaths, options = { data: data, childrenHash: headers[headerName + "Hash"], dimensions: loadOptions[headerName], expandedPathsHash: loadOptions.headerName !== headerName && expandedPaths && expandedPaths.hash }; fillHierarchyItemIndexesCore(result, options, headers[headerName], expandIndex); return result; } function generateAggregationCells(data, cells, headers, options) { var cellSet = [], x, y, rowIndex, columnIndex; var rowIndexes = generateHierarchyItems(data, options, headers, "rows"); var columnIndexes = generateHierarchyItems(data, options, headers, "columns"); for (y = 0; y < rowIndexes.length; y++) { rowIndex = rowIndexes[y]; cells[rowIndex] = cells[rowIndex] || []; for (x = 0; x < columnIndexes.length; x++) { columnIndex = columnIndexes[x]; cellSet.push(cells[rowIndex][columnIndex] = cells[rowIndex][columnIndex] || []); } } return cellSet; } function fillHashExpandedPath(expandedPaths) { if (expandedPaths) { var hash = expandedPaths.hash = {}; expandedPaths.forEach(function (path) { var pathValue = path.map(function (value) { return value + ""; }).join("."); hash[pathValue] = true; }); } } function prepareLoadOption(options) { options.rows = options.rows || []; options.columns = options.columns || []; options.filters = options.filters || []; fillHashExpandedPath(options.columnExpandedPaths); fillHashExpandedPath(options.rowExpandedPaths); prepareFields(options.columns); prepareFields(options.rows); prepareFields(options.values); prepareFields(options.filters); } function getAggregator(field) { if (field.summaryType === "custom") { field.calculateCustomSummary = field.calculateCustomSummary || commonUtils.noop; return { seed: function seed() { var options = { summaryProcess: "start", totalValue: undefined }; field.calculateCustomSummary(options); return options; }, step: function step(options, value) { options.summaryProcess = "calculate"; options.value = value; field.calculateCustomSummary(options); return options; }, finalize: function finalize(options) { options.summaryProcess = "finalize"; delete options.value; field.calculateCustomSummary(options); return options.totalValue; } }; } return dataUtils.aggregators[field.summaryType] || dataUtils.aggregators.count; } function aggregationStep(measures, aggregationCells, data) { for (var aggregatorIndex = 0; aggregatorIndex < measures.length; aggregatorIndex++) { var cellField = measures[aggregatorIndex]; var cellValue = cellField.selector(data); var aggregator = getAggregator(cellField), isAggregatorSeedFunction = typeof aggregator.seed === "function"; for (var cellSetIndex = 0; cellSetIndex < aggregationCells.length; cellSetIndex++) { var cell = aggregationCells[cellSetIndex]; if (cell.length <= aggregatorIndex) { cell[aggregatorIndex] = isAggregatorSeedFunction ? aggregator.seed() : aggregator.seed; } if (cell[aggregatorIndex] === undefined) { cell[aggregatorIndex] = cellValue; } else if (typeUtils.isDefined(cellValue)) { cell[aggregatorIndex] = aggregator.step(cell[aggregatorIndex], cellValue); } } } } function aggregationFinalize(measures, cells) { each(measures, function (aggregatorIndex, cellField) { var aggregator = getAggregator(cellField); if (aggregator.finalize) { each(cells, function (_, row) { each(row, function (_, cell) { if (cell && cell[aggregatorIndex] !== undefined) { cell[aggregatorIndex] = aggregator.finalize(cell[aggregatorIndex]); } }); }); } }); } function areValuesEqual(filterValue, fieldValue) { var valueOfFilter = filterValue && filterValue.valueOf(), valueOfField = fieldValue && fieldValue.valueOf(); if (Array.isArray(filterValue)) { fieldValue = fieldValue || []; for (var i = 0; i < filterValue.length; i++) { valueOfFilter = filterValue[i] && filterValue[i].valueOf(); valueOfField = fieldValue[i] && fieldValue[i].valueOf(); if (valueOfFilter !== valueOfField) { return false; } } return true; } else { return valueOfFilter === valueOfField; } } function getGroupValue(levels, data) { var value = []; each(levels, function (_, field) { value.push(field.selector(data)); }); return value; } function createDimensionFilters(dimension) { var filters = []; each(dimension, function (_, field) { var filterValues = field.filterValues || [], groupName = field.groupName, filter; if (groupName && typeUtils.isNumeric(field.groupIndex)) { return; } filter = function filter(dataItem) { var value = field.levels ? getGroupValue(field.levels, dataItem) : field.selector(dataItem), result = false; for (var i = 0; i < filterValues.length; i++) { if (areValuesEqual(filterValues[i], value)) { result = true; break; } } return field.filterType === "exclude" ? !result : result; }; filterValues.length && filters.push(filter); }); return filters; } function createFilter(options) { var filters = createDimensionFilters(options.rows).concat(createDimensionFilters(options.columns)).concat(createDimensionFilters(options.filters)), expandedDimensions = options[options.headerName], path = options.path; if (expandedDimensions) { filters.push(function (dataItem) { var expandValue; for (var i = 0; i < path.length; i++) { expandValue = expandedDimensions[i].selector(dataItem); if (dataCoreUtils.toComparable(expandValue, true) !== dataCoreUtils.toComparable(path[i], true)) { return false; } } return true; }); } return function (dataItem) { for (var i = 0; i < filters.length; i++) { if (!filters[i](dataItem)) { return false; } } return true; }; } function loadCore(items, options, notifyProgress) { var headers = { columns: [], rows: [], columnsHash: { length: 1 }, rowsHash: { length: 1 } }, values = [], aggregationCells, filter, data, d = new Deferred(), i = 0; filter = createFilter(options); function processData() { var t = new Date(), startIndex = i; for (; i < items.length; i++) { if (i > startIndex && i % 10000 === 0) { if (new Date() - t >= 300) { notifyProgress(i / items.length); setTimeout(processData, 0); return; } } data = items[i]; if (filter(data)) { aggregationCells = generateAggregationCells(data, values, headers, options); aggregationStep(options.values, aggregationCells, data); } } aggregationFinalize(options.values, values); notifyProgress(1); d.resolve({ rows: headers.rows, columns: headers.columns, values: values, grandTotalRowIndex: 0, grandTotalColumnIndex: 0 }); } processData(); return d; } function filterDataSource(dataSource, fieldSelectors) { var filter = dataSource.filter(); if (dataSource.store() instanceof CustomStore && filter) { filter = processFilter(filter, fieldSelectors); return dataQuery(dataSource.items()).filter(filter).toArray(); } return dataSource.items(); } function loadDataSource(dataSource, fieldSelectors, reload) { var d = new Deferred(); var customizeStoreLoadOptionsHandler = function customizeStoreLoadOptionsHandler(options) { if (dataSource.store() instanceof ArrayStore) { options.storeLoadOptions.filter = processFilter(options.storeLoadOptions.filter, fieldSelectors); } }; dataSource.on("customizeStoreLoadOptions", customizeStoreLoadOptionsHandler); if (!dataSource.isLoaded() || reload) { var loadDeferred = reload ? dataSource.load() : dataSource.reload(); when(loadDeferred).done(function () { loadDataSource(dataSource, fieldSelectors).done(function () { d.resolve(filterDataSource(dataSource, fieldSelectors)); }).fail(d.reject); }).fail(d.reject); } else { d.resolve(filterDataSource(dataSource, fieldSelectors)); } return d.always(function () { dataSource.off("customizeStoreLoadOptions", customizeStoreLoadOptionsHandler); }); } function fillSelectorsByFields(selectors, fields) { fields.forEach(function (field) { if (field.dataField && field.dataType === "date") { var valueSelector = getDateValue(getDataSelector(field.dataField)); selectors[field.dataField] = function (data) { return valueSelector(data); }; } }); } function getFieldSelectors(options) { var selectors = {}; if (Array.isArray(options)) { fillSelectorsByFields(selectors, options); } else if (options) { ["rows", "columns", "filters"].forEach(function (area) { options[area] && fillSelectorsByFields(selectors, options[area]); }); } return selectors; } function processFilter(filter, fieldSelectors) { if (!Array.isArray(filter)) { return filter; } filter = filter.slice(0); if (typeUtils.isString(filter[0]) && (filter[1] instanceof Date || filter[2] instanceof Date)) { filter[0] = fieldSelectors[filter[0]]; } for (var i = 0; i < filter.length; i++) { filter[i] = processFilter(filter[i], fieldSelectors); } return filter; } return { ctor: function ctor(options) { this._progressChanged = options.onProgressChanged || commonUtils.noop; this._dataSource = new DataSourceModule.DataSource(options); this._dataSource.paginate(false); }, getFields: function getFields(fields) { var that = this, dataSource = that._dataSource, d = new Deferred(); loadDataSource(dataSource, getFieldSelectors(fields)).done(function (data) { d.resolve(pivotGridUtils.discoverObjectFields(data, fields)); }).fail(d.reject); return d; }, key: function key() { return this._dataSource.key(); }, load: function load(options) { var that = this, dataSource = that._dataSource, d = new Deferred(); prepareLoadOption(options); loadDataSource(dataSource, getFieldSelectors(options), options.reload).done(function (data) { when(loadCore(data, options, that._progressChanged)).done(d.resolve); }).fail(d.reject); return d; }, filter: function filter() { var dataSource = this._dataSource; return dataSource.filter.apply(dataSource, arguments); }, supportSorting: function supportSorting() { return false; }, getDrillDownItems: function getDrillDownItems(loadOptions, params) { loadOptions = loadOptions || {}; params = params || {}; prepareLoadOption(loadOptions); var drillDownItems = [], items = this._dataSource.items(), item, maxRowCount = params.maxRowCount, customColumns = params.customColumns, filter = createFilter(loadOptions), pathFilter = createFilter({ rows: getFiltersByPath(loadOptions.rows, params.rowPath), columns: getFiltersByPath(loadOptions.columns, params.columnPath), filters: [] }); for (var i = 0; i < items.length; i++) { if (pathFilter(items[i]) && filter(items[i])) { if (customColumns) { item = {}; for (var j = 0; j < customColumns.length; j++) { item[customColumns[j]] = items[i][customColumns[j]]; } } else { item = items[i]; } drillDownItems.push(item); } if (maxRowCount > 0 && drillDownItems.length === maxRowCount) { break; } } return drillDownItems; } }; }()).include(pivotGridUtils.storeDrillDownMixin);