2012-02-23 09:11:31 +01:00
|
|
|
# The original line graph.
|
|
|
|
#
|
2012-03-01 09:06:38 +01:00
|
|
|
$ = jQuery
|
|
|
|
|
2012-02-26 01:28:58 +01:00
|
|
|
Morris = {}
|
|
|
|
class Morris.Line
|
2012-02-23 09:11:31 +01:00
|
|
|
# Initialise the graph.
|
|
|
|
#
|
|
|
|
# @param {Object} options
|
2012-02-26 01:28:58 +01:00
|
|
|
constructor: (options) ->
|
|
|
|
if not (this instanceof Morris.Line)
|
|
|
|
return new Morris.Line(options)
|
2012-02-28 23:00:08 +01:00
|
|
|
if typeof options.element is 'string'
|
|
|
|
@el = $ document.getElementById(options.element)
|
|
|
|
else
|
|
|
|
@el = $ options.element
|
2012-02-28 22:06:55 +01:00
|
|
|
@options = $.extend {}, @defaults, options
|
2012-02-23 09:11:31 +01:00
|
|
|
# bail if there's no data
|
|
|
|
if @options.data is undefined or @options.data.length is 0
|
|
|
|
return
|
|
|
|
@el.addClass 'graph-initialised'
|
|
|
|
@precalc()
|
|
|
|
@redraw()
|
|
|
|
|
|
|
|
# Default configuration
|
|
|
|
#
|
|
|
|
defaults:
|
|
|
|
lineWidth: 3
|
|
|
|
pointSize: 4
|
|
|
|
lineColors: [
|
|
|
|
'#0b62a4'
|
|
|
|
'#7A92A3'
|
|
|
|
'#4da74d'
|
|
|
|
'#afd8f8'
|
|
|
|
'#edc240'
|
|
|
|
'#cb4b4b'
|
|
|
|
'#9440ed'
|
|
|
|
]
|
2012-02-28 00:23:43 +01:00
|
|
|
ymax: 'auto'
|
2012-03-08 21:03:45 +01:00
|
|
|
ymin: 'auto 0'
|
2012-02-23 09:11:31 +01:00
|
|
|
marginTop: 25
|
|
|
|
marginRight: 25
|
|
|
|
marginBottom: 30
|
|
|
|
marginLeft: 25
|
|
|
|
numLines: 5
|
|
|
|
gridLineColor: '#aaa'
|
|
|
|
gridTextColor: '#888'
|
|
|
|
gridTextSize: 12
|
|
|
|
gridStrokeWidth: 0.5
|
2012-02-23 22:59:45 +01:00
|
|
|
hoverPaddingX: 10
|
|
|
|
hoverPaddingY: 5
|
|
|
|
hoverMargin: 10
|
|
|
|
hoverFillColor: '#fff'
|
|
|
|
hoverBorderColor: '#ccc'
|
|
|
|
hoverBorderWidth: 2
|
|
|
|
hoverOpacity: 0.95
|
|
|
|
hoverLabelColor: '#444'
|
|
|
|
hoverFontSize: 12
|
2012-02-28 22:27:33 +01:00
|
|
|
smooth: true
|
2012-03-06 22:38:01 +01:00
|
|
|
hideHover: false
|
2012-03-06 23:05:00 +01:00
|
|
|
parseTime: true
|
2012-03-08 21:50:43 +01:00
|
|
|
units: ''
|
2012-03-15 22:44:55 +01:00
|
|
|
dateFormat: (x) -> new Date(x).toString()
|
2012-02-23 09:11:31 +01:00
|
|
|
|
|
|
|
# Do any necessary pre-processing for a new dataset
|
|
|
|
#
|
|
|
|
precalc: ->
|
2012-02-29 21:53:53 +01:00
|
|
|
# sort data
|
|
|
|
@options.data.sort (a, b) => (a[@options.xkey] < b[@options.xkey]) - (b[@options.xkey] < a[@options.xkey])
|
2012-02-23 09:11:31 +01:00
|
|
|
# extract labels
|
2012-02-23 22:59:45 +01:00
|
|
|
@columnLabels = $.map @options.data, (d) => d[@options.xkey]
|
|
|
|
@seriesLabels = @options.labels
|
2012-02-23 09:11:31 +01:00
|
|
|
|
|
|
|
# extract series data
|
|
|
|
@series = []
|
|
|
|
for ykey in @options.ykeys
|
|
|
|
@series.push $.map @options.data, (d) -> d[ykey]
|
|
|
|
|
|
|
|
# translate x labels into nominal dates
|
|
|
|
# note: currently using decimal years to specify dates
|
2012-03-06 23:05:00 +01:00
|
|
|
if @options.parseTime
|
|
|
|
@xvals = $.map @columnLabels, (x) => @parseYear x
|
|
|
|
else
|
|
|
|
@xvals = [(@columnLabels.length-1)..0]
|
2012-03-15 22:44:55 +01:00
|
|
|
# translate column labels, if they're timestamps
|
|
|
|
@columnLabels = $.map @columnLabels, (d) =>
|
|
|
|
if typeof d is 'number'
|
|
|
|
@options.dateFormat(d)
|
|
|
|
else
|
|
|
|
d
|
2012-02-23 09:11:31 +01:00
|
|
|
@xmin = Math.min.apply null, @xvals
|
|
|
|
@xmax = Math.max.apply null, @xvals
|
|
|
|
if @xmin is @xmax
|
|
|
|
@xmin -= 1
|
|
|
|
@xmax += 1
|
|
|
|
|
2012-02-28 00:23:43 +01:00
|
|
|
# Compute the vertical range of the graph if desired
|
2012-02-28 09:20:11 +01:00
|
|
|
if typeof @options.ymax is 'string' and @options.ymax[0..3] is 'auto'
|
2012-03-08 21:03:45 +01:00
|
|
|
# use Array.concat to flatten arrays and find the max y value
|
|
|
|
ymax = Math.max.apply null, Array.prototype.concat.apply([], @series)
|
|
|
|
if @options.ymax.length > 5
|
|
|
|
@options.ymax = Math.max parseInt(@options.ymax[5..], 10), ymax
|
|
|
|
else
|
|
|
|
@options.ymax = ymax
|
|
|
|
if typeof @options.ymin is 'string' and @options.ymin[0..3] is 'auto'
|
|
|
|
ymin = Math.min.apply null, Array.prototype.concat.apply([], @series)
|
|
|
|
if @options.ymin.length > 5
|
|
|
|
@options.ymin = Math.min parseInt(@options.ymin[5..], 10), ymin
|
|
|
|
else
|
|
|
|
@options.ymin = ymin
|
2012-02-23 09:11:31 +01:00
|
|
|
|
|
|
|
# Clear and redraw the graph
|
|
|
|
#
|
|
|
|
redraw: ->
|
|
|
|
# remove child elements (get rid of old drawings)
|
|
|
|
@el.empty()
|
|
|
|
|
|
|
|
# the raphael drawing instance
|
|
|
|
@r = new Raphael(@el[0])
|
|
|
|
|
|
|
|
# calculate grid dimensions
|
2012-03-08 21:03:45 +01:00
|
|
|
maxYLabelWidth = Math.max(
|
2012-03-08 21:50:43 +01:00
|
|
|
@measureText(@options.ymin + @options.units, @options.gridTextSize).width,
|
|
|
|
@measureText(@options.ymax + @options.units, @options.gridTextSize).width)
|
2012-03-08 21:03:45 +01:00
|
|
|
left = maxYLabelWidth + @options.marginLeft
|
2012-02-23 09:11:31 +01:00
|
|
|
width = @el.width() - left - @options.marginRight
|
|
|
|
height = @el.height() - @options.marginTop - @options.marginBottom
|
|
|
|
dx = width / (@xmax - @xmin)
|
2012-03-08 21:03:45 +01:00
|
|
|
dy = height / (@options.ymax - @options.ymin)
|
2012-02-23 09:11:31 +01:00
|
|
|
|
|
|
|
# quick translation helpers
|
|
|
|
transX = (x) =>
|
|
|
|
if @xvals.length is 1
|
|
|
|
left + width / 2
|
|
|
|
else
|
|
|
|
left + (x - @xmin) * dx
|
|
|
|
transY = (y) =>
|
2012-03-08 21:03:45 +01:00
|
|
|
return @options.marginTop + height - (y - @options.ymin) * dy
|
2012-02-23 09:11:31 +01:00
|
|
|
|
|
|
|
# draw y axis labels, horizontal lines
|
2012-03-08 21:03:45 +01:00
|
|
|
yInterval = (@options.ymax - @options.ymin) / (@options.numLines - 1)
|
|
|
|
firstY = Math.ceil(@options.ymin / yInterval) * yInterval
|
|
|
|
lastY = Math.floor(@options.ymax / yInterval) * yInterval
|
|
|
|
for lineY in [firstY..lastY] by yInterval
|
|
|
|
v = Math.floor(lineY)
|
|
|
|
y = transY(v)
|
2012-03-08 21:50:43 +01:00
|
|
|
@r.text(left - @options.marginLeft/2, y, v + @options.units)
|
2012-02-23 09:11:31 +01:00
|
|
|
.attr('font-size', @options.gridTextSize)
|
|
|
|
.attr('fill', @options.gridTextColor)
|
|
|
|
.attr('text-anchor', 'end')
|
|
|
|
@r.path("M" + left + "," + y + 'H' + (left + width))
|
|
|
|
.attr('stroke', @options.gridLineColor)
|
|
|
|
.attr('stroke-width', @options.gridStrokeWidth)
|
|
|
|
|
2012-03-15 21:47:15 +01:00
|
|
|
## draw x axis labels
|
2012-03-15 22:06:21 +01:00
|
|
|
prevLabelMargin = null
|
|
|
|
xLabelMargin = 50 # make this an option?
|
|
|
|
if @options.parseTime
|
|
|
|
x1 = new Date(@xmin).getFullYear()
|
|
|
|
x2 = new Date(@xmax).getFullYear()
|
|
|
|
else
|
|
|
|
x1 = @xmin
|
|
|
|
x2 = @xmax
|
|
|
|
for i in [x1..x2]
|
|
|
|
if @options.parseTime
|
|
|
|
xpos = new Date(i, 0, 1).getTime()
|
|
|
|
if xpos < @xmin
|
|
|
|
continue
|
|
|
|
else
|
|
|
|
xpos = i
|
|
|
|
labelText = if @options.parseTime then i else @columnLabels[@columnLabels.length-i-1]
|
|
|
|
label = @r.text(transX(xpos), @options.marginTop + height + @options.marginBottom / 2, labelText)
|
|
|
|
.attr('font-size', @options.gridTextSize)
|
|
|
|
.attr('fill', @options.gridTextColor)
|
|
|
|
labelBox = label.getBBox()
|
|
|
|
# ensure a minimum of `xLabelMargin` pixels between labels
|
|
|
|
if prevLabelMargin is null or prevLabelMargin <= labelBox.x
|
|
|
|
prevLabelMargin = labelBox.x + labelBox.width + xLabelMargin
|
|
|
|
else
|
|
|
|
label.remove()
|
2012-02-23 09:11:31 +01:00
|
|
|
|
|
|
|
# draw the actual series
|
|
|
|
columns = (transX(x) for x in @xvals)
|
|
|
|
seriesCoords = []
|
|
|
|
for s in @series
|
|
|
|
seriesCoords.push($.map(s, (y, i) -> x: columns[i], y: transY(y)))
|
|
|
|
for i in [seriesCoords.length-1..0]
|
|
|
|
coords = seriesCoords[i]
|
|
|
|
if coords.length > 1
|
2012-02-28 22:27:33 +01:00
|
|
|
path = @createPath coords, @options.marginTop, left, @options.marginTop + height, left + width
|
2012-02-23 09:11:31 +01:00
|
|
|
@r.path(path)
|
|
|
|
.attr('stroke', @options.lineColors[i])
|
|
|
|
.attr('stroke-width', @options.lineWidth)
|
|
|
|
seriesPoints = ([] for i in [0..seriesCoords.length-1])
|
|
|
|
for i in [seriesCoords.length-1..0]
|
|
|
|
for c in seriesCoords[i]
|
|
|
|
circle = @r.circle(c.x, c.y, @options.pointSize)
|
|
|
|
.attr('fill', @options.lineColors[i])
|
|
|
|
.attr('stroke-width', 1)
|
|
|
|
.attr('stroke', '#ffffff')
|
|
|
|
seriesPoints[i].push(circle)
|
|
|
|
|
|
|
|
# hover labels
|
2012-02-23 22:59:45 +01:00
|
|
|
hoverHeight = @options.hoverFontSize * 1.5 * (@series.length + 1)
|
|
|
|
hover = @r.rect(-10, -hoverHeight / 2 - @options.hoverPaddingY, 20, hoverHeight + @options.hoverPaddingY * 2, 10)
|
|
|
|
.attr('fill', @options.hoverFillColor)
|
|
|
|
.attr('stroke', @options.hoverBorderColor)
|
|
|
|
.attr('stroke-width', @options.hoverBorderWidth)
|
|
|
|
.attr('opacity', @options.hoverOpacity)
|
|
|
|
xLabel = @r.text(0, (@options.hoverFontSize * 0.75) - hoverHeight / 2, '')
|
|
|
|
.attr('fill', @options.hoverLabelColor)
|
|
|
|
.attr('font-weight', 'bold')
|
|
|
|
.attr('font-size', @options.hoverFontSize)
|
|
|
|
hoverSet = @r.set()
|
|
|
|
hoverSet.push(hover)
|
|
|
|
hoverSet.push(xLabel)
|
|
|
|
yLabels = []
|
|
|
|
for i in [0..@series.length-1]
|
|
|
|
yLabel = @r.text(0, @options.hoverFontSize * 1.5 * (i + 1.5) - hoverHeight / 2, '')
|
|
|
|
.attr('fill', @options.lineColors[i])
|
|
|
|
.attr('font-size', @options.hoverFontSize)
|
|
|
|
yLabels.push(yLabel)
|
|
|
|
hoverSet.push(yLabel)
|
|
|
|
updateHover = (index) =>
|
|
|
|
hoverSet.show()
|
|
|
|
xLabel.attr('text', @columnLabels[index])
|
|
|
|
for i in [0..@series.length-1]
|
2012-03-08 21:50:43 +01:00
|
|
|
yLabels[i].attr('text', "#{@seriesLabels[i]}: #{@commas(@series[i][index])}#{@options.units}")
|
2012-02-23 22:59:45 +01:00
|
|
|
# recalculate hover box width
|
|
|
|
maxLabelWidth = Math.max.apply null, $.map yLabels, (l) ->
|
|
|
|
l.getBBox().width
|
|
|
|
maxLabelWidth = Math.max maxLabelWidth, xLabel.getBBox().width
|
|
|
|
hover.attr 'width', maxLabelWidth + @options.hoverPaddingX * 2
|
|
|
|
hover.attr 'x', -@options.hoverPaddingX - maxLabelWidth / 2
|
|
|
|
# move to y pos
|
|
|
|
yloc = Math.min.apply null, $.map @series, (s) =>
|
|
|
|
transY s[index]
|
|
|
|
if yloc > hoverHeight + @options.hoverPaddingY * 2 + @options.hoverMargin + @options.marginTop
|
|
|
|
yloc = yloc - hoverHeight / 2 - @options.hoverPaddingY - @options.hoverMargin
|
|
|
|
else
|
|
|
|
yloc = yloc + hoverHeight / 2 + @options.hoverPaddingY + @options.hoverMargin
|
|
|
|
yloc = Math.max @options.marginTop + hoverHeight / 2 + @options.hoverPaddingY, yloc
|
|
|
|
yloc = Math.min @options.marginTop + height - hoverHeight / 2 - @options.hoverPaddingY, yloc
|
|
|
|
xloc = Math.min left + width - maxLabelWidth / 2 - @options.hoverPaddingX, columns[index]
|
|
|
|
xloc = Math.max left + maxLabelWidth / 2 + @options.hoverPaddingX, xloc
|
|
|
|
hoverSet.attr 'transform', "t#{xloc},#{yloc}"
|
|
|
|
hideHover = ->
|
|
|
|
hoverSet.hide()
|
|
|
|
|
|
|
|
# column hilight
|
2012-02-23 09:11:31 +01:00
|
|
|
hoverMargins = $.map columns.slice(1), (x, i) -> (x + columns[i]) / 2
|
2012-02-23 22:59:45 +01:00
|
|
|
prevHilight = null
|
|
|
|
pointGrow = Raphael.animation r: @options.pointSize + 3, 25, 'linear'
|
|
|
|
pointShrink = Raphael.animation r: @options.pointSize, 25, 'linear'
|
|
|
|
hilight = (index) =>
|
|
|
|
if prevHilight isnt null and prevHilight isnt index
|
|
|
|
for i in [0..seriesPoints.length-1]
|
|
|
|
seriesPoints[i][prevHilight].animate pointShrink
|
|
|
|
if index isnt null and prevHilight isnt index
|
|
|
|
for i in [0..seriesPoints.length-1]
|
|
|
|
seriesPoints[i][index].animate pointGrow
|
|
|
|
updateHover index
|
|
|
|
prevHilight = index
|
|
|
|
if index is null
|
|
|
|
hideHover()
|
|
|
|
updateHilight = (x) =>
|
|
|
|
x -= @el.offset().left
|
2012-02-29 09:10:48 +01:00
|
|
|
for hoverIndex in [hoverMargins.length..0]
|
|
|
|
if hoverIndex == 0 || hoverMargins[hoverIndex - 1] > x
|
|
|
|
hilight hoverIndex
|
2012-02-23 22:59:45 +01:00
|
|
|
break
|
|
|
|
@el.mousemove (evt) =>
|
|
|
|
updateHilight evt.pageX
|
2012-03-06 22:38:01 +01:00
|
|
|
if @options.hideHover
|
|
|
|
@el.mouseout (evt) =>
|
|
|
|
hilight null
|
2012-02-23 22:59:45 +01:00
|
|
|
touchHandler = (evt) =>
|
|
|
|
touch = evt.originalEvent.touches[0] or evt.originalEvent.changedTouches[0]
|
|
|
|
updateHilight touch.pageX
|
|
|
|
return touch
|
|
|
|
@el.bind 'touchstart', touchHandler
|
|
|
|
@el.bind 'touchmove', touchHandler
|
|
|
|
@el.bind 'touchend', touchHandler
|
2012-03-06 22:38:01 +01:00
|
|
|
hilight(if @options.hideHover then null else 0)
|
2012-02-23 09:11:31 +01:00
|
|
|
|
|
|
|
# create a path for a data series
|
|
|
|
#
|
|
|
|
createPath: (coords, top, left, bottom, right) ->
|
|
|
|
path = ""
|
2012-02-28 22:27:33 +01:00
|
|
|
if @options.smooth
|
|
|
|
grads = @gradients coords
|
|
|
|
for i in [0..coords.length-1]
|
|
|
|
c = coords[i]
|
|
|
|
if i is 0
|
|
|
|
path += "M#{c.x},#{c.y}"
|
|
|
|
else
|
|
|
|
g = grads[i]
|
|
|
|
lc = coords[i - 1]
|
|
|
|
lg = grads[i - 1]
|
|
|
|
ix = (c.x - lc.x) / 4
|
|
|
|
x1 = lc.x + ix
|
|
|
|
y1 = Math.min(bottom, lc.y + ix * lg)
|
|
|
|
x2 = c.x - ix
|
|
|
|
y2 = Math.min(bottom, c.y - ix * g)
|
|
|
|
path += "C#{x1},#{y1},#{x2},#{y2},#{c.x},#{c.y}"
|
|
|
|
else
|
|
|
|
path = "M" + $.map(coords, (c) -> "#{c.x},#{c.y}").join("L")
|
2012-02-28 14:30:52 +01:00
|
|
|
return path
|
|
|
|
|
2012-02-23 09:11:31 +01:00
|
|
|
# calculate a gradient at each point for a series of points
|
|
|
|
#
|
|
|
|
gradients: (coords) ->
|
|
|
|
$.map coords, (c, i) ->
|
|
|
|
if i is 0
|
|
|
|
(coords[1].y - c.y) / (coords[1].x - c.x)
|
|
|
|
else if i is (coords.length - 1)
|
|
|
|
(c.y - coords[i - 1].y) / (c.x - coords[i - 1].x)
|
|
|
|
else
|
|
|
|
(coords[i + 1].y - coords[i - 1].y) / (coords[i + 1].x - coords[i - 1].x)
|
|
|
|
|
|
|
|
measureText: (text, fontSize = 12) ->
|
|
|
|
tt = @r.text(100, 100, text).attr('font-size', fontSize)
|
|
|
|
ret = tt.getBBox()
|
|
|
|
tt.remove()
|
|
|
|
return ret
|
|
|
|
|
2012-02-23 23:27:26 +01:00
|
|
|
parseYear: (date) ->
|
2012-03-15 21:47:15 +01:00
|
|
|
if typeof date is 'number'
|
|
|
|
return date
|
|
|
|
m = date.match /^(\d+) Q(\d)$/
|
|
|
|
n = date.match /^(\d+)-(\d+)$/
|
|
|
|
o = date.match /^(\d+)-(\d+)-(\d+)$/
|
|
|
|
p = date.match /^(\d+) W(\d+)$/
|
2012-03-15 22:18:39 +01:00
|
|
|
q = date.match /^(\d+)-(\d+)-(\d+) (\d+):(\d+)$/
|
|
|
|
r = date.match /^(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+(\.\d+)?)$/
|
2012-02-23 09:11:31 +01:00
|
|
|
if m
|
2012-03-15 21:47:15 +01:00
|
|
|
new Date(
|
|
|
|
parseInt(m[1], 10),
|
2012-03-15 22:06:21 +01:00
|
|
|
parseInt(m[2], 10) * 3 - 1,
|
|
|
|
1).getTime()
|
2012-03-15 21:47:15 +01:00
|
|
|
else if n
|
|
|
|
new Date(
|
|
|
|
parseInt(n[1], 10),
|
2012-03-15 22:06:21 +01:00
|
|
|
parseInt(n[2], 10) - 1,
|
|
|
|
1).getTime()
|
2012-03-15 21:47:15 +01:00
|
|
|
else if o
|
|
|
|
new Date(
|
|
|
|
parseInt(o[1], 10),
|
|
|
|
parseInt(o[2], 10) - 1,
|
|
|
|
parseInt(o[3], 10)).getTime()
|
2012-03-04 17:11:48 +01:00
|
|
|
else if p
|
|
|
|
# calculate number of weeks in year given
|
2012-03-15 21:47:15 +01:00
|
|
|
ret = new Date(parseInt(p[1], 10), 0, 1);
|
2012-03-04 18:01:09 +01:00
|
|
|
# first thursday in year (ISO 8601 standard)
|
2012-03-15 21:47:15 +01:00
|
|
|
if ret.getDay() isnt 4
|
|
|
|
ret.setMonth(0, 1 + ((4 - ret.getDay()) + 7) % 7);
|
|
|
|
# add weeks
|
|
|
|
ret.getTime() + parseInt(p[2], 10) * 604800000
|
2012-03-15 22:18:39 +01:00
|
|
|
else if q
|
|
|
|
new Date(
|
|
|
|
parseInt(q[1], 10),
|
|
|
|
parseInt(q[2], 10) - 1,
|
|
|
|
parseInt(q[3], 10),
|
|
|
|
parseInt(q[4], 10),
|
|
|
|
parseInt(q[5], 10)).getTime()
|
|
|
|
else if r
|
|
|
|
secs = parseFloat(r[6])
|
|
|
|
isecs = Math.floor(secs)
|
|
|
|
msecs = Math.floor((secs - isecs) * 1000)
|
|
|
|
new Date(
|
|
|
|
parseInt(r[1], 10),
|
|
|
|
parseInt(r[2], 10) - 1,
|
|
|
|
parseInt(r[3], 10),
|
|
|
|
parseInt(r[4], 10),
|
|
|
|
parseInt(r[5], 10),
|
|
|
|
isecs,
|
|
|
|
msecs).getTime()
|
2012-02-23 09:11:31 +01:00
|
|
|
else
|
2012-03-15 22:06:21 +01:00
|
|
|
new Date(parseInt(date, 10), 0, 1)
|
2012-02-23 09:11:31 +01:00
|
|
|
|
2012-02-23 22:59:45 +01:00
|
|
|
# make long numbers prettier by inserting commas
|
|
|
|
# eg: commas(1234567) -> '1,234,567'
|
|
|
|
#
|
|
|
|
commas: (num) ->
|
2012-03-08 21:50:43 +01:00
|
|
|
ret = if num < 0 then "-" else ""
|
2012-03-15 21:18:33 +01:00
|
|
|
absnum = Math.abs(num)
|
|
|
|
intnum = Math.floor(absnum).toFixed(0)
|
|
|
|
ret += intnum.replace(/(?=(?:\d{3})+$)(?!^)/g, ',')
|
|
|
|
strabsnum = absnum.toString()
|
|
|
|
if strabsnum.length > intnum.length
|
|
|
|
ret += strabsnum.slice(intnum.length)
|
|
|
|
ret
|
2012-02-23 22:59:45 +01:00
|
|
|
|
2012-02-26 01:28:58 +01:00
|
|
|
window.Morris = Morris
|
2012-02-23 09:16:42 +01:00
|
|
|
# vim: set et ts=2 sw=2 sts=2
|