967 lines
38 KiB
JavaScript
967 lines
38 KiB
JavaScript
(function ($) {
|
|
var DonutChart = function (element, options) {
|
|
this.init(element, options);
|
|
};
|
|
|
|
var settings = {
|
|
"type": "pie",
|
|
"theme": "light",
|
|
"addClassNames": true,
|
|
"titleField": "Label",
|
|
"valueField": "Cost",
|
|
"bringToFront": true,
|
|
"labelsEnabled": false,
|
|
"marginBottom": 0,
|
|
"marginTop": 30,
|
|
"pullOutRadius": 0,
|
|
"balloon": {
|
|
"fixedPosition": false
|
|
},
|
|
"fontFamily": "Open Sans",
|
|
"numberFormatter": {
|
|
precision: 0,
|
|
decimalSeparator: ".",
|
|
thousandsSeparator: ","
|
|
}
|
|
};
|
|
|
|
var outerDonutSettings = {
|
|
"radius": 120,
|
|
"innerRadius": 100,
|
|
"allLabels": [{
|
|
"id": "label-title",
|
|
"size": 14,
|
|
"text": "Projects by Goal and Type",
|
|
"color": "#555",
|
|
"align": "center",
|
|
"bold": true,
|
|
"x": 0,
|
|
"y": 14
|
|
}]
|
|
};
|
|
|
|
var innerDonutSettings = {
|
|
"alpha": 0.8,
|
|
"radius": 100,
|
|
"innerRadius": 80
|
|
};
|
|
|
|
DonutChart.prototype = {
|
|
init: function (element, options) {
|
|
AmCharts.addInitHandler(initChartHandler, ["pie"]);
|
|
|
|
var innerChartDiv = $(element).append("<div id='innerChartDiv' class='donutChartDiv'></div>"),
|
|
outerChartDiv = $(element).append("<div id='outerChartDiv' class='donutChartDiv'></div>"),
|
|
containerId = $(element).attr("id");
|
|
|
|
var btnSize = 18;
|
|
|
|
this.donutClearFilterBtn = $("<button id='donutClearFilterBtn' title='Remove Filter'></button>")
|
|
.appendTo($(element))
|
|
.css({
|
|
height: btnSize,
|
|
width: btnSize,
|
|
left: "49%",//$(element).width() / 2 - btnSize / 2,
|
|
top: $(element).height() / 2 - btnSize / 2 + 40,
|
|
"z-index": 4,
|
|
position: "absolute",
|
|
border: "none",
|
|
display: "none",
|
|
"background-image": "url('" + options.clearFilterButtonBackground + "')"
|
|
})
|
|
.click(function () {
|
|
$("#" + containerId).data('donutChart').totalClickHandler();
|
|
$("#donutClearFilterBtn").hide();
|
|
});
|
|
|
|
outerDonutSettings = $.extend(true, outerDonutSettings, settings);
|
|
innerDonutSettings = $.extend(true, innerDonutSettings, settings);
|
|
|
|
this.options = options;
|
|
this.containerId = containerId;
|
|
this.costMode = options.costMode || true;
|
|
this.mode = options.mode || "goalType";
|
|
this.related = options.related || false;
|
|
|
|
this.projectsFilterArray = this.options.projectsFilterArray || [];
|
|
this.goalsFilterArray = this.options.goalsFilterArray || [];
|
|
|
|
this.innerDonut = AmCharts.makeChart("innerChartDiv", innerDonutSettings);
|
|
this.outerDonut = AmCharts.makeChart("outerChartDiv", outerDonutSettings);
|
|
|
|
this.outerDonut.addListener("drawn", this.onDrawn);
|
|
|
|
this.innerDonut.balloonFunction = function (gi) { return getBalloonText(gi, containerId); };
|
|
this.outerDonut.balloonFunction = function (gi) { return getBalloonText(gi, containerId); };
|
|
|
|
// Set data fields for sectors custom display color
|
|
this.innerDonut.colorField = "PresetColor";
|
|
this.outerDonut.colorField = "PresetColor";
|
|
|
|
this.colorCache = {};
|
|
this.lastUsedPaletteColor = -1;
|
|
|
|
this.outerDonut.addListener("clickSlice", function (event) {
|
|
var dataContext = event.dataItem.dataContext;
|
|
var donut = $("#chartDonut1").data("donutChart");
|
|
donut.itemClick(dataContext, true);
|
|
});
|
|
this.innerDonut.addListener("clickSlice", function (event) {
|
|
var dataContext = event.dataItem.dataContext;
|
|
var donut = $("#chartDonut1").data("donutChart");
|
|
donut.itemClick(dataContext, false);
|
|
});
|
|
},
|
|
|
|
onDrawn: function (event) {
|
|
},
|
|
|
|
getLegendItemHtml: function (dataItem, color, index) {
|
|
var unit = this.costMode ? "$" : "";
|
|
var value = this.costMode ? dataItem.Cost.formatNumber(0) : dataItem.Duration.formatNumber(0);
|
|
value = unit + value;
|
|
var label = formatLegendLabel(dataItem.Label);
|
|
var groupType = dataItem.GroupType;
|
|
var donutSelector = "$(\"#" + this.containerId + "\").data(\"donutChart\")";
|
|
var id = dataItem.TypeId.length == 1 ? "id='legend-item-" + dataItem.TypeId[0] + "'" : "";
|
|
var result =
|
|
"<td class='donut-legend-item'" + id + " " +
|
|
"onmouseenter='" + donutSelector + ".onLegendItemMouseEnter(event, \"" + groupType + "\", " + index + ");' " +
|
|
"onmouseleave='" + donutSelector + ".onLegendItemMouseLeave(event, \"" + groupType + "\", " + index + ");' " +
|
|
"onclick='" + donutSelector + ".onLegendItemClick(" + index + ", \"" + groupType + "\");'>" +
|
|
"<div class='donut-legend-indicator' style='background-color:" + color + "!important; -webkit-print-color-adjust: exact; '></div>" +
|
|
"<span class='donut-legend-item-label'>" + label +
|
|
"<div class='pull-right'>" + value + "</div></td>";
|
|
|
|
return result;
|
|
},
|
|
|
|
onLegendItemMouseEnter: function (event, groupType, index) {
|
|
var chart = getChart(groupType, this);
|
|
chart.div.style.zIndex = 2;
|
|
chart.rollOverSlice(index);
|
|
|
|
},
|
|
|
|
onLegendItemMouseLeave: function (event, groupType, index) {
|
|
var chart = getChart(groupType, this);
|
|
chart.div.style.zIndex = 1;
|
|
chart.rollOutSlice(index);
|
|
},
|
|
|
|
onLegendItemClick: function (index, groupType) {
|
|
var chart = getChart(groupType, this);
|
|
//var isOuter = true;
|
|
//if (this.mode === "goalType" && groupType == "ProjectType") {
|
|
// isOuter = false;
|
|
//} else if (this.mode === "typeGoal" && groupType == "Goal") {
|
|
// isOuter = false;
|
|
//}
|
|
var dataItem = chart.dataProvider[index];
|
|
this.options.onLegendItemClick(dataItem, index, groupType);
|
|
|
|
//this.itemClick(dataItem, isOuter);
|
|
},
|
|
|
|
loadData: function (filter) {
|
|
var that = this;
|
|
if (this.options.onLoadBegin) {
|
|
this.options.onLoadBegin();
|
|
}
|
|
$.post(this.options.dataSourceUrl, filter)
|
|
.done(function (response) {
|
|
var chartData = response,
|
|
total = that.costMode ? chartData.TotalCost : chartData.TotalDuration;
|
|
|
|
that.chartData = chartData;
|
|
that.costData = chartData.CostData;
|
|
that.durationData = chartData.DurationData;
|
|
that.totalCost = chartData.TotalCost;
|
|
that.totalDuration = chartData.TotalDuration;
|
|
that.mode = that.options.mode;
|
|
that.costMode = that.options.costMode;
|
|
|
|
// Set colors for donut sectors manually
|
|
that.setCustomColors(that.costData);
|
|
that.setCustomColors(that.durationData);
|
|
|
|
that.updateData();
|
|
that.updateTotal();
|
|
that.createLegend();
|
|
|
|
if (that.options.onLoadEnd) {
|
|
that.options.onLoadEnd();
|
|
}
|
|
});
|
|
},
|
|
|
|
getData: function () {
|
|
var data = this.costMode ? this.costData : this.durationData
|
|
|
|
return data;
|
|
},
|
|
|
|
createLegend: function () {
|
|
var chart = this.outerDonut,
|
|
data = this.getData(),
|
|
legendDiv = $("#" + this.options.legendDivId),
|
|
allData = [];
|
|
|
|
if (this.mode === "goalType" || this.mode === "typeGoal") {
|
|
allData = this.outerDonut.dataProvider.concat(this.innerDonut.dataProvider);
|
|
} else {
|
|
allData = this.outerDonut.dataProvider;
|
|
}
|
|
|
|
var goals, projectTypes;
|
|
if (this.mode === "goalType" || this.mode === "goal") {
|
|
goals = this.outerDonut.dataProvider;
|
|
projectTypes = this.innerDonut.dataProvider;
|
|
} else if (this.mode == "typeGoal" || this.mode === "type") {
|
|
projectTypes = this.outerDonut.dataProvider;
|
|
goals = this.innerDonut.dataProvider;
|
|
}
|
|
|
|
legendDiv.empty();
|
|
chart.customLegend = legendDiv;
|
|
var legendHtml = "<table class='donut-legend'>",
|
|
colCount = 2;
|
|
|
|
for (var i = 0; i < allData.length; i++) {
|
|
var row = Math.floor(i / colCount),
|
|
col = i % colCount,
|
|
dataItem = allData[i];
|
|
|
|
if (col == 0) {
|
|
legendHtml += "<tr class='donut-legend-item'>";
|
|
}
|
|
var index = dataItem.GroupType == "Goal" ? goals.indexOf(dataItem) : projectTypes.indexOf(dataItem);
|
|
var color = dataItem.PresetColor;
|
|
|
|
if (this.related && (dataItem.Alpha !== undefined)) {
|
|
color = this.getSliceColor(dataItem);
|
|
}
|
|
else {
|
|
if (this.innerDonut.dataProvider.indexOf(dataItem) >= 0) {
|
|
color = this.getColorWithOpacity(dataItem.PresetColor, innerDonutSettings.alpha);
|
|
}
|
|
}
|
|
|
|
legendHtml += this.getLegendItemHtml(dataItem, color, index);
|
|
if (col == colCount - 1) {
|
|
legendHtml += "</tr>";
|
|
}
|
|
}
|
|
legendHtml += "</table>";
|
|
legendDiv.append(legendHtml);
|
|
|
|
var allFilter = this.projectsFilterArray.concat(this.goalsFilterArray);
|
|
$.each(allFilter, function (index, id) {
|
|
$("#legend-item-" + id).css("text-decoration", "underline");
|
|
$("#legend-item-" + id).css("cursor", "pointer");
|
|
});
|
|
|
|
},
|
|
|
|
totalClickHandler: function () {
|
|
if (this.options.onTotalClick && typeof this.options.onTotalClick === "function") {
|
|
this.options.onTotalClick();
|
|
}
|
|
},
|
|
|
|
updateTotal: function () {
|
|
var total = this.costMode ? this.totalCost : this.totalDuration;
|
|
//var valueField = this.costMode ? "Cost" : "Duration";
|
|
//$.each(this.outerDonut.dataProvider, function (index, dataItem) {
|
|
// total += dataItem[valueField];
|
|
//});
|
|
if (this.options.formatTotal && typeof this.options.formatTotal === "function") {
|
|
total = this.options.formatTotal(total);
|
|
}
|
|
if (this.innerDonut.allLabels.length == 0) {
|
|
var fn = "javascript:$('#" + this.containerId + "').data('donutChart').totalClickHandler();";
|
|
this.innerDonut.addLabel(null, "46%", "Total", "center", 14, "#000000", 0, 1, false, fn);
|
|
this.innerDonut.addLabel(null, "53%", total, "center", 12, "#000000", 0, 1, true, fn);
|
|
this.outerDonut.addLabel(null, "46%", "Total", "center", 14, "#000000", 0, 1, false, fn);
|
|
this.outerDonut.addLabel(null, "53%", total, "center", 12, "#000000", 0, 1, true, fn);
|
|
} else {
|
|
this.innerDonut.allLabels[1].text = total;
|
|
this.outerDonut.allLabels[2].text = total;
|
|
}
|
|
this.innerDonut.validateNow();
|
|
this.outerDonut.validateNow();
|
|
},
|
|
|
|
setDonutData: function (donut, data) {
|
|
var valueField = this.costMode ? "Cost" : "Duration";
|
|
|
|
if (data.length > 0 && data[0].Value != undefined && data[0].Value != null) {
|
|
valueField = "Value";
|
|
}
|
|
|
|
this.innerDonut.alphaField = this.related ? "Alpha" : "";
|
|
donut.valueField = valueField;
|
|
donut.dataProvider = data;
|
|
donut.validateData();
|
|
},
|
|
|
|
updateData: function () {
|
|
var data = this.getData(),
|
|
outerData = [],
|
|
innerData = [];
|
|
|
|
if (this.mode === "goalType") {
|
|
outerData = data.Goals;
|
|
innerData = this.related ? this.getRelatedData(outerData) : data.ProjectTypes;
|
|
} else if (this.mode === "typeGoal") {
|
|
outerData = data.ProjectTypes;
|
|
innerData = this.related ? this.getRelatedData(data.ProjectTypes) : data.Goals;
|
|
} else if (this.mode === "goal") {
|
|
outerData = data.Goals;
|
|
innerData = [];
|
|
} else if (this.mode === "type") {
|
|
outerData = data.ProjectTypes;
|
|
innerData = [];
|
|
}
|
|
|
|
this.setDonutData(this.outerDonut, outerData);
|
|
this.setDonutData(this.innerDonut, innerData);
|
|
},
|
|
|
|
getRelatedData: function (outerData) {
|
|
var result = [];
|
|
var costMode = this.costMode;
|
|
|
|
$.each(outerData, function (i, parent) {
|
|
var childItems = parent.ChildItems;
|
|
var alpha = 0.8;
|
|
var colorAlphaStep = (alpha - 0.2) / childItems.length;
|
|
var baseColor = parent.PresetColor;
|
|
|
|
childItems = childItems.sort(function (a, b) {
|
|
if (b.Label == "Other") {
|
|
return -2;
|
|
}
|
|
if (a.Label == "Other") {
|
|
return 2;
|
|
}
|
|
var field = costMode ? "Cost" : "Duration";
|
|
|
|
return b[field] == a[field] ? 0 : ((b[field] > a[field]) ? 1 : -1);
|
|
});
|
|
|
|
var otherDataItem = null;
|
|
|
|
$.each(childItems, function (j, child) {
|
|
child.Alpha = alpha;
|
|
child.PresetColor = baseColor;
|
|
alpha -= colorAlphaStep;
|
|
|
|
if (child.PieDatas && (child.PieDatas.length > 0)) {
|
|
// Among child items of current outer sector we found Other element
|
|
otherDataItem = child;
|
|
}
|
|
});
|
|
|
|
if (otherDataItem != null) {
|
|
// Set colors to items, packed into Other element in inner donut
|
|
var otherInnerAlpha = 0.8;
|
|
var otherInnerAlphaStep = (otherInnerAlpha - 0.2) / otherDataItem.PieDatas.length;
|
|
|
|
$.each(otherDataItem.PieDatas, function (j, otherItemChild) {
|
|
otherItemChild.Alpha = otherInnerAlpha;
|
|
otherItemChild.PresetColor = baseColor;
|
|
otherInnerAlpha -= otherInnerAlphaStep;
|
|
});
|
|
}
|
|
|
|
result = $.merge(result, childItems || []);
|
|
});
|
|
|
|
return result;
|
|
},
|
|
|
|
setCustomColors: function (data) {
|
|
if (!data || !this.options || !this.options.colors)
|
|
return;
|
|
|
|
if (this.options.colors.length < 1)
|
|
return;
|
|
|
|
if (this.lastUsedPaletteColor === undefined)
|
|
this.lastUsedPaletteColor = -1;
|
|
|
|
var otherDataItem = null;
|
|
|
|
if (data.Goals && (data.Goals.length > 0)) {
|
|
// Set colors for general Goals sectors
|
|
for (var index = 0; index < data.Goals.length; index++) {
|
|
var itemId = data.Goals[index].GoalId;
|
|
this.setItemColor(itemId, data.Goals[index]);
|
|
|
|
if (data.Goals[index].PieDatas && (data.Goals[index].PieDatas.length > 0)) {
|
|
otherDataItem = data.Goals[index];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (data.OtherGoals && (data.OtherGoals.length > 0)) {
|
|
// Set colors for Goals sectors, packed into Other sector
|
|
for (var index = 0; index < data.OtherGoals.length; index++) {
|
|
var itemId = data.OtherGoals[index].GoalId;
|
|
this.setItemColor(itemId, data.OtherGoals[index]);
|
|
}
|
|
}
|
|
|
|
if (otherDataItem != null) {
|
|
// Set colors for Goals sectors, packed into Other sector (alternative data struct)
|
|
for (var index = 0; index < otherDataItem.PieDatas.length; index++) {
|
|
var itemId = otherDataItem.PieDatas[index].GoalId;
|
|
this.setItemColor(itemId, otherDataItem.PieDatas[index]);
|
|
}
|
|
otherDataItem = null;
|
|
}
|
|
|
|
if (data.ProjectTypes && (data.ProjectTypes.length > 0)) {
|
|
// Set colors for general ProjectTypes sectors
|
|
for (var index = 0; index < data.ProjectTypes.length; index++) {
|
|
var itemId = data.ProjectTypes[index].ProjectTypeId;
|
|
this.setItemColor(itemId, data.ProjectTypes[index]);
|
|
|
|
if (data.ProjectTypes[index].PieDatas && (data.ProjectTypes[index].PieDatas.length > 0)) {
|
|
otherDataItem = data.ProjectTypes[index];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (data.OtherProjectTypes && (data.OtherProjectTypes.length > 0)) {
|
|
// Set colors for ProjectTypes sectors, packed into Other sector
|
|
for (var index = 0; index < data.OtherProjectTypes.length; index++) {
|
|
var itemId = data.OtherProjectTypes[index].ProjectTypeId;
|
|
this.setItemColor(itemId, data.OtherProjectTypes[index]);
|
|
}
|
|
}
|
|
|
|
if (otherDataItem != null) {
|
|
// Set colors for ProjectTypes sectors, packed into Other sector (alternative data struct)
|
|
for (var index = 0; index < otherDataItem.PieDatas.length; index++) {
|
|
var itemId = otherDataItem.PieDatas[index].ProjectTypeId;
|
|
this.setItemColor(itemId, otherDataItem.PieDatas[index]);
|
|
}
|
|
}
|
|
},
|
|
|
|
// Sets color for individual sector. Uses cached data (if found) or picks the next color from palette
|
|
setItemColor: function (itemId, item) {
|
|
if (Object.keys(this.colorCache).indexOf(itemId) >= 0)
|
|
item.PresetColor = this.colorCache[itemId];
|
|
else {
|
|
this.lastUsedPaletteColor++;
|
|
var itemColor = getColor(this.options.colors, this.lastUsedPaletteColor);
|
|
item.PresetColor = itemColor;
|
|
this.colorCache[itemId] = itemColor;
|
|
}
|
|
},
|
|
|
|
setMode: function (mode) {
|
|
this.mode = mode;
|
|
this.updateData();
|
|
this.updateTotal();
|
|
this.createLegend();
|
|
},
|
|
|
|
setCostMode: function (costMode) {
|
|
this.costMode = costMode;
|
|
this.updateData();
|
|
this.updateTotal();
|
|
this.createLegend();
|
|
},
|
|
|
|
setRelation: function (related) {
|
|
this.related = related;
|
|
this.updateData();
|
|
this.createLegend();
|
|
},
|
|
|
|
itemClick_: function (dataContext, isOuter) {
|
|
var isOtherClicked = dataContext.PieDatas != null && dataContext.PieDatas.length > 0;
|
|
this.singleSliceData = [dataContext];
|
|
if (isOtherClicked) {
|
|
this.showOtherMode = true;
|
|
} else {
|
|
this.singleSliceMode = true;
|
|
}
|
|
if (isOuter) {
|
|
this.singleOuterSliceMode = true;
|
|
this.singleInnerSliceMode = false;
|
|
} else {
|
|
this.singleOuterSliceMode = false;
|
|
this.singleInnerSliceMode = true;
|
|
}
|
|
this.updateData();
|
|
this.createLegend();
|
|
},
|
|
|
|
itemClick: function (dataContext, isOuter) {
|
|
if (dataContext.Label == "No Goal") {
|
|
return null;
|
|
}
|
|
var array = [dataContext];
|
|
var donut = $("#chartDonut1").data("donutChart");
|
|
if (dataContext.PieDatas != null && dataContext.PieDatas.length > 0) {
|
|
var data = this.costMode ? this.costData : this.durationData;
|
|
|
|
if (this.mode === "goalType") {
|
|
if (isOuter) {
|
|
clickOther(data.Goals, this.outerDonut, dataContext.PieDatas);
|
|
if (this.related) {
|
|
var childrenData = this.getRelatedData(dataContext.PieDatas);
|
|
this.setDonutData(this.innerDonut, childrenData);
|
|
}
|
|
} else {
|
|
disableDonutReload = true;
|
|
clickOther(data.ProjectTypes, this.innerDonut, dataContext.PieDatas);
|
|
}
|
|
} else if (this.mode === "typeGoal") {
|
|
if (isOuter) {
|
|
clickOther(data.ProjectTypes, this.outerDonut, dataContext.PieDatas);
|
|
if (this.related) {
|
|
var childrenData = this.getRelatedData(dataContext.PieDatas);
|
|
this.setDonutData(this.innerDonut, childrenData);
|
|
}
|
|
} else {
|
|
disableDonutReload = true;
|
|
clickOther(data.Goals, this.innerDonut, dataContext.PieDatas);
|
|
}
|
|
} else if (this.mode === "goal") {
|
|
clickOther(data.Goals, this.outerDonut, dataContext.PieDatas);
|
|
} else if (this.mode === "type") {
|
|
clickOther(data.ProjectTypes, this.outerDonut, dataContext.PieDatas);
|
|
}
|
|
}
|
|
else {
|
|
var typeId = dataContext.TypeId;
|
|
var itemColor = null;
|
|
|
|
if (typeId.length > 0) {
|
|
if (isOuter) {
|
|
for (var i = 0; i < donut.outerDonut.chartData.length; i++) {
|
|
if (typeId == donut.outerDonut.chartData[i].dataContext.TypeId[0]) {
|
|
itemColor = donut.outerDonut.chartData[i].dataContext.PresetColor;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (itemColor == null) {
|
|
this.lastUsedPaletteColor++;
|
|
itemColor = getColor(this.options.colors, this.lastUsedPaletteColor);
|
|
}
|
|
|
|
array[0].PresetColor = itemColor;
|
|
array[0].Alpha = 1;
|
|
|
|
this.setDonutData(this.outerDonut, array);
|
|
|
|
if (this.related) {
|
|
var childrenData = this.getRelatedData(array, itemColor);
|
|
this.setDonutData(this.innerDonut, childrenData);
|
|
}
|
|
} else {
|
|
if (!this.related) {
|
|
for (var i = 0; i < donut.innerDonut.chartData.length; i++) {
|
|
if (typeId == donut.innerDonut.chartData[i].dataContext.TypeId[0]) {
|
|
//colorIndex = colorIndex + i;
|
|
itemColor = donut.innerDonut.chartData[i].dataContext.PresetColor;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (itemColor == null) {
|
|
this.lastUsedPaletteColor++;
|
|
itemColor = getColor(this.options.colors, this.lastUsedPaletteColor);
|
|
}
|
|
|
|
array[0].PresetColor = itemColor;
|
|
array[0].Alpha = 1;
|
|
this.setDonutData(this.innerDonut, array);
|
|
} else {
|
|
//TODO: inner slice clicked. filter data here if don't want to reload data from server
|
|
}
|
|
}
|
|
|
|
var groupType = dataContext.GroupType;
|
|
this.options.addFilter(typeId[0], groupType);
|
|
}
|
|
}
|
|
this.createLegend();
|
|
this.updateTotal();
|
|
},
|
|
|
|
getSliceColor: function (dataItem) {
|
|
var color = dataItem.PresetColor;
|
|
var rgb = hexToRgb(dataItem.PresetColor);
|
|
color = "rgba(" + rgb.r + "," + rgb.g + "," + rgb.b + "," + dataItem.Alpha + ")";
|
|
|
|
return color;
|
|
},
|
|
|
|
getColorWithOpacity: function (color, opacity) {
|
|
var rgb = hexToRgb(color);
|
|
var result = "rgba(" +rgb.r + "," +rgb.g + "," + rgb.b + "," + opacity + ")";
|
|
return result;
|
|
},
|
|
|
|
changeFromFilterPanel: function () {
|
|
var curentData = this.getData();
|
|
|
|
var goals = getChartDataItems("Goal", this.goalsFilterArray, curentData);
|
|
var projectType = getChartDataItems("ProjectType", this.projectsFilterArray, curentData);
|
|
if (!this.related) {
|
|
if (this.mode === "goalType") {
|
|
if (goals.length > 0) {
|
|
this.setDonutData(this.outerDonut, goals);
|
|
} else {
|
|
this.setDonutData(this.outerDonut, curentData.Goals);
|
|
}
|
|
if (projectType.length > 0) {
|
|
this.setDonutData(this.innerDonut, projectType);
|
|
} else {
|
|
this.setDonutData(this.innerDonut, curentData.ProjectTypes);
|
|
}
|
|
} else if (this.mode === "typeGoal") {
|
|
if (projectType.length > 0) {
|
|
this.setDonutData(this.outerDonut, projectType);
|
|
} else {
|
|
this.setDonutData(this.outerDonut, curentData.ProjectTypes);
|
|
}
|
|
if (goals.length > 0) {
|
|
this.setDonutData(this.innerDonut, goals);
|
|
} else {
|
|
this.setDonutData(this.innerDonut, curentData.Goals);
|
|
}
|
|
} else if (this.mode === "goal") {
|
|
if (goals.length > 0) {
|
|
this.setDonutData(this.outerDonut, goals);
|
|
} else {
|
|
this.setDonutData(this.outerDonut, curentData.Goals);
|
|
}
|
|
} else if (this.mode === "type") {
|
|
if (projectType.length > 0) {
|
|
this.setDonutData(this.outerDonut, projectType);
|
|
} else {
|
|
this.setDonutData(this.outerDonut, curentData.ProjectTypes);
|
|
}
|
|
}
|
|
} else {
|
|
if (this.mode === "goalType") {
|
|
if (goals.length > 0) {
|
|
var outerCollection = projectType.length > 0 ? getRelatedItemsOuther(goals, projectType) : curentData.Goals;
|
|
this.setDonutData(this.outerDonut, outerCollection);
|
|
} else {
|
|
var outerCollection = goals.length > 0 ? getRelatedItemsOuther(projectType, curentData.Goals) : curentData.Goals;
|
|
this.setDonutData(this.outerDonut, outerCollection);
|
|
}
|
|
|
|
var childItems = this.getRelatedData(outerCollection);
|
|
|
|
if (projectType.length > 0) {
|
|
var collection = getRelatedItemsInner(outerCollection, projectType, this);
|
|
|
|
this.setDonutData(this.innerDonut, collection);
|
|
} else {
|
|
this.setDonutData(this.innerDonut, childItems);
|
|
}
|
|
|
|
} else if (this.mode === "typeGoal") {
|
|
if (projectType.length > 0) {
|
|
var outerCollection = goals.length > 0 ? getRelatedItemsOuther(projectType, goals) : curentData.ProjectTypes;
|
|
this.setDonutData(this.outerDonut, outerCollection);
|
|
} else {
|
|
var outerCollection = goals.length > 0 ? getRelatedItemsOuther(curentData.ProjectTypes, goals) : curentData.ProjectTypes;
|
|
this.setDonutData(this.outerDonut, outerCollection);
|
|
}
|
|
|
|
var childItems = this.getRelatedData(outerCollection);
|
|
|
|
if (goals.length > 0) {
|
|
var collection = getRelatedItemsInner(outerCollection, goals, this);
|
|
|
|
this.setDonutData(this.innerDonut, collection);
|
|
} else {
|
|
this.setDonutData(this.innerDonut, childItems);
|
|
}
|
|
} else if (this.mode === "goal") {
|
|
if (goals.length > 0) {
|
|
this.setDonutData(this.outerDonut, goals);
|
|
} else {
|
|
this.setDonutData(this.outerDonut, curentData.Goals);
|
|
}
|
|
} else if (this.mode === "type") {
|
|
if (projectType.length > 0) {
|
|
this.setDonutData(this.outerDonut, projectType);
|
|
} else {
|
|
this.setDonutData(this.outerDonut, curentData.ProjectTypes);
|
|
}
|
|
}
|
|
}
|
|
this.createLegend();
|
|
}
|
|
};
|
|
|
|
function getProjectTypesForGoal(allProjectTypes, goal) {
|
|
var projectTypes = [];
|
|
$.each(allProjectTypes, function (i, projectType) {
|
|
if (goal.ChildItems) {
|
|
projectTypes = goal.ChildItems.filter(function (item) {
|
|
return item.TypeId[0] == projectType.TypeId[0];
|
|
});
|
|
}
|
|
});
|
|
|
|
return projectTypes;
|
|
};
|
|
|
|
function formatLegendLabel(label) {
|
|
if (label.length > 15) {
|
|
label = label.substring(0, 15) + "...";
|
|
}
|
|
|
|
return label;
|
|
}
|
|
|
|
function getColor(palette, index) {
|
|
return palette[index % (palette.length - 1)];
|
|
}
|
|
|
|
function getBalloonText(graphDataItem, containerId) {
|
|
//no more ways to get costMode
|
|
var chart = $("#" + containerId).data('donutChart');
|
|
var costMode = chart.costMode;
|
|
var unit = chart.costMode ? "$" : "";
|
|
with (graphDataItem) {
|
|
var valueStr = unit + (chart.costMode ? dataContext.Cost.formatNumber() : dataContext.Duration.formatNumber());
|
|
var balloonText = dataContext.Label + "<br /><b>" + valueStr + "</b> (" + percents.formatNumber() + "%)";
|
|
return balloonText;
|
|
}
|
|
};
|
|
|
|
function getChart(groupType, item) {
|
|
var chart;
|
|
if (groupType == "Goal" && item.mode == "typeGoal") {
|
|
chart = item.innerDonut;
|
|
} else if (groupType == "ProjectType" && item.mode == "goalType") {
|
|
chart = item.innerDonut;
|
|
} else {
|
|
chart = item.outerDonut;
|
|
}
|
|
return chart;
|
|
};
|
|
|
|
function getChartDataItems(groupType, collection, data) {
|
|
var dataCollection = [];
|
|
if (groupType == "Goal") {
|
|
for (var i = 0; i <= data.Goals.length - 1; i++) {
|
|
if (collection.includes(data.Goals[i].TypeId[0]) && data.Goals[i].PieDatas == null) {
|
|
dataCollection.push(data.Goals[i]);
|
|
}
|
|
}
|
|
for (var i = 0; i <= data.OtherGoals.length - 1; i++) {
|
|
if (collection.includes(data.OtherGoals[i].TypeId[0])) {
|
|
dataCollection.push(data.OtherGoals[i]);
|
|
}
|
|
}
|
|
} else if (groupType == "ProjectType") {
|
|
for (var i = 0; i <= data.ProjectTypes.length - 1; i++) {
|
|
if (collection.includes(data.ProjectTypes[i].TypeId[0]) && data.ProjectTypes[i].PieDatas == null) {
|
|
dataCollection.push(data.ProjectTypes[i]);
|
|
}
|
|
}
|
|
for (var i = 0; i <= data.OtherProjectTypes.length - 1; i++) {
|
|
if (collection.includes(data.OtherProjectTypes[i].TypeId[0])) {
|
|
dataCollection.push(data.OtherProjectTypes[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return dataCollection;
|
|
};
|
|
|
|
function getRelatedItemsOuther(mainCollection, relatedCollection) {
|
|
var returnCollection = [];
|
|
for (var i = 0; i < mainCollection.length; i++) {
|
|
var isContains = false;
|
|
for (var j = 0; j < mainCollection[i].ChildItems.length; j++) {
|
|
for (var k = 0; k < relatedCollection.length; k++) {
|
|
if (relatedCollection[k].TypeId[0] == mainCollection[i].ChildItems[j].TypeId[0]) {
|
|
isContains = true;
|
|
}
|
|
}
|
|
}
|
|
if (isContains) {
|
|
returnCollection.push(mainCollection[i]);
|
|
}
|
|
}
|
|
return returnCollection;
|
|
};
|
|
|
|
function getRelatedItemsInner(mainCollection, relatedCollection, thet) {
|
|
var returnCollection = [];
|
|
var values = [];
|
|
for (var i = 0; i < mainCollection.length; i++) {
|
|
var mainValue = thet.costMode ? mainCollection[i].Cost : mainCollection[i].Duration;
|
|
var childCollection = [];
|
|
for (var j = 0; j < mainCollection[i].ChildItems.length; j++) {
|
|
for (var k = 0; k < relatedCollection.length; k++) {
|
|
if (relatedCollection[k].TypeId[0] == mainCollection[i].ChildItems[j].TypeId[0]) {
|
|
var str = JSON.stringify(mainCollection[i].ChildItems[j]);
|
|
var obj = JSON.parse(str);
|
|
childCollection.push(obj);
|
|
}
|
|
}
|
|
}
|
|
var childValueSumm = 0;
|
|
for (var m = 0; m < childCollection.length; m++) {
|
|
var curentVal = thet.costMode ? childCollection[m].Cost : childCollection[m].Duration;
|
|
childValueSumm += curentVal;
|
|
}
|
|
var dif = mainValue / childValueSumm;
|
|
for (var m = 0; m < childCollection.length; m++) {
|
|
var childValue = thet.costMode ? childCollection[m].Cost : childCollection[m].Duration;
|
|
values.push(childValue * dif);
|
|
}
|
|
returnCollection = returnCollection.concat(childCollection);
|
|
}
|
|
for (var l = 0; l < returnCollection.length; l++) {
|
|
returnCollection[l].Value = values[l];
|
|
}
|
|
return returnCollection;
|
|
};
|
|
|
|
function clickOther(data, donut, otherDatas) {
|
|
var donutData = otherDatas.slice();
|
|
//donutData.splice(donutData.length - 1, 1);
|
|
//donutData = donutData.concat(otherDatas);
|
|
var donutElement = $("#chartDonut1").data("donutChart");
|
|
donutElement.setDonutData(donut, donutData);
|
|
};
|
|
|
|
function hexToRgb(hex) {
|
|
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
return result ? {
|
|
r: parseInt(result[1], 16),
|
|
g: parseInt(result[2], 16),
|
|
b: parseInt(result[3], 16)
|
|
} : null;
|
|
};
|
|
|
|
//http://www.amcharts.com/tips/nested-donut-chart/
|
|
function initChartHandler(chart) {
|
|
// init holder for nested charts
|
|
if (AmCharts.nestedChartHolder === undefined)
|
|
AmCharts.nestedChartHolder = {};
|
|
|
|
if (chart.bringToFront === true) {
|
|
chart.addListener("init", function (event) {
|
|
// chart inited
|
|
var chart = event.chart;
|
|
var div = chart.div;
|
|
var parent = div.parentNode;
|
|
|
|
// add to holder
|
|
if (AmCharts.nestedChartHolder[parent] === undefined)
|
|
AmCharts.nestedChartHolder[parent] = [];
|
|
AmCharts.nestedChartHolder[parent].push(chart);
|
|
|
|
chart.div.addEventListener('mousemove', function (event) {
|
|
var x = Math.abs(chart.mouseX - (chart.realWidth / 2));
|
|
var y = Math.abs(chart.mouseY - (chart.realHeight / 2));
|
|
var r = Math.sqrt(x * x + y * y);
|
|
//TODO: refactor
|
|
if (r < 70) {
|
|
$("#donutClearFilterBtn").show();
|
|
}
|
|
});
|
|
|
|
chart.div.addEventListener('mouseleave', function (event) {
|
|
//TODO: hide when mouse will out of circle with radius equals to inner radius of ther inner donut
|
|
if (event.relatedTarget && event.relatedTarget.id != "donutClearFilterBtn") {
|
|
$("#donutClearFilterBtn").hide();
|
|
}
|
|
});
|
|
|
|
// add mouse move event
|
|
chart.div.addEventListener('mousemove', function () {
|
|
// calculate current radius
|
|
var x = Math.abs(chart.mouseX -(chart.realWidth / 2));
|
|
var y = Math.abs(chart.mouseY -(chart.realHeight / 2));
|
|
var r = Math.sqrt(x * x +y * y);
|
|
|
|
// check which chart smallest chart still matches this radius
|
|
var smallChart;
|
|
var smallRadius;
|
|
for (var i = 0; i < AmCharts.nestedChartHolder[parent].length; i++) {
|
|
var checkChart = AmCharts.nestedChartHolder[parent][i];
|
|
|
|
if ((checkChart.radiusReal < r) || (smallRadius < checkChart.radiusReal)) {
|
|
checkChart.div.style.zIndex = 1;
|
|
}
|
|
else {
|
|
if (smallChart !== undefined)
|
|
smallChart.div.style.zIndex = 1;
|
|
checkChart.div.style.zIndex = 2;
|
|
smallChart = checkChart;
|
|
smallRadius = checkChart.radiusReal;
|
|
}
|
|
|
|
}
|
|
}, false);
|
|
});
|
|
}
|
|
}
|
|
|
|
$.fn.donutChart = function (option, args) {
|
|
return this.each(function () {
|
|
var $this = $(this),
|
|
data = $this.data('donutChart'),
|
|
options = $.extend({}, $.fn.donutChart.defaults, $this.data(), typeof option === 'object' && option);
|
|
|
|
if (!data) $this.data('donutChart', (data = new DonutChart(this, options)));
|
|
if (typeof option === 'string')
|
|
data[option].apply(data, [].concat(args));
|
|
});
|
|
};
|
|
$.fn.donutChart.defaults = {
|
|
costMode: true
|
|
};
|
|
|
|
$.fn.donutChart.Constructor = DonutChart;
|
|
}(jQuery));
|
|
|
|
if (!Array.prototype.includes) {
|
|
Array.prototype.includes = function (searchElement /*, fromIndex*/) {
|
|
'use strict';
|
|
var O = Object(this);
|
|
var len = parseInt(O.length, 10) || 0;
|
|
if (len === 0) {
|
|
return false;
|
|
}
|
|
var n = parseInt(arguments[1], 10) || 0;
|
|
var k;
|
|
if (n >= 0) {
|
|
k = n;
|
|
} else {
|
|
k = len + n;
|
|
if (k < 0) { k = 0; }
|
|
}
|
|
var currentElement;
|
|
while (k < len) {
|
|
currentElement = O[k];
|
|
if (searchElement === currentElement ||
|
|
(searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN
|
|
return true;
|
|
}
|
|
k++;
|
|
}
|
|
return false;
|
|
};
|
|
} |