2012-03-20 23:43:21 +00:00
/ * * * * *
* A no frills tooltip implementation .
* * * * * /
( function ( $ ) {
var nvtooltip = window . nvtooltip = { } ;
nvtooltip . show = function ( pos , content , gravity , dist ) {
var container = $ ( '<div class="nvtooltip">' ) ;
gravity = gravity || 's' ;
dist = dist || 20 ;
container
. html ( content )
. css ( { left : - 1000 , top : - 1000 , opacity : 0 } )
. appendTo ( 'body' ) ; //append the container out of view so we can get measurements
var height = container . height ( ) + parseInt ( container . css ( 'padding-top' ) ) + parseInt ( container . css ( 'padding-bottom' ) ) ,
width = container . width ( ) + parseInt ( container . css ( 'padding-left' ) ) + parseInt ( container . css ( 'padding-right' ) ) ,
windowWidth = $ ( window ) . width ( ) ,
windowHeight = $ ( window ) . height ( ) ,
scrollTop = $ ( 'body' ) . scrollTop ( ) ,
scrollLeft = $ ( 'body' ) . scrollLeft ( ) ,
left , top ;
switch ( gravity ) {
case 'e' :
left = pos [ 0 ] - width - dist ;
top = pos [ 1 ] - ( height / 2 ) ;
if ( left < scrollLeft ) left = pos [ 0 ] + dist ;
if ( top < scrollTop ) top = scrollTop + 5 ;
if ( top + height > scrollTop + windowHeight ) top = scrollTop - height - 5 ;
break ;
case 'w' :
left = pos [ 0 ] + dist ;
top = pos [ 1 ] - ( height / 2 ) ;
if ( left + width > windowWidth ) left = pos [ 0 ] - width - dist ;
if ( top < scrollTop ) top = scrollTop + 5 ;
if ( top + height > scrollTop + windowHeight ) top = scrollTop - height - 5 ;
break ;
case 'n' :
left = pos [ 0 ] - ( width / 2 ) ;
top = pos [ 1 ] + dist ;
if ( left < scrollLeft ) left = scrollLeft + 5 ;
if ( left + width > windowWidth ) left = windowWidth - width - 5 ;
if ( top + height > scrollTop + windowHeight ) top = pos [ 1 ] - height - dist ;
break ;
case 's' :
left = pos [ 0 ] - ( width / 2 ) ;
top = pos [ 1 ] - height - dist ;
if ( left < scrollLeft ) left = scrollLeft + 5 ;
if ( left + width > windowWidth ) left = windowWidth - width - 5 ;
if ( scrollTop > top ) top = pos [ 1 ] + 20 ;
break ;
}
container
. css ( {
left : left ,
top : top ,
opacity : 1
} ) ;
return container ;
} ;
nvtooltip . cleanup = function ( ) {
var tooltips = $ ( '.nvtooltip' ) ;
tooltips . css ( {
'transition-delay' : '0 !important' ,
'-moz-transition-delay' : '0 !important' ,
'-webkit-transition-delay' : '0 !important'
} ) ;
tooltips . css ( 'opacity' , 0 ) ;
setTimeout ( function ( ) {
tooltips . remove ( )
} , 500 ) ;
} ;
} ) ( jQuery ) ;
2012-03-21 22:03:18 +00:00
( function ( ) {
2012-03-20 23:43:21 +00:00
var nv = { version : "0.0.1" } ;
window . nv = nv ;
nv . models = { } ;
nv . graphs = [ ] ;
nv . log = { } ;
nv . dispatch = d3 . dispatch ( "render_start" , "render_end" ) ;
// ********************************************
// Public Helper functions, not part of NV
window . log = function ( obj ) {
if ( ( typeof ( window . console ) === "object" )
&& ( typeof ( window . console . log ) === "function" ) )
console . log . apply ( console , arguments ) ;
return obj ;
} ;
// ********************************************
// Public Core NV functions
nv . dispatch . on ( "render_start" , function ( e ) {
nv . log . startTime = + new Date ;
//log('start', nv.log.startTime);
} ) ;
nv . dispatch . on ( "render_end" , function ( e ) {
nv . log . endTime = + new Date ;
nv . log . totalTime = nv . log . endTime - nv . log . startTime ;
//log('end', nv.log.endTime);
log ( 'total' , nv . log . totalTime ) ;
} ) ;
// ********************************************
// Public Core NV functions
nv . render = function render ( stepSize ) {
var step = stepSize || 1 ; // number of graphs to generate in each timout loop
render . active = true ;
nv . dispatch . render _start ( ) ;
setTimeout ( function ( ) {
var chart ;
for ( var i = 0 ; i < step && ( graph = render . queue [ i ] ) ; i ++ ) {
chart = graph . generate ( ) ;
if ( typeof graph . callback === 'function' ) graph . callback ( chart ) ;
nv . graphs . push ( chart ) ;
}
render . queue . splice ( 0 , i ) ;
if ( render . queue . length > 0 ) setTimeout ( arguments . callee , 0 ) ;
else {
nv . render . active = false ;
nv . dispatch . render _end ( ) ;
}
} , 0 ) ;
} ;
nv . render . queue = [ ] ;
nv . addGraph = function ( obj ) {
if ( typeof arguments [ 0 ] === "function" )
obj = { generate : arguments [ 0 ] , callback : arguments [ 1 ] } ;
nv . render . queue . push ( obj ) ;
if ( ! nv . render . active ) nv . render ( ) ;
} ;
nv . strip = function ( s ) {
return s . replace ( /(\s|&)/g , '' ) ;
}
nv . models . legend = function ( ) {
var margin = { top : 5 , right : 0 , bottom : 5 , left : 10 } ,
width = 400 ,
height = 20 ,
color = d3 . scale . category10 ( ) . range ( ) ,
dispatch = d3 . dispatch ( 'legendClick' , 'legendMouseover' , 'legendMouseout' ) ;
function chart ( selection ) {
selection . each ( function ( data ) {
var wrap = d3 . select ( this ) . selectAll ( 'g.legend' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'legend' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' )
. attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
var series = g . selectAll ( '.series' )
. data ( function ( d ) { return d } ) ;
var seriesEnter = series . enter ( ) . append ( 'g' ) . attr ( 'class' , 'series' )
. on ( 'mouseover' , function ( d , i ) {
dispatch . legendMouseover ( d , i ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
dispatch . legendMouseout ( d , i ) ;
} )
. on ( 'click' , function ( d , i ) {
dispatch . legendClick ( d , i ) ;
} ) ;
seriesEnter . append ( 'circle' )
. style ( 'fill' , function ( d , i ) { return d . color || color [ i % 20 ] } )
. style ( 'stroke' , function ( d , i ) { return d . color || color [ i % 20 ] } )
. style ( 'stroke-width' , 2 )
. attr ( 'r' , 5 ) ;
seriesEnter . append ( 'text' )
. text ( function ( d ) { return d . key } )
. attr ( 'text-anchor' , 'start' )
. attr ( 'dy' , '.32em' )
. attr ( 'dx' , '8' ) ;
series . classed ( 'disabled' , function ( d ) { return d . disabled } ) ;
series . exit ( ) . remove ( ) ;
var ypos = 5 ,
newxpos = 5 ,
maxwidth = 0 ,
xpos ;
series
. attr ( 'transform' , function ( d , i ) {
var length = d3 . select ( this ) . select ( 'text' ) . node ( ) . getComputedTextLength ( ) + 28 ;
xpos = newxpos ;
if ( width < margin . left + margin . right + xpos + length ) {
newxpos = xpos = 5 ;
ypos += 20 ;
}
newxpos += length ;
if ( newxpos > maxwidth ) maxwidth = newxpos ;
return 'translate(' + xpos + ',' + ypos + ')' ;
} ) ;
//position legend as far right as possible within the total width
g . attr ( 'transform' , 'translate(' + ( width - margin . right - maxwidth ) + ',' + margin . top + ')' ) ;
//update height value if calculated larger than current
//Asuming legend is always horizontal for now, removing if clause because this does not let legend shrink after expanding
//TODO: allow legend to be horizontal or vertical, instead of definign height/width define one, and maybe call it maxHeight/maxWidth
//if (height < margin.top + margin.bottom + ypos + 15)
height = margin . top + margin . bottom + ypos + 15 ;
} ) ;
return chart ;
}
chart . dispatch = dispatch ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin = _ ;
return chart ;
} ;
chart . width = function ( _ ) {
if ( ! arguments . length ) return width ;
width = _ ;
return chart ;
} ;
chart . height = function ( _ ) {
if ( ! arguments . length ) return height ;
height = _ ;
return chart ;
} ;
return chart ;
}
nv . models . xaxis = function ( ) {
var domain = [ 0 , 1 ] , //just to have something to start with, maybe I dont need this
range = [ 0 , 1 ] ,
axisLabelText = false ;
var x = d3 . scale . linear ( ) ,
axis = d3 . svg . axis ( ) . scale ( x ) . orient ( 'bottom' ) ;
function chart ( selection ) {
selection . each ( function ( data ) {
x . domain ( domain )
. range ( range ) ;
//TODO: consider calculating height based on whether or not label is added, for reference in charts using this component
var axisLabel = d3 . select ( this ) . selectAll ( 'text.axislabel' )
. data ( [ axisLabelText || null ] ) ;
axisLabel . enter ( ) . append ( 'text' ) . attr ( 'class' , 'axislabel' )
. attr ( 'text-anchor' , 'middle' )
. attr ( 'x' , range [ 1 ] / 2 )
. attr ( 'y' , 25 ) ;
axisLabel . exit ( ) . remove ( ) ;
axisLabel . text ( function ( d ) { return d } ) ;
//d3.select(this)
d3 . transition ( d3 . select ( this ) )
. call ( axis ) ;
d3 . select ( this )
. selectAll ( 'line.tick' )
//.filter(function(d) { return !parseFloat(d) })
. filter ( function ( d ) { return ! parseFloat ( Math . round ( d * 100000 ) / 1000000 ) } )
. classed ( 'zero' , true ) ;
} ) ;
return chart ;
}
chart . domain = function ( _ ) {
if ( ! arguments . length ) return domain ;
domain = _ ;
return chart ;
} ;
chart . range = function ( _ ) {
if ( ! arguments . length ) return range ;
range = _ ;
return chart ;
} ;
chart . axisLabel = function ( _ ) {
if ( ! arguments . length ) return axisLabelText ;
axisLabelText = _ ;
return chart ;
}
d3 . rebind ( chart , axis , 'scale' , 'orient' , 'ticks' , 'tickSubdivide' , 'tickSize' , 'tickPadding' , 'tickFormat' ) ;
return chart ;
}
nv . models . yaxis = function ( ) {
var domain = [ 0 , 1 ] , //just to have something to start with
range = [ 0 , 1 ] ,
axisLabelText = false ;
var y = d3 . scale . linear ( ) ,
axis = d3 . svg . axis ( ) . scale ( y ) . orient ( 'left' ) ;
function chart ( selection ) {
selection . each ( function ( data ) {
y . domain ( domain )
. range ( range ) ;
//TODO: consider calculating width based on whether or not label is added, for reference in charts using this component
var axisLabel = d3 . select ( this ) . selectAll ( 'text.axislabel' )
. data ( [ axisLabelText || null ] ) ;
axisLabel . enter ( ) . append ( 'text' ) . attr ( 'class' , 'axislabel' )
. attr ( 'transform' , 'rotate(-90)' )
. attr ( 'text-anchor' , 'middle' )
. attr ( 'y' , - 40 ) ; //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
axisLabel . exit ( ) . remove ( ) ;
axisLabel
. attr ( 'x' , - range [ 0 ] / 2 )
. text ( function ( d ) { return d } ) ;
//d3.select(this)
d3 . transition ( d3 . select ( this ) )
. call ( axis ) ;
d3 . select ( this )
. selectAll ( 'line.tick' )
//.filter(function(d) { return !parseFloat(d) })
. filter ( function ( d ) { return ! parseFloat ( Math . round ( d * 100000 ) / 1000000 ) } )
. classed ( 'zero' , true ) ;
} ) ;
return chart ;
}
chart . domain = function ( _ ) {
if ( ! arguments . length ) return domain ;
domain = _ ;
return chart ;
} ;
chart . range = function ( _ ) {
if ( ! arguments . length ) return range ;
range = _ ;
return chart ;
} ;
chart . axisLabel = function ( _ ) {
if ( ! arguments . length ) return axisLabelText ;
axisLabelText = _ ;
return chart ;
}
d3 . rebind ( chart , axis , 'scale' , 'orient' , 'ticks' , 'tickSubdivide' , 'tickSize' , 'tickPadding' , 'tickFormat' ) ;
return chart ;
}
nv . models . bar = function ( ) {
var margin = { top : 20 , right : 10 , bottom : 20 , left : 60 } ,
width = 960 ,
height = 500 ,
animate = 500 ;
var x = d3 . scale . ordinal ( ) ,
y = d3 . scale . linear ( ) ,
xAxis = d3 . svg . axis ( ) . scale ( x ) . orient ( 'bottom' ) . ticks ( 5 ) ,
yAxis = d3 . svg . axis ( ) . scale ( y ) . orient ( 'left' ) ;
function chart ( selection ) {
selection . each ( function ( data ) {
//x .domain(data.map(function(d,i) { return d.label }))
x . domain ( [ "One" , "Two" , "Three" , "Four" , "Five" ] )
. rangeRoundBands ( [ 0 , width - margin . left - margin . right ] , . 1 ) ;
y . domain ( [ 0 , d3 . max ( data , function ( d ) { return d . y ; } ) ] )
. range ( [ height - margin . top - margin . bottom , 0 ] ) ;
xAxis . ticks ( width / 100 ) ;
yAxis . ticks ( height / 36 ) . tickSize ( - ( width - margin . right - margin . left ) , 0 ) ;
yAxis . tickSize ( - ( width - margin . right - margin . left ) , 0 ) ;
var wrap = d3 . select ( this ) . selectAll ( 'g.wrap' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'wrap' ) . append ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'x axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'y axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'bars' ) ;
wrap . attr ( 'width' , width )
. attr ( 'height' , height ) ;
var g = wrap . select ( 'g' )
. attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
var bars = wrap . select ( '.bars' ) . selectAll ( '.bar' )
. data ( function ( d ) { return d } ) ;
bars . exit ( ) . remove ( ) ;
var barsEnter = bars . enter ( ) . append ( 'g' )
. attr ( 'class' , 'bar' )
. on ( 'mouseover' , function ( d , i ) { d3 . select ( this ) . classed ( 'hover' , true ) } )
. on ( 'mouseout' , function ( d , i ) { d3 . select ( this ) . classed ( 'hover' , false ) } ) ;
barsEnter . append ( 'rect' )
. attr ( 'y' , function ( d ) { return y ( 0 ) } ) ;
barsEnter . append ( 'text' )
. attr ( 'text-anchor' , 'middle' )
. attr ( 'dy' , '-4px' ) ;
bars
. attr ( 'transform' , function ( d , i ) { return 'translate(' + x ( d . label ) + ',0)' } )
bars . selectAll ( 'rect' )
. order ( )
. attr ( 'width' , x . rangeBand )
. transition ( )
. duration ( animate )
. attr ( 'x' , 0 )
. attr ( 'y' , function ( d ) { return y ( d . y ) } )
. attr ( 'height' , function ( d ) { return y . range ( ) [ 0 ] - y ( d . y ) } ) ;
bars . selectAll ( 'text' )
. attr ( 'x' , 0 )
. attr ( 'y' , function ( d ) { return y ( d . y ) } )
. attr ( 'dx' , x . rangeBand ( ) / 2 )
. text ( function ( d ) { return d . y } ) ;
g . select ( '.x.axis' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' )
. call ( xAxis ) ;
g . select ( '.y.axis' )
. call ( yAxis ) ;
} ) ;
return chart ;
}
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin = _ ;
return chart ;
} ;
chart . width = function ( _ ) {
if ( ! arguments . length ) return width ;
if ( margin . left + margin . right + 20 > _ )
width = margin . left + margin . right + 20 // Min width
else
width = _ ;
return chart ;
} ;
chart . height = function ( _ ) {
if ( ! arguments . length ) return height ;
if ( margin . top + margin . bottom + 20 > _ )
height = margin . top + margin . bottom + 20 // Min height
else
height = _ ;
return chart ;
} ;
chart . animate = function ( _ ) {
if ( ! arguments . length ) return animate ;
animate = _ ;
return chart ;
} ;
chart . xaxis = { } ;
// Expose the x-axis' tickFormat method.
d3 . rebind ( chart . xaxis , xAxis , 'tickFormat' ) ;
chart . yaxis = { } ;
// Expose the y-axis' tickFormat method.
d3 . rebind ( chart . yaxis , yAxis , 'tickFormat' ) ;
return chart ;
}
//TODO: consider adding axes
// -How to deal with time vs generic linear, vs any other scale?
nv . models . line = function ( ) {
//Default Settings
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 } , //consider removing margin options from here... or make margin padding inside the chart (subtract margin from range)
width = 960 ,
height = 500 ,
dotRadius = function ( ) { return 2.5 } , //consider removing this, or making similar to scatter
color = d3 . scale . category10 ( ) . range ( ) ,
id = Math . floor ( Math . random ( ) * 10000 ) , //Create semi-unique ID incase user doesn't select one
getX = function ( d ) { return d . x } ,
getY = function ( d ) { return d . y } ,
interactive = true ,
xDomain , yDomain ;
var x = d3 . scale . linear ( ) ,
y = d3 . scale . linear ( ) ,
dispatch = d3 . dispatch ( 'pointMouseover' , 'pointMouseout' ) ,
x0 , y0 ;
function chart ( selection ) {
selection . each ( function ( data ) {
var seriesData = data . map ( function ( d ) { return d . values } ) ;
x0 = x0 || x ;
y0 = y0 || y ;
//TODO: consider reusing the parent's scales (almost always making duplicates of the same scale)
x . domain ( xDomain || d3 . extent ( d3 . merge ( seriesData ) , getX ) )
. range ( [ 0 , width - margin . left - margin . right ] ) ;
y . domain ( yDomain || d3 . extent ( d3 . merge ( seriesData ) , getY ) )
. range ( [ height - margin . top - margin . bottom , 0 ] ) ;
var wrap = d3 . select ( this ) . selectAll ( 'g.d3line' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'd3line' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'lines' ) ;
var g = wrap . select ( 'g' )
. attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
wrapEnter . append ( 'defs' ) . append ( 'clipPath' )
. attr ( 'id' , 'chart-clip-path-' + id )
. append ( 'rect' ) ;
wrap . select ( '#chart-clip-path-' + id + ' rect' )
. attr ( 'width' , width - margin . left - margin . right )
. attr ( 'height' , height - margin . top - margin . bottom ) ;
gEnter
. attr ( 'clip-path' , 'url(#chart-clip-path-' + id + ')' ) ;
var shiftWrap = gEnter . append ( 'g' ) . attr ( 'class' , 'shiftWrap' ) ;
//TODO: currently doesnt remove if user renders, then turns off interactions... currently must turn off before the first render (will need to fix)
if ( interactive ) {
shiftWrap . append ( 'g' ) . attr ( 'class' , 'point-clips' ) ;
shiftWrap . append ( 'g' ) . attr ( 'class' , 'point-paths' ) ;
var vertices = d3 . merge ( data . map ( function ( line , lineIndex ) {
return line . values . map ( function ( point , pointIndex ) {
2012-03-22 23:28:13 +00:00
//return [x(getX(point)), y(getY(point)), lineIndex, pointIndex]; //inject series and point index for reference into voronoi
return [ x ( getX ( point ) ) * ( Math . random ( ) / 1e12 + 1 ) , y ( getY ( point ) ) * ( Math . random ( ) / 1e12 + 1 ) , lineIndex , pointIndex ] ; //temp hack to add noise untill I think of a better way so there are no duplicates
2012-03-20 23:43:21 +00:00
} )
} )
) ;
//var pointClips = wrap.select('.point-clips').selectAll('clipPath') // **BROWSER BUG** can't reselect camel cased elements
var pointClips = wrap . select ( '.point-clips' ) . selectAll ( '.clip-path' )
. data ( vertices ) ;
pointClips . enter ( ) . append ( 'clipPath' ) . attr ( 'class' , 'clip-path' )
. append ( 'circle' )
. attr ( 'r' , 25 ) ;
pointClips . exit ( ) . remove ( ) ;
pointClips
. attr ( 'id' , function ( d , i ) { return 'clip-' + id + '-' + d [ 2 ] + '-' + d [ 3 ] } )
. attr ( 'transform' , function ( d ) { return 'translate(' + d [ 0 ] + ',' + d [ 1 ] + ')' } )
//inject series and point index for reference into voronoi
var voronoi = d3 . geom . voronoi ( vertices ) . map ( function ( d , i ) { return { 'data' : d , 'series' : vertices [ i ] [ 2 ] , 'point' : vertices [ i ] [ 3 ] } } ) ;
//TODO: Add small amount noise to prevent duplicates
var pointPaths = wrap . select ( '.point-paths' ) . selectAll ( 'path' )
. data ( voronoi ) ;
pointPaths . enter ( ) . append ( 'path' )
. attr ( 'class' , function ( d , i ) { return 'path-' + i ; } )
//.style('fill', d3.rgb(230, 230, 230))
//.style('stroke', d3.rgb(200, 200, 200))
. style ( 'fill-opacity' , 0 ) ;
pointPaths . exit ( ) . remove ( ) ;
pointPaths
. attr ( 'clip-path' , function ( d , i ) { return 'url(#clip-' + id + '-' + d . series + '-' + d . point + ')' } )
. attr ( 'd' , function ( d ) { return 'M' + d . data . join ( ',' ) + 'Z' ; } )
. on ( 'mouseover' , function ( d ) {
var series = data [ d . series ] ,
point = series . values [ d . point ] ;
dispatch . pointMouseover ( {
point : point ,
series : series ,
pos : [ x ( getX ( point ) ) + margin . left , y ( getY ( point ) ) + margin . top ] ,
seriesIndex : d . series ,
pointIndex : d . point
} ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
dispatch . pointMouseout ( {
point : data [ d . series ] . values [ d . point ] ,
series : data [ d . series ] ,
seriesIndex : d . series ,
pointIndex : d . point
} ) ;
} ) ;
dispatch . on ( 'pointMouseover.point' , function ( d ) {
wrap . select ( '.series-' + d . seriesIndex + ' .point-' + d . pointIndex )
. classed ( 'hover' , true ) ;
} ) ;
dispatch . on ( 'pointMouseout.point' , function ( d ) {
wrap . select ( '.series-' + d . seriesIndex + ' circle.point-' + d . pointIndex )
. classed ( 'hover' , false ) ;
} ) ;
}
var lines = wrap . select ( '.lines' ) . selectAll ( '.line' )
. data ( function ( d ) { return d } , function ( d ) { return d . key } ) ;
lines . enter ( ) . append ( 'g' )
. style ( 'stroke-opacity' , 1e-6 )
. style ( 'fill-opacity' , 1e-6 ) ;
d3 . transition ( lines . exit ( ) )
. style ( 'stroke-opacity' , 1e-6 )
. style ( 'fill-opacity' , 1e-6 )
. remove ( ) ;
lines
. attr ( 'class' , function ( d , i ) { return 'line series-' + i } )
. classed ( 'hover' , function ( d ) { return d . hover } )
. style ( 'fill' , function ( d , i ) { return color [ i % 20 ] } )
. style ( 'stroke' , function ( d , i ) { return color [ i % 20 ] } )
d3 . transition ( lines )
. style ( 'stroke-opacity' , 1 )
. style ( 'fill-opacity' , . 5 ) ;
var paths = lines . selectAll ( 'path' )
. data ( function ( d , i ) { return [ d . values ] } ) ;
paths . enter ( ) . append ( 'path' )
. attr ( 'd' , d3 . svg . line ( )
. x ( function ( d ) { return x0 ( getX ( d ) ) } )
. y ( function ( d ) { return y0 ( getY ( d ) ) } )
) ;
2012-03-23 19:16:59 +00:00
//d3.transition(paths.exit())
d3 . transition ( lines . exit ( ) . selectAll ( 'path' ) )
. attr ( 'd' , d3 . svg . line ( )
. x ( function ( d ) { return x ( getX ( d ) ) } )
. y ( function ( d ) { return y ( getY ( d ) ) } )
)
. remove ( ) ;
2012-03-20 23:43:21 +00:00
d3 . transition ( paths )
. attr ( 'd' , d3 . svg . line ( )
. x ( function ( d ) { return x ( getX ( d ) ) } )
. y ( function ( d ) { return y ( getY ( d ) ) } )
) ;
var points = lines . selectAll ( 'circle.point' )
. data ( function ( d ) { return d . values } ) ;
points . enter ( ) . append ( 'circle' )
. attr ( 'cx' , function ( d ) { return x0 ( getX ( d ) ) } )
. attr ( 'cy' , function ( d ) { return y0 ( getY ( d ) ) } ) ;
2012-03-23 19:16:59 +00:00
//d3.transition(points.exit())
d3 . transition ( lines . exit ( ) . selectAll ( 'circle.point' ) )
. attr ( 'cx' , function ( d ) { return x ( getX ( d ) ) } )
. attr ( 'cy' , function ( d ) { return y ( getY ( d ) ) } )
. remove ( ) ;
2012-03-20 23:43:21 +00:00
points . attr ( 'class' , function ( d , i ) { return 'point point-' + i } ) ;
d3 . transition ( points )
. attr ( 'cx' , function ( d ) { return x ( getX ( d ) ) } )
. attr ( 'cy' , function ( d ) { return y ( getY ( d ) ) } )
. attr ( 'r' , dotRadius ) ;
x0 = x . copy ( ) ;
y0 = y . copy ( ) ;
} ) ;
return chart ;
}
chart . dispatch = dispatch ;
chart . x = function ( _ ) {
if ( ! arguments . length ) return getX ;
getX = _ ;
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return getY ;
getY = _ ;
return chart ;
} ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin = _ ;
return chart ;
} ;
chart . width = function ( _ ) {
if ( ! arguments . length ) return width ;
width = _ ;
return chart ;
} ;
chart . height = function ( _ ) {
if ( ! arguments . length ) return height ;
height = _ ;
return chart ;
} ;
chart . xDomain = function ( _ ) {
if ( ! arguments . length ) return xDomain ;
xDomain = _ ;
return chart ;
} ;
chart . yDomain = function ( _ ) {
if ( ! arguments . length ) return yDomain ;
yDomain = _ ;
return chart ;
} ;
chart . interactive = function ( _ ) {
if ( ! arguments . length ) return interactive ;
interactive = _ ;
return chart ;
} ;
chart . dotRadius = function ( _ ) {
if ( ! arguments . length ) return dotRadius ;
dotRadius = d3 . functor ( _ ) ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = _ ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
return chart ;
}
2012-03-21 22:20:54 +00:00
nv . models . lineWithFocus = function ( ) {
2012-03-20 23:43:21 +00:00
var margin = { top : 30 , right : 20 , bottom : 30 , left : 60 } ,
margin2 = { top : 0 , right : 20 , bottom : 20 , left : 60 } ,
width = 960 ,
height = 500 ,
height1 = 400 ,
height2 = 100 ,
dotRadius = function ( ) { return 2.5 } ,
color = d3 . scale . category10 ( ) . range ( ) ,
2012-03-23 19:16:59 +00:00
getX = function ( d ) { return d . x } ,
getY = function ( d ) { return d . y } ,
id = Math . floor ( Math . random ( ) * 10000 ) ; //Create semi-unique ID incase user doesn't select one
2012-03-20 23:43:21 +00:00
var x = d3 . scale . linear ( ) ,
y = d3 . scale . linear ( ) ,
x2 = d3 . scale . linear ( ) ,
y2 = d3 . scale . linear ( ) ,
xAxis = nv . models . xaxis ( ) . scale ( x ) ,
yAxis = nv . models . yaxis ( ) . scale ( y ) ,
xAxis2 = nv . models . xaxis ( ) . scale ( x2 ) ,
yAxis2 = nv . models . yaxis ( ) . scale ( y2 ) ,
legend = nv . models . legend ( ) . height ( 30 ) ,
focus = nv . models . line ( ) ,
2012-03-23 19:16:59 +00:00
context = nv . models . line ( ) . dotRadius ( . 1 ) . interactive ( false ) ,
dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' ) ;
2012-03-20 23:43:21 +00:00
var brush = d3 . svg . brush ( )
. x ( x2 )
. on ( 'brush' , onBrush ) ;
2012-03-22 23:28:13 +00:00
var wrap , gEnter , g , focus , focusLines , contextWrap , focusWrap , contextLines ; //brought all variables to this scope for use within brush function... is this a bad idea?
2012-03-20 23:43:21 +00:00
2012-03-22 23:28:13 +00:00
var seriesData ; //Temporarily bringing this data to this scope.... may be bad idea (same with above).. may need to rethink brushing
2012-03-20 23:43:21 +00:00
function chart ( selection ) {
selection . each ( function ( data ) {
2012-03-22 23:28:13 +00:00
seriesData = data . filter ( function ( d ) { return ! d . disabled } )
2012-03-20 23:43:21 +00:00
. map ( function ( d ) { return d . values } ) ;
x2 . domain ( d3 . extent ( d3 . merge ( seriesData ) , getX ) )
. range ( [ 0 , width - margin . left - margin . right ] ) ;
y2 . domain ( d3 . extent ( d3 . merge ( seriesData ) , getY ) )
. range ( [ height2 - margin2 . top - margin2 . bottom , 0 ] ) ;
x . domain ( brush . empty ( ) ? x2 . domain ( ) : brush . extent ( ) )
. range ( [ 0 , width - margin . left - margin . right ] ) ;
y . domain ( y2 . domain ( ) )
. range ( [ height1 - margin . top - margin . bottom , 0 ] ) ;
focus
. width ( width - margin . left - margin . right )
. height ( height1 - margin . top - margin . bottom )
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % 10 ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) )
context
. width ( width - margin . left - margin . right )
. height ( height2 - margin2 . top - margin2 . bottom )
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % 10 ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) )
wrap = d3 . select ( this ) . selectAll ( 'g.wrap' ) . data ( [ data ] ) ;
gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'wrap d3lineWithFocus' ) . append ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'focus' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'context' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'legendWrap' ) ;
g = wrap . select ( 'g' )
//.attr('transform', 'translate(0,0)');
// ********** LEGEND **********
legend . width ( width / 2 - margin . right ) ;
g . select ( '.legendWrap' )
. datum ( data )
. attr ( 'transform' , 'translate(' + ( width / 2 - margin . left ) + ',0)' )
. call ( legend ) ;
//TODO: margins should be adjusted based on what components are used: axes, axis labels, legend
margin . top = legend . height ( ) ;
// ********** FOCUS **********
focusWrap = g . select ( '.focus' )
. attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
gEnter . select ( '.focus' ) . append ( 'g' ) . attr ( 'class' , 'x axis' ) ;
gEnter . select ( '.focus' ) . append ( 'g' ) . attr ( 'class' , 'y axis' ) ;
gEnter . select ( '.focus' ) . append ( 'g' ) . attr ( 'class' , 'focusLines' ) ;
focusLines = g . select ( '.focusLines' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( focusLines ) . call ( focus ) ;
xAxis
. domain ( x . domain ( ) )
. range ( x . range ( ) )
. ticks ( width / 100 )
. tickSize ( - ( height1 - margin . top - margin . bottom ) , 0 ) ;
g . select ( '.x.axis' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( g . select ( '.x.axis' ) )
. call ( xAxis ) ;
yAxis
. domain ( y . domain ( ) )
. range ( y . range ( ) )
. ticks ( height / 36 )
. tickSize ( - ( width - margin . right - margin . left ) , 0 ) ;
d3 . transition ( g . select ( '.y.axis' ) )
. call ( yAxis ) ;
// ********** CONTEXT **********
contextWrap = g . select ( '.context' )
. attr ( 'transform' , 'translate(' + margin2 . left + ',' + height1 + ')' ) ;
gEnter . select ( '.context' ) . append ( 'g' ) . attr ( 'class' , 'x2 axis' ) ;
gEnter . select ( '.context' ) . append ( 'g' ) . attr ( 'class' , 'y2 axis' ) ;
gEnter . select ( '.context' ) . append ( 'g' ) . attr ( 'class' , 'contextLines' ) ;
gEnter . select ( '.context' ) . append ( 'g' ) . attr ( 'class' , 'x brush' )
. attr ( 'class' , 'x brush' )
. call ( brush )
. selectAll ( 'rect' )
. attr ( 'y' , - 5 )
. attr ( 'height' , height2 + 4 ) ;
contextLines = contextWrap . select ( '.contextLines' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( contextLines ) . call ( context ) ;
xAxis2
. domain ( x2 . domain ( ) )
. range ( x2 . range ( ) )
. ticks ( width / 100 )
. tickSize ( - ( height2 - margin2 . top - margin2 . bottom ) , 0 ) ;
contextWrap . select ( '.x2.axis' )
. attr ( 'transform' , 'translate(0,' + y2 . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( contextWrap . select ( '.x2.axis' ) )
. call ( xAxis2 ) ;
yAxis2
. domain ( y2 . domain ( ) )
. range ( y2 . range ( ) )
. ticks ( ( height2 - margin2 . top - margin2 . bottom ) / 24 )
. tickSize ( - ( width - margin2 . right - margin2 . left ) , 0 ) ;
contextWrap . select ( '.y2.axis' ) ;
d3 . transition ( contextWrap . select ( '.y2.axis' ) )
. call ( yAxis2 ) ;
// ********** EVENT LISTENERS **********
legend . dispatch . on ( 'legendClick' , function ( d , i ) {
d . disabled = ! d . disabled ;
if ( ! data . filter ( function ( d ) { return ! d . disabled } ) . length ) {
data . map ( function ( d ) {
d . disabled = false ;
wrap . selectAll ( '.series' ) . classed ( 'disabled' , false ) ;
return d ;
} ) ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
legend . dispatch . on ( 'legendMouseover' , function ( d , i ) {
d . hover = true ;
selection . transition ( ) . call ( chart )
} ) ;
legend . dispatch . on ( 'legendMouseout' , function ( d , i ) {
d . hover = false ;
selection . transition ( ) . call ( chart )
} ) ;
focus . dispatch . on ( 'pointMouseover.tooltip' , function ( e ) {
dispatch . tooltipShow ( {
point : e . point ,
series : e . series ,
pos : [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ,
seriesIndex : e . seriesIndex ,
pointIndex : e . pointIndex
} ) ;
} ) ;
focus . dispatch . on ( 'pointMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
} ) ;
return chart ;
}
// ********** FUNCTIONS **********
function onBrush ( ) {
2012-03-22 23:28:13 +00:00
var yDomain = brush . empty ( ) ? y2 . domain ( ) : d3 . extent ( d3 . merge ( seriesData ) . filter ( function ( d ) {
return getX ( d ) >= brush . extent ( ) [ 0 ] && getX ( d ) <= brush . extent ( ) [ 1 ] ;
} ) , getY ) ;
if ( typeof yDomain [ 0 ] == 'undefined' ) yDomain = y2 . domain ( ) ;
2012-03-20 23:43:21 +00:00
x . domain ( brush . empty ( ) ? x2 . domain ( ) : brush . extent ( ) ) ;
2012-03-22 23:28:13 +00:00
y . domain ( yDomain ) ;
//y.domain(brush.empty() ? y2.domain() : d3.extent(d3.merge(seriesData).filter(function(d) {
//return getX(d) >= brush.extent()[0] && getX(d) <= brush.extent()[1];
//}), getY) || y2.domain() );
2012-03-20 23:43:21 +00:00
focus . xDomain ( x . domain ( ) ) ;
2012-03-22 23:28:13 +00:00
focus . yDomain ( y . domain ( ) ) ;
2012-03-20 23:43:21 +00:00
focusLines . call ( focus )
wrap . select ( '.x.axis' ) . call ( xAxis ) ;
2012-03-22 23:28:13 +00:00
wrap . select ( '.y.axis' ) . call ( yAxis ) ;
2012-03-20 23:43:21 +00:00
}
// ********** PUBLIC ACCESSORS **********
chart . dispatch = dispatch ;
chart . x = function ( _ ) {
if ( ! arguments . length ) return getX ;
getX = _ ;
focus . x ( _ ) ;
context . x ( _ ) ;
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return getY ;
getY = _ ;
focus . y ( _ ) ;
context . y ( _ ) ;
return chart ;
} ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin = _ ;
return chart ;
} ;
chart . width = function ( _ ) {
if ( ! arguments . length ) return width ;
width = _ ;
return chart ;
} ;
chart . height = function ( _ ) {
if ( ! arguments . length ) return height ;
height = _ ;
height1 = _ - height2 ;
return chart ;
} ;
chart . contextHeight = function ( _ ) {
if ( ! arguments . length ) return height2 ;
height2 = _ ;
height1 = height - _ ;
return chart ;
} ;
chart . dotRadius = function ( _ ) {
if ( ! arguments . length ) return dotRadius ;
dotRadius = d3 . functor ( _ ) ;
focus . dotRadius = _ ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
// Chart has multiple similar Axes, to prevent code duplication, probably need to link all axis functions manually like below
chart . xTickFormat = function ( _ ) {
if ( ! arguments . length ) return x . tickFormat ( ) ;
xAxis . tickFormat ( _ ) ;
xAxis2 . tickFormat ( _ ) ;
return chart ;
} ;
chart . yTickFormat = function ( _ ) {
if ( ! arguments . length ) return y . tickFormat ( ) ;
yAxis . tickFormat ( _ ) ;
yAxis2 . tickFormat ( _ ) ;
return chart ;
} ;
//TODO: allow for both focus and context axes to be linked
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
return chart ;
}
nv . models . lineWithLegend = function ( ) {
var margin = { top : 30 , right : 20 , bottom : 50 , left : 60 } ,
width = 960 ,
height = 500 ,
dotRadius = function ( ) { return 2.5 } ,
color = d3 . scale . category10 ( ) . range ( ) ,
dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' ) ;
var x = d3 . scale . linear ( ) ,
y = d3 . scale . linear ( ) ,
getX = function ( d ) { return d . x } ,
getY = function ( d ) { return d . y } ,
xAxis = nv . models . xaxis ( ) . scale ( x ) ,
yAxis = nv . models . yaxis ( ) . scale ( y ) ,
legend = nv . models . legend ( ) . height ( 30 ) ,
lines = nv . models . line ( ) ;
function chart ( selection ) {
selection . each ( function ( data ) {
var series = data . filter ( function ( d ) { return ! d . disabled } )
. map ( function ( d ) { return d . values } ) ;
x . domain ( d3 . extent ( d3 . merge ( series ) , getX ) )
. range ( [ 0 , width - margin . left - margin . right ] ) ;
y . domain ( d3 . extent ( d3 . merge ( series ) , getY ) )
. range ( [ height - margin . top - margin . bottom , 0 ] ) ;
lines
. width ( width - margin . left - margin . right )
. height ( height - margin . top - margin . bottom )
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % 10 ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) )
var wrap = d3 . select ( this ) . selectAll ( 'g.wrap' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'wrap d3lineWithLegend' ) . append ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'x axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'y axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'linesWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'legendWrap' ) ;
legend . dispatch . on ( 'legendClick' , function ( d , i ) {
d . disabled = ! d . disabled ;
if ( ! data . filter ( function ( d ) { return ! d . disabled } ) . length ) {
data . map ( function ( d ) {
d . disabled = false ;
wrap . selectAll ( '.series' ) . classed ( 'disabled' , false ) ;
return d ;
} ) ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
legend . dispatch . on ( 'legendMouseover' , function ( d , i ) {
d . hover = true ;
selection . transition ( ) . call ( chart )
} ) ;
legend . dispatch . on ( 'legendMouseout' , function ( d , i ) {
d . hover = false ;
selection . transition ( ) . call ( chart )
} ) ;
lines . dispatch . on ( 'pointMouseover.tooltip' , function ( e ) {
dispatch . tooltipShow ( {
point : e . point ,
series : e . series ,
pos : [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ,
seriesIndex : e . seriesIndex ,
pointIndex : e . pointIndex
} ) ;
} ) ;
lines . dispatch . on ( 'pointMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
//TODO: margins should be adjusted based on what components are used: axes, axis labels, legend
margin . top = legend . height ( ) ;
var g = wrap . select ( 'g' )
. attr ( 'transform' , 'translate(' + margin . left + ',' + legend . height ( ) + ')' ) ;
legend . width ( width / 2 - margin . right ) ;
g . select ( '.legendWrap' )
. datum ( data )
. attr ( 'transform' , 'translate(' + ( width / 2 - margin . left ) + ',' + ( - legend . height ( ) ) + ')' )
. call ( legend ) ;
var linesWrap = g . select ( '.linesWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( linesWrap ) . call ( lines ) ;
xAxis
. domain ( x . domain ( ) )
. range ( x . range ( ) )
. ticks ( width / 100 )
. tickSize ( - ( height - margin . top - margin . bottom ) , 0 ) ;
g . select ( '.x.axis' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( g . select ( '.x.axis' ) )
. call ( xAxis ) ;
yAxis
. domain ( y . domain ( ) )
. range ( y . range ( ) )
. ticks ( height / 36 )
. tickSize ( - ( width - margin . right - margin . left ) , 0 ) ;
d3 . transition ( g . select ( '.y.axis' ) )
. call ( yAxis ) ;
} ) ;
return chart ;
}
chart . dispatch = dispatch ;
chart . x = function ( _ ) {
if ( ! arguments . length ) return getX ;
getX = _ ;
lines . x ( _ ) ;
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return getY ;
getY = _ ;
lines . y ( _ ) ;
return chart ;
} ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin = _ ;
return chart ;
} ;
chart . width = function ( _ ) {
if ( ! arguments . length ) return width ;
width = _ ;
return chart ;
} ;
chart . height = function ( _ ) {
if ( ! arguments . length ) return height ;
height = _ ;
return chart ;
} ;
chart . dotRadius = function ( _ ) {
if ( ! arguments . length ) return dotRadius ;
dotRadius = d3 . functor ( _ ) ;
lines . dotRadius = _ ;
return chart ;
} ;
// Expose the x-axis' tickFormat method.
//chart.xAxis = {};
//d3.rebind(chart.xAxis, xAxis, 'tickFormat');
chart . xAxis = xAxis ;
// Expose the y-axis' tickFormat method.
//chart.yAxis = {};
//d3.rebind(chart.yAxis, yAxis, 'tickFormat');
chart . yAxis = yAxis ;
return chart ;
}
nv . models . scatter = function ( ) {
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 } ,
width = 960 ,
height = 500 ,
color = d3 . scale . category10 ( ) . range ( ) ,
id = Math . floor ( Math . random ( ) * 100000 ) , //Create semi-unique ID incase user doesn't selet one
x = d3 . scale . linear ( ) ,
y = d3 . scale . linear ( ) ,
z = d3 . scale . sqrt ( ) , //sqrt because point size is done by area, not radius
getX = function ( d ) { return d . x } , // or d[0]
getY = function ( d ) { return d . y } , // or d[1]
getSize = function ( d ) { return d . size } , // or d[2]
forceX = [ ] ,
forceY = [ ] ,
x0 , y0 , z0 ,
dispatch = d3 . dispatch ( 'pointMouseover' , 'pointMouseout' ) ;
function chart ( selection ) {
selection . each ( function ( data ) {
var seriesData = data . map ( function ( d ) { return d . values } ) ;
x0 = x0 || x ;
y0 = y0 || y ;
z0 = z0 || z ;
//TODO: reconsider points {x: #, y: #} instead of [x,y]
//add series data to each point for future ease of use
data = data . map ( function ( series , i ) {
series . values = series . values . map ( function ( point ) {
//point.label = series.label;
//point.color = series.color;
point . series = i ;
return point ;
} ) ;
return series ;
} ) ;
//TODO: figure out the best way to deal with scales with equal MIN and MAX
x . domain ( d3 . extent ( d3 . merge ( seriesData ) . map ( getX ) . concat ( forceX ) ) )
. range ( [ 0 , width - margin . left - margin . right ] ) ;
y . domain ( d3 . extent ( d3 . merge ( seriesData ) . map ( getY ) . concat ( forceY ) ) )
. range ( [ height - margin . top - margin . bottom , 0 ] ) ;
z . domain ( d3 . extent ( d3 . merge ( seriesData ) , getSize ) )
. range ( [ 2 , 10 ] ) ;
var vertices = d3 . merge ( data . map ( function ( group , groupIndex ) {
return group . values . map ( function ( point , pointIndex ) {
return [ x ( getX ( point ) ) , y ( getY ( point ) ) , groupIndex , pointIndex ] ; //inject series and point index for reference into voronoi
} )
} )
) ;
var wrap = d3 . select ( this ) . selectAll ( 'g.d3scatter' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'd3scatter' ) . append ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'groups' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'point-clips' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'point-paths' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'distribution' ) ;
var g = wrap . select ( 'g' )
. attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
var voronoiClip = gEnter . append ( 'g' ) . attr ( 'class' , 'voronoi-clip' )
. append ( 'clipPath' )
. attr ( 'id' , 'voronoi-clip-path-' + id )
. append ( 'rect' ) ;
wrap . select ( '.voronoi-clip rect' )
. attr ( 'x' , - 10 )
. attr ( 'y' , - 10 )
. attr ( 'width' , width - margin . left - margin . right + 20 )
. attr ( 'height' , height - margin . top - margin . bottom + 20 ) ;
wrap . select ( '.point-paths' )
. attr ( 'clip-path' , 'url(#voronoi-clip-path-' + id + ')' ) ;
//var pointClips = wrap.select('.point-clips').selectAll('clipPath') // **BROWSER BUG** can't reselect camel cased elements
var pointClips = wrap . select ( '.point-clips' ) . selectAll ( '.clip-path' )
. data ( vertices ) ;
pointClips . enter ( ) . append ( 'clipPath' ) . attr ( 'class' , 'clip-path' )
. append ( 'circle' )
. attr ( 'r' , 25 ) ;
pointClips . exit ( ) . remove ( ) ;
pointClips
. attr ( 'id' , function ( d , i ) { return 'clip-' + id + '-' + d [ 2 ] + '-' + d [ 3 ] } )
. attr ( 'transform' , function ( d ) { return 'translate(' + d [ 0 ] + ',' + d [ 1 ] + ')' } )
//inject series and point index for reference into voronoi
var voronoi = d3 . geom . voronoi ( vertices ) . map ( function ( d , i ) { return { 'data' : d , 'series' : vertices [ i ] [ 2 ] , 'point' : vertices [ i ] [ 3 ] } } ) ;
//TODO: Need to deal with duplicates, maybe add small amount of noise to all
var pointPaths = wrap . select ( '.point-paths' ) . selectAll ( 'path' )
. data ( voronoi ) ;
pointPaths . enter ( ) . append ( 'path' )
. attr ( 'class' , function ( d , i ) { return 'path-' + i ; } ) ;
pointPaths . exit ( ) . remove ( ) ;
pointPaths
. attr ( 'clip-path' , function ( d , i ) { return 'url(#clip-' + id + '-' + d . series + '-' + d . point + ')' } )
. attr ( 'd' , function ( d ) { return 'M' + d . data . join ( ',' ) + 'Z' ; } )
. on ( 'mouseover' , function ( d ) {
dispatch . pointMouseover ( {
point : data [ d . series ] . values [ d . point ] ,
series : data [ d . series ] ,
pos : [ x ( getX ( data [ d . series ] . values [ d . point ] ) ) + margin . left , y ( getY ( data [ d . series ] . values [ d . point ] ) ) + margin . top ] ,
seriesIndex : d . series ,
pointIndex : d . point
}
) ;
} )
. on ( 'mouseout' , function ( d , i ) {
dispatch . pointMouseout ( {
point : data [ d . series ] . values [ d . point ] ,
series : data [ d . series ] ,
seriesIndex : d . series ,
pointIndex : d . point
} ) ;
} ) ;
var groups = wrap . select ( '.groups' ) . selectAll ( '.group' )
. data ( function ( d ) { return d } , function ( d ) { return d . key } ) ;
groups . enter ( ) . append ( 'g' )
. style ( 'stroke-opacity' , 1e-6 )
. style ( 'fill-opacity' , 1e-6 ) ;
d3 . transition ( groups . exit ( ) )
. style ( 'stroke-opacity' , 1e-6 )
. style ( 'fill-opacity' , 1e-6 )
. remove ( ) ;
groups
. attr ( 'class' , function ( d , i ) { return 'group series-' + i } )
. classed ( 'hover' , function ( d ) { return d . hover && ! d . disabled } ) ;
d3 . transition ( groups )
. style ( 'fill' , function ( d , i ) { return color [ i % 10 ] } )
. style ( 'stroke' , function ( d , i ) { return color [ i % 10 ] } )
. style ( 'stroke-opacity' , 1 )
. style ( 'fill-opacity' , . 5 ) ;
var points = groups . selectAll ( 'circle.point' )
. data ( function ( d ) { return d . values } ) ;
points . enter ( ) . append ( 'circle' )
. attr ( 'cx' , function ( d ) { return x0 ( getX ( d ) ) } )
. attr ( 'cy' , function ( d ) { return y0 ( getY ( d ) ) } )
. attr ( 'r' , function ( d ) { return z0 ( getSize ( d ) ) } ) ;
2012-03-23 19:16:59 +00:00
//d3.transition(points.exit())
d3 . transition ( groups . exit ( ) . selectAll ( 'circle.point' ) )
. attr ( 'cx' , function ( d ) { return x ( getX ( d ) ) } )
. attr ( 'cy' , function ( d ) { return y ( getY ( d ) ) } )
. attr ( 'r' , function ( d ) { return z ( getSize ( d ) ) } )
. remove ( ) ;
2012-03-20 23:43:21 +00:00
points . attr ( 'class' , function ( d , i ) { return 'point point-' + i } ) ;
d3 . transition ( points )
. attr ( 'cx' , function ( d ) { return x ( getX ( d ) ) } )
. attr ( 'cy' , function ( d ) { return y ( getY ( d ) ) } )
. attr ( 'r' , function ( d ) { return z ( getSize ( d ) ) } ) ;
var distX = groups . selectAll ( 'line.distX' )
. data ( function ( d ) { return d . values } )
distX . enter ( ) . append ( 'line' )
. attr ( 'x1' , function ( d ) { return x0 ( getX ( d ) ) } )
. attr ( 'x2' , function ( d ) { return x0 ( getX ( d ) ) } )
2012-03-23 19:16:59 +00:00
//d3.transition(distX.exit())
d3 . transition ( groups . exit ( ) . selectAll ( 'line.distX' ) )
. attr ( 'x1' , function ( d ) { return x ( getX ( d ) ) } )
. attr ( 'x2' , function ( d ) { return x ( getX ( d ) ) } )
. remove ( ) ;
2012-03-20 23:43:21 +00:00
distX
. attr ( 'class' , function ( d , i ) { return 'distX distX-' + i } )
. attr ( 'y1' , y . range ( ) [ 0 ] )
. attr ( 'y2' , y . range ( ) [ 0 ] + 8 ) ;
d3 . transition ( distX )
. attr ( 'x1' , function ( d ) { return x ( getX ( d ) ) } )
. attr ( 'x2' , function ( d ) { return x ( getX ( d ) ) } )
var distY = groups . selectAll ( 'line.distY' )
. data ( function ( d ) { return d . values } )
distY . enter ( ) . append ( 'line' )
. attr ( 'y1' , function ( d ) { return y0 ( getY ( d ) ) } )
. attr ( 'y2' , function ( d ) { return y0 ( getY ( d ) ) } ) ;
2012-03-23 19:16:59 +00:00
//d3.transition(distY.exit())
d3 . transition ( groups . exit ( ) . selectAll ( 'line.distY' ) )
. attr ( 'y1' , function ( d ) { return y ( getY ( d ) ) } )
. attr ( 'y2' , function ( d ) { return y ( getY ( d ) ) } )
. remove ( ) ;
2012-03-20 23:43:21 +00:00
distY
. attr ( 'class' , function ( d , i ) { return 'distY distY-' + i } )
. attr ( 'x1' , x . range ( ) [ 0 ] )
. attr ( 'x2' , x . range ( ) [ 0 ] - 8 )
d3 . transition ( distY )
. attr ( 'y1' , function ( d ) { return y ( getY ( d ) ) } )
. attr ( 'y2' , function ( d ) { return y ( getY ( d ) ) } ) ;
dispatch . on ( 'pointMouseover.point' , function ( d ) {
wrap . select ( '.series-' + d . seriesIndex + ' .point-' + d . pointIndex )
. classed ( 'hover' , true ) ;
wrap . select ( '.series-' + d . seriesIndex + ' .distX-' + d . pointIndex )
. attr ( 'y1' , d . pos [ 1 ] - margin . top ) ;
wrap . select ( '.series-' + d . seriesIndex + ' .distY-' + d . pointIndex )
. attr ( 'x1' , d . pos [ 0 ] - margin . left ) ;
} ) ;
dispatch . on ( 'pointMouseout.point' , function ( d ) {
wrap . select ( '.series-' + d . seriesIndex + ' circle.point-' + d . pointIndex )
. classed ( 'hover' , false ) ;
wrap . select ( '.series-' + d . seriesIndex + ' .distX-' + d . pointIndex )
. attr ( 'y1' , y . range ( ) [ 0 ] ) ;
wrap . select ( '.series-' + d . seriesIndex + ' .distY-' + d . pointIndex )
. attr ( 'x1' , x . range ( ) [ 0 ] ) ;
} ) ;
//store old scales for use in transitions on update
x0 = x . copy ( ) ;
y0 = y . copy ( ) ;
z0 = z . copy ( ) ;
} ) ;
return chart ;
}
chart . dispatch = dispatch ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin = _ ;
return chart ;
} ;
chart . width = function ( _ ) {
if ( ! arguments . length ) return width ;
width = _ ;
return chart ;
} ;
chart . height = function ( _ ) {
if ( ! arguments . length ) return height ;
height = _ ;
return chart ;
} ;
chart . x = function ( _ ) {
if ( ! arguments . length ) return getX ;
getX = d3 . functor ( _ ) ;
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return getY ;
getY = d3 . functor ( _ ) ;
return chart ;
} ;
chart . size = function ( _ ) {
if ( ! arguments . length ) return getSize ;
getSize = d3 . functor ( _ ) ;
return chart ;
} ;
chart . forceX = function ( _ ) {
if ( ! arguments . length ) return forceX ;
forceX = _ ;
return chart ;
} ;
chart . forceY = function ( _ ) {
if ( ! arguments . length ) return forceY ;
forceY = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = _ ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
return chart ;
}
nv . models . scatterWithLegend = function ( ) {
var margin = { top : 30 , right : 20 , bottom : 50 , left : 60 } ,
width = 960 ,
height = 500 ,
animate = 500 ,
xAxisRender = true ,
yAxisRender = true ,
xAxisLabelText = false ,
yAxisLabelText = false ,
color = d3 . scale . category10 ( ) . range ( ) ,
getX = function ( d ) { return d . x } , // or d[0]
getY = function ( d ) { return d . y } , // or d[1]
getSize = function ( d ) { return d . size } , // or d[2]
forceX = [ ] ,
forceY = [ ] ,
dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' ) ;
var x = d3 . scale . linear ( ) ,
y = d3 . scale . linear ( ) ,
xAxis = nv . models . xaxis ( ) . scale ( x ) . tickPadding ( 10 ) ,
yAxis = nv . models . yaxis ( ) . scale ( y ) . tickPadding ( 10 ) ,
legend = nv . models . legend ( ) . height ( 30 ) ,
scatter = nv . models . scatter ( ) ;
function chart ( selection ) {
selection . each ( function ( data ) {
var seriesData = data . filter ( function ( d ) { return ! d . disabled } )
. map ( function ( d ) { return d . values } ) ;
x . domain ( d3 . extent ( d3 . merge ( seriesData ) . map ( getX ) . concat ( forceX ) ) )
. range ( [ 0 , width - margin . left - margin . right ] ) ;
y . domain ( d3 . extent ( d3 . merge ( seriesData ) . map ( getY ) . concat ( forceY ) ) )
. range ( [ height - margin . top - margin . bottom , 0 ] ) ;
scatter
. width ( width - margin . left - margin . right )
. height ( height - margin . top - margin . bottom )
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % 20 ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) )
xAxis
. ticks ( width / 100 )
. tickSize ( - ( height - margin . top - margin . bottom ) , 0 ) ;
yAxis
. ticks ( height / 36 )
. tickSize ( - ( width - margin . right - margin . left ) , 0 ) ;
var wrap = d3 . select ( this ) . selectAll ( 'g.wrap' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'wrap d3lineWithLegend' ) . append ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'legendWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'x axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'y axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'scatterWrap' ) ;
legend . dispatch . on ( 'legendClick' , function ( d , i , that ) {
d . disabled = ! d . disabled ;
//d3.select(that).classed('disabled', d.disabled); //TODO: do this from the data, not manually
if ( ! data . filter ( function ( d ) { return ! d . disabled } ) . length ) {
data . map ( function ( d ) {
d . disabled = false ;
wrap . selectAll ( '.series' ) . classed ( 'disabled' , false ) ;
return d ;
} ) ;
}
selection . transition ( animate ) . call ( chart )
//d3.transition(selection).call(chart);
} ) ;
2012-03-23 19:16:59 +00:00
/ *
2012-03-20 23:43:21 +00:00
legend . dispatch . on ( 'legendMouseover' , function ( d , i ) {
d . hover = true ;
selection . transition ( ) . call ( chart )
} ) ;
legend . dispatch . on ( 'legendMouseout' , function ( d , i ) {
d . hover = false ;
selection . transition ( ) . call ( chart )
} ) ;
2012-03-23 19:16:59 +00:00
* /
2012-03-20 23:43:21 +00:00
scatter . dispatch . on ( 'pointMouseover.tooltip' , function ( e ) {
dispatch . tooltipShow ( {
point : e . point ,
series : e . series ,
pos : [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ,
seriesIndex : e . seriesIndex ,
pointIndex : e . pointIndex
} ) ;
} ) ;
scatter . dispatch . on ( 'pointMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
legend . width ( width / 2 - margin . right ) ;
wrap . select ( '.legendWrap' )
. datum ( data )
. attr ( 'transform' , 'translate(' + ( width / 2 - margin . left ) + ',' + ( - legend . height ( ) ) + ')' )
. call ( legend ) ;
//TODO: margins should be adjusted based on what components are used: axes, axis labels, legend
margin . top = legend . height ( ) ;
var g = wrap . select ( 'g' )
. attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
var scatterWrap = wrap . select ( '.scatterWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) ) ;
//log(d3.transition()[0][0].duration); //get parent's duration
d3 . transition ( scatterWrap ) . call ( scatter ) ;
xAxis
. domain ( x . domain ( ) )
. range ( x . range ( ) )
. ticks ( width / 100 )
. tickSize ( - ( height - margin . top - margin . bottom ) , 0 ) ;
g . select ( '.x.axis' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( g . select ( '.x.axis' ) )
. call ( xAxis ) ;
yAxis
. domain ( y . domain ( ) )
. range ( y . range ( ) )
. ticks ( height / 36 )
. tickSize ( - ( width - margin . right - margin . left ) , 0 ) ;
d3 . transition ( g . select ( '.y.axis' ) )
. call ( yAxis ) ;
} ) ;
return chart ;
}
chart . dispatch = dispatch ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin = _ ;
return chart ;
} ;
chart . width = function ( _ ) {
if ( ! arguments . length ) return width ;
width = _ ;
return chart ;
} ;
chart . height = function ( _ ) {
if ( ! arguments . length ) return height ;
height = _ ;
return chart ;
} ;
chart . forceX = function ( _ ) {
if ( ! arguments . length ) return forceX ;
forceX = _ ;
scatter . forceX ( _ ) ;
return chart ;
} ;
chart . forceY = function ( _ ) {
if ( ! arguments . length ) return forceY ;
forceY = _ ;
scatter . forceY ( _ ) ;
return chart ;
} ;
chart . animate = function ( _ ) {
if ( ! arguments . length ) return animate ;
animate = _ ;
return chart ;
} ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
return chart ;
}
nv . models . sparkline = function ( ) {
var margin = { top : 3 , right : 3 , bottom : 3 , left : 3 } ,
width = 200 ,
height = 20 ,
animate = true ,
color = d3 . scale . category20 ( ) . range ( ) ;
var x = d3 . scale . linear ( ) ,
y = d3 . scale . linear ( ) ;
function chart ( selection ) {
selection . each ( function ( data ) {
x . domain ( d3 . extent ( data , function ( d ) { return d . x } ) )
. range ( [ 0 , width - margin . left - margin . right ] ) ;
y . domain ( d3 . extent ( data , function ( d ) { return d . y } ) )
. range ( [ height - margin . top - margin . bottom , 0 ] ) ;
var svg = d3 . select ( this ) . selectAll ( 'svg' ) . data ( [ data ] ) ;
var gEnter = svg . enter ( ) . append ( 'svg' ) . append ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'sparkline' )
. attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' )
//.style('fill', function(d, i){ return d.color || color[i * 2 % 20] })
. style ( 'stroke' , function ( d , i ) { return d . color || color [ i * 2 % 20 ] } ) ;
svg . attr ( 'width' , width )
. attr ( 'height' , height ) ;
var paths = gEnter . select ( '.sparkline' ) . selectAll ( 'path' )
. data ( function ( d ) { return [ d ] } ) ;
paths . enter ( ) . append ( 'path' ) ;
paths . exit ( ) . remove ( ) ;
paths
. attr ( 'd' , d3 . svg . line ( )
. x ( function ( d ) { return x ( d . x ) } )
. y ( function ( d ) { return y ( d . y ) } )
) ;
var points = gEnter . select ( '.sparkline' ) . selectAll ( 'circle.point' )
. data ( function ( d ) { return d . filter ( function ( p ) { return y . domain ( ) . indexOf ( p . y ) != - 1 } ) } ) ;
points . enter ( ) . append ( 'circle' ) . attr ( 'class' , 'point' ) ;
points . exit ( ) . remove ( ) ;
points
. attr ( 'cx' , function ( d ) { return x ( d . x ) } )
. attr ( 'cy' , function ( d ) { return y ( d . y ) } )
. attr ( 'r' , 2 )
. style ( 'stroke' , function ( d , i ) { return d . y == y . domain ( ) [ 0 ] ? '#d62728' : '#2ca02c' } )
. style ( 'fill' , function ( d , i ) { return d . y == y . domain ( ) [ 0 ] ? '#d62728' : '#2ca02c' } ) ;
} ) ;
return chart ;
}
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin = _ ;
return chart ;
} ;
chart . width = function ( _ ) {
if ( ! arguments . length ) return width ;
width = _ ;
return chart ;
} ;
chart . height = function ( _ ) {
if ( ! arguments . length ) return height ;
height = _ ;
return chart ;
} ;
chart . animate = function ( _ ) {
if ( ! arguments . length ) return animate ;
animate = _ ;
return chart ;
} ;
return chart ;
}
2012-03-21 22:03:18 +00:00
} ) ( ) ;