devextreme
Version:
HTML5 JavaScript Component Suite for Responsive Web Development
387 lines (323 loc) • 13.7 kB
JavaScript
var extend = require("../../core/utils/extend").extend,
layoutElementModule = require("../core/layout_element"),
_isNumber = require("../../core/utils/type").isNumeric,
_min = Math.min,
_max = Math.max,
_floor = Math.floor,
_sqrt = Math.sqrt,
consts = require("../components/consts"),
RADIAL_LABEL_INDENT = consts.radialLabelIndent;
function getNearestCoord(firstCoord, secondCoord, pointCenterCoord) {
var nearestCoord;
if (pointCenterCoord < firstCoord) {
nearestCoord = firstCoord;
} else if (secondCoord < pointCenterCoord) {
nearestCoord = secondCoord;
} else {
nearestCoord = pointCenterCoord;
}
return nearestCoord;
}
function getLabelLayout(point) {
if (point._label.isVisible() && point._label.getLayoutOptions().position !== "inside") {
return point._label.getBoundingRect();
}
}
function getPieRadius(series, paneCenterX, paneCenterY, accessibleRadius, minR) {
series.some(function (singleSeries) {
return singleSeries.getVisiblePoints().reduce(function (radiusIsFound, point) {
var labelBBox = getLabelLayout(point);
if (labelBBox) {
var xCoords = getNearestCoord(labelBBox.x, labelBBox.x + labelBBox.width, paneCenterX),
yCoords = getNearestCoord(labelBBox.y, labelBBox.y + labelBBox.height, paneCenterY);
accessibleRadius = _min(_max(getLengthFromCenter(xCoords, yCoords, paneCenterX, paneCenterY) - RADIAL_LABEL_INDENT, minR), accessibleRadius);
radiusIsFound = true;
}
return radiusIsFound;
}, false);
});
return accessibleRadius;
}
function getSizeLabels(series) {
return series.reduce(function (res, singleSeries) {
var maxWidth = singleSeries.getVisiblePoints().reduce(function (width, point) {
var labelBBox = getLabelLayout(point);
if (labelBBox && labelBBox.width > width) {
width = labelBBox.width;
}
return width;
}, 0);
if (maxWidth) {
res.outerLabelsCount++;
if (res.outerLabelsCount > 1) {
maxWidth += consts.pieLabelSpacing;
}
}
res.sizes.push(maxWidth);
res.common += maxWidth;
return res;
}, { sizes: [], common: 0, outerLabelsCount: 0 });
}
function correctLabelRadius(sizes, radius, series, canvas, averageWidthLabels) {
var curRadius,
i,
centerX = (canvas.width - canvas.left - canvas.right) / 2 + canvas.left,
runningWidth = 0;
for (i = 0; i < series.length; i++) {
if (sizes[i] === 0) {
curRadius && (curRadius += sizes[i - 1]);
continue;
}
curRadius = _floor(curRadius ? curRadius + sizes[i - 1] : radius);
series[i].correctLabelRadius(curRadius);
runningWidth += averageWidthLabels || sizes[i];
sizes[i] = averageWidthLabels || sizes[i];
series[i].setVisibleArea({
left: centerX - radius - runningWidth,
right: canvas.width - (centerX + radius + runningWidth),
top: canvas.top,
bottom: canvas.bottom,
width: canvas.width,
height: canvas.height
});
}
}
function getLengthFromCenter(x, y, paneCenterX, paneCenterY) {
return _sqrt((x - paneCenterX) * (x - paneCenterX) + (y - paneCenterY) * (y - paneCenterY));
}
function getInnerRadius(series) {
var innerRadius;
if (series.type === "pie") {
innerRadius = 0;
} else {
innerRadius = _isNumber(series.innerRadius) ? Number(series.innerRadius) : 0.5;
innerRadius = innerRadius < 0.2 ? 0.2 : innerRadius;
innerRadius = innerRadius > 0.8 ? 0.8 : innerRadius;
}
return innerRadius;
}
var inverseAlign = {
left: "right",
right: "left",
top: "bottom",
bottom: "top",
center: "center"
};
function downSize(canvas, layoutOptions) {
canvas[layoutOptions.cutLayoutSide] += layoutOptions.cutSide === "horizontal" ? layoutOptions.width : layoutOptions.height;
}
function getOffset(layoutOptions, offsets) {
var side = layoutOptions.cutLayoutSide,
offset = {
horizontal: 0,
vertical: 0
};
switch (side) {
case "top":
case "left":
offset[layoutOptions.cutSide] = -offsets[side];
break;
case "bottom":
case "right":
offset[layoutOptions.cutSide] = offsets[side];
break;
}
return offset;
}
function LayoutManager() {}
function toLayoutElementCoords(canvas) {
return new layoutElementModule.WrapperLayoutElement(null, {
x: canvas.left,
y: canvas.top,
width: canvas.width - canvas.left - canvas.right,
height: canvas.height - canvas.top - canvas.bottom
});
}
function correctAvailableRadius(availableRadius, canvas, series, minR, paneCenterX, paneCenterY) {
var sizeLabels = getSizeLabels(series),
averageWidthLabels,
fullRadiusWithLabels = paneCenterX - canvas.left - (sizeLabels.outerLabelsCount > 0 ? sizeLabels.common + RADIAL_LABEL_INDENT : 0);
if (fullRadiusWithLabels < minR) {
availableRadius = minR;
averageWidthLabels = (paneCenterX - availableRadius - RADIAL_LABEL_INDENT - canvas.left) / sizeLabels.outerLabelsCount;
} else {
availableRadius = _min(getPieRadius(series, paneCenterX, paneCenterY, availableRadius, minR), fullRadiusWithLabels);
}
correctLabelRadius(sizeLabels.sizes, availableRadius + RADIAL_LABEL_INDENT, series, canvas, averageWidthLabels);
return availableRadius;
}
LayoutManager.prototype = {
constructor: LayoutManager,
setOptions: function setOptions(options) {
this._options = options;
},
applyPieChartSeriesLayout: function applyPieChartSeriesLayout(canvas, series, hideLayoutLabels) {
var paneSpaceHeight = canvas.height - canvas.top - canvas.bottom,
paneSpaceWidth = canvas.width - canvas.left - canvas.right,
paneCenterX = paneSpaceWidth / 2 + canvas.left,
paneCenterY = paneSpaceHeight / 2 + canvas.top,
piePercentage = this._options.piePercentage,
availableRadius,
minR;
if (_isNumber(piePercentage)) {
availableRadius = minR = piePercentage * _min(canvas.height, canvas.width) / 2;
} else {
availableRadius = _min(paneSpaceWidth, paneSpaceHeight) / 2;
minR = this._options.minPiePercentage * availableRadius;
}
if (!hideLayoutLabels) {
availableRadius = correctAvailableRadius(availableRadius, canvas, series, minR, paneCenterX, paneCenterY);
}
return {
centerX: _floor(paneCenterX),
centerY: _floor(paneCenterY),
radiusInner: _floor(availableRadius * getInnerRadius(series[0])),
radiusOuter: _floor(availableRadius)
};
},
applyEqualPieChartLayout: function applyEqualPieChartLayout(series, layout) {
var radius = layout.radius;
return {
centerX: _floor(layout.x),
centerY: _floor(layout.y),
radiusInner: _floor(radius * getInnerRadius(series[0])),
radiusOuter: _floor(radius)
};
},
needMoreSpaceForPanesCanvas: function needMoreSpaceForPanesCanvas(panes, rotated) {
var options = this._options,
width = options.width,
height = options.height,
piePercentage = options.piePercentage,
percentageIsValid = _isNumber(piePercentage),
needHorizontalSpace = 0,
needVerticalSpace = 0;
panes.forEach(function (pane) {
var paneCanvas = pane.canvas,
minSize = percentageIsValid ? _min(paneCanvas.width, paneCanvas.height) * piePercentage : undefined,
needPaneHorizontalSpace = (percentageIsValid ? minSize : width) - (paneCanvas.width - paneCanvas.left - paneCanvas.right),
needPaneVerticalSpace = (percentageIsValid ? minSize : height) - (paneCanvas.height - paneCanvas.top - paneCanvas.bottom);
if (rotated) {
needHorizontalSpace += needPaneHorizontalSpace > 0 ? needPaneHorizontalSpace : 0;
needVerticalSpace = _max(needPaneVerticalSpace > 0 ? needPaneVerticalSpace : 0, needVerticalSpace);
} else {
needHorizontalSpace = _max(needPaneHorizontalSpace > 0 ? needPaneHorizontalSpace : 0, needHorizontalSpace);
needVerticalSpace += needPaneVerticalSpace > 0 ? needPaneVerticalSpace : 0;
}
});
return needHorizontalSpace > 0 || needVerticalSpace > 0 ? { width: needHorizontalSpace, height: needVerticalSpace } : false;
},
layoutElements: function layoutElements(elements, canvas, funcAxisDrawer, panes, rotated) {
this._elements = elements;
this._probeDrawing(canvas);
this._drawElements(canvas);
funcAxisDrawer();
this._processAdaptiveLayout(panes, rotated, canvas, funcAxisDrawer);
this._positionElements(canvas);
},
_processAdaptiveLayout: function _processAdaptiveLayout(panes, rotated, canvas, funcAxisDrawer) {
var that = this,
size = that.needMoreSpaceForPanesCanvas(panes, rotated),
items = this._elements;
if (!size) return;
function processCanvases(item, layoutOptions, side) {
if (!item.getLayoutOptions()[side]) {
canvas[layoutOptions.cutLayoutSide] -= layoutOptions[side];
size[side] = size[side] - layoutOptions[side];
}
}
items.slice().reverse().forEach(function (item) {
var layoutOptions = item.getLayoutOptions(),
needRedraw = false,
sizeObject,
cutSide;
if (!layoutOptions) {
return;
}
sizeObject = extend({}, layoutOptions);
needRedraw = layoutOptions.cutSide === "vertical" && size.width < 0 || layoutOptions.cutSide === "horizontal" && size.height < 0 || layoutOptions.cutSide === "vertical" && size.height > 0 || layoutOptions.cutSide === "horizontal" && size.width > 0;
cutSide = layoutOptions.cutSide === "horizontal" ? "width" : "height";
if (needRedraw) {
var width = sizeObject.width - size.width;
var height = sizeObject.height - size.height;
if (cutSide === "height" && size.width < 0) {
width = canvas.width - canvas.left - canvas.right;
}
if (cutSide === "width" && size.height < 0) {
height = canvas.height - canvas.top - canvas.bottom;
}
item.draw(width, height);
}
processCanvases(item, layoutOptions, cutSide);
});
funcAxisDrawer(size);
},
_probeDrawing: function _probeDrawing(canvas) {
var that = this;
this._elements.forEach(function (item) {
var layoutOptions = item.getLayoutOptions(),
sizeObject;
if (!layoutOptions) {
return;
}
sizeObject = { width: canvas.width - canvas.left - canvas.right, height: canvas.height - canvas.top - canvas.bottom };
if (layoutOptions.cutSide === "vertical") {
sizeObject.height -= that._options.height;
} else {
sizeObject.width -= that._options.width;
}
item.probeDraw(sizeObject.width, sizeObject.height);
downSize(canvas, item.getLayoutOptions());
});
},
_drawElements: function _drawElements(canvas) {
this._elements.slice().reverse().forEach(function (item) {
var layoutOptions = item.getLayoutOptions(),
sizeObject,
cutSide,
length;
if (!layoutOptions) {
return;
}
sizeObject = {
width: canvas.width - canvas.left - canvas.right,
height: canvas.height - canvas.top - canvas.bottom
};
cutSide = layoutOptions.cutSide;
length = cutSide === "horizontal" ? "width" : "height";
sizeObject[length] = layoutOptions[length];
item.draw(sizeObject.width, sizeObject.height);
});
},
_positionElements: function _positionElements(canvas) {
var offsets = {
left: 0,
right: 0,
top: 0,
bottom: 0
};
this._elements.slice().reverse().forEach(function (item) {
var layoutOptions = item.getLayoutOptions(),
position,
cutSide,
my;
if (!layoutOptions) {
return;
}
position = layoutOptions.position;
cutSide = layoutOptions.cutSide;
my = {
horizontal: position.horizontal,
vertical: position.vertical
};
my[cutSide] = inverseAlign[my[cutSide]];
item.position({
of: toLayoutElementCoords(canvas), my: my,
at: position, offset: getOffset(layoutOptions, offsets)
});
offsets[layoutOptions.cutLayoutSide] += layoutOptions[layoutOptions.cutSide === "horizontal" ? "width" : "height"];
});
}
};
exports.LayoutManager = LayoutManager;
;