devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
1,202 lines (1,034 loc) • 47.8 kB
JavaScript
"use strict";
var formatHelper = require("../../format_helper"),
dateUtils = require("../../core/utils/date"),
extend = require("../../core/utils/extend").extend,
generateDateBreaks = require("./datetime_breaks").generateDateBreaks,
getNextDateUnit = dateUtils.getNextDateUnit,
correctDateWithUnitBeginning = dateUtils.correctDateWithUnitBeginning,
noop = require("../../core/utils/common").noop,
vizUtils = require("../core/utils"),
isDefined = require("../../core/utils/type").isDefined,
constants = require("./axes_constants"),
_extend = extend,
_math = Math,
_max = _math.max,
TOP = constants.top,
BOTTOM = constants.bottom,
LEFT = constants.left,
RIGHT = constants.right,
CENTER = constants.center,
SCALE_BREAK_OFFSET = 3,
RANGE_RATIO = 0.3,
WAVED_LINE_CENTER = 2,
WAVED_LINE_TOP = 0,
WAVED_LINE_BOTTOM = 4,
WAVED_LINE_LENGTH = 24;
function prepareDatesDifferences(datesDifferences, tickInterval) {
var dateUnitInterval, i;
if (tickInterval === "week") {
tickInterval = "day";
}
if (tickInterval === "quarter") {
tickInterval = "month";
}
if (datesDifferences[tickInterval]) {
for (i = 0; i < dateUtils.dateUnitIntervals.length; i++) {
dateUnitInterval = dateUtils.dateUnitIntervals[i];
if (datesDifferences[dateUnitInterval]) {
datesDifferences[dateUnitInterval] = false;
datesDifferences.count--;
}
if (dateUnitInterval === tickInterval) {
break;
}
}
}
}
function sortingBreaks(breaks) {
return breaks.sort(function (a, b) {
return a.from - b.from;
});
}
function filterBreaks(breaks, viewport, breakStyle) {
var minVisible = viewport.minVisible,
maxVisible = viewport.maxVisible,
breakSize = breakStyle ? breakStyle.width : 0;
return breaks.reduce(function (result, currentBreak) {
var from = currentBreak.from,
to = currentBreak.to,
lastResult = result[result.length - 1],
newBreak;
if (!isDefined(from) || !isDefined(to)) {
return result;
}
if (from > to) {
to = [from, from = to][0];
}
if (result.length && from < lastResult.to) {
if (to > lastResult.to) {
lastResult.to = to > maxVisible ? maxVisible : to;
if (lastResult.gapSize) {
lastResult.gapSize = undefined;
lastResult.cumulativeWidth += breakSize;
}
}
} else {
if ((from >= minVisible && from < maxVisible || to <= maxVisible && to > minVisible) && to - from < maxVisible - minVisible) {
from = from >= minVisible ? from : minVisible;
to = to <= maxVisible ? to : maxVisible;
newBreak = {
from: from,
to: to,
cumulativeWidth: (lastResult ? lastResult.cumulativeWidth : 0) + breakSize
};
if (currentBreak.gapSize) {
newBreak.gapSize = dateUtils.convertMillisecondsToDateUnits(to - from);
newBreak.cumulativeWidth = lastResult ? lastResult.cumulativeWidth : 0;
}
result.push(newBreak);
}
}
return result;
}, []);
}
function getMarkerDates(min, max, markerInterval) {
var origMin = min,
dates;
min = correctDateWithUnitBeginning(min, markerInterval);
max = correctDateWithUnitBeginning(max, markerInterval);
dates = dateUtils.getSequenceByInterval(min, max, markerInterval);
if (dates.length && origMin > dates[0]) {
dates = dates.slice(1);
}
return dates;
}
function getStripHorizontalAlignmentPosition(alignment) {
var position = "start";
if (alignment === "center") {
position = "center";
}
if (alignment === "right") {
position = "end";
}
return position;
}
function getStripVerticalAlignmentPosition(alignment) {
var position = "start";
if (alignment === "center") {
position = "center";
}
if (alignment === "bottom") {
position = "end";
}
return position;
}
function getMarkerInterval(tickInterval) {
var markerInterval = getNextDateUnit(tickInterval);
if (markerInterval === "quarter") {
markerInterval = getNextDateUnit(markerInterval);
}
return markerInterval;
}
function getMarkerFormat(curDate, prevDate, tickInterval, markerInterval) {
var format = markerInterval,
datesDifferences = prevDate && dateUtils.getDatesDifferences(prevDate, curDate);
if (prevDate && tickInterval !== "year") {
prepareDatesDifferences(datesDifferences, tickInterval);
format = formatHelper.getDateFormatByDifferences(datesDifferences);
}
return format;
}
function getMaxSide(act, boxes) {
return boxes.reduce(function (prevValue, box) {
return _max(prevValue, act(box));
}, 0);
}
function getDistanceByAngle(bBox, rotationAngle) {
rotationAngle = _math.abs(rotationAngle);
rotationAngle = rotationAngle % 180 >= 90 ? 90 - rotationAngle % 90 : rotationAngle % 90;
var a = rotationAngle * (_math.PI / 180);
if (a >= _math.atan(bBox.height / bBox.width)) {
return bBox.height / _math.abs(_math.sin(a));
} else {
return bBox.width;
}
}
function getMaxConstantLinePadding(constantLines) {
return constantLines.reduce(function (padding, options) {
return _max(padding, options.paddingTopBottom);
}, 0);
}
function getConstantLineLabelMarginForVerticalAlignment(constantLines, alignment, labelHeight) {
return constantLines.some(function (options) {
return options.label.verticalAlignment === alignment;
}) && labelHeight || 0;
}
function getLeftMargin(bBox) {
return _math.abs(bBox.x) || 0;
}
function getRightMargin(bBox) {
return _math.abs(bBox.width - _math.abs(bBox.x)) || 0;
}
function generateRangesOnPoints(points, edgePoints, getRange) {
var i,
length,
maxRange = null,
ranges = [],
curValue,
prevValue,
curRange;
for (i = 1, length = points.length; i < length; i++) {
curValue = points[i];
prevValue = points[i - 1];
curRange = getRange(curValue, prevValue);
if (edgePoints.indexOf(curValue) >= 0) {
if (!maxRange || curRange > maxRange.length) {
maxRange = { start: curValue, end: prevValue, length: curRange };
}
} else {
if (maxRange && curRange < maxRange.length) {
ranges.push(maxRange);
} else {
ranges.push({ start: curValue, end: prevValue, length: curRange });
}
maxRange = null;
}
}
if (maxRange) {
ranges.push(maxRange);
}
return ranges;
}
function generateAutoBreaks(options, series, viewport) {
var ranges,
breaks = [],
i,
getRange = options.type === "logarithmic" ? function (min, max) {
return vizUtils.getLog(max / min, options.logarithmBase);
} : function (min, max) {
return max - min;
},
visibleRange = getRange(viewport.minVisible, viewport.maxVisible),
maxAutoBreakCount,
points = series.reduce(function (result, s) {
var points = s.getPointsInViewPort();
result[0] = result[0].concat(points[0]);
result[1] = result[1].concat(points[1]);
return result;
}, [[], []]),
sortedAllPoints = points[0].concat(points[1]).sort(function (a, b) {
return b - a;
}),
edgePoints = points[1].filter(function (p) {
return points[0].indexOf(p) < 0;
}),
epsilon = visibleRange / 1E10,
minDiff = RANGE_RATIO * visibleRange;
ranges = generateRangesOnPoints(sortedAllPoints, edgePoints, getRange).sort(function (a, b) {
return b.length - a.length;
});
maxAutoBreakCount = isDefined(options.maxAutoBreakCount) ? Math.min(options.maxAutoBreakCount, ranges.length) : ranges.length;
for (i = 0; i < maxAutoBreakCount; i++) {
if (ranges[i].length >= minDiff) {
if (visibleRange <= ranges[i].length) {
break;
}
visibleRange -= ranges[i].length;
if (visibleRange > epsilon || visibleRange < -epsilon) {
breaks.push({ from: ranges[i].start, to: ranges[i].end });
minDiff = RANGE_RATIO * visibleRange;
}
} else {
break;
}
}
sortingBreaks(breaks);
return breaks;
}
module.exports = {
linear: {
_getStep: function _getStep(boxes, rotationAngle) {
var spacing = this._options.label.minSpacing,
func = this._isHorizontal ? function (box) {
return box.width + spacing;
} : function (box) {
return box.height;
},
maxLabelLength = getMaxSide(func, boxes);
if (rotationAngle) {
maxLabelLength = getDistanceByAngle({ width: maxLabelLength, height: this._getMaxLabelHeight(boxes, 0) }, rotationAngle);
}
return constants.getTicksCountInRange(this._majorTicks, this._isHorizontal ? "x" : "y", maxLabelLength);
},
_getMaxLabelHeight: function _getMaxLabelHeight(boxes, spacing) {
return getMaxSide(function (box) {
return box.height;
}, boxes) + spacing;
},
_validateOverlappingMode: function _validateOverlappingMode(mode, displayMode) {
if (this._isHorizontal && (displayMode === "rotate" || displayMode === "stagger") || !this._isHorizontal) {
return constants.validateOverlappingMode(mode);
}
return mode;
},
_validateDisplayMode: function _validateDisplayMode(mode) {
return this._isHorizontal ? mode : "standard";
},
getMarkerTrackers: function getMarkerTrackers() {
return this._markerTrackers;
},
_getSharpParam: function _getSharpParam(opposite) {
return this._isHorizontal ^ opposite ? "h" : "v";
},
_createAxisElement: function _createAxisElement() {
return this._renderer.path([], "line");
},
_updateAxisElementPosition: function _updateAxisElementPosition() {
if (!this._axisElement) {
return;
}
var axisCoord = this._axisPosition,
canvas = this._getCanvasStartEnd();
this._axisElement.attr({
points: this._isHorizontal ? [canvas.start, axisCoord, canvas.end, axisCoord] : [axisCoord, canvas.start, axisCoord, canvas.end]
});
},
_getTranslatedCoord: function _getTranslatedCoord(value, offset) {
return this._translator.translate(value, offset);
},
_initAxisPositions: function _initAxisPositions() {
var that = this,
position = that._options.position;
that._axisPosition = that._orthogonalPositions[position === "top" || position === "left" ? "start" : "end"];
},
_getTickMarkPoints: function _getTickMarkPoints(tick, length) {
var coords = tick.coords,
isHorizontal = this._isHorizontal,
tickCorrection = {
left: -1,
top: -1,
right: 0,
bottom: 0,
center: -0.5
}[this._options.tickOrientation || "center"];
return [coords.x + (isHorizontal ? 0 : tickCorrection * length), coords.y + (isHorizontal ? tickCorrection * length : 0), coords.x + (isHorizontal ? 0 : tickCorrection * length + length), coords.y + (isHorizontal ? tickCorrection * length + length : 0)];
},
_getTitleCoords: function _getTitleCoords() {
var that = this,
x = that._axisPosition,
y = that._axisPosition,
canvas = that._getCanvasStartEnd(),
center = canvas.start + (canvas.end - canvas.start) / 2;
if (that._isHorizontal) {
x = center;
} else {
y = center;
}
return { x: x, y: y };
},
_drawTitleText: function _drawTitleText(group, coords) {
var options = this._options,
titleOptions = options.title,
attrs = { opacity: titleOptions.opacity, align: "center" };
if (!titleOptions.text || !group) {
return;
}
coords = coords || this._getTitleCoords();
if (!this._isHorizontal) {
attrs.rotate = options.position === LEFT ? 270 : 90;
}
var text = this._renderer.text(titleOptions.text, coords.x, coords.y).css(vizUtils.patchFontOptions(titleOptions.font)).attr(attrs).append(group);
return text;
},
_updateTitleCoords: function _updateTitleCoords() {
this._title && this._title.element.attr(this._getTitleCoords());
},
_drawTitle: function _drawTitle() {
var title = this._drawTitleText(this._axisTitleGroup);
if (title) {
this._title = {
element: title
};
}
},
_measureTitle: function _measureTitle() {
if (this._title) {
this._title.bBox = this._title.element.getBBox();
}
},
_drawDateMarker: function _drawDateMarker(date, options, range) {
var that = this,
markerOptions = that._options.marker,
invert = that._translator.getBusinessRange().invert,
textIndent = markerOptions.width + markerOptions.textLeftIndent,
text,
pathElement;
if (options.x === null) return;
if (!options.withoutStick) {
pathElement = that._renderer.path([options.x, options.y, options.x, options.y + markerOptions.separatorHeight], "line").attr({ "stroke-width": markerOptions.width, stroke: markerOptions.color, "stroke-opacity": markerOptions.opacity, sharp: "h" }).append(that._axisElementsGroup);
}
text = String(that.formatLabel(date, options.labelOptions, range));
return {
date: date,
x: options.x,
y: options.y,
cropped: options.withoutStick,
label: that._renderer.text(text, options.x, options.y).css(vizUtils.patchFontOptions(markerOptions.label.font)).append(that._axisElementsGroup),
line: pathElement,
getEnd: function getEnd() {
return this.x + (invert ? -1 : 1) * (textIndent + this.labelBBox.width);
},
setTitle: function setTitle() {
this.title = text;
},
hideLabel: function hideLabel() {
this.label.dispose();
this.label = null;
this.title = text;
},
hide: function hide() {
if (pathElement) {
pathElement.dispose();
pathElement = null;
}
this.label.dispose();
this.label = null;
this.hidden = true;
}
};
},
_drawDateMarkers: function _drawDateMarkers() {
var that = this,
options = that._options,
translator = that._translator,
viewport = that._getViewportRange(),
minBound = viewport.minVisible,
tickInterval,
markerInterval,
markerDates,
dateMarkers = [],
markersAreaTop,
dateMarker;
function draw(markerDate, format, withoutStick) {
return that._drawDateMarker(markerDate, {
x: translator.translate(markerDate),
y: markersAreaTop,
labelOptions: that._getLabelFormatOptions(format),
withoutStick: withoutStick
}, viewport);
}
if (!options.marker.visible || options.argumentType !== "datetime" || options.type === "discrete" || that._majorTicks.length <= 1) {
return [];
}
markersAreaTop = that._axisPosition + options.marker.topIndent;
tickInterval = dateUtils.getDateUnitInterval(this._tickInterval);
markerInterval = getMarkerInterval(tickInterval);
markerDates = getMarkerDates(minBound, viewport.maxVisible, markerInterval);
if (markerDates.length > 1 || markerDates.length === 1 && minBound < markerDates[0]) {
dateMarkers = markerDates.reduce(function (markers, curDate, i, dates) {
var marker = draw(curDate, getMarkerFormat(curDate, dates[i - 1] || minBound < curDate && minBound, tickInterval, markerInterval));
marker && markers.push(marker);
return markers;
}, []);
if (minBound < markerDates[0]) {
dateMarker = draw(minBound, getMarkerFormat(minBound, markerDates[0], tickInterval, markerInterval), true);
dateMarker && dateMarkers.unshift(dateMarker);
}
}
return dateMarkers;
},
_adjustDateMarkers: function _adjustDateMarkers(offset) {
offset = offset || 0;
var that = this,
markerOptions = this._options.marker,
textIndent = markerOptions.width + markerOptions.textLeftIndent,
invert = this._translator.getBusinessRange().invert,
canvas = that._getCanvasStartEnd(),
dateMarkers = this._dateMarkers;
if (!dateMarkers.length) {
return offset;
}
if (dateMarkers[0].cropped) {
if (!this._checkMarkersPosition(invert, dateMarkers[1], dateMarkers[0])) {
dateMarkers[0].hideLabel();
}
}
var prevDateMarker;
dateMarkers.forEach(function (marker, i, markers) {
if (marker.cropped) {
return;
}
if (invert ? marker.getEnd() < canvas.end : marker.getEnd() > canvas.end) {
marker.hideLabel();
} else if (that._checkMarkersPosition(invert, marker, prevDateMarker)) {
prevDateMarker = marker;
} else {
marker.hide();
}
});
this._dateMarkers.forEach(function (marker) {
if (marker.label) {
var labelBBox = marker.labelBBox,
dy = marker.y + markerOptions.textTopIndent - labelBBox.y;
marker.label.attr({
translateX: invert ? marker.x - textIndent - labelBBox.x - labelBBox.width : marker.x + textIndent - labelBBox.x,
translateY: dy + offset
});
}
if (marker.line) {
marker.line.attr({
translateY: offset
});
}
});
that._initializeMarkersTrackers(offset);
return offset + markerOptions.topIndent + markerOptions.separatorHeight;
},
_checkMarkersPosition: function _checkMarkersPosition(invert, dateMarker, prevDateMarker) {
if (prevDateMarker === undefined) {
return true;
}
return invert ? dateMarker.x < prevDateMarker.getEnd() : dateMarker.x > prevDateMarker.getEnd();
},
_initializeMarkersTrackers: function _initializeMarkersTrackers(offset) {
var that = this,
separatorHeight = that._options.marker.separatorHeight,
renderer = that._renderer,
businessRange = this._translator.getBusinessRange(),
canvas = that._getCanvasStartEnd(),
group = that._axisElementsGroup;
that._markerTrackers = this._dateMarkers.filter(function (marker) {
return !marker.hidden;
}).map(function (marker, i, markers) {
var nextMarker = markers[i + 1] || {
x: canvas.end,
date: businessRange.max
},
x = marker.x,
y = marker.y + offset,
markerTracker = renderer.path([x, y, x, y + separatorHeight, nextMarker.x, y + separatorHeight, nextMarker.x, y, x, y], "area").attr({
"stroke-width": 1,
stroke: "grey",
fill: "grey",
opacity: 0.0001
}).append(group);
markerTracker.data("range", { startValue: marker.date, endValue: nextMarker.date });
if (marker.title) {
markerTracker.setTitle(marker.title);
}
return markerTracker;
});
},
_getLabelFormatOptions: function _getLabelFormatOptions(formatString) {
var that = this,
markerLabelOptions = that._markerLabelOptions;
if (!markerLabelOptions) {
that._markerLabelOptions = markerLabelOptions = _extend(true, {}, that._options.marker.label);
}
if (!isDefined(that._options.marker.label.format)) {
markerLabelOptions.format = formatString;
}
return markerLabelOptions;
},
_adjustConstantLineLabels: function _adjustConstantLineLabels(constantLines) {
var that = this,
axisPosition = that._options.position,
canvas = that.getCanvas(),
canvasLeft = canvas.left,
canvasRight = canvas.width - canvas.right,
canvasTop = canvas.top,
canvasBottom = canvas.height - canvas.bottom,
verticalCenter = canvasTop + (canvasBottom - canvasTop) / 2,
horizontalCenter = canvasLeft + (canvasRight - canvasLeft) / 2,
maxLabel = 0;
constantLines.forEach(function (item) {
var isHorizontal = that._isHorizontal,
linesOptions = item.options,
paddingTopBottom = linesOptions.paddingTopBottom,
paddingLeftRight = linesOptions.paddingLeftRight,
labelOptions = linesOptions.label,
labelVerticalAlignment = labelOptions.verticalAlignment,
labelHorizontalAlignment = labelOptions.horizontalAlignment,
labelIsInside = labelOptions.position === "inside",
label = item.label,
box = item.labelBBox,
translateX,
translateY;
if (label === null) {
return;
}
if (isHorizontal) {
if (labelIsInside) {
if (labelHorizontalAlignment === LEFT) {
translateX = item.coord - paddingLeftRight - box.x - box.width;
} else {
translateX = item.coord + paddingLeftRight - box.x;
}
switch (labelVerticalAlignment) {
case CENTER:
translateY = verticalCenter - box.y - box.height / 2;
break;
case BOTTOM:
translateY = canvasBottom - paddingTopBottom - box.y - box.height;
break;
default:
translateY = canvasTop + paddingTopBottom - box.y;
break;
}
} else {
if (axisPosition === labelVerticalAlignment) {
maxLabel = _max(maxLabel, box.height + paddingTopBottom);
}
translateX = item.coord - box.x - box.width / 2;
if (labelVerticalAlignment === BOTTOM) {
translateY = canvasBottom + paddingTopBottom - box.y;
} else {
translateY = canvasTop - paddingTopBottom - box.y - box.height;
}
}
} else {
if (labelIsInside) {
if (labelVerticalAlignment === BOTTOM) {
translateY = item.coord + paddingTopBottom - box.y;
} else {
translateY = item.coord - paddingTopBottom - box.y - box.height;
}
switch (labelHorizontalAlignment) {
case CENTER:
translateX = horizontalCenter - box.x - box.width / 2;
break;
case RIGHT:
translateX = canvasRight - paddingLeftRight - box.x - box.width;
break;
default:
translateX = canvasLeft + paddingLeftRight - box.x;
break;
}
} else {
if (axisPosition === labelHorizontalAlignment) {
maxLabel = _max(maxLabel, box.width + paddingLeftRight);
}
translateY = item.coord - box.y - box.height / 2;
if (labelHorizontalAlignment === RIGHT) {
translateX = canvasRight + paddingLeftRight - box.x;
} else {
translateX = canvasLeft - paddingLeftRight - box.x - box.width;
}
}
}
label.attr({
translateX: translateX,
translateY: translateY
});
});
return maxLabel;
},
_drawConstantLinesForEstimating: function _drawConstantLinesForEstimating(constantLines) {
var that = this,
renderer = this._renderer,
group = renderer.g();
constantLines.forEach(function (options) {
that._drawConstantLineLabelText(options.label.text, 0, 0, options.label, group).attr({ align: "center" });
});
return group.append(renderer.root);
},
_estimateLabelHeight: function _estimateLabelHeight(bBox, labelOptions) {
var height = bBox.height,
drawingType = labelOptions.drawingType;
if (this._validateDisplayMode(drawingType) === "stagger" || this._validateOverlappingMode(labelOptions.overlappingBehavior, drawingType) === "stagger") {
height = height * 2 + labelOptions.staggeringSpacing;
}
if (this._validateDisplayMode(drawingType) === "rotate" || this._validateOverlappingMode(labelOptions.overlappingBehavior, drawingType) === "rotate") {
var sinCos = vizUtils.getCosAndSin(labelOptions.rotationAngle);
height = height * sinCos.cos + bBox.width * sinCos.sin;
}
return height && (height + labelOptions.indentFromAxis || 0) || 0;
},
estimateMargins: function estimateMargins(canvas) {
this.updateCanvas(canvas);
var that = this,
range = that._getViewportRange(),
ticksData = this._createTicksAndLabelFormat(range),
ticks = ticksData.ticks,
tickInterval = ticksData.tickInterval,
options = this._options,
constantLineOptions = (options.constantLines || []).filter(function (options) {
that._checkAlignmentConstantLineLabels(options.label);
return options.label.position === "outside" && options.label.visible;
}),
rootElement = that._renderer.root,
labelIsVisible = options.label.visible && !range.stubData && ticks.length,
labelValue = labelIsVisible && that.formatLabel(ticks[ticks.length - 1], options.label, undefined, undefined, tickInterval, ticks),
labelElement = labelIsVisible && that._renderer.text(labelValue, 0, 0).css(that._textFontStyles).attr(that._textOptions).append(rootElement),
titleElement = that._drawTitleText(rootElement, { x: 0, y: 0 }),
constantLinesLabelsElement = that._drawConstantLinesForEstimating(constantLineOptions),
labelBox = labelElement && labelElement.getBBox() || { x: 0, y: 0, width: 0, height: 0 },
titleBox = titleElement && titleElement.getBBox() || { x: 0, y: 0, width: 0, height: 0 },
constantLinesBox = constantLinesLabelsElement.getBBox(),
titleHeight = titleBox.height ? titleBox.height + options.title.margin : 0,
labelHeight = that._estimateLabelHeight(labelBox, options.label),
constantLinesHeight = constantLinesBox.height ? constantLinesBox.height + getMaxConstantLinePadding(constantLineOptions) : 0,
height = labelHeight + titleHeight,
margins = {
left: _max(getLeftMargin(labelBox), getLeftMargin(constantLinesBox)),
right: _max(getRightMargin(labelBox), getRightMargin(constantLinesBox)),
top: (options.position === "top" ? height : 0) + getConstantLineLabelMarginForVerticalAlignment(constantLineOptions, "top", constantLinesHeight),
bottom: (options.position !== "top" ? height : 0) + getConstantLineLabelMarginForVerticalAlignment(constantLineOptions, "bottom", constantLinesHeight)
};
labelElement && labelElement.remove();
titleElement && titleElement.remove();
constantLinesLabelsElement && constantLinesLabelsElement.remove();
return margins;
},
_checkAlignmentConstantLineLabels: function _checkAlignmentConstantLineLabels(labelOptions) {
var position = labelOptions.position,
verticalAlignment = (labelOptions.verticalAlignment || "").toLowerCase(),
horizontalAlignment = (labelOptions.horizontalAlignment || "").toLowerCase();
if (this._isHorizontal) {
if (position === "outside") {
verticalAlignment = verticalAlignment === BOTTOM ? BOTTOM : TOP;
horizontalAlignment = CENTER;
} else {
verticalAlignment = verticalAlignment === CENTER ? CENTER : verticalAlignment === BOTTOM ? BOTTOM : TOP;
horizontalAlignment = horizontalAlignment === LEFT ? LEFT : RIGHT;
}
} else {
if (position === "outside") {
verticalAlignment = CENTER;
horizontalAlignment = horizontalAlignment === LEFT ? LEFT : RIGHT;
} else {
verticalAlignment = verticalAlignment === BOTTOM ? BOTTOM : TOP;
horizontalAlignment = horizontalAlignment === RIGHT ? RIGHT : horizontalAlignment === CENTER ? CENTER : LEFT;
}
}
labelOptions.verticalAlignment = verticalAlignment;
labelOptions.horizontalAlignment = horizontalAlignment;
},
_getConstantLineLabelsCoords: function _getConstantLineLabelsCoords(value, lineLabelOptions) {
var that = this,
x = value,
y = value;
if (that._isHorizontal) {
y = that._orthogonalPositions[lineLabelOptions.verticalAlignment === "top" ? "start" : "end"];
} else {
x = that._orthogonalPositions[lineLabelOptions.horizontalAlignment === "right" ? "end" : "start"];
}
return { x: x, y: y };
},
_getAdjustedStripLabelCoords: function _getAdjustedStripLabelCoords(strip) {
var stripOptions = strip.options,
paddingTopBottom = stripOptions.paddingTopBottom,
paddingLeftRight = stripOptions.paddingLeftRight,
horizontalAlignment = stripOptions.label.horizontalAlignment,
verticalAlignment = stripOptions.label.verticalAlignment,
box = strip.labelBBox,
labelHeight = box.height,
labelWidth = box.width,
labelCoords = strip.labelCoords,
y = labelCoords.y - box.y,
x = labelCoords.x - box.x;
if (verticalAlignment === TOP) {
y += paddingTopBottom;
} else if (verticalAlignment === CENTER) {
y -= labelHeight / 2;
} else if (verticalAlignment === BOTTOM) {
y -= paddingTopBottom + labelHeight;
}
if (horizontalAlignment === LEFT) {
x += paddingLeftRight;
} else if (horizontalAlignment === CENTER) {
x -= labelWidth / 2;
} else if (horizontalAlignment === RIGHT) {
x -= paddingLeftRight + labelWidth;
}
return { translateX: x, translateY: y };
},
_adjustTitle: function _adjustTitle(offset) {
offset = offset || 0;
if (!this._title) {
return;
}
var that = this,
options = that._options,
position = options.position,
margin = options.title.margin,
title = that._title,
boxTitle = title.bBox,
x = boxTitle.x,
y = boxTitle.y,
width = boxTitle.width,
height = boxTitle.height,
axisPosition = that._axisPosition,
loCoord = axisPosition - margin - offset,
hiCoord = axisPosition + margin + offset,
params = {};
if (that._isHorizontal) {
if (position === TOP) {
params.translateY = loCoord - (y + height);
} else {
params.translateY = hiCoord - y;
}
} else {
if (position === LEFT) {
params.translateX = loCoord - (x + width);
} else {
params.translateX = hiCoord - x;
}
}
title.element.attr(params);
},
_checkTitleOverflow: function _checkTitleOverflow() {
if (!this._title) {
return;
}
var canvasLength = this._getScreenDelta(),
title = this._title,
boxTitle = title.bBox;
if ((this._isHorizontal ? boxTitle.width : boxTitle.height) > canvasLength) {
title.element.applyEllipsis(canvasLength) && title.element.setTitle(this._options.title.text);
} else {
title.element.restoreText();
}
},
coordsIn: function coordsIn(x, y) {
var canvas = this.getCanvas(),
isHorizontal = this._options.isHorizontal,
position = this._options.position,
coord = isHorizontal ? y : x;
if (isHorizontal && position === constants.top || !isHorizontal && position === constants.left) {
return coord < canvas[position];
}
return coord > canvas[isHorizontal ? "height" : "width"] - canvas[position];
},
_boundaryTicksVisibility: {
min: true,
max: true
},
_getMinMax: function _getMinMax() {
return { min: this._options.min, max: this._options.max };
},
_getStick: function _getStick() {
return !this._options.valueMarginsEnabled;
},
_getStripLabelCoords: function _getStripLabelCoords(from, to, stripLabelOptions) {
var that = this,
orthogonalPositions = that._orthogonalPositions,
isHorizontal = that._isHorizontal,
horizontalAlignment = stripLabelOptions.horizontalAlignment,
verticalAlignment = stripLabelOptions.verticalAlignment,
x,
y;
if (isHorizontal) {
if (horizontalAlignment === CENTER) {
x = from + (to - from) / 2;
} else if (horizontalAlignment === LEFT) {
x = from;
} else if (horizontalAlignment === RIGHT) {
x = to;
}
y = orthogonalPositions[getStripVerticalAlignmentPosition(verticalAlignment)];
} else {
x = orthogonalPositions[getStripHorizontalAlignmentPosition(horizontalAlignment)];
if (verticalAlignment === TOP) {
y = from;
} else if (verticalAlignment === CENTER) {
y = to + (from - to) / 2;
} else if (verticalAlignment === BOTTOM) {
y = to;
}
}
return { x: x, y: y };
},
_getTranslatedValue: function _getTranslatedValue(value, offset) {
var pos1 = this._translator.translate(value, offset, this._options.type === "semidiscrete" && this._options.tickInterval),
pos2 = this._axisPosition,
isHorizontal = this._isHorizontal;
return {
x: isHorizontal ? pos1 : pos2,
y: isHorizontal ? pos2 : pos1
};
},
areCoordsOutsideAxis: function areCoordsOutsideAxis(coords) {
// getCanvasVisibleArea takes into account inverted case
var canvas = this._translator.getCanvasVisibleArea(),
coord = this._isHorizontal ? coords.x : coords.y;
if (coord < canvas.min || coord > canvas.max) {
return true;
}
return false;
},
_getSkippedCategory: function _getSkippedCategory(ticks) {
var skippedCategory;
if (this._options.type === constants.discrete && this._tickOffset && ticks.length !== 0) {
skippedCategory = ticks[ticks.length - 1];
}
return skippedCategory;
},
_getScaleBreaks: function _getScaleBreaks(axisOptions, viewport, series, isArgumentAxis) {
var that = this,
breaks = (axisOptions.breaks || []).map(function (b) {
return {
from: that.parser(b.startValue),
to: that.parser(b.endValue)
};
});
if (axisOptions.type !== "discrete" && axisOptions.dataType === "datetime" && axisOptions.workdaysOnly) {
breaks = breaks.concat(generateDateBreaks(viewport.minVisible, viewport.maxVisible, axisOptions.workWeek, axisOptions.singleWorkdays, axisOptions.holidays));
}
if (!isArgumentAxis && axisOptions.type !== "discrete" && axisOptions.dataType !== "datetime" && axisOptions.autoBreaksEnabled && axisOptions.maxAutoBreakCount !== 0) {
breaks = breaks.concat(generateAutoBreaks(axisOptions, series, viewport));
}
return filterBreaks(sortingBreaks(breaks), viewport, axisOptions.breakStyle);
},
_drawBreak: function _drawBreak(translatedEnd, positionFrom, positionTo, width, options, group) {
var that = this,
breakStart = translatedEnd - (!that._translator.isInverted() ? width + 1 : 0),
attr = {
"stroke-width": 1,
stroke: options.borderColor,
sharp: !options.isWaved ? options.isHorizontal ? "h" : "v" : undefined
},
spaceAttr = {
stroke: options.color,
"stroke-width": width
},
getPoints = that._isHorizontal ? rotateLine : function (p) {
return p;
},
drawer = getLineDrawer(that._renderer, spaceAttr, attr, group, getPoints, positionFrom, breakStart, positionTo, options.isWaved);
drawer(width / 2, spaceAttr);
drawer(0, attr);
drawer(width, attr);
},
_createBreakClipRect: function _createBreakClipRect(from, to) {
var that = this,
canvas = that._canvas,
clipWidth = to - from,
clipRect;
if (that._isHorizontal) {
clipRect = that._renderer.clipRect(canvas.left, from, canvas.width, clipWidth);
} else {
clipRect = that._renderer.clipRect(from, canvas.top, clipWidth, canvas.height);
}
that._breaksElements = that._breaksElements || [];
that._breaksElements.push(clipRect);
return clipRect.id;
},
_createBreaksGroup: function _createBreaksGroup(clipFrom, clipTo) {
var that = this,
group = that._renderer.g().attr({
"class": that._axisCssPrefix + "breaks",
"clip-path": that._createBreakClipRect(clipFrom, clipTo)
}).append(that._scaleBreaksGroup);
that._breaksElements = that._breaksElements || [];
that._breaksElements.push(group);
return group;
},
_disposeBreaksGroup: function _disposeBreaksGroup() {
(this._breaksElements || []).forEach(function (clipRect) {
clipRect.dispose();
});
this._breaksElements = null;
},
drawScaleBreaks: function drawScaleBreaks(customCanvas) {
var that = this,
options = that._options,
breakStyle = options.breakStyle,
position = options.position,
positionFrom,
positionTo,
breaks = that._translator.getBusinessRange().breaks || [],
additionGroup,
additionBreakFrom,
additionBreakTo,
mainGroup,
breakOptions;
that._disposeBreaksGroup();
if (!(breaks && breaks.length)) {
return;
}
breakOptions = {
color: that._options.containerColor,
borderColor: breakStyle.color,
isHorizontal: that._isHorizontal,
isWaved: breakStyle.line.toLowerCase() !== "straight"
};
if (customCanvas) {
positionFrom = customCanvas.start;
positionTo = customCanvas.end;
} else {
positionFrom = that._orthogonalPositions.start - (options.visible && !that._axisShift && (position === "left" || position === "top") ? SCALE_BREAK_OFFSET : 0);
positionTo = that._orthogonalPositions.end + (options.visible && (position === "right" || position === "bottom") ? SCALE_BREAK_OFFSET : 0);
}
mainGroup = that._createBreaksGroup(positionFrom, positionTo);
if (that._axisShift && options.visible) {
additionBreakFrom = that._axisPosition - that._axisShift - SCALE_BREAK_OFFSET;
additionBreakTo = additionBreakFrom + SCALE_BREAK_OFFSET * 2;
additionGroup = that._createBreaksGroup(additionBreakFrom, additionBreakTo);
}
breaks.forEach(function (br) {
if (!br.gapSize) {
var breakCoord = that._getTranslatedCoord(br.to);
that._drawBreak(breakCoord, positionFrom, positionTo, breakStyle.width, breakOptions, mainGroup);
if (that._axisShift && options.visible) {
that._drawBreak(breakCoord, additionBreakFrom, additionBreakTo, breakStyle.width, breakOptions, additionGroup);
}
}
});
},
_getSpiderCategoryOption: noop,
shift: function shift(margins) {
var that = this,
options = that._options,
isHorizontal = options.isHorizontal,
axesSpacing = that.getMultipleAxesSpacing(),
constantLinesGroups = that._axisConstantLineGroups;
function shiftGroup(side, group) {
var attr = {},
shift = margins[side] ? margins[side] + axesSpacing : 0;
attr[isHorizontal ? "translateY" : "translateX"] = (side === "left" || side === "top" ? -1 : 1) * shift;
(group[side] || group).attr(attr);
return shift;
}
that._axisShift = shiftGroup(options.position, that._axisGroup);
if (isHorizontal) {
shiftGroup("top", constantLinesGroups);
shiftGroup("bottom", constantLinesGroups);
} else {
shiftGroup("left", constantLinesGroups);
shiftGroup("right", constantLinesGroups);
}
}
}
};
function getLineDrawer(renderer, spaceAttr, elementAttr, root, rotatePoints, positionFrom, breakStart, positionTo, isWaved) {
var elementType = isWaved ? "bezier" : "line",
group = renderer.g().append(root);
return function (offset, attr) {
renderer.path(rotatePoints(getPoints(positionFrom, breakStart, positionTo, offset, isWaved)), elementType).attr(attr).append(group);
};
}
function getPoints(positionFrom, breakStart, positionTo, offset, isWaved) {
if (!isWaved) {
return [positionFrom, breakStart + offset, positionTo, breakStart + offset];
}
breakStart += offset;
var currentPosition,
topPoint = breakStart + WAVED_LINE_TOP,
centerPoint = breakStart + WAVED_LINE_CENTER,
bottomPoint = breakStart + WAVED_LINE_BOTTOM,
points = [[positionFrom, centerPoint]];
for (currentPosition = positionFrom; currentPosition < positionTo + WAVED_LINE_LENGTH; currentPosition += WAVED_LINE_LENGTH) {
points.push([currentPosition + 6, topPoint, currentPosition + 6, topPoint, currentPosition + 12, centerPoint, currentPosition + 18, bottomPoint, currentPosition + 18, bottomPoint, currentPosition + 24, centerPoint]);
}
return [].concat.apply([], points);
}
function rotateLine(lineCoords) {
var points = [],
i;
for (i = 0; i < lineCoords.length; i += 2) {
points.push(lineCoords[i + 1]);
points.push(lineCoords[i]);
}
return points;
}