// ugly get date class hacking Date.prototype.getWeek = function() { var onejan = new Date(this.getFullYear(),0,1); return Math.ceil((((this - onejan) / 86400000) + onejan.getDay()+1)/7); } var gfx = function(){ return{ chartStyles: { barChart: { width: 666 - 110 - 5, height: 77 - 20, margin: { left: 110, top: 20, bottom: 0, right: 5 } }, timeChart: { width: 200, height: 650, margin: { left: 0, top: 0, bottom: 0, right: 0 } }, bubbleChart: { width: 650, height: 10, margin: { left: 40, top: 0, bottom: 0, right: 0 } } }, shortMonths: [ 'Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'June', 'July', 'Aug.', 'Sept.', 'Oct.', 'Nov.', 'Dec.' ], fullMonths: [ 'January', 'Febuary', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ], startWeek: null, endWeek: null, hackIntervalIn: true, minRadius: 1, maxRadius: 26.2, rowHeight: 90, rowsToStart: 10, dateCounter: 0, scaleBy: 'distance', startDate: new Date(2013,08,02), endDate: new Date(2013,08,08), init: function(user){ this.setup(); this.getData(user) this.tooltip = new Ink.Tooltip({ align: 'aboveCenter'}); }, bindEvents: function(){ var months = $('.g-month-title'); this.offsets = []; var me = this; months.each(function(d,i){ me.offsets.push({ y: $(this).offset().top -20, title: $(this).text(), interval: $(this).data('interval') }) }) var hs = _.debounce($.proxy(this.handleScroll, this), 0) $(window).scroll(hs) }, handleScroll: function(){ var y = $(window).scrollTop() this.offsets.forEach(function(o,i){ if(y >= o.y && i != gfx.offsets.length - 1){ if(y < gfx.offsets[i+1].y){ $('#row-summary').text(o.title) return; } } }) }, setup: function(){ // this.timeChart= d3.select("#g-barNav-chart").append("svg") // .attr("width", this.chartStyles.timeChart.width + this.chartStyles.timeChart.margin.left + this.chartStyles.timeChart.margin.right) // .attr("height", this.chartStyles.timeChart.height + this.chartStyles.timeChart.margin.top + this.chartStyles.timeChart.margin.bottom) // .append("g") // .attr("transform", "translate(" + this.chartStyles.timeChart.margin.left + "," + this.chartStyles.timeChart.margin.top + ")"); this.barChart = d3.select("#g-volume").append("svg") .attr("width", this.chartStyles.barChart.width + this.chartStyles.barChart.margin.left + this.chartStyles.barChart.margin.right) .attr("height", this.chartStyles.barChart.height + this.chartStyles.barChart.margin.top + this.chartStyles.barChart.margin.bottom) .append("g") .attr("transform", "translate(" + this.chartStyles.barChart.margin.left + "," + this.chartStyles.barChart.margin.top + ")"); }, getData: function(user){ var me = this; d3.json(user+'.json',function(data){ me.handleData(data) }); }, getDatesFromInterval: function(interval){ }, handleData: function(data){ var me = this; // this.formatDate = d3.time.format("%Y-%m-%dT%H:%M:%S-%H:%M"); this.formatDate = d3.time.format.utc("%Y-%m-%dT%H:%M:%S.%L+00:00"); this.intervalDate = d3.time.format("%Y\y%W\w"); data.forEach(function(d){ d.entry.structured_activities = [ [], [], [], [], [], [], [] ]; if(d.entry.activities.length > 0){ d.entry.activities.forEach(function(activity,n){ activity.formattedDate = new Date(0); activity.formattedDate.setUTCSeconds(activity.start_date - activity.utc_offset) activity.dayOfWeek = activity.formattedDate.getDay() activity.week = activity.formattedDate.getWeek() d.week = activity.week; activity.month_year = activity.formattedDate.getMonth()+'_'+activity.formattedDate.getFullYear() d.entry.structured_activities[activity.dayOfWeek].push(activity); }) } }); // //if(this.hackIntervalIn === true){ // c = this.startWeek; // data.forEach(function(d, i){ // data[i].week = c; // data[i].distance = 0 // data[i].moving_time = 0 // data[i].elev_gain = 0 // data[i].activities = [ // [], //0 // [], //1 // [], //2 // [], //3 // [], //4 // [], //5 // [] //6 // ] // c++; // if(d.length > 0){ // d.forEach(function(_d){ // _d.formattedDate = gfx.formatDate2.parse(_d.start_date_local); // _d.dayOfWeek = _d.formattedDate.getDay(); // _d.week = _d.formattedDate.getWeek(); // data[i].formattedDate = _d.formattedDate.getMonth() // data[i].activities[_d.dayOfWeek].push(_d); // // add it all up // data[i].distance += _d.distance; // data[i].moving_time += _d.moving_time; // data[i].elev_gain += _d.elev_gain; // }); // } // }); // } // console.log(_.pluck(data, 'week')) this.data = data.reverse(); var activities = _.map(this.data, function(d){ return d.entry.activities; }); this.activities = _.flatten(activities); this.activities = _.reject(this.activities, function(activity){ return activity.formattedDate.getFullYear() === 2012 }) this.dates = _.map(this.activities, function(d){ return d.formattedDate }) this.months = _.toArray(d3.nest() .key(function(d){ return d.month_year }) .map(this.activities)) this.races = _.toArray(d3.nest() .key(function(d){ return d.month_year }) .key(function(d){ return d.workout_type }) .map(_.select(this.activities, function(a){ return a.workout_type === 1 }))) this.setScales(); this.render(); }, setScales: function(){ var me = this; var intervals = _.pluck(this.data, 'interval') $('body').attr('class','g-scaleBy-'+gfx.scaleBy); this.timeYScale = d3.time.scale() .range([0, this.chartStyles.timeChart.height]) .domain(d3.extent(this.dates).reverse()) this.barXScale = d3.scale.ordinal() .rangeBands([this.chartStyles.barChart.margin.left, this.chartStyles.barChart.width]) .domain(intervals) this.barYScale = d3.scale.linear() .range([0,this.chartStyles.barChart.height]) .domain(d3.extent(this.data, function(d){ return d.distance })) .nice() this.barYAxisScale = d3.scale.linear() .range([this.chartStyles.barChart.height,0]) .domain(d3.extent(this.data, function(d){ return d.distance })) .nice() this.bubbleXScale = d3.scale.ordinal() .rangeBands([me.chartStyles.bubbleChart.margin.left, me.chartStyles.bubbleChart.width]) .domain([8,1,2,3,4,5,6,0]) ext = d3.extent(this.activities, function(d){ return d[gfx.scaleBy] }) if(this.scaleBy === 'distance'){ this.runScale = d3.scale.sqrt() .range([5, 26]) .domain([5, d3.max(this.activities, function(d){ return d[gfx.scaleBy] })]) }else{ this.runScale = d3.scale.sqrt() .range([this.minRadius, 26.2]) .domain([1, d3.max(this.activities, function(d){ return d[gfx.scaleBy] })]); } }, format_distance_value: function(val){ return this.getMile(val).toFixed(1) }, format_time_value: function(val){ // var totalSec = new Date().getTime() / 1000; var totalSec = val var hours = parseInt( totalSec / 3600 ) % 24; var minutes = parseInt( totalSec / 60 ) % 60; var seconds = totalSec % 60; var result = (hours < 10 ? hours+'hr' : hours+'hr') + " " + (minutes < 10 ? "0" + minutes+'min' : minutes+'min'); return result }, format_elev_gain_value: function(val){ return val.toString().replace('.0','') }, elev_gain_suffix: function(){ return 'ft' }, distance_suffix: function(){ return 'mi' }, time_suffix: function(){ return '' }, getMile: function(distance){ return distance * 0.000621371 }, getMeter: function(){ }, updateScales: function(){ this.setScales(); }, update: function(scaleBy){ var me = this; this.scaleBy = scaleBy; this.setScales() // this.barChart.selectAll('.weekBar') // .transition() // .duration(700) // .attr('width', function(d){ return me.barXScale(d[gfx.scaleBy]) }) var days = d3.selectAll('.dayWrap') d3.selectAll('.weekTotal').text(function(d){ if(d[gfx.scaleBy]) { return me['format_'+gfx.scaleBy+'_value'](d[gfx.scaleBy])+gfx[gfx.scaleBy+'_suffix']() }}) days.each(function(d,n){ if(d.length > 0){ var total = _(d).pluck(gfx.scaleBy).reduce(function(d1,d2){ return d1 + d2}) }else{ var total = 0; } var packSize = [me.runScale(total), me.runScale(total)]; // d3.select(this) // .attr('transform',function(__d){ return 'translate('+(me.bubbleXScale(n)-(packSize[0]/2))+','+(me.rowHeight - packSize[0] / 2) +')' }) if(total > 0){ pack = d3.layout.pack() .sort(d3.descending) .size(packSize) .value(function(__d){ if(gfx.scaleBy in __d){ return __d[gfx.scaleBy];} else{ return 0 } }) .children( function(__d,i){ return __d; } ) .radius(function(__d){ return me.runScale(__d) }) var runs = d3.select(this).selectAll('.run') .data(pack.nodes) d3.select(this).select('.sum.no-rest') .transition() .attr('transform', function(d){ return 'translate('+packSize[0]/2+','+(packSize[0] / 2) +')' }) .text(function(__d){ if(gfx.scaleBy === 'distance'){ var t = me.getMile(total).toFixed(1).toString() t = (t.indexOf('.0') > 0) ? t.replace('.0','') : t; return t }else{ return me['format_'+gfx.scaleBy+'_value'](__d.value)+gfx[gfx.scaleBy+'_suffix']() } }) runs.transition() .attr("transform", function(__d) { return "translate(" + __d.x + "," + (__d.y)+ ")"; }) .attr("r", function(__d){ return __d.r }) }else{ } }); // if(d.length > 0){ // var total = _(d).pluck(gfx.scaleBy).reduce(function(d1,d2){ return d1 + d2}) // }else{ // var total = 0; // } // var packSize = [me.runScale(total), me.runScale(total)]; // // d3.select(this) // // .attr('transform',function(__d){ return 'translate('+(me.bubbleXScale(n)-(packSize[0]/2))+','+(me.rowHeight - packSize[0] / 2) +')' }) // // d3.select(this) // // .append('rect') // // .attr('class',function(d){ return (total === 0) ? 'text-bg' : 'text-bg-hidden' }) // // .attr('width',40) // // .attr('height', 30) // // .attr('transform', function(d){ return 'translate(-18,-10)' }) // // .style('opacity',0) // if(total > 0){ // pack = d3.layout.pack() // .sort(d3.ascending) // .size(packSize) // .value(function(__d){ if(gfx.scaleBy in __d){ return __d[gfx.scaleBy];} else{ return 0 } }) // .children( // function(__d,i){ // return __d; // } // ) // .radius(function(__d){ // return me.runScale(__d) // }) // d3.selectAll('.sum') // .text(function(){ // if (total > 0){ // if(scaleBy === 'elev_gain'){ // return total+'ft.' // }else{ // var t = me.getMile(total).toFixed(1).toString() // t = (t.indexOf('.0') > 0) ? t.replace('.0','') : t; // return t // } // }else{ // return 'Rest' // } // }) // .attr('text-anchor', 'middle') // .attr('dy','4px') // .attr('transform', function(d){ return 'translate('+packSize[0]/2+','+(packSize[0] / 2) +')' }) // // .style('font-size',0) // // .style('opacity',0) // // text // d3.select(this).selectAll('.run') // .data(pack.nodes) // .enter() // .append('circle') // .attr('class', function(d){ return 'run run-depth-'+d.depth+' run-type-'+d.workout_type }) // .attr("transform", function(d) { return "translate(" + d.x + "," + (d.y)+ ")"; }) // .attr("r", 0) // transition = me.bubbleChart.transition().duration(700).delay(0) // transition.selectAll('.run') // .transition() // .attr("r", function(d,i){ // return d.r; // }) // .attr("transform", function(d) { return "translate(" + d.x + "," + (d.y)+ ")"; }) // } // }) // // // d3.select(this).selectAll('.run') // .data(pack.nodes) // .enter() // .append('circle') // .attr('class', function(d){ return 'run run-depth-'+d.depth }) // .attr("transform", function(d) { return "translate(" + d.x + "," + (d.y)+ ")"; }) // .attr("r", 0) // }); // transition = this.bubbleChart.transition().duration(1000).delay(0) // transition.selectAll('.run') // .update() // .transition() // .attr("r", function(d,i){ // return d.r; // }) }, render: function(){ this.renderBarChart(); this.renderTimeChart(); // rows = $('#g-rows'); var bubble = this.chartStyles.bubbleChart; // this.bubbleChart = d3.select('#g-rows').append('svg') // .attr("width", bubble.width + bubble.margin.left + bubble.margin.right) // .attr("height", (this.rowHeight * this.data.length) + bubble.margin.top + bubble.margin.bottom) // .append("g") // .attr("transform", "translate(" + bubble.margin.left + "," + bubble.margin.top + ")"); // this.renderRows(); this.renderMonthRows(); this.bindEvents(); this.fixie = new Ink.Fixie('#g-header', {offsetY: 0}) this.navFixie = new Ink.Fixie('#g-barNav', {offsetY: 0}) this.navFixie2 = new Ink.Fixie('#g-barNav-chart', {offsetY: 0}) }, workout_types: [ 'Run', 'Race', 'Long Run', 'Workout' ], renderMonthRows: function(){ var bubble = this.chartStyles.bubbleChart; var me = this; var rows = d3.select('#g-rows').selectAll('.g-row') .data(this.months) .enter() .append('div') .attr('class','g-row') rows.append('h3') .attr('class','g-month-title') .html(function(d,i){ if(d[i]){ return gfx.fullMonths[d[i].formattedDate.getMonth()] } }) .attr('data-interval',function(d){ return d[0].formattedDate.getMonth() }) var bubbleCharts = rows.append('svg') .attr('class','g-bubble-chart') .attr('width', bubble.width + bubble.margin.left + bubble.margin.right) .attr("height", gfx.rowHeight*4.5) .append('g') .attr('transform',"translate(" + bubble.margin.left + "," + bubble.margin.top + ")"); var groups = bubbleCharts.selectAll('.week') .data(function(d){ // // _.select(gfx.data, ) var weeks = _.pluck(d, 'week') return _.filter(gfx.data, function(_d){ return _.contains(weeks,_d.week) }) // return _.toArray(d3.nest().key(function(_d){ return _d.week }).map(d)) }) .enter() .append('g') .attr('class', 'week') .attr('transform',function(d,i){ return 'translate(0,'+(i*gfx.rowHeight)+')'; }); var dividers = groups.selectAll('.divider') .data(function(d){ return [ { distance: d[gfx.scaleBy] }] }) .enter() .append('line') .attr('class', 'divider') .attr('x1', this.chartStyles.bubbleChart.margin.left +10) .attr('y1', me.rowHeight - 1) .attr('x2', 0) .attr('y2', me.rowHeight - 1) // .style('opacity', 0) .transition() .duration(1000) .delay(function(d, i){ return i / me.data.length * 1000 }) .attr('x2',this.bubbleXScale(0)) var totals = groups.selectAll('.weekTotals') .data(function(d){ return [ { interval: d['interval'], distance: d['distance'], time: d['moving_time'], elev_gain: d['elev_gain'] }]}) .enter() .append('g') .attr('class','weekTotals') .attr('transform',function(d,i){ return 'translate(45,91)'; }) .attr('text-anchor','end') totals.append('text') .attr('class', 'weekTotal') .text(function(d){ if(d[gfx.scaleBy]) { return me['format_'+gfx.scaleBy+'_value'](d[gfx.scaleBy])+gfx[gfx.scaleBy+'_suffix']() }}) .attr('text-anchor','end') totals.append('text') .attr('class', 'weekTotal-time') .text(function(d){ if(d[gfx.scaleBy]) { return me['format_time_value'](d.time)+gfx.time_suffix() }}) .attr('transform','translate(0,17)') .attr('text-anchor','end') totals.append('text') .attr('class', 'weekTotal-elev') .text(function(d){ if(d[gfx.scaleBy]) { return me['format_elev_gain_value'](d.elev_gain)+gfx.elev_gain_suffix() }}) .attr('transform','translate(0,32)') .attr('text-anchor','end') var dformat1 = d3.time.format("%b. %e") var dformat2 = d3.time.format("%-e") var months = [ 'Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'June', 'July', 'Aug.', 'Sept.', 'Oct.', 'Nov.', 'Dec.' ] var dateRanges = totals.append('text') .attr('class','dateRange') .attr('transform',function(d,i){ if(d[gfx.scaleBy] > 0){ return 'translate(0,-22)'; }else{ return 'translate(0,-2)'; } }) .text(function(d){ _start = new Date(), start = new Date(), _end = new Date(), end = new Date(); year = parseInt(d.interval.substr(0,4),10) week = parseInt(d.interval.substr(5,2),10) _start.setFullYear(year) _start.setMonth(0) _start.setDate(1) _end.setFullYear(year) _end.setMonth(0) _end.setDate((week * 7)) moment_start = moment(_start).add('days',(week*7)) moment_end = moment(_end).add('days',7) if(moment_start.month() === moment_end.month()){ return moment_start.format('MMM. D')+'-'+moment_end.format('D') }else{ return moment_start.format('MMM. D')+'-'+moment_end.format('MMM. D') } }) // .attr('text-anchor','end') var days = groups.selectAll('.dayWrap') .data(function(d){ return d.entry.structured_activities }) .enter() .append('g') .attr('class', function(d){ return 'dayWrap'+' dayWrap-children-'+d.length }) days.each(function(d,n){ if(d.length > 0){ var total = _(d).pluck(gfx.scaleBy).reduce(function(d1,d2){ return d1 + d2}) }else{ var total = 0; } var packSize = [me.runScale(total), me.runScale(total)]; d3.select(this) .attr('transform',function(__d){ return 'translate('+(me.bubbleXScale(n)-(packSize[0]/2))+','+(me.rowHeight - packSize[0] / 2) +')' }) if(total > 0){ pack = d3.layout.pack() .sort(d3.descending) .size(packSize) .value(function(__d){ if(gfx.scaleBy in __d){ return __d[gfx.scaleBy];} else{ return 0 } }) .children( function(__d,i){ return __d; } ) .radius(function(__d){ return me.runScale(__d) }) d3.select(this).selectAll('.run') .data(pack.nodes) .enter() .append('circle') .attr('class', function(d){ var childLength = ('children' in d) ? d.children.length : 0; return 'run run-depth-'+d.depth+' run-children-'+childLength+' run-type-'+d.workout_type }) .attr("transform", function(d) { return "translate(" + d.x + "," + (d.y)+ ")"; }) .attr("r", 0) // .style('opacity', 0) .on('mouseover', function(d,i){ // if (true) {}; if(d.depth === 0){ var parent = this.parentNode; d3.select(parent).classed('active', true) return } var parent = this.parentNode; d3.select(parent).classed('active', true) d3.select(this).classed('active', true) m = d3.mouse(this) // p = d.avg_pace.split(':')[0]+':'+d.avg_pace.split(':')[1] me.tooltip.content('
'+gfx.workout_types[d.workout_type]+'
'+d.name+'
Distance: '+gfx.getMile(d.distance).toFixed(1)+'mi.
Moving Time: '+gfx.format_time_value(d.moving_time)+'
Avg Pace: '+d.speed+'/mi
') x = event.pageX - m[0] - (me.tooltip.$el.width() / 2) - 10 y = event.pageY - m[1] - gfx.tooltip.$el.height() - 50 gfx.tooltip.position({ left: x, top: y }).show() d3.select(this).classed('active', true) // console.log('day: ', d.dayOfWeek) }) .on('mouseout', function(){ me.tooltip.hide(); var parent = this.parentNode; d3.select(this).classed('active', false) d3.select(parent).classed('active', false) d3.select(parent).select('.sum').classed('activeText',false) }) d3.select(this).selectAll('.day-total') .data(pack.nodes) .enter() .append('text') .attr('class',function(d){ var depth = ('parent' in d) ? d.parent.length : 0; return 'day-total day-total-depth-'+depth+' day-total-value-'+d.value }) .text(function(d){ var t = me.getMile(d.distance).toFixed(1).toString() t = (t.indexOf('.0') > 0) ? t.replace('.0','') : t; return t }) // .style('font-size', function(d){ // return '100%' // }) .attr('transform',function(d){ return 'translate('+d.x+','+d.y+')' }) .attr('text-anchor', 'middle') .attr('dy','4px') } // .padding(-2) // blocks d3.select(this) .append('rect') .attr('class',function(d){ return (total === 0) ? 'text-bg' : 'text-bg-hidden' }) .attr('width',40) .attr('height', 30) .attr('transform', function(d){ return 'translate(-18,-10)' }) .style('opacity',0) d3.select(this) .append('text') .attr('class', function(d){ if(total === 0){ return 'sum rest' }else{ return 'sum no-rest' } }) .text(function(){ if (total > 0){ var t = me.getMile(total).toFixed(1).toString() t = (t.indexOf('.0') > 0) ? t.replace('.0','') : t; return t }else{ return 'rest' } }) .attr('text-anchor', 'middle') .attr('dy','4px') .attr('transform', function(d){ return 'translate('+packSize[0]/2+','+(packSize[0] / 2) +')' }) .style('font-size',0) .style('opacity',0) // text }); revealTransition = d3.transition().duration(1000) .delay(function(d, i) { return i / 11 * 1000; }) revealTransition.selectAll('.run') .attr("r", function(d,i){ return d.r; }) .style("opacity", 1) revealTransition.selectAll('.text-bg').style('opacity',1) revealTransition.selectAll('.sum').style('font-size','13px').style('opacity',1) revealTransition.transition() }, renderRows: function(){ var me = this var groups = this.bubbleChart.selectAll('.week') .data(this.data) .enter() .append('g') .attr('class', 'week') .attr('transform',function(d,i){ return 'translate(0,'+(i*me.rowHeight)+')'; }); var dividers = groups.selectAll('.divider') .data(function(d){ return [ { distance: d[gfx.scaleBy] }] }) .enter() .append('line') .attr('class', 'divider') .attr('x1', this.chartStyles.bubbleChart.margin.left +10) .attr('y1', me.rowHeight - 1) .attr('x2', 0) .attr('y2', me.rowHeight - 1) // .style('opacity', 0) .transition() .duration(1000) .delay(function(d, i){ return i / me.data.length * 1000 }) .attr('x2',this.bubbleXScale(0)) var totals = groups.selectAll('.weekTotals') .data(function(d){ return [ { interval: d['interval'], distance: d['distance'], time: d['moving_time'], elev_gain: d['elev_gain'] }]}) .enter() .append('g') .attr('class','weekTotals') .attr('transform',function(d,i){ return 'translate(-20,'+(i*me.rowHeight+(me.rowHeight + 5))+')'; }) totals.append('text') .attr('class', 'weekTotal') .text(function(d){ if(d[gfx.scaleBy]) { return me['format_'+gfx.scaleBy+'_value'](d[gfx.scaleBy])+gfx[gfx.scaleBy+'_suffix']() }}) // .attr('text-anchor','end') var dformat1 = d3.time.format("%b. %e") var dformat2 = d3.time.format("%-e") var months = [ 'Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'June', 'July', 'Aug.', 'Sept.', 'Oct.', 'Nov.', 'Dec.' ] var dateRanges = totals.append('text') .attr('class','dateRange') .attr('transform',function(d,i){ if(d[gfx.scaleBy] > 0){ return 'translate(0,-22)'; }else{ return 'translate(0,-2)'; } }) .text(function(d){ _start = new Date(), start = new Date(), _end = new Date(), end = new Date(); year = parseInt(d.interval.substr(0,4),10) week = parseInt(d.interval.substr(5,2),10) _start.setFullYear(year) _start.setMonth(0) _start.setDate(1) _end.setFullYear(year) _end.setMonth(0) _end.setDate((week * 7)) moment_start = moment(_start).add('days',(week*7)) moment_end = moment(_end).add('days',7) //var dateStartEnd = new Date().setDate(new Date().setFullYear(year).getDate() + (week * 7)) // if(start.getMonth() === end.getMonth()){ // dt = months[start.getMonth()]+' '+start.getDate()+'-'+end.getDate(); // }else{ // dt = months[start.getMonth()]+' '+start.getDate()+'-'+months[end.getMonth()]+' '+end.getDate(); // } //dt = dformat1(dateStart) // nextWeekStart = new Date() // nextWeekEnd = new Date(); // gfx.dateCounter = gfx.dateCounter + 7 // nextWeekStart.setDate(me.startDate.getDate()-gfx.dateCounter) // nextWeekEnd.setDate(me.endDate.getDate()-gfx.dateCounter) // me.startDate = nextWeekStart // me.endDate = nextWeekEnd if(moment_start.month() === moment_end.month()){ return moment_start.format('MMM. D')+'-'+moment_end.format('D') }else{ return moment_start.format('MMM. D')+'-'+moment_end.format('MMM. D') } }) // .attr('text-anchor','end') var days = groups.selectAll('.dayWrap') .data(function(d){ return d.entry.structured_activities }) .enter() .append('g') .attr('class', 'dayWrap') days.each(function(d,n){ if(d.length > 0){ var total = _(d).pluck(gfx.scaleBy).reduce(function(d1,d2){ return d1 + d2}) }else{ var total = 0; } var packSize = [me.runScale(total), me.runScale(total)]; d3.select(this) .attr('transform',function(__d){ return 'translate('+(me.bubbleXScale(n)-(packSize[0]/2))+','+(me.rowHeight - packSize[0] / 2) +')' }) if(total > 0){ pack = d3.layout.pack() .sort(d3.descending) .size(packSize) .value(function(__d){ if(gfx.scaleBy in __d){ return __d[gfx.scaleBy];} else{ return 0 } }) .children( function(__d,i){ return __d; } ) .radius(function(__d){ return me.runScale(__d) }) d3.select(this).selectAll('.run') .data(pack.nodes) .enter() .append('circle') .attr('class', function(d){ var childLength = ('children' in d) ? d.children.length : 0; return 'run run-depth-'+d.depth+' run-children-'+childLength+' run-type-'+d.workout_type }) .attr("transform", function(d) { return "translate(" + d.x + "," + (d.y)+ ")"; }) .attr("r", 0) // .style('opacity', 0) .on('mouseover', function(d,i){ // if (true) {}; if(d.depth === 0){ var parent = this.parentNode; d3.select(parent).select('.sum').classed('activeText',true) return } // var parent = this.parentNode; d3.select(this).classed('active', true) m = d3.mouse(this) // p = d.avg_pace.split(':')[0]+':'+d.avg_pace.split(':')[1] me.tooltip.content('
'+d.name+'
Distance: '+gfx.getMile(d.distance).toFixed(1)+'mi.
Moving Time: '+gfx.format_time_value(d.moving_time)+'
') x = event.pageX - m[0] - (me.tooltip.$el.width() / 2) - 10 y = event.pageY - m[1] - gfx.tooltip.$el.height() - 50 gfx.tooltip.position({ left: x, top: y }).show() d3.select(this).classed('active', true) // console.log('day: ', d.dayOfWeek) }) .on('mouseout', function(){ me.tooltip.hide(); var parent = this.parentNode; d3.select(this).classed('active', false) d3.select(parent).select('.sum').classed('activeText',false) }) } // .padding(-2) // blocks d3.select(this) .append('rect') .attr('class',function(d){ return (total === 0) ? 'text-bg' : 'text-bg-hidden' }) .attr('width',40) .attr('height', 30) .attr('transform', function(d){ return 'translate(-18,-10)' }) .style('opacity',0) d3.select(this) .append('text') .attr('class', function(d){ if(total === 0){ return 'sum rest' }else{ return 'sum no-rest' } }) .text(function(){ if (total > 0){ var t = me.getMile(total).toFixed(1).toString() t = (t.indexOf('.0') > 0) ? t.replace('.0','') : t; return t }else{ return 'Rest' } }) .attr('text-anchor', 'middle') .attr('dy','4px') .attr('transform', function(d){ return 'translate('+packSize[0]/2+','+(packSize[0] / 2) +')' }) .style('font-size',0) .style('opacity',0) // text }); revealTransition = d3.transition().duration(1000) .delay(function(d, i) { return i / 11 * 1000; }) revealTransition.selectAll('.run') .attr("r", function(d,i){ return d.r; }) .style("opacity", 1) revealTransition.selectAll('.text-bg').style('opacity',1) revealTransition.selectAll('.sum').style('font-size','13px').style('opacity',1) revealTransition.transition() }, renderRow: function(){ }, renderTimeChart: function(){ var me = this; var sidebarStuff = ['

2013 Race History

'] if(gfx.races.length > 0){ gfx.races.forEach(function(race_set, i){ race_set = _.flatten(_.toArray(race_set)) console.log(race_set) sidebarStuff.push('') }); $('#g-barNav-chart').append(sidebarStuff.join('')) } // this.timeYAxis = d3.svg.axis() // .scale(me.timeYScale) // .ticks(12) // .tickSize(-me.chartStyles.barChart.width+1) // .orient('right') // .tickPadding([6]) // var events = this.timeChart.selectAll('.race-event') // .data(races) // .enter() // .append('g') // .attr('class','race-event') // .attr('transform',function(d){ return 'translate(0,'+me.timeYScale(d.formattedDate)+')' }) // events.append('rect') // .attr('class','race-event') // .attr('width',5) // .attr('height',2) // events.append('text') // .attr('class','race-event-title') // .text(function(d){ return d.name }) // .attr('dy',5) // .attr('dx',12) // this.timeChart.append('g').attr('class','y axis') // .attr('transform','translate('+me.chartStyles.barChart.width+',-3)') // .call(this.timeYAxis) // this.timeChart.append('text') // .attr('transform','translate(10,19)' ) // .attr('class','g-bigBarLabel') // .text('Races - Jan-Sept 2013') }, renderBarChart: function(){ var me = this; var goalDistance = 103098 / 2 this.barChart.selectAll('.g-goal-line') .data(this.data).enter() .append('rect') .attr('class','g-goal-line') .attr('x', function(d){ return me.barXScale(d.interval) }) .attr('y', function(d){ return Math.round(me.chartStyles.barChart.height) }) .attr('width', function(d){ return me.barXScale.rangeBand()-1 }) .attr('height', function(d){ return (me.barYScale(goalDistance)) }) .transition() .duration(700) .delay(function(d, i) { return i / 38 * 700; }) .attr('y', function(d){ return Math.round(me.chartStyles.barChart.height)-me.barYScale(goalDistance) }) this.barChart.selectAll('.weekBar') .data(this.data).enter() .append('rect') .attr('class','weekBar') .attr('x', function(d){ return me.barXScale(d.interval) }) .attr('y', function(d){ return me.chartStyles.barChart.height }) .attr('width', me.barXScale.rangeBand()-1) .attr('height', function(d){ return (me.barYScale(d.distance)) }) .on('click', function(d){ d3.selectAll('.weekBar').classed('weekBar-selected', false) d3.select(this).classed('weekBar-selected', true) var offset = $('#g-rows').offset().top var set = me.data set.forEach(function(s,i){ if(s.week === d.week){ console.log(d) var offset = $('.g-month-title[data-interval="'+d.entry.activities[0].formattedDate.getMonth()+'"]').offset().top $('html,body').animate({ scrollTop: offset }, 700) return } }) }) .on('mouseover', function(d){ weekStart = d.entry.activities[0].formattedDate x = me.barXScale(d.interval); y = me.barYAxisScale(d[gfx.scaleBy]) if(d.entry.activities.length > 0){ var t = gfx.shortMonths[d.entry.activities[0].formattedDate.getMonth()] +' '+d.entry.activities[0].formattedDate.getDate()+'-' t = t + gfx.shortMonths[d.entry.activities[d.entry.activities.length-1].formattedDate.getMonth()]+' ' t = t + d.entry.activities[d.entry.activities.length-1].formattedDate.getDate()+', ' t = t + gfx.format_distance_value(d.distance)+'mi' }else{ t = '' } me.barChart.append('text') .attr('class','g-barLabel') .text(function(d){ return t }) .attr('transform',function(_d){ return 'translate('+x+','+y+')' }) .attr('text-anchor','middle') .attr('dy','-5') .attr('dx','4') }) .on('mouseout', function(d){ me.barChart.select('.g-barLabel').remove() }) .style('opacity',0) .transition() .duration(700) .delay(function(d, i) { return i / 38 * 700; }) .style('opacity',1) .attr('y', function(d){ return me.chartStyles.barChart.height - me.barYScale(d.distance) }) var exceededGoalWeeks = _.select(this.data, function(d){ return d.distance > goalDistance }); this.barChart.selectAll('.g-goal-line-exceeded') .data(exceededGoalWeeks).enter() .append('rect') .attr('class','g-goal-line-exceeded') .attr('x', function(d){ return me.barXScale(d.interval) }) .attr('y', function(d){ return Math.round(me.chartStyles.barChart.height)-me.barYScale(goalDistance) }) .attr('width', function(d){ return me.barXScale.rangeBand()-1 }) .attr('height', 1) .style('opacity',0) .transition() .duration(1000) .delay(function(d, i) { return i / 38 * 1000; }) .style('opacity',1) var maxVal = d3.max(this.data, function(d){ return d.distance }) this.barYAxis = d3.svg.axis() .scale(me.barYAxisScale) .ticks(3) // .tickValues([maxVal/3, maxVal]) // .tickSize(-me.chartStyles.barChart.width+1) .orient('left') .tickPadding([6]) .tickSize([4]) .tickFormat(function(d){ return (d*0.000621371).toFixed(0)+'mi' }) var key = this.barChart.append('g') .attr('transform','translate(-92,5)') key.append('text') .attr('transform','translate(0,0)' ) .attr('class','g-bigBarLabel') .text('2013 Running Volume') key.append('line') .attr('class','g-key-goal-line') .attr('x1','0' ) .attr('x2','17' ) .attr('y1', 16 ) .attr('y2',16) key.append('text') .attr('transform','translate(25,20)' ) .attr('class','g-smallBarLabel') .text('Weekly Goal') key.append('line') .attr('class','g-key-recorded-line') .attr('x1','0' ) .attr('x2','17' ) .attr('y1', 32 ) .attr('y2',32) // key.append('text') // .attr('transform','translate(5,35)' ) // .attr('class','g-smallBarLabel') // .text('✓') key.append('text') .attr('transform','translate(25,35)' ) .attr('class','g-smallBarLabel') .text('Run Mileage/Time') this.barChart.append('g').attr('class','y axis') .attr('transform','translate('+me.chartStyles.barChart.margin.left+',0)') .call(this.barYAxis) }, getTime: function(v){ var mins = (v / 60).toFixed() var hours = (mins / 60).toFixed() return hours+':'+mins; }, getDistance: function(){ }, handleTypeChange: function(){ var type = $('#typeToggle').val(); this.update(type); } } }(); gfx.init('galen__2013y38w')