window.Dashboard = (function(d3, moment) { // private var data, interval, index, maxIndex, dateStrings, charts, values, rates, times, dates, tables, x, y, bars function init(opts) { data = opts.data interval = opts.interval index = 0 maxIndex = data.length - 1 dateStrings = data.map(function (d, i) { var date = moment.unix(d.key).tz('America/New_York') var str = '' switch (interval) { case 'hourly': str += date.format('HH:mm') + '–' + date.add(59, 'm').format('HH:mm z'); break case 'daily': str += date.format('MMMM D'); break case 'weekly': str += 'the week of ' + date.format('MMMM D'); break case 'monthly': str += date.format(i < 12 ? 'MMMM' : 'MMMM YYYY'); break default: str += date.format('X') } if (i === 0) { str += ' (so far)'} return str }) charts = d3.selectAll('.data-chart') .call(setDataByKey) .append('svg') values = d3.selectAll('.data-value') .call(setDataByKey) rates = d3.selectAll('.data-rate') .call(setDataByKey) times = d3.selectAll('.data-time') .call(setDataByKey) tables = d3.select('.data-table') .call(setDataByKey) dates = d3.selectAll('.data-date') x = d3.scaleBand() .domain(d3.range(0, data.length)) .paddingInner(0.1) y = d3.local() charts.property(y, function (d) { var max switch (d.key) { default: max = d3.max(d.values) } max = max > 1 ? max : 1 return d3.scaleLinear() .domain([0, max]) }) charts.append('g').append('title').text('Historical Data') bars = charts.selectAll('.bar') .data(function (d) { return d.values }) .enter().append('rect') .attr('class', 'bar') .attr('title', function (d, i) { return dateStrings[i] + ': ' + d }) resize() d3.select(window) .on('resize', resize) .on('keydown', function () { switch (d3.event.keyCode || d3.event.detail.keyCode) { case 37: index += 1; d3.event.preventDefault(); break // left case 39: index -= 1; d3.event.preventDefault(); break // right } if (index > maxIndex) { index = 0 } else if (index < 0) { index = maxIndex } update() }) charts.on('mousemove', function () { index = maxIndex - Math.max(Math.floor((d3.event.offsetX) / x.step()), 0) update() }) } function resize() { var testNode = d3.select('.data-chart').node() var width = testNode.offsetWidth - 2 var height = testNode.offsetHeight - 2 x.range([width, 0]) charts .attr('width', width) .attr('height', height) .each(function () { y.get(this).range([2, height / 2]) }) bars .attr('x', function (d, i) { return x(i) }) .attr('y', function (d) { return height - y.get(this)(d || 0) }) .attr('width', x.bandwidth()) .attr('height', function (d) { return y.get(this)(d || 0) }) update() } function update() { values.text(function (d) { return d.values[index] }) rates.html(function (d) { return rateFormat(d.values[index]) }) times.html(function (d) { return timeFormat(d.values[index]) }) dates.text('for ' + dateStrings[index]) bars.attr('opacity', function (d, i) { return i === index ? 0.67 : 0.33 }) rows = tables.selectAll('tr') .data(function (d) { return d.values[index] }) var enterRows = rows.enter().append('tr') enterRows.append('td').attr('class', 'id') enterRows.append('td').attr('class', 'count') rows.exit().remove() rows = rows.merge(enterRows) rows.each(function (d) { var row = d3.select(this); row.select('.id').text(d.id) row.select('.count').text(d.count + (d.count === 1 ? ' Download' : ' Downloads')) }) } function setDataByKey(sel) { return sel.datum(function () { var mapper, key = d3.select(this).attr('data-key'), keys = key.split('/'), isRate = false if (keys.length === 2) { isRate = true mapper = function (d) { return d.value[keys[1]] === 0 ? null : d.value[keys[0]] / d.value[keys[1]] } } else { mapper = function (d) { return d.value[key] } } return { key: key, isRate: isRate, values: data.map(mapper) } }) } var format = d3.format('.2f') function rateFormat(n) { return (n === null ? '??' : Math.round(n * 100)) + ' %' } function timeFormat(seconds) { return !seconds ? '?? sec' : seconds > 60 ? format(seconds / 60) + ' min' : format(seconds) + ' sec' } // public return { init: init } })(d3, moment)