class @PlasticineColumn constructor: (@holder) -> @holder = $('.plasticine-column') @setLocale() @drawTooltip() width = @holderWidth() height = @holder.height() @xScale = d3.scale.ordinal() @setXScaleRange() @yScale = d3.scale.linear().range([height,0]) @setXAxis() @setYAxis() @svg = d3.select("##{@holder.attr('id')}").append('svg').style('width', width).style('height', height).append('g') d3.json @holder.data('url'), (data) => @build(data) build: (data) -> columnIds = data.columns[0].y_values.map (d,i) -> "col#{i+1}" @colGroup = d3.scale.ordinal().range(columnIds).domain(columnIds) @xAxisFormat = data.axis_x_format @yAxisFormat = data.axis_y_format @yAxisTickCount = data.axis_y_tick_count @quarterStartMonth = Number(data.quarter_start_month) data.columns.forEach (d) => y0 = 0 d.yValues = @colGroup.domain().map (name,i) => { name: name, y0: y0, y1: y0 += @formatValue(d.y_values[i], @yAxisFormat) } d.total = d.yValues[d.yValues.length - 1].y1 d.x = @formatValue(d.x_value, @xAxisFormat) @xScale.domain data.columns.map( (d) -> d.x ) @yScale.domain [0, d3.max(data.columns, (d) => d.total * @holder.data('y-spacing-ratio'))] @drawYAxis() @columns = @svg.append('g').attr("class", "columns").selectAll('.column').data(data.columns) @refreshColumns() @drawXAxis() d3.select(window).on("resize." + @holder.attr("id"), () => @resizeX()) drawTooltip: () -> @holder.append('
') drawXAxis: () -> height = @holder.height() @svg.append('g').attr('class', 'x axis') .attr('transform', 'translate(0,' + height + ')') .call @xAxis @refreshXAxis() drawYAxis: () -> group = @svg.append("g").attr("class", "y axis").call @yAxis group.selectAll("g").filter((d) -> d).classed("minor", true) @refreshYAxis() formatValue: (value, format) -> switch format when 'date' then new Date(value) else value holderWidth: () -> @holder.width() refreshXAxis: () -> @setXAxis() @svg.select('.x.axis').call @xAxis refreshYAxis: () -> @setYAxis() @svg.select('.y.axis').call @yAxis setXAxis: () -> @xAxis = d3.svg.axis().scale(@xScale).orient('bottom') @xAxis.tickSize -@holder.height() switch @xAxisFormat when 'date' then @xAxis.tickFormat(d3.time.format('%b')) when 'quarter' then @xAxis.tickFormat (d) => @toQuarter(d) when 'year' then @xAxis.tickFormat (d) => @toYear(d) when 'money' then @xAxis.tickFormat (d) => @toPrice(d) setYAxis: () -> @yAxis = d3.svg.axis().scale(@yScale).orient("left"); @yAxis.tickSize -@holderWidth() @yAxis.ticks @yAxisTickCount switch @yAxisFormat when 'date' then @yAxis.tickFormat(d3.time.format('%b')) when 'money' then @yAxis.tickFormat (d) => @toPrice(d) hideTooltip: () -> @holder.find('.tooltip').hide() refreshColumns: () -> self = this $('g.column').remove() @columns.enter().append('g') .attr('class', 'column') .attr('data-x-val', (d) -> d.x_value) .attr('transform', (d) => 'translate(' + @xScale(d.x) + ',0)') .attr('x', (d) => @xScale(d.x)) .on("mouseover", (d) -> self.showTooltip(@, d)) .on("mouseout", (d) -> self.hideTooltip(@)) @columns.exit().remove() @columns.selectAll('rect') .data((d) -> d.yValues) .enter() .append('rect') .attr('width', @xScale.rangeBand()) .attr('y', (d) => @yScale d.y1) .attr('height', (d) => @yScale(d.y0) - @yScale(d.y1)) .attr('class', (d) => @colGroup d.name) resizeX: () -> d3.select(@svg.node().parentNode).style('width', @holderWidth() + 'px') @setXScaleRange() @refreshXAxis() @refreshYAxis() @hideTooltip() @refreshColumns() setLocale: () -> french = d3.locale( "decimal": ",", "thousands": " ", "grouping": [3], "currency": ["$", ""], "dateTime": "%a %b %e %X %Y", "date": "%d-%m-%Y", "time": "%H:%M:%S", "periods": ["AM", "PM"], "days": ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"], "shortDays": ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"], "months": ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"], "shortMonths": ["Jan", "Fev", "Mar", "Avr", "Mai", "Jun", "Jul", "Aou", "Sep", "Oct", "Nov", "Dec"] ) d3.time.format = french.timeFormat d3.format = french.numberFormat setXScaleRange: () -> columnsMargin = @holder.data('columns-margin') columnsLeftPadding = @holder.data('columns-left-padding') columnsRightPadding = @holder.data('columns-right-padding') width = @holderWidth() @xScale = @xScale.rangeRoundBands([columnsLeftPadding, width - columnsRightPadding], columnsMargin, 0) showTooltip: (bar, data) -> barNode = $($(bar).find('rect:last-child')[0]) barX = $(bar).attr('x') barY = barNode.attr('y') xPosition = parseInt(barX) + @holder.offset().left yPosition = parseInt(barY) + @holder.offset().top tooltip = @holder.find('.tooltip') tooltip.html(data.tooltip) tooltip.fadeIn 200 tooltipX = xPosition - (tooltip.outerWidth() / 2) + (parseInt(barNode.attr("width")) / 2) d3.select(".tooltip") .style("left", tooltipX + "px") .style("top", (yPosition - tooltip.outerHeight()) + "px") toPrice: (amount) -> if amount is 0 price = "0" else price = String(amount)[0..-3] price = "0" if price is "" price = switch price.length when 4 then [price.slice(0, 1), ' ', price.slice(1)].join('') when 5 then [price.slice(0, 2), ' ', price.slice(2)].join('') when 6 then [price.slice(0, 3), ' ', price.slice(3)].join('') when 7 then [price.slice(0, 4), ' ', price.slice(4)].join('') else price # Show decimals #price += ',' + String(amount).slice(-2) price += '$' toQuarter: (strDate) -> date = new Date(strDate + 'T12:00:00') month = date.getMonth() + 1 monthEquiv = (month + (13 - @quarterStartMonth)) % 12 value = switch when monthEquiv <= 3 then 'Q1' when monthEquiv <= 6 then 'Q2' when monthEquiv <= 9 then 'Q3' else 'Q4' year = Number(date.getFullYear()) if (value is "Q1" and @quarterStartMonth >= 10) or ((value is "Q1" or value is "Q2") and @quarterStartMonth >= 7 and @quarterStartMonth < 10) year += 1 value += " #{year}" value toYear: (strDate) -> date = new Date(strDate + 'T12:00:00') year = Number(date.getFullYear()) if @quarterStartMonth >= 7 and (date.getMonth() + 1) >= 7 year += 1 year