d3_rails 2.8.0 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.DS_Store +0 -0
- data/README.md +27 -5
- data/lib/d3_rails/version.rb +1 -1
- data/vendor/.DS_Store +0 -0
- data/vendor/assets/.DS_Store +0 -0
- data/vendor/assets/javascripts/.DS_Store +0 -0
- data/vendor/assets/javascripts/d3.v2.js +99 -80
- data/vendor/assets/javascripts/morris.js +1 -0
- data/vendor/assets/javascripts/morris/.DS_Store +0 -0
- data/vendor/assets/javascripts/morris/Makefile +10 -0
- data/vendor/assets/javascripts/morris/README.md +87 -0
- data/vendor/assets/javascripts/morris/examples/_template.html +18 -0
- data/vendor/assets/javascripts/morris/examples/days.html +36 -0
- data/vendor/assets/javascripts/morris/examples/decimal.html +31 -0
- data/vendor/assets/javascripts/morris/examples/lib/example.css +13 -0
- data/vendor/assets/javascripts/morris/examples/lib/example.js +4 -0
- data/vendor/assets/javascripts/morris/examples/lib/prettify.css +1 -0
- data/vendor/assets/javascripts/morris/examples/lib/prettify.js +28 -0
- data/vendor/assets/javascripts/morris/examples/months-no-smooth.html +37 -0
- data/vendor/assets/javascripts/morris/examples/negative.html +35 -0
- data/vendor/assets/javascripts/morris/examples/non-date.html +36 -0
- data/vendor/assets/javascripts/morris/examples/quarters.html +53 -0
- data/vendor/assets/javascripts/morris/examples/timestamps.html +37 -0
- data/vendor/assets/javascripts/morris/examples/weeks.html +52 -0
- data/vendor/assets/javascripts/morris/morris.coffee +444 -0
- data/vendor/assets/javascripts/morris/morris.js +493 -0
- data/vendor/assets/javascripts/morris/morris.min.js +1 -0
- data/vendor/assets/javascripts/tesseract.js +1 -0
- data/vendor/assets/javascripts/tesseract/.gitignore +2 -0
- data/vendor/assets/javascripts/tesseract/LICENSE +12 -0
- data/vendor/assets/javascripts/tesseract/Makefile +48 -0
- data/vendor/assets/javascripts/tesseract/README.md +11 -0
- data/vendor/assets/javascripts/tesseract/index.js +1 -0
- data/vendor/assets/javascripts/tesseract/lib/dart/AUTHORS +9 -0
- data/vendor/assets/javascripts/tesseract/lib/dart/LICENSE +25 -0
- data/vendor/assets/javascripts/tesseract/lib/dart/dual_pivot_quicksort.dart +342 -0
- data/vendor/assets/javascripts/tesseract/package.json +11 -0
- data/vendor/assets/javascripts/tesseract/src/array.js +32 -0
- data/vendor/assets/javascripts/tesseract/src/bisect.js +44 -0
- data/vendor/assets/javascripts/tesseract/src/filter.js +19 -0
- data/vendor/assets/javascripts/tesseract/src/heap.js +44 -0
- data/vendor/assets/javascripts/tesseract/src/heapselect.js +36 -0
- data/vendor/assets/javascripts/tesseract/src/identity.js +3 -0
- data/vendor/assets/javascripts/tesseract/src/insertionsort.js +18 -0
- data/vendor/assets/javascripts/tesseract/src/null.js +3 -0
- data/vendor/assets/javascripts/tesseract/src/package.js +14 -0
- data/vendor/assets/javascripts/tesseract/src/permute.js +8 -0
- data/vendor/assets/javascripts/tesseract/src/quicksort.js +282 -0
- data/vendor/assets/javascripts/tesseract/src/reduce.js +19 -0
- data/vendor/assets/javascripts/tesseract/src/tesseract.js +663 -0
- data/vendor/assets/javascripts/tesseract/src/version.js +1 -0
- data/vendor/assets/javascripts/tesseract/src/zero.js +3 -0
- data/vendor/assets/javascripts/tesseract/tesseract.js +1177 -0
- data/vendor/assets/javascripts/tesseract/tesseract.min.js +1 -0
- data/vendor/assets/javascripts/tesseract/test/benchmark.js +177 -0
- data/vendor/assets/javascripts/tesseract/test/bisect-test.js +206 -0
- data/vendor/assets/javascripts/tesseract/test/heap-test.js +44 -0
- data/vendor/assets/javascripts/tesseract/test/permute-test.js +51 -0
- data/vendor/assets/javascripts/tesseract/test/select-test.js +63 -0
- data/vendor/assets/javascripts/tesseract/test/sort-test.js +83 -0
- data/vendor/assets/javascripts/tesseract/test/tesseract-test.js +655 -0
- data/vendor/assets/javascripts/tesseract/test/version-test.js +16 -0
- metadata +63 -8
@@ -0,0 +1,52 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<head>
|
3
|
+
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
|
4
|
+
<script src="https://raw.github.com/DmitryBaranovskiy/raphael/300aa589f5a0ba7fce667cd62c7cdda0bd5ad904/raphael-min.js"></script>
|
5
|
+
<script src="../morris.js"></script>
|
6
|
+
<script src="lib/prettify.js"></script>
|
7
|
+
<script src="lib/example.js"></script>
|
8
|
+
<link rel="stylesheet" href="lib/example.css">
|
9
|
+
<link rel="stylesheet" href="lib/prettify.css">
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
<h1>Formatting Dates With Weeks</h1>
|
13
|
+
<div id="graph"></div>
|
14
|
+
<pre id="code" class="prettyprint linenums">
|
15
|
+
var week_data = [
|
16
|
+
{"period": "2011 W27", "licensed": 3407, "sorned": 660},
|
17
|
+
{"period": "2011 W26", "licensed": 3351, "sorned": 629},
|
18
|
+
{"period": "2011 W25", "licensed": 3269, "sorned": 618},
|
19
|
+
{"period": "2011 W24", "licensed": 3246, "sorned": 661},
|
20
|
+
{"period": "2011 W23", "licensed": 3257, "sorned": 667},
|
21
|
+
{"period": "2011 W22", "licensed": 3248, "sorned": 627},
|
22
|
+
{"period": "2011 W21", "licensed": 3171, "sorned": 660},
|
23
|
+
{"period": "2011 W20", "licensed": 3171, "sorned": 676},
|
24
|
+
{"period": "2011 W19", "licensed": 3201, "sorned": 656},
|
25
|
+
{"period": "2011 W18", "licensed": 3215, "sorned": 622},
|
26
|
+
{"period": "2011 W17", "licensed": 3148, "sorned": 632},
|
27
|
+
{"period": "2011 W16", "licensed": 3155, "sorned": 681},
|
28
|
+
{"period": "2011 W15", "licensed": 3190, "sorned": 667},
|
29
|
+
{"period": "2011 W14", "licensed": 3226, "sorned": 620},
|
30
|
+
{"period": "2011 W13", "licensed": 3245, "sorned": null},
|
31
|
+
{"period": "2011 W12", "licensed": 3289, "sorned": null},
|
32
|
+
{"period": "2011 W11", "licensed": 3263, "sorned": null},
|
33
|
+
{"period": "2011 W10", "licensed": 3189, "sorned": null},
|
34
|
+
{"period": "2011 W09", "licensed": 3079, "sorned": null},
|
35
|
+
{"period": "2011 W08", "licensed": 3085, "sorned": null},
|
36
|
+
{"period": "2011 W07", "licensed": 3055, "sorned": null},
|
37
|
+
{"period": "2011 W06", "licensed": 3063, "sorned": null},
|
38
|
+
{"period": "2011 W05", "licensed": 2943, "sorned": null},
|
39
|
+
{"period": "2011 W04", "licensed": 2806, "sorned": null},
|
40
|
+
{"period": "2011 W03", "licensed": 2674, "sorned": null},
|
41
|
+
{"period": "2011 W02", "licensed": 1702, "sorned": null},
|
42
|
+
{"period": "2011 W01", "licensed": 1732, "sorned": null}
|
43
|
+
];
|
44
|
+
Morris.Line({
|
45
|
+
element: 'graph',
|
46
|
+
data: week_data,
|
47
|
+
xkey: 'period',
|
48
|
+
ykeys: ['licensed', 'sorned'],
|
49
|
+
labels: ['Licensed', 'SORN']
|
50
|
+
});
|
51
|
+
</pre>
|
52
|
+
</body>
|
@@ -0,0 +1,444 @@
|
|
1
|
+
# The original line graph.
|
2
|
+
#
|
3
|
+
$ = jQuery
|
4
|
+
|
5
|
+
Morris = {}
|
6
|
+
class Morris.Line
|
7
|
+
# Initialise the graph.
|
8
|
+
#
|
9
|
+
# @param {Object} options
|
10
|
+
constructor: (options) ->
|
11
|
+
if not (this instanceof Morris.Line)
|
12
|
+
return new Morris.Line(options)
|
13
|
+
if typeof options.element is 'string'
|
14
|
+
@el = $ document.getElementById(options.element)
|
15
|
+
else
|
16
|
+
@el = $ options.element
|
17
|
+
@options = $.extend {}, @defaults, options
|
18
|
+
# bail if there's no data
|
19
|
+
if @options.data is undefined or @options.data.length is 0
|
20
|
+
return
|
21
|
+
@el.addClass 'graph-initialised'
|
22
|
+
@precalc()
|
23
|
+
@redraw()
|
24
|
+
|
25
|
+
# Default configuration
|
26
|
+
#
|
27
|
+
defaults:
|
28
|
+
lineWidth: 3
|
29
|
+
pointSize: 4
|
30
|
+
lineColors: [
|
31
|
+
'#0b62a4'
|
32
|
+
'#7A92A3'
|
33
|
+
'#4da74d'
|
34
|
+
'#afd8f8'
|
35
|
+
'#edc240'
|
36
|
+
'#cb4b4b'
|
37
|
+
'#9440ed'
|
38
|
+
]
|
39
|
+
ymax: 'auto'
|
40
|
+
ymin: 'auto 0'
|
41
|
+
marginTop: 25
|
42
|
+
marginRight: 25
|
43
|
+
marginBottom: 30
|
44
|
+
marginLeft: 25
|
45
|
+
numLines: 5
|
46
|
+
gridLineColor: '#aaa'
|
47
|
+
gridTextColor: '#888'
|
48
|
+
gridTextSize: 12
|
49
|
+
gridStrokeWidth: 0.5
|
50
|
+
hoverPaddingX: 10
|
51
|
+
hoverPaddingY: 5
|
52
|
+
hoverMargin: 10
|
53
|
+
hoverFillColor: '#fff'
|
54
|
+
hoverBorderColor: '#ccc'
|
55
|
+
hoverBorderWidth: 2
|
56
|
+
hoverOpacity: 0.95
|
57
|
+
hoverLabelColor: '#444'
|
58
|
+
hoverFontSize: 12
|
59
|
+
smooth: true
|
60
|
+
hideHover: false
|
61
|
+
parseTime: true
|
62
|
+
units: ''
|
63
|
+
dateFormat: (x) -> new Date(x).toString()
|
64
|
+
|
65
|
+
# Do any necessary pre-processing for a new dataset
|
66
|
+
#
|
67
|
+
precalc: ->
|
68
|
+
# sort data
|
69
|
+
@options.data.sort (a, b) => (a[@options.xkey] < b[@options.xkey]) - (b[@options.xkey] < a[@options.xkey])
|
70
|
+
# extract labels
|
71
|
+
@columnLabels = $.map @options.data, (d) => d[@options.xkey]
|
72
|
+
@seriesLabels = @options.labels
|
73
|
+
|
74
|
+
# extract series data
|
75
|
+
@series = []
|
76
|
+
for ykey in @options.ykeys
|
77
|
+
series_data = []
|
78
|
+
for d in @options.data
|
79
|
+
series_data.push(d[ykey])
|
80
|
+
@series.push(series_data)
|
81
|
+
|
82
|
+
# translate x labels into nominal dates
|
83
|
+
# note: currently using decimal years to specify dates
|
84
|
+
if @options.parseTime
|
85
|
+
@xvals = $.map @columnLabels, (x) => @parseYear x
|
86
|
+
else
|
87
|
+
@xvals = [(@columnLabels.length-1)..0]
|
88
|
+
# translate column labels, if they're timestamps
|
89
|
+
if @options.parseTime
|
90
|
+
@columnLabels = $.map @columnLabels, (d) =>
|
91
|
+
if typeof d is 'number'
|
92
|
+
@options.dateFormat(d)
|
93
|
+
else
|
94
|
+
d
|
95
|
+
@xmin = Math.min.apply null, @xvals
|
96
|
+
@xmax = Math.max.apply null, @xvals
|
97
|
+
if @xmin is @xmax
|
98
|
+
@xmin -= 1
|
99
|
+
@xmax += 1
|
100
|
+
|
101
|
+
# Compute the vertical range of the graph if desired
|
102
|
+
if typeof @options.ymax is 'string' and @options.ymax[0..3] is 'auto'
|
103
|
+
# use Array.concat to flatten arrays and find the max y value
|
104
|
+
ymax = Math.max.apply null, Array.prototype.concat.apply([], @series)
|
105
|
+
if @options.ymax.length > 5
|
106
|
+
@options.ymax = Math.max parseInt(@options.ymax[5..], 10), ymax
|
107
|
+
else
|
108
|
+
@options.ymax = ymax
|
109
|
+
if typeof @options.ymin is 'string' and @options.ymin[0..3] is 'auto'
|
110
|
+
ymin = Math.min.apply null, Array.prototype.concat.apply([], @series)
|
111
|
+
if @options.ymin.length > 5
|
112
|
+
@options.ymin = Math.min parseInt(@options.ymin[5..], 10), ymin
|
113
|
+
else
|
114
|
+
@options.ymin = ymin
|
115
|
+
|
116
|
+
# Some instance variables for later
|
117
|
+
@pointGrow = Raphael.animation r: @options.pointSize + 3, 25, 'linear'
|
118
|
+
@pointShrink = Raphael.animation r: @options.pointSize, 25, 'linear'
|
119
|
+
@elementWidth = null
|
120
|
+
@elementHeight = null
|
121
|
+
# column hilight events
|
122
|
+
@prevHilight = null
|
123
|
+
@el.mousemove (evt) =>
|
124
|
+
@updateHilight evt.pageX
|
125
|
+
if @options.hideHover
|
126
|
+
@el.mouseout (evt) =>
|
127
|
+
@hilight null
|
128
|
+
touchHandler = (evt) =>
|
129
|
+
touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0]
|
130
|
+
@updateHilight touch.pageX
|
131
|
+
return touch
|
132
|
+
@el.bind 'touchstart', touchHandler
|
133
|
+
@el.bind 'touchmove', touchHandler
|
134
|
+
@el.bind 'touchend', touchHandler
|
135
|
+
|
136
|
+
# Do any size-related calculations
|
137
|
+
#
|
138
|
+
calc: ->
|
139
|
+
w = @el.width()
|
140
|
+
h = @el.height()
|
141
|
+
if @elementWidth != w or @elementHeight != h
|
142
|
+
# calculate grid dimensions
|
143
|
+
@maxYLabelWidth = Math.max(
|
144
|
+
@measureText(@options.ymin + @options.units, @options.gridTextSize).width,
|
145
|
+
@measureText(@options.ymax + @options.units, @options.gridTextSize).width)
|
146
|
+
@left = @maxYLabelWidth + @options.marginLeft
|
147
|
+
@width = @el.width() - @left - @options.marginRight
|
148
|
+
@height = @el.height() - @options.marginTop - @options.marginBottom
|
149
|
+
@dx = @width / (@xmax - @xmin)
|
150
|
+
@dy = @height / (@options.ymax - @options.ymin)
|
151
|
+
# calculate series data point coordinates
|
152
|
+
@columns = (@transX(x) for x in @xvals)
|
153
|
+
@seriesCoords = []
|
154
|
+
for s in @series
|
155
|
+
scoords = []
|
156
|
+
$.each s, (i, y) =>
|
157
|
+
if y == null
|
158
|
+
scoords.push(null)
|
159
|
+
else
|
160
|
+
scoords.push(x: @columns[i], y: @transY(y))
|
161
|
+
@seriesCoords.push(scoords)
|
162
|
+
# calculate hover margins
|
163
|
+
@hoverMargins = $.map @columns.slice(1), (x, i) => (x + @columns[i]) / 2
|
164
|
+
|
165
|
+
# quick translation helpers
|
166
|
+
#
|
167
|
+
transX: (x) =>
|
168
|
+
if @xvals.length is 1
|
169
|
+
@left + @width / 2
|
170
|
+
else
|
171
|
+
@left + (x - @xmin) * @dx
|
172
|
+
|
173
|
+
transY: (y) =>
|
174
|
+
return @options.marginTop + @height - (y - @options.ymin) * @dy
|
175
|
+
|
176
|
+
# Clear and redraw the graph
|
177
|
+
#
|
178
|
+
redraw: ->
|
179
|
+
# remove child elements (get rid of old drawings)
|
180
|
+
@el.empty()
|
181
|
+
|
182
|
+
# the raphael drawing instance
|
183
|
+
@r = new Raphael(@el[0])
|
184
|
+
|
185
|
+
@calc()
|
186
|
+
@drawGrid()
|
187
|
+
@drawSeries()
|
188
|
+
@drawHover()
|
189
|
+
@hilight(if @options.hideHover then null else 0)
|
190
|
+
|
191
|
+
# draw the grid, and axes labels
|
192
|
+
#
|
193
|
+
drawGrid: ->
|
194
|
+
# draw y axis labels, horizontal lines
|
195
|
+
yInterval = (@options.ymax - @options.ymin) / (@options.numLines - 1)
|
196
|
+
firstY = Math.ceil(@options.ymin / yInterval) * yInterval
|
197
|
+
lastY = Math.floor(@options.ymax / yInterval) * yInterval
|
198
|
+
for lineY in [firstY..lastY] by yInterval
|
199
|
+
v = Math.floor(lineY)
|
200
|
+
y = @transY(v)
|
201
|
+
@r.text(@left - @options.marginLeft/2, y, @commas(v) + @options.units)
|
202
|
+
.attr('font-size', @options.gridTextSize)
|
203
|
+
.attr('fill', @options.gridTextColor)
|
204
|
+
.attr('text-anchor', 'end')
|
205
|
+
@r.path("M#{@left},#{y}H#{@left + @width}")
|
206
|
+
.attr('stroke', @options.gridLineColor)
|
207
|
+
.attr('stroke-width', @options.gridStrokeWidth)
|
208
|
+
|
209
|
+
## draw x axis labels
|
210
|
+
prevLabelMargin = null
|
211
|
+
xLabelMargin = 50 # make this an option?
|
212
|
+
if @options.parseTime
|
213
|
+
x1 = new Date(@xmin).getFullYear()
|
214
|
+
x2 = new Date(@xmax).getFullYear()
|
215
|
+
else
|
216
|
+
x1 = @xmin
|
217
|
+
x2 = @xmax
|
218
|
+
for i in [x1..x2]
|
219
|
+
if @options.parseTime
|
220
|
+
xpos = new Date(i, 0, 1).getTime()
|
221
|
+
if xpos < @xmin
|
222
|
+
continue
|
223
|
+
else
|
224
|
+
xpos = i
|
225
|
+
labelText = if @options.parseTime then i else @columnLabels[@columnLabels.length-i-1]
|
226
|
+
label = @r.text(@transX(xpos), @options.marginTop + @height + @options.marginBottom / 2, labelText)
|
227
|
+
.attr('font-size', @options.gridTextSize)
|
228
|
+
.attr('fill', @options.gridTextColor)
|
229
|
+
labelBox = label.getBBox()
|
230
|
+
# ensure a minimum of `xLabelMargin` pixels between labels
|
231
|
+
if prevLabelMargin is null or prevLabelMargin <= labelBox.x
|
232
|
+
prevLabelMargin = labelBox.x + labelBox.width + xLabelMargin
|
233
|
+
else
|
234
|
+
label.remove()
|
235
|
+
|
236
|
+
# draw the data series
|
237
|
+
#
|
238
|
+
drawSeries: ->
|
239
|
+
for i in [@seriesCoords.length-1..0]
|
240
|
+
coords = @seriesCoords[i]
|
241
|
+
if coords.length > 1
|
242
|
+
path = @createPath coords, @options.marginTop, @left, @options.marginTop + @height, @left + @width
|
243
|
+
@r.path(path)
|
244
|
+
.attr('stroke', @options.lineColors[i])
|
245
|
+
.attr('stroke-width', @options.lineWidth)
|
246
|
+
@seriesPoints = ([] for i in [0..@seriesCoords.length-1])
|
247
|
+
for i in [@seriesCoords.length-1..0]
|
248
|
+
for c in @seriesCoords[i]
|
249
|
+
if c == null
|
250
|
+
circle = null
|
251
|
+
else
|
252
|
+
circle = @r.circle(c.x, c.y, @options.pointSize)
|
253
|
+
.attr('fill', @options.lineColors[i])
|
254
|
+
.attr('stroke-width', 1)
|
255
|
+
.attr('stroke', '#ffffff')
|
256
|
+
@seriesPoints[i].push(circle)
|
257
|
+
|
258
|
+
# create a path for a data series
|
259
|
+
#
|
260
|
+
createPath: (all_coords, top, left, bottom, right) ->
|
261
|
+
path = ""
|
262
|
+
coords = $.map(all_coords, (c) -> c)
|
263
|
+
if @options.smooth
|
264
|
+
grads = @gradients coords
|
265
|
+
for i in [0..coords.length-1]
|
266
|
+
c = coords[i]
|
267
|
+
if i is 0
|
268
|
+
path += "M#{c.x},#{c.y}"
|
269
|
+
else
|
270
|
+
g = grads[i]
|
271
|
+
lc = coords[i - 1]
|
272
|
+
lg = grads[i - 1]
|
273
|
+
ix = (c.x - lc.x) / 4
|
274
|
+
x1 = lc.x + ix
|
275
|
+
y1 = Math.min(bottom, lc.y + ix * lg)
|
276
|
+
x2 = c.x - ix
|
277
|
+
y2 = Math.min(bottom, c.y - ix * g)
|
278
|
+
path += "C#{x1},#{y1},#{x2},#{y2},#{c.x},#{c.y}"
|
279
|
+
else
|
280
|
+
path = "M" + $.map(coords, (c) -> "#{c.x},#{c.y}").join("L")
|
281
|
+
return path
|
282
|
+
|
283
|
+
# calculate a gradient at each point for a series of points
|
284
|
+
#
|
285
|
+
gradients: (coords) ->
|
286
|
+
$.map coords, (c, i) ->
|
287
|
+
if i is 0
|
288
|
+
(coords[1].y - c.y) / (coords[1].x - c.x)
|
289
|
+
else if i is (coords.length - 1)
|
290
|
+
(c.y - coords[i - 1].y) / (c.x - coords[i - 1].x)
|
291
|
+
else
|
292
|
+
(coords[i + 1].y - coords[i - 1].y) / (coords[i + 1].x - coords[i - 1].x)
|
293
|
+
|
294
|
+
# draw the hover tooltip
|
295
|
+
#
|
296
|
+
drawHover: ->
|
297
|
+
# hover labels
|
298
|
+
@hoverHeight = @options.hoverFontSize * 1.5 * (@series.length + 1)
|
299
|
+
@hover = @r.rect(-10, -@hoverHeight / 2 - @options.hoverPaddingY, 20, @hoverHeight + @options.hoverPaddingY * 2, 10)
|
300
|
+
.attr('fill', @options.hoverFillColor)
|
301
|
+
.attr('stroke', @options.hoverBorderColor)
|
302
|
+
.attr('stroke-width', @options.hoverBorderWidth)
|
303
|
+
.attr('opacity', @options.hoverOpacity)
|
304
|
+
@xLabel = @r.text(0, (@options.hoverFontSize * 0.75) - @hoverHeight / 2, '')
|
305
|
+
.attr('fill', @options.hoverLabelColor)
|
306
|
+
.attr('font-weight', 'bold')
|
307
|
+
.attr('font-size', @options.hoverFontSize)
|
308
|
+
@hoverSet = @r.set()
|
309
|
+
@hoverSet.push(@hover)
|
310
|
+
@hoverSet.push(@xLabel)
|
311
|
+
@yLabels = []
|
312
|
+
for i in [0..@series.length-1]
|
313
|
+
yLabel = @r.text(0, @options.hoverFontSize * 1.5 * (i + 1.5) - @hoverHeight / 2, '')
|
314
|
+
.attr('fill', @options.lineColors[i])
|
315
|
+
.attr('font-size', @options.hoverFontSize)
|
316
|
+
@yLabels.push(yLabel)
|
317
|
+
@hoverSet.push(yLabel)
|
318
|
+
|
319
|
+
updateHover: (index) =>
|
320
|
+
@hoverSet.show()
|
321
|
+
@xLabel.attr('text', @columnLabels[index])
|
322
|
+
for i in [0..@series.length-1]
|
323
|
+
@yLabels[i].attr('text', "#{@seriesLabels[i]}: #{@commas(@series[i][index])}#{@options.units}")
|
324
|
+
# recalculate hover box width
|
325
|
+
maxLabelWidth = Math.max.apply null, $.map @yLabels, (l) ->
|
326
|
+
l.getBBox().width
|
327
|
+
maxLabelWidth = Math.max maxLabelWidth, @xLabel.getBBox().width
|
328
|
+
@hover.attr 'width', maxLabelWidth + @options.hoverPaddingX * 2
|
329
|
+
@hover.attr 'x', -@options.hoverPaddingX - maxLabelWidth / 2
|
330
|
+
# move to y pos
|
331
|
+
yloc = Math.min.apply null, $.map @series, (s) =>
|
332
|
+
@transY s[index]
|
333
|
+
if yloc > @hoverHeight + @options.hoverPaddingY * 2 + @options.hoverMargin + @options.marginTop
|
334
|
+
yloc = yloc - @hoverHeight / 2 - @options.hoverPaddingY - @options.hoverMargin
|
335
|
+
else
|
336
|
+
yloc = yloc + @hoverHeight / 2 + @options.hoverPaddingY + @options.hoverMargin
|
337
|
+
yloc = Math.max @options.marginTop + @hoverHeight / 2 + @options.hoverPaddingY, yloc
|
338
|
+
yloc = Math.min @options.marginTop + @height - @hoverHeight / 2 - @options.hoverPaddingY, yloc
|
339
|
+
xloc = Math.min @left + @width - maxLabelWidth / 2 - @options.hoverPaddingX, @columns[index]
|
340
|
+
xloc = Math.max @left + maxLabelWidth / 2 + @options.hoverPaddingX, xloc
|
341
|
+
@hoverSet.attr 'transform', "t#{xloc},#{yloc}"
|
342
|
+
|
343
|
+
hideHover: ->
|
344
|
+
@hoverSet.hide()
|
345
|
+
|
346
|
+
hilight: (index) =>
|
347
|
+
if @prevHilight isnt null and @prevHilight isnt index
|
348
|
+
for i in [0..@seriesPoints.length-1]
|
349
|
+
if @seriesPoints[i][@prevHilight]
|
350
|
+
@seriesPoints[i][@prevHilight].animate @pointShrink
|
351
|
+
if index isnt null and @prevHilight isnt index
|
352
|
+
for i in [0..@seriesPoints.length-1]
|
353
|
+
if @seriesPoints[i][index]
|
354
|
+
@seriesPoints[i][index].animate @pointGrow
|
355
|
+
@updateHover index
|
356
|
+
@prevHilight = index
|
357
|
+
if index is null
|
358
|
+
@hideHover()
|
359
|
+
|
360
|
+
updateHilight: (x) =>
|
361
|
+
x -= @el.offset().left
|
362
|
+
for hoverIndex in [@hoverMargins.length..0]
|
363
|
+
if hoverIndex == 0 || @hoverMargins[hoverIndex - 1] > x
|
364
|
+
@hilight hoverIndex
|
365
|
+
break
|
366
|
+
|
367
|
+
measureText: (text, fontSize = 12) ->
|
368
|
+
tt = @r.text(100, 100, text).attr('font-size', fontSize)
|
369
|
+
ret = tt.getBBox()
|
370
|
+
tt.remove()
|
371
|
+
return ret
|
372
|
+
|
373
|
+
parseYear: (date) ->
|
374
|
+
if typeof date is 'number'
|
375
|
+
return date
|
376
|
+
m = date.match /^(\d+) Q(\d)$/
|
377
|
+
n = date.match /^(\d+)-(\d+)$/
|
378
|
+
o = date.match /^(\d+)-(\d+)-(\d+)$/
|
379
|
+
p = date.match /^(\d+) W(\d+)$/
|
380
|
+
q = date.match /^(\d+)-(\d+)-(\d+) (\d+):(\d+)$/
|
381
|
+
r = date.match /^(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+(\.\d+)?)$/
|
382
|
+
if m
|
383
|
+
new Date(
|
384
|
+
parseInt(m[1], 10),
|
385
|
+
parseInt(m[2], 10) * 3 - 1,
|
386
|
+
1).getTime()
|
387
|
+
else if n
|
388
|
+
new Date(
|
389
|
+
parseInt(n[1], 10),
|
390
|
+
parseInt(n[2], 10) - 1,
|
391
|
+
1).getTime()
|
392
|
+
else if o
|
393
|
+
new Date(
|
394
|
+
parseInt(o[1], 10),
|
395
|
+
parseInt(o[2], 10) - 1,
|
396
|
+
parseInt(o[3], 10)).getTime()
|
397
|
+
else if p
|
398
|
+
# calculate number of weeks in year given
|
399
|
+
ret = new Date(parseInt(p[1], 10), 0, 1);
|
400
|
+
# first thursday in year (ISO 8601 standard)
|
401
|
+
if ret.getDay() isnt 4
|
402
|
+
ret.setMonth(0, 1 + ((4 - ret.getDay()) + 7) % 7);
|
403
|
+
# add weeks
|
404
|
+
ret.getTime() + parseInt(p[2], 10) * 604800000
|
405
|
+
else if q
|
406
|
+
new Date(
|
407
|
+
parseInt(q[1], 10),
|
408
|
+
parseInt(q[2], 10) - 1,
|
409
|
+
parseInt(q[3], 10),
|
410
|
+
parseInt(q[4], 10),
|
411
|
+
parseInt(q[5], 10)).getTime()
|
412
|
+
else if r
|
413
|
+
secs = parseFloat(r[6])
|
414
|
+
isecs = Math.floor(secs)
|
415
|
+
msecs = Math.floor((secs - isecs) * 1000)
|
416
|
+
new Date(
|
417
|
+
parseInt(r[1], 10),
|
418
|
+
parseInt(r[2], 10) - 1,
|
419
|
+
parseInt(r[3], 10),
|
420
|
+
parseInt(r[4], 10),
|
421
|
+
parseInt(r[5], 10),
|
422
|
+
isecs,
|
423
|
+
msecs).getTime()
|
424
|
+
else
|
425
|
+
new Date(parseInt(date, 10), 0, 1)
|
426
|
+
|
427
|
+
# make long numbers prettier by inserting commas
|
428
|
+
# eg: commas(1234567) -> '1,234,567'
|
429
|
+
#
|
430
|
+
commas: (num) ->
|
431
|
+
if num is null
|
432
|
+
"n/a"
|
433
|
+
else
|
434
|
+
ret = if num < 0 then "-" else ""
|
435
|
+
absnum = Math.abs(num)
|
436
|
+
intnum = Math.floor(absnum).toFixed(0)
|
437
|
+
ret += intnum.replace(/(?=(?:\d{3})+$)(?!^)/g, ',')
|
438
|
+
strabsnum = absnum.toString()
|
439
|
+
if strabsnum.length > intnum.length
|
440
|
+
ret += strabsnum.slice(intnum.length)
|
441
|
+
ret
|
442
|
+
|
443
|
+
window.Morris = Morris
|
444
|
+
# vim: set et ts=2 sw=2 sts=2
|