@ -33,6 +33,121 @@ Licensed under the MIT license.
// the actual Flot code
// the actual Flot code
( function ( $ ) {
( function ( $ ) {
///////////////////////////////////////////////////////////////////////////
// The Canvas object is a wrapper around an HTML5 <canvas> tag.
//
// @constructor
// @param {string} cls List of classes to apply to the canvas.
// @param {element} container Element onto which to append the canvas.
//
// Requiring a container is a little iffy, but unfortunately canvas
// operations don't work unless the canvas is attached to the DOM.
function Canvas ( cls , container ) {
var element = document . createElement ( "canvas" ) ;
element . className = cls ;
$ ( element ) . css ( { direction : "ltr" , position : "absolute" , left : 0 , top : 0 } )
. data ( "canvas" , this )
. appendTo ( container ) ;
// If HTML5 Canvas isn't available, fall back to Excanvas
if ( ! element . getContext ) {
if ( window . G _vmlCanvasManager ) {
element = window . G _vmlCanvasManager . initElement ( element ) ;
} else {
throw new Error ( "Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode." ) ;
}
}
var context = element . getContext ( "2d" ) ;
this . element = element ;
this . context = context ;
// Determine the screen's ratio of physical to device-independent
// pixels. This is the ratio between the canvas width that the browser
// advertises and the number of pixels actually present in that space.
// The iPhone 4, for example, has a device-independent width of 320px,
// but its screen is actually 640px wide. It therefore has a pixel
// ratio of 2, while most normal devices have a ratio of 1.
var devicePixelRatio = window . devicePixelRatio || 1 ,
backingStoreRatio =
context . webkitBackingStorePixelRatio ||
context . mozBackingStorePixelRatio ||
context . msBackingStorePixelRatio ||
context . oBackingStorePixelRatio ||
context . backingStorePixelRatio || 1 ;
this . pixelRatio = devicePixelRatio / backingStoreRatio ;
// Size the canvas to match the internal dimensions of its container
this . resize ( container . width ( ) , container . height ( ) ) ;
} ;
// Resizes the canvas to the given dimensions.
//
// @param {number} width New width of the canvas, in pixels.
// @param {number} width New height of the canvas, in pixels.
Canvas . prototype . resize = function ( width , height ) {
if ( width <= 0 || height <= 0 ) {
throw new Error ( "Invalid dimensions for plot, width = " + width + ", height = " + height ) ;
}
var element = this . element ,
context = this . context ,
pixelRatio = this . pixelRatio ;
// Resize the canvas, increasing its density based on the display's
// pixel ratio; basically giving it more pixels without increasing the
// size of its element, to take advantage of the fact that retina
// displays have that many more pixels in the same advertised space.
// Resizing should reset the state (excanvas seems to be buggy though)
if ( this . width != width ) {
element . width = width * pixelRatio ;
element . style . width = width + "px" ;
this . width = width ;
}
if ( this . height != height ) {
element . height = height * pixelRatio ;
element . style . height = height + "px" ;
this . height = height ;
}
// Save the context, so we can reset in case we get replotted. The
// restore ensure that we're really back at the initial state, and
// should be safe even if we haven't saved the initial state yet.
context . restore ( ) ;
context . save ( ) ;
// Scale the coordinate space to match the display density; so even though we
// may have twice as many pixels, we still want lines and other drawing to
// appear at the same size; the extra pixels will just make them crisper.
context . scale ( pixelRatio , pixelRatio ) ;
}
// Clears the entire canvas area.
Canvas . prototype . clear = function ( ) {
this . context . clearRect ( 0 , 0 , this . width , this . height ) ;
}
///////////////////////////////////////////////////////////////////////////
// The top-level container for the entire plot.
function Plot ( placeholder , data _ , options _ , plugins ) {
function Plot ( placeholder , data _ , options _ , plugins ) {
// data is on the form:
// data is on the form:
// [ series1, series2 ... ]
// [ series1, series2 ... ]
@ -154,7 +269,6 @@ Licensed under the MIT license.
ctx = null , octx = null ,
ctx = null , octx = null ,
xaxes = [ ] , yaxes = [ ] ,
xaxes = [ ] , yaxes = [ ] ,
plotOffset = { left : 0 , right : 0 , top : 0 , bottom : 0 } ,
plotOffset = { left : 0 , right : 0 , top : 0 , bottom : 0 } ,
canvasWidth = 0 , canvasHeight = 0 ,
plotWidth = 0 , plotHeight = 0 ,
plotWidth = 0 , plotHeight = 0 ,
hooks = {
hooks = {
processOptions : [ ] ,
processOptions : [ ] ,
@ -175,7 +289,7 @@ Licensed under the MIT license.
plot . setupGrid = setupGrid ;
plot . setupGrid = setupGrid ;
plot . draw = draw ;
plot . draw = draw ;
plot . getPlaceholder = function ( ) { return placeholder ; } ;
plot . getPlaceholder = function ( ) { return placeholder ; } ;
plot . getCanvas = function ( ) { return surface ; } ;
plot . getCanvas = function ( ) { return surface .element ; } ;
plot . getPlotOffset = function ( ) { return plotOffset ; } ;
plot . getPlotOffset = function ( ) { return plotOffset ; } ;
plot . width = function ( ) { return plotWidth ; } ;
plot . width = function ( ) { return plotWidth ; } ;
plot . height = function ( ) { return plotHeight ; } ;
plot . height = function ( ) { return plotHeight ; } ;
@ -210,9 +324,10 @@ Licensed under the MIT license.
} ;
} ;
plot . shutdown = shutdown ;
plot . shutdown = shutdown ;
plot . resize = function ( ) {
plot . resize = function ( ) {
getCanvasDimensions ( ) ;
var width = placeholder . width ( ) ,
resizeCanvas ( surface ) ;
height = placeholder . height ( ) ;
resizeCanvas ( overlay ) ;
surface . resize ( width , height ) ;
overlay . resize ( width , height ) ;
} ;
} ;
// public attributes
// public attributes
@ -730,111 +845,6 @@ Licensed under the MIT license.
} ) ;
} ) ;
}
}
//////////////////////////////////////////////////////////////////////////////////
// Returns the display's ratio between physical and device-independent pixels.
//
// This is the ratio between the width that the browser advertises and the number
// of pixels actually available in that space. The iPhone 4, for example, has a
// device-independent width of 320px, but its screen is actually 640px wide. It
// therefore has a pixel ratio of 2, while most normal devices have a ratio of 1.
function getPixelRatio ( cctx ) {
var devicePixelRatio = window . devicePixelRatio || 1 ;
var backingStoreRatio =
cctx . webkitBackingStorePixelRatio ||
cctx . mozBackingStorePixelRatio ||
cctx . msBackingStorePixelRatio ||
cctx . oBackingStorePixelRatio ||
cctx . backingStorePixelRatio || 1 ;
return devicePixelRatio / backingStoreRatio ;
}
function makeCanvas ( cls ) {
var c = document . createElement ( 'canvas' ) ;
c . className = cls ;
$ ( c ) . css ( { direction : "ltr" , position : "absolute" , left : 0 , top : 0 } )
. appendTo ( placeholder ) ;
// If HTML5 Canvas isn't available, fall back to Excanvas
if ( ! c . getContext ) {
if ( window . G _vmlCanvasManager ) {
c = window . G _vmlCanvasManager . initElement ( c ) ;
} else {
throw new Error ( "Canvas is not available. If you're using IE with a fall-back such as Excanvas, then there's either a mistake in your conditional include, or the page has no DOCTYPE and is rendering in Quirks Mode." ) ;
}
}
var cctx = c . getContext ( "2d" ) ;
// Increase the canvas density based on the display's pixel ratio; basically
// giving the canvas more pixels without increasing the size of its element,
// to take advantage of the fact that retina displays have that many more
// pixels than they actually use for page & element widths.
var pixelRatio = getPixelRatio ( cctx ) ;
c . width = canvasWidth * pixelRatio ;
c . height = canvasHeight * pixelRatio ;
c . style . width = canvasWidth + "px" ;
c . style . height = canvasHeight + "px" ;
// Save the context so we can reset in case we get replotted
cctx . save ( ) ;
// Scale the coordinate space to match the display density; so even though we
// may have twice as many pixels, we still want lines and other drawing to
// appear at the same size; the extra pixels will just make them crisper.
cctx . scale ( pixelRatio , pixelRatio ) ;
return c ;
}
function getCanvasDimensions ( ) {
canvasWidth = placeholder . width ( ) ;
canvasHeight = placeholder . height ( ) ;
if ( canvasWidth <= 0 || canvasHeight <= 0 )
throw new Error ( "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight ) ;
}
function resizeCanvas ( c ) {
var cctx = c . getContext ( "2d" ) ;
// Handle pixel ratios > 1 for retina displays, as explained in makeCanvas
var pixelRatio = getPixelRatio ( cctx ) ;
// Resizing should reset the state (excanvas seems to be buggy though)
if ( c . style . width != canvasWidth ) {
c . width = canvasWidth * pixelRatio ;
c . style . width = canvasWidth + "px" ;
}
if ( c . style . height != canvasHeight ) {
c . height = canvasHeight * pixelRatio ;
c . style . height = canvasHeight + "px" ;
}
// so try to get back to the initial state (even if it's
// gone now, this should be safe according to the spec)
cctx . restore ( ) ;
// and save again
cctx . save ( ) ;
// Apply scaling for retina displays, as explained in makeCanvas
cctx . scale ( pixelRatio , pixelRatio ) ;
}
function setupCanvases ( ) {
function setupCanvases ( ) {
var reused ,
var reused ,
existingSurface = placeholder . children ( "canvas.flot-base" ) ,
existingSurface = placeholder . children ( "canvas.flot-base" ) ,
@ -850,27 +860,25 @@ Licensed under the MIT license.
if ( placeholder . css ( "position" ) == 'static' )
if ( placeholder . css ( "position" ) == 'static' )
placeholder . css ( "position" , "relative" ) ; // for positioning labels and overlay
placeholder . css ( "position" , "relative" ) ; // for positioning labels and overlay
getCanvasDimensions ( ) ;
surface = new Canvas ( "flot-base" , placeholder ) ;
overlay = new Canvas ( "flot-overlay" , placeholder ) ; // overlay canvas for interactive features
surface = makeCanvas ( "flot-base" ) ;
overlay = makeCanvas ( "flot-overlay" ) ; // overlay canvas for interactive features
reused = false ;
reused = false ;
}
}
else {
else {
// reuse existing elements
// reuse existing elements
surface = existingSurface . get( 0 ) ;
surface = existingSurface . data( "canvas" ) ;
overlay = existingOverlay . get( 0 ) ;
overlay = existingOverlay . data( "canvas" ) ;
reused = true ;
reused = true ;
}
}
ctx = surface . getContext( "2d" ) ;
ctx = surface . context ;
octx = overlay . getContext( "2d" ) ;
octx = overlay . context ;
// define which element we're listening for events on
// define which element we're listening for events on
eventHolder = $ ( overlay );
eventHolder = $ ( overlay .element );
if ( reused ) {
if ( reused ) {
// run shutdown in the old plot object
// run shutdown in the old plot object
@ -880,11 +888,12 @@ Licensed under the MIT license.
plot . resize ( ) ;
plot . resize ( ) ;
// make sure overlay pixels are cleared (canvas is cleared when we redraw)
// make sure overlay pixels are cleared (canvas is cleared when we redraw)
octx . clearRect ( 0 , 0 , canvasWidth , canvasHeight ) ;
overlay . clear ( ) ;
// then whack any remaining obvious garbage left
// then whack any remaining obvious garbage left
eventHolder . unbind ( ) ;
eventHolder . unbind ( ) ;
placeholder . children ( ) . not ( [ surface , overlay ] ) . remove ( ) ;
placeholder . children ( ) . not ( [ surface .element , overlay . element ] ) . remove ( ) ;
}
}
// save in case we get replotted
// save in case we get replotted
@ -1053,7 +1062,7 @@ Licensed under the MIT license.
if ( pos == "bottom" ) {
if ( pos == "bottom" ) {
plotOffset . bottom += lh + axisMargin ;
plotOffset . bottom += lh + axisMargin ;
axis . box = { top : canvasH eight - plotOffset . bottom , height : lh } ;
axis . box = { top : surface. h eight - plotOffset . bottom , height : lh } ;
}
}
else {
else {
axis . box = { top : plotOffset . top + axisMargin , height : lh } ;
axis . box = { top : plotOffset . top + axisMargin , height : lh } ;
@ -1069,7 +1078,7 @@ Licensed under the MIT license.
}
}
else {
else {
plotOffset . right += lw + axisMargin ;
plotOffset . right += lw + axisMargin ;
axis . box = { left : canvasW idth - plotOffset . right , width : lw } ;
axis . box = { left : surface. w idth - plotOffset . right , width : lw } ;
}
}
}
}
@ -1085,11 +1094,11 @@ Licensed under the MIT license.
// dimension, we can set the remaining dimension coordinates
// dimension, we can set the remaining dimension coordinates
if ( axis . direction == "x" ) {
if ( axis . direction == "x" ) {
axis . box . left = plotOffset . left - axis . labelWidth / 2 ;
axis . box . left = plotOffset . left - axis . labelWidth / 2 ;
axis . box . width = canvasW idth - plotOffset . left - plotOffset . right + axis . labelWidth ;
axis . box . width = surface. w idth - plotOffset . left - plotOffset . right + axis . labelWidth ;
}
}
else {
else {
axis . box . top = plotOffset . top - axis . labelHeight / 2 ;
axis . box . top = plotOffset . top - axis . labelHeight / 2 ;
axis . box . height = canvasH eight - plotOffset . bottom - plotOffset . top + axis . labelHeight ;
axis . box . height = surface. h eight - plotOffset . bottom - plotOffset . top + axis . labelHeight ;
}
}
}
}
@ -1198,8 +1207,8 @@ Licensed under the MIT license.
} ) ;
} ) ;
}
}
plotWidth = canvasW idth - plotOffset . left - plotOffset . right ;
plotWidth = surface. w idth - plotOffset . left - plotOffset . right ;
plotHeight = canvasH eight - plotOffset . bottom - plotOffset . top ;
plotHeight = surface. h eight - plotOffset . bottom - plotOffset . top ;
// now we got the proper plot dimensions, we can compute the scaling
// now we got the proper plot dimensions, we can compute the scaling
$ . each ( axes , function ( _ , axis ) {
$ . each ( axes , function ( _ , axis ) {
@ -1258,7 +1267,7 @@ Licensed under the MIT license.
else
else
// heuristic based on the model a*sqrt(x) fitted to
// heuristic based on the model a*sqrt(x) fitted to
// some data points that seemed reasonable
// some data points that seemed reasonable
noTicks = 0.3 * Math . sqrt ( axis . direction == "x" ? canvasWidth : canvasH eight) ;
noTicks = 0.3 * Math . sqrt ( axis . direction == "x" ? surface. width : surface . h eight) ;
axis . delta = ( axis . max - axis . min ) / noTicks ;
axis . delta = ( axis . max - axis . min ) / noTicks ;
@ -1429,7 +1438,8 @@ Licensed under the MIT license.
}
}
function draw ( ) {
function draw ( ) {
ctx . clearRect ( 0 , 0 , canvasWidth , canvasHeight ) ;
surface . clear ( ) ;
executeHooks ( hooks . drawBackground , [ ctx ] ) ;
executeHooks ( hooks . drawBackground , [ ctx ] ) ;
@ -2543,7 +2553,7 @@ Licensed under the MIT license.
// draw highlights
// draw highlights
octx . save ( ) ;
octx . save ( ) ;
o ctx. clearRect ( 0 , 0 , canvasWidth , canvasHeight ) ;
o verlay. clear ( ) ;
octx . translate ( plotOffset . left , plotOffset . top ) ;
octx . translate ( plotOffset . left , plotOffset . top ) ;
var i , hi ;
var i , hi ;