UNPKG

devextreme

Version:

HTML5 JavaScript Component Suite for Responsive Web Development

480 lines (446 loc) • 17.6 kB
/** * DevExtreme (viz/components/data_validator.js) * Version: 18.1.3 * Build date: Tue May 15 2018 * * Copyright (c) 2012 - 2018 Developer Express Inc. ALL RIGHTS RESERVED * Read about DevExtreme licensing here: https://js.devexpress.com/Licensing/ */ "use strict"; var typeUtils = require("../../core/utils/type"), STRING = "string", NUMERIC = "numeric", DATETIME = "datetime", DISCRETE = "discrete", SEMIDISCRETE = "semidiscrete", CONTINUOUS = "continuous", LOGARITHMIC = "logarithmic", VALUE_TYPE = "valueType", ARGUMENT_TYPE = "argumentType", extend = require("../../core/utils/extend").extend, axisTypeParser = require("../core/utils").enumParser([STRING, NUMERIC, DATETIME]), _getParser = require("./parse_utils").getParser, _isDefined = typeUtils.isDefined, _isFunction = typeUtils.isFunction, _isArray = Array.isArray, _isString = typeUtils.isString, _isDate = typeUtils.isDate, _isNumber = typeUtils.isNumeric, _isObject = typeUtils.isObject; function groupingValues(data, others, valueField, index) { if (index >= 0) { data.slice(index).forEach(function(cell) { if (_isDefined(cell[valueField])) { others[valueField] += cell[valueField]; cell[valueField] = void 0 } }) } } function processGroups(groups) { groups.forEach(function(group) { group.valueType = group.valueAxisType = null; group.series.forEach(function(series) { series.updateDataType({}) }); group.valueAxis && group.valueAxis.resetTypes(VALUE_TYPE) }) } function sortValues(data, asc, selector) { var func = asc ? function(a, b) { return a - b } : function(a, b) { return b - a }; data.sort(function(a, b) { var valA = selector(a), valB = selector(b), aa = _isDefined(valA) ? 1 : 0, bb = _isDefined(valB) ? 1 : 0; return aa && bb ? func(valA, valB) : func(aa, bb) }); return data } function resetArgumentAxes(axes) { axes && axes.forEach(function(axis) { axis.resetTypes(ARGUMENT_TYPE) }) } function parseCategories(categories, parser) { var newArray = []; categories.forEach(function(category) { var parsedCategory = parser(category); void 0 !== parsedCategory && newArray.push(parsedCategory) }); return newArray } function parseAxisCategories(groupsData, parsers) { var argumentCategories = groupsData.argumentOptions && groupsData.argumentOptions.categories, valueParser = parsers[1]; groupsData.groups.forEach(function(valueGroup) { var categories = valueGroup.valueOptions && valueGroup.valueOptions.categories; if (categories) { valueGroup.valueOptions.categories = parseCategories(categories, valueParser) } }); if (argumentCategories) { groupsData.argumentOptions.categories = parseCategories(argumentCategories, parsers[0]) } } function filterForLogAxis(val, field, incidentOccurred) { if (val <= 0 && null !== val) { incidentOccurred("E2004", [field]); val = null } return val } function eigen(x) { return x } function getType(unit, type) { var result = type; if (type === STRING || _isString(unit)) { result = STRING } else { if (type === DATETIME || _isDate(unit)) { result = DATETIME } else { if (_isNumber(unit)) { result = NUMERIC } } } return result } function correctAxisType(type, axisType, hasCategories, incidentOccurred) { if (type === STRING && (axisType === CONTINUOUS || axisType === LOGARITHMIC || axisType === SEMIDISCRETE)) { incidentOccurred("E2002") } return axisType === LOGARITHMIC ? LOGARITHMIC : hasCategories || axisType === DISCRETE || type === STRING ? DISCRETE : axisType === SEMIDISCRETE ? SEMIDISCRETE : CONTINUOUS } function validUnit(unit, field, incidentOccurred) { if (unit) { incidentOccurred(!_isNumber(unit) && !_isDate(unit) && !_isString(unit) ? "E2003" : "E2004", [field]) } } function createParserUnit(type, axisType, ignoreEmptyPoints, skipFields, incidentOccurred) { var parser = type ? _getParser(type) : eigen, filter = axisType === LOGARITHMIC ? filterForLogAxis : eigen, filterInfinity = axisType !== DISCRETE ? function(x) { return isFinite(x) || void 0 === x ? x : null } : eigen, filterNulls = ignoreEmptyPoints ? function(x) { return null === x ? void 0 : x } : eigen; return function(unit, field) { var filterLogValues = function(x) { return filter(x, field, incidentOccurred) }, parseUnit = filterNulls(filterLogValues(filterInfinity(parser(unit)))); if (void 0 === parseUnit) { skipFields[field] = (skipFields[field] || 0) + 1; validUnit(unit, field, incidentOccurred) } return parseUnit } } function prepareParsers(groupsData, skipFields, incidentOccurred) { var sizeParser, valueParser, ignoreEmptyPoints, argumentParser = createParserUnit(groupsData.argumentType, groupsData.argumentAxisType, false, skipFields, incidentOccurred), categoryParsers = [argumentParser], cache = {}, list = []; groupsData.groups.forEach(function(group) { group.series.forEach(function(series) { ignoreEmptyPoints = series.getOptions().ignoreEmptyPoints; valueParser = createParserUnit(group.valueType, group.valueAxisType, ignoreEmptyPoints, skipFields, incidentOccurred); sizeParser = createParserUnit(NUMERIC, CONTINUOUS, ignoreEmptyPoints, skipFields, incidentOccurred); cache[series.getArgumentField()] = argumentParser; series.getValueFields().forEach(function(field) { !categoryParsers[1] && (categoryParsers[1] = valueParser); cache[field] = valueParser }); if (series.getSizeField()) { cache[series.getSizeField()] = sizeParser } }) }); for (var field in cache) { list.push([field, cache[field]]) } list.length && parseAxisCategories(groupsData, categoryParsers); return list } function getParsedCell(cell, parsers) { var i, field, value, ii = parsers.length, obj = extend({}, cell); for (i = 0; i < ii; ++i) { field = parsers[i][0]; value = cell[field]; obj[field] = parsers[i][1](value, field) } return obj } function parse(data, parsers) { var i, parsedData = [], ii = data.length; parsedData.length = ii; for (i = 0; i < ii; ++i) { parsedData[i] = getParsedCell(data[i], parsers) } return parsedData } function findIndexByThreshold(data, valueField, threshold) { var i, value, ii = data.length; for (i = 0; i < ii; ++i) { value = data[i][valueField]; if (_isDefined(value) && threshold > value) { break } } return i } function groupMinSlices(originalData, argumentField, valueField, smallValuesGrouping) { smallValuesGrouping = smallValuesGrouping || {}; var data, mode = smallValuesGrouping.mode, others = {}; if (!mode || "none" === mode) { return } others[argumentField] = String(smallValuesGrouping.groupName || "others"); others[valueField] = 0; data = sortValues(originalData.slice(), false, function(a) { return a[valueField] }); groupingValues(data, others, valueField, "smallValueThreshold" === mode ? findIndexByThreshold(data, valueField, smallValuesGrouping.threshold) : smallValuesGrouping.topCount); others[valueField] && originalData.push(others) } function groupPieData(data, groupsData) { var firstSeries = groupsData.groups[0] && groupsData.groups[0].series[0], isPie = firstSeries && ("pie" === firstSeries.type || "doughnut" === firstSeries.type || "donut" === firstSeries.type); if (!isPie) { return } groupsData.groups.forEach(function(group) { group.series.forEach(function(series) { groupMinSlices(data, series.getArgumentField(), series.getValueFields()[0], series.getOptions().smallValuesGrouping) }) }) } function addUniqueItemToCollection(item, collection, itemsHash) { if (!itemsHash[item]) { collection.push(item); itemsHash[item] = true } } function getUniqueArgumentFields(groupsData) { var uniqueArgumentFields = [], hash = {}; groupsData.groups.forEach(function(group) { group.series.forEach(function(series) { addUniqueItemToCollection(series.getArgumentField(), uniqueArgumentFields, hash) }) }); return uniqueArgumentFields } function sort(a, b) { var result = a - b; if (isNaN(result)) { if (!_isDefined(a)) { return 1 } if (!_isDefined(b)) { return -1 } return 0 } return result } function sortByArgument(data, argumentField) { return data.slice().sort(function(a, b) { return sort(a[argumentField], b[argumentField]) }) } function sortByCallback(data, callback) { return data.slice().sort(callback) } function checkValueTypeOfGroup(group, cell) { group.series.forEach(function(series) { series.getValueFields().forEach(function(field) { group.valueType = getType(cell[field], group.valueType) }) }); return group.valueType } function getSortByCategories(categories) { var hash = {}; categories.forEach(function(value, i) { hash[value] = i }); return function(data, argumentField) { return sortValues(data.slice(), true, function(a) { return hash[a[argumentField]] }) } } function sortData(data, groupsData, options, uniqueArgumentFields) { var reSortCategories, dataByArguments = {}, isDiscrete = groupsData.argumentAxisType === DISCRETE, userCategories = isDiscrete && groupsData.argumentOptions && groupsData.argumentOptions.categories, sortFunction = function(data) { return data }, sortingMethodOption = options.sortingMethod; if (!userCategories && _isFunction(sortingMethodOption)) { data = sortByCallback(data, sortingMethodOption) } if (isDiscrete) { groupsData.categories = getCategories(data, uniqueArgumentFields, userCategories) } if (userCategories || !_isFunction(sortingMethodOption) && groupsData.argumentType === STRING && !options._skipArgumentSorting) { sortFunction = getSortByCategories(groupsData.categories) } else { if (true === sortingMethodOption && groupsData.argumentType !== STRING) { sortFunction = sortByArgument; reSortCategories = isDiscrete } } uniqueArgumentFields.forEach(function(field) { dataByArguments[field] = sortFunction(data, field) }); if (reSortCategories) { groupsData.categories = groupsData.categories.sort(sort) } return dataByArguments } function checkItemExistence(collection, item) { return collection.map(function(collectionItem) { return collectionItem.valueOf() }).indexOf(item.valueOf()) === -1 } function getCategories(data, uniqueArgumentFields, userCategories) { var categories = userCategories ? userCategories.slice() : []; uniqueArgumentFields.forEach(function(field) { data.forEach(function(item) { var dataItem = item[field]; _isDefined(dataItem) && checkItemExistence(categories, dataItem) && categories.push(dataItem) }) }); return categories } function checkArgumentTypeOfGroup(series, cell, groupsData) { series.forEach(function(currentSeries) { groupsData.argumentType = getType(cell[currentSeries.getArgumentField()], groupsData.argumentType) }); return groupsData.argumentType } function checkType(data, groupsData, checkTypeForAllData) { var groupsIndexes, groupsWithUndefinedValueType = [], groupsWithUndefinedArgumentType = [], argumentTypeGroup = groupsData.argumentOptions && axisTypeParser(groupsData.argumentOptions.argumentType); groupsData.groups.forEach(function(group) { if (!group.series.length) { return } var valueTypeGroup = group.valueOptions && axisTypeParser(group.valueOptions.valueType); group.valueType = valueTypeGroup; groupsData.argumentType = argumentTypeGroup; !valueTypeGroup && groupsWithUndefinedValueType.push(group); !argumentTypeGroup && groupsWithUndefinedArgumentType.push(group) }); if (groupsWithUndefinedValueType.length || groupsWithUndefinedArgumentType.length) { groupsIndexes = groupsWithUndefinedValueType.map(function(_, index) { return index }); data.some(function(cell) { var defineArg; groupsWithUndefinedValueType.forEach(function(group, groupIndex) { if (checkValueTypeOfGroup(group, cell) && groupsIndexes.indexOf(groupIndex) >= 0) { groupsIndexes.splice(groupIndex, 1) } }); if (!defineArg) { groupsWithUndefinedArgumentType.forEach(function(group) { defineArg = checkArgumentTypeOfGroup(group.series, cell, groupsData) }) } if (!checkTypeForAllData && defineArg && 0 === groupsIndexes.length) { return true } }) } } function checkAxisType(groupsData, incidentOccurred) { var argumentOptions = groupsData.argumentOptions || {}, userArgumentCategories = argumentOptions && argumentOptions.categories || [], argumentAxisType = correctAxisType(groupsData.argumentType, argumentOptions.type, !!userArgumentCategories.length, incidentOccurred); groupsData.groups.forEach(function(group) { var valueOptions = group.valueOptions || {}, valueCategories = valueOptions.categories || [], valueAxisType = correctAxisType(group.valueType, valueOptions.type, !!valueCategories.length, incidentOccurred); group.series.forEach(function(series) { var optionsSeries = {}; optionsSeries.argumentAxisType = argumentAxisType; optionsSeries.valueAxisType = valueAxisType; groupsData.argumentAxisType = groupsData.argumentAxisType || optionsSeries.argumentAxisType; group.valueAxisType = group.valueAxisType || optionsSeries.valueAxisType; optionsSeries.argumentType = groupsData.argumentType; optionsSeries.valueType = group.valueType; optionsSeries.showZero = valueOptions.showZero; series.updateDataType(optionsSeries) }); group.valueAxisType = group.valueAxisType || valueAxisType; if (group.valueAxis) { group.valueAxis.setTypes(group.valueAxisType, group.valueType, VALUE_TYPE); group.valueAxis.validate() } }); groupsData.argumentAxisType = groupsData.argumentAxisType || argumentAxisType; if (groupsData.argumentAxes) { groupsData.argumentAxes.forEach(function(axis) { axis.setTypes(groupsData.argumentAxisType, groupsData.argumentType, ARGUMENT_TYPE); axis.validate() }) } } function verifyData(source, incidentOccurred) { var i, ii, k, item, data = [], sourceIsDefined = _isDefined(source), hasError = sourceIsDefined && !_isArray(source); if (sourceIsDefined && !hasError) { for (i = 0, ii = source.length, k = 0; i < ii; ++i) { item = source[i]; if (_isObject(item)) { data[k++] = item } else { if (item) { hasError = true } } } } if (hasError) { incidentOccurred("E2001") } return data } function validateData(data, groupsData, incidentOccurred, options) { var dataByArgumentFields, field, skipFields = {}; data = verifyData(data, incidentOccurred); groupsData.argumentType = groupsData.argumentAxisType = null; processGroups(groupsData.groups); resetArgumentAxes(groupsData.argumentAxes); checkType(data, groupsData, options.checkTypeForAllData); checkAxisType(groupsData, incidentOccurred); if (options.convertToAxisDataType) { data = parse(data, prepareParsers(groupsData, skipFields, incidentOccurred)) } groupPieData(data, groupsData); dataByArgumentFields = sortData(data, groupsData, options, getUniqueArgumentFields(groupsData)); for (field in skipFields) { if (skipFields[field] === data.length) { incidentOccurred("W2002", [field]) } } return dataByArgumentFields } exports.validateData = validateData;