( function ( ) {
var nv = {
version : '0.0.1a' ,
dev : true //set false when in production
} ;
window . nv = nv ;
nv . tooltip = { } ; // For the tooltip system
nv . utils = { } ; // Utility subsystem
nv . models = { } ; //stores all the possible models/components
nv . charts = { } ; //stores all the ready to use charts
nv . graphs = [ ] ; //stores all the graphs currently on the page
nv . logs = { } ; //stores some statistics and potential error messages
nv . dispatch = d3 . dispatch ( 'render_start' , 'render_end' ) ;
// *************************************************************************
// Development render timers - disabled if dev = false
if ( nv . dev ) {
nv . dispatch . on ( 'render_start' , function ( e ) {
nv . logs . startTime = + new Date ( ) ;
} ) ;
nv . dispatch . on ( 'render_end' , function ( e ) {
nv . logs . endTime = + new Date ( ) ;
nv . logs . totalTime = nv . logs . endTime - nv . logs . startTime ;
nv . log ( 'total' , nv . logs . totalTime ) ; // used for development, to keep track of graph generation times
} ) ;
}
// ********************************************
// Public Core NV functions
// Logs all arguments, and returns the last so you can test things in place
nv . log = function ( ) {
if ( nv . dev && console . log && console . log . apply ) console . log . apply ( console , arguments ) ;
return arguments [ arguments . length - 1 ] ;
} ;
nv . render = function render ( step ) {
step = step || 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 == typeof ( Function ) ) graph . callback ( chart ) ;
nv . graphs . push ( chart ) ;
}
render . queue . splice ( 0 , i ) ;
if ( render . queue . length ) setTimeout ( arguments . callee , 0 ) ;
else { nv . render . active = false ; nv . dispatch . render _end ( ) ; }
} , 0 ) ;
} ;
nv . render . active = false ;
nv . render . queue = [ ] ;
nv . addGraph = function ( obj ) {
if ( typeof arguments [ 0 ] === typeof ( Function ) )
obj = { generate : arguments [ 0 ] , callback : arguments [ 1 ] } ;
nv . render . queue . push ( obj ) ;
if ( ! nv . render . active ) nv . render ( ) ;
} ;
nv . identity = function ( d ) { return d ; } ;
nv . strip = function ( s ) { return s . replace ( /(\s|&)/g , '' ) ; } ;
function daysInMonth ( month , year ) {
return ( new Date ( year , month + 1 , 0 ) ) . getDate ( ) ;
}
function d3 _time _range ( floor , step , number ) {
return function ( t0 , t1 , dt ) {
var time = floor ( t0 ) , times = [ ] ;
if ( time < t0 ) step ( time ) ;
if ( dt > 1 ) {
while ( time < t1 ) {
var date = new Date ( + time ) ;
if ( ( number ( date ) % dt === 0 ) ) times . push ( date ) ;
step ( time ) ;
}
} else {
while ( time < t1 ) { times . push ( new Date ( + time ) ) ; step ( time ) ; }
}
return times ;
} ;
}
d3 . time . monthEnd = function ( date ) {
return new Date ( date . getFullYear ( ) , date . getMonth ( ) , 0 ) ;
} ;
d3 . time . monthEnds = d3 _time _range ( d3 . time . monthEnd , function ( date ) {
date . setUTCDate ( date . getUTCDate ( ) + 1 ) ;
date . setDate ( daysInMonth ( date . getMonth ( ) + 1 , date . getFullYear ( ) ) ) ;
} , function ( date ) {
return date . getMonth ( ) ;
}
) ;
/ * * * * *
* A no frills tooltip implementation .
* * * * * /
( function ( ) {
var nvtooltip = window . nv . tooltip = { } ;
nvtooltip . show = function ( pos , content , gravity , dist , parentContainer , classes ) {
var container = document . createElement ( 'div' ) ;
container . className = 'nvtooltip ' + ( classes ? classes : 'xy-tooltip' ) ;
gravity = gravity || 's' ;
dist = dist || 20 ;
var body = parentContainer ? parentContainer : document . getElementsByTagName ( 'body' ) [ 0 ] ;
container . innerHTML = content ;
container . style . left = 0 ;
container . style . top = 0 ;
container . style . opacity = 0 ;
body . appendChild ( container ) ;
var height = parseInt ( container . offsetHeight ) ,
width = parseInt ( container . offsetWidth ) ,
windowWidth = nv . utils . windowSize ( ) . width ,
windowHeight = nv . utils . windowSize ( ) . 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 . style . left = left + 'px' ;
container . style . top = top + 'px' ;
container . style . opacity = 1 ;
container . style . position = 'absolute' ; //fix scroll bar issue
container . style . pointerEvents = 'none' ; //fix scroll bar issue
return container ;
} ;
nvtooltip . cleanup = function ( ) {
// Find the tooltips, mark them for removal by this class (so others cleanups won't find it)
var tooltips = document . getElementsByClassName ( 'nvtooltip' ) ;
var purging = [ ] ;
while ( tooltips . length ) {
purging . push ( tooltips [ 0 ] ) ;
tooltips [ 0 ] . style . transitionDelay = '0 !important' ;
tooltips [ 0 ] . style . opacity = 0 ;
tooltips [ 0 ] . className = 'nvtooltip-pending-removal' ;
}
setTimeout ( function ( ) {
while ( purging . length ) {
var removeMe = purging . pop ( ) ;
removeMe . parentNode . removeChild ( removeMe ) ;
}
} , 500 ) ;
} ;
} ) ( ) ;
nv . models . axis = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 60 //only used for tickLabel currently
, height = 60 //only used for tickLabel currently
, scale = d3 . scale . linear ( )
, axisLabelText = null
, showMaxMin = true //TODO: showMaxMin should be disabled on all ordinal scaled axes
, highlightZero = true
, rotateLabels = 0
, rotateYLabel = true
, ticks = null
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var axis = d3 . svg . axis ( )
. scale ( scale )
. orient ( 'bottom' )
, scale0 ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ;
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-axis' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-axis' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' )
//------------------------------------------------------------
if ( ticks !== null )
axis . ticks ( ticks ) ;
else if ( axis . orient ( ) == 'top' || axis . orient ( ) == 'bottom' )
axis . ticks ( Math . abs ( scale . range ( ) [ 1 ] - scale . range ( ) [ 0 ] ) / 100 ) ;
//TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component
d3 . transition ( g )
. call ( axis ) ;
scale0 = scale0 || axis . scale ( ) ;
var fmt = axis . tickFormat ( ) ;
if ( fmt == null ) {
fmt = scale0 . tickFormat ( ) ;
}
var axisLabel = g . selectAll ( 'text.nv-axislabel' )
. data ( [ axisLabelText || null ] ) ;
axisLabel . exit ( ) . remove ( ) ;
switch ( axis . orient ( ) ) {
case 'top' :
axisLabel . enter ( ) . append ( 'text' ) . attr ( 'class' , 'nv-axislabel' )
. attr ( 'text-anchor' , 'middle' )
. attr ( 'y' , 0 ) ;
var w = ( scale . range ( ) . length == 2 ) ? scale . range ( ) [ 1 ] : ( scale . range ( ) [ scale . range ( ) . length - 1 ] + ( scale . range ( ) [ 1 ] - scale . range ( ) [ 0 ] ) ) ;
axisLabel
. attr ( 'x' , w / 2 ) ;
if ( showMaxMin ) {
var axisMaxMin = wrap . selectAll ( 'g.nv-axisMaxMin' )
. data ( scale . domain ( ) ) ;
axisMaxMin . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nv-axisMaxMin' ) . append ( 'text' ) ;
axisMaxMin . exit ( ) . remove ( ) ;
axisMaxMin
. attr ( 'transform' , function ( d , i ) {
return 'translate(' + scale ( d ) + ',0)'
} )
. select ( 'text' )
. attr ( 'dy' , '0em' )
. attr ( 'y' , - axis . tickPadding ( ) )
. attr ( 'text-anchor' , 'middle' )
. text ( function ( d , i ) {
var v = fmt ( d ) ;
return ( '' + v ) . match ( 'NaN' ) ? '' : v ;
} ) ;
d3 . transition ( axisMaxMin )
. attr ( 'transform' , function ( d , i ) {
return 'translate(' + scale . range ( ) [ i ] + ',0)'
} ) ;
}
break ;
case 'bottom' :
var xLabelMargin = 30 ;
var maxTextWidth = 30 ;
if ( rotateLabels % 360 ) {
var xTicks = g . selectAll ( 'g' ) . select ( "text" ) ;
//Calculate the longest xTick width
xTicks . each ( function ( d , i ) {
var width = this . getBBox ( ) . width ;
if ( width > maxTextWidth ) maxTextWidth = width ;
} ) ;
//Convert to radians before calculating sin. Add 30 to margin for healthy padding.
var sin = Math . abs ( Math . sin ( rotateLabels * Math . PI / 180 ) ) ;
var xLabelMargin = ( sin ? sin * maxTextWidth : maxTextWidth ) + 30 ;
//Rotate all xTicks
xTicks . attr ( 'transform' , function ( d , i , j ) { return 'rotate(' + rotateLabels + ' 0,0)' } )
. attr ( 'text-anchor' , rotateLabels % 360 > 0 ? 'start' : 'end' ) ;
}
axisLabel . enter ( ) . append ( 'text' ) . attr ( 'class' , 'nv-axislabel' )
. attr ( 'text-anchor' , 'middle' )
. attr ( 'y' , xLabelMargin ) ;
var w = ( scale . range ( ) . length == 2 ) ? scale . range ( ) [ 1 ] : ( scale . range ( ) [ scale . range ( ) . length - 1 ] + ( scale . range ( ) [ 1 ] - scale . range ( ) [ 0 ] ) ) ;
axisLabel
. attr ( 'x' , w / 2 ) ;
if ( showMaxMin ) {
var axisMaxMin = wrap . selectAll ( 'g.nv-axisMaxMin' )
. data ( scale . domain ( ) ) ;
axisMaxMin . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nv-axisMaxMin' ) . append ( 'text' ) ;
axisMaxMin . exit ( ) . remove ( ) ;
axisMaxMin
. attr ( 'transform' , function ( d , i ) {
return 'translate(' + scale ( d ) + ',0)'
} )
. select ( 'text' )
. attr ( 'dy' , '.71em' )
. attr ( 'y' , axis . tickPadding ( ) )
. attr ( 'transform' , function ( d , i , j ) { return 'rotate(' + rotateLabels + ' 0,0)' } )
. attr ( 'text-anchor' , rotateLabels % 360 > 0 ? 'start' : 'end' )
. text ( function ( d , i ) {
var v = fmt ( d ) ;
return ( '' + v ) . match ( 'NaN' ) ? '' : v ;
} ) ;
d3 . transition ( axisMaxMin )
. attr ( 'transform' , function ( d , i ) {
return 'translate(' + scale . range ( ) [ i ] + ',0)'
} ) ;
}
break ;
case 'right' :
axisLabel . enter ( ) . append ( 'text' ) . attr ( 'class' , 'nv-axislabel' )
. attr ( 'text-anchor' , rotateYLabel ? 'middle' : 'begin' )
. attr ( 'transform' , rotateYLabel ? 'rotate(90)' : '' )
. attr ( 'y' , rotateYLabel ? ( - Math . max ( margin . right , width ) - 12 ) : - 10 ) ; //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
axisLabel
. attr ( 'x' , rotateYLabel ? ( scale . range ( ) [ 0 ] / 2 ) : axis . tickPadding ( ) ) ;
if ( showMaxMin ) {
var axisMaxMin = wrap . selectAll ( 'g.nv-axisMaxMin' )
. data ( scale . domain ( ) ) ;
axisMaxMin . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nv-axisMaxMin' ) . append ( 'text' )
. style ( 'opacity' , 0 ) ;
axisMaxMin . exit ( ) . remove ( ) ;
axisMaxMin
. attr ( 'transform' , function ( d , i ) {
return 'translate(0,' + scale ( d ) + ')'
} )
. select ( 'text' )
. attr ( 'dy' , '.32em' )
. attr ( 'y' , 0 )
. attr ( 'x' , axis . tickPadding ( ) )
. attr ( 'text-anchor' , 'start' )
. text ( function ( d , i ) {
var v = fmt ( d ) ;
return ( '' + v ) . match ( 'NaN' ) ? '' : v ;
} ) ;
d3 . transition ( axisMaxMin )
. attr ( 'transform' , function ( d , i ) {
return 'translate(0,' + scale . range ( ) [ i ] + ')'
} )
. select ( 'text' )
. style ( 'opacity' , 1 ) ;
}
break ;
case 'left' :
axisLabel . enter ( ) . append ( 'text' ) . attr ( 'class' , 'nv-axislabel' )
. attr ( 'text-anchor' , rotateYLabel ? 'middle' : 'end' )
. attr ( 'transform' , rotateYLabel ? 'rotate(-90)' : '' )
. attr ( 'y' , rotateYLabel ? ( - Math . max ( margin . left , width ) + 12 ) : - 10 ) ; //TODO: consider calculating this based on largest tick width... OR at least expose this on chart
axisLabel
. attr ( 'x' , rotateYLabel ? ( - scale . range ( ) [ 0 ] / 2 ) : - axis . tickPadding ( ) ) ;
if ( showMaxMin ) {
var axisMaxMin = wrap . selectAll ( 'g.nv-axisMaxMin' )
. data ( scale . domain ( ) ) ;
axisMaxMin . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nv-axisMaxMin' ) . append ( 'text' )
. style ( 'opacity' , 0 ) ;
axisMaxMin . exit ( ) . remove ( ) ;
axisMaxMin
. attr ( 'transform' , function ( d , i ) {
return 'translate(0,' + scale0 ( d ) + ')'
} )
. select ( 'text' )
. attr ( 'dy' , '.32em' )
. attr ( 'y' , 0 )
. attr ( 'x' , - axis . tickPadding ( ) )
. attr ( 'text-anchor' , 'end' )
. text ( function ( d , i ) {
var v = fmt ( d ) ;
return ( '' + v ) . match ( 'NaN' ) ? '' : v ;
} ) ;
d3 . transition ( axisMaxMin )
. attr ( 'transform' , function ( d , i ) {
return 'translate(0,' + scale . range ( ) [ i ] + ')'
} )
. select ( 'text' )
. style ( 'opacity' , 1 ) ;
}
break ;
}
axisLabel
. text ( function ( d ) { return d } ) ;
//check if max and min overlap other values, if so, hide the values that overlap
if ( showMaxMin && ( axis . orient ( ) === 'left' || axis . orient ( ) === 'right' ) ) {
g . selectAll ( 'g' ) // the g's wrapping each tick
. each ( function ( d , i ) {
if ( scale ( d ) < scale . range ( ) [ 1 ] + 10 || scale ( d ) > scale . range ( ) [ 0 ] - 10 ) { // 10 is assuming text height is 16... if d is 0, leave it!
if ( d > 1e-10 || d < - 1e-10 ) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
d3 . select ( this ) . remove ( ) ;
else
d3 . select ( this ) . select ( 'text' ) . remove ( ) ; // Don't remove the ZERO line!!
}
} ) ;
}
if ( showMaxMin && ( axis . orient ( ) === 'top' || axis . orient ( ) === 'bottom' ) ) {
var maxMinRange = [ ] ;
wrap . selectAll ( 'g.nv-axisMaxMin' )
. each ( function ( d , i ) {
if ( i ) // i== 1, max position
maxMinRange . push ( scale ( d ) - this . getBBox ( ) . width - 4 ) //assuming the max and min labels are as wide as the next tick (with an extra 4 pixels just in case)
else // i==0, min position
maxMinRange . push ( scale ( d ) + this . getBBox ( ) . width + 4 )
} ) ;
g . selectAll ( 'g' ) // the g's wrapping each tick
. each ( function ( d , i ) {
if ( scale ( d ) < maxMinRange [ 0 ] || scale ( d ) > maxMinRange [ 1 ] ) {
if ( d > 1e-10 || d < - 1e-10 ) // accounts for minor floating point errors... though could be problematic if the scale is EXTREMELY SMALL
d3 . select ( this ) . remove ( ) ;
else
d3 . select ( this ) . select ( 'text' ) . remove ( ) ; // Don't remove the ZERO line!!
}
} ) ;
}
//highlight zero line ... Maybe should not be an option and should just be in CSS?
if ( highlightZero )
g . selectAll ( 'line.tick' )
. filter ( function ( d ) { return ! parseFloat ( Math . round ( d * 100000 ) / 1000000 ) } ) //this is because sometimes the 0 tick is a very small fraction, TODO: think of cleaner technique
. classed ( 'zero' , true ) ;
//store old scales for use in transitions on update
scale0 = scale . copy ( ) ;
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
d3 . rebind ( chart , axis , 'orient' , 'tickValues' , 'tickSubdivide' , 'tickSize' , 'tickPadding' , 'tickFormat' ) ;
d3 . rebind ( chart , scale , 'domain' , 'range' , 'rangeBand' , 'rangeBands' ) ; //these are also accessible by chart.scale(), but added common ones directly for ease of use
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
return chart ;
}
chart . width = function ( _ ) {
if ( ! arguments . length ) return width ;
width = _ ;
return chart ;
} ;
chart . ticks = function ( _ ) {
if ( ! arguments . length ) return ticks ;
ticks = _ ;
return chart ;
} ;
chart . height = function ( _ ) {
if ( ! arguments . length ) return height ;
height = _ ;
return chart ;
} ;
chart . axisLabel = function ( _ ) {
if ( ! arguments . length ) return axisLabelText ;
axisLabelText = _ ;
return chart ;
}
chart . showMaxMin = function ( _ ) {
if ( ! arguments . length ) return showMaxMin ;
showMaxMin = _ ;
return chart ;
}
chart . highlightZero = function ( _ ) {
if ( ! arguments . length ) return highlightZero ;
highlightZero = _ ;
return chart ;
}
chart . scale = function ( _ ) {
if ( ! arguments . length ) return scale ;
scale = _ ;
axis . scale ( scale ) ;
d3 . rebind ( chart , scale , 'domain' , 'range' , 'rangeBand' , 'rangeBands' ) ;
return chart ;
}
chart . rotateYLabel = function ( _ ) {
if ( ! arguments . length ) return rotateYLabel ;
rotateYLabel = _ ;
return chart ;
}
chart . rotateLabels = function ( _ ) {
if ( ! arguments . length ) return rotateLabels ;
rotateLabels = _ ;
return chart ;
}
//============================================================
return chart ;
}
// Chart design based on the recommendations of Stephen Few. Implementation
// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
// http://projects.instantcognition.com/protovis/bulletchart/
nv . models . bulletChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var bullet = nv . models . bullet ( )
;
var orient = 'left' // TODO top & bottom
, reverse = false
, margin = { top : 5 , right : 40 , bottom : 20 , left : 120 }
, ranges = function ( d ) { return d . ranges }
, markers = function ( d ) { return d . markers }
, measures = function ( d ) { return d . measures }
, width = null
, height = 55
, tickFormat = null
, tooltips = true
, tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + e . label + '</h3>' +
'<p>' + e . value + '</p>'
}
, noData = "No Data Available."
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function ( e , offsetElement ) {
var offsetElement = document . getElementById ( "chart" ) ,
left = e . pos [ 0 ] + offsetElement . offsetLeft + margin . left ,
top = e . pos [ 1 ] + offsetElement . offsetTop + margin . top ;
var content = '<h3>' + e . label + '</h3>' +
'<p>' + e . value + '</p>' ;
nv . tooltip . show ( [ left , top ] , content , e . value < 0 ? 'e' : 'w' , null , offsetElement ) ;
} ;
//============================================================
function chart ( selection ) {
selection . each ( function ( d , i ) {
var container = d3 . select ( this ) ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
that = this ;
chart . update = function ( ) { chart ( selection ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
/ *
// Disabled until I figure out a better way to check for no data with the bullet chart
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
* /
//------------------------------------------------------------
var rangez = ranges . call ( this , d , i ) . slice ( ) . sort ( d3 . descending ) ,
markerz = markers . call ( this , d , i ) . slice ( ) . sort ( d3 . descending ) ,
measurez = measures . call ( this , d , i ) . slice ( ) . sort ( d3 . descending ) ;
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-bulletChart' ) . data ( [ d ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-bulletChart' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-bulletWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-titles' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
// Compute the new x-scale.
var x1 = d3 . scale . linear ( )
. domain ( [ 0 , Math . max ( rangez [ 0 ] , markerz [ 0 ] , measurez [ 0 ] ) ] ) // TODO: need to allow forceX and forceY, and xDomain, yDomain
. range ( reverse ? [ availableWidth , 0 ] : [ 0 , availableWidth ] ) ;
// Retrieve the old x-scale, if this is an update.
var x0 = this . _ _chart _ _ || d3 . scale . linear ( )
. domain ( [ 0 , Infinity ] )
. range ( x1 . range ( ) ) ;
// Stash the new scale.
this . _ _chart _ _ = x1 ;
/ *
// Derive width-scales from the x-scales.
var w0 = bulletWidth ( x0 ) ,
w1 = bulletWidth ( x1 ) ;
function bulletWidth ( x ) {
var x0 = x ( 0 ) ;
return function ( d ) {
return Math . abs ( x ( d ) - x ( 0 ) ) ;
} ;
}
function bulletTranslate ( x ) {
return function ( d ) {
return 'translate(' + x ( d ) + ',0)' ;
} ;
}
* /
var w0 = function ( d ) { return Math . abs ( x0 ( d ) - x0 ( 0 ) ) } , // TODO: could optimize by precalculating x0(0) and x1(0)
w1 = function ( d ) { return Math . abs ( x1 ( d ) - x1 ( 0 ) ) } ;
var title = gEnter . select ( '.nv-titles' ) . append ( "g" )
. attr ( "text-anchor" , "end" )
. attr ( "transform" , "translate(-6," + ( height - margin . top - margin . bottom ) / 2 + ")" ) ;
title . append ( "text" )
. attr ( "class" , "nv-title" )
. text ( function ( d ) { return d . title ; } ) ;
title . append ( "text" )
. attr ( "class" , "nv-subtitle" )
. attr ( "dy" , "1em" )
. text ( function ( d ) { return d . subtitle ; } ) ;
bullet
. width ( availableWidth )
. height ( availableHeight )
var bulletWrap = g . select ( '.nv-bulletWrap' ) ;
d3 . transition ( bulletWrap ) . call ( bullet ) ;
// Compute the tick format.
var format = tickFormat || x1 . tickFormat ( 8 ) ;
// Update the tick groups.
var tick = g . selectAll ( 'g.nv-tick' )
. data ( x1 . ticks ( 8 ) , function ( d ) {
return this . textContent || format ( d ) ;
} ) ;
// Initialize the ticks with the old scale, x0.
var tickEnter = tick . enter ( ) . append ( 'g' )
. attr ( 'class' , 'nv-tick' )
. attr ( 'transform' , function ( d ) { return 'translate(' + x0 ( d ) + ',0)' } )
. style ( 'opacity' , 1e-6 ) ;
tickEnter . append ( 'line' )
. attr ( 'y1' , availableHeight )
. attr ( 'y2' , availableHeight * 7 / 6 ) ;
tickEnter . append ( 'text' )
. attr ( 'text-anchor' , 'middle' )
. attr ( 'dy' , '1em' )
. attr ( 'y' , availableHeight * 7 / 6 )
. text ( format ) ;
// Transition the entering ticks to the new scale, x1.
d3 . transition ( tickEnter )
. attr ( 'transform' , function ( d ) { return 'translate(' + x1 ( d ) + ',0)' } )
. style ( 'opacity' , 1 ) ;
// Transition the updating ticks to the new scale, x1.
var tickUpdate = d3 . transition ( tick )
. attr ( 'transform' , function ( d ) { return 'translate(' + x1 ( d ) + ',0)' } )
. style ( 'opacity' , 1 ) ;
tickUpdate . select ( 'line' )
. attr ( 'y1' , availableHeight )
. attr ( 'y2' , availableHeight * 7 / 6 ) ;
tickUpdate . select ( 'text' )
. attr ( 'y' , availableHeight * 7 / 6 ) ;
// Transition the exiting ticks to the new scale, x1.
d3 . transition ( tick . exit ( ) )
. attr ( 'transform' , function ( d ) { return 'translate(' + x1 ( d ) + ',0)' } )
. style ( 'opacity' , 1e-6 )
. remove ( ) ;
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode ) ;
} ) ;
//============================================================
} ) ;
d3 . timer . flush ( ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
bullet . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
dispatch . tooltipShow ( e ) ;
} ) ;
bullet . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
chart . dispatch = dispatch ;
chart . bullet = bullet ;
// left, right, top, bottom
chart . orient = function ( x ) {
if ( ! arguments . length ) return orient ;
orient = x ;
reverse = orient == 'right' || orient == 'bottom' ;
return chart ;
} ;
// ranges (bad, satisfactory, good)
chart . ranges = function ( x ) {
if ( ! arguments . length ) return ranges ;
ranges = x ;
return chart ;
} ;
// markers (previous, goal)
chart . markers = function ( x ) {
if ( ! arguments . length ) return markers ;
markers = x ;
return chart ;
} ;
// measures (actual, forecast)
chart . measures = function ( x ) {
if ( ! arguments . length ) return measures ;
measures = x ;
return chart ;
} ;
chart . width = function ( x ) {
if ( ! arguments . length ) return width ;
width = x ;
return chart ;
} ;
chart . height = function ( x ) {
if ( ! arguments . length ) return height ;
height = x ;
return chart ;
} ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
return chart ;
} ;
chart . tickFormat = function ( x ) {
if ( ! arguments . length ) return tickFormat ;
tickFormat = x ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
return chart ;
} ;
// Chart design based on the recommendations of Stephen Few. Implementation
// based on the work of Clint Ivy, Jamie Love, and Jason Davies.
// http://projects.instantcognition.com/protovis/bulletchart/
nv . models . bullet = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, orient = 'left' // TODO top & bottom
, reverse = false
, ranges = function ( d ) { return d . ranges }
, markers = function ( d ) { return d . markers }
, measures = function ( d ) { return d . measures }
, forceX = [ 0 ] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
, width = 380
, height = 30
, tickFormat = null
, dispatch = d3 . dispatch ( 'elementMouseover' , 'elementMouseout' )
;
//============================================================
function chart ( selection ) {
selection . each ( function ( d , i ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
var rangez = ranges . call ( this , d , i ) . slice ( ) . sort ( d3 . descending ) ,
markerz = markers . call ( this , d , i ) . slice ( ) . sort ( d3 . descending ) ,
measurez = measures . call ( this , d , i ) . slice ( ) . sort ( d3 . descending ) ;
nv . log ( rangez , markerz , measurez ) ;
//------------------------------------------------------------
// Setup Scales
// Compute the new x-scale.
var x1 = d3 . scale . linear ( )
. domain ( [ 0 , Math . max ( rangez [ 0 ] , markerz [ 0 ] , measurez [ 0 ] ) ] ) // TODO: need to allow forceX and forceY, and xDomain, yDomain
. range ( reverse ? [ availableWidth , 0 ] : [ 0 , availableWidth ] ) ;
// Retrieve the old x-scale, if this is an update.
var x0 = this . _ _chart _ _ || d3 . scale . linear ( )
. domain ( [ 0 , Infinity ] )
. range ( x1 . range ( ) ) ;
// Stash the new scale.
this . _ _chart _ _ = x1 ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-bullet' ) . data ( [ d ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-bullet' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
var w0 = function ( d ) { return Math . abs ( x0 ( d ) - x0 ( 0 ) ) } , // TODO: could optimize by precalculating x0(0) and x1(0)
w1 = function ( d ) { return Math . abs ( x1 ( d ) - x1 ( 0 ) ) } ;
// Update the range rects.
var range = g . selectAll ( 'rect.nv-range' )
. data ( rangez ) ;
range . enter ( ) . append ( 'rect' )
. attr ( 'class' , function ( d , i ) { return 'nv-range nv-s' + i ; } )
. attr ( 'width' , w0 )
. attr ( 'height' , availableHeight )
. attr ( 'x' , reverse ? x0 : 0 )
. on ( 'mouseover' , function ( d , i ) {
dispatch . elementMouseover ( {
value : d ,
label : ( i <= 0 ) ? 'Maximum' : ( i > 1 ) ? 'Minimum' : 'Mean' , //TODO: make these labels a variable
pos : [ x1 ( d ) , availableHeight / 2 ]
} )
} )
. on ( 'mouseout' , function ( d , i ) {
dispatch . elementMouseout ( {
value : d ,
label : ( i <= 0 ) ? 'Minimum' : ( i >= 1 ) ? 'Maximum' : 'Mean' //TODO: make these labels a variable
} )
} )
d3 . transition ( range )
. attr ( 'x' , reverse ? x1 : 0 )
. attr ( 'width' , w1 )
. attr ( 'height' , availableHeight ) ;
// Update the measure rects.
var measure = g . selectAll ( 'rect.nv-measure' )
. data ( measurez ) ;
measure . enter ( ) . append ( 'rect' )
. attr ( 'class' , function ( d , i ) { return 'nv-measure nv-s' + i ; } )
. attr ( 'width' , w0 )
. attr ( 'height' , availableHeight / 3 )
. attr ( 'x' , reverse ? x0 : 0 )
. attr ( 'y' , availableHeight / 3 )
. on ( 'mouseover' , function ( d ) {
dispatch . elementMouseover ( {
value : d ,
label : 'Current' , //TODO: make these labels a variable
pos : [ x1 ( d ) , availableHeight / 2 ]
} )
} )
. on ( 'mouseout' , function ( d ) {
dispatch . elementMouseout ( {
value : d ,
label : 'Current' //TODO: make these labels a variable
} )
} )
d3 . transition ( measure )
. attr ( 'width' , w1 )
. attr ( 'height' , availableHeight / 3 )
. attr ( 'x' , reverse ? x1 : 0 )
. attr ( 'y' , availableHeight / 3 ) ;
// Update the marker lines.
var marker = g . selectAll ( 'path.nv-markerTriangle' )
. data ( markerz ) ;
var h3 = availableHeight / 6 ;
marker . enter ( ) . append ( 'path' )
. attr ( 'class' , 'nv-markerTriangle' )
. attr ( 'transform' , function ( d ) { return 'translate(' + x0 ( d ) + ',' + ( availableHeight / 2 ) + ')' } )
. attr ( 'd' , 'M0,' + h3 + 'L' + h3 + ',' + ( - h3 ) + ' ' + ( - h3 ) + ',' + ( - h3 ) + 'Z' )
. on ( 'mouseover' , function ( d , i ) {
dispatch . elementMouseover ( {
value : d ,
label : 'Previous' ,
pos : [ x1 ( d ) , availableHeight / 2 ]
} )
} )
. on ( 'mouseout' , function ( d , i ) {
dispatch . elementMouseout ( {
value : d ,
label : 'Previous'
} )
} ) ;
d3 . transition ( marker )
. attr ( 'transform' , function ( d ) { return 'translate(' + x1 ( d ) + ',' + ( availableHeight / 2 ) + ')' } ) ;
marker . exit ( ) . remove ( ) ;
} ) ;
d3 . timer . flush ( ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
chart . dispatch = dispatch ;
// left, right, top, bottom
chart . orient = function ( _ ) {
if ( ! arguments . length ) return orient ;
orient = _ ;
reverse = orient == 'right' || orient == 'bottom' ;
return chart ;
} ;
// ranges (bad, satisfactory, good)
chart . ranges = function ( _ ) {
if ( ! arguments . length ) return ranges ;
ranges = _ ;
return chart ;
} ;
// markers (previous, goal)
chart . markers = function ( _ ) {
if ( ! arguments . length ) return markers ;
markers = _ ;
return chart ;
} ;
// measures (actual, forecast)
chart . measures = function ( _ ) {
if ( ! arguments . length ) return measures ;
measures = _ ;
return chart ;
} ;
chart . forceX = function ( _ ) {
if ( ! arguments . length ) return forceX ;
forceX = _ ;
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 . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
return chart ;
} ;
chart . tickFormat = function ( _ ) {
if ( ! arguments . length ) return tickFormat ;
tickFormat = _ ;
return chart ;
} ;
//============================================================
return chart ;
} ;
nv . models . cumulativeLineChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var lines = nv . models . line ( )
, xAxis = nv . models . axis ( )
, yAxis = nv . models . axis ( )
, legend = nv . models . legend ( )
, controls = nv . models . legend ( )
;
var margin = { top : 30 , right : 30 , bottom : 50 , left : 60 }
, color = nv . utils . defaultColor ( )
, width = null
, height = null
, showLegend = true
, tooltips = true
, showControls = true
, rescaleY = true
, tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' at ' + x + '</p>'
}
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
, id = lines . id ( )
, noData = 'No Data Available.'
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
;
xAxis
. orient ( 'bottom' )
. tickPadding ( 5 )
;
yAxis
. orient ( 'left' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var dx = d3 . scale . linear ( )
, index = { i : 0 , x : 0 }
;
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( lines . x ( ) ( e . point , e . pointIndex ) ) ,
y = yAxis . tickFormat ( ) ( lines . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , null , null , offsetElement ) ;
} ;
var indexDrag = d3 . behavior . drag ( )
. on ( 'dragstart' , dragStart )
. on ( 'drag' , dragMove )
. on ( 'dragend' , dragEnd ) ;
function dragStart ( d , i ) { }
function dragMove ( d , i ) {
d . x += d3 . event . dx ;
d . i = Math . round ( dx . invert ( d . x ) ) ;
d3 . select ( this ) . attr ( 'transform' , 'translate(' + dx ( d . i ) + ',0)' ) ;
}
function dragEnd ( d , i ) {
chart . update ( ) ;
}
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) . classed ( 'nv-chart-' + id , true ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { chart ( selection ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = lines . xScale ( ) ;
y = lines . yScale ( ) ;
if ( ! rescaleY ) {
var seriesDomains = data
. filter ( function ( series ) { return ! series . disabled } )
. map ( function ( series , i ) {
var initialDomain = d3 . extent ( series . values , lines . y ( ) ) ;
return [
( initialDomain [ 0 ] - initialDomain [ 1 ] ) / ( 1 + initialDomain [ 1 ] ) ,
( initialDomain [ 1 ] - initialDomain [ 0 ] ) / ( 1 + initialDomain [ 0 ] )
] ;
} ) ;
var completeDomain = [
d3 . min ( seriesDomains , function ( d ) { return d [ 0 ] } ) ,
d3 . max ( seriesDomains , function ( d ) { return d [ 1 ] } )
]
lines . yDomain ( completeDomain ) ;
} else {
lines . yDomain ( null ) ;
}
dx . domain ( [ 0 , data [ 0 ] . values . length - 1 ] ) //Assumes all series have same length
. range ( [ 0 , availableWidth ] )
. clamp ( true ) ;
//------------------------------------------------------------
var data = indexify ( index . i , data ) ;
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-cumulativeLine' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-cumulativeLine' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-linesWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-controlsWrap' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Legend
if ( showLegend ) {
legend . width ( availableWidth ) ;
g . select ( '.nv-legendWrap' )
. datum ( data )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
g . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
}
//------------------------------------------------------------
//------------------------------------------------------------
// Controls
if ( showControls ) {
var controlsData = [
{ key : 'Re-scale y-axis' , disabled : ! rescaleY }
] ;
controls . width ( 140 ) . color ( [ '#444' , '#444' , '#444' ] ) ;
g . select ( '.nv-controlsWrap' )
. datum ( controlsData )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
. call ( controls ) ;
}
//------------------------------------------------------------
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
// Main Chart Component(s)
lines
//.x(function(d) { return d.x })
. y ( function ( d ) { return d . display . y } )
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) ) ;
var linesWrap = g . select ( '.nv-linesWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( linesWrap ) . call ( lines ) ;
var indexLine = linesWrap . selectAll ( '.nv-indexLine' )
. data ( [ index ] ) ;
indexLine . enter ( ) . append ( 'rect' ) . attr ( 'class' , 'nv-indexLine' )
. attr ( 'width' , 3 )
. attr ( 'x' , - 2 )
. attr ( 'fill' , 'red' )
. attr ( 'fill-opacity' , . 5 )
. call ( indexDrag )
indexLine
. attr ( 'transform' , function ( d ) { return 'translate(' + dx ( d . i ) + ',0)' } )
. attr ( 'height' , availableHeight )
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Axes
xAxis
. scale ( x )
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( g . select ( '.nv-x.nv-axis' ) )
. call ( xAxis ) ;
yAxis
. scale ( y )
. ticks ( availableHeight / 36 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.nv-y.nv-axis' ) )
. call ( yAxis ) ;
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
controls . dispatch . on ( 'legendClick' , function ( d , i ) {
d . disabled = ! d . disabled ;
rescaleY = ! d . disabled ;
selection . transition ( ) . call ( chart ) ;
} ) ;
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 ( '.nv-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 )
} ) ;
* /
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode ) ;
} ) ;
//============================================================
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
lines . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
lines . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . lines = lines ;
chart . legend = legend ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
d3 . rebind ( chart , lines , 'defined' , 'isArea' , 'x' , 'y' , 'size' , 'xDomain' , 'yDomain' , 'forceX' , 'forceY' , 'interactive' , 'clipEdge' , 'clipVoronoi' , 'id' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
return chart ;
} ;
chart . showControls = function ( _ ) {
if ( ! arguments . length ) return showControls ;
showControls = _ ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
//============================================================
// Functions
//------------------------------------------------------------
/* Normalize the data according to an index point. */
function indexify ( idx , data ) {
return data . map ( function ( line , i ) {
var v = lines . y ( ) ( line . values [ idx ] , idx ) ;
line . values = line . values . map ( function ( point , pointIndex ) {
point . display = { 'y' : ( lines . y ( ) ( point , pointIndex ) - v ) / ( 1 + v ) } ;
return point ;
} )
/ *
TODO : implement check below , and disable series if series loses 100 % or more cause divide by 0 issue
if ( v < - . 9 ) {
//if a series loses more than 100%, calculations fail.. anything close can cause major distortion (but is mathematically currect till it hits 100)
}
* /
return line ;
} )
}
//============================================================
return chart ;
}
nv . models . discreteBarChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var discretebar = nv . models . discreteBar ( )
, xAxis = nv . models . axis ( )
, yAxis = nv . models . axis ( )
;
var margin = { top : 10 , right : 10 , bottom : 50 , left : 60 }
, width = null
, height = null
, color = nv . utils . getColor ( )
, staggerLabels = false
, tooltips = true
, tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + x + '</h3>' +
'<p>' + y + '</p>'
}
, x
, y
, noData = "No Data Available."
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
;
xAxis
. orient ( 'bottom' )
. highlightZero ( false )
. showMaxMin ( false )
. tickFormat ( function ( d ) { return d } )
;
yAxis
. orient ( 'left' )
. tickFormat ( d3 . format ( ',.1f' ) )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( discretebar . x ( ) ( e . point , e . pointIndex ) ) ,
y = yAxis . tickFormat ( ) ( discretebar . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , e . value < 0 ? 'n' : 's' , null , offsetElement ) ;
} ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { selection . transition ( ) . call ( chart ) ; } ;
chart . container = this ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = discretebar . xScale ( ) ;
y = discretebar . yScale ( ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-discreteBarWithAxes' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-discreteBarWithAxes' ) . append ( 'g' ) ;
var defsEnter = gEnter . append ( 'defs' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-barsWrap' ) ;
g . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Main Chart Component(s)
discretebar
. width ( availableWidth )
. height ( availableHeight ) ;
var barsWrap = g . select ( '.nv-barsWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( barsWrap ) . call ( discretebar ) ;
//------------------------------------------------------------
defsEnter . append ( 'clipPath' )
. attr ( 'id' , 'nv-x-label-clip-' + discretebar . id ( ) )
. append ( 'rect' ) ;
g . select ( '#nv-x-label-clip-' + discretebar . id ( ) + ' rect' )
. attr ( 'width' , x . rangeBand ( ) * ( staggerLabels ? 2 : 1 ) )
. attr ( 'height' , 16 )
. attr ( 'x' , - x . rangeBand ( ) / ( staggerLabels ? 1 : 2 ) ) ;
//------------------------------------------------------------
// Setup Axes
xAxis
. scale ( x )
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + ( y . range ( ) [ 0 ] + ( ( discretebar . showValues ( ) && y . domain ( ) [ 0 ] < 0 ) ? 16 : 0 ) ) + ')' ) ;
//d3.transition(g.select('.nv-x.nv-axis'))
g . select ( '.nv-x.nv-axis' ) . transition ( ) . duration ( 0 )
. call ( xAxis ) ;
var xTicks = g . select ( '.nv-x.nv-axis' ) . selectAll ( 'g' ) ;
if ( staggerLabels )
xTicks
. selectAll ( 'text' )
. attr ( 'transform' , function ( d , i , j ) { return 'translate(0,' + ( j % 2 == 0 ? '0' : '12' ) + ')' } )
yAxis
. scale ( y )
. ticks ( availableHeight / 36 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.nv-y.nv-axis' ) )
. call ( yAxis ) ;
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode ) ;
} ) ;
//============================================================
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
discretebar . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
discretebar . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . discretebar = discretebar ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
d3 . rebind ( chart , discretebar , 'x' , 'y' , 'xDomain' , 'yDomain' , 'forceX' , 'forceY' , 'id' , 'showValues' , 'valueFormat' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
discretebar . color ( color ) ;
return chart ;
} ;
chart . staggerLabels = function ( _ ) {
if ( ! arguments . length ) return staggerLabels ;
staggerLabels = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
//TODO: consider deprecating by adding necessary features to multiBar model
nv . models . discreteBar = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 960
, height = 500
, id = Math . floor ( Math . random ( ) * 10000 ) //Create semi-unique ID in case user doesn't select one
, x = d3 . scale . ordinal ( )
, y = d3 . scale . linear ( )
, getX = function ( d ) { return d . x }
, getY = function ( d ) { return d . y }
, forceY = [ 0 ] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
, color = nv . utils . defaultColor ( )
, showValues = false
, valueFormat = d3 . format ( ',.2f' )
, xDomain
, yDomain
, dispatch = d3 . dispatch ( 'chartClick' , 'elementClick' , 'elementDblClick' , 'elementMouseover' , 'elementMouseout' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var x0 , y0 ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
//add series index to each data point for reference
data = data . map ( function ( series , i ) {
series . values = series . values . map ( function ( point ) {
point . series = i ;
return point ;
} ) ;
return series ;
} ) ;
//------------------------------------------------------------
// Setup Scales
// remap and flatten the data for use in calculating the scales' domains
var seriesData = ( xDomain && yDomain ) ? [ ] : // if we know xDomain and yDomain, no need to calculate
data . map ( function ( d ) {
return d . values . map ( function ( d , i ) {
return { x : getX ( d , i ) , y : getY ( d , i ) , y0 : d . y0 }
} )
} ) ;
x . domain ( xDomain || d3 . merge ( seriesData ) . map ( function ( d ) { return d . x } ) )
. rangeBands ( [ 0 , availableWidth ] , . 1 ) ;
y . domain ( yDomain || d3 . extent ( d3 . merge ( seriesData ) . map ( function ( d ) { return d . y } ) . concat ( forceY ) ) ) ;
// If showValues, pad the Y axis range to account for label height
if ( showValues ) y . range ( [ availableHeight - ( y . domain ( ) [ 0 ] < 0 ? 12 : 0 ) , y . domain ( ) [ 1 ] > 0 ? 12 : 0 ] ) ;
else y . range ( [ availableHeight , 0 ] ) ;
//store old scales if they exist
x0 = x0 || x ;
y0 = y0 || y . copy ( ) . range ( [ y ( 0 ) , y ( 0 ) ] ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-discretebar' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-discretebar' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-groups' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
//TODO: by definition, the discrete bar should not have multiple groups, will modify/remove later
var groups = wrap . select ( '.nv-groups' ) . selectAll ( '.nv-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 'nv-group nv-series-' + i } )
. classed ( 'hover' , function ( d ) { return d . hover } ) ;
d3 . transition ( groups )
. style ( 'stroke-opacity' , 1 )
. style ( 'fill-opacity' , . 75 ) ;
var bars = groups . selectAll ( 'g.nv-bar' )
. data ( function ( d ) { return d . values } ) ;
bars . exit ( ) . remove ( ) ;
var barsEnter = bars . enter ( ) . append ( 'g' )
. attr ( 'transform' , function ( d , i , j ) {
return 'translate(' + x ( getX ( d , i ) ) + ', ' + y ( 0 ) + ')'
} )
. on ( 'mouseover' , function ( d , i ) { //TODO: figure out why j works above, but not here
d3 . select ( this ) . classed ( 'hover' , true ) ;
dispatch . elementMouseover ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( x . rangeBand ( ) * ( d . series + . 5 ) / data . length ) , y ( getY ( d , i ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , false ) ;
dispatch . elementMouseout ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
} )
. on ( 'click' , function ( d , i ) {
dispatch . elementClick ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( x . rangeBand ( ) * ( d . series + . 5 ) / data . length ) , y ( getY ( d , i ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
d3 . event . stopPropagation ( ) ;
} )
. on ( 'dblclick' , function ( d , i ) {
dispatch . elementDblClick ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( x . rangeBand ( ) * ( d . series + . 5 ) / data . length ) , y ( getY ( d , i ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
d3 . event . stopPropagation ( ) ;
} ) ;
barsEnter . append ( 'rect' )
. attr ( 'height' , 0 )
. attr ( 'width' , x . rangeBand ( ) / data . length )
if ( showValues ) {
barsEnter . append ( 'text' )
. attr ( 'text-anchor' , 'middle' )
bars . selectAll ( 'text' )
. attr ( 'x' , x . rangeBand ( ) / 2 )
. attr ( 'y' , function ( d , i ) { return getY ( d , i ) < 0 ? y ( getY ( d , i ) ) - y ( 0 ) + 12 : - 4 } )
. text ( function ( d , i ) { return valueFormat ( getY ( d , i ) ) } ) ;
} else {
bars . selectAll ( 'text' ) . remove ( ) ;
}
bars
. attr ( 'class' , function ( d , i ) { return getY ( d , i ) < 0 ? 'nv-bar negative' : 'nv-bar positive' } )
. style ( 'fill' , function ( d , i ) { return d . color || color ( d , i ) } )
. style ( 'stroke' , function ( d , i ) { return d . color || color ( d , i ) } )
//.attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
. attr ( 'transform' , function ( d , i ) {
return 'translate(' + x ( getX ( d , i ) ) + ', ' + ( getY ( d , i ) < 0 ? y0 ( 0 ) : y0 ( getY ( d , i ) ) ) + ')'
} )
. selectAll ( 'rect' )
. attr ( 'width' , x . rangeBand ( ) / data . length ) ;
d3 . transition ( bars )
//.delay(function(d,i) { return i * 1200 / data[0].values.length })
. attr ( 'transform' , function ( d , i ) {
return 'translate(' + x ( getX ( d , i ) ) + ', ' + ( getY ( d , i ) < 0 ? y ( 0 ) : y ( getY ( d , i ) ) ) + ')'
} )
. selectAll ( 'rect' )
. attr ( 'height' , function ( d , i ) {
return Math . abs ( y ( getY ( d , i ) ) - y ( 0 ) )
} ) ;
//store old scales for use in transitions on update
x0 = x . copy ( ) ;
y0 = y . copy ( ) ;
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
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 . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . xScale = function ( _ ) {
if ( ! arguments . length ) return x ;
x = _ ;
return chart ;
} ;
chart . yScale = function ( _ ) {
if ( ! arguments . length ) return y ;
y = _ ;
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 . forceY = function ( _ ) {
if ( ! arguments . length ) return forceY ;
forceY = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
chart . showValues = function ( _ ) {
if ( ! arguments . length ) return showValues ;
showValues = _ ;
return chart ;
} ;
chart . valueFormat = function ( _ ) {
if ( ! arguments . length ) return valueFormat ;
valueFormat = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . distribution = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 400 //technically width or height depending on x or y....
, size = 8
, axis = 'x' // 'x' or 'y'... horizontal or vertical
, getData = function ( d ) { return d [ axis ] } // defaults d.x or d.y
, color = nv . utils . defaultColor ( )
, scale = d3 . scale . linear ( )
, domain
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var scale0 ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableLength = width - ( axis === 'x' ? margin . left + margin . right : margin . top + margin . bottom ) ,
naxis = axis == 'x' ? 'y' : 'x' ,
container = d3 . select ( this ) ;
//------------------------------------------------------------
// Setup Scales
scale0 = scale0 || scale ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-distribution' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-distribution' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' )
//------------------------------------------------------------
var distWrap = g . selectAll ( 'g.nv-dist' )
. data ( function ( d ) { return d } , function ( d ) { return d . key } ) ;
distWrap . enter ( ) . append ( 'g' ) ;
distWrap
. attr ( 'class' , function ( d , i ) { return 'nv-dist nv-series-' + i } )
. style ( 'stroke' , function ( d , i ) { return color ( d , i ) } ) ;
var dist = distWrap . selectAll ( 'line.nv-dist' + axis )
. data ( function ( d ) { return d . values } )
dist . enter ( ) . append ( 'line' )
. attr ( axis + '1' , function ( d , i ) { return scale0 ( getData ( d , i ) ) } )
. attr ( axis + '2' , function ( d , i ) { return scale0 ( getData ( d , i ) ) } )
d3 . transition ( distWrap . exit ( ) . selectAll ( 'line.nv-dist' + axis ) )
. attr ( axis + '1' , function ( d , i ) { return scale ( getData ( d , i ) ) } )
. attr ( axis + '2' , function ( d , i ) { return scale ( getData ( d , i ) ) } )
. style ( 'stroke-opacity' , 0 )
. remove ( ) ;
dist
. attr ( 'class' , function ( d , i ) { return 'nv-dist' + axis + ' nv-dist' + axis + '-' + i } )
. attr ( naxis + '1' , 0 )
. attr ( naxis + '2' , size ) ;
d3 . transition ( dist )
. attr ( axis + '1' , function ( d , i ) { return scale ( getData ( d , i ) ) } )
. attr ( axis + '2' , function ( d , i ) { return scale ( getData ( d , i ) ) } )
scale0 = scale . copy ( ) ;
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
return chart ;
} ;
chart . width = function ( _ ) {
if ( ! arguments . length ) return width ;
width = _ ;
return chart ;
} ;
chart . axis = function ( _ ) {
if ( ! arguments . length ) return axis ;
axis = _ ;
return chart ;
} ;
chart . size = function ( _ ) {
if ( ! arguments . length ) return size ;
size = _ ;
return chart ;
} ;
chart . getData = function ( _ ) {
if ( ! arguments . length ) return getData ;
getData = d3 . functor ( _ ) ;
return chart ;
} ;
chart . scale = function ( _ ) {
if ( ! arguments . length ) return scale ;
scale = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
//============================================================
return chart ;
}
//TODO: consider deprecating and using multibar with single series for this
nv . models . historicalBar = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 960
, height = 500
, id = Math . floor ( Math . random ( ) * 10000 ) //Create semi-unique ID in case user doesn't select one
, x = d3 . scale . linear ( )
, y = d3 . scale . linear ( )
, getX = function ( d ) { return d . x }
, getY = function ( d ) { return d . y }
, forceX = [ ]
, forceY = [ 0 ]
, clipEdge = true
, color = nv . utils . defaultColor ( )
, xDomain
, yDomain
, dispatch = d3 . dispatch ( 'chartClick' , 'elementClick' , 'elementDblClick' , 'elementMouseover' , 'elementMouseout' )
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
//------------------------------------------------------------
// Setup Scales
x . domain ( xDomain || d3 . extent ( data [ 0 ] . values . map ( getX ) . concat ( forceX ) ) )
. range ( [ 0 , availableWidth ] ) ;
y . domain ( yDomain || d3 . extent ( data [ 0 ] . values . map ( getY ) . concat ( forceY ) ) )
. range ( [ availableHeight , 0 ] ) ;
// If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
if ( x . domain ( ) [ 0 ] === x . domain ( ) [ 1 ] || y . domain ( ) [ 0 ] === y . domain ( ) [ 1 ] ) singlePoint = true ;
if ( x . domain ( ) [ 0 ] === x . domain ( ) [ 1 ] )
x . domain ( ) [ 0 ] ?
x . domain ( [ x . domain ( ) [ 0 ] - x . domain ( ) [ 0 ] * 0.01 , x . domain ( ) [ 1 ] + x . domain ( ) [ 1 ] * 0.01 ] )
: x . domain ( [ - 1 , 1 ] ) ;
if ( y . domain ( ) [ 0 ] === y . domain ( ) [ 1 ] )
y . domain ( ) [ 0 ] ?
y . domain ( [ y . domain ( ) [ 0 ] + y . domain ( ) [ 0 ] * 0.01 , y . domain ( ) [ 1 ] - y . domain ( ) [ 1 ] * 0.01 ] )
: y . domain ( [ - 1 , 1 ] ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-bar' ) . data ( [ data [ 0 ] . values ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-bar' ) ;
var defsEnter = wrapEnter . append ( 'defs' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-bars' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
container
. on ( 'click' , function ( d , i ) {
dispatch . chartClick ( {
data : d ,
index : i ,
pos : d3 . event ,
id : id
} ) ;
} ) ;
defsEnter . append ( 'clipPath' )
. attr ( 'id' , 'nv-chart-clip-path-' + id )
. append ( 'rect' ) ;
wrap . select ( '#nv-chart-clip-path-' + id + ' rect' )
. attr ( 'width' , availableWidth )
. attr ( 'height' , availableHeight ) ;
g . attr ( 'clip-path' , clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '' ) ;
var bars = wrap . select ( '.nv-bars' ) . selectAll ( '.nv-bar' )
. data ( function ( d ) { return d } ) ;
bars . exit ( ) . remove ( ) ;
var barsEnter = bars . enter ( ) . append ( 'rect' )
. attr ( 'class' , function ( d , i , j ) { return ( getY ( d , i ) < 0 ? 'nv-bar negative' : 'nv-bar positive' ) + ' nv-bar-' + j + '-' + i } )
. attr ( 'fill' , function ( d , i ) { return color ( d , i ) ; } )
. attr ( 'x' , 0 )
. attr ( 'y' , function ( d , i ) { return y ( Math . max ( 0 , getY ( d , i ) ) ) } )
. attr ( 'height' , function ( d , i ) { return Math . abs ( y ( getY ( d , i ) ) - y ( 0 ) ) } )
. on ( 'mouseover' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , true ) ;
dispatch . elementMouseover ( {
point : d ,
series : data [ 0 ] ,
pos : [ x ( getX ( d , i ) ) , y ( getY ( d , i ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : 0 ,
e : d3 . event
} ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , false ) ;
dispatch . elementMouseout ( {
point : d ,
series : data [ 0 ] ,
pointIndex : i ,
seriesIndex : 0 ,
e : d3 . event
} ) ;
} )
. on ( 'click' , function ( d , i ) {
dispatch . elementClick ( {
//label: d[label],
value : getY ( d , i ) ,
data : d ,
index : i ,
pos : [ x ( getX ( d , i ) ) , y ( getY ( d , i ) ) ] ,
e : d3 . event ,
id : id
} ) ;
d3 . event . stopPropagation ( ) ;
} )
. on ( 'dblclick' , function ( d , i ) {
dispatch . elementDblClick ( {
//label: d[label],
value : getY ( d , i ) ,
data : d ,
index : i ,
pos : [ x ( getX ( d , i ) ) , y ( getY ( d , i ) ) ] ,
e : d3 . event ,
id : id
} ) ;
d3 . event . stopPropagation ( ) ;
} ) ;
bars
. attr ( 'class' , function ( d , i , j ) { return ( getY ( d , i ) < 0 ? 'nv-bar negative' : 'nv-bar positive' ) + ' nv-bar-' + j + '-' + i } )
. attr ( 'transform' , function ( d , i ) { return 'translate(' + ( x ( getX ( d , i ) ) - ( ( availableWidth / data [ 0 ] . values . length ) * . 5 ) ) + ',0)' ; } ) //TODO: better width calculations that don't assume always uniform data spacing;w
. attr ( 'width' , ( availableWidth / data [ 0 ] . values . length ) * . 9 )
d3 . transition ( bars )
. attr ( 'y' , function ( d , i ) { return y ( Math . max ( 0 , getY ( d , i ) ) ) } )
. attr ( 'height' , function ( d , i ) { return Math . abs ( y ( getY ( d , i ) ) - y ( 0 ) ) } ) ;
//.order(); // not sure if this makes any sense for this model
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
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 . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . xScale = function ( _ ) {
if ( ! arguments . length ) return x ;
x = _ ;
return chart ;
} ;
chart . yScale = function ( _ ) {
if ( ! arguments . length ) return y ;
y = _ ;
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 . forceX = function ( _ ) {
if ( ! arguments . length ) return forceX ;
forceX = _ ;
return chart ;
} ;
chart . forceY = function ( _ ) {
if ( ! arguments . length ) return forceY ;
forceY = _ ;
return chart ;
} ;
chart . clipEdge = function ( _ ) {
if ( ! arguments . length ) return clipEdge ;
clipEdge = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . indentedTree = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 } //TODO: implement, maybe as margin on the containing div
, width = 960
, height = 500
, color = nv . utils . defaultColor ( )
, id = Math . floor ( Math . random ( ) * 10000 )
, header = true
, noData = "No Data Available."
, childIndent = 20
, columns = [ { key : 'key' , label : 'Name' , type : 'text' } ] //TODO: consider functions like chart.addColumn, chart.removeColumn, instead of a block like this
, tableClass = null
, iconOpen = 'images/grey-plus.png' //TODO: consider removing this and replacing with a '+' or '-' unless user defines images
, iconClose = 'images/grey-minus.png'
, dispatch = d3 . dispatch ( 'elementClick' , 'elementDblclick' , 'elementMouseover' , 'elementMouseout' )
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var i = 0 ,
depth = 1 ;
var tree = d3 . layout . tree ( )
. children ( function ( d ) { return d . values } )
. size ( [ height , childIndent ] ) ; //Not sure if this is needed now that the result is HTML
chart . update = function ( ) { selection . transition ( ) . call ( chart ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
if ( ! data [ 0 ] . key ) data [ 0 ] . key = noData ;
//------------------------------------------------------------
var nodes = tree . nodes ( data [ 0 ] ) ;
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = d3 . select ( this ) . selectAll ( 'div' ) . data ( [ [ nodes ] ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'div' ) . attr ( 'class' , 'nvd3 nv-wrap nv-indentedtree' ) ;
var tableEnter = wrapEnter . append ( 'table' ) ;
var table = wrap . select ( 'table' ) . attr ( 'width' , '100%' ) . attr ( 'class' , tableClass ) ;
//------------------------------------------------------------
if ( header ) {
var thead = tableEnter . append ( 'thead' ) ;
var theadRow1 = thead . append ( 'tr' ) ;
columns . forEach ( function ( column ) {
theadRow1
. append ( 'th' )
. attr ( 'width' , column . width ? column . width : '10%' )
. style ( 'text-align' , column . type == 'numeric' ? 'right' : 'left' )
. append ( 'span' )
. text ( column . label ) ;
} ) ;
}
var tbody = table . selectAll ( 'tbody' )
. data ( function ( d ) { return d } ) ;
tbody . enter ( ) . append ( 'tbody' ) ;
//compute max generations
depth = d3 . max ( nodes , function ( node ) { return node . depth } ) ;
tree . size ( [ height , depth * childIndent ] ) ; //TODO: see if this is necessary at all
// Update the nodes…
var node = tbody . selectAll ( 'tr' )
. data ( function ( d ) { return d } , function ( d ) { return d . id || ( d . id == ++ i ) } ) ;
//.style('display', 'table-row'); //TODO: see if this does anything
node . exit ( ) . remove ( ) ;
node . select ( 'img.nv-treeicon' )
. attr ( 'src' , icon )
. classed ( 'folded' , folded ) ;
var nodeEnter = node . enter ( ) . append ( 'tr' ) ;
columns . forEach ( function ( column , index ) {
var nodeName = nodeEnter . append ( 'td' )
. style ( 'padding-left' , function ( d ) { return ( index ? 0 : d . depth * childIndent + 12 + ( icon ( d ) ? 0 : 16 ) ) + 'px' } , 'important' ) //TODO: check why I did the ternary here
. style ( 'text-align' , column . type == 'numeric' ? 'right' : 'left' ) ;
if ( index == 0 ) {
nodeName . append ( 'img' )
. classed ( 'nv-treeicon' , true )
. classed ( 'nv-folded' , folded )
. attr ( 'src' , icon )
. style ( 'width' , '14px' )
. style ( 'height' , '14px' )
. style ( 'padding' , '0 1px' )
. style ( 'display' , function ( d ) { return icon ( d ) ? 'inline-block' : 'none' ; } )
. on ( 'click' , click ) ;
}
nodeName . append ( 'span' )
. attr ( 'class' , d3 . functor ( column . classes ) )
. text ( function ( d ) { return column . format ? column . format ( d ) :
( d [ column . key ] || '-' ) } ) ;
if ( column . showCount )
nodeName . append ( 'span' )
. attr ( 'class' , 'nv-childrenCount' )
. text ( function ( d ) {
return ( ( d . values && d . values . length ) || ( d . _values && d . _values . length ) ) ?
'(' + ( ( d . values && d . values . length ) || ( d . _values && d . _values . length ) ) + ')'
: ''
} ) ;
if ( column . click )
nodeName . select ( 'span' ) . on ( 'click' , column . click ) ;
} ) ;
node
. order ( )
. on ( 'click' , function ( d ) {
dispatch . elementClick ( {
row : this , //TODO: decide whether or not this should be consistent with scatter/line events or should be an html link (a href)
data : d ,
pos : [ d . x , d . y ]
} ) ;
} )
. on ( 'dblclick' , function ( d ) {
dispatch . elementDblclick ( {
row : this ,
data : d ,
pos : [ d . x , d . y ]
} ) ;
} )
. on ( 'mouseover' , function ( d ) {
dispatch . elementMouseover ( {
row : this ,
data : d ,
pos : [ d . x , d . y ]
} ) ;
} )
. on ( 'mouseout' , function ( d ) {
dispatch . elementMouseout ( {
row : this ,
data : d ,
pos : [ d . x , d . y ]
} ) ;
} ) ;
// Toggle children on click.
function click ( d , _ , unshift ) {
d3 . event . stopPropagation ( ) ;
if ( d3 . event . shiftKey && ! unshift ) {
//If you shift-click, it'll toggle fold all the children, instead of itself
d3 . event . shiftKey = false ;
d . values && d . values . forEach ( function ( node ) {
if ( node . values || node . _values ) {
click ( node , 0 , true ) ;
}
} ) ;
return true ;
}
if ( ! hasChildren ( d ) ) {
//download file
//window.location.href = d.url;
return true ;
}
if ( d . values ) {
d . _values = d . values ;
d . values = null ;
} else {
d . values = d . _values ;
d . _values = null ;
}
chart . update ( ) ;
}
function icon ( d ) {
return ( d . _values && d . _values . length ) ? iconOpen : ( d . values && d . values . length ) ? iconClose : '' ;
}
function folded ( d ) {
return ( d . _values && d . _values . length ) ;
}
function hasChildren ( d ) {
var values = d . values || d . _values ;
return ( values && values . length ) ;
}
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
scatter . color ( color ) ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
chart . header = function ( _ ) {
if ( ! arguments . length ) return header ;
header = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
chart . columns = function ( _ ) {
if ( ! arguments . length ) return columns ;
columns = _ ;
return chart ;
} ;
chart . tableClass = function ( _ ) {
if ( ! arguments . length ) return tableClass ;
tableClass = _ ;
return chart ;
} ;
chart . iconOpen = function ( _ ) {
if ( ! arguments . length ) return iconOpen ;
iconOpen = _ ;
return chart ;
}
chart . iconClose = function ( _ ) {
if ( ! arguments . length ) return iconClose ;
iconClose = _ ;
return chart ;
}
//============================================================
return chart ;
}
nv . models . legend = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 5 , right : 0 , bottom : 5 , left : 0 }
, width = 400
, height = 20
, getKey = function ( d ) { return d . key }
, color = nv . utils . defaultColor ( )
, align = true
, dispatch = d3 . dispatch ( 'legendClick' , 'legendDblclick' , 'legendMouseover' , 'legendMouseout' )
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
container = d3 . select ( this ) ;
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-legend' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-legend' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
var series = g . selectAll ( '.nv-series' )
. data ( function ( d ) { return d } ) ;
var seriesEnter = series . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nv-series' )
. on ( 'mouseover' , function ( d , i ) {
dispatch . legendMouseover ( d , i ) ; //TODO: Make consistent with other event objects
} )
. on ( 'mouseout' , function ( d , i ) {
dispatch . legendMouseout ( d , i ) ;
} )
. on ( 'click' , function ( d , i ) {
dispatch . legendClick ( d , i ) ;
} )
. on ( 'dblclick' , function ( d , i ) {
dispatch . legendDblclick ( d , i ) ;
} ) ;
seriesEnter . append ( 'circle' )
. style ( 'stroke-width' , 2 )
. attr ( 'r' , 5 ) ;
seriesEnter . append ( 'text' )
. attr ( 'text-anchor' , 'start' )
. attr ( 'dy' , '.32em' )
. attr ( 'dx' , '8' ) ;
series . classed ( 'disabled' , function ( d ) { return d . disabled } ) ;
series . exit ( ) . remove ( ) ;
series . select ( 'circle' )
. style ( 'fill' , function ( d , i ) { return d . color || color ( d , i ) } )
. style ( 'stroke' , function ( d , i ) { return d . color || color ( d , i ) } ) ;
series . select ( 'text' ) . text ( getKey ) ;
//TODO: implement fixed-width and max-width options (max-width is especially useful with the align option)
// NEW ALIGNING CODE, TODO: clean up
if ( align ) {
var seriesWidths = [ ] ;
series . each ( function ( d , i ) {
seriesWidths . push ( d3 . select ( this ) . select ( 'text' ) . node ( ) . getComputedTextLength ( ) + 28 ) ; // 28 is ~ the width of the circle plus some padding
} ) ;
//nv.log('Series Widths: ', JSON.stringify(seriesWidths));
var seriesPerRow = 0 ;
var legendWidth = 0 ;
var columnWidths = [ ] ;
while ( legendWidth < availableWidth && seriesPerRow < seriesWidths . length ) {
columnWidths [ seriesPerRow ] = seriesWidths [ seriesPerRow ] ;
legendWidth += seriesWidths [ seriesPerRow ++ ] ;
}
while ( legendWidth > availableWidth && seriesPerRow > 1 ) {
columnWidths = [ ] ;
seriesPerRow -- ;
for ( k = 0 ; k < seriesWidths . length ; k ++ ) {
if ( seriesWidths [ k ] > ( columnWidths [ k % seriesPerRow ] || 0 ) )
columnWidths [ k % seriesPerRow ] = seriesWidths [ k ] ;
}
legendWidth = columnWidths . reduce ( function ( prev , cur , index , array ) {
return prev + cur ;
} ) ;
}
//console.log(columnWidths, legendWidth, seriesPerRow);
var xPositions = [ ] ;
for ( var i = 0 , curX = 0 ; i < seriesPerRow ; i ++ ) {
xPositions [ i ] = curX ;
curX += columnWidths [ i ] ;
}
series
. attr ( 'transform' , function ( d , i ) {
return 'translate(' + xPositions [ i % seriesPerRow ] + ',' + ( 5 + Math . floor ( i / seriesPerRow ) * 20 ) + ')' ;
} ) ;
//position legend as far right as possible within the total width
g . attr ( 'transform' , 'translate(' + ( width - margin . right - legendWidth ) + ',' + margin . top + ')' ) ;
height = margin . top + margin . bottom + ( Math . ceil ( seriesWidths . length / seriesPerRow ) * 20 ) ;
} else {
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 + ')' ) ;
height = margin . top + margin . bottom + ypos + 15 ;
}
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
chart . dispatch = dispatch ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . key = function ( _ ) {
if ( ! arguments . length ) return getKey ;
getKey = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
chart . align = function ( _ ) {
if ( ! arguments . length ) return align ;
align = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . lineChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var lines = nv . models . line ( )
, xAxis = nv . models . axis ( )
, yAxis = nv . models . axis ( )
, legend = nv . models . legend ( )
;
var margin = { top : 30 , right : 20 , bottom : 50 , left : 60 }
, color = nv . utils . defaultColor ( )
, width = null
, height = null
, showLegend = true
, tooltips = true
, tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' at ' + x + '</p>'
}
, x
, y
, noData = 'No Data Available.'
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
;
xAxis
. orient ( 'bottom' )
. tickPadding ( 5 )
;
yAxis
. orient ( 'left' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function ( e , offsetElement ) {
// New addition to calculate position if SVG is scaled with viewBox, may move TODO: consider implementing everywhere else
if ( offsetElement ) {
var svg = d3 . select ( offsetElement ) . select ( 'svg' ) ;
var viewBox = svg . attr ( 'viewBox' ) ;
if ( viewBox ) {
viewBox = viewBox . split ( ' ' ) ;
var ratio = parseInt ( svg . style ( 'width' ) ) / viewBox [ 2 ] ;
e . pos [ 0 ] = e . pos [ 0 ] * ratio ;
e . pos [ 1 ] = e . pos [ 1 ] * ratio ;
}
}
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( lines . x ( ) ( e . point , e . pointIndex ) ) ,
y = yAxis . tickFormat ( ) ( lines . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , null , null , offsetElement ) ;
} ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { chart ( selection ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display noData message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = lines . xScale ( ) ;
y = lines . yScale ( ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-lineChart' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-lineChart' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-linesWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Legend
if ( showLegend ) {
legend . width ( availableWidth ) ;
g . select ( '.nv-legendWrap' )
. datum ( data )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
wrap . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
}
//------------------------------------------------------------
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
// Main Chart Component(s)
lines
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) ) ;
var linesWrap = g . select ( '.nv-linesWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( linesWrap ) . call ( lines ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Axes
xAxis
. scale ( x )
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( g . select ( '.nv-x.nv-axis' ) )
. call ( xAxis ) ;
yAxis
. scale ( y )
. ticks ( availableHeight / 36 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.nv-y.nv-axis' ) )
. call ( yAxis ) ;
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
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 ( '.nv-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 )
} ) ;
* /
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode ) ;
} ) ;
//============================================================
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
lines . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
lines . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . lines = lines ;
chart . legend = legend ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
d3 . rebind ( chart , lines , 'defined' , 'isArea' , 'x' , 'y' , 'size' , 'xScale' , 'yScale' , 'xDomain' , 'yDomain' , 'forceX' , 'forceY' , 'interactive' , 'clipEdge' , 'clipVoronoi' , 'id' , 'interpolate' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . line = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 960
, height = 500
, color = nv . utils . defaultColor ( ) // a function that returns a color
, id = Math . floor ( Math . random ( ) * 10000 ) //Create semi-unique ID incase user doesn't select one
, getX = function ( d ) { return d . x } // accessor to get the x value from a data point
, getY = function ( d ) { return d . y } // accessor to get the y value from a data point
, defined = function ( d , i ) { return ! isNaN ( getY ( d , i ) ) && getY ( d , i ) !== null } // allows a line to be not continous when it is not defined
, isArea = function ( d ) { return d . area } // decides if a line is an area or just a line
, clipEdge = false // if true, masks lines within x and y scale
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
, interpolate = "linear" // controls the line interpolation
, scatter = nv . models . scatter ( )
;
scatter
. id ( id )
. size ( 16 ) // default size
. sizeDomain ( [ 16 , 256 ] ) //set to speed up calculation, needs to be unset if there is a custom size accessor
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var x0 , y0 //used to store previous scales
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
//------------------------------------------------------------
// Setup Scales
x = scatter . xScale ( ) ;
y = scatter . yScale ( ) ;
x0 = x0 || x ;
y0 = y0 || y ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-line' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-line' ) ;
var defsEnter = wrapEnter . append ( 'defs' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' )
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-groups' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-scatterWrap' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
scatter
. width ( availableWidth )
. height ( availableHeight )
var scatterWrap = wrap . select ( '.nv-scatterWrap' ) ;
//.datum(data); // Data automatically trickles down from the wrap
d3 . transition ( scatterWrap ) . call ( scatter ) ;
defsEnter . append ( 'clipPath' )
. attr ( 'id' , 'nv-edge-clip-' + id )
. append ( 'rect' ) ;
wrap . select ( '#nv-edge-clip-' + id + ' rect' )
. attr ( 'width' , availableWidth )
. attr ( 'height' , availableHeight ) ;
g . attr ( 'clip-path' , clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '' ) ;
scatterWrap
. attr ( 'clip-path' , clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '' ) ;
var groups = wrap . select ( '.nv-groups' ) . selectAll ( '.nv-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 'nv-group nv-series-' + i } )
. classed ( 'hover' , function ( d ) { return d . hover } )
. style ( 'fill' , function ( d , i ) { return color ( d , i ) } )
. style ( 'stroke' , function ( d , i ) { return color ( d , i ) } ) ;
d3 . transition ( groups )
. style ( 'stroke-opacity' , 1 )
. style ( 'fill-opacity' , . 5 ) ;
var areaPaths = groups . selectAll ( 'path.nv-area' )
. data ( function ( d ) { return isArea ( d ) ? [ d ] : [ ] } ) ; // this is done differently than lines because I need to check if series is an area
areaPaths . enter ( ) . append ( 'path' )
. attr ( 'class' , 'nv-area' )
. attr ( 'd' , function ( d ) {
return d3 . svg . area ( )
. interpolate ( interpolate )
. defined ( defined )
. x ( function ( d , i ) { return x0 ( getX ( d , i ) ) } )
. y0 ( function ( d , i ) { return y0 ( getY ( d , i ) ) } )
. y1 ( function ( d , i ) { return y0 ( y . domain ( ) [ 0 ] <= 0 ? y . domain ( ) [ 1 ] >= 0 ? 0 : y . domain ( ) [ 1 ] : y . domain ( ) [ 0 ] ) } )
//.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
. apply ( this , [ d . values ] )
} ) ;
d3 . transition ( groups . exit ( ) . selectAll ( 'path.nv-area' ) )
. attr ( 'd' , function ( d ) {
return d3 . svg . area ( )
. interpolate ( interpolate )
. defined ( defined )
. x ( function ( d , i ) { return x0 ( getX ( d , i ) ) } )
. y0 ( function ( d , i ) { return y0 ( getY ( d , i ) ) } )
. y1 ( function ( d , i ) { return y0 ( y . domain ( ) [ 0 ] <= 0 ? y . domain ( ) [ 1 ] >= 0 ? 0 : y . domain ( ) [ 1 ] : y . domain ( ) [ 0 ] ) } )
//.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
. apply ( this , [ d . values ] )
} ) ;
d3 . transition ( areaPaths )
. attr ( 'd' , function ( d ) {
return d3 . svg . area ( )
. interpolate ( interpolate )
. defined ( defined )
. x ( function ( d , i ) { return x0 ( getX ( d , i ) ) } )
. y0 ( function ( d , i ) { return y0 ( getY ( d , i ) ) } )
. y1 ( function ( d , i ) { return y0 ( y . domain ( ) [ 0 ] <= 0 ? y . domain ( ) [ 1 ] >= 0 ? 0 : y . domain ( ) [ 1 ] : y . domain ( ) [ 0 ] ) } )
//.y1(function(d,i) { return y0(0) }) //assuming 0 is within y domain.. may need to tweak this
. apply ( this , [ d . values ] )
} ) ;
var linePaths = groups . selectAll ( 'path.nv-line' )
. data ( function ( d ) { return [ d . values ] } ) ;
linePaths . enter ( ) . append ( 'path' )
. attr ( 'class' , 'nv-line' )
. attr ( 'd' ,
d3 . svg . line ( )
. interpolate ( interpolate )
. defined ( defined )
. x ( function ( d , i ) { return x0 ( getX ( d , i ) ) } )
. y ( function ( d , i ) { return y0 ( getY ( d , i ) ) } )
) ;
d3 . transition ( groups . exit ( ) . selectAll ( 'path.nv-line' ) )
. attr ( 'd' ,
d3 . svg . line ( )
. interpolate ( interpolate )
. defined ( defined )
. x ( function ( d , i ) { return x ( getX ( d , i ) ) } )
. y ( function ( d , i ) { return y ( getY ( d , i ) ) } )
) ;
d3 . transition ( linePaths )
. attr ( 'd' ,
d3 . svg . line ( )
. interpolate ( interpolate )
. defined ( defined )
. x ( function ( d , i ) { return x ( getX ( d , i ) ) } )
. y ( function ( d , i ) { return y ( getY ( d , i ) ) } )
) ;
//store old scales for use in transitions on update
x0 = x . copy ( ) ;
y0 = y . copy ( ) ;
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
chart . dispatch = scatter . dispatch ;
chart . scatter = scatter ;
d3 . rebind ( chart , scatter , 'interactive' , 'size' , 'xScale' , 'yScale' , 'zScale' , 'xDomain' , 'yDomain' , 'sizeDomain' , 'forceX' , 'forceY' , 'forceSize' , 'clipVoronoi' , 'clipRadius' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 = _ ;
scatter . x ( _ ) ;
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return getY ;
getY = _ ;
scatter . y ( _ ) ;
return chart ;
} ;
chart . clipEdge = function ( _ ) {
if ( ! arguments . length ) return clipEdge ;
clipEdge = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
scatter . color ( color ) ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
chart . interpolate = function ( _ ) {
if ( ! arguments . length ) return interpolate ;
interpolate = _ ;
return chart ;
} ;
chart . defined = function ( _ ) {
if ( ! arguments . length ) return defined ;
defined = _ ;
return chart ;
} ;
chart . isArea = function ( _ ) {
if ( ! arguments . length ) return isArea ;
isArea = d3 . functor ( _ ) ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . linePlusBarChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var lines = nv . models . line ( )
, bars = nv . models . historicalBar ( )
, xAxis = nv . models . axis ( )
, y1Axis = nv . models . axis ( )
, y2Axis = nv . models . axis ( )
, legend = nv . models . legend ( )
;
var margin = { top : 30 , right : 60 , bottom : 50 , left : 60 }
, width = null
, height = null
, getX = function ( d ) { return d . x }
, getY = function ( d ) { return d . y }
, color = nv . utils . defaultColor ( )
, showLegend = true
, tooltips = true
, tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' at ' + x + '</p>' ;
}
, x
, y1
, y2
, noData = "No Data Available."
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
;
xAxis
. orient ( 'bottom' )
. tickPadding ( 5 )
;
y1Axis
. orient ( 'left' )
;
y2Axis
. orient ( 'right' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( lines . x ( ) ( e . point , e . pointIndex ) ) ,
y = ( e . series . bar ? y1Axis : y2Axis ) . tickFormat ( ) ( lines . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , e . value < 0 ? 'n' : 's' , null , offsetElement ) ;
} ;
//------------------------------------------------------------
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { chart ( selection ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = xAxis . scale ( ) ;
y1 = bars . yScale ( ) ;
y2 = lines . yScale ( ) ;
var dataBars = data . filter ( function ( d ) { return ! d . disabled && d . bar } ) ;
var dataLines = data . filter ( function ( d ) { return ! d . disabled && ! d . bar } ) ;
//TODO: try to remove x scale computation from this layer
var series1 = data . filter ( function ( d ) { return ! d . disabled && d . bar } )
. map ( function ( d ) {
return d . values . map ( function ( d , i ) {
return { x : getX ( d , i ) , y : getY ( d , i ) }
} )
} ) ;
var series2 = data . filter ( function ( d ) { return ! d . disabled && ! d . bar } )
. map ( function ( d ) {
return d . values . map ( function ( d , i ) {
return { x : getX ( d , i ) , y : getY ( d , i ) }
} )
} ) ;
x . domain ( d3 . extent ( d3 . merge ( series1 . concat ( series2 ) ) , function ( d ) { return d . x } ) )
. range ( [ 0 , availableWidth ] ) ;
/ *
x . domain ( d3 . extent ( d3 . merge ( data . map ( function ( d ) { return d . values } ) ) , getX ) )
. range ( [ 0 , availableWidth ] ) ;
y1 . domain ( d3 . extent ( d3 . merge ( dataBars ) , function ( d ) { return d . y } ) )
. range ( [ availableHeight , 0 ] ) ;
y2 . domain ( d3 . extent ( d3 . merge ( dataLines ) , function ( d ) { return d . y } ) )
. range ( [ availableHeight , 0 ] ) ;
* /
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = d3 . select ( this ) . selectAll ( 'g.nv-wrap.nv-linePlusBar' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-linePlusBar' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y1 nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y2 nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-barsWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-linesWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Legend
if ( showLegend ) {
legend . width ( availableWidth / 2 ) ;
g . select ( '.nv-legendWrap' )
. datum ( data . map ( function ( series ) {
series . originalKey = series . originalKey === undefined ? series . key : series . originalKey ;
series . key = series . originalKey + ( series . bar ? ' (left axis)' : ' (right axis)' ) ;
return series ;
} ) )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
g . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(' + ( availableWidth / 2 ) + ',' + ( - margin . top ) + ')' ) ;
}
//------------------------------------------------------------
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
// Main Chart Component(s)
lines
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled && ! data [ i ] . bar } ) )
bars
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled && data [ i ] . bar } ) )
var barsWrap = g . select ( '.nv-barsWrap' )
. datum ( dataBars . length ? dataBars : [ { values : [ ] } ] )
var linesWrap = g . select ( '.nv-linesWrap' )
. datum ( dataLines . length ? dataLines : [ { values : [ ] } ] )
d3 . transition ( barsWrap ) . call ( bars ) ;
d3 . transition ( linesWrap ) . call ( lines ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Axes
xAxis
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + y1 . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( g . select ( '.nv-x.nv-axis' ) )
. call ( xAxis ) ;
y1Axis
. scale ( y1 )
. ticks ( availableHeight / 36 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.nv-y1.nv-axis' ) )
. style ( 'opacity' , dataBars . length ? 1 : 0 )
. call ( y1Axis ) ;
y2Axis
. scale ( y2 )
. ticks ( availableHeight / 36 )
. tickSize ( dataBars . length ? 0 : - availableWidth , 0 ) ; // Show the y2 rules only if y1 has none
g . select ( '.nv-y2.nv-axis' )
. style ( 'opacity' , dataLines . length ? 1 : 0 )
. attr ( 'transform' , 'translate(' + x . range ( ) [ 1 ] + ',0)' ) ;
d3 . transition ( g . select ( '.nv-y2.nv-axis' ) )
. call ( y2Axis ) ;
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
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 ( '.nv-series' ) . classed ( 'disabled' , false ) ;
return d ;
} ) ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode ) ;
} ) ;
//============================================================
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
lines . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
lines . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
bars . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
bars . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . legend = legend ;
chart . lines = lines ;
chart . bars = bars ;
chart . xAxis = xAxis ;
chart . y1Axis = y1Axis ;
chart . y2Axis = y2Axis ;
d3 . rebind ( chart , lines , 'defined' , 'size' , 'clipVoronoi' , 'interpolate' ) ;
//TODO: consider rebinding x, y and some other stuff, and simply do soemthign lile bars.x(lines.x()), etc.
//d3.rebind(chart, lines, 'x', 'y', 'size', 'xDomain', 'yDomain', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'id');
chart . x = function ( _ ) {
if ( ! arguments . length ) return getX ;
getX = _ ;
lines . x ( _ ) ;
bars . x ( _ ) ;
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return getY ;
getY = _ ;
lines . y ( _ ) ;
bars . y ( _ ) ;
return chart ;
} ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . lineChart = function ( ) {
var margin = { top : 30 , right : 20 , bottom : 50 , left : 60 } ,
color = nv . utils . defaultColor ( ) ,
width = null ,
height = null ,
showLegend = true ,
showControls = true ,
fisheye = 0 ,
pauseFisheye = false ,
tooltips = true ,
tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' at ' + x + '</p>'
} ,
noData = "No Data Available."
;
var x = d3 . fisheye . scale ( d3 . scale . linear ) . distortion ( 0 ) ;
var lines = nv . models . line ( ) . xScale ( x ) ,
//x = lines.xScale(),
y = lines . yScale ( ) ,
xAxis = nv . models . axis ( ) . scale ( x ) . orient ( 'bottom' ) . tickPadding ( 5 ) ,
yAxis = nv . models . axis ( ) . scale ( y ) . orient ( 'left' ) ,
legend = nv . models . legend ( ) . height ( 30 ) ,
controls = nv . models . legend ( ) . height ( 30 ) ,
dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' ) ;
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( lines . x ( ) ( e . point , e . pointIndex ) ) ,
y = yAxis . tickFormat ( ) ( lines . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , null , null , offsetElement ) ;
} ;
var controlsData = [
{ key : 'Magnify' , disabled : true }
] ;
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
container . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'x' , availableWidth / 2 )
. attr ( 'y' , availableHeight / 2 )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' )
. text ( noData ) ;
return chart ;
} else {
container . select ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
var wrap = container . selectAll ( 'g.nv-wrap.nv-lineChart' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-lineChart' ) . append ( 'g' ) ;
gEnter . append ( 'rect' )
. attr ( 'class' , 'nvd3 nv-background' )
. attr ( 'width' , availableWidth )
. attr ( 'height' , availableHeight ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-linesWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-controlsWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-controlsWrap' ) ;
var g = wrap . select ( 'g' ) ;
if ( showLegend ) {
legend . width ( availableWidth ) ;
g . select ( '.nv-legendWrap' )
. datum ( data )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
g . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
}
if ( showControls ) {
controls . width ( 180 ) . color ( [ '#444' ] ) ;
g . select ( '.nv-controlsWrap' )
. datum ( controlsData )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
. call ( controls ) ;
}
lines
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) ) ;
g . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
var linesWrap = g . select ( '.nv-linesWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( linesWrap ) . call ( lines ) ;
xAxis
//.scale(x)
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( g . select ( '.nv-x.nv-axis' ) )
. call ( xAxis ) ;
yAxis
//.scale(y)
. ticks ( availableHeight / 36 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.nv-y.nv-axis' ) )
. call ( yAxis ) ;
g . select ( '.nv-background' ) . on ( 'mousemove' , updateFisheye ) ;
g . select ( '.nv-background' ) . on ( 'click' , function ( ) { pauseFisheye = ! pauseFisheye ; } ) ;
//g.select('.point-paths').on('mousemove', updateFisheye);
function updateFisheye ( ) {
if ( pauseFisheye ) {
//g.select('.background') .style('pointer-events', 'none');
g . select ( '.nv-point-paths' ) . style ( 'pointer-events' , 'all' ) ;
return false ;
}
g . select ( '.nv-background' ) . style ( 'pointer-events' , 'all' ) ;
g . select ( '.nv-point-paths' ) . style ( 'pointer-events' , 'none' ) ;
var mouse = d3 . mouse ( this ) ;
linesWrap . call ( lines ) ;
g . select ( '.nv-x.nv-axis' ) . call ( xAxis ) ;
x . distortion ( fisheye ) . focus ( mouse [ 0 ] ) ;
}
controls . dispatch . on ( 'legendClick' , function ( d , i ) {
d . disabled = ! d . disabled ;
fisheye = d . disabled ? 0 : 5 ;
g . select ( '.nv-background' ) . style ( 'pointer-events' , d . disabled ? 'none' : 'all' ) ;
g . select ( '.nv-point-paths' ) . style ( 'pointer-events' , d . disabled ? 'all' : 'none' ) ;
//scatter.interactive(d.disabled);
//tooltips = d.disabled;
if ( d . disabled ) {
x . distortion ( fisheye ) . focus ( 0 ) ;
linesWrap . call ( lines ) ;
g . select ( '.nv-x.nv-axis' ) . call ( xAxis ) ;
} else {
pauseFisheye = false ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
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 ( '.nv-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 ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
if ( tooltips ) dispatch . on ( 'tooltipShow' , function ( e ) { showTooltip ( e , that . parentNode ) } ) ; // TODO: maybe merge with above?
lines . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
if ( tooltips ) dispatch . on ( 'tooltipHide' , nv . tooltip . cleanup ) ;
} ) ;
//TODO: decide if this is a good idea, and if it should be in all models
chart . update = function ( ) { chart ( selection ) } ;
chart . container = this ; // I need a reference to the container in order to have outside code check if the chart is visible or not
return chart ;
}
chart . dispatch = dispatch ;
chart . legend = legend ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
d3 . rebind ( chart , lines , 'defined' , 'x' , 'y' , 'size' , 'xDomain' , 'yDomain' , 'forceX' , 'forceY' , 'interactive' , 'clipEdge' , 'clipVoronoi' , 'id' , 'interpolate' ) ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
return chart ;
}
nv . models . line = function ( ) {
//Default Settings
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 } ,
width = 960 ,
height = 500 ,
color = nv . utils . defaultColor ( ) , // function that returns colors
id = Math . floor ( Math . random ( ) * 10000 ) , //Create semi-unique ID incase user doesn't select one
getX = function ( d ) { return d . x } , // accessor to get the x value from a data point
getY = function ( d ) { return d . y } , // accessor to get the y value from a data point
clipEdge = false , // if true, masks lines within x and y scale
interpolate = "linear" ; // controls the line interpolation
var scatter = nv . models . scatter ( )
. id ( id )
. size ( 16 ) // default size
. sizeDomain ( [ 16 , 256 ] ) , //set to speed up calculation, needs to be unset if there is a cstom size accessor
//x = scatter.xScale(),
//y = scatter.yScale(),
x , y ,
x0 , y0 , timeoutID ;
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ;
//get the scales inscase scatter scale was set manually
x = x || scatter . xScale ( ) ;
y = y || scatter . yScale ( ) ;
//store old scales if they exist
x0 = x0 || x ;
y0 = y0 || y ;
var wrap = d3 . select ( this ) . selectAll ( 'g.nv-wrap.nv-line' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-line' ) ;
var defsEnter = wrapEnter . append ( 'defs' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' )
wrapEnter . append ( 'g' ) . attr ( 'class' , 'nv-scatterWrap' ) ;
var scatterWrap = wrap . select ( '.nv-scatterWrap' ) . datum ( data ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-groups' ) ;
scatter
. width ( availableWidth )
. height ( availableHeight )
d3 . transition ( scatterWrap ) . call ( scatter ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
defsEnter . append ( 'clipPath' )
. attr ( 'id' , 'nv-edge-clip-' + id )
. append ( 'rect' ) ;
wrap . select ( '#nv-edge-clip-' + id + ' rect' )
. attr ( 'width' , availableWidth )
. attr ( 'height' , availableHeight ) ;
g . attr ( 'clip-path' , clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '' ) ;
scatterWrap
. attr ( 'clip-path' , clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '' ) ;
var groups = wrap . select ( '.nv-groups' ) . selectAll ( '.nv-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 'nv-group nv-series-' + i } )
. classed ( 'hover' , function ( d ) { return d . hover } )
. style ( 'fill' , function ( d , i ) { return color ( d , i ) } )
. style ( 'stroke' , function ( d , i ) { return color ( d , i ) } )
d3 . transition ( groups )
. style ( 'stroke-opacity' , 1 )
. style ( 'fill-opacity' , . 5 )
var paths = groups . selectAll ( 'path' )
. data ( function ( d , i ) { return [ d . values ] } ) ;
paths . enter ( ) . append ( 'path' )
. attr ( 'class' , 'nv-line' )
. attr ( 'd' , d3 . svg . line ( )
. interpolate ( interpolate )
. x ( function ( d , i ) { return x0 ( getX ( d , i ) ) } )
. y ( function ( d , i ) { return y0 ( getY ( d , i ) ) } )
) ;
d3 . transition ( groups . exit ( ) . selectAll ( 'path' ) )
. attr ( 'd' , d3 . svg . line ( )
. interpolate ( interpolate )
. x ( function ( d , i ) { return x ( getX ( d , i ) ) } )
. y ( function ( d , i ) { return y ( getY ( d , i ) ) } )
)
. remove ( ) ; // redundant? line is already being removed
d3 . transition ( paths )
. attr ( 'd' , d3 . svg . line ( )
. interpolate ( interpolate )
. x ( function ( d , i ) { return x ( getX ( d , i ) ) } )
. y ( function ( d , i ) { return y ( getY ( d , i ) ) } )
) ;
//store old scales for use in transitions on update, to animate from old to new positions
x0 = x . copy ( ) ;
y0 = y . copy ( ) ;
} ) ;
return chart ;
}
chart . dispatch = scatter . dispatch ;
d3 . rebind ( chart , scatter , 'interactive' , 'size' , 'xScale' , 'yScale' , 'zScale' , 'xDomain' , 'yDomain' , 'sizeDomain' , 'forceX' , 'forceY' , 'forceSize' , 'clipVoronoi' , 'clipRadius' ) ;
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 = _ ;
scatter . x ( _ ) ;
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return getY ;
getY = _ ;
scatter . y ( _ ) ;
return chart ;
} ;
chart . clipEdge = function ( _ ) {
if ( ! arguments . length ) return clipEdge ;
clipEdge = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
scatter . color ( color ) ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
chart . interpolate = function ( _ ) {
if ( ! arguments . length ) return interpolate ;
interpolate = _ ;
return chart ;
} ;
chart . defined = function ( _ ) {
if ( ! arguments . length ) return defined ;
defined = _ ;
return chart ;
} ;
return chart ;
}
nv . models . lineWithFocusChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var lines = nv . models . line ( )
, lines2 = nv . models . line ( )
, xAxis = nv . models . axis ( )
, yAxis = nv . models . axis ( )
, x2Axis = nv . models . axis ( )
, y2Axis = nv . models . axis ( )
, legend = nv . models . legend ( )
, brush = d3 . svg . brush ( )
;
var margin = { top : 30 , right : 30 , bottom : 30 , left : 60 }
, margin2 = { top : 0 , right : 30 , bottom : 20 , left : 60 }
, color = nv . utils . defaultColor ( )
, width = null
, height = null
, height2 = 100
, x
, y
, x2
, y2
, showLegend = true
, brushExtent = null
, tooltips = true
, tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' at ' + x + '</p>'
}
, noData = "No Data Available."
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' , 'brush' )
;
lines
. clipEdge ( true )
;
lines2
. interactive ( false )
;
xAxis
. orient ( 'bottom' )
. tickPadding ( 5 )
;
yAxis
. orient ( 'left' )
;
x2Axis
. orient ( 'bottom' )
. tickPadding ( 5 )
;
y2Axis
. orient ( 'left' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( lines . x ( ) ( e . point , e . pointIndex ) ) ,
y = yAxis . tickFormat ( ) ( lines . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , null , null , offsetElement ) ;
} ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight1 = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom - height2 ,
availableHeight2 = height2 - margin2 . top - margin2 . bottom ;
chart . update = function ( ) { chart ( selection ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = lines . xScale ( ) ;
y = lines . yScale ( ) ;
x2 = lines2 . xScale ( ) ;
y2 = lines2 . yScale ( ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-lineWithFocusChart' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-lineWithFocusChart' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
var focusEnter = gEnter . append ( 'g' ) . attr ( 'class' , 'nv-focus' ) ;
focusEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
focusEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
focusEnter . append ( 'g' ) . attr ( 'class' , 'nv-linesWrap' ) ;
var contextEnter = gEnter . append ( 'g' ) . attr ( 'class' , 'nv-context' ) ;
contextEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
contextEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
contextEnter . append ( 'g' ) . attr ( 'class' , 'nv-linesWrap' ) ;
contextEnter . append ( 'g' ) . attr ( 'class' , 'nv-brushBackground' ) ;
contextEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-brush' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Legend
if ( showLegend ) {
legend . width ( availableWidth ) ;
g . select ( '.nv-legendWrap' )
. datum ( data )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight1 = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom - height2 ;
}
g . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
}
//------------------------------------------------------------
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
// Main Chart Component(s)
lines
. width ( availableWidth )
. height ( availableHeight1 )
. color (
data
. map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} )
. filter ( function ( d , i ) {
return ! data [ i ] . disabled ;
} )
) ;
lines2
. defined ( lines . defined ( ) )
. width ( availableWidth )
. height ( availableHeight2 )
. color (
data
. map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} )
. filter ( function ( d , i ) {
return ! data [ i ] . disabled ;
} )
) ;
g . select ( '.nv-context' )
. attr ( 'transform' , 'translate(0,' + ( availableHeight1 + margin . bottom + margin2 . top ) + ')' )
var contextLinesWrap = g . select ( '.nv-context .nv-linesWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( contextLinesWrap ) . call ( lines2 ) ;
//------------------------------------------------------------
/ *
var focusLinesWrap = g . select ( '.nv-focus .nv-linesWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( focusLinesWrap ) . call ( lines ) ;
* /
//------------------------------------------------------------
// Setup Main (Focus) Axes
xAxis
. scale ( x )
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight1 , 0 ) ;
yAxis
. scale ( y )
. ticks ( availableHeight1 / 36 )
. tickSize ( - availableWidth , 0 ) ;
g . select ( '.nv-focus .nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + availableHeight1 + ')' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Brush
brush
. x ( x2 )
. on ( 'brush' , onBrush ) ;
if ( brushExtent ) brush . extent ( brushExtent ) ;
var brushBG = g . select ( '.nv-brushBackground' ) . selectAll ( 'g' )
. data ( [ brushExtent || brush . extent ( ) ] )
var brushBGenter = brushBG . enter ( )
. append ( 'g' ) ;
brushBGenter . append ( 'rect' )
. attr ( 'class' , 'left' )
. attr ( 'x' , 0 )
. attr ( 'y' , 0 )
. attr ( 'height' , availableHeight2 ) ;
brushBGenter . append ( 'rect' )
. attr ( 'class' , 'right' )
. attr ( 'x' , 0 )
. attr ( 'y' , 0 )
. attr ( 'height' , availableHeight2 ) ;
gBrush = g . select ( '.nv-x.nv-brush' )
. call ( brush ) ;
gBrush . selectAll ( 'rect' )
//.attr('y', -5)
. attr ( 'height' , availableHeight2 ) ;
gBrush . selectAll ( '.resize' ) . append ( 'path' ) . attr ( 'd' , resizePath ) ;
onBrush ( ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Secondary (Context) Axes
x2Axis
. scale ( x2 )
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight2 , 0 ) ;
g . select ( '.nv-context .nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + y2 . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( g . select ( '.nv-context .nv-x.nv-axis' ) )
. call ( x2Axis ) ;
y2Axis
. scale ( y2 )
. ticks ( availableHeight2 / 36 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.nv-context .nv-y.nv-axis' ) )
. call ( y2Axis ) ;
g . select ( '.nv-context .nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + y2 . range ( ) [ 0 ] + ')' ) ;
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
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 ( '.nv-series' ) . classed ( 'disabled' , false ) ;
return d ;
} ) ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode ) ;
} ) ;
//============================================================
//============================================================
// Functions
//------------------------------------------------------------
// Taken from crossfilter (http://square.github.com/crossfilter/)
function resizePath ( d ) {
var e = + ( d == 'e' ) ,
x = e ? 1 : - 1 ,
y = availableHeight2 / 3 ;
return 'M' + ( . 5 * x ) + ',' + y
+ 'A6,6 0 0 ' + e + ' ' + ( 6.5 * x ) + ',' + ( y + 6 )
+ 'V' + ( 2 * y - 6 )
+ 'A6,6 0 0 ' + e + ' ' + ( . 5 * x ) + ',' + ( 2 * y )
+ 'Z'
+ 'M' + ( 2.5 * x ) + ',' + ( y + 8 )
+ 'V' + ( 2 * y - 8 )
+ 'M' + ( 4.5 * x ) + ',' + ( y + 8 )
+ 'V' + ( 2 * y - 8 ) ;
}
function updateBrushBG ( ) {
if ( ! brush . empty ( ) ) brush . extent ( brushExtent ) ;
brushBG
. data ( [ brush . empty ( ) ? x2 . domain ( ) : brushExtent ] )
. each ( function ( d , i ) {
var leftWidth = x2 ( d [ 0 ] ) - x . range ( ) [ 0 ] ,
rightWidth = x . range ( ) [ 1 ] - x2 ( d [ 1 ] ) ;
d3 . select ( this ) . select ( '.left' )
. attr ( 'width' , leftWidth < 0 ? 0 : leftWidth ) ;
d3 . select ( this ) . select ( '.right' )
. attr ( 'x' , x2 ( d [ 1 ] ) )
. attr ( 'width' , rightWidth < 0 ? 0 : rightWidth ) ;
} ) ;
}
function onBrush ( ) {
brushExtent = brush . empty ( ) ? null : brush . extent ( ) ;
extent = brush . empty ( ) ? x2 . domain ( ) : brush . extent ( ) ;
dispatch . brush ( { extent : extent , brush : brush } ) ;
updateBrushBG ( ) ;
// Update Main (Focus)
var focusLinesWrap = g . select ( '.nv-focus .nv-linesWrap' )
. datum (
data
. filter ( function ( d ) { return ! d . disabled } )
. map ( function ( d , i ) {
return {
key : d . key ,
values : d . values . filter ( function ( d , i ) {
return lines . x ( ) ( d , i ) >= extent [ 0 ] && lines . x ( ) ( d , i ) <= extent [ 1 ] ;
} )
}
} )
) ;
d3 . transition ( focusLinesWrap ) . call ( lines ) ;
// Update Main (Focus) Axes
d3 . transition ( g . select ( '.nv-focus .nv-x.nv-axis' ) )
. call ( xAxis ) ;
d3 . transition ( g . select ( '.nv-focus .nv-y.nv-axis' ) )
. call ( yAxis ) ;
}
//============================================================
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
lines . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
lines . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . legend = legend ;
chart . lines = lines ;
chart . lines2 = lines2 ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
chart . x2Axis = x2Axis ;
chart . y2Axis = y2Axis ;
d3 . rebind ( chart , lines , 'defined' , 'isArea' , 'size' , 'xDomain' , 'yDomain' , 'forceX' , 'forceY' , 'interactive' , 'clipEdge' , 'clipVoronoi' , 'id' ) ;
chart . x = function ( _ ) {
if ( ! arguments . length ) return lines . x ;
lines . x ( _ ) ;
lines2 . x ( _ ) ;
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return lines . y ;
lines . y ( _ ) ;
lines2 . y ( _ ) ;
return chart ;
} ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
return chart ;
} ;
chart . margin2 = function ( _ ) {
if ( ! arguments . length ) return margin2 ;
margin2 = _ ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . interpolate = function ( _ ) {
if ( ! arguments . length ) return lines . interpolate ( ) ;
lines . interpolate ( _ ) ;
lines2 . interpolate ( _ ) ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
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 xAxis . tickFormat ( ) ;
xAxis . tickFormat ( _ ) ;
x2Axis . tickFormat ( _ ) ;
return chart ;
} ;
chart . yTickFormat = function ( _ ) {
if ( ! arguments . length ) return yAxis . tickFormat ( ) ;
yAxis . tickFormat ( _ ) ;
y2Axis . tickFormat ( _ ) ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . multiBarChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var multibar = nv . models . multiBar ( )
, xAxis = nv . models . axis ( )
, yAxis = nv . models . axis ( )
, legend = nv . models . legend ( )
, controls = nv . models . legend ( )
;
var margin = { top : 30 , right : 20 , bottom : 50 , left : 60 }
, width = null
, height = null
, color = nv . utils . defaultColor ( )
, showControls = true
, showLegend = true
, reduceXTicks = true // if false a tick will show for every data point
, rotateLabels = 0
, tooltips = true
, tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' on ' + x + '</p>'
}
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
, noData = "No Data Available."
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
;
multibar
. stacked ( false )
;
xAxis
. orient ( 'bottom' )
. tickPadding ( 5 )
. highlightZero ( false )
. showMaxMin ( false )
. tickFormat ( function ( d ) { return d } )
;
yAxis
. orient ( 'left' )
. tickFormat ( d3 . format ( ',.1f' ) )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( multibar . x ( ) ( e . point , e . pointIndex ) ) ,
y = yAxis . tickFormat ( ) ( multibar . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , e . value < 0 ? 'n' : 's' , null , offsetElement ) ;
} ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { selection . transition ( ) . call ( chart ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display noData message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = multibar . xScale ( ) ;
y = multibar . yScale ( ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-multiBarWithLegend' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-multiBarWithLegend' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-barsWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-controlsWrap' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Legend
if ( showLegend ) {
legend . width ( availableWidth / 2 ) ;
g . select ( '.nv-legendWrap' )
. datum ( data )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
g . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(' + ( availableWidth / 2 ) + ',' + ( - margin . top ) + ')' ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Controls
if ( showControls ) {
var controlsData = [
{ key : 'Grouped' , disabled : multibar . stacked ( ) } ,
{ key : 'Stacked' , disabled : ! multibar . stacked ( ) }
] ;
controls . width ( 180 ) . color ( [ '#444' , '#444' , '#444' ] ) ;
g . select ( '.nv-controlsWrap' )
. datum ( controlsData )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
. call ( controls ) ;
}
//------------------------------------------------------------
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
// Main Chart Component(s)
multibar
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) )
var barsWrap = g . select ( '.nv-barsWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( barsWrap ) . call ( multibar ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Axes
xAxis
. scale ( x )
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( g . select ( '.nv-x.nv-axis' ) )
. call ( xAxis ) ;
var xTicks = g . select ( '.nv-x.nv-axis > g' ) . selectAll ( 'g' ) ;
xTicks
. selectAll ( 'line, text' )
. style ( 'opacity' , 1 )
if ( reduceXTicks )
xTicks
. filter ( function ( d , i ) {
return i % Math . ceil ( data [ 0 ] . values . length / ( availableWidth / 100 ) ) !== 0 ;
} )
. selectAll ( 'text, line' )
. style ( 'opacity' , 0 ) ;
if ( rotateLabels )
xTicks
. selectAll ( 'text' )
. attr ( 'transform' , function ( d , i , j ) { return 'rotate(' + rotateLabels + ' 0,0)' } )
. attr ( 'text-transform' , rotateLabels > 0 ? 'start' : 'end' ) ;
yAxis
. scale ( y )
. ticks ( availableHeight / 36 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.nv-y.nv-axis' ) )
. call ( yAxis ) ;
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
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 ( '.nv-series' ) . classed ( 'disabled' , false ) ;
return d ;
} ) ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
controls . dispatch . on ( 'legendClick' , function ( d , i ) {
if ( ! d . disabled ) return ;
controlsData = controlsData . map ( function ( s ) {
s . disabled = true ;
return s ;
} ) ;
d . disabled = false ;
switch ( d . key ) {
case 'Grouped' :
multibar . stacked ( false ) ;
break ;
case 'Stacked' :
multibar . stacked ( true ) ;
break ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode )
} ) ;
//============================================================
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
multibar . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
multibar . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . multibar = multibar ;
chart . legend = legend ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
d3 . rebind ( chart , multibar , 'x' , 'y' , 'xDomain' , 'yDomain' , 'forceX' , 'forceY' , 'clipEdge' , 'id' , 'stacked' , 'delay' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
return chart ;
} ;
chart . showControls = function ( _ ) {
if ( ! arguments . length ) return showControls ;
showControls = _ ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . reduceXTicks = function ( _ ) {
if ( ! arguments . length ) return reduceXTicks ;
reduceXTicks = _ ;
return chart ;
} ;
chart . rotateLabels = function ( _ ) {
if ( ! arguments . length ) return rotateLabels ;
rotateLabels = _ ;
return chart ;
}
chart . tooltip = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . multiBarHorizontalChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var multibar = nv . models . multiBarHorizontal ( )
, xAxis = nv . models . axis ( )
, yAxis = nv . models . axis ( )
, legend = nv . models . legend ( ) . height ( 30 )
, controls = nv . models . legend ( ) . height ( 30 )
;
var margin = { top : 30 , right : 20 , bottom : 50 , left : 60 }
, width = null
, height = null
, color = nv . utils . defaultColor ( )
, showControls = true
, showLegend = true
, stacked = false
, tooltips = true
, tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + key + ' - ' + x + '</h3>' +
'<p>' + y + '</p>'
}
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
, noData = 'No Data Available.'
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
;
multibar
. stacked ( stacked )
;
xAxis
. orient ( 'left' )
. tickPadding ( 5 )
. highlightZero ( false )
. showMaxMin ( false )
. tickFormat ( function ( d ) { return d } )
;
yAxis
. orient ( 'bottom' )
. tickFormat ( d3 . format ( ',.1f' ) )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( multibar . x ( ) ( e . point , e . pointIndex ) ) ,
y = yAxis . tickFormat ( ) ( multibar . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , e . value < 0 ? 'e' : 'w' , null , offsetElement ) ;
} ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { selection . transition ( ) . call ( chart ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = multibar . xScale ( ) ;
y = multibar . yScale ( ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-multiBarHorizontalChart' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-multiBarHorizontalChart' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-barsWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-controlsWrap' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Legend
if ( showLegend ) {
legend . width ( availableWidth / 2 ) ;
g . select ( '.nv-legendWrap' )
. datum ( data )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
g . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(' + ( availableWidth / 2 ) + ',' + ( - margin . top ) + ')' ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Controls
if ( showControls ) {
var controlsData = [
{ key : 'Grouped' , disabled : multibar . stacked ( ) } ,
{ key : 'Stacked' , disabled : ! multibar . stacked ( ) }
] ;
controls . width ( 180 ) . color ( [ '#444' , '#444' , '#444' ] ) ;
g . select ( '.nv-controlsWrap' )
. datum ( controlsData )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
. call ( controls ) ;
}
//------------------------------------------------------------
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
// Main Chart Component(s)
multibar
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) )
var barsWrap = g . select ( '.nv-barsWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( barsWrap ) . call ( multibar ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Axes
xAxis
. scale ( x )
. ticks ( availableHeight / 24 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.nv-x.nv-axis' ) )
. call ( xAxis ) ;
var xTicks = g . select ( '.nv-x.nv-axis' ) . selectAll ( 'g' ) ;
xTicks
. selectAll ( 'line, text' )
. style ( 'opacity' , 1 )
yAxis
. scale ( y )
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.nv-y.nv-axis' )
. attr ( 'transform' , 'translate(0,' + availableHeight + ')' ) ;
d3 . transition ( g . select ( '.nv-y.nv-axis' ) )
. call ( yAxis ) ;
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
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 ( '.nv-series' ) . classed ( 'disabled' , false ) ;
return d ;
} ) ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
controls . dispatch . on ( 'legendClick' , function ( d , i ) {
if ( ! d . disabled ) return ;
controlsData = controlsData . map ( function ( s ) {
s . disabled = true ;
return s ;
} ) ;
d . disabled = false ;
switch ( d . key ) {
case 'Grouped' :
multibar . stacked ( false ) ;
break ;
case 'Stacked' :
multibar . stacked ( true ) ;
break ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode ) ;
} ) ;
//============================================================
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
multibar . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
multibar . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . multibar = multibar ;
chart . legend = legend ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
d3 . rebind ( chart , multibar , 'x' , 'y' , 'xDomain' , 'yDomain' , 'forceX' , 'forceY' , 'clipEdge' , 'id' , 'delay' , 'showValues' , 'valueFormat' , 'stacked' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
return chart ;
} ;
chart . showControls = function ( _ ) {
if ( ! arguments . length ) return showControls ;
showControls = _ ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . tooltip = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . multiBarHorizontal = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 960
, height = 500
, id = Math . floor ( Math . random ( ) * 10000 ) //Create semi-unique ID in case user doesn't select one
, x = d3 . scale . ordinal ( )
, y = d3 . scale . linear ( )
, getX = function ( d ) { return d . x }
, getY = function ( d ) { return d . y }
, forceY = [ 0 ] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
, color = nv . utils . defaultColor ( )
, stacked = false
, showValues = false
, valuePadding = 60
, valueFormat = d3 . format ( ',.2f' )
, delay = 1200
, xDomain
, yDomain
, dispatch = d3 . dispatch ( 'chartClick' , 'elementClick' , 'elementDblClick' , 'elementMouseover' , 'elementMouseout' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var x0 , y0 //used to store previous scales
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
if ( stacked )
data = d3 . layout . stack ( )
. offset ( 'zero' )
. values ( function ( d ) { return d . values } )
. y ( getY )
( data ) ;
//add series index to each data point for reference
data = data . map ( function ( series , i ) {
series . values = series . values . map ( function ( point ) {
point . series = i ;
return point ;
} ) ;
return series ;
} ) ;
//------------------------------------------------------------
// Setup Scales
// remap and flatten the data for use in calculating the scales' domains
var seriesData = ( xDomain && yDomain ) ? [ ] : // if we know xDomain and yDomain, no need to calculate
data . map ( function ( d ) {
return d . values . map ( function ( d , i ) {
return { x : getX ( d , i ) , y : getY ( d , i ) , y0 : d . y0 }
} )
} ) ;
x . domain ( xDomain || d3 . merge ( seriesData ) . map ( function ( d ) { return d . x } ) )
. rangeBands ( [ 0 , availableHeight ] , . 1 ) ;
y . domain ( yDomain || d3 . extent ( d3 . merge ( seriesData ) . map ( function ( d ) { return d . y + ( stacked ? d . y0 : 0 ) } ) . concat ( forceY ) ) )
if ( showValues && ! stacked )
y . range ( [ ( y . domain ( ) [ 0 ] < 0 ? valuePadding : 0 ) , availableWidth - ( y . domain ( ) [ 1 ] > 0 ? valuePadding : 0 ) ] ) ;
else
y . range ( [ 0 , availableWidth ] ) ;
x0 = x0 || x ;
y0 = y0 || d3 . scale . linear ( ) . domain ( y . domain ( ) ) . range ( [ y ( 0 ) , y ( 0 ) ] ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = d3 . select ( this ) . selectAll ( 'g.nv-wrap.nv-multibarHorizontal' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-multibarHorizontal' ) ;
var defsEnter = wrapEnter . append ( 'defs' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-groups' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
var groups = wrap . select ( '.nv-groups' ) . selectAll ( '.nv-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 'nv-group nv-series-' + i } )
. classed ( 'hover' , function ( d ) { return d . hover } )
. style ( 'fill' , function ( d , i ) { return color ( d , i ) } )
. style ( 'stroke' , function ( d , i ) { return color ( d , i ) } ) ;
d3 . transition ( groups )
. style ( 'stroke-opacity' , 1 )
. style ( 'fill-opacity' , . 75 ) ;
var bars = groups . selectAll ( 'g.nv-bar' )
. data ( function ( d ) { return d . values } ) ;
bars . exit ( ) . remove ( ) ;
var barsEnter = bars . enter ( ) . append ( 'g' )
. attr ( 'transform' , function ( d , i , j ) {
return 'translate(' + y0 ( stacked ? d . y0 : 0 ) + ',' + ( stacked ? 0 : ( j * x . rangeBand ( ) / data . length ) + x ( getX ( d , i ) ) ) + ')'
} ) ;
barsEnter . append ( 'rect' )
. attr ( 'width' , 0 )
. attr ( 'height' , x . rangeBand ( ) / ( stacked ? 1 : data . length ) )
bars
. on ( 'mouseover' , function ( d , i ) { //TODO: figure out why j works above, but not here
d3 . select ( this ) . classed ( 'hover' , true ) ;
dispatch . elementMouseover ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) , x ( getX ( d , i ) ) + ( x . rangeBand ( ) * ( stacked ? data . length / 2 : d . series + . 5 ) / data . length ) ] ,
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , false ) ;
dispatch . elementMouseout ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
} )
. on ( 'click' , function ( d , i ) {
dispatch . elementClick ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( x . rangeBand ( ) * ( stacked ? data . length / 2 : d . series + . 5 ) / data . length ) , y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
d3 . event . stopPropagation ( ) ;
} )
. on ( 'dblclick' , function ( d , i ) {
dispatch . elementDblClick ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( x . rangeBand ( ) * ( stacked ? data . length / 2 : d . series + . 5 ) / data . length ) , y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
d3 . event . stopPropagation ( ) ;
} ) ;
if ( showValues && ! stacked ) {
barsEnter . append ( 'text' )
. attr ( 'text-anchor' , function ( d , i ) { return getY ( d , i ) < 0 ? 'end' : 'start' } )
bars . select ( 'text' )
. attr ( 'y' , x . rangeBand ( ) / 2 )
. attr ( 'dy' , '-.32em' )
. text ( function ( d , i ) { return valueFormat ( getY ( d , i ) ) } )
d3 . transition ( bars )
//.delay(function(d,i) { return i * delay / data[0].values.length })
. select ( 'text' )
. attr ( 'x' , function ( d , i ) { return getY ( d , i ) < 0 ? - 4 : y ( getY ( d , i ) ) - y ( 0 ) + 4 } )
} else {
bars . selectAll ( 'text' ) . remove ( ) ;
}
bars
. attr ( 'class' , function ( d , i ) { return getY ( d , i ) < 0 ? 'nv-bar negative' : 'nv-bar positive' } )
//.attr('transform', function(d,i,j) {
//return 'translate(' + y0(stacked ? d.y0 : 0) + ',' + x(getX(d,i)) + ')'
//})
if ( stacked )
d3 . transition ( bars )
//.delay(function(d,i) { return i * delay / data[0].values.length })
. attr ( 'transform' , function ( d , i ) {
//return 'translate(' + y(d.y0) + ',0)'
return 'translate(' + y ( d . y0 ) + ',' + x ( getX ( d , i ) ) + ')'
} )
. select ( 'rect' )
. attr ( 'width' , function ( d , i ) {
return Math . abs ( y ( getY ( d , i ) + d . y0 ) - y ( d . y0 ) )
} )
. attr ( 'height' , x . rangeBand ( ) ) ;
else
d3 . transition ( bars )
//.delay(function(d,i) { return i * delay / data[0].values.length })
. attr ( 'transform' , function ( d , i ) {
//TODO: stacked must be all positive or all negative, not both?
return 'translate(' +
( getY ( d , i ) < 0 ? y ( getY ( d , i ) ) : y ( 0 ) )
+ ',' +
( d . series * x . rangeBand ( ) / data . length
+
x ( getX ( d , i ) ) )
+ ')'
} )
. select ( 'rect' )
. attr ( 'height' , x . rangeBand ( ) / data . length )
. attr ( 'width' , function ( d , i ) {
return Math . abs ( y ( getY ( d , i ) ) - y ( 0 ) )
} ) ;
//store old scales for use in transitions on update
x0 = x . copy ( ) ;
y0 = y . copy ( ) ;
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
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 . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . xScale = function ( _ ) {
if ( ! arguments . length ) return x ;
x = _ ;
return chart ;
} ;
chart . yScale = function ( _ ) {
if ( ! arguments . length ) return y ;
y = _ ;
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 . forceY = function ( _ ) {
if ( ! arguments . length ) return forceY ;
forceY = _ ;
return chart ;
} ;
chart . stacked = function ( _ ) {
if ( ! arguments . length ) return stacked ;
stacked = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
chart . delay = function ( _ ) {
if ( ! arguments . length ) return delay ;
delay = _ ;
return chart ;
} ;
chart . showValues = function ( _ ) {
if ( ! arguments . length ) return showValues ;
showValues = _ ;
return chart ;
} ;
chart . valueFormat = function ( _ ) {
if ( ! arguments . length ) return valueFormat ;
valueFormat = _ ;
return chart ;
} ;
chart . valuePadding = function ( _ ) {
if ( ! arguments . length ) return valuePadding ;
valuePadding = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . multiBar = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 960
, height = 500
, x = d3 . scale . ordinal ( )
, y = d3 . scale . linear ( )
, id = Math . floor ( Math . random ( ) * 10000 ) //Create semi-unique ID in case user doesn't select one
, getX = function ( d ) { return d . x }
, getY = function ( d ) { return d . y }
, forceY = [ 0 ] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
, clipEdge = true
, stacked = false
, color = nv . utils . defaultColor ( )
, delay = 1200
, xDomain
, yDomain
, dispatch = d3 . dispatch ( 'chartClick' , 'elementClick' , 'elementDblClick' , 'elementMouseover' , 'elementMouseout' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var x0 , y0 //used to store previous scales
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
if ( stacked )
data = d3 . layout . stack ( )
. offset ( 'zero' )
. values ( function ( d ) { return d . values } )
. y ( getY )
( data ) ;
//add series index to each data point for reference
data = data . map ( function ( series , i ) {
series . values = series . values . map ( function ( point ) {
point . series = i ;
return point ;
} ) ;
return series ;
} ) ;
//------------------------------------------------------------
// Setup Scales
// remap and flatten the data for use in calculating the scales' domains
var seriesData = ( xDomain && yDomain ) ? [ ] : // if we know xDomain and yDomain, no need to calculate
data . map ( function ( d ) {
return d . values . map ( function ( d , i ) {
return { x : getX ( d , i ) , y : getY ( d , i ) , y0 : d . y0 }
} )
} ) ;
x . domain ( d3 . merge ( seriesData ) . map ( function ( d ) { return d . x } ) )
. rangeBands ( [ 0 , availableWidth ] , . 1 ) ;
y . domain ( yDomain || d3 . extent ( d3 . merge ( seriesData ) . map ( function ( d ) { return d . y + ( stacked ? d . y0 : 0 ) } ) . concat ( forceY ) ) )
. range ( [ availableHeight , 0 ] ) ;
// If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
if ( x . domain ( ) [ 0 ] === x . domain ( ) [ 1 ] || y . domain ( ) [ 0 ] === y . domain ( ) [ 1 ] ) singlePoint = true ;
if ( x . domain ( ) [ 0 ] === x . domain ( ) [ 1 ] )
x . domain ( ) [ 0 ] ?
x . domain ( [ x . domain ( ) [ 0 ] - x . domain ( ) [ 0 ] * 0.01 , x . domain ( ) [ 1 ] + x . domain ( ) [ 1 ] * 0.01 ] )
: x . domain ( [ - 1 , 1 ] ) ;
if ( y . domain ( ) [ 0 ] === y . domain ( ) [ 1 ] )
y . domain ( ) [ 0 ] ?
y . domain ( [ y . domain ( ) [ 0 ] + y . domain ( ) [ 0 ] * 0.01 , y . domain ( ) [ 1 ] - y . domain ( ) [ 1 ] * 0.01 ] )
: y . domain ( [ - 1 , 1 ] ) ;
x0 = x0 || x ;
y0 = y0 || y ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-multibar' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-multibar' ) ;
var defsEnter = wrapEnter . append ( 'defs' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' )
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-groups' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
defsEnter . append ( 'clipPath' )
. attr ( 'id' , 'nv-edge-clip-' + id )
. append ( 'rect' ) ;
wrap . select ( '#nv-edge-clip-' + id + ' rect' )
. attr ( 'width' , availableWidth )
. attr ( 'height' , availableHeight ) ;
g . attr ( 'clip-path' , clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '' ) ;
var groups = wrap . select ( '.nv-groups' ) . selectAll ( '.nv-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)
. selectAll ( 'rect.nv-bar' )
. delay ( function ( d , i ) { return i * delay / data [ 0 ] . values . length } )
. attr ( 'y' , function ( d ) { return stacked ? y0 ( d . y0 ) : y0 ( 0 ) } )
. attr ( 'height' , 0 )
. remove ( ) ;
groups
. attr ( 'class' , function ( d , i ) { return 'nv-group nv-series-' + i } )
. classed ( 'hover' , function ( d ) { return d . hover } )
. style ( 'fill' , function ( d , i ) { return color ( d , i ) } )
. style ( 'stroke' , function ( d , i ) { return color ( d , i ) } ) ;
d3 . transition ( groups )
. style ( 'stroke-opacity' , 1 )
. style ( 'fill-opacity' , . 75 ) ;
var bars = groups . selectAll ( 'rect.nv-bar' )
. data ( function ( d ) { return d . values } ) ;
bars . exit ( ) . remove ( ) ;
var barsEnter = bars . enter ( ) . append ( 'rect' )
. attr ( 'class' , function ( d , i ) { return getY ( d , i ) < 0 ? 'nv-bar negative' : 'nv-bar positive' } )
. attr ( 'x' , function ( d , i , j ) {
return stacked ? 0 : ( j * x . rangeBand ( ) / data . length )
} )
. attr ( 'y' , function ( d ) { return y0 ( stacked ? d . y0 : 0 ) } )
. attr ( 'height' , 0 )
. attr ( 'width' , x . rangeBand ( ) / ( stacked ? 1 : data . length ) ) ;
bars
. on ( 'mouseover' , function ( d , i ) { //TODO: figure out why j works above, but not here
d3 . select ( this ) . classed ( 'hover' , true ) ;
dispatch . elementMouseover ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( x . rangeBand ( ) * ( stacked ? data . length / 2 : d . series + . 5 ) / data . length ) , y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , false ) ;
dispatch . elementMouseout ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
} )
. on ( 'click' , function ( d , i ) {
dispatch . elementClick ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( x . rangeBand ( ) * ( stacked ? data . length / 2 : d . series + . 5 ) / data . length ) , y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
d3 . event . stopPropagation ( ) ;
} )
. on ( 'dblclick' , function ( d , i ) {
dispatch . elementDblClick ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( x . rangeBand ( ) * ( stacked ? data . length / 2 : d . series + . 5 ) / data . length ) , y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
d3 . event . stopPropagation ( ) ;
} ) ;
bars
. attr ( 'class' , function ( d , i ) { return getY ( d , i ) < 0 ? 'nv-bar negative' : 'nv-bar positive' } )
. attr ( 'transform' , function ( d , i ) { return 'translate(' + x ( getX ( d , i ) ) + ',0)' ; } )
if ( stacked )
d3 . transition ( bars )
. delay ( function ( d , i ) { return i * delay / data [ 0 ] . values . length } )
. attr ( 'y' , function ( d , i ) {
return y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) ;
} )
. attr ( 'height' , function ( d , i ) {
return Math . abs ( y ( d . y + ( stacked ? d . y0 : 0 ) ) - y ( ( stacked ? d . y0 : 0 ) ) )
} )
. each ( 'end' , function ( ) {
d3 . transition ( d3 . select ( this ) )
. attr ( 'x' , function ( d , i ) {
return stacked ? 0 : ( d . series * x . rangeBand ( ) / data . length )
} )
. attr ( 'width' , x . rangeBand ( ) / ( stacked ? 1 : data . length ) ) ;
} )
else
d3 . transition ( bars )
. delay ( function ( d , i ) { return i * delay / data [ 0 ] . values . length } )
. attr ( 'x' , function ( d , i ) {
return d . series * x . rangeBand ( ) / data . length
} )
. attr ( 'width' , x . rangeBand ( ) / data . length )
. each ( 'end' , function ( ) {
d3 . transition ( d3 . select ( this ) )
. attr ( 'y' , function ( d , i ) {
return getY ( d , i ) < 0 ?
y ( 0 ) :
y ( getY ( d , i ) )
} )
. attr ( 'height' , function ( d , i ) {
return Math . abs ( y ( getY ( d , i ) ) - y ( 0 ) )
} ) ;
} )
//store old scales for use in transitions on update
x0 = x . copy ( ) ;
y0 = y . copy ( ) ;
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
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 . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . xScale = function ( _ ) {
if ( ! arguments . length ) return x ;
x = _ ;
return chart ;
} ;
chart . yScale = function ( _ ) {
if ( ! arguments . length ) return y ;
y = _ ;
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 . forceY = function ( _ ) {
if ( ! arguments . length ) return forceY ;
forceY = _ ;
return chart ;
} ;
chart . stacked = function ( _ ) {
if ( ! arguments . length ) return stacked ;
stacked = _ ;
return chart ;
} ;
chart . clipEdge = function ( _ ) {
if ( ! arguments . length ) return clipEdge ;
clipEdge = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
chart . delay = function ( _ ) {
if ( ! arguments . length ) return delay ;
delay = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . multiBarTimeSeriesChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var multibar = nv . models . multiBarTimeSeries ( )
, xAxis = nv . models . axis ( )
, yAxis = nv . models . axis ( )
, legend = nv . models . legend ( )
, controls = nv . models . legend ( )
;
var margin = { top : 30 , right : 20 , bottom : 50 , left : 60 }
, width = null
, height = null
, color = nv . utils . defaultColor ( )
, showControls = true
, showLegend = true
, reduceXTicks = true // if false a tick will show for every data point
, rotateLabels = 0
, tooltips = true
, tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' on ' + x + '</p>'
}
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
, noData = "No Data Available."
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
;
multibar
. stacked ( false )
;
xAxis
. orient ( 'bottom' )
. tickPadding ( 5 )
. highlightZero ( false )
. showMaxMin ( false )
;
yAxis
. orient ( 'left' )
. tickFormat ( d3 . format ( ',.1f' ) )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( multibar . x ( ) ( e . point , e . pointIndex ) ) ,
y = yAxis . tickFormat ( ) ( multibar . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , e . value < 0 ? 'n' : 's' , null , offsetElement ) ;
} ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { selection . transition ( ) . call ( chart ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display noData message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = multibar . xScale ( ) ;
y = multibar . yScale ( ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-multiBarWithLegend' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-multiBarWithLegend' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-barsWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-controlsWrap' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Legend
if ( showLegend ) {
legend . width ( availableWidth / 2 ) ;
g . select ( '.nv-legendWrap' )
. datum ( data )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
g . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(' + ( availableWidth / 2 ) + ',' + ( - margin . top ) + ')' ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Controls
if ( showControls ) {
var controlsData = [
{ key : 'Grouped' , disabled : multibar . stacked ( ) } ,
{ key : 'Stacked' , disabled : ! multibar . stacked ( ) }
] ;
controls . width ( 180 ) . color ( [ '#444' , '#444' , '#444' ] ) ;
g . select ( '.nv-controlsWrap' )
. datum ( controlsData )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
. call ( controls ) ;
}
//------------------------------------------------------------
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
// Main Chart Component(s)
multibar
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) )
var barsWrap = g . select ( '.nv-barsWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( barsWrap ) . call ( multibar ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Axes
xAxis
. scale ( x )
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' ) ;
d3 . transition ( g . select ( '.nv-x.nv-axis' ) )
. call ( xAxis ) ;
var xTicks = g . select ( '.nv-x.nv-axis > g' ) . selectAll ( 'g' ) ;
xTicks
. selectAll ( 'line, text' )
. style ( 'opacity' , 1 )
if ( reduceXTicks )
xTicks
. filter ( function ( d , i ) {
return i % Math . ceil ( data [ 0 ] . values . length / ( availableWidth / 100 ) ) !== 0 ;
} )
. selectAll ( 'text, line' )
. style ( 'opacity' , 0 ) ;
if ( rotateLabels )
xTicks
. selectAll ( 'text' )
. attr ( 'transform' , function ( d , i , j ) { return 'rotate(' + rotateLabels + ' 0,0)' } )
. attr ( 'text-transform' , rotateLabels > 0 ? 'start' : 'end' ) ;
yAxis
. scale ( y )
. ticks ( availableHeight / 36 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.nv-y.nv-axis' ) )
. call ( yAxis ) ;
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
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 ( '.nv-series' ) . classed ( 'disabled' , false ) ;
return d ;
} ) ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
controls . dispatch . on ( 'legendClick' , function ( d , i ) {
if ( ! d . disabled ) return ;
controlsData = controlsData . map ( function ( s ) {
s . disabled = true ;
return s ;
} ) ;
d . disabled = false ;
switch ( d . key ) {
case 'Grouped' :
multibar . stacked ( false ) ;
break ;
case 'Stacked' :
multibar . stacked ( true ) ;
break ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode )
} ) ;
//============================================================
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
multibar . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
multibar . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . multibar = multibar ;
chart . legend = legend ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
d3 . rebind ( chart , multibar , 'x' , 'y' , 'xDomain' , 'yDomain' , 'forceX' , 'forceY' , 'clipEdge' , 'id' , 'stacked' , 'delay' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
return chart ;
} ;
chart . showControls = function ( _ ) {
if ( ! arguments . length ) return showControls ;
showControls = _ ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . reduceXTicks = function ( _ ) {
if ( ! arguments . length ) return reduceXTicks ;
reduceXTicks = _ ;
return chart ;
} ;
chart . rotateLabels = function ( _ ) {
if ( ! arguments . length ) return rotateLabels ;
rotateLabels = _ ;
return chart ;
}
chart . tooltip = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . multiBarTimeSeries = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 960
, height = 500
, x = d3 . time . scale ( )
, y = d3 . scale . linear ( )
, id = Math . floor ( Math . random ( ) * 10000 ) //Create semi-unique ID in case user doesn't select one
, getX = function ( d ) { return d . x }
, getY = function ( d ) { return d . y }
, forceY = [ 0 ] // 0 is forced by default.. this makes sense for the majority of bar graphs... user can always do chart.forceY([]) to remove
, clipEdge = true
, stacked = false
, color = nv . utils . defaultColor ( )
, delay = 1200
, xDomain
, yDomain
, dispatch = d3 . dispatch ( 'chartClick' , 'elementClick' , 'elementDblClick' , 'elementMouseover' , 'elementMouseout' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var x0 , y0 //used to store previous scales
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
if ( stacked )
data = d3 . layout . stack ( )
. offset ( 'zero' )
. values ( function ( d ) { return d . values } )
. y ( getY )
( data ) ;
//add series index to each data point for reference
data = data . map ( function ( series , i ) {
series . values = series . values . map ( function ( point ) {
point . series = i ;
return point ;
} ) ;
return series ;
} ) ;
//------------------------------------------------------------
// Setup Scales
// remap and flatten the data for use in calculating the scales' domains
var seriesData = ( xDomain && yDomain ) ? [ ] : // if we know xDomain and yDomain, no need to calculate
data . map ( function ( d ) {
return d . values . map ( function ( d , i ) {
return { x : getX ( d , i ) , y : getY ( d , i ) , y0 : d . y0 }
} )
} ) ;
x . domain ( d3 . extent ( d3 . merge ( seriesData ) . map ( function ( d ) { return d . x } ) ) )
. range ( [ 0 , availableWidth ] ) ;
y . domain ( yDomain || d3 . extent ( d3 . merge ( seriesData ) . map ( function ( d ) { return d . y + ( stacked ? d . y0 : 0 ) } ) . concat ( forceY ) ) )
. range ( [ availableHeight , 0 ] ) ;
// If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
if ( x . domain ( ) [ 0 ] === x . domain ( ) [ 1 ] || y . domain ( ) [ 0 ] === y . domain ( ) [ 1 ] ) singlePoint = true ;
if ( x . domain ( ) [ 0 ] === x . domain ( ) [ 1 ] )
x . domain ( ) [ 0 ] ?
x . domain ( [ x . domain ( ) [ 0 ] - x . domain ( ) [ 0 ] * 0.01 , x . domain ( ) [ 1 ] + x . domain ( ) [ 1 ] * 0.01 ] )
: x . domain ( [ - 1 , 1 ] ) ;
if ( y . domain ( ) [ 0 ] === y . domain ( ) [ 1 ] )
y . domain ( ) [ 0 ] ?
y . domain ( [ y . domain ( ) [ 0 ] + y . domain ( ) [ 0 ] * 0.01 , y . domain ( ) [ 1 ] - y . domain ( ) [ 1 ] * 0.01 ] )
: y . domain ( [ - 1 , 1 ] ) ;
x0 = x0 || x ;
y0 = y0 || y ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-multibar' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-multibar' ) ;
var defsEnter = wrapEnter . append ( 'defs' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' )
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-groups' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
defsEnter . append ( 'clipPath' )
. attr ( 'id' , 'nv-edge-clip-' + id )
. append ( 'rect' ) ;
wrap . select ( '#nv-edge-clip-' + id + ' rect' )
. attr ( 'width' , availableWidth )
. attr ( 'height' , availableHeight ) ;
g . attr ( 'clip-path' , clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '' ) ;
var groups = wrap . select ( '.nv-groups' ) . selectAll ( '.nv-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)
. selectAll ( 'rect.nv-bar' )
. delay ( function ( d , i ) { return i * delay / data [ 0 ] . values . length } )
. attr ( 'y' , function ( d ) { return stacked ? y0 ( d . y0 ) : y0 ( 0 ) } )
. attr ( 'height' , 0 )
. remove ( ) ;
groups
. attr ( 'class' , function ( d , i ) { return 'nv-group nv-series-' + i } )
. classed ( 'hover' , function ( d ) { return d . hover } )
. style ( 'fill' , function ( d , i ) { return color ( d , i ) } )
. style ( 'stroke' , function ( d , i ) { return color ( d , i ) } ) ;
d3 . transition ( groups )
. style ( 'stroke-opacity' , 1 )
. style ( 'fill-opacity' , . 75 ) ;
var bars = groups . selectAll ( 'rect.nv-bar' )
. data ( function ( d ) { return d . values } ) ;
bars . exit ( ) . remove ( ) ;
var maxElements = 0 ;
for ( var ei = 0 ; ei < seriesData . length ; ei += 1 ) {
maxElements = Math . max ( seriesData [ ei ] . length , maxElements ) ;
}
var bandWidth = ( availableWidth / maxElements ) - 0.1 ;
var barWidth = bandWidth / data . length ;
var barsEnter = bars . enter ( ) . append ( 'rect' )
. attr ( 'class' , function ( d , i ) { return getY ( d , i ) < 0 ? 'nv-bar negative' : 'nv-bar positive' } )
. attr ( 'x' , function ( d , i , j ) {
return stacked ? 0 : ( i * bandWidth ) + ( j * barWidth )
} )
. attr ( 'y' , function ( d ) { return y0 ( stacked ? d . y0 : 0 ) } )
. attr ( 'height' , 0 )
. attr ( 'width' , stacked ? bandWidth : barWidth ) ;
bars
. on ( 'mouseover' , function ( d , i ) { //TODO: figure out why j works above, but not here
d3 . select ( this ) . classed ( 'hover' , true ) ;
dispatch . elementMouseover ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( barWidth * ( stacked ? data . length / 2 : d . series + . 5 ) / data . length ) , y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , false ) ;
dispatch . elementMouseout ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
} )
. on ( 'click' , function ( d , i ) {
dispatch . elementClick ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( barWidth * ( stacked ? data . length / 2 : d . series + . 5 ) / data . length ) , y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
d3 . event . stopPropagation ( ) ;
} )
. on ( 'dblclick' , function ( d , i ) {
dispatch . elementDblClick ( {
value : getY ( d , i ) ,
point : d ,
series : data [ d . series ] ,
pos : [ x ( getX ( d , i ) ) + ( barWidth * ( stacked ? data . length / 2 : d . series + . 5 ) / data . length ) , y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : d . series ,
e : d3 . event
} ) ;
d3 . event . stopPropagation ( ) ;
} ) ;
bars
. attr ( 'class' , function ( d , i ) { return getY ( d , i ) < 0 ? 'nv-bar negative' : 'nv-bar positive' } )
. attr ( 'transform' , function ( d , i ) { return 'translate(' + x ( getX ( d , i ) ) + ',0)' ; } )
if ( stacked )
d3 . transition ( bars )
. delay ( function ( d , i ) { return i * delay / data [ 0 ] . values . length } )
. attr ( 'y' , function ( d , i ) {
return y ( getY ( d , i ) + ( stacked ? d . y0 : 0 ) ) ;
} )
. attr ( 'height' , function ( d , i ) {
return Math . abs ( y ( d . y + ( stacked ? d . y0 : 0 ) ) - y ( ( stacked ? d . y0 : 0 ) ) )
} )
. each ( 'end' , function ( ) {
d3 . transition ( d3 . select ( this ) )
. attr ( 'x' , function ( d , i ) {
return stacked ? 0 : ( i * bandWidth ) + ( j * barWidth )
} )
. attr ( 'width' , stacked ? bandWidth : barWidth ) ;
} )
else
d3 . transition ( bars )
. delay ( function ( d , i ) { return i * delay / data [ 0 ] . values . length } )
. attr ( 'x' , function ( d , i ) {
return d . series * barWidth
} )
. attr ( 'width' , barWidth )
. each ( 'end' , function ( ) {
d3 . transition ( d3 . select ( this ) )
. attr ( 'y' , function ( d , i ) {
return getY ( d , i ) < 0 ?
y ( 0 ) :
y ( getY ( d , i ) )
} )
. attr ( 'height' , function ( d , i ) {
return Math . abs ( y ( getY ( d , i ) ) - y ( 0 ) )
} ) ;
} )
//store old scales for use in transitions on update
x0 = x . copy ( ) ;
y0 = y . copy ( ) ;
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
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 . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . xScale = function ( _ ) {
if ( ! arguments . length ) return x ;
x = _ ;
return chart ;
} ;
chart . yScale = function ( _ ) {
if ( ! arguments . length ) return y ;
y = _ ;
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 . forceY = function ( _ ) {
if ( ! arguments . length ) return forceY ;
forceY = _ ;
return chart ;
} ;
chart . stacked = function ( _ ) {
if ( ! arguments . length ) return stacked ;
stacked = _ ;
return chart ;
} ;
chart . clipEdge = function ( _ ) {
if ( ! arguments . length ) return clipEdge ;
clipEdge = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
chart . delay = function ( _ ) {
if ( ! arguments . length ) return delay ;
delay = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . multiChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 30 , right : 20 , bottom : 50 , left : 60 } ,
color = d3 . scale . category20 ( ) . range ( ) ,
width = null ,
height = null ,
showLegend = true ,
tooltips = true ,
tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' at ' + x + '</p>'
} ,
x , y ; //can be accessed via chart.lines.[x/y]Scale()
//============================================================
// Private Variables
//------------------------------------------------------------
var x = d3 . scale . linear ( ) ,
yScale1 = d3 . scale . linear ( ) ,
yScale2 = d3 . scale . linear ( ) ,
lines1 = nv . models . line ( ) . yScale ( yScale1 ) ,
lines2 = nv . models . line ( ) . yScale ( yScale2 ) ,
bars1 = nv . models . multiBar ( ) . stacked ( false ) . yScale ( yScale1 ) ,
bars2 = nv . models . multiBar ( ) . stacked ( false ) . yScale ( yScale2 ) ,
stack1 = nv . models . stackedArea ( ) . yScale ( yScale1 ) ,
stack2 = nv . models . stackedArea ( ) . yScale ( yScale2 ) ,
xAxis = nv . models . axis ( ) . scale ( x ) . orient ( 'bottom' ) . tickPadding ( 5 ) ,
yAxis1 = nv . models . axis ( ) . scale ( yScale1 ) . orient ( 'left' ) ,
yAxis2 = nv . models . axis ( ) . scale ( yScale2 ) . orient ( 'right' ) ,
legend = nv . models . legend ( ) . height ( 30 ) ,
dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' ) ;
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( lines1 . x ( ) ( e . point , e . pointIndex ) ) ,
y = ( e . series . bar ? yAxis1 : yAxis2 ) . tickFormat ( ) ( lines1 . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , undefined , undefined , offsetElement . offsetParent ) ;
} ;
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
var dataLines1 = data . filter ( function ( d ) { return ! d . disabled && d . type == 'line' && d . yAxis == 1 } )
var dataLines2 = data . filter ( function ( d ) { return ! d . disabled && d . type == 'line' && d . yAxis == 2 } )
var dataBars1 = data . filter ( function ( d ) { return ! d . disabled && d . type == 'bar' && d . yAxis == 1 } )
var dataBars2 = data . filter ( function ( d ) { return ! d . disabled && d . type == 'bar' && d . yAxis == 2 } )
var dataStack1 = data . filter ( function ( d ) { return ! d . disabled && d . type == 'area' && d . yAxis == 1 } )
var dataStack2 = data . filter ( function ( d ) { return ! d . disabled && d . type == 'area' && d . yAxis == 2 } )
var series1 = data . filter ( function ( d ) { return ! d . disabled && d . yAxis == 1 } )
. map ( function ( d ) {
return d . values . map ( function ( d , i ) {
return { x : d . x , y : d . y }
} )
} )
var series2 = data . filter ( function ( d ) { return ! d . disabled && d . yAxis == 2 } )
. map ( function ( d ) {
return d . values . map ( function ( d , i ) {
return { x : d . x , y : d . y }
} )
} )
x . domain ( d3 . extent ( d3 . merge ( series1 . concat ( series2 ) ) , function ( d ) { return d . x } ) )
. range ( [ 0 , availableWidth ] ) ;
var wrap = container . selectAll ( 'g.wrap.multiChart' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'wrap nvd3 multiChart' ) . append ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'x axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'y1 axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'y2 axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'lines1Wrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'lines2Wrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'bars1Wrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'bars2Wrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'stack1Wrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'stack2Wrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'legendWrap' ) ;
var g = wrap . select ( 'g' ) ;
if ( showLegend ) {
legend . width ( availableWidth / 2 ) ;
g . select ( '.legendWrap' )
. datum ( data . map ( function ( series ) {
series . originalKey = series . originalKey === undefined ? series . key : series . originalKey ;
series . key = series . originalKey + ( series . yAxis == 1 ? '' : ' (right axis)' ) ;
return series ;
} ) )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
g . select ( '.legendWrap' )
. attr ( 'transform' , 'translate(' + ( availableWidth / 2 ) + ',' + ( - margin . top ) + ')' ) ;
}
lines1
. width ( availableWidth )
. height ( availableHeight )
. interpolate ( "monotone" )
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % color . length ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled && data [ i ] . yAxis == 1 && data [ i ] . type == 'line' } ) ) ;
lines2
. width ( availableWidth )
. height ( availableHeight )
. interpolate ( "monotone" )
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % color . length ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled && data [ i ] . yAxis == 2 && data [ i ] . type == 'line' } ) ) ;
bars1
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % color . length ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled && data [ i ] . yAxis == 1 && data [ i ] . type == 'bar' } ) ) ;
bars2
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % color . length ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled && data [ i ] . yAxis == 2 && data [ i ] . type == 'bar' } ) ) ;
stack1
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % color . length ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled && data [ i ] . yAxis == 1 && data [ i ] . type == 'area' } ) ) ;
stack2
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color [ i % color . length ] ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled && data [ i ] . yAxis == 2 && data [ i ] . type == 'area' } ) ) ;
g . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
var lines1Wrap = g . select ( '.lines1Wrap' )
. datum ( dataLines1 )
var bars1Wrap = g . select ( '.bars1Wrap' )
. datum ( dataBars1 )
var stack1Wrap = g . select ( '.stack1Wrap' )
. datum ( dataStack1 )
var lines2Wrap = g . select ( '.lines2Wrap' )
. datum ( dataLines2 )
var bars2Wrap = g . select ( '.bars2Wrap' )
. datum ( dataBars2 )
var stack2Wrap = g . select ( '.stack2Wrap' )
. datum ( dataStack2 )
var extraValue1 = dataStack1 . length ? dataStack1 . map ( function ( a ) { return a . values } ) . reduce ( function ( a , b ) {
return a . map ( function ( aVal , i ) { return { x : aVal . x , y : aVal . y + b [ i ] . y } } )
} ) . concat ( [ { x : 0 , y : 0 } ] ) : [ ]
var extraValue2 = dataStack2 . length ? dataStack2 . map ( function ( a ) { return a . values } ) . reduce ( function ( a , b ) {
return a . map ( function ( aVal , i ) { return { x : aVal . x , y : aVal . y + b [ i ] . y } } )
} ) . concat ( [ { x : 0 , y : 0 } ] ) : [ ]
yScale1 . domain ( d3 . extent ( d3 . merge ( series1 ) . concat ( extraValue1 ) , function ( d ) { return d . y } ) )
. range ( [ 0 , availableHeight ] )
yScale2 . domain ( d3 . extent ( d3 . merge ( series2 ) . concat ( extraValue2 ) , function ( d ) { return d . y } ) )
. range ( [ 0 , availableHeight ] )
lines1 . yDomain ( yScale1 . domain ( ) )
bars1 . yDomain ( yScale1 . domain ( ) )
stack1 . yDomain ( yScale1 . domain ( ) )
lines2 . yDomain ( yScale2 . domain ( ) )
bars2 . yDomain ( yScale2 . domain ( ) )
stack2 . yDomain ( yScale2 . domain ( ) )
if ( dataStack1 . length ) { d3 . transition ( stack1Wrap ) . call ( stack1 ) ; }
if ( dataStack2 . length ) { d3 . transition ( stack2Wrap ) . call ( stack2 ) ; }
if ( dataBars1 . length ) { d3 . transition ( bars1Wrap ) . call ( bars1 ) ; }
if ( dataBars2 . length ) { d3 . transition ( bars2Wrap ) . call ( bars2 ) ; }
if ( dataLines1 . length ) { d3 . transition ( lines1Wrap ) . call ( lines1 ) ; }
if ( dataLines2 . length ) { d3 . transition ( lines2Wrap ) . call ( lines2 ) ; }
xAxis
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.x.axis' )
. attr ( 'transform' , 'translate(0,' + availableHeight + ')' ) ;
d3 . transition ( g . select ( '.x.axis' ) )
. call ( xAxis ) ;
yAxis1
. ticks ( availableHeight / 36 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.y1.axis' ) )
. call ( yAxis1 ) ;
yAxis2
. ticks ( availableHeight / 36 )
. tickSize ( - availableWidth , 0 ) ;
d3 . transition ( g . select ( '.y2.axis' ) )
. call ( yAxis2 ) ;
g . select ( '.y2.axis' )
. style ( 'opacity' , series2 . length ? 1 : 0 )
. attr ( 'transform' , 'translate(' + x . range ( ) [ 1 ] + ',0)' ) ;
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 ) ;
} ) ;
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode ) ;
} ) ;
} ) ;
chart . update = function ( ) { chart ( selection ) } ;
chart . container = this ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
lines1 . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
lines1 . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
lines2 . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
lines2 . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
bars1 . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
bars1 . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
bars2 . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
bars2 . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
stack1 . dispatch . on ( 'tooltipShow' , function ( e ) {
//disable tooltips when value ~= 0
//// TODO: consider removing points from voronoi that have 0 value instead of this hack
if ( ! Math . round ( stack1 . y ( ) ( e . point ) * 100 ) ) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
setTimeout ( function ( ) { d3 . selectAll ( '.point.hover' ) . classed ( 'hover' , false ) } , 0 ) ;
return false ;
}
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ,
dispatch . tooltipShow ( e ) ;
} ) ;
stack1 . dispatch . on ( 'tooltipHide' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
stack2 . dispatch . on ( 'tooltipShow' , function ( e ) {
//disable tooltips when value ~= 0
//// TODO: consider removing points from voronoi that have 0 value instead of this hack
if ( ! Math . round ( stack2 . y ( ) ( e . point ) * 100 ) ) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
setTimeout ( function ( ) { d3 . selectAll ( '.point.hover' ) . classed ( 'hover' , false ) } , 0 ) ;
return false ;
}
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ,
dispatch . tooltipShow ( e ) ;
} ) ;
stack2 . dispatch . on ( 'tooltipHide' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
lines1 . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
lines1 . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
lines2 . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
lines2 . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
// Global getters and setters
//------------------------------------------------------------
chart . dispatch = dispatch ;
chart . lines1 = lines1 ;
chart . lines2 = lines2 ;
chart . bars1 = bars1 ;
chart . bars2 = bars2 ;
chart . stack1 = stack1 ;
chart . stack2 = stack2 ;
chart . xAxis = xAxis ;
chart . yAxis1 = yAxis1 ;
chart . yAxis2 = yAxis2 ;
chart . x = function ( _ ) {
if ( ! arguments . length ) return getX ;
getX = _ ;
lines1 . x ( _ ) ;
bars1 . x ( _ ) ;
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return getY ;
getY = _ ;
lines1 . y ( _ ) ;
bars1 . 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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = _ ;
legend . color ( _ ) ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
return chart ;
}
nv . models . ohlcBar = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 960
, height = 500
, id = Math . floor ( Math . random ( ) * 10000 ) //Create semi-unique ID in case user doesn't select one
, x = d3 . scale . linear ( )
, y = d3 . scale . linear ( )
, getX = function ( d ) { return d . x }
, getY = function ( d ) { return d . y }
, getOpen = function ( d ) { return d . open }
, getClose = function ( d ) { return d . close }
, getHigh = function ( d ) { return d . high }
, getLow = function ( d ) { return d . low }
, forceX = [ ]
, forceY = [ ]
, clipEdge = true
, color = nv . utils . defaultColor ( )
, xDomain
, yDomain
, dispatch = d3 . dispatch ( 'chartClick' , 'elementClick' , 'elementDblClick' , 'elementMouseover' , 'elementMouseout' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
//TODO: store old scales for transitions
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
//------------------------------------------------------------
// Setup Scales
x . domain ( xDomain || d3 . extent ( data [ 0 ] . values . map ( getX ) . concat ( forceX ) ) )
. range ( [ 0 , availableWidth ] ) ;
y . domain ( yDomain || [
d3 . min ( data [ 0 ] . values . map ( getLow ) . concat ( forceY ) ) ,
d3 . max ( data [ 0 ] . values . map ( getHigh ) . concat ( forceY ) )
] )
. range ( [ availableHeight , 0 ] ) ;
// If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
if ( x . domain ( ) [ 0 ] === x . domain ( ) [ 1 ] || y . domain ( ) [ 0 ] === y . domain ( ) [ 1 ] ) singlePoint = true ;
if ( x . domain ( ) [ 0 ] === x . domain ( ) [ 1 ] )
x . domain ( ) [ 0 ] ?
x . domain ( [ x . domain ( ) [ 0 ] - x . domain ( ) [ 0 ] * 0.01 , x . domain ( ) [ 1 ] + x . domain ( ) [ 1 ] * 0.01 ] )
: x . domain ( [ - 1 , 1 ] ) ;
if ( y . domain ( ) [ 0 ] === y . domain ( ) [ 1 ] )
y . domain ( ) [ 0 ] ?
y . domain ( [ y . domain ( ) [ 0 ] + y . domain ( ) [ 0 ] * 0.01 , y . domain ( ) [ 1 ] - y . domain ( ) [ 1 ] * 0.01 ] )
: y . domain ( [ - 1 , 1 ] ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = d3 . select ( this ) . selectAll ( 'g.nv-wrap.nv-ohlcBar' ) . data ( [ data [ 0 ] . values ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-ohlcBar' ) ;
var defsEnter = wrapEnter . append ( 'defs' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-ticks' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
container
. on ( 'click' , function ( d , i ) {
dispatch . chartClick ( {
data : d ,
index : i ,
pos : d3 . event ,
id : id
} ) ;
} ) ;
defsEnter . append ( 'clipPath' )
. attr ( 'id' , 'nv-chart-clip-path-' + id )
. append ( 'rect' ) ;
wrap . select ( '#nv-chart-clip-path-' + id + ' rect' )
. attr ( 'width' , availableWidth )
. attr ( 'height' , availableHeight ) ;
g . attr ( 'clip-path' , clipEdge ? 'url(#nv-chart-clip-path-' + id + ')' : '' ) ;
var ticks = wrap . select ( '.nv-ticks' ) . selectAll ( '.nv-tick' )
. data ( function ( d ) { return d } ) ;
ticks . exit ( ) . remove ( ) ;
var ticksEnter = ticks . enter ( ) . append ( 'path' )
. attr ( 'class' , function ( d , i , j ) { return ( getOpen ( d , i ) > getClose ( d , i ) ? 'nv-tick negative' : 'nv-tick positive' ) + ' nv-tick-' + j + '-' + i } )
. attr ( 'd' , function ( d , i ) {
var w = ( availableWidth / data [ 0 ] . values . length ) * . 9 ;
return 'm0,0l0,'
+ ( y ( getOpen ( d , i ) )
- y ( getHigh ( d , i ) ) )
+ 'l'
+ ( - w / 2 )
+ ',0l'
+ ( w / 2 )
+ ',0l0,'
+ ( y ( getLow ( d , i ) ) - y ( getOpen ( d , i ) ) )
+ 'l0,'
+ ( y ( getClose ( d , i ) )
- y ( getLow ( d , i ) ) )
+ 'l'
+ ( w / 2 )
+ ',0l'
+ ( - w / 2 )
+ ',0z' ;
} )
. attr ( 'transform' , function ( d , i ) { return 'translate(' + x ( getX ( d , i ) ) + ',' + y ( getHigh ( d , i ) ) + ')' ; } )
//.attr('fill', function(d,i) { return color[0]; })
//.attr('stroke', function(d,i) { return color[0]; })
//.attr('x', 0 )
//.attr('y', function(d,i) { return y(Math.max(0, getY(d,i))) })
//.attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) })
. on ( 'mouseover' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , true ) ;
dispatch . elementMouseover ( {
point : d ,
series : data [ 0 ] ,
pos : [ x ( getX ( d , i ) ) , y ( getY ( d , i ) ) ] , // TODO: Figure out why the value appears to be shifted
pointIndex : i ,
seriesIndex : 0 ,
e : d3 . event
} ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , false ) ;
dispatch . elementMouseout ( {
point : d ,
series : data [ 0 ] ,
pointIndex : i ,
seriesIndex : 0 ,
e : d3 . event
} ) ;
} )
. on ( 'click' , function ( d , i ) {
dispatch . elementClick ( {
//label: d[label],
value : getY ( d , i ) ,
data : d ,
index : i ,
pos : [ x ( getX ( d , i ) ) , y ( getY ( d , i ) ) ] ,
e : d3 . event ,
id : id
} ) ;
d3 . event . stopPropagation ( ) ;
} )
. on ( 'dblclick' , function ( d , i ) {
dispatch . elementDblClick ( {
//label: d[label],
value : getY ( d , i ) ,
data : d ,
index : i ,
pos : [ x ( getX ( d , i ) ) , y ( getY ( d , i ) ) ] ,
e : d3 . event ,
id : id
} ) ;
d3 . event . stopPropagation ( ) ;
} ) ;
ticks
. attr ( 'class' , function ( d , i , j ) { return ( getOpen ( d , i ) > getClose ( d , i ) ? 'nv-tick negative' : 'nv-tick positive' ) + ' nv-tick-' + j + '-' + i } )
d3 . transition ( ticks )
. attr ( 'transform' , function ( d , i ) { return 'translate(' + x ( getX ( d , i ) ) + ',' + y ( getHigh ( d , i ) ) + ')' ; } )
. attr ( 'd' , function ( d , i ) {
var w = ( availableWidth / data [ 0 ] . values . length ) * . 9 ;
return 'm0,0l0,'
+ ( y ( getOpen ( d , i ) )
- y ( getHigh ( d , i ) ) )
+ 'l'
+ ( - w / 2 )
+ ',0l'
+ ( w / 2 )
+ ',0l0,'
+ ( y ( getLow ( d , i ) )
- y ( getOpen ( d , i ) ) )
+ 'l0,'
+ ( y ( getClose ( d , i ) )
- y ( getLow ( d , i ) ) )
+ 'l'
+ ( w / 2 )
+ ',0l'
+ ( - w / 2 )
+ ',0z' ;
} )
//.attr('width', (availableWidth / data[0].values.length) * .9 )
//d3.transition(ticks)
//.attr('y', function(d,i) { return y(Math.max(0, getY(d,i))) })
//.attr('height', function(d,i) { return Math.abs(y(getY(d,i)) - y(0)) });
//.order(); // not sure if this makes any sense for this model
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
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 . open = function ( _ ) {
if ( ! arguments . length ) return getOpen ;
getOpen = _ ;
return chart ;
} ;
chart . close = function ( _ ) {
if ( ! arguments . length ) return getClose ;
getClose = _ ;
return chart ;
} ;
chart . high = function ( _ ) {
if ( ! arguments . length ) return getHigh ;
getHigh = _ ;
return chart ;
} ;
chart . low = function ( _ ) {
if ( ! arguments . length ) return getLow ;
getLow = _ ;
return chart ;
} ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . xScale = function ( _ ) {
if ( ! arguments . length ) return x ;
x = _ ;
return chart ;
} ;
chart . yScale = function ( _ ) {
if ( ! arguments . length ) return y ;
y = _ ;
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 . forceX = function ( _ ) {
if ( ! arguments . length ) return forceX ;
forceX = _ ;
return chart ;
} ;
chart . forceY = function ( _ ) {
if ( ! arguments . length ) return forceY ;
forceY = _ ;
return chart ;
} ;
chart . clipEdge = function ( _ ) {
if ( ! arguments . length ) return clipEdge ;
clipEdge = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . pieChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var pie = nv . models . pie ( )
, legend = nv . models . legend ( )
;
var margin = { top : 30 , right : 20 , bottom : 20 , left : 20 }
, width = null
, height = null
, showLegend = true
, color = nv . utils . defaultColor ( )
, tooltips = true
, tooltip = function ( key , y , e , graph ) {
return '<h3>' + key + '</h3>' +
'<p>' + y + '</p>'
}
, noData = "No Data Available."
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( ( offsetElement && offsetElement . offsetLeft ) || 0 ) ,
top = e . pos [ 1 ] + ( ( offsetElement && offsetElement . offsetTop ) || 0 ) ,
y = pie . valueFormat ( ) ( pie . y ( ) ( e . point ) ) ,
content = tooltip ( pie . x ( ) ( e . point ) , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , e . value < 0 ? 'n' : 's' , null , offsetElement ) ;
} ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { chart ( selection ) ; } ;
chart . container = this ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
if ( ! data || ! data . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-pieChart' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-pieChart' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-pieWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Legend
if ( showLegend ) {
legend
. width ( availableWidth )
. key ( pie . x ( ) ) ;
wrap . select ( '.nv-legendWrap' )
. datum ( pie . values ( ) ( data [ 0 ] ) )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
wrap . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' ) ;
}
//------------------------------------------------------------
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
// Main Chart Component(s)
pie
. width ( availableWidth )
. height ( availableHeight ) ;
var pieWrap = g . select ( '.nv-pieWrap' )
. datum ( data ) ;
d3 . transition ( pieWrap ) . call ( pie ) ;
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
legend . dispatch . on ( 'legendClick' , function ( d , i , that ) {
d . disabled = ! d . disabled ;
if ( ! pie . values ( ) ( data [ 0 ] ) . filter ( function ( d ) { return ! d . disabled } ) . length ) {
pie . values ( ) ( data [ 0 ] ) . map ( function ( d ) {
d . disabled = false ;
wrap . selectAll ( '.nv-series' ) . classed ( 'disabled' , false ) ;
return d ;
} ) ;
}
selection . transition ( ) . call ( chart )
} ) ;
pie . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
//============================================================
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
pie . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . pie = pie ;
d3 . rebind ( chart , pie , 'valueFormat' , 'values' , 'x' , 'y' , 'id' , 'showLabels' , 'donutLabelsOutside' , 'donut' , 'labelThreshold' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
pie . color ( color ) ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . pie = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 500
, height = 500
, getValues = function ( d ) { return d . values }
, getX = function ( d ) { return d . x }
, getY = function ( d ) { return d . y }
, id = Math . floor ( Math . random ( ) * 10000 ) //Create semi-unique ID in case user doesn't select one
, color = nv . utils . defaultColor ( )
, valueFormat = d3 . format ( ',.2f' )
, showLabels = true
, donutLabelsOutside = false
, labelThreshold = . 02 //if slice percentage is under this, don't show label
, donut = false
, dispatch = d3 . dispatch ( 'chartClick' , 'elementClick' , 'elementDblClick' , 'elementMouseover' , 'elementMouseout' )
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
radius = Math . min ( availableWidth , availableHeight ) / 2 ,
container = d3 . select ( this ) ;
//------------------------------------------------------------
// Setup containers and skeleton of chart
//var wrap = container.selectAll('.nv-wrap.nv-pie').data([data]);
var wrap = container . selectAll ( '.nv-wrap.nv-pie' ) . data ( [ getValues ( data [ 0 ] ) ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-pie nv-chart-' + id ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-pie' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
g . select ( '.nv-pie' ) . attr ( 'transform' , 'translate(' + availableWidth / 2 + ',' + availableHeight / 2 + ')' ) ;
//------------------------------------------------------------
container
. on ( 'click' , function ( d , i ) {
dispatch . chartClick ( {
data : d ,
index : i ,
pos : d3 . event ,
id : id
} ) ;
} ) ;
var arc = d3 . svg . arc ( )
. outerRadius ( ( radius - ( radius / 5 ) ) ) ;
if ( donut ) arc . innerRadius ( radius / 2 ) ;
// Setup the Pie chart and choose the data element
var pie = d3 . layout . pie ( )
. sort ( null )
. value ( function ( d ) { return d . disabled ? 0 : getY ( d ) } ) ;
var slices = wrap . select ( '.nv-pie' ) . selectAll ( '.nv-slice' )
. data ( pie ) ;
slices . exit ( ) . remove ( ) ;
var ae = slices . enter ( ) . append ( 'g' )
. attr ( 'class' , 'nv-slice' )
. on ( 'mouseover' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , true ) ;
dispatch . elementMouseover ( {
label : getX ( d . data ) ,
value : getY ( d . data ) ,
point : d . data ,
pointIndex : i ,
pos : [ d3 . event . pageX , d3 . event . pageY ] ,
id : id
} ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , false ) ;
dispatch . elementMouseout ( {
label : getX ( d . data ) ,
value : getY ( d . data ) ,
point : d . data ,
index : i ,
id : id
} ) ;
} )
. on ( 'click' , function ( d , i ) {
dispatch . elementClick ( {
label : getX ( d . data ) ,
value : getY ( d . data ) ,
point : d . data ,
index : i ,
pos : d3 . event ,
id : id
} ) ;
d3 . event . stopPropagation ( ) ;
} )
. on ( 'dblclick' , function ( d , i ) {
dispatch . elementDblClick ( {
label : getX ( d . data ) ,
value : getY ( d . data ) ,
point : d . data ,
index : i ,
pos : d3 . event ,
id : id
} ) ;
d3 . event . stopPropagation ( ) ;
} ) ;
slices
. attr ( 'fill' , function ( d , i ) { return color ( d , i ) ; } )
. attr ( 'stroke' , function ( d , i ) { return color ( d , i ) ; } ) ;
var paths = ae . append ( 'path' )
. each ( function ( d ) { this . _current = d ; } ) ;
//.attr('d', arc);
d3 . transition ( slices . select ( 'path' ) )
. attr ( 'd' , arc )
. attrTween ( 'd' , arcTween ) ;
if ( showLabels ) {
// This does the normal label
var labelsArc = arc ;
if ( donutLabelsOutside ) {
labelsArc = d3 . svg . arc ( ) . outerRadius ( arc . outerRadius ( ) )
}
ae . append ( "g" ) . classed ( "nv-label" , true )
. each ( function ( d , i ) {
var group = d3 . select ( this ) ;
group
. attr ( 'transform' , function ( d ) {
d . outerRadius = radius + 10 ; // Set Outer Coordinate
d . innerRadius = radius + 15 ; // Set Inner Coordinate
return 'translate(' + labelsArc . centroid ( d ) + ')'
} ) ;
group . append ( 'rect' )
. style ( 'stroke' , '#fff' )
. style ( 'fill' , '#fff' )
. attr ( "rx" , 3 )
. attr ( "ry" , 3 ) ;
group . append ( 'text' )
. style ( 'text-anchor' , 'middle' ) //center the text on it's origin
. style ( 'fill' , '#000' )
} ) ;
slices . select ( ".nv-label" ) . transition ( )
. attr ( 'transform' , function ( d ) {
d . outerRadius = radius + 10 ; // Set Outer Coordinate
d . innerRadius = radius + 15 ; // Set Inner Coordinate
return 'translate(' + labelsArc . centroid ( d ) + ')' ;
} ) ;
slices . each ( function ( d , i ) {
var slice = d3 . select ( this ) ;
slice
. select ( ".nv-label text" )
. text ( function ( d , i ) {
var percent = ( d . endAngle - d . startAngle ) / ( 2 * Math . PI ) ;
return ( d . value && percent > labelThreshold ) ? getX ( d . data ) : '' ;
} ) ;
var textBox = slice . select ( 'text' ) . node ( ) . getBBox ( ) ;
slice . select ( ".nv-label rect" )
. attr ( "width" , textBox . width + 10 )
. attr ( "height" , textBox . height + 10 )
. attr ( "transform" , function ( ) {
return "translate(" + [ textBox . x - 5 , textBox . y - 5 ] + ")" ;
} ) ;
} ) ;
}
// Computes the angle of an arc, converting from radians to degrees.
function angle ( d ) {
var a = ( d . startAngle + d . endAngle ) * 90 / Math . PI - 90 ;
return a > 90 ? a - 180 : a ;
}
function arcTween ( a ) {
if ( ! donut ) a . innerRadius = 0 ;
var i = d3 . interpolate ( this . _current , a ) ;
this . _current = i ( 0 ) ;
return function ( t ) {
return arc ( i ( t ) ) ;
} ;
}
function tweenPie ( b ) {
b . innerRadius = 0 ;
var i = d3 . interpolate ( { startAngle : 0 , endAngle : 0 } , b ) ;
return function ( t ) {
return arc ( i ( t ) ) ;
} ;
}
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
chart . dispatch = dispatch ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . values = function ( _ ) {
if ( ! arguments . length ) return getValues ;
getValues = _ ;
return chart ;
} ;
chart . x = function ( _ ) {
if ( ! arguments . length ) return getX ;
getX = _ ;
return chart ;
} ;
chart . y = function ( _ ) {
if ( ! arguments . length ) return getY ;
getY = d3 . functor ( _ ) ;
return chart ;
} ;
chart . showLabels = function ( _ ) {
if ( ! arguments . length ) return showLabels ;
showLabels = _ ;
return chart ;
} ;
chart . donutLabelsOutside = function ( _ ) {
if ( ! arguments . length ) return donutLabelsOutside ;
donutLabelsOutside = _ ;
return chart ;
} ;
chart . donut = function ( _ ) {
if ( ! arguments . length ) return donut ;
donut = _ ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
chart . valueFormat = function ( _ ) {
if ( ! arguments . length ) return valueFormat ;
valueFormat = _ ;
return chart ;
} ;
chart . labelThreshold = function ( _ ) {
if ( ! arguments . length ) return labelThreshold ;
labelThreshold = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . scatterChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var scatter = nv . models . scatter ( )
, xAxis = nv . models . axis ( )
, yAxis = nv . models . axis ( )
, legend = nv . models . legend ( )
, controls = nv . models . legend ( )
, distX = nv . models . distribution ( )
, distY = nv . models . distribution ( )
;
var margin = { top : 30 , right : 20 , bottom : 50 , left : 60 }
, width = null
, height = null
, color = nv . utils . defaultColor ( )
, x = d3 . fisheye ? d3 . fisheye . scale ( d3 . scale . linear ) . distortion ( 0 ) : scatter . xScale ( )
, y = d3 . fisheye ? d3 . fisheye . scale ( d3 . scale . linear ) . distortion ( 0 ) : scatter . yScale ( )
, showDistX = false
, showDistY = false
, showLegend = true
, showControls = ! ! d3 . fisheye
, fisheye = 0
, pauseFisheye = false
, tooltips = true
, tooltipX = function ( key , x , y ) { return '<strong>' + x + '</strong>' }
, tooltipY = function ( key , x , y ) { return '<strong>' + y + '</strong>' }
//, tooltip = function(key, x, y) { return '<h3>' + key + '</h3>' }
, tooltip = null
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
, noData = "No Data Available."
;
scatter
. xScale ( x )
. yScale ( y )
;
xAxis
. orient ( 'bottom' )
. tickPadding ( 10 )
;
yAxis
. orient ( 'left' )
. tickPadding ( 10 )
;
distX
. axis ( 'x' )
;
distY
. axis ( 'y' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var x0 , y0 ;
var showTooltip = function ( e , offsetElement ) {
//TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?)
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
leftX = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
topX = y . range ( ) [ 0 ] + margin . top + ( offsetElement . offsetTop || 0 ) ,
leftY = x . range ( ) [ 0 ] + margin . left + ( offsetElement . offsetLeft || 0 ) ,
topY = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
xVal = xAxis . tickFormat ( ) ( scatter . x ( ) ( e . point , e . pointIndex ) ) ,
yVal = yAxis . tickFormat ( ) ( scatter . y ( ) ( e . point , e . pointIndex ) ) ;
if ( tooltipX != null )
nv . tooltip . show ( [ leftX , topX ] , tooltipX ( e . series . key , xVal , yVal , e , chart ) , 'n' , 1 , offsetElement , 'x-nvtooltip' ) ;
if ( tooltipY != null )
nv . tooltip . show ( [ leftY , topY ] , tooltipY ( e . series . key , xVal , yVal , e , chart ) , 'e' , 1 , offsetElement , 'y-nvtooltip' ) ;
if ( tooltip != null )
nv . tooltip . show ( [ left , top ] , tooltip ( e . series . key , xVal , yVal , e , chart ) , e . value < 0 ? 'n' : 's' , null , offsetElement ) ;
} ;
var controlsData = [
{ key : 'Magnify' , disabled : true }
] ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { chart ( selection ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display noData message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = scatter . xScale ( ) ;
y = scatter . yScale ( ) ;
x0 = x0 || x ;
y0 = y0 || y ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-scatterChart' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-scatterChart nv-chart-' + scatter . id ( ) ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' )
// background for pointer events
gEnter . append ( 'rect' ) . attr ( 'class' , 'nvd3 nv-background' )
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-scatterWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-distWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-controlsWrap' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Legend
if ( showLegend ) {
legend . width ( availableWidth / 2 ) ;
wrap . select ( '.nv-legendWrap' )
. datum ( data )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
wrap . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(' + ( availableWidth / 2 ) + ',' + ( - margin . top ) + ')' ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Controls
if ( showControls ) {
controls . width ( 180 ) . color ( [ '#444' ] ) ;
g . select ( '.nv-controlsWrap' )
. datum ( controlsData )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
. call ( controls ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Main Chart Component(s)
scatter
. width ( availableWidth )
. height ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) )
wrap . select ( '.nv-scatterWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
. call ( scatter ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Axes
xAxis
. scale ( x )
. ticks ( xAxis . ticks ( ) ? xAxis . ticks ( ) : availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' )
. call ( xAxis ) ;
yAxis
. scale ( y )
. ticks ( yAxis . ticks ( ) ? yAxis . ticks ( ) : availableHeight / 36 )
. tickSize ( - availableWidth , 0 ) ;
g . select ( '.nv-y.nv-axis' )
. call ( yAxis ) ;
if ( showDistX ) {
distX
. getData ( scatter . x ( ) )
. scale ( x )
. width ( availableWidth )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) ) ;
gEnter . select ( '.nv-distWrap' ) . append ( 'g' )
. attr ( 'class' , 'nv-distributionX' ) ;
g . select ( '.nv-distributionX' )
. attr ( 'transform' , 'translate(0,' + y . range ( ) [ 0 ] + ')' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
. call ( distX ) ;
}
if ( showDistY ) {
distY
. getData ( scatter . y ( ) )
. scale ( y )
. width ( availableHeight )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) ) ;
gEnter . select ( '.nv-distWrap' ) . append ( 'g' )
. attr ( 'class' , 'nv-distributionY' ) ;
g . select ( '.nv-distributionY' )
. attr ( 'transform' , 'translate(-' + distY . size ( ) + ',0)' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
. call ( distY ) ;
}
//------------------------------------------------------------
if ( d3 . fisheye ) {
g . select ( '.nv-background' )
. attr ( 'width' , availableWidth )
. attr ( 'height' , availableHeight ) ;
g . select ( '.nv-background' ) . on ( 'mousemove' , updateFisheye ) ;
g . select ( '.nv-background' ) . on ( 'click' , function ( ) { pauseFisheye = ! pauseFisheye ; } ) ;
scatter . dispatch . on ( 'elementClick.freezeFisheye' , function ( ) {
pauseFisheye = ! pauseFisheye ;
} ) ;
}
function updateFisheye ( ) {
if ( pauseFisheye ) {
g . select ( '.nv-point-paths' ) . style ( 'pointer-events' , 'all' ) ;
return false ;
}
g . select ( '.nv-point-paths' ) . style ( 'pointer-events' , 'none' ) ;
var mouse = d3 . mouse ( this ) ;
x . distortion ( fisheye ) . focus ( mouse [ 0 ] ) ;
y . distortion ( fisheye ) . focus ( mouse [ 1 ] ) ;
g . select ( '.nv-scatterWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
. call ( scatter ) ;
g . select ( '.nv-x.nv-axis' ) . call ( xAxis ) ;
g . select ( '.nv-y.nv-axis' ) . call ( yAxis ) ;
g . select ( '.nv-distributionX' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
. call ( distX ) ;
g . select ( '.nv-distributionY' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
. call ( distY ) ;
}
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
controls . dispatch . on ( 'legendClick' , function ( d , i ) {
d . disabled = ! d . disabled ;
fisheye = d . disabled ? 0 : 2.5 ;
g . select ( '.nv-background' ) . style ( 'pointer-events' , d . disabled ? 'none' : 'all' ) ;
g . select ( '.nv-point-paths' ) . style ( 'pointer-events' , d . disabled ? 'all' : 'none' ) ;
if ( d . disabled ) {
x . distortion ( fisheye ) . focus ( 0 ) ;
y . distortion ( fisheye ) . focus ( 0 ) ;
g . select ( '.nv-scatterWrap' ) . call ( scatter ) ;
g . select ( '.nv-x.nv-axis' ) . call ( xAxis ) ;
g . select ( '.nv-y.nv-axis' ) . call ( yAxis ) ;
} else {
pauseFisheye = false ;
}
chart ( selection ) ;
} ) ;
legend . dispatch . on ( 'legendClick' , function ( d , i , that ) {
d . disabled = ! d . disabled ;
if ( ! data . filter ( function ( d ) { return ! d . disabled } ) . length ) {
data . map ( function ( d ) {
d . disabled = false ;
wrap . selectAll ( '.nv-series' ) . classed ( 'disabled' , false ) ;
return d ;
} ) ;
}
chart ( selection ) ;
} ) ;
/ *
legend . dispatch . on ( 'legendMouseover' , function ( d , i ) {
d . hover = true ;
chart ( selection ) ;
} ) ;
legend . dispatch . on ( 'legendMouseout' , function ( d , i ) {
d . hover = false ;
chart ( selection ) ;
} ) ;
* /
scatter . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
d3 . select ( '.nv-chart-' + scatter . id ( ) + ' .nv-series-' + e . seriesIndex + ' .nv-distx-' + e . pointIndex )
. attr ( 'y1' , e . pos [ 1 ] - availableHeight ) ;
d3 . select ( '.nv-chart-' + scatter . id ( ) + ' .nv-series-' + e . seriesIndex + ' .nv-disty-' + e . pointIndex )
. attr ( 'x2' , e . pos [ 0 ] + distX . size ( ) ) ;
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ;
dispatch . tooltipShow ( e ) ;
} ) ;
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode ) ;
} ) ;
//============================================================
//store old scales for use in transitions on update
x0 = x . copy ( ) ;
y0 = y . copy ( ) ;
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
scatter . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
d3 . select ( '.nv-chart-' + scatter . id ( ) + ' .nv-series-' + e . seriesIndex + ' .nv-distx-' + e . pointIndex )
. attr ( 'y1' , 0 ) ;
d3 . select ( '.nv-chart-' + scatter . id ( ) + ' .nv-series-' + e . seriesIndex + ' .nv-disty-' + e . pointIndex )
. attr ( 'x2' , distY . size ( ) ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . scatter = scatter ;
chart . legend = legend ;
chart . controls = controls ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
chart . distX = distX ;
chart . distY = distY ;
d3 . rebind ( chart , scatter , 'id' , 'interactive' , 'pointActive' , 'x' , 'y' , 'shape' , 'size' , 'xScale' , 'yScale' , 'zScale' , 'xDomain' , 'yDomain' , 'sizeDomain' , 'sizeRange' , 'forceX' , 'forceY' , 'forceSize' , 'clipVoronoi' , 'clipRadius' , 'useVoronoi' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
distX . color ( color ) ;
distY . color ( color ) ;
return chart ;
} ;
chart . showDistX = function ( _ ) {
if ( ! arguments . length ) return showDistX ;
showDistX = _ ;
return chart ;
} ;
chart . showDistY = function ( _ ) {
if ( ! arguments . length ) return showDistY ;
showDistY = _ ;
return chart ;
} ;
chart . showControls = function ( _ ) {
if ( ! arguments . length ) return showControls ;
showControls = _ ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . fisheye = function ( _ ) {
if ( ! arguments . length ) return fisheye ;
fisheye = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . tooltipXContent = function ( _ ) {
if ( ! arguments . length ) return tooltipX ;
tooltipX = _ ;
return chart ;
} ;
chart . tooltipYContent = function ( _ ) {
if ( ! arguments . length ) return tooltipY ;
tooltipY = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . scatter = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 960
, height = 500
, color = nv . utils . defaultColor ( ) // chooses color
, 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 . linear ( ) //linear because d3.svg.shape.size is treated as area
, getX = function ( d ) { return d . x } // accessor to get the x value
, getY = function ( d ) { return d . y } // accessor to get the y value
, getSize = function ( d ) { return d . size } // accessor to get the point size
, getShape = function ( d ) { return d . shape || 'circle' } // accessor to get point shape
, forceX = [ ] // List of numbers to Force into the X scale (ie. 0, or a max / min, etc.)
, forceY = [ ] // List of numbers to Force into the Y scale
, forceSize = [ ] // List of numbers to Force into the Size scale
, interactive = true // If true, plots a voronoi overlay for advanced point interection
, pointActive = function ( d ) { return ! d . notActive } // any points that return false will be filtered out
, clipEdge = false // if true, masks points within x and y scale
, clipVoronoi = true // if true, masks each point with a circle... can turn off to slightly increase performance
, clipRadius = function ( ) { return 25 } // function to get the radius for voronoi point clips
, xDomain = null // Override x domain (skips the calculation from data)
, yDomain = null // Override y domain
, sizeDomain = null // Override point size domain
, sizeRange = null
, singlePoint = false
, dispatch = d3 . dispatch ( 'elementClick' , 'elementMouseover' , 'elementMouseout' )
, useVoronoi = true
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var x0 , y0 , z0 // used to store previous scales
, timeoutID
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
//add series index to each data point for reference
data = data . map ( function ( series , i ) {
series . values = series . values . map ( function ( point ) {
point . series = i ;
return point ;
} ) ;
return series ;
} ) ;
//------------------------------------------------------------
// Setup Scales
// remap and flatten the data for use in calculating the scales' domains
var seriesData = ( xDomain && yDomain && sizeDomain ) ? [ ] : // if we know xDomain and yDomain and sizeDomain, no need to calculate.... if Size is constant remember to set sizeDomain to speed up performance
d3 . merge (
data . map ( function ( d ) {
return d . values . map ( function ( d , i ) {
return { x : getX ( d , i ) , y : getY ( d , i ) , size : getSize ( d , i ) }
} )
} )
) ;
x . domain ( xDomain || d3 . extent ( seriesData . map ( function ( d ) { return d . x } ) . concat ( forceX ) ) )
. range ( [ 0 , availableWidth ] ) ;
y . domain ( yDomain || d3 . extent ( seriesData . map ( function ( d ) { return d . y } ) . concat ( forceY ) ) )
. range ( [ availableHeight , 0 ] ) ;
z . domain ( sizeDomain || d3 . extent ( seriesData . map ( function ( d ) { return d . size } ) . concat ( forceSize ) ) )
. range ( sizeRange || [ 16 , 256 ] ) ;
// If scale's domain don't have a range, slightly adjust to make one... so a chart can show a single data point
if ( x . domain ( ) [ 0 ] === x . domain ( ) [ 1 ] || y . domain ( ) [ 0 ] === y . domain ( ) [ 1 ] ) singlePoint = true ;
if ( x . domain ( ) [ 0 ] === x . domain ( ) [ 1 ] )
x . domain ( ) [ 0 ] ?
x . domain ( [ x . domain ( ) [ 0 ] - x . domain ( ) [ 0 ] * 0.01 , x . domain ( ) [ 1 ] + x . domain ( ) [ 1 ] * 0.01 ] )
: x . domain ( [ - 1 , 1 ] ) ;
if ( y . domain ( ) [ 0 ] === y . domain ( ) [ 1 ] )
y . domain ( ) [ 0 ] ?
y . domain ( [ y . domain ( ) [ 0 ] + y . domain ( ) [ 0 ] * 0.01 , y . domain ( ) [ 1 ] - y . domain ( ) [ 1 ] * 0.01 ] )
: y . domain ( [ - 1 , 1 ] ) ;
x0 = x0 || x ;
y0 = y0 || y ;
z0 = z0 || z ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-scatter' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-scatter nv-chart-' + id + ( singlePoint ? ' nv-single-point' : '' ) ) ;
var defsEnter = wrapEnter . append ( 'defs' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-groups' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-point-paths' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
defsEnter . append ( 'clipPath' )
. attr ( 'id' , 'nv-edge-clip-' + id )
. append ( 'rect' ) ;
wrap . select ( '#nv-edge-clip-' + id + ' rect' )
. attr ( 'width' , availableWidth )
. attr ( 'height' , availableHeight ) ;
g . attr ( 'clip-path' , clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '' ) ;
function updateInteractiveLayer ( ) {
if ( ! interactive ) return false ;
var eventElements ;
var vertices = d3 . merge ( data . map ( function ( group , groupIndex ) {
return group . values
. filter ( pointActive ) // remove non-interactive points
. map ( function ( point , pointIndex ) {
// *Adding noise to make duplicates very unlikely
// **Injecting series and point index for reference
return [ x ( getX ( point , pointIndex ) ) * ( Math . random ( ) / 1e12 + 1 ) , y ( getY ( point , pointIndex ) ) * ( Math . random ( ) / 1e12 + 1 ) , groupIndex , pointIndex ] ; //temp hack to add noise untill I think of a better way so there are no duplicates
} )
} )
) ;
if ( clipVoronoi ) {
defsEnter . append ( 'clipPath' ) . attr ( 'id' , 'nv-points-clip-' + id ) ;
var pointClips = wrap . select ( '#nv-points-clip-' + id ) . selectAll ( 'circle' )
. data ( vertices ) ;
pointClips . enter ( ) . append ( 'circle' )
. attr ( 'r' , clipRadius ) ;
pointClips . exit ( ) . remove ( ) ;
pointClips
. attr ( 'cx' , function ( d ) { return d [ 0 ] } )
. attr ( 'cy' , function ( d ) { return d [ 1 ] } ) ;
wrap . select ( '.nv-point-paths' )
. attr ( 'clip-path' , 'url(#nv-points-clip-' + id + ')' ) ;
}
//inject series and point index for reference into voronoi
if ( useVoronoi === true ) {
var voronoi = d3 . geom . voronoi ( vertices ) . map ( function ( d , i ) {
return {
'data' : d ,
'series' : vertices [ i ] [ 2 ] ,
'point' : vertices [ i ] [ 3 ]
}
} ) ;
var pointPaths = wrap . select ( '.nv-point-paths' ) . selectAll ( 'path' )
. data ( voronoi ) ;
pointPaths . enter ( ) . append ( 'path' )
. attr ( 'class' , function ( d , i ) { return 'nv-path-' + i ; } ) ;
pointPaths . exit ( ) . remove ( ) ;
pointPaths
. attr ( 'd' , function ( d ) { return 'M' + d . data . join ( ',' ) + 'Z' ; } ) ;
eventElements = pointPaths ;
} else {
// bring data in form needed for click handlers
var dataWithPoints = vertices . map ( function ( d , i ) {
return {
'data' : d ,
'series' : vertices [ i ] [ 2 ] ,
'point' : vertices [ i ] [ 3 ]
}
} ) ;
// add event handlers to points instead voronoi paths
eventElements = wrap . select ( '.nv-groups' ) . selectAll ( '.nv-group' )
. selectAll ( 'path.nv-point' )
. data ( dataWithPoints )
. style ( 'pointer-events' , 'auto' ) ; // recativate events, disabled by css
}
eventElements
. on ( 'click' , function ( d ) {
var series = data [ d . series ] ,
point = series . values [ d . point ] ;
dispatch . elementClick ( {
point : point ,
series : series ,
pos : [ x ( getX ( point , d . point ) ) + margin . left , y ( getY ( point , d . point ) ) + margin . top ] ,
seriesIndex : d . series ,
pointIndex : d . point
} ) ;
} )
. on ( 'mouseover' , function ( d ) {
var series = data [ d . series ] ,
point = series . values [ d . point ] ;
dispatch . elementMouseover ( {
point : point ,
series : series ,
pos : [ x ( getX ( point , d . point ) ) + margin . left , y ( getY ( point , d . point ) ) + margin . top ] ,
seriesIndex : d . series ,
pointIndex : d . point
} ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
var series = data [ d . series ] ,
point = series . values [ d . point ] ;
dispatch . elementMouseout ( {
point : point ,
series : series ,
seriesIndex : d . series ,
pointIndex : d . point
} ) ;
} ) ;
}
var groups = wrap . select ( '.nv-groups' ) . selectAll ( '.nv-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 'nv-group nv-series-' + i } )
. classed ( 'hover' , function ( d ) { return d . hover } ) ;
d3 . transition ( groups )
. style ( 'fill' , function ( d , i ) { return color ( d , i ) } )
. style ( 'stroke' , function ( d , i ) { return color ( d , i ) } )
. style ( 'stroke-opacity' , 1 )
. style ( 'fill-opacity' , . 5 ) ;
var points = groups . selectAll ( 'path.nv-point' )
. data ( function ( d ) { return d . values } ) ;
points . enter ( ) . append ( 'path' )
. attr ( 'transform' , function ( d , i ) {
return 'translate(' + x0 ( getX ( d , i ) ) + ',' + y0 ( getY ( d , i ) ) + ')'
} )
. attr ( 'd' ,
d3 . svg . symbol ( )
. type ( getShape )
. size ( function ( d , i ) { return z ( getSize ( d , i ) ) } )
) ;
points . exit ( ) . remove ( ) ;
d3 . transition ( groups . exit ( ) . selectAll ( 'path.nv-point' ) )
. attr ( 'transform' , function ( d , i ) {
return 'translate(' + x ( getX ( d , i ) ) + ',' + y ( getY ( d , i ) ) + ')'
} )
. remove ( ) ;
points . attr ( 'class' , function ( d , i ) { return 'nv-point nv-point-' + i } ) ;
d3 . transition ( points )
. attr ( 'transform' , function ( d , i ) {
return 'translate(' + x ( getX ( d , i ) ) + ',' + y ( getY ( d , i ) ) + ')'
} )
. attr ( 'd' ,
d3 . svg . symbol ( )
. type ( getShape )
. size ( function ( d , i ) { return z ( getSize ( d , i ) ) } )
) ;
// Delay updating the invisible interactive layer for smoother animation
clearTimeout ( timeoutID ) ; // stop repeat calls to updateInteractiveLayer
timeoutID = setTimeout ( updateInteractiveLayer , 1000 ) ;
//store old scales for use in transitions on update
x0 = x . copy ( ) ;
y0 = y . copy ( ) ;
z0 = z . copy ( ) ;
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
dispatch . on ( 'elementMouseover.point' , function ( d ) {
if ( interactive )
d3 . select ( '.nv-chart-' + id + ' .nv-series-' + d . seriesIndex + ' .nv-point-' + d . pointIndex )
. classed ( 'hover' , true ) ;
} ) ;
dispatch . on ( 'elementMouseout.point' , function ( d ) {
if ( interactive )
d3 . select ( '.nv-chart-' + id + ' .nv-series-' + d . seriesIndex + ' .nv-point-' + d . pointIndex )
. classed ( 'hover' , false ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
chart . dispatch = dispatch ;
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 . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . xScale = function ( _ ) {
if ( ! arguments . length ) return x ;
x = _ ;
return chart ;
} ;
chart . yScale = function ( _ ) {
if ( ! arguments . length ) return y ;
y = _ ;
return chart ;
} ;
chart . zScale = function ( _ ) {
if ( ! arguments . length ) return z ;
z = _ ;
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 . sizeDomain = function ( _ ) {
if ( ! arguments . length ) return sizeDomain ;
sizeDomain = _ ;
return chart ;
} ;
chart . sizeRange = function ( _ ) {
if ( ! arguments . length ) return sizeRange ;
sizeRange = _ ;
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 . forceSize = function ( _ ) {
if ( ! arguments . length ) return forceSize ;
forceSize = _ ;
return chart ;
} ;
chart . interactive = function ( _ ) {
if ( ! arguments . length ) return interactive ;
interactive = _ ;
return chart ;
} ;
chart . pointActive = function ( _ ) {
if ( ! arguments . length ) return pointActive ;
pointActive = _ ;
return chart ;
} ;
chart . clipEdge = function ( _ ) {
if ( ! arguments . length ) return clipEdge ;
clipEdge = _ ;
return chart ;
} ;
chart . clipVoronoi = function ( _ ) {
if ( ! arguments . length ) return clipVoronoi ;
clipVoronoi = _ ;
return chart ;
} ;
chart . useVoronoi = function ( _ ) {
if ( ! arguments . length ) return useVoronoi ;
useVoronoi = _ ;
if ( useVoronoi === false ) {
clipVoronoi = false ;
}
return chart ;
} ;
chart . clipRadius = function ( _ ) {
if ( ! arguments . length ) return clipRadius ;
clipRadius = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
chart . shape = function ( _ ) {
if ( ! arguments . length ) return getShape ;
getShape = _ ;
return chart ;
} ;
chart . id = function ( _ ) {
if ( ! arguments . length ) return id ;
id = _ ;
return chart ;
} ;
chart . singlePoint = function ( _ ) {
if ( ! arguments . length ) return singlePoint ;
singlePoint = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . sparkline = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 2 , right : 0 , bottom : 2 , left : 0 }
, width = 400
, height = 32
, animate = true
, x = d3 . scale . linear ( )
, y = d3 . scale . linear ( )
, getX = function ( d ) { return d . x }
, getY = function ( d ) { return d . y }
, color = nv . utils . getColor ( [ '#000' ] )
, xDomain
, yDomain
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
//------------------------------------------------------------
// Setup Scales
x . domain ( xDomain || d3 . extent ( data , getX ) )
. range ( [ 0 , availableWidth ] ) ;
y . domain ( yDomain || d3 . extent ( data , getY ) )
. range ( [ availableHeight , 0 ] ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-sparkline' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-sparkline' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' )
//------------------------------------------------------------
var paths = wrap . selectAll ( 'path' )
. data ( function ( d ) { return [ d ] } ) ;
paths . enter ( ) . append ( 'path' ) ;
paths . exit ( ) . remove ( ) ;
paths
. style ( 'stroke' , function ( d , i ) { return d . color || color ( d , i ) } )
. attr ( 'd' , d3 . svg . line ( )
. x ( function ( d , i ) { return x ( getX ( d , i ) ) } )
. y ( function ( d , i ) { return y ( getY ( d , i ) ) } )
) ;
// TODO: Add CURRENT data point (Need Min, Mac, Current / Most recent)
var points = wrap . selectAll ( 'circle.nv-point' )
. data ( function ( d ) { return d . filter ( function ( p , i ) { return y . domain ( ) . indexOf ( getY ( p , i ) ) != - 1 || getX ( p , i ) == x . domain ( ) [ 1 ] } ) } ) ;
points . enter ( ) . append ( 'circle' ) . attr ( 'class' , 'nv-point' ) ;
points . exit ( ) . remove ( ) ;
points
. attr ( 'cx' , function ( d , i ) { return x ( getX ( d , i ) ) } )
. attr ( 'cy' , function ( d , i ) { return y ( getY ( d , i ) ) } )
. attr ( 'r' , 2 )
. style ( 'stroke' , function ( d , i ) { return d . x == x . domain ( ) [ 1 ] ? '#444' : d . y == y . domain ( ) [ 0 ] ? '#d62728' : '#2ca02c' } )
. style ( 'fill' , function ( d , i ) { return d . x == x . domain ( ) [ 1 ] ? '#444' : d . y == y . domain ( ) [ 0 ] ? '#d62728' : '#2ca02c' } ) ;
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . xScale = function ( _ ) {
if ( ! arguments . length ) return x ;
x = _ ;
return chart ;
} ;
chart . yScale = function ( _ ) {
if ( ! arguments . length ) return y ;
y = _ ;
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 . animate = function ( _ ) {
if ( ! arguments . length ) return animate ;
animate = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . sparklinePlus = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var sparkline = nv . models . sparkline ( ) ;
var margin = { top : 15 , right : 40 , bottom : 3 , left : 40 }
, width = null
, height = null
, x
, y
, color = nv . utils . defaultColor ( )
, index
, paused = false
, xTickFormat = d3 . format ( ',r' )
, yTickFormat = d3 . format ( ',.2f' )
, noData = "No Data Available."
;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { chart ( selection ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
if ( ! data || ! data . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = sparkline . xScale ( ) ;
y = sparkline . yScale ( ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-sparklineplus' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-sparklineplus' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-sparklineWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-hoverArea' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Main Chart Component(s)
var sparklineWrap = g . select ( '.nv-sparklineWrap' ) ;
sparkline
. width ( availableWidth )
. height ( availableHeight ) ;
sparklineWrap
. style ( 'stroke' , function ( d , i ) { return d . color || color ( d , i ) } )
. call ( sparkline ) ;
//------------------------------------------------------------
gEnter . select ( '.nv-hoverArea' ) . append ( 'rect' )
. on ( 'mousemove' , sparklineHover )
. on ( 'click' , function ( ) { paused = ! paused } )
. on ( 'mouseout' , function ( ) { index = null ; updateValueLine ( ) ; } ) ;
g . select ( '.nv-hoverArea rect' )
. attr ( 'transform' , function ( d ) { return 'translate(' + - margin . left + ',' + - margin . top + ')' } )
. attr ( 'width' , availableWidth + margin . left + margin . right )
. attr ( 'height' , availableHeight + margin . top ) ;
// if index is not set, default to last point
//index = typeof index == 'undefined' ? data.length - 1 : index;
// if index is not set, default to null
index = typeof index == 'undefined' ? null : index ;
var hoverValue = g . selectAll ( '.nv-hoverValue' ) . data ( [ index ] ) ;
var hoverG = hoverValue . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nv-hoverValue' ) ;
var hoverLine = hoverG . append ( 'line' )
. attr ( 'x1' , 0 )
. attr ( 'y1' , - margin . top )
. attr ( 'x2' , 0 )
. attr ( 'y2' , availableHeight ) ;
var hoverX = hoverG . append ( 'text' ) . attr ( 'class' , 'nv-xValue' )
. attr ( 'x' , - 6 )
. attr ( 'y' , - margin . top )
. attr ( 'text-anchor' , 'end' )
. attr ( 'dy' , '.9em' ) ;
var hoverY = hoverG . append ( 'text' ) . attr ( 'class' , 'nv-yValue' )
. attr ( 'x' , 6 )
. attr ( 'y' , - margin . top )
. attr ( 'text-anchor' , 'start' )
. attr ( 'dy' , '.9em' ) ;
updateValueLine ( ) ;
function updateValueLine ( ) { //index is currently global (within the chart), may or may not keep it that way
if ( paused ) return ;
hoverValue . data ( [ index ] )
//d3.transition(hoverValue)
hoverValue
. transition ( ) . duration ( 250 )
. style ( 'stroke-opacity' , function ( d ) { return d === null ? 0 : 1 } )
. style ( 'fill-opacity' , function ( d ) { return d === null ? 0 : 1 } ) ;
if ( index == null ) return ;
hoverValue
. attr ( 'transform' , function ( d ) { return 'translate(' + x ( sparkline . x ( ) ( data [ d ] , d ) ) + ',0)' } )
hoverValue . select ( '.nv-xValue' )
. text ( xTickFormat ( sparkline . x ( ) ( data [ index ] , index ) ) ) ;
hoverValue . select ( '.nv-yValue' )
. text ( yTickFormat ( sparkline . y ( ) ( data [ index ] , index ) ) ) ;
}
function sparklineHover ( ) {
if ( paused ) return ;
var pos = d3 . event . offsetX - margin . left ;
function getClosestIndex ( data , x ) {
var distance = Math . abs ( sparkline . x ( ) ( data [ 0 ] , 0 ) - x ) ;
var closestIndex = 0 ;
for ( var i = 0 ; i < data . length ; i ++ ) {
if ( Math . abs ( sparkline . x ( ) ( data [ i ] , i ) - x ) < distance ) {
distance = Math . abs ( sparkline . x ( ) ( data [ i ] , i ) - x ) ;
closestIndex = i ;
}
}
return closestIndex ;
}
index = getClosestIndex ( data , Math . round ( x . invert ( pos ) ) ) ;
updateValueLine ( ) ;
}
} ) ;
return chart ;
}
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . sparkline = sparkline ;
d3 . rebind ( chart , sparkline , 'x' , 'y' , 'xScale' , 'yScale' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . xTickFormat = function ( _ ) {
if ( ! arguments . length ) return xTickFormat ;
xTickFormat = _ ;
return chart ;
} ;
chart . yTickFormat = function ( _ ) {
if ( ! arguments . length ) return yTickFormat ;
yTickFormat = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
//============================================================
return chart ;
}
nv . models . stackedAreaChart = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var stacked = nv . models . stackedArea ( )
, xAxis = nv . models . axis ( )
, yAxis = nv . models . axis ( )
, legend = nv . models . legend ( )
, controls = nv . models . legend ( )
;
var margin = { top : 30 , right : 25 , bottom : 50 , left : 60 }
, width = null
, height = null
, color = nv . utils . defaultColor ( ) // a function that takes in d, i and returns color
, showControls = true
, showLegend = true
, tooltips = true
, tooltip = function ( key , x , y , e , graph ) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' on ' + x + '</p>'
}
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
, yAxisTickFormat = d3 . format ( ',.2f' )
, noData = 'No Data Available.'
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' )
;
xAxis
. orient ( 'bottom' )
. tickPadding ( 5 )
;
yAxis
. orient ( 'left' )
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function ( e , offsetElement ) {
var left = e . pos [ 0 ] + ( offsetElement . offsetLeft || 0 ) ,
top = e . pos [ 1 ] + ( offsetElement . offsetTop || 0 ) ,
x = xAxis . tickFormat ( ) ( stacked . x ( ) ( e . point , e . pointIndex ) ) ,
y = yAxis . tickFormat ( ) ( stacked . y ( ) ( e . point , e . pointIndex ) ) ,
content = tooltip ( e . series . key , x , y , e , chart ) ;
nv . tooltip . show ( [ left , top ] , content , e . value < 0 ? 'n' : 's' , null , offsetElement ) ;
} ;
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var container = d3 . select ( this ) ,
that = this ;
var availableWidth = ( width || parseInt ( container . style ( 'width' ) ) || 960 )
- margin . left - margin . right ,
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
chart . update = function ( ) { chart ( selection ) } ;
chart . container = this ;
//------------------------------------------------------------
// Display No Data message if there's nothing to show.
if ( ! data || ! data . length || ! data . filter ( function ( d ) { return d . values . length } ) . length ) {
var noDataText = container . selectAll ( '.nv-noData' ) . data ( [ noData ] ) ;
noDataText . enter ( ) . append ( 'text' )
. attr ( 'class' , 'nvd3 nv-noData' )
. attr ( 'dy' , '-.7em' )
. style ( 'text-anchor' , 'middle' ) ;
noDataText
. attr ( 'x' , margin . left + availableWidth / 2 )
. attr ( 'y' , margin . top + availableHeight / 2 )
. text ( function ( d ) { return d } ) ;
return chart ;
} else {
container . selectAll ( '.nv-noData' ) . remove ( ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = stacked . xScale ( ) ;
y = stacked . yScale ( ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-stackedAreaChart' ) . data ( [ data ] ) ;
var gEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-stackedAreaChart' ) . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-x nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-y nv-axis' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-stackedWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-legendWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-controlsWrap' ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Legend
if ( showLegend ) {
legend
. width ( availableWidth / 2 ) ;
g . select ( '.nv-legendWrap' )
. datum ( data )
. call ( legend ) ;
if ( margin . top != legend . height ( ) ) {
margin . top = legend . height ( ) ;
availableHeight = ( height || parseInt ( container . style ( 'height' ) ) || 400 )
- margin . top - margin . bottom ;
}
g . select ( '.nv-legendWrap' )
. attr ( 'transform' , 'translate(' + ( availableWidth / 2 ) + ',' + ( - margin . top ) + ')' ) ;
}
//------------------------------------------------------------
//------------------------------------------------------------
// Controls
if ( showControls ) {
var controlsData = [
{ key : 'Stacked' , disabled : stacked . offset ( ) != 'zero' } ,
{ key : 'Stream' , disabled : stacked . offset ( ) != 'wiggle' } ,
{ key : 'Expanded' , disabled : stacked . offset ( ) != 'expand' }
] ;
controls . width ( 280 ) . color ( [ '#444' , '#444' , '#444' ] ) ;
g . select ( '.nv-controlsWrap' )
. datum ( controlsData )
. attr ( 'transform' , 'translate(0,' + ( - margin . top ) + ')' )
. call ( controls ) ;
}
//------------------------------------------------------------
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
// Main Chart Component(s)
stacked
. width ( availableWidth )
. height ( availableHeight )
var stackedWrap = g . select ( '.nv-stackedWrap' )
. datum ( data ) ;
d3 . transition ( stackedWrap ) . call ( stacked ) ;
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Axes
xAxis
. scale ( x )
. ticks ( availableWidth / 100 )
. tickSize ( - availableHeight , 0 ) ;
g . select ( '.nv-x.nv-axis' )
. attr ( 'transform' , 'translate(0,' + availableHeight + ')' ) ;
d3 . transition ( g . select ( '.nv-x.nv-axis' ) )
. call ( xAxis ) ;
yAxis
. scale ( y )
. ticks ( stacked . offset ( ) == 'wiggle' ? 0 : availableHeight / 36 )
. tickSize ( - availableWidth , 0 )
. setTickFormat ( stacked . offset ( ) == 'expand' ? d3 . format ( '%' ) : yAxisTickFormat ) ;
d3 . transition ( g . select ( '.nv-y.nv-axis' ) )
. call ( yAxis ) ;
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
stacked . dispatch . on ( 'areaClick.toggle' , function ( e ) {
if ( data . filter ( function ( d ) { return ! d . disabled } ) . length === 1 )
data = data . map ( function ( d ) {
d . disabled = false ;
return d
} ) ;
else
data = data . map ( function ( d , i ) {
d . disabled = ( i != e . seriesIndex ) ;
return d
} ) ;
selection . transition ( ) . call ( chart ) ;
} ) ;
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 ;
return d ;
} ) ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
controls . dispatch . on ( 'legendClick' , function ( d , i ) {
if ( ! d . disabled ) return ;
controlsData = controlsData . map ( function ( s ) {
s . disabled = true ;
return s ;
} ) ;
d . disabled = false ;
switch ( d . key ) {
case 'Stacked' :
stacked . style ( 'stack' ) ;
break ;
case 'Stream' :
stacked . style ( 'stream' ) ;
break ;
case 'Expanded' :
stacked . style ( 'expand' ) ;
break ;
}
selection . transition ( ) . call ( chart ) ;
} ) ;
dispatch . on ( 'tooltipShow' , function ( e ) {
if ( tooltips ) showTooltip ( e , that . parentNode ) ;
} ) ;
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
stacked . dispatch . on ( 'tooltipShow' , function ( e ) {
//disable tooltips when value ~= 0
//// TODO: consider removing points from voronoi that have 0 value instead of this hack
if ( ! Math . round ( stacked . y ( ) ( e . point ) * 100 ) ) { // 100 will not be good for very small numbers... will have to think about making this valu dynamic, based on data range
setTimeout ( function ( ) { d3 . selectAll ( '.point.hover' ) . classed ( 'hover' , false ) } , 0 ) ;
return false ;
}
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ,
dispatch . tooltipShow ( e ) ;
} ) ;
stacked . dispatch . on ( 'tooltipHide' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
dispatch . on ( 'tooltipHide' , function ( ) {
if ( tooltips ) nv . tooltip . cleanup ( ) ;
} ) ;
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart . dispatch = dispatch ;
chart . stacked = stacked ;
chart . legend = legend ;
chart . controls = controls ;
chart . xAxis = xAxis ;
chart . yAxis = yAxis ;
d3 . rebind ( chart , stacked , 'x' , 'y' , 'size' , 'xScale' , 'yScale' , 'xDomain' , 'yDomain' , 'sizeDomain' , 'interactive' , 'offset' , 'order' , 'style' , 'clipEdge' , 'forceX' , 'forceY' , 'forceSize' , 'interpolate' ) ;
chart . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
return chart ;
} ;
chart . width = function ( _ ) {
if ( ! arguments . length ) return getWidth ;
width = _ ;
return chart ;
} ;
chart . height = function ( _ ) {
if ( ! arguments . length ) return getHeight ;
height = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
legend . color ( color ) ;
stacked . color ( color ) ;
return chart ;
} ;
chart . showControls = function ( _ ) {
if ( ! arguments . length ) return showControls ;
showControls = _ ;
return chart ;
} ;
chart . showLegend = function ( _ ) {
if ( ! arguments . length ) return showLegend ;
showLegend = _ ;
return chart ;
} ;
chart . tooltip = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . tooltips = function ( _ ) {
if ( ! arguments . length ) return tooltips ;
tooltips = _ ;
return chart ;
} ;
chart . tooltipContent = function ( _ ) {
if ( ! arguments . length ) return tooltip ;
tooltip = _ ;
return chart ;
} ;
chart . noData = function ( _ ) {
if ( ! arguments . length ) return noData ;
noData = _ ;
return chart ;
} ;
yAxis . setTickFormat = yAxis . tickFormat ;
yAxis . tickFormat = function ( _ ) {
if ( ! arguments . length ) return yAxisTickFormat ;
yAxisTickFormat = _ ;
return yAxis ;
} ;
//============================================================
return chart ;
}
nv . models . stackedArea = function ( ) {
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var margin = { top : 0 , right : 0 , bottom : 0 , left : 0 }
, width = 960
, height = 500
, color = nv . utils . defaultColor ( ) // a function that computes the color
, id = Math . floor ( Math . random ( ) * 100000 ) //Create semi-unique ID incase user doesn't selet one
, getX = function ( d ) { return d . x } // accessor to get the x value from a data point
, getY = function ( d ) { return d . y } // accessor to get the y value from a data point
, style = 'stack'
, offset = 'zero'
, order = 'default'
, interpolate = 'linear' // controls the line interpolation
, clipEdge = false // if true, masks lines within x and y scale
, x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale()
, scatter = nv . models . scatter ( )
, dispatch = d3 . dispatch ( 'tooltipShow' , 'tooltipHide' , 'areaClick' , 'areaMouseover' , 'areaMouseout' )
;
scatter
. size ( 2.2 ) // default size
. sizeDomain ( [ 2.2 ] ) // all the same size by default
;
/ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* offset :
* 'wiggle' ( stream )
* 'zero' ( stacked )
* 'expand' ( normalize to 100 % )
* 'silhouette' ( simple centered )
*
* order :
* 'inside-out' ( stream )
* 'default' ( input order )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /
//============================================================
function chart ( selection ) {
selection . each ( function ( data ) {
var availableWidth = width - margin . left - margin . right ,
availableHeight = height - margin . top - margin . bottom ,
container = d3 . select ( this ) ;
//------------------------------------------------------------
// Setup Scales
x = scatter . xScale ( ) ;
y = scatter . yScale ( ) ;
//------------------------------------------------------------
// Injecting point index into each point because d3.layout.stack().out does not give index
// ***Also storing getY(d,i) as stackedY so that it can be set to 0 if series is disabled
data = data . map ( function ( aseries , i ) {
aseries . values = aseries . values . map ( function ( d , j ) {
d . index = j ;
d . stackedY = aseries . disabled ? 0 : getY ( d , j ) ;
return d ;
} )
return aseries ;
} ) ;
data = d3 . layout . stack ( )
. order ( order )
. offset ( offset )
. values ( function ( d ) { return d . values } ) //TODO: make values customizeable in EVERY model in this fashion
. x ( getX )
. y ( function ( d ) { return d . stackedY } )
. out ( function ( d , y0 , y ) {
d . display = {
y : y ,
y0 : y0
} ;
} )
( data ) ;
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container . selectAll ( 'g.nv-wrap.nv-stackedarea' ) . data ( [ data ] ) ;
var wrapEnter = wrap . enter ( ) . append ( 'g' ) . attr ( 'class' , 'nvd3 nv-wrap nv-stackedarea' ) ;
var defsEnter = wrapEnter . append ( 'defs' ) ;
var gEnter = wrapEnter . append ( 'g' ) ;
var g = wrap . select ( 'g' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-areaWrap' ) ;
gEnter . append ( 'g' ) . attr ( 'class' , 'nv-scatterWrap' ) ;
wrap . attr ( 'transform' , 'translate(' + margin . left + ',' + margin . top + ')' ) ;
//------------------------------------------------------------
scatter
. width ( availableWidth )
. height ( availableHeight )
. x ( getX )
. y ( function ( d ) { return d . display . y + d . display . y0 } )
. forceY ( [ 0 ] )
. color ( data . map ( function ( d , i ) {
return d . color || color ( d , i ) ;
} ) . filter ( function ( d , i ) { return ! data [ i ] . disabled } ) ) ;
var scatterWrap = g . select ( '.nv-scatterWrap' )
. datum ( data . filter ( function ( d ) { return ! d . disabled } ) )
d3 . transition ( scatterWrap ) . call ( scatter ) ;
defsEnter . append ( 'clipPath' )
. attr ( 'id' , 'nv-edge-clip-' + id )
. append ( 'rect' ) ;
wrap . select ( '#nv-edge-clip-' + id + ' rect' )
. attr ( 'width' , availableWidth )
. attr ( 'height' , availableHeight ) ;
g . attr ( 'clip-path' , clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '' ) ;
var area = d3 . svg . area ( )
. x ( function ( d , i ) { return x ( getX ( d , i ) ) } )
. y0 ( function ( d ) { return y ( d . display . y0 ) } )
. y1 ( function ( d ) { return y ( d . display . y + d . display . y0 ) } )
. interpolate ( interpolate ) ;
var zeroArea = d3 . svg . area ( )
. x ( function ( d , i ) { return x ( getX ( d , i ) ) } )
. y0 ( function ( d ) { return y ( d . display . y0 ) } )
. y1 ( function ( d ) { return y ( d . display . y0 ) } ) ;
var path = g . select ( '.nv-areaWrap' ) . selectAll ( 'path.nv-area' )
. data ( function ( d ) { return d } ) ;
//.data(function(d) { return d }, function(d) { return d.key });
path . enter ( ) . append ( 'path' ) . attr ( 'class' , function ( d , i ) { return 'nv-area nv-area-' + i } )
. on ( 'mouseover' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , true ) ;
dispatch . areaMouseover ( {
point : d ,
series : d . key ,
pos : [ d3 . event . pageX , d3 . event . pageY ] ,
seriesIndex : i
} ) ;
} )
. on ( 'mouseout' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , false ) ;
dispatch . areaMouseout ( {
point : d ,
series : d . key ,
pos : [ d3 . event . pageX , d3 . event . pageY ] ,
seriesIndex : i
} ) ;
} )
. on ( 'click' , function ( d , i ) {
d3 . select ( this ) . classed ( 'hover' , false ) ;
dispatch . areaClick ( {
point : d ,
series : d . key ,
pos : [ d3 . event . pageX , d3 . event . pageY ] ,
seriesIndex : i
} ) ;
} )
d3 . transition ( path . exit ( ) )
. attr ( 'd' , function ( d , i ) { return zeroArea ( d . values , i ) } )
. remove ( ) ;
path
. style ( 'fill' , function ( d , i ) { return d . color || color ( d , i ) } )
. style ( 'stroke' , function ( d , i ) { return d . color || color ( d , i ) } ) ;
d3 . transition ( path )
. attr ( 'd' , function ( d , i ) { return area ( d . values , i ) } )
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
scatter . dispatch . on ( 'elementMouseover.area' , function ( e ) {
g . select ( '.nv-chart-' + id + ' .nv-area-' + e . seriesIndex ) . classed ( 'hover' , true ) ;
} ) ;
scatter . dispatch . on ( 'elementMouseout.area' , function ( e ) {
g . select ( '.nv-chart-' + id + ' .nv-area-' + e . seriesIndex ) . classed ( 'hover' , false ) ;
} ) ;
//============================================================
} ) ;
return chart ;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
scatter . dispatch . on ( 'elementClick.area' , function ( e ) {
dispatch . areaClick ( e ) ;
} )
scatter . dispatch . on ( 'elementMouseover.tooltip' , function ( e ) {
e . pos = [ e . pos [ 0 ] + margin . left , e . pos [ 1 ] + margin . top ] ,
dispatch . tooltipShow ( e ) ;
} ) ;
scatter . dispatch . on ( 'elementMouseout.tooltip' , function ( e ) {
dispatch . tooltipHide ( e ) ;
} ) ;
//============================================================
//============================================================
// Global getters and setters
//------------------------------------------------------------
chart . dispatch = dispatch ;
chart . scatter = scatter ;
d3 . rebind ( chart , scatter , 'interactive' , 'size' , 'xScale' , 'yScale' , 'zScale' , 'xDomain' , 'yDomain' , 'sizeDomain' , 'forceX' , 'forceY' , 'forceSize' , 'clipVoronoi' , 'clipRadius' ) ;
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 . margin = function ( _ ) {
if ( ! arguments . length ) return margin ;
margin . top = typeof _ . top != 'undefined' ? _ . top : margin . top ;
margin . right = typeof _ . right != 'undefined' ? _ . right : margin . right ;
margin . bottom = typeof _ . bottom != 'undefined' ? _ . bottom : margin . bottom ;
margin . left = typeof _ . left != 'undefined' ? _ . left : margin . left ;
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 . clipEdge = function ( _ ) {
if ( ! arguments . length ) return clipEdge ;
clipEdge = _ ;
return chart ;
} ;
chart . color = function ( _ ) {
if ( ! arguments . length ) return color ;
color = nv . utils . getColor ( _ ) ;
return chart ;
} ;
chart . offset = function ( _ ) {
if ( ! arguments . length ) return offset ;
offset = _ ;
return chart ;
} ;
chart . order = function ( _ ) {
if ( ! arguments . length ) return order ;
order = _ ;
return chart ;
} ;
//shortcut for offset + order
chart . style = function ( _ ) {
if ( ! arguments . length ) return style ;
style = _ ;
switch ( style ) {
case 'stack' :
chart . offset ( 'zero' ) ;
chart . order ( 'default' ) ;
break ;
case 'stream' :
chart . offset ( 'wiggle' ) ;
chart . order ( 'inside-out' ) ;
break ;
case 'stream-center' :
chart . offset ( 'silhouette' ) ;
chart . order ( 'inside-out' ) ;
break ;
case 'expand' :
chart . offset ( 'expand' ) ;
chart . order ( 'default' ) ;
break ;
}
return chart ;
} ;
chart . interpolate = function ( _ ) {
if ( ! arguments . length ) return interpolate ;
interpolate = _ ;
return interpolate ;
} ;
//============================================================
return chart ;
}
} ) ( ) ;