devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
426 lines (373 loc) • 14.3 kB
JavaScript
var noop = require("../../core/utils/common").noop,
typeUtils = require("../../core/utils/type"),
extend = require("../../core/utils/extend").extend,
each = require("../../core/utils/iterator").each,
adjust = require("../../core/utils/math").adjust,
isDefined = typeUtils.isDefined,
isNumber = typeUtils.isNumeric,
isExponential = typeUtils.isExponential,
_math = Math,
_round = _math.round,
_sqrt = Math.sqrt;
var PI = Math.PI,
MAX_PIXEL_COUNT = 1E10,
PI_DIV_180 = PI / 180,
LN10 = Math.LN10;
var cosFunc = Math.cos,
sinFunc = Math.sin,
abs = Math.abs,
log = Math.log,
floor = Math.floor,
ceil = Math.ceil,
max = Math.max,
_isNaN = isNaN,
_Number = Number,
_NaN = NaN;
var getLog = function getLog(value, base) {
if (!value) {
return _NaN;
}
return Math.log(value) / Math.log(base);
};
var getAdjustedLog10 = function getAdjustedLog10(value) {
return adjust(getLog(value, 10));
};
var raiseTo = function raiseTo(power, base) {
return Math.pow(base, power);
};
// Translates angle to [0, 360)
// Expects number, no validation
var normalizeAngle = function normalizeAngle(angle) {
return (angle % 360 + 360) % 360;
};
// Maps angle in trigonometric space to angle in 'renderer' space
// Expects numbers, no validation
var convertAngleToRendererSpace = function convertAngleToRendererSpace(angle) {
return 90 - angle;
};
// Maps angle in degrees to angle in radians
// Expects number, no validation
var degreesToRadians = function degreesToRadians(value) {
return PI * value / 180;
};
// Calculates sin and cos for <angle> in degrees
// Expects number, no validation
var getCosAndSin = function getCosAndSin(angle) {
var angleInRadians = degreesToRadians(angle);
return { cos: cosFunc(angleInRadians), sin: sinFunc(angleInRadians) };
};
// Because Math.log(1000) / Math.LN10 < 3 though it is exactly 3
// Same happens for 1E6, 1E9, 1E12, 1E13, 1E15, ...
var DECIMAL_ORDER_THRESHOLD = 1E-14;
// ____________________
// / 2 2
// \/ (y2-y1) + (x2-x1)
var getDistance = function getDistance(x1, y1, x2, y2) {
var diffX = x2 - x1,
diffY = y2 - y1;
return Math.sqrt(diffY * diffY + diffX * diffX);
};
var getDecimalOrder = function getDecimalOrder(number) {
var n = abs(number),
cn;
if (!_isNaN(n)) {
if (n > 0) {
n = log(n) / LN10;
cn = ceil(n);
return cn - n < DECIMAL_ORDER_THRESHOLD ? cn : floor(n);
}
return 0;
}
return _NaN;
};
var getAppropriateFormat = function getAppropriateFormat(start, end, count) {
var order = max(getDecimalOrder(start), getDecimalOrder(end)),
precision = -getDecimalOrder(abs(end - start) / count),
format;
if (!_isNaN(order) && !_isNaN(precision)) {
if (abs(order) <= 4) {
format = 'fixedPoint';
precision < 0 && (precision = 0);
precision > 4 && (precision = 4);
} else {
format = 'exponential';
precision += order - 1;
precision > 3 && (precision = 3);
}
return { type: format, precision: precision };
}
return null;
};
var roundValue = function roundValue(value, precision) {
if (precision > 20) {
precision = 20;
}
if (isNumber(value)) {
if (isExponential(value)) {
return _Number(value.toExponential(precision));
} else {
return _Number(value.toFixed(precision));
}
}
};
var getPower = function getPower(value) {
return value.toExponential().split("e")[1];
};
function map(array, callback) {
var i = 0,
len = array.length,
result = [],
value;
while (i < len) {
value = callback(array[i], i);
if (value !== null) {
result.push(value);
}
i++;
}
return result;
}
function selectByKeys(object, keys) {
return map(keys, function (key) {
return object[key] ? object[key] : null;
});
}
function decreaseFields(object, keys, eachDecrease, decrease) {
var dec = decrease;
each(keys, function (_, key) {
if (object[key]) {
object[key] -= eachDecrease;
dec -= eachDecrease;
}
});
return dec;
}
function normalizeEnum(value) {
return String(value).toLowerCase();
}
function setCanvasValues(canvas) {
if (canvas) {
canvas.originalTop = canvas.top;
canvas.originalBottom = canvas.bottom;
canvas.originalLeft = canvas.left;
canvas.originalRight = canvas.right;
}
}
function normalizeBBoxField(value) {
return -MAX_PIXEL_COUNT < value && value < +MAX_PIXEL_COUNT ? value : 0;
}
function normalizeBBox(bBox) {
var xl = normalizeBBoxField(floor(bBox.x)),
yt = normalizeBBoxField(floor(bBox.y)),
xr = normalizeBBoxField(ceil(bBox.width + bBox.x)),
yb = normalizeBBoxField(ceil(bBox.height + bBox.y)),
result = {
x: xl,
y: yt,
width: xr - xl,
height: yb - yt
};
result.isEmpty = !result.x && !result.y && !result.width && !result.height;
return result;
}
// Angle is expected to be from right-handed cartesian (not svg) space - positive is counterclockwise
function rotateBBox(bBox, center, angle) {
var cos = _Number(cosFunc(angle * PI_DIV_180).toFixed(3)),
sin = _Number(sinFunc(angle * PI_DIV_180).toFixed(3)),
w2 = bBox.width / 2,
h2 = bBox.height / 2,
centerX = bBox.x + w2,
centerY = bBox.y + h2,
w2_ = abs(w2 * cos) + abs(h2 * sin),
h2_ = abs(w2 * sin) + abs(h2 * cos),
// Note that the following slightly differs from theoretical formula:
// x' = x * cos - y * sin, y' = x * sin + y * cos
// That is because in svg y goes down (not up) - so sign of sin is reverted
// x' = x * cos + y * sin, y' = -x * sin + y * cos
centerX_ = center[0] + (centerX - center[0]) * cos + (centerY - center[1]) * sin,
centerY_ = center[1] - (centerX - center[0]) * sin + (centerY - center[1]) * cos;
return normalizeBBox({
x: centerX_ - w2_,
y: centerY_ - h2_,
width: 2 * w2_,
height: 2 * h2_
});
}
extend(exports, {
decreaseGaps: function decreaseGaps(object, keys, decrease) {
var arrayGaps;
do {
arrayGaps = selectByKeys(object, keys);
arrayGaps.push(_math.ceil(decrease / arrayGaps.length));
decrease = decreaseFields(object, keys, _math.min.apply(null, arrayGaps), decrease);
} while (decrease > 0 && arrayGaps.length > 1);
return decrease;
},
normalizeEnum: normalizeEnum,
parseScalar: function parseScalar(value, defaultValue) {
return value !== undefined ? value : defaultValue;
},
enumParser: function enumParser(values) {
var stored = {},
i,
ii;
for (i = 0, ii = values.length; i < ii; ++i) {
stored[normalizeEnum(values[i])] = 1;
}
return function (value, defaultValue) {
var _value = normalizeEnum(value);
return stored[_value] ? _value : defaultValue;
};
},
patchFontOptions: function patchFontOptions(options) {
var fontOptions = {};
each(options || {}, function (key, value) {
if (/^(cursor|opacity)$/i.test(key)) {
// TODO check other properties, add tests
} else if (key === "color") {
key = "fill";
} else {
key = "font-" + key;
}
fontOptions[key] = value;
});
return fontOptions;
},
convertPolarToXY: function convertPolarToXY(centerCoords, startAngle, angle, radius) {
var shiftAngle = 90,
cosSin;
angle = isDefined(angle) ? angle + startAngle - shiftAngle : 0;
cosSin = getCosAndSin(angle);
return { x: _round(centerCoords.x + radius * cosSin.cos), y: _round(centerCoords.y + radius * cosSin.sin) };
},
convertXYToPolar: function convertXYToPolar(centerCoords, x, y) {
var radius = getDistance(centerCoords.x, centerCoords.y, x, y),
angle = _math.atan2(y - centerCoords.y, x - centerCoords.x);
return { phi: _round(normalizeAngle(angle * 180 / _math.PI)), r: _round(radius) };
},
processSeriesTemplate: function processSeriesTemplate(seriesTemplate, items) {
var customizeSeries = typeUtils.isFunction(seriesTemplate.customizeSeries) ? seriesTemplate.customizeSeries : noop,
nameField = seriesTemplate.nameField,
generatedSeries = {},
seriesOrder = [],
series,
i = 0,
length,
data;
items = items || [];
for (length = items.length; i < length; i++) {
data = items[i];
if (nameField in data) {
series = generatedSeries[data[nameField]];
if (!series) {
series = generatedSeries[data[nameField]] = { name: data[nameField] };
seriesOrder.push(series.name);
}
}
}
return map(seriesOrder, function (orderedName) {
var group = generatedSeries[orderedName];
return extend(group, customizeSeries.call(null, group.name));
});
},
getCategoriesInfo: function getCategoriesInfo(categories, startValue, endValue) {
if (categories.length === 0) {
return { categories: [] };
}
startValue = isDefined(startValue) ? startValue : categories[0];
endValue = isDefined(endValue) ? endValue : categories[categories.length - 1];
var categoriesValue = map(categories, function (category) {
return isDefined(category) ? category.valueOf() : null;
}),
visibleCategories,
indexStartValue = categoriesValue.indexOf(startValue.valueOf()),
indexEndValue = categoriesValue.indexOf(endValue.valueOf()),
swapBuf,
inverted = false,
lastIdx;
indexStartValue < 0 && (indexStartValue = 0);
indexEndValue < 0 && (indexEndValue = categories.length - 1);
if (indexEndValue < indexStartValue) {
swapBuf = indexEndValue;
indexEndValue = indexStartValue;
indexStartValue = swapBuf;
inverted = true;
}
visibleCategories = categories.slice(indexStartValue, indexEndValue + 1);
lastIdx = visibleCategories.length - 1;
return {
categories: visibleCategories,
start: visibleCategories[inverted ? lastIdx : 0],
end: visibleCategories[inverted ? 0 : lastIdx],
inverted: inverted
};
},
setCanvasValues: setCanvasValues,
updatePanesCanvases: function updatePanesCanvases(panes, canvas, rotated) {
var weightSum = 0;
each(panes, function (_, pane) {
pane.weight = pane.weight || 1;
weightSum += pane.weight;
});
var distributedSpace = 0,
padding = panes.padding || 10,
paneSpace = rotated ? canvas.width - canvas.left - canvas.right : canvas.height - canvas.top - canvas.bottom,
oneWeight = (paneSpace - padding * (panes.length - 1)) / weightSum,
startName = rotated ? "left" : "top",
endName = rotated ? "right" : "bottom";
each(panes, function (_, pane) {
var calcLength = _round(pane.weight * oneWeight);
pane.canvas = pane.canvas || {};
extend(pane.canvas, canvas);
pane.canvas[startName] = canvas[startName] + distributedSpace;
pane.canvas[endName] = canvas[endName] + (paneSpace - calcLength - distributedSpace);
distributedSpace = distributedSpace + calcLength + padding;
setCanvasValues(pane.canvas);
});
},
unique: function unique(array) {
var values = {};
return map(array, function (item) {
var result = !values[item] ? item : null;
values[item] = true;
return result;
});
},
map: map,
getVerticallyShiftedAngularCoords: function getVerticallyShiftedAngularCoords(bBox, dy, center) {
// TODO: Use center instead of left top corner - that is more correct and allows to get rid of "isPositive"
// horizontalOffset1 = bBox.x + bBox.width / 2 - center.x
// horizontalOffset2 = bBox.y + bBox.height / 2 - center.y
// verticalOffset2 = newCoord.y + bBox.height / 2 - center.y
var isPositive = bBox.x + bBox.width / 2 >= center.x,
horizontalOffset1 = (isPositive ? bBox.x : bBox.x + bBox.width) - center.x,
verticalOffset1 = bBox.y - center.y,
verticalOffset2 = verticalOffset1 + dy,
horizontalOffset2 = _round(_sqrt(horizontalOffset1 * horizontalOffset1 + verticalOffset1 * verticalOffset1 - verticalOffset2 * verticalOffset2)),
dx = (isPositive ? +horizontalOffset2 : -horizontalOffset2) || horizontalOffset1;
return { x: center.x + (isPositive ? dx : dx - bBox.width), y: bBox.y + dy };
},
mergeMarginOptions: function mergeMarginOptions(opt1, opt2) {
return {
checkInterval: opt1.checkInterval || opt2.checkInterval,
size: Math.max(opt1.size || 0, opt2.size || 0),
percentStick: opt1.percentStick || opt2.percentStick,
sizePointNormalState: Math.max(opt1.sizePointNormalState || 0, opt2.sizePointNormalState || 0)
};
}
});
exports.getLog = getLog;
exports.getAdjustedLog10 = getAdjustedLog10;
exports.raiseTo = raiseTo;
exports.normalizeAngle = normalizeAngle;
exports.convertAngleToRendererSpace = convertAngleToRendererSpace;
exports.degreesToRadians = degreesToRadians;
exports.getCosAndSin = getCosAndSin;
exports.getDecimalOrder = getDecimalOrder;
exports.getAppropriateFormat = getAppropriateFormat;
exports.getDistance = getDistance;
exports.roundValue = roundValue;
exports.getPower = getPower;
exports.rotateBBox = rotateBBox;
exports.normalizeBBox = normalizeBBox;
;