summaryrefslogtreecommitdiffstats
path: root/debian/missing-sources/epoch/src/basic
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--debian/missing-sources/epoch/src/basic.coffee243
-rw-r--r--debian/missing-sources/epoch/src/basic/area.coffee51
-rw-r--r--debian/missing-sources/epoch/src/basic/bar.coffee273
-rw-r--r--debian/missing-sources/epoch/src/basic/histogram.coffee61
-rw-r--r--debian/missing-sources/epoch/src/basic/line.coffee46
-rw-r--r--debian/missing-sources/epoch/src/basic/pie.coffee59
-rw-r--r--debian/missing-sources/epoch/src/basic/scatter.coffee59
7 files changed, 792 insertions, 0 deletions
diff --git a/debian/missing-sources/epoch/src/basic.coffee b/debian/missing-sources/epoch/src/basic.coffee
new file mode 100644
index 0000000..8af3465
--- /dev/null
+++ b/debian/missing-sources/epoch/src/basic.coffee
@@ -0,0 +1,243 @@
+# Base class for all two-dimensional basic d3 charts. This class handles axes and
+# margins so that subclasses can focus on the construction of particular chart
+# types.
+class Epoch.Chart.Plot extends Epoch.Chart.SVG
+ defaults =
+ domain: null,
+ range: null,
+ axes: ['left', 'bottom']
+ ticks:
+ top: 14
+ bottom: 14
+ left: 5
+ right: 5
+ tickFormats:
+ top: Epoch.Formats.regular
+ bottom: Epoch.Formats.regular
+ left: Epoch.Formats.si
+ right: Epoch.Formats.si
+
+ defaultAxisMargins =
+ top: 25
+ right: 50
+ bottom: 25
+ left: 50
+
+ optionListeners =
+ 'option:margins.top': 'marginsChanged'
+ 'option:margins.right': 'marginsChanged'
+ 'option:margins.bottom': 'marginsChanged'
+ 'option:margins.left': 'marginsChanged'
+ 'option:axes': 'axesChanged'
+ 'option:ticks.top': 'ticksChanged'
+ 'option:ticks.right': 'ticksChanged'
+ 'option:ticks.bottom': 'ticksChanged'
+ 'option:ticks.left': 'ticksChanged'
+ 'option:tickFormats.top': 'tickFormatsChanged'
+ 'option:tickFormats.right': 'tickFormatsChanged'
+ 'option:tickFormats.bottom': 'tickFormatsChanged'
+ 'option:tickFormats.left': 'tickFormatsChanged'
+ 'option:domain': 'domainChanged'
+ 'option:range': 'rangeChanged'
+
+ # Creates a new plot chart.
+ # @param [Object] options Options to use when constructing the plot.
+ # @option options [Object] margins For setting explicit values for the top,
+ # right, bottom, and left margins in the visualization. Normally these can
+ # be omitted and the class will set appropriately sized margins given which
+ # axes are specified.
+ # @option options [Array] axes A list of axes to display (top, left, bottom, right).
+ # @option options [Object] ticks Number of ticks to place on the top, left bottom
+ # and right axes.
+ # @option options [Object] tickFormats What tick formatting functions to use for
+ # the top, bottom, left, and right axes.
+ constructor: (@options={}) ->
+ givenMargins = Epoch.Util.copy(@options.margins) or {}
+ super(@options = Epoch.Util.defaults(@options, defaults))
+
+ # Margins are used in a special way and only for making room for axes.
+ # However, a user may explicitly set margins in the options, so we need
+ # to determine if they did so, and zero out the ones they didn't if no
+ # axis is present.
+ @margins = {}
+ for pos in ['top', 'right', 'bottom', 'left']
+ @margins[pos] = if @options.margins? and @options.margins[pos]?
+ @options.margins[pos]
+ else if @hasAxis(pos)
+ defaultAxisMargins[pos]
+ else
+ 6
+
+ # Add a translation for the top and left margins
+ @g = @svg.append("g")
+ .attr("transform", "translate(#{@margins.left}, #{@margins.top})")
+
+ # Register option change events
+ @onAll optionListeners
+
+ # Sets the tick formatting function to use on the given axis.
+ # @param [String] axis Name of the axis.
+ # @param [Function] fn Formatting function to use.
+ setTickFormat: (axis, fn) ->
+ @options.tickFormats[axis] = fn
+
+ # @return [Boolean] <code>true</code> if the chart has an axis with a given name, <code>false</code> otherwise.
+ # @param [String] axis Name of axis to check.
+ hasAxis: (axis) ->
+ @options.axes.indexOf(axis) > -1
+
+ # @return [Number] Width of the visualization portion of the chart (width - margins).
+ innerWidth: ->
+ @width - (@margins.left + @margins.right)
+
+ # @return [Number] Height of the visualization portion of the chart (height - margins).
+ innerHeight: ->
+ @height - (@margins.top + @margins.bottom)
+
+ # @return [Function] The x scale for the visualization.
+ x: ->
+ domain = @options.domain ? @extent((d) -> d.x)
+ d3.scale.linear()
+ .domain(domain)
+ .range([0, @innerWidth()])
+
+ # @return [Function] The y scale for the visualization.
+ y: (givenDomain) ->
+ d3.scale.linear()
+ .domain(@_getScaleDomain(givenDomain))
+ .range([@innerHeight(), 0])
+
+ # @return [Function] d3 axis to use for the bottom of the visualization.
+ bottomAxis: ->
+ d3.svg.axis().scale(@x()).orient('bottom')
+ .ticks(@options.ticks.bottom)
+ .tickFormat(@options.tickFormats.bottom)
+
+ # @return [Function] d3 axis to use for the top of the visualization.
+ topAxis: ->
+ d3.svg.axis().scale(@x()).orient('top')
+ .ticks(@options.ticks.top)
+ .tickFormat(@options.tickFormats.top)
+
+ # @return [Function] d3 axis to use on the left of the visualization.
+ leftAxis: ->
+ range = if @options.range then @options.range.left else null
+ d3.svg.axis().scale(@y(range)).orient('left')
+ .ticks(@options.ticks.left)
+ .tickFormat(@options.tickFormats.left)
+
+ # @return [Function] d3 axis to use on the right of the visualization.
+ rightAxis: ->
+ range = if @options.range then @options.range.right else null
+ d3.svg.axis().scale(@y(range)).orient('right')
+ .ticks(@options.ticks.right)
+ .tickFormat(@options.tickFormats.right)
+
+ # Renders the axes for the visualization (subclasses must implement specific
+ # drawing routines).
+ draw: ->
+ if @_axesDrawn
+ @_redrawAxes()
+ else
+ @_drawAxes()
+ super()
+
+ # Redraws the axes for the visualization.
+ _redrawAxes: ->
+ if @hasAxis('bottom')
+ @g.selectAll('.x.axis.bottom').transition()
+ .duration(500)
+ .ease('linear')
+ .call(@bottomAxis())
+ if @hasAxis('top')
+ @g.selectAll('.x.axis.top').transition()
+ .duration(500)
+ .ease('linear')
+ .call(@topAxis())
+ if @hasAxis('left')
+ @g.selectAll('.y.axis.left').transition()
+ .duration(500)
+ .ease('linear')
+ .call(@leftAxis())
+ if @hasAxis('right')
+ @g.selectAll('.y.axis.right').transition()
+ .duration(500)
+ .ease('linear')
+ .call(@rightAxis())
+
+ # Draws the initial axes for the visualization.
+ _drawAxes: ->
+ if @hasAxis('bottom')
+ @g.append("g")
+ .attr("class", "x axis bottom")
+ .attr("transform", "translate(0, #{@innerHeight()})")
+ .call(@bottomAxis())
+ if @hasAxis('top')
+ @g.append("g")
+ .attr('class', 'x axis top')
+ .call(@topAxis())
+ if @hasAxis('left')
+ @g.append("g")
+ .attr("class", "y axis left")
+ .call(@leftAxis())
+ if @hasAxis('right')
+ @g.append('g')
+ .attr('class', 'y axis right')
+ .attr('transform', "translate(#{@innerWidth()}, 0)")
+ .call(@rightAxis())
+ @_axesDrawn = true
+
+ dimensionsChanged: ->
+ super()
+ @g.selectAll('.axis').remove()
+ @_axesDrawn = false
+ @draw()
+
+ # Updates margins in response to a <code>option:margin.*</code> event.
+ marginsChanged: ->
+ return unless @options.margins?
+ for own pos, size of @options.margins
+ unless size?
+ @margins[pos] = 6
+ else
+ @margins[pos] = size
+
+ @g.transition()
+ .duration(750)
+ .attr("transform", "translate(#{@margins.left}, #{@margins.top})")
+
+ @draw()
+
+ # Updates axes in response to a <code>option:axes</code> event.
+ axesChanged: ->
+ # Remove default axis margins
+ for pos in ['top', 'right', 'bottom', 'left']
+ continue if @options.margins? and @options.margins[pos]?
+ if @hasAxis(pos)
+ @margins[pos] = defaultAxisMargins[pos]
+ else
+ @margins[pos] = 6
+
+ # Update the margin offset
+ @g.transition()
+ .duration(750)
+ .attr("transform", "translate(#{@margins.left}, #{@margins.top})")
+
+ # Remove the axes and redraw
+ @g.selectAll('.axis').remove()
+ @_axesDrawn = false
+ @draw()
+
+ # Updates ticks in response to a <code>option:ticks.*</code> event.
+ ticksChanged: -> @draw()
+
+ # Updates tick formats in response to a <code>option:tickFormats.*</code> event.
+ tickFormatsChanged: -> @draw()
+
+ # Updates chart in response to a <code>option:domain</code> event.
+ domainChanged: -> @draw()
+
+ # Updates chart in response to a <code>option:range</code> event.
+ rangeChanged: -> @draw()
+
+# "They will see us waving from such great heights, come down now..." - The Postal Service
diff --git a/debian/missing-sources/epoch/src/basic/area.coffee b/debian/missing-sources/epoch/src/basic/area.coffee
new file mode 100644
index 0000000..e44b480
--- /dev/null
+++ b/debian/missing-sources/epoch/src/basic/area.coffee
@@ -0,0 +1,51 @@
+
+# Static stacked area chart implementation using d3.
+class Epoch.Chart.Area extends Epoch.Chart.Plot
+ constructor: (@options={}) ->
+ @options.type ?= 'area'
+ super(@options)
+ @draw()
+
+ # Generates a scale needed to appropriately render the stacked visualization.
+ # @return [Function] The y scale for the visualization.
+ y: ->
+ a = []
+ for layer in @getVisibleLayers()
+ for own k, v of layer.values
+ a[k] += v.y if a[k]?
+ a[k] = v.y unless a[k]?
+ d3.scale.linear()
+ .domain(@options.range ? [0, d3.max(a)])
+ .range([@height - @margins.top - @margins.bottom, 0])
+
+ # Renders the SVG elements needed to display the stacked area chart.
+ draw: ->
+ [x, y, layers] = [@x(), @y(), @getVisibleLayers()]
+
+ @g.selectAll('.layer').remove()
+ return if layers.length == 0
+
+ area = d3.svg.area()
+ .x((d) -> x(d.x))
+ .y0((d) -> y(d.y0))
+ .y1((d) -> y(d.y0 + d.y))
+
+ stack = d3.layout.stack()
+ .values((d) -> d.values)
+
+ data = stack layers
+
+ layer = @g.selectAll('.layer')
+ .data(layers, (d) -> d.category)
+
+ layer.select('.area')
+ .attr('d', (d) -> area(d.values))
+
+ layer.enter().append('g')
+ .attr('class', (d) -> d.className)
+
+ layer.append('path')
+ .attr('class', 'area')
+ .attr('d', (d) -> area(d.values))
+
+ super()
diff --git a/debian/missing-sources/epoch/src/basic/bar.coffee b/debian/missing-sources/epoch/src/basic/bar.coffee
new file mode 100644
index 0000000..8fbc427
--- /dev/null
+++ b/debian/missing-sources/epoch/src/basic/bar.coffee
@@ -0,0 +1,273 @@
+# Static bar chart implementation (using d3).
+class Epoch.Chart.Bar extends Epoch.Chart.Plot
+ defaults =
+ type: 'bar'
+ style: 'grouped'
+ orientation: 'vertical'
+ padding:
+ bar: 0.08
+ group: 0.1
+ outerPadding:
+ bar: 0.08
+ group: 0.1
+
+ horizontal_specific =
+ tickFormats:
+ top: Epoch.Formats.si
+ bottom: Epoch.Formats.si
+ left: Epoch.Formats.regular
+ right: Epoch.Formats.regular
+
+ horizontal_defaults = Epoch.Util.defaults(horizontal_specific, defaults)
+
+ optionListeners =
+ 'option:orientation': 'orientationChanged'
+ 'option:padding': 'paddingChanged'
+ 'option:outerPadding': 'paddingChanged'
+ 'option:padding:bar': 'paddingChanged'
+ 'option:padding:group': 'paddingChanged'
+ 'option:outerPadding:bar': 'paddingChanged'
+ 'option:outerPadding:group': 'paddingChanged'
+
+ constructor: (@options={}) ->
+ if @_isHorizontal()
+ @options = Epoch.Util.defaults(@options, horizontal_defaults)
+ else
+ @options = Epoch.Util.defaults(@options, defaults)
+ super(@options)
+ @onAll optionListeners
+ @draw()
+
+ # @return [Boolean] True if the chart is vertical, false otherwise
+ _isVertical: ->
+ @options.orientation == 'vertical'
+
+ # @return [Boolean] True if the chart is horizontal, false otherwise
+ _isHorizontal: ->
+ @options.orientation == 'horizontal'
+
+ # @return [Function] The scale used to generate the chart's x scale.
+ x: ->
+ if @_isVertical()
+ d3.scale.ordinal()
+ .domain(Epoch.Util.domain(@getVisibleLayers()))
+ .rangeRoundBands([0, @innerWidth()], @options.padding.group, @options.outerPadding.group)
+ else
+ extent = @extent((d) -> d.y)
+ extent[0] = Math.min(0, extent[0])
+ d3.scale.linear()
+ .domain(extent)
+ .range([0, @width - @margins.left - @margins.right])
+
+ # @return [Function] The x scale used to render the horizontal bar chart.
+ x1: (x0) ->
+ d3.scale.ordinal()
+ .domain((layer.category for layer in @getVisibleLayers()))
+ .rangeRoundBands([0, x0.rangeBand()], @options.padding.bar, @options.outerPadding.bar)
+
+ # @return [Function] The y scale used to render the bar chart.
+ y: ->
+ if @_isVertical()
+ extent = @extent((d) -> d.y)
+ extent[0] = Math.min(0, extent[0])
+ d3.scale.linear()
+ .domain(extent)
+ .range([@height - @margins.top - @margins.bottom, 0])
+ else
+ d3.scale.ordinal()
+ .domain(Epoch.Util.domain(@getVisibleLayers()))
+ .rangeRoundBands([0, @innerHeight()], @options.padding.group, @options.outerPadding.group)
+
+ # @return [Function] The x scale used to render the vertical bar chart.
+ y1: (y0) ->
+ d3.scale.ordinal()
+ .domain((layer.category for layer in @getVisibleLayers()))
+ .rangeRoundBands([0, y0.rangeBand()], @options.padding.bar, @options.outerPadding.bar)
+
+ # Remaps the bar chart data into a form that is easier to display.
+ # @return [Array] The reorganized data.
+ _remapData: ->
+ map = {}
+ for layer in @getVisibleLayers()
+ className = 'bar ' + layer.className.replace(/\s*layer\s*/, '')
+ for entry in layer.values
+ map[entry.x] ?= []
+ map[entry.x].push { label: layer.category, y: entry.y, className: className }
+ ({group: k, values: v} for own k, v of map)
+
+ # Draws the bar char.
+ draw: ->
+ if @_isVertical()
+ @_drawVertical()
+ else
+ @_drawHorizontal()
+ super()
+
+ # Draws the bar chart with a vertical orientation
+ _drawVertical: ->
+ [x0, y] = [@x(), @y()]
+ x1 = @x1(x0)
+ height = @height - @margins.top - @margins.bottom
+ data = @_remapData()
+
+ # 1) Join
+ layer = @g.selectAll(".layer")
+ .data(data, (d) -> d.group)
+
+ # 2) Update
+ layer.transition().duration(750)
+ .attr("transform", (d) -> "translate(#{x0(d.group)}, 0)")
+
+ # 3) Enter / Create
+ layer.enter().append("g")
+ .attr('class', 'layer')
+ .attr("transform", (d) -> "translate(#{x0(d.group)}, 0)")
+
+ rects = layer.selectAll('rect')
+ .data((group) -> group.values)
+
+ rects.attr('class', (d) -> d.className)
+
+ rects.transition().duration(600)
+ .attr('x', (d) -> x1(d.label))
+ .attr('y', (d) -> y(d.y))
+ .attr('width', x1.rangeBand())
+ .attr('height', (d) -> height - y(d.y))
+
+ rects.enter().append('rect')
+ .attr('class', (d) -> d.className)
+ .attr('x', (d) -> x1(d.label))
+ .attr('y', (d) -> y(d.y))
+ .attr('width', x1.rangeBand())
+ .attr('height', (d) -> height - y(d.y))
+
+ rects.exit().transition()
+ .duration(150)
+ .style('opacity', '0')
+ .remove()
+
+ # 4) Update new and existing
+
+ # 5) Exit / Remove
+ layer.exit()
+ .transition()
+ .duration(750)
+ .style('opacity', '0')
+ .remove()
+
+ # Draws the bar chart with a horizontal orientation
+ _drawHorizontal: ->
+ [x, y0] = [@x(), @y()]
+ y1 = @y1(y0)
+ width = @width - @margins.left - @margins.right
+ data = @_remapData()
+
+ # 1) Join
+ layer = @g.selectAll(".layer")
+ .data(data, (d) -> d.group)
+
+ # 2) Update
+ layer.transition().duration(750)
+ .attr("transform", (d) -> "translate(0, #{y0(d.group)})")
+
+ # 3) Enter / Create
+ layer.enter().append("g")
+ .attr('class', 'layer')
+ .attr("transform", (d) -> "translate(0, #{y0(d.group)})")
+
+ rects = layer.selectAll('rect')
+ .data((group) -> group.values)
+
+ rects.attr('class', (d) -> d.className)
+
+ rects.transition().duration(600)
+ .attr('x', (d) -> 0)
+ .attr('y', (d) -> y1(d.label))
+ .attr('height', y1.rangeBand())
+ .attr('width', (d) -> x(d.y))
+
+ rects.enter().append('rect')
+ .attr('class', (d) -> d.className)
+ .attr('x', (d) -> 0)
+ .attr('y', (d) -> y1(d.label))
+ .attr('height', y1.rangeBand())
+ .attr('width', (d) -> x(d.y))
+
+ rects.exit().transition()
+ .duration(150)
+ .style('opacity', '0')
+ .remove()
+
+ # 4) Update new and existing
+
+ # 5) Exit / Remove
+ layer.exit()
+ .transition()
+ .duration(750)
+ .style('opacity', '0')
+ .remove()
+
+ # Generates specific tick marks to emulate d3's linear scale axis ticks
+ # for ordinal scales. Note: this should only be called if the user has
+ # defined a set number of ticks for a given axis.
+ # @param [Number] numTicks Number of ticks to generate
+ # @param [String] dataKey Property name of a datum to use for the tick value
+ # @return [Array] The ticks for the given axis
+ _getTickValues: (numTicks, dataKey='x') ->
+ return [] unless @data[0]?
+ total = @data[0].values.length
+ step = Math.ceil(total / numTicks)|0
+ tickValues = (@data[0].values[i].x for i in [0...total] by step)
+
+ # @return [Function] d3 axis to use for the bottom of the visualization.
+ bottomAxis: ->
+ axis = d3.svg.axis().scale(@x()).orient('bottom')
+ .ticks(@options.ticks.bottom)
+ .tickFormat(@options.tickFormats.bottom)
+ if @_isVertical() and @options.ticks.bottom?
+ axis.tickValues @_getTickValues(@options.ticks.bottom)
+ axis
+
+ # @return [Function] d3 axis to use for the top of the visualization.
+ topAxis: ->
+ axis = d3.svg.axis().scale(@x()).orient('top')
+ .ticks(@options.ticks.top)
+ .tickFormat(@options.tickFormats.top)
+ if @_isVertical() and @options.ticks.top?
+ axis.tickValues @_getTickValues(@options.ticks.top)
+ axis
+
+ # @return [Function] d3 axis to use on the left of the visualization.
+ leftAxis: ->
+ axis = d3.svg.axis().scale(@y()).orient('left')
+ .ticks(@options.ticks.left)
+ .tickFormat(@options.tickFormats.left)
+ if @_isHorizontal() and @options.ticks.left?
+ axis.tickValues @_getTickValues(@options.ticks.left)
+ axis
+
+ # @return [Function] d3 axis to use on the right of the visualization.
+ rightAxis: ->
+ axis = d3.svg.axis().scale(@y()).orient('right')
+ .ticks(@options.ticks.right)
+ .tickFormat(@options.tickFormats.right)
+ if @_isHorizontal() and @options.ticks.right?
+ axis.tickValues @_getTickValues(@options.ticks.right)
+ axis
+
+ # Updates orientation in response <code>option:orientation</code>.
+ orientationChanged: ->
+ top = @options.tickFormats.top
+ bottom = @options.tickFormats.bottom
+ left = @options.tickFormats.left
+ right = @options.tickFormats.right
+
+ @options.tickFormats.left = top
+ @options.tickFormats.right = bottom
+ @options.tickFormats.top = left
+ @options.tickFormats.bottom = right
+
+ @draw()
+
+ # Updates padding in response to <code>option:padding:*</code> and <code>option:outerPadding:*</code>.
+ paddingChanged: -> @draw()
diff --git a/debian/missing-sources/epoch/src/basic/histogram.coffee b/debian/missing-sources/epoch/src/basic/histogram.coffee
new file mode 100644
index 0000000..4548e67
--- /dev/null
+++ b/debian/missing-sources/epoch/src/basic/histogram.coffee
@@ -0,0 +1,61 @@
+class Epoch.Chart.Histogram extends Epoch.Chart.Bar
+ defaults =
+ type: 'histogram'
+ domain: [0, 100]
+ bucketRange: [0, 100]
+ buckets: 10
+ cutOutliers: false
+
+ optionListeners =
+ 'option:bucketRange': 'bucketRangeChanged'
+ 'option:buckets': 'bucketsChanged'
+ 'option:cutOutliers': 'cutOutliersChanged'
+
+ constructor: (@options={}) ->
+ super(@options = Epoch.Util.defaults(@options, defaults))
+ @onAll optionListeners
+ @draw()
+
+ # Prepares data by sorting it into histogram buckets as instructed by the chart options.
+ # @param [Array] data Data to prepare for rendering.
+ # @return [Array] The data prepared to be displayed as a histogram.
+ _prepareData: (data) ->
+ bucketSize = (@options.bucketRange[1] - @options.bucketRange[0]) / @options.buckets
+
+ prepared = []
+ for layer in data
+ buckets = (0 for i in [0...@options.buckets])
+ for point in layer.values
+ index = parseInt((point.x - @options.bucketRange[0]) / bucketSize)
+
+ if @options.cutOutliers and ((index < 0) or (index >= @options.buckets))
+ continue
+ if index < 0
+ index = 0
+ else if index >= @options.buckets
+ index = @options.buckets - 1
+
+ buckets[index] += parseInt point.y
+
+ preparedLayer = { values: (buckets.map (d, i) -> {x: parseInt(i) * bucketSize, y: d}) }
+ for own k, v of layer
+ preparedLayer[k] = v unless k == 'values'
+
+ prepared.push preparedLayer
+
+ return prepared
+
+ # Called when options change, this prepares the raw data for the chart according to the new
+ # options, sets it, and renders the chart.
+ resetData: ->
+ @setData @rawData
+ @draw()
+
+ # Updates the chart in response to an <code>option:bucketRange</code> event.
+ bucketRangeChanged: -> @resetData()
+
+ # Updates the chart in response to an <code>option:buckets</code> event.
+ bucketsChanged: -> @resetData()
+
+ # Updates the chart in response to an <code>option:cutOutliers</code> event.
+ cutOutliersChanged: -> @resetData()
diff --git a/debian/missing-sources/epoch/src/basic/line.coffee b/debian/missing-sources/epoch/src/basic/line.coffee
new file mode 100644
index 0000000..de56780
--- /dev/null
+++ b/debian/missing-sources/epoch/src/basic/line.coffee
@@ -0,0 +1,46 @@
+# Static line chart implementation (using d3).
+class Epoch.Chart.Line extends Epoch.Chart.Plot
+ constructor: (@options={}) ->
+ @options.type ?= 'line'
+ super(@options)
+ @draw()
+
+ # @return [Function] The line generator used to construct the plot.
+ line: (layer) ->
+ [x, y] = [@x(), @y(layer.range)]
+ d3.svg.line()
+ .x((d) -> x(d.x))
+ .y((d) -> y(d.y))
+
+ # Draws the line chart.
+ draw: ->
+ [x, y, layers] = [@x(), @y(), @getVisibleLayers()]
+
+ # Zero visible layers, just drop all and get out
+ if layers.length == 0
+ return @g.selectAll('.layer').remove()
+
+ # 1) Join
+ layer = @g.selectAll('.layer')
+ .data(layers, (d) -> d.category)
+
+ # 2) Update (only existing)
+ layer.select('.line').transition().duration(500)
+ .attr('d', (l) => @line(l)(l.values))
+
+ # 3) Enter (Create)
+ layer.enter().append('g')
+ .attr('class', (l) -> l.className)
+ .append('path')
+ .attr('class', 'line')
+ .attr('d', (l) => @line(l)(l.values))
+
+ # 4) Update (existing & new)
+ # Nuuupp
+
+ # 5) Exit (Remove)
+ layer.exit().transition().duration(750)
+ .style('opacity', '0')
+ .remove()
+
+ super()
diff --git a/debian/missing-sources/epoch/src/basic/pie.coffee b/debian/missing-sources/epoch/src/basic/pie.coffee
new file mode 100644
index 0000000..e794a2f
--- /dev/null
+++ b/debian/missing-sources/epoch/src/basic/pie.coffee
@@ -0,0 +1,59 @@
+
+# Static Pie Chart implementation (using d3).
+class Epoch.Chart.Pie extends Epoch.Chart.SVG
+ defaults =
+ type: 'pie'
+ margin: 10
+ inner: 0
+
+ # Creates a new pie chart.
+ # @param [Object] options Options for the pie chart.
+ # @option options [Number] margin Margins to add around the pie chart (default: 10).
+ # @option options [Number] inner The inner radius for the chart (default: 0).
+ constructor: (@options={}) ->
+ super(@options = Epoch.Util.defaults(@options, defaults))
+ @pie = d3.layout.pie().sort(null)
+ .value (d) -> d.value
+ @arc = d3.svg.arc()
+ .outerRadius(=> (Math.max(@width, @height) / 2) - @options.margin)
+ .innerRadius(=> @options.inner)
+ @g = @svg.append('g')
+ .attr("transform", "translate(#{@width/2}, #{@height/2})")
+ @on 'option:margin', 'marginChanged'
+ @on 'option:inner', 'innerChanged'
+ @draw()
+
+ # Draws the pie chart
+ draw: ->
+ @g.selectAll('.arc').remove()
+
+ arcs = @g.selectAll(".arc")
+ .data(@pie(@getVisibleLayers()), (d) -> d.data.category)
+
+ arcs.enter().append('g')
+ .attr('class', (d) -> "arc pie " + d.data.className)
+
+ arcs.select('path')
+ .attr('d', @arc)
+
+ arcs.select('text')
+ .attr("transform", (d) => "translate(#{@arc.centroid(d)})")
+ .text((d) -> d.data.label or d.data.category)
+
+ path = arcs.append("path")
+ .attr("d", @arc)
+ .each((d) -> @._current = d)
+
+ text = arcs.append("text")
+ .attr("transform", (d) => "translate(#{@arc.centroid(d)})")
+ .attr("dy", ".35em")
+ .style("text-anchor", "middle")
+ .text((d) -> d.data.label or d.data.category)
+
+ super()
+
+ # Updates margins in response to an <code>option:margin</code> event.
+ marginChanged: -> @draw()
+
+ # Updates inner margin in response to an <code>option:inner</code> event.
+ innerChanged: -> @draw()
diff --git a/debian/missing-sources/epoch/src/basic/scatter.coffee b/debian/missing-sources/epoch/src/basic/scatter.coffee
new file mode 100644
index 0000000..b8563ec
--- /dev/null
+++ b/debian/missing-sources/epoch/src/basic/scatter.coffee
@@ -0,0 +1,59 @@
+
+# Static scatter plot implementation (using d3).
+class Epoch.Chart.Scatter extends Epoch.Chart.Plot
+ defaults =
+ type: 'scatter'
+ radius: 3.5
+ axes: ['top', 'bottom', 'left', 'right']
+
+ # Creates a new scatter plot.
+ # @param [Object] options Options for the plot.
+ # @option options [Number] radius The default radius to use for the points in
+ # the plot (default 3.5). This can be overrwitten by individual points.
+ constructor: (@options={}) ->
+ super(@options = Epoch.Util.defaults(@options, defaults))
+ @on 'option:radius', 'radiusChanged'
+ @draw()
+
+ # Draws the scatter plot.
+ draw: ->
+ [x, y, layers] = [@x(), @y(), @getVisibleLayers()]
+ radius = @options.radius
+
+ if layers.length == 0
+ return @g.selectAll('.layer').remove()
+
+ layer = @g.selectAll('.layer')
+ .data(layers, (d) -> d.category)
+
+ layer.enter().append('g')
+ .attr('class', (d) -> d.className)
+
+ dots = layer.selectAll('.dot')
+ .data((l) -> l.values)
+
+ dots.transition().duration(500)
+ .attr("r", (d) -> d.r ? radius)
+ .attr("cx", (d) -> x(d.x))
+ .attr("cy", (d) -> y(d.y))
+
+ dots.enter().append('circle')
+ .attr('class', 'dot')
+ .attr("r", (d) -> d.r ? radius)
+ .attr("cx", (d) -> x(d.x))
+ .attr("cy", (d) -> y(d.y))
+
+ dots.exit().transition()
+ .duration(750)
+ .style('opacity', 0)
+ .remove()
+
+ layer.exit().transition()
+ .duration(750)
+ .style('opacity', 0)
+ .remove()
+
+ super()
+
+ # Updates radius in response to an <code>option:radius</code> event.
+ radiusChanged: -> @draw()