For now we're only adding rules related to spacing and double quotes, following the same rules we use in Ruby, which are the same rules CoffeeScript followed when compiling these files. We're also using the recommended ESLint rules, which will warn us about many JavaScript common pitfalls, the `strict` rule which enforces using strict mode, and the `no-console` rule, which will prevent us from shipping code meant for debugging. Although it's arguably more common to use the JSON format to define these rules, I've chosen YAML because it's the format we use in all our linters.
306 lines
8.0 KiB
JavaScript
306 lines
8.0 KiB
JavaScript
//= require d3
|
|
//= require c3
|
|
//= require c3ext
|
|
|
|
// TODO: This module is complex enough to require its own tests. Rewrite it using Ecma6 class syntax and
|
|
// write tests for this feature after consul has been migrated to Rails 5.1
|
|
(function() {
|
|
"use strict";
|
|
|
|
var ProposalGraph = function(url) {
|
|
this.url = url;
|
|
this.successfulProposalDataUrl = null;
|
|
this.proposalAchievementsUrl = null;
|
|
this.targetId = null;
|
|
this.groupBy = null;
|
|
this.proposalSuccess = null;
|
|
this.maximumValue = 0;
|
|
this.progressLabel = "Progress";
|
|
this.supportsLabel = "Supports";
|
|
this.successLabel = "Success";
|
|
this.goals = null;
|
|
this.achievements = null;
|
|
this.xColumnValues = null;
|
|
this.progressColumnValues = null;
|
|
this.resourcesUrl = null;
|
|
};
|
|
|
|
ProposalGraph.prototype.refresh = function() {
|
|
this.refreshGoals()
|
|
.then(this.refreshData.bind(this))
|
|
.then(this.refreshSuccessfulData.bind(this))
|
|
.then(this.refreshAchievements.bind(this))
|
|
.done(this.draw.bind(this));
|
|
};
|
|
|
|
ProposalGraph.prototype.refreshGoals = function() {
|
|
return $.ajax({
|
|
url: this.resourcesUrl,
|
|
cache: false,
|
|
success: function(data) {
|
|
this.parseGoals(data);
|
|
}.bind(this)
|
|
});
|
|
};
|
|
|
|
ProposalGraph.prototype.parseGoals = function(data) {
|
|
this.goals = data.map(function(item) {
|
|
return {
|
|
value: item.required_supports,
|
|
text: item.title
|
|
};
|
|
});
|
|
};
|
|
|
|
ProposalGraph.prototype.refreshData = function() {
|
|
return $.ajax({
|
|
url: this.url,
|
|
cache: false,
|
|
success: function(data) {
|
|
this.parseData(data);
|
|
}.bind(this),
|
|
data: {
|
|
group_by: this.groupBy
|
|
}
|
|
});
|
|
};
|
|
|
|
ProposalGraph.prototype.parseData = function(data) {
|
|
var key;
|
|
|
|
this.xColumnValues = [];
|
|
this.progressColumnValues = [this.progressLabel];
|
|
|
|
for (key in data) {
|
|
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
this.xColumnValues.push(key);
|
|
this.progressColumnValues.push(data[key]);
|
|
|
|
if (this.maximumValue < data[key]) {
|
|
this.maximumValue = data[key];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
ProposalGraph.prototype.refreshSuccessfulData = function() {
|
|
return $.ajax({
|
|
url: this.successfulProposalDataUrl,
|
|
cache: false,
|
|
success: function(data) {
|
|
this.parseSuccessfulProposalData(data);
|
|
}.bind(this),
|
|
data: {
|
|
group_by: this.groupBy
|
|
}
|
|
});
|
|
};
|
|
|
|
ProposalGraph.prototype.parseSuccessfulProposalData = function(data) {
|
|
var key;
|
|
|
|
this.successfulColumnValues = [this.successLabel];
|
|
|
|
for (key in data) {
|
|
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
this.addXColumnValue(key);
|
|
this.successfulColumnValues.push(data[key]);
|
|
}
|
|
}
|
|
};
|
|
|
|
ProposalGraph.prototype.refreshAchievements = function() {
|
|
return $.ajax({
|
|
url: this.proposalAchievementsUrl,
|
|
cache: false,
|
|
success: function(data) {
|
|
this.parseAchievements(data);
|
|
}.bind(this),
|
|
data: {
|
|
group_by: this.groupBy
|
|
}
|
|
});
|
|
};
|
|
|
|
ProposalGraph.prototype.parseAchievements = function(data) {
|
|
var group;
|
|
|
|
this.achievements = [];
|
|
for (group in data) {
|
|
if (Object.prototype.hasOwnProperty.call(data, group)) {
|
|
this.addXColumnValue(group);
|
|
this.achievements.push({
|
|
value: this.formatGroup(group),
|
|
text: data[group].title,
|
|
position: "start"
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
ProposalGraph.prototype.addXColumnValue = function(value) {
|
|
if (this.xColumnValues.indexOf(value) === -1) {
|
|
this.xColumnValues.push(value);
|
|
}
|
|
};
|
|
|
|
ProposalGraph.prototype.draw = function() {
|
|
var colors = {},
|
|
maximumValue = this.maximumValue === 0 ? this.proposalSuccess : Math.round(this.maximumValue * 1.10);
|
|
|
|
this.formatXColumnValues();
|
|
|
|
colors[this.progressColumnValues[0]] = "#004a83";
|
|
colors[this.successfulColumnValues[0]] = "#ff7f0e";
|
|
|
|
c3.generate({
|
|
bindto: "#" + this.targetId,
|
|
data: {
|
|
x: "x",
|
|
columns: [
|
|
this.xColumnValues,
|
|
this.progressColumnValues,
|
|
this.successfulColumnValues
|
|
],
|
|
colors: colors,
|
|
color: function(color, d) {
|
|
var achievement;
|
|
|
|
if (d.id === this.successfulColumnValues[0] || !Object.prototype.hasOwnProperty.call(d, "x")) {
|
|
return color;
|
|
}
|
|
|
|
achievement = this.achievements.find(function(element) {
|
|
return element.value === this.xColumnValues[d.index + 1];
|
|
}.bind(this));
|
|
|
|
if (achievement !== undefined) {
|
|
return "#ff0000";
|
|
}
|
|
|
|
return color;
|
|
}.bind(this)
|
|
},
|
|
axis: {
|
|
y: {
|
|
tick: {
|
|
values: this.tickYValues()
|
|
},
|
|
min: (this.maximumValue === 0 ? Math.round(this.proposalSuccess * 0.10) : 0),
|
|
max: maximumValue,
|
|
label: {
|
|
text: this.supportsLabel,
|
|
position: "outer-middle"
|
|
}
|
|
},
|
|
x: {
|
|
type: "category",
|
|
tick: {
|
|
values: this.tickXValues(),
|
|
centered: true
|
|
}
|
|
}
|
|
},
|
|
grid: {
|
|
y: {
|
|
lines: this.goals
|
|
}
|
|
},
|
|
zoom: {
|
|
enabled: true
|
|
},
|
|
tooltip: {
|
|
format: {
|
|
title: function(d) {
|
|
var achievement = this.achievements.find(function(element) {
|
|
return element.value === this.xColumnValues[d + 1];
|
|
}.bind(this));
|
|
|
|
if (achievement !== undefined) {
|
|
return this.xColumnValues[d + 1] + ": " + achievement.text;
|
|
}
|
|
|
|
return this.xColumnValues[d + 1];
|
|
}.bind(this)
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
ProposalGraph.prototype.tickYValues = function() {
|
|
var i,
|
|
tick = [0],
|
|
maximumValue = this.maximumValue === 0 ? this.proposalSuccess : Math.round(this.maximumValue * 1.10),
|
|
step = maximumValue <= 10 ? 1 : Math.round(maximumValue / 10);
|
|
|
|
for (i = step; i < maximumValue; i += step) {
|
|
tick.push(i);
|
|
}
|
|
|
|
tick.push(maximumValue);
|
|
|
|
return tick;
|
|
};
|
|
|
|
ProposalGraph.prototype.tickXValues = function() {
|
|
var i,
|
|
l,
|
|
tick = [],
|
|
step = this.xColumnValues.length < 13 ? 1 : Math.round((this.xColumnValues.length - 1) / 12);
|
|
|
|
if (this.xColumnValues.length > 1) {
|
|
tick.push(0);
|
|
|
|
for (i = step, l = this.xColumnValues.length - 1; i < l; i += step) {
|
|
tick.push(i);
|
|
}
|
|
}
|
|
|
|
return tick;
|
|
};
|
|
|
|
ProposalGraph.prototype.formatXColumnValues = function() {
|
|
var i, l;
|
|
|
|
this.xColumnValues = this.xColumnValues.sort();
|
|
|
|
if (this.isDailyGrouped()) {
|
|
for (i = 0, l = this.xColumnValues.length; i < l; i += 1) {
|
|
this.xColumnValues[i] = this.formatGroup(this.xColumnValues[i]);
|
|
}
|
|
}
|
|
|
|
this.xColumnValues.unshift("x");
|
|
};
|
|
|
|
ProposalGraph.prototype.formatGroup = function(group) {
|
|
if (this.isDailyGrouped()) {
|
|
var parts = group.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);
|
|
return parts[2] + "/" + parts[3];
|
|
}
|
|
|
|
return group;
|
|
};
|
|
|
|
ProposalGraph.prototype.isDailyGrouped = function() {
|
|
return this.groupBy === undefined || this.groupBy === "" || this.groupBy === null;
|
|
};
|
|
|
|
$(document).ready(function() {
|
|
$("[data-proposal-graph-url]").each(function() {
|
|
var graph = new ProposalGraph($(this).data("proposal-graph-url"));
|
|
graph.successfulProposalDataUrl = $(this).data("successful-proposal-graph-url");
|
|
graph.proposalAchievementsUrl = $(this).data("proposal-achievements-url");
|
|
graph.targetId = $(this).attr("id");
|
|
graph.groupBy = $(this).data("proposal-graph-group-by");
|
|
graph.progressLabel = $(this).data("proposal-graph-progress-label");
|
|
graph.supportsLabel = $(this).data("proposal-graph-supports-label");
|
|
graph.successLabel = $(this).data("proposal-graph-success-label");
|
|
graph.proposalSuccess = parseInt($(this).data("proposal-success"), 10);
|
|
graph.resourcesUrl = $(this).data("proposal-resources-url");
|
|
|
|
graph.refresh();
|
|
});
|
|
});
|
|
})();
|