summaryrefslogtreecommitdiffstats
path: root/public/vendor/plugins/calendar-heatmap/calendar-heatmap.js
diff options
context:
space:
mode:
Diffstat (limited to 'public/vendor/plugins/calendar-heatmap/calendar-heatmap.js')
-rw-r--r--public/vendor/plugins/calendar-heatmap/calendar-heatmap.js311
1 files changed, 311 insertions, 0 deletions
diff --git a/public/vendor/plugins/calendar-heatmap/calendar-heatmap.js b/public/vendor/plugins/calendar-heatmap/calendar-heatmap.js
new file mode 100644
index 0000000000..f380fa287a
--- /dev/null
+++ b/public/vendor/plugins/calendar-heatmap/calendar-heatmap.js
@@ -0,0 +1,311 @@
+// https://github.com/DKirwan/calendar-heatmap
+
+function calendarHeatmap() {
+ // defaults
+ var width = 750;
+ var height = 110;
+ var legendWidth = 150;
+ var selector = 'body';
+ var SQUARE_LENGTH = 11;
+ var SQUARE_PADDING = 2;
+ var MONTH_LABEL_PADDING = 6;
+ var now = moment().endOf('day').toDate();
+ var yearAgo = moment().startOf('day').subtract(1, 'year').toDate();
+ var startDate = null;
+ var counterMap= {};
+ var data = [];
+ var max = null;
+ var colorRange = ['#D8E6E7', '#218380'];
+ var tooltipEnabled = true;
+ var tooltipUnit = 'contribution';
+ var legendEnabled = true;
+ var onClick = null;
+ var weekStart = 1; //0 for Sunday, 1 for Monday
+ var locale = {
+ months: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
+ days: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
+ No: 'No',
+ on: 'on',
+ Less: 'Less',
+ More: 'More'
+ };
+ var v = Number(d3.version.split('.')[0]);
+
+ // setters and getters
+ chart.data = function (value) {
+ if (!arguments.length) { return data; }
+ data = value;
+
+ counterMap= {};
+
+ data.forEach(function (element, index) {
+ var key= moment(element.date).format( 'YYYY-MM-DD' );
+ var counter= counterMap[key] || 0;
+ counterMap[key]= counter + element.count;
+ });
+
+ return chart;
+ };
+
+ chart.max = function (value) {
+ if (!arguments.length) { return max; }
+ max = value;
+ return chart;
+ };
+
+ chart.selector = function (value) {
+ if (!arguments.length) { return selector; }
+ selector = value;
+ return chart;
+ };
+
+ chart.startDate = function (value) {
+ if (!arguments.length) { return startDate; }
+ yearAgo = value;
+ now = moment(value).endOf('day').add(1, 'year').toDate();
+ return chart;
+ };
+
+ chart.colorRange = function (value) {
+ if (!arguments.length) { return colorRange; }
+ colorRange = value;
+ return chart;
+ };
+
+ chart.tooltipEnabled = function (value) {
+ if (!arguments.length) { return tooltipEnabled; }
+ tooltipEnabled = value;
+ return chart;
+ };
+
+ chart.tooltipUnit = function (value) {
+ if (!arguments.length) { return tooltipUnit; }
+ tooltipUnit = value;
+ return chart;
+ };
+
+ chart.legendEnabled = function (value) {
+ if (!arguments.length) { return legendEnabled; }
+ legendEnabled = value;
+ return chart;
+ };
+
+ chart.onClick = function (value) {
+ if (!arguments.length) { return onClick(); }
+ onClick = value;
+ return chart;
+ };
+
+ chart.locale = function (value) {
+ if (!arguments.length) { return locale; }
+ locale = value;
+ return chart;
+ };
+
+ function chart() {
+
+ d3.select(chart.selector()).selectAll('svg.calendar-heatmap').remove(); // remove the existing chart, if it exists
+
+ var dateRange = ((d3.time && d3.time.days) || d3.timeDays)(yearAgo, now); // generates an array of date objects within the specified range
+ var monthRange = ((d3.time && d3.time.months) || d3.timeMonths)(moment(yearAgo).startOf('month').toDate(), now); // it ignores the first month if the 1st date is after the start of the month
+ var firstDate = moment(dateRange[0]);
+ if (chart.data().length == 0) {
+ max = 0;
+ } else if (max === null) {
+ max = d3.max(chart.data(), function (d) { return d.count; }); // max data value
+ }
+
+ // color range
+ var color = ((d3.scale && d3.scale.linear) || d3.scaleLinear)()
+ .range(chart.colorRange())
+ .domain([0, max]);
+
+ var tooltip;
+ var dayRects;
+
+ drawChart();
+
+ function drawChart() {
+ var svg = d3.select(chart.selector())
+ .style('position', 'relative')
+ .append('svg')
+ .attr('width', width)
+ .attr('class', 'calendar-heatmap')
+ .attr('height', height)
+ .style('padding', '36px');
+
+ dayRects = svg.selectAll('.day-cell')
+ .data(dateRange); // array of days for the last yr
+
+ var enterSelection = dayRects.enter().append('rect')
+ .attr('class', 'day-cell')
+ .attr('width', SQUARE_LENGTH)
+ .attr('height', SQUARE_LENGTH)
+ .attr('fill', function(d) { return color(countForDate(d)); })
+ .attr('x', function (d, i) {
+ var cellDate = moment(d);
+ var result = cellDate.week() - firstDate.week() + (firstDate.weeksInYear() * (cellDate.weekYear() - firstDate.weekYear()));
+ return result * (SQUARE_LENGTH + SQUARE_PADDING);
+ })
+ .attr('y', function (d, i) {
+ return MONTH_LABEL_PADDING + formatWeekday(d.getDay()) * (SQUARE_LENGTH + SQUARE_PADDING);
+ });
+
+ if (typeof onClick === 'function') {
+ (v === 3 ? enterSelection : enterSelection.merge(dayRects)).on('click', function(d) {
+ var count = countForDate(d);
+ onClick({ date: d, count: count});
+ });
+ }
+
+ if (chart.tooltipEnabled()) {
+ (v === 3 ? enterSelection : enterSelection.merge(dayRects)).on('mouseover', function(d, i) {
+ tooltip = d3.select(chart.selector())
+ .append('div')
+ .attr('class', 'day-cell-tooltip')
+ .html(tooltipHTMLForDate(d))
+ .style('left', function () { return Math.floor(i / 7) * SQUARE_LENGTH + 'px'; })
+ .style('top', function () {
+ return formatWeekday(d.getDay()) * (SQUARE_LENGTH + SQUARE_PADDING) + MONTH_LABEL_PADDING * 2 + 'px';
+ });
+ })
+ .on('mouseout', function (d, i) {
+ tooltip.remove();
+ });
+ }
+
+ if (chart.legendEnabled()) {
+ var colorRange = [color(0)];
+ for (var i = 3; i > 0; i--) {
+ colorRange.push(color(max / i));
+ }
+
+ var legendGroup = svg.append('g');
+ legendGroup.selectAll('.calendar-heatmap-legend')
+ .data(colorRange)
+ .enter()
+ .append('rect')
+ .attr('class', 'calendar-heatmap-legend')
+ .attr('width', SQUARE_LENGTH)
+ .attr('height', SQUARE_LENGTH)
+ .attr('x', function (d, i) { return (width - legendWidth) + (i + 1) * 13; })
+ .attr('y', height + SQUARE_PADDING)
+ .attr('fill', function (d) { return d; });
+
+ legendGroup.append('text')
+ .attr('class', 'calendar-heatmap-legend-text calendar-heatmap-legend-text-less')
+ .attr('x', width - legendWidth - 13)
+ .attr('y', height + SQUARE_LENGTH)
+ .text(locale.Less);
+
+ legendGroup.append('text')
+ .attr('class', 'calendar-heatmap-legend-text calendar-heatmap-legend-text-more')
+ .attr('x', (width - legendWidth + SQUARE_PADDING) + (colorRange.length + 1) * 13)
+ .attr('y', height + SQUARE_LENGTH)
+ .text(locale.More);
+ }
+
+ dayRects.exit().remove();
+ var monthLabels = svg.selectAll('.month')
+ .data(monthRange)
+ .enter().append('text')
+ .attr('class', 'month-name')
+ .text(function (d) {
+ return locale.months[d.getMonth()];
+ })
+ .attr('x', function (d, i) {
+ var matchIndex = 0;
+ dateRange.find(function (element, index) {
+ matchIndex = index;
+ return moment(d).isSame(element, 'month') && moment(d).isSame(element, 'year');
+ });
+
+ return Math.floor(matchIndex / 7) * (SQUARE_LENGTH + SQUARE_PADDING);
+ })
+ .attr('y', 0); // fix these to the top
+
+ locale.days.forEach(function (day, index) {
+ index = formatWeekday(index);
+ if (index % 2) {
+ svg.append('text')
+ .attr('class', 'day-initial')
+ .attr('transform', 'translate(-8,' + (SQUARE_LENGTH + SQUARE_PADDING) * (index + 1) + ')')
+ .style('text-anchor', 'middle')
+ .attr('dy', '2')
+ .text(day);
+ }
+ });
+ }
+
+ function pluralizedTooltipUnit (count) {
+ if ('string' === typeof tooltipUnit) {
+ return (tooltipUnit + (count === 1 ? '' : 's'));
+ }
+ for (var i in tooltipUnit) {
+ var _rule = tooltipUnit[i];
+ var _min = _rule.min;
+ var _max = _rule.max || _rule.min;
+ _max = _max === 'Infinity' ? Infinity : _max;
+ if (count >= _min && count <= _max) {
+ return _rule.unit;
+ }
+ }
+ }
+
+ function tooltipHTMLForDate(d) {
+ var dateStr = moment(d).format('ddd, MMM Do YYYY');
+ var count = countForDate(d);
+ return '<span><strong>' + (count ? count : locale.No) + ' ' + pluralizedTooltipUnit(count) + '</strong> ' + locale.on + ' ' + dateStr + '</span>';
+ }
+
+ function countForDate(d) {
+ var key= moment(d).format( 'YYYY-MM-DD' );
+ return counterMap[key] || 0;
+ }
+
+ function formatWeekday(weekDay) {
+ if (weekStart === 1) {
+ if (weekDay === 0) {
+ return 6;
+ } else {
+ return weekDay - 1;
+ }
+ }
+ return weekDay;
+ }
+
+ var daysOfChart = chart.data().map(function (day) {
+ return day.date.toDateString();
+ });
+
+ }
+
+ return chart;
+}
+
+
+// polyfill for Array.find() method
+/* jshint ignore:start */
+if (!Array.prototype.find) {
+ Array.prototype.find = function (predicate) {
+ if (this === null) {
+ throw new TypeError('Array.prototype.find called on null or undefined');
+ }
+ if (typeof predicate !== 'function') {
+ throw new TypeError('predicate must be a function');
+ }
+ var list = Object(this);
+ var length = list.length >>> 0;
+ var thisArg = arguments[1];
+ var value;
+
+ for (var i = 0; i < length; i++) {
+ value = list[i];
+ if (predicate.call(thisArg, value, i, list)) {
+ return value;
+ }
+ }
+ return undefined;
+ };
+}
+/* jshint ignore:end */