jui.define("chart.widget.legend", [ "util.base" ], function(_) {
var WIDTH = 17, HEIGHT = 13, PADDING = 5, RADIUS = 5.5, RATIO = 1.2, POINT = 2;
/**
* @class chart.widget.legend
* implements legend widget
* @extends chart.widget.core
* @alias LegendWidget
* @requires util.base
*
*/
var LegendWidget = function(chart, axis, widget) {
var columns = [];
var colorIndex = {};
function getIndexArray(brush) {
var list = [ 0 ];
if(_.typeCheck("array", brush)) {
list = brush;
} else if(_.typeCheck("integer", brush)) {
list = [ brush ];
}
return list;
}
function getBrushAll() {
var list = getIndexArray(widget.brush),
result = [];
for(var i = 0; i < list.length; i++) {
result[i] = chart.get("brush", list[i]);
}
return result;
}
function setLegendStatus(brush) {
if(!widget.filter) return;
if(!columns[brush.index]) {
columns[brush.index] = {};
}
for(var i = 0; i < brush.target.length; i++) {
columns[brush.index][brush.target[i]] = true;
}
}
function changeTargetOption(brushList) {
var target = [],
colors = [],
index = brushList[0].index;
for(var key in columns[index]) {
if(columns[index][key]) {
target.push(key);
colors.push(colorIndex[key]);
}
}
for(var i = 0; i < brushList.length; i++) {
chart.updateBrush(brushList[i].index, {
target: target,
colors: colors
});
}
// 차트 렌더링이 활성화되지 않았을 경우
if(!chart.isRender()) {
chart.render();
}
chart.emit("legend.filter", [ target ]);
}
this.getLegendIcon = function(brush) {
var arr = [],
data = brush.target,
count = data.length;
for(var i = 0; i < count; i++) {
var group = chart.svg.group(),
target = brush.target[i],
text = target,
color = chart.color(i, widget.colors || brush.colors);
// 컬러 인덱스 설정
colorIndex[target] = color;
// 타겟 별 포맷 설정
if(_.typeCheck("function", widget.format)) {
text = this.format(target);
}
// 텍스트 길이 구하기
var rect = chart.svg.getTextSize(text, {
fontSize : chart.theme('legendFontSize')
});
if(widget.filter) {
group.append(chart.svg.line({
x1: 0,
x2: WIDTH,
y1: -(RADIUS / 2),
y2: -(RADIUS / 2),
stroke: color,
"stroke-width": HEIGHT,
"stroke-linecap": "round"
}));
group.append(chart.svg.circle({
cx : WIDTH,
cy : -(RADIUS / 2),
r : RADIUS,
fill : chart.theme("legendSwitchCircleColor")
}));
group.append(chart.text({
x : WIDTH + (PADDING * 2),
y : 0,
"font-size" : chart.theme("legendFontSize"),
"fill" : chart.theme("legendFontColor"),
"text-anchor" : "start"
}, text));
arr.push({
icon : group,
width : WIDTH + rect.width + (PADDING * 2.5),
height : HEIGHT + (PADDING / 2)
});
(function(key, element) {
element.attr({
cursor: "pointer"
});
element.on("click", function(e) {
if(columns[brush.index][key]) {
element.get(0).attr({ stroke: chart.theme("legendSwitchDisableColor") });
element.get(2).attr({ fill: chart.theme("legendSwitchDisableColor") });
element.get(1).attr({ cx: 0 });
columns[brush.index][key] = false;
} else {
element.get(0).attr({ stroke: colorIndex[key] });
element.get(2).attr({ fill: chart.theme("legendFontColor") });
element.get(1).attr({ cx: WIDTH });
columns[brush.index][key] = true;
}
changeTargetOption((widget.brushSync) ? getBrushAll() : [ brush ]);
});
})(target, group);
} else {
var size = chart.theme("legendFontSize");
if(widget.icon != null) {
var icon = _.typeCheck("function", widget.icon) ? widget.icon.apply(chart, [ target ]) : widget.icon;
group.append(chart.text({
x: 0,
y: POINT,
"font-size": size,
"fill": color
}, icon));
} else {
group.append(chart.svg.circle({
cx : size / 2,
cy : -POINT,
r : size / 2,
fill : color
}));
}
group.append(chart.text({
x : size * RATIO,
y : 0,
"font-size" : size,
"fill" : chart.theme("legendFontColor"),
"text-anchor" : "start"
}, text));
arr.push({
icon : group,
width : size + rect.width + (PADDING * 2),
height : HEIGHT + (PADDING / 2)
});
}
}
return arr;
}
this.draw = function() {
var group = chart.svg.group();
var x = 0,
y = 0,
total_width = 0,
total_height = 0,
max_width = 0,
max_height = 0,
brushes = getIndexArray(widget.brush);
var total_widthes = [];
for(var i = 0; i < brushes.length; i++) {
var index = brushes[i];
// brushSync가 true일 경우, 한번만 실행함
if(widget.brushSync && i > 0) continue;
var brush = chart.get("brush", brushes[index]),
arr = this.getLegendIcon(brush);
for(var k = 0; k < arr.length; k++) {
group.append(arr[k].icon);
arr[k].icon.translate(x, y);
if (widget.orient == "bottom" || widget.orient == "top") {
if (x + arr[k].width > chart.area("x2")) {
x = 0;
y += arr[k].height;
max_height += arr[k].height;
arr[k].icon.translate(x, y); // HERE
total_widthes.push(total_width);
total_width = 0;
}
// @thanks to canelia04
x += arr[k].width + (PADDING * 2.5);
total_width += arr[k].width + (PADDING * 2.5);
if (max_height < arr[k].height) {
max_height = arr[k].height;
}
} else {
y += arr[k].height;
total_height += arr[k].height;
if (max_width < arr[k].width) {
max_width = arr[k].width;
}
}
}
if (total_width > 0) {
total_widthes.push(total_width);
}
if (total_widthes.length > 0) {
total_width = Math.max.apply(Math, total_widthes);
}
setLegendStatus(brush);
}
// legend 위치 선정
if (widget.orient == "bottom" || widget.orient == "top") {
var y = ((widget.orient == "bottom") ?
chart.area("y2") + chart.padding("bottom") - max_height :
chart.area("y") - chart.padding("top")) + PADDING;
if (widget.align == "start") {
x = chart.area("x");
} else if (widget.align == "center") {
x = chart.area("x") + (chart.area("width")/2 - total_width / 2);
} else if (widget.align == "end") {
x = chart.area("x2") - total_width;
}
} else {
var x = ((widget.orient == "left") ?
chart.area("x") - chart.padding("left") :
chart.area("x2") + chart.padding("right") - max_width) + PADDING;
if (widget.align == "start") {
y = chart.area("y");
} else if (widget.align == "center") {
y = chart.area("y") + (chart.area("height") / 2 - total_height / 2);
} else if (widget.align == "end") {
y = chart.area("y2") - total_height;
}
}
group.translate(Math.floor(x) + widget.dx, Math.floor(y) + widget.dy);
return group;
}
}
LegendWidget.setup = function() {
return {
/** @cfg {"bottom"/"top"/"left"/"right" } Sets the location where the label is displayed (top, bottom). */
orient: "bottom",
/** @cfg {"start"/"center"/"end" } Aligns the label (center, start, end). */
align: "center", // or start, end
/** @cfg {Boolean} [filter=false] Performs filtering so that only label(s) selected by the brush can be shown. */
filter: false,
/** @cfg {Function/String} [icon=null] */
icon: null,
/** @cfg {Number} [dx=0] Moves the x coordinate by a set value from the location where the chart is drawn. */
dx: 0,
/** @cfg {Number} [dy=0] Moves the y coordinate by a set value from the location where the chart is drawn. */
dy: 0,
/** @cfg {Array} [colors=null] */
colors: null,
/** @cfg {Boolean} [brushSync=false] Applies all brushes equally when using a filter function. */
brushSync: false,
/** @cfg {Number/Array} [brush=0] Specifies a brush index for which a widget is used. */
brush: 0,
/** @cfg {Function} [format=null] Sets the format of the key that is displayed on the legend. */
format: null
};
}
/**
* @event legend_filter
* Event that occurs when the filter function of the legend widget is activated. (real name ``` legend.filter ```)
* @param {String} target The selected data field.
*/
return LegendWidget;
}, "chart.widget.core");