pbosetti-flotr 1.3 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/jquery.flot.js DELETED
@@ -1,2187 +0,0 @@
1
- /*
2
- * Flot v0.9.0
3
- *
4
- * Released under the MIT license.
5
- */
6
-
7
- ( function( $ ) {
8
- function Plot( target_, data_, options_ ) {
9
- var series = [];
10
- var options = {
11
- // the color theme used for graphs
12
- colors: ["#edc240", "#afd8f8", "#cb4b4b", "#4da74d", "#9440ed"],
13
- legend: {
14
- show: true,
15
- noColumns: 1, // number of colums in legend table
16
- labelFormatter: null, // fn: string -> string
17
- labelBoxBorderColor: "#ccc", // border color for the little label boxes
18
- container: null, // container (as jQuery object) to put legend in, null means default on top of graph
19
- position: "ne", // position of default legend container within plot
20
- margin: 5, // distance from grid edge to default legend container within plot
21
- backgroundColor: null, // null means auto-detect
22
- backgroundOpacity: 0.85 // set to 0 to avoid background
23
- },
24
- xaxis: {
25
- label: null,
26
- showLabels: true,
27
- mode: null, // null or "time"
28
- min: null, // min. value to show, null means set automatically
29
- max: null, // max. value to show, null means set automatically
30
- autoscaleMargin: null, // margin in % to add if auto-setting min/max
31
- ticks: null, // either [1, 3] or [[1, "a"], 3] or (fn: axis info -> ticks) or app. number of ticks for auto-ticks
32
- tickFormatter: null, // fn: number -> string
33
- labelWidth: null, // size of tick labels in pixels
34
- labelHeight: null,
35
-
36
- // mode specific options
37
- tickDecimals: null, // no. of decimals, null means auto
38
- tickSize: null, // number or [number, "unit"]
39
- minTickSize: null, // number or [number, "unit"]
40
- monthNames: null, // list of names of months
41
- timeformat: null // format string to use
42
- },
43
- yaxis: {
44
- label: null,
45
- showLabels: true,
46
- autoscaleMargin: 0.02
47
- },
48
- points: {
49
- show: false,
50
- radius: 3,
51
- lineWidth: 2, // in pixels
52
- fill: true,
53
- fillColor: "#ffffff"
54
- },
55
- lines: {
56
- show: false,
57
- lineWidth: 2, // in pixels
58
- fill: false,
59
- fillColor: null
60
- },
61
- bars: {
62
- show: false,
63
- lineWidth: 2, // in pixels
64
- barWidth: 1, // in units of the x axis
65
- fill: true,
66
- fillOpacity: 0.4,
67
- fillColor: null
68
- },
69
- deltas: {
70
- show: false,
71
- color: { above: '#A00', below: '#00A', equal: '#D52' },
72
- markerWidth: 3
73
- },
74
- grid: {
75
- showLines: 'both',
76
- showBorder: true,
77
- markers: [], // see API.txt for details
78
- labelFontSize: 16, // default is 16px font size for axis labels
79
- color: "#545454", // primary color used for outline and labels
80
- backgroundColor: null, // null for transparent, else color
81
- tickColor: "#dddddd", // color used for the ticks
82
- tickWidth: 1, // thickness of grid lines
83
- labelMargin: 3, // in pixels
84
- borderWidth: 2,
85
- clickable: null,
86
- hoverable: false,
87
- hoverColor: null,
88
- hoverFill: null,
89
- hoverRadius: null,
90
- mouseCatchingArea: 15,
91
- coloredAreas: null, // array of { x1, y1, x2, y2 } or fn: plot area -> areas
92
- coloredAreasColor: "#f4f4f4"
93
- },
94
- hints: {
95
- show: false,
96
- showColorBox: true,
97
- showSeriesLabel: true,
98
- labelFormatter: defaultLabelFormatter,
99
- hintFormatter: defaultHintFormatter,
100
- backgroundColor: "#DDD", // null means auto-detect
101
- backgroundOpacity: 0.7, // set to 0 to avoid background
102
- borderColor: "#BBB" // set to 'transparent' for none
103
- },
104
- selection: {
105
- snapToTicks: false, // boolean for if we should snap to ticks on selection
106
- mode: null, // one of null, "x", "y" or "xy"
107
- color: "#e8cfac"
108
- },
109
- shadowSize: 4,
110
- sortData: true
111
- };
112
-
113
- var canvas = null, overlay = null, eventHolder = null,
114
- ctx = null, octx = null,
115
- target = target_,
116
- xaxis = {}, yaxis = {},
117
- plotOffset = { left: 0, right: 0, top: 0, bottom: 0},
118
- canvasWidth = 0, canvasHeight = 0,
119
- plotWidth = 0, plotHeight = 0,
120
- hozScale = 0, vertScale = 0,
121
- hintDiv = null, hintBackground = null,
122
- lastMarker = null,
123
- // dedicated to storing data for buggy standard compliance cases
124
- workarounds = {},
125
- // buffer constants
126
- RIGHT_SIDE_BUFFER = 10,
127
- BOTTOM_SIDE_BUFFER = 10;
128
-
129
- this.setData = setData;
130
- this.setupGrid = setupGrid;
131
- this.highlight = highlight;
132
- this.draw = draw;
133
- this.cleanup = cleanup;
134
- this.clearSelection = clearSelection;
135
- this.setSelection = setSelection;
136
- this.getCanvas = function () { return canvas; };
137
- this.getPlotOffset = function () { return plotOffset; };
138
- this.getData = function () { return series; };
139
- this.getAxes = function () { return { xaxis: xaxis, yaxis: yaxis }; };
140
-
141
- // initialize
142
- $.extend( true, options, options_ );
143
- setData( data_ );
144
- constructCanvas();
145
- setupGrid();
146
- draw();
147
-
148
- // kill hints and highlighted points when the mouse leaves the graph
149
- if( options.grid.hoverable ) $(target).mouseout( cleanup );
150
-
151
- function setData( d ) {
152
- series = parseData(d);
153
- fillInSeriesOptions();
154
- processData();
155
- }
156
-
157
- // normalize the data given to the call to $.plot. If we're
158
- // going to be monitoring mousemove's then sort the data
159
- function parseData( d ) {
160
- function sortData( a, b ) {
161
- if( !a || !b ) return 0;
162
- if( a.x > b.x ) return 1;
163
- else if( a.x < b.x ) return -1;
164
- else return 0;
165
- }
166
-
167
- var res = [];
168
- for( var i = 0; i < d.length; ++i ) {
169
- var s = {};
170
- if( d[i].data ) {
171
- for( var v in d[i] ) s[v] = d[i][v];
172
- }
173
- else {
174
- s.data = d[i];
175
- }
176
- res.push( s );
177
- }
178
-
179
- // normalize the old style [x,y] data format
180
- for( var i in res ) {
181
- for( var j in res[i].data ) {
182
- var datapoint = res[i].data[j];
183
- if( datapoint != null && datapoint.x == undefined ) {
184
- res[i].data[j] = { x: datapoint[0], y: datapoint[1] };
185
- }
186
- }
187
- if( options.sortData ) res[i].data.sort( sortData );
188
- }
189
- return res;
190
- }
191
-
192
- function fillInSeriesOptions() {
193
- var i;
194
-
195
- // collect what we already got of colors
196
- var neededColors = series.length;
197
- var usedColors = [];
198
- var assignedColors = [];
199
- for( i = 0; i < series.length; ++i ) {
200
- var sc = series[i].color;
201
- if( sc != null ) {
202
- --neededColors;
203
- if( typeof sc == "number" ) { assignedColors.push( sc ); }
204
- else { usedColors.push( parseColor( series[i].color ) ); }
205
- }
206
- }
207
-
208
- // we might need to generate more colors if higher indices
209
- // are assigned
210
- for( i = 0; i < assignedColors.length; ++i ) {
211
- neededColors = Math.max( neededColors, assignedColors[i] + 1 );
212
- }
213
-
214
- // produce colors as needed
215
- var colors = [];
216
- var variation = 0;
217
- i = 0;
218
- while( colors.length < neededColors ) {
219
- var c;
220
- if( options.colors.length == i ) { c = new Color( 100, 100, 100 ); }
221
- else { c = parseColor( options.colors[i] ); }
222
-
223
- // vary color if needed
224
- var sign = variation % 2 == 1 ? -1 : 1;
225
- var factor = 1 + sign * Math.ceil( variation / 2 ) * 0.2;
226
- c.scale( factor, factor, factor );
227
-
228
- // FIXME: if we're getting to close to something else,
229
- // we should probably skip this one
230
- colors.push( c );
231
-
232
- ++i;
233
- if( i >= options.colors.length ) {
234
- i = 0;
235
- ++variation;
236
- }
237
- }
238
-
239
- // fill in the options
240
- var colori = 0, s;
241
- for( i = 0; i < series.length; ++i ) {
242
- s = series[i];
243
-
244
- // assign colors
245
- if( s.color == null ) {
246
- s.color = colors[colori].toString();
247
- ++colori;
248
- }
249
- else if( typeof s.color == "number" ) {
250
- s.color = colors[s.color].toString();
251
- }
252
-
253
- // copy the rest
254
- s.lines = $.extend(true, {}, options.lines, s.lines);
255
- s.points = $.extend(true, {}, options.points, s.points);
256
- s.bars = $.extend(true, {}, options.bars, s.bars);
257
- s.deltas = $.extend(true, {}, options.deltas, s.deltas);
258
- s.hints = $.extend(true, {}, options.hints, s.hints);
259
- if (s.shadowSize == null) s.shadowSize = options.shadowSize;
260
- }
261
- }
262
-
263
- function processData() {
264
- var top_sentry = Number.POSITIVE_INFINITY,
265
- bottom_sentry = Number.NEGATIVE_INFINITY;
266
-
267
- xaxis.datamin = yaxis.datamin = top_sentry;
268
- xaxis.datamax = yaxis.datamax = bottom_sentry;
269
-
270
- for( var i = 0; i < series.length; ++i ) {
271
- var data = series[i].data;
272
- for( var j = 0; j < data.length; ++j ) {
273
- if( data[j] == null ) continue;
274
-
275
- var x = data[j].x, y = data[j].y;
276
-
277
- // convert to number
278
- if( x == null || y == null ||
279
- isNaN( x = +x ) || isNaN( y = +y ) ) {
280
- data[j] = null; // mark this point as invalid
281
- continue;
282
- }
283
-
284
- if( x < xaxis.datamin ) xaxis.datamin = x;
285
- if( x > xaxis.datamax ) xaxis.datamax = x;
286
- if( y < yaxis.datamin ) yaxis.datamin = y;
287
- if( y > yaxis.datamax ) yaxis.datamax = y;
288
- }
289
- }
290
-
291
- if( xaxis.datamin == top_sentry ) xaxis.datamin = 0;
292
- if( yaxis.datamin == top_sentry ) yaxis.datamin = 0;
293
- if( xaxis.datamax == bottom_sentry ) xaxis.datamax = 1;
294
- if( yaxis.datamax == bottom_sentry ) yaxis.datamax = 1;
295
- }
296
-
297
- function constructCanvas() {
298
- canvasWidth = target.width();
299
- canvasHeight = target.height();
300
- target.html( '' ).css( 'position', 'relative' );
301
-
302
- if( canvasWidth <= 0 || canvasHeight <= 0 ) {
303
- throw "Invalid dimensions for plot, width = " + canvasWidth + ", height = " + canvasHeight;
304
- }
305
-
306
- // the canvas
307
- canvas = $('<canvas width="' + canvasWidth + '" height="' + canvasHeight + '"></canvas>').appendTo( target ).get( 0 );
308
- if( $.browser.msie ) { canvas = window.G_vmlCanvasManager.initElement( canvas ); }
309
- ctx = canvas.getContext( '2d' );
310
-
311
- // overlay canvas for interactive features
312
- overlay = $('<canvas style="position:absolute;left:0px;top:0px;" width="' + canvasWidth + '" height="' + canvasHeight + '"></canvas>').appendTo( target ).get( 0 );
313
- if( $.browser.msie ) { overlay = window.G_vmlCanvasManager.initElement( overlay ); }
314
- octx = overlay.getContext( '2d' );
315
-
316
- // we include the canvas in the event holder too, because IE 7
317
- // sometimes has trouble with the stacking order
318
- eventHolder = $( [ overlay, canvas ] );
319
-
320
- // bind events
321
- if( options.selection.mode != null ) {
322
- eventHolder.mousedown( onMouseDown ).mousemove( onMouseMove );
323
- }
324
-
325
- if( options.grid.hoverable ) {
326
- eventHolder.mousemove( onMouseMove );
327
- }
328
-
329
- if( options.grid.clickable ) {
330
- eventHolder.click( onClick );
331
- }
332
- }
333
-
334
- function setupGrid() {
335
- // x axis
336
- setRange( xaxis, options.xaxis );
337
- prepareTickGeneration( xaxis, options.xaxis );
338
- setTicks( xaxis, options.xaxis );
339
- extendXRangeIfNeededByBar();
340
-
341
- // y axis
342
- setRange( yaxis, options.yaxis );
343
- prepareTickGeneration( yaxis, options.yaxis );
344
- setTicks( yaxis, options.yaxis );
345
-
346
- setSpacing();
347
- insertTickLabels();
348
- insertLegend();
349
- insertAxisLabels();
350
- }
351
-
352
- function setRange( axis, axisOptions ) {
353
- var min = axisOptions.min != null ? axisOptions.min : axis.datamin;
354
- var max = axisOptions.max != null ? axisOptions.max : axis.datamax;
355
-
356
- if( max - min == 0.0 ) {
357
- // degenerate case
358
- var widen;
359
- if( max == 0.0 ) widen = 1.0;
360
- else widen = 0.01;
361
-
362
- min -= widen;
363
- max += widen;
364
- }
365
- else {
366
- // consider autoscaling
367
- var margin = axisOptions.autoscaleMargin;
368
- if( margin != null ) {
369
- if( axisOptions.min == null ) {
370
- min -= ( max - min ) * margin;
371
- // make sure we don't go below zero if all values
372
- // are positive
373
- if( min < 0 && axis.datamin >= 0 ) min = 0;
374
- }
375
- if( axisOptions.max == null ) {
376
- max += ( max - min ) * margin;
377
- if( max > 0 && axis.datamax <= 0 ) max = 0;
378
- }
379
- }
380
- }
381
- axis.min = min;
382
- axis.max = max;
383
- }
384
-
385
- function prepareTickGeneration( axis, axisOptions ) {
386
- // estimate number of ticks
387
- var noTicks;
388
- if( typeof axisOptions.ticks == "number" && axisOptions.ticks > 0 ) {
389
- noTicks = axisOptions.ticks;
390
- }
391
- else if( axis == xaxis ) {
392
- noTicks = canvasWidth / 100;
393
- }
394
- else {
395
- noTicks = canvasHeight / 60;
396
- }
397
-
398
- var delta = ( axis.max - axis.min ) / noTicks;
399
- var size, generator, unit, formatter, i, magn, norm;
400
-
401
- if( axisOptions.mode == "time" ) {
402
- // pretty handling of time
403
-
404
- function formatDate( d, fmt, monthNames ) {
405
- var leftPad = function( n ) {
406
- n = '' + n;
407
- return n.length == 1 ? '0' + n : n;
408
- };
409
-
410
- var r = [];
411
- var escape = false;
412
- if( monthNames == null ) {
413
- monthNames = [ "Jan", "Feb", "Mar", "Apr", "May",
414
- "Jun", "Jul", "Aug", "Sep", "Oct",
415
- "Nov", "Dec" ];
416
- }
417
- for( var i = 0; i < fmt.length; ++i ) {
418
- var c = fmt.charAt( i );
419
-
420
- if( escape ) {
421
- switch (c) {
422
- case 'h': c = "" + d.getUTCHours(); break;
423
- case 'H': c = leftPad( d.getUTCHours() ); break;
424
- case 'M': c = leftPad( d.getUTCMinutes() ); break;
425
- case 'S': c = leftPad( d.getUTCSeconds() ); break;
426
- case 'd': c = "" + d.getUTCDate(); break;
427
- case 'm': c = "" + ( d.getUTCMonth() + 1 ); break;
428
- case 'y': c = "" + d.getUTCFullYear(); break;
429
- case 'b': c = "" + monthNames[d.getUTCMonth()]; break;
430
- }
431
- r.push( c );
432
- escape = false;
433
- }
434
- else {
435
- if( c == "%" ) escape = true;
436
- else r.push( c );
437
- }
438
- }
439
- return r.join( '' );
440
- }
441
-
442
- // map of app. size of time units in milliseconds
443
- var timeUnitSize = {
444
- "second": 1000,
445
- "minute": 60 * 1000,
446
- "hour": 60 * 60 * 1000,
447
- "day": 24 * 60 * 60 * 1000,
448
- "month": 30 * 24 * 60 * 60 * 1000,
449
- "year": 365.2425 * 24 * 60 * 60 * 1000
450
- };
451
-
452
- // the allowed tick sizes, after 1 year we use
453
- // an integer algorithm
454
- var spec = [
455
- [ 1, "second" ], [ 2, "second" ], [ 5, "second" ],
456
- [ 10, "second" ], [ 30, "second" ], [ 1, "minute" ],
457
- [ 2, "minute" ], [ 5, "minute" ], [ 10, "minute" ],
458
- [ 30, "minute" ], [ 1, "hour" ], [ 2, "hour" ],
459
- [ 4, "hour" ], [ 8, "hour" ], [ 12, "hour" ],
460
- [ 1, "day" ], [ 2, "day" ], [ 3, "day" ],
461
- [ 0.25, "month" ], [ 0.5, "month" ], [ 1, "month" ],
462
- [ 2, "month" ], [ 3, "month" ], [ 6, "month" ],
463
- [ 1, "year" ]
464
- ];
465
-
466
- var minSize = 0;
467
- if( axisOptions.minTickSize != null ) {
468
- minSize = typeof axisOptions.tickSize == 'number' ?
469
- axisOptions.tickSize:
470
- axisOptions.minTickSize[0] *
471
- timeUnitSize[axisOptions.minTickSize[1]];
472
- }
473
-
474
- for( i = 0; i < spec.length - 1; ++i ) {
475
- var d = spec[i][0] * timeUnitSize[spec[i][1]] +
476
- spec[i + 1][0] * timeUnitSize[spec[i + 1][1]];
477
- if( delta < d / 2 &&
478
- spec[i][0] * timeUnitSize[spec[i][1]] >= minSize ) {
479
- break;
480
- }
481
- }
482
-
483
- size = spec[i][0];
484
- unit = spec[i][1];
485
-
486
- // special-case the possibility of several years
487
- if( unit == "year" ) {
488
- magn = Math.pow( 10,
489
- Math.floor(
490
- Math.log( delta / timeUnitSize.year ) / Math.LN10
491
- )
492
- );
493
- norm = ( delta / timeUnitSize.year ) / magn;
494
- if( norm < 1.5 ) size = 1;
495
- else if( norm < 3 ) size = 2;
496
- else if( norm < 7.5 ) size = 5;
497
- else size = 10;
498
-
499
- size *= magn;
500
- }
501
-
502
- if( axisOptions.tickSize ) {
503
- size = axisOptions.tickSize[0];
504
- unit = axisOptions.tickSize[1];
505
- }
506
-
507
- generator = function( axis ) {
508
- var ticks = [],
509
- tickSize = axis.tickSize[0],
510
- unit = axis.tickSize[1],
511
- d = new Date( axis.min );
512
-
513
- var step = tickSize * timeUnitSize[unit];
514
-
515
- if( unit == 'second' ) d.setUTCSeconds( floorInBase( d.getUTCSeconds(), tickSize ) );
516
- if( unit == 'minute' ) d.setUTCMinutes( floorInBase( d.getUTCMinutes(), tickSize ) );
517
- if( unit == 'hour' ) d.setUTCHours( floorInBase( d.getUTCHours(), tickSize ) );
518
- if( unit == 'month' ) d.setUTCMonth( floorInBase( d.getUTCMonth(), tickSize ) );
519
- if( unit == 'year' ) d.setUTCFullYear( floorInBase( d.getUTCFullYear(), tickSize ) );
520
-
521
- // reset smaller components
522
- d.setUTCMilliseconds( 0 );
523
- if( step >= timeUnitSize.minute ) d.setUTCSeconds( 0 );
524
- if( step >= timeUnitSize.hour ) d.setUTCMinutes( 0 );
525
- if( step >= timeUnitSize.day ) d.setUTCHours( 0 );
526
- if( step >= timeUnitSize.day * 4 ) d.setUTCDate( 1 );
527
- if( step >= timeUnitSize.year ) d.setUTCMonth( 0 );
528
-
529
- var carry = 0,
530
- v = Number.NaN,
531
- prev;
532
-
533
- do {
534
- prev = v;
535
- v = d.getTime();
536
- ticks.push( { v: v, label: axis.tickFormatter( v, axis ) } );
537
- if( unit == 'month' ) {
538
- if( tickSize < 1 ) {
539
- // a bit complicated - we'll divide the month
540
- // up but we need to take care of fractions
541
- // so we don't end up in the middle of a day
542
- d.setUTCDate( 1 );
543
- var start = d.getTime();
544
- d.setUTCMonth( d.getUTCMonth() + 1 );
545
- var end = d.getTime();
546
- d.setTime( v + carry * timeUnitSize.hour +
547
- ( end - start ) * tickSize );
548
- carry = d.getUTCHours();
549
- d.setUTCHours( 0 );
550
- }
551
- else {
552
- d.setUTCMonth( d.getUTCMonth() + tickSize );
553
- }
554
- }
555
- else if( unit == 'year' ) {
556
- d.setUTCFullYear( d.getUTCFullYear() + tickSize );
557
- }
558
- else {
559
- d.setTime( v + step );
560
- }
561
- } while( v < axis.max && v != prev );
562
-
563
- return ticks;
564
- };
565
-
566
- formatter = function( v, axis ) {
567
- var d = new Date( v );
568
-
569
- // first check global format
570
- if( axisOptions.timeformat ) {
571
- return formatDate( d, axisOptions.timeformat, axisOptions.monthNames );
572
- }
573
-
574
- var t = axis.tickSize[0] * timeUnitSize[axis.tickSize[1]];
575
- var span = axis.max - axis.min;
576
-
577
- if( t < timeUnitSize.minute ) { fmt = "%h:%M:%S"; }
578
- else if( t < timeUnitSize.day ) {
579
- if( span < 2 * timeUnitSize.day ) fmt = "%h:%M";
580
- else fmt = "%b %d %h:%M";
581
- }
582
- else if( t < timeUnitSize.month ) { fmt = "%b %d"; }
583
- else if( t < timeUnitSize.year ) {
584
- if( span < timeUnitSize.year ) fmt = "%b";
585
- else fmt = "%b %y";
586
- }
587
- else { fmt = "%y"; }
588
-
589
- return formatDate( d, fmt, axisOptions.monthNames );
590
- };
591
- }
592
- else {
593
- // pretty rounding of base-10 numbers
594
- var maxDec = axisOptions.tickDecimals;
595
- var dec = -Math.floor( Math.log( delta ) / Math.LN10 );
596
- if( maxDec && dec > maxDec ) dec = maxDec;
597
-
598
- magn = Math.pow( 10, -dec );
599
- norm = delta / magn; // norm is between 1.0 and 10.0
600
-
601
- if( norm < 1.5 ) { size = 1; }
602
- else if( norm < 3 ) {
603
- size = 2;
604
- // special case for 2.5, requires an extra decimal
605
- if( norm > 2.25 && ( !maxDec || dec + 1 <= maxDec ) ) {
606
- size = 2.5;
607
- ++dec;
608
- }
609
- }
610
- else if( norm < 7.5 ) { size = 5; }
611
- else { size = 10; }
612
-
613
- size *= magn;
614
-
615
- if( axisOptions.minTickSize && size < axisOptions.minTickSize ) {
616
- size = axisOptions.minTickSize;
617
- }
618
-
619
- if( axisOptions.tickSize ) {
620
- size = axisOptions.tickSize;
621
- }
622
-
623
- axis.tickDecimals = Math.max( 0, ( maxDec ) ? maxDec : dec );
624
-
625
- generator = function( axis ) {
626
- var ticks = [];
627
- var start = floorInBase( axis.min, axis.tickSize );
628
- // then spew out all possible ticks
629
- var i = 0,
630
- v = Number.NaN,
631
- prev;
632
-
633
- do {
634
- prev = v;
635
- v = start + i * axis.tickSize;
636
- ticks.push( { v: v, label: axis.tickFormatter( v, axis ) } );
637
- ++i;
638
- } while( v < axis.max && v != prev );
639
- return ticks;
640
- };
641
-
642
- formatter = function( v, axis ) {
643
- return v.toFixed( axis.tickDecimals );
644
- };
645
- }
646
-
647
- axis.tickSize = unit ? [size, unit] : size;
648
- axis.tickGenerator = generator;
649
- if( $.isFunction( axisOptions.tickFormatter ) ) {
650
- axis.tickFormatter = function( v, axis ) {
651
- return '' + axisOptions.tickFormatter( v, axis );
652
- };
653
- }
654
- else {
655
- axis.tickFormatter = formatter;
656
- }
657
-
658
- if( axisOptions.labelWidth ) axis.labelWidth = axisOptions.labelWidth;
659
- if( axisOptions.labelHeight ) axis.labelHeight = axisOptions.labelHeight;
660
- }
661
-
662
- function extendXRangeIfNeededByBar() {
663
- if( !options.xaxis.max ) {
664
- // great, we're autoscaling, check if we might need a bump
665
- var newmax = xaxis.max;
666
- for( var i = 0; i < series.length; ++i ) {
667
- if( series[i].bars.show && series[i].bars.barWidth +
668
- xaxis.datamax > newmax ) {
669
- newmax = xaxis.datamax + series[i].bars.barWidth;
670
- }
671
- }
672
- xaxis.max = newmax;
673
- }
674
- }
675
-
676
- function setTicks( axis, axisOptions ) {
677
- axis.ticks = [];
678
-
679
- if( !axisOptions.ticks ) {
680
- axis.ticks = axis.tickGenerator( axis );
681
- }
682
- else if( typeof axisOptions.ticks == 'number' ) {
683
- if( axisOptions.ticks > 0 ) axis.ticks = axis.tickGenerator( axis );
684
- }
685
- else if( axisOptions.ticks ) {
686
- var ticks = axisOptions.ticks;
687
-
688
- if( $.isFunction( ticks ) ) {
689
- // generate the ticks
690
- ticks = ticks( { min: axis.min, max: axis.max } );
691
- }
692
-
693
- // clean up the user-supplied ticks, copy them over
694
- var i, v;
695
- for( i = 0; i < ticks.length; ++i ) {
696
- var label = null;
697
- var t = ticks[i];
698
- if( typeof t == 'object' ) {
699
- v = t[0];
700
- if( t.length > 1 ) label = t[1];
701
- }
702
- else {
703
- v = t;
704
- }
705
-
706
- if( !label ) label = axis.tickFormatter( v, axis );
707
- axis.ticks[i] = { v: v, label: label };
708
- }
709
- }
710
-
711
- if( axisOptions.autoscaleMargin && axis.ticks.length > 0 ) {
712
- // snap to ticks
713
- if( !axisOptions.min ) {
714
- axis.min = Math.min( axis.min, axis.ticks[0].v );
715
- }
716
- if( !axisOptions.max && axis.ticks.length > 1 ) {
717
- axis.max = Math.min( axis.max, axis.ticks[axis.ticks.length - 1].v );
718
- }
719
- }
720
- }
721
-
722
- function setSpacing() {
723
- var i, l,
724
- labels = [];
725
-
726
- if( !yaxis.labelWidth || !yaxis.labelHeight ) {
727
- // calculate y label dimensions
728
- for( i = 0; i < yaxis.ticks.length; ++i ) {
729
- l = yaxis.ticks[i].label;
730
- if( l ) labels.push( '<div class="tickLabel">' + l + '</div>' );
731
- }
732
-
733
- if( labels.length > 0 ) {
734
- var dummyDiv = $( '<div style="position:absolute;top:-10000px;font-size:smaller">' +
735
- labels.join('') + '</div>' ).appendTo( target );
736
- if( !yaxis.labelWidth ) yaxis.labelWidth = dummyDiv.width();
737
- if( !yaxis.labelHeight ) yaxis.labelHeight = dummyDiv.find('div').height();
738
- dummyDiv.remove();
739
- }
740
-
741
- if( !yaxis.labelWidth ) yaxis.labelWidth = 0;
742
- if( !yaxis.labelHeight ) yaxis.labelHeight = 0;
743
- }
744
-
745
- var maxOutset = options.grid.borderWidth / 2;
746
- if( options.points.show ) {
747
- maxOutset = Math.max( maxOutset, options.points.radius +
748
- options.points.lineWidth / 2 );
749
- }
750
- for( i = 0; i < series.length; ++i ) {
751
- if( series[i].points.show ) {
752
- maxOutset = Math.max( maxOutset, series[i].points.radius +
753
- series[i].points.lineWidth / 2 );
754
- }
755
- }
756
-
757
- plotOffset.left = plotOffset.right = plotOffset.top = plotOffset.bottom = maxOutset;
758
-
759
- if( yaxis.labelWidth > 0 && options.xaxis.showLabels ) {
760
- plotOffset.left += yaxis.labelWidth + options.grid.labelMargin;
761
- }
762
-
763
- plotWidth = canvasWidth - plotOffset.left - plotOffset.right - RIGHT_SIDE_BUFFER;
764
-
765
- // set width for labels; to avoid measuring the widths of
766
- // the labels, we construct fixed-size boxes and put the
767
- // labels inside them, the fixed-size boxes are easy to
768
- // mid-align
769
- if( !xaxis.labelWidth ) xaxis.labelWidth = plotWidth / 6;
770
-
771
- if( !xaxis.labelHeight ) {
772
- // measure x label heights
773
- labels = [];
774
- for( i = 0; i < xaxis.ticks.length; ++i ) {
775
- l = xaxis.ticks[i].label;
776
- if( l ) labels.push( '<span class="tickLabel" width="' + xaxis.labelWidth + '">' + l + '</span>' );
777
- }
778
-
779
- xaxis.labelHeight = 0;
780
- if( labels.length > 0 ) {
781
- var dummyDiv = $( '<div style="position:absolute;top:-10000px;font-size:smaller">' +
782
- labels.join('') + '</div>' ).appendTo( target );
783
- xaxis.labelHeight = dummyDiv.height();
784
- dummyDiv.remove();
785
- }
786
- }
787
-
788
- if( xaxis.labelHeight > 0 && options.yaxis.showLabels ) {
789
- plotOffset.bottom += xaxis.labelHeight + options.grid.labelMargin;
790
- }
791
-
792
- // add a bit of extra buffer on the bottom of the graph to account
793
- // for the axis label, if there is one
794
- if( options.xaxis.label ) plotOffset.bottom += BOTTOM_SIDE_BUFFER;
795
-
796
- plotHeight = canvasHeight - plotOffset.bottom - BOTTOM_SIDE_BUFFER - plotOffset.top;
797
- hozScale = plotWidth / ( xaxis.max - xaxis.min );
798
- vertScale = plotHeight / ( yaxis.max - yaxis.min );
799
- }
800
-
801
- function draw() {
802
- drawGrid();
803
- drawMarkers();
804
- for( var i = 0; i < series.length; i++ ) {
805
- drawSeries( series[i] );
806
- }
807
- }
808
-
809
- function tHoz( x ) { return ( x - xaxis.min ) * hozScale; }
810
- function tVert( y ) { return plotHeight - ( y - yaxis.min ) * vertScale; }
811
-
812
- function drawGrid() {
813
- var i;
814
-
815
- ctx.save();
816
- ctx.clearRect( 0, 0, canvasWidth, canvasHeight );
817
- ctx.translate( plotOffset.left, plotOffset.top );
818
-
819
- // draw background, if any
820
- if( options.grid.backgroundColor ) {
821
- ctx.fillStyle = options.grid.backgroundColor;
822
- ctx.fillRect( 0, 0, plotWidth, plotHeight );
823
- }
824
-
825
- // draw colored areas
826
- if( options.grid.coloredAreas ) {
827
- var areas = options.grid.coloredAreas;
828
- if( $.isFunction( areas ) ) {
829
- areas = areas( { xmin: xaxis.min, xmax: xaxis.max,
830
- ymin: yaxis.min, ymax: yaxis.max } );
831
- }
832
-
833
- for( i = 0; i < areas.length; ++i ) {
834
- var a = areas[i];
835
-
836
- // clip
837
- if( !a.x1 || a.x1 < xaxis.min ) a.x1 = xaxis.min;
838
- if( !a.x2 || a.x2 > xaxis.max ) a.x2 = xaxis.max;
839
- if( !a.y1 || a.y1 < yaxis.min ) a.y1 = yaxis.min;
840
- if( !a.y2 || a.y2 > yaxis.max ) a.y2 = yaxis.max;
841
-
842
- var tmp;
843
- if( a.x1 > a.x2 ) {
844
- tmp = a.x1;
845
- a.x1 = a.x2;
846
- a.x2 = tmp;
847
- }
848
- if (a.y1 > a.y2) {
849
- tmp = a.y1;
850
- a.y1 = a.y2;
851
- a.y2 = tmp;
852
- }
853
-
854
- if( a.x1 >= xaxis.max || a.x2 <= xaxis.min || a.x1 == a.x2 ||
855
- a.y1 >= yaxis.max || a.y2 <= yaxis.min || a.y1 == a.y2 ) {
856
- continue;
857
- }
858
-
859
- ctx.fillStyle = a.color || options.grid.coloredAreasColor;
860
- ctx.fillRect( Math.floor( tHoz( a.x1 ) ),
861
- Math.floor( tVert( a.y2 ) ),
862
- Math.floor( tHoz( a.x2 ) - tHoz( a.x1 ) ),
863
- Math.floor( tVert( a.y1 ) - tVert( a.y2 ) ) );
864
- }
865
- }
866
-
867
- // draw the inner grid
868
- ctx.lineWidth = options.grid.tickWidth;
869
- ctx.strokeStyle = options.grid.tickColor;
870
- ctx.beginPath();
871
- var v;
872
- if( options.grid.showLines == 'x' || options.grid.showLines == 'both' ) {
873
- for( i = 0; i < xaxis.ticks.length; ++i ) {
874
- v = xaxis.ticks[i].v;
875
- // skip those lying on the axes
876
- if( v <= xaxis.min || v >= xaxis.max ) continue;
877
-
878
- ctx.moveTo( Math.floor( tHoz( v ) ) + ctx.lineWidth / 2, 0 );
879
- ctx.lineTo( Math.floor( tHoz( v ) ) + ctx.lineWidth / 2, plotHeight );
880
- }
881
- }
882
-
883
- if( options.grid.showLines == 'y' || options.grid.showLines == 'both' ) {
884
- for( i = 0; i < yaxis.ticks.length; ++i ) {
885
- v = yaxis.ticks[i].v;
886
- if( v <= yaxis.min || v >= yaxis.max ) continue;
887
-
888
- ctx.moveTo( 0, Math.floor( tVert( v ) ) + ctx.lineWidth / 2 );
889
- ctx.lineTo( plotWidth, Math.floor( tVert( v ) ) + ctx.lineWidth / 2 );
890
- }
891
- }
892
-
893
- ctx.stroke();
894
-
895
- if( options.grid.showBorder && options.grid.borderWidth ) {
896
- // draw border
897
- ctx.lineWidth = options.grid.borderWidth;
898
- ctx.strokeStyle = options.grid.color;
899
- ctx.lineJoin = 'round';
900
- ctx.strokeRect( 0, 0, plotWidth, plotHeight );
901
- ctx.restore();
902
- }
903
- }
904
-
905
- function insertTickLabels() {
906
- target.find('.tickLabels').remove();
907
-
908
- var i, tick;
909
- var html = '<div class="tickLabels" style="font-size:smaller;color:' + options.grid.color + '">';
910
-
911
- // do the x-axis
912
- if( options.xaxis.showLabels ) {
913
- for( i = 0; i < xaxis.ticks.length; ++i ) {
914
- tick = xaxis.ticks[i];
915
- if( !tick.label || tick.v < xaxis.min || tick.v > xaxis.max ) continue;
916
- html += '<div style="position:absolute;top:' +
917
- ( plotOffset.top + plotHeight + options.grid.labelMargin ) +
918
- 'px;left:' + ( plotOffset.left + tHoz( tick.v ) - xaxis.labelWidth / 2) +
919
- 'px;width:' + xaxis.labelWidth + 'px;text-align:center" class="tickLabel">' +
920
- tick.label + "</div>";
921
- }
922
- }
923
-
924
- // do the y-axis
925
- if( options.yaxis.showLabels ) {
926
- for( i = 0; i < yaxis.ticks.length; ++i ) {
927
- tick = yaxis.ticks[i];
928
- if( !tick.label || tick.v < yaxis.min || tick.v > yaxis.max ) continue;
929
- html += '<div style="position:absolute;top:' +
930
- ( plotOffset.top + tVert( tick.v ) - yaxis.labelHeight / 2 ) +
931
- 'px;left:0;width:' + yaxis.labelWidth + 'px;text-align:right" class="tickLabel">' +
932
- tick.label + "</div>";
933
- }
934
- }
935
-
936
- html += '</div>';
937
-
938
- target.append( html );
939
- }
940
-
941
- function insertAxisLabels() {
942
- if( options.xaxis.label ) {
943
- yLocation = plotOffset.top + plotHeight + ( xaxis.labelHeight * 1.5 );
944
- xLocation = plotOffset.left;
945
- target.find('#xaxislabel').remove();
946
- target.append( "<div id='xaxislabel' style='color:" +
947
- options.grid.color + ";width:" + plotWidth +
948
- "px;text-align:center;position:absolute;top:" +
949
- yLocation + "px;left:" + xLocation + "px;'>" +
950
- options.xaxis.label + "</div>" );
951
- }
952
- if( options.yaxis.label ) {
953
- var element;
954
- if( $.browser.msie ) {
955
- element = "<span class='yaxis axislabel' style='writing-mode: tb-rl;filter: flipV flipH;'>" +
956
- options.yaxis.label + "</span>";
957
- }
958
- else {
959
- // we'll use svg instead
960
- var element = document.createElement( 'object' );
961
- element.setAttribute( 'type', 'image/svg+xml' );
962
- xAxisHeight = $('#xaxislabel').height();
963
- string = '<svg:svg baseProfile="full" height="' + plotHeight +
964
- '" width="' + xAxisHeight * 1.5 +
965
- '" xmlns:svg="http://www.w3.org/2000/svg" ' +
966
- 'xmlns="http://www.w3.org/2000/svg" xmlns:xlink=' +
967
- '"http://www.w3.org/1999/xlink"><svg:g>';
968
- string += '<svg:text text-anchor="middle" style="fill:#545454; ' +
969
- 'stroke:none" x="' + options.grid.labelFontSize + '" y="' +
970
- plotHeight / 2 + '" ' + 'transform="rotate(-90,' +
971
- options.grid.labelFontSize + ',' + plotHeight / 2 +
972
- ')" font-size="' + options.grid.labelFontSize + '">' +
973
- options.yaxis.label + '</svg:text></svg:g></svg:svg>';
974
- element.setAttribute( 'data', 'data:image/svg+xml,' + string );
975
- }
976
-
977
- xLocation = plotOffset.left - ( yaxis.labelWidth * 1.5 ) -
978
- options.grid.labelFontSize;
979
- yLocation = plotOffset.top;
980
-
981
- var yAxisLabel = $("<div id='yaxislabel' style='color:" +
982
- options.grid.color + ";height:" + plotHeight +
983
- "px;text-align:center;position:absolute;top:" +
984
- yLocation + "px;left:" + xLocation + "px;'</div>");
985
- yAxisLabel.append( element );
986
-
987
- target.find('#yaxislabel').remove().end().append( yAxisLabel );
988
- }
989
- }
990
-
991
- function drawSeries( series ) {
992
- if( series.lines.show || ( !series.bars.show && !series.points.show && !series.deltas.show ) ) {
993
- drawSeriesLines( series );
994
- }
995
- if( series.bars.show ) drawSeriesBars( series );
996
- if( series.points.show ) drawSeriesPoints( series );
997
- if( series.deltas.show ) drawSeriesDeltas( series );
998
- }
999
-
1000
- function drawSeriesLines( series ) {
1001
- function plotLine( data, offset ) {
1002
- var prev = cur = drawx = drawy = null;
1003
-
1004
- ctx.beginPath();
1005
- for( var i = 0; i < data.length; ++i ) {
1006
- prev = cur;
1007
- cur = data[i];
1008
-
1009
- if( !prev || !cur ) continue;
1010
-
1011
- var x1 = prev.x, y1 = prev.y,
1012
- x2 = cur.x, y2 = cur.y;
1013
-
1014
- // clip with ymin
1015
- if( y1 <= y2 && y1 < yaxis.min ) {
1016
- if( y2 < yaxis.min ) continue; // line segment is outside
1017
- // compute new intersection point
1018
- x1 = ( yaxis.min - y1 ) / ( y2 - y1 ) * ( x2 - x1 ) + x1;
1019
- y1 = yaxis.min;
1020
- }
1021
- else if( y2 <= y1 && y2 < yaxis.min ) {
1022
- if( y1 < yaxis.min ) continue;
1023
- x2 = ( yaxis.min - y1 ) / ( y2 - y1 ) * ( x2 - x1 ) + x1;
1024
- y2 = yaxis.min;
1025
- }
1026
-
1027
- // clip with ymax
1028
- if( y1 >= y2 && y1 > yaxis.max ) {
1029
- if( y2 > yaxis.max ) continue;
1030
- x1 = ( yaxis.max - y1 ) / ( y2 - y1 ) * ( x2 - x1 ) + x1;
1031
- y1 = yaxis.max;
1032
- }
1033
- else if( y2 >= y1 && y2 > yaxis.max ) {
1034
- if( y1 > yaxis.max ) continue;
1035
- x2 = ( yaxis.max - y1 ) / ( y2 - y1 ) * ( x2 - x1 ) + x1;
1036
- y2 = yaxis.max;
1037
- }
1038
-
1039
- // clip with xmin
1040
- if( x1 <= x2 && x1 < xaxis.min ) {
1041
- if( x2 < xaxis.min ) continue;
1042
- y1 = ( xaxis.min - x1 ) / ( x2 - x1 ) * ( y2 - y1 ) + y1;
1043
- x1 = xaxis.min;
1044
- }
1045
- else if( x2 <= x1 && x2 < xaxis.min ) {
1046
- if( x1 < xaxis.min ) continue;
1047
- y2 = ( xaxis.min - x1 ) / ( x2 - x1 ) * ( y2 - y1 ) + y1;
1048
- x2 = xaxis.min;
1049
- }
1050
-
1051
- // clip with xmax
1052
- if( x1 >= x2 && x1 > xaxis.max ) {
1053
- if( x2 > xaxis.max ) continue;
1054
- y1 = ( xaxis.max - x1 ) / ( x2 - x1 ) * ( y2 - y1 ) + y1;
1055
- x1 = xaxis.max;
1056
- }
1057
- else if( x2 >= x1 && x2 > xaxis.max ) {
1058
- if( x1 > xaxis.max ) continue;
1059
- y2 = ( xaxis.max - x1 ) / ( x2 - x1 ) * ( y2 - y1 ) + y1;
1060
- x2 = xaxis.max;
1061
- }
1062
-
1063
- if( drawx != tHoz( x1 ) || drawy != tVert( y1 ) + offset ) {
1064
- ctx.moveTo( tHoz( x1 ), tVert( y1 ) + offset );
1065
- }
1066
-
1067
- drawx = tHoz( x2 );
1068
- drawy = tVert( y2 ) + offset;
1069
- ctx.lineTo( drawx, drawy );
1070
- }
1071
- ctx.stroke();
1072
- }
1073
-
1074
- function plotLineArea( data ) {
1075
- var prev = cur = null;
1076
- var bottom = Math.min( Math.max( 0, yaxis.min ), yaxis.max );
1077
- var top, lastX = 0;
1078
- var areaOpen = false;
1079
-
1080
- for( var i = 0; i < data.length; ++i ) {
1081
- prev = cur;
1082
- cur = data[i];
1083
-
1084
- if( areaOpen && prev && !cur ) {
1085
- // close area
1086
- ctx.lineTo( tHoz( lastX ), tVert( bottom ) );
1087
- ctx.fill();
1088
- areaOpen = false;
1089
- continue;
1090
- }
1091
-
1092
- if( !prev || !cur ) continue;
1093
-
1094
- var x1 = prev.x, y1 = prev.y,
1095
- x2 = cur.x, y2 = cur.y;
1096
-
1097
- // clip with xmin
1098
- if( x1 <= x2 && x1 < xaxis.min ) {
1099
- if( x2 < xaxis.min ) continue;
1100
- y1 = ( xaxis.min - x1 ) / ( x2 - x1 ) * ( y2 - y1 ) + y1;
1101
- x1 = xaxis.min;
1102
- }
1103
- else if( x2 <= x1 && x2 < xaxis.min ) {
1104
- if( x1 < xaxis.min ) continue;
1105
- y2 = ( xaxis.min - x1 ) / (x2 - x1) * (y2 - y1) + y1;
1106
- x2 = xaxis.min;
1107
- }
1108
-
1109
- // clip with xmax
1110
- if( x1 >= x2 && x1 > xaxis.max ) {
1111
- if( x2 > xaxis.max ) continue;
1112
- y1 = ( xaxis.max - x1 ) / ( x2 - x1 ) * ( y2 - y1 ) + y1;
1113
- x1 = xaxis.max;
1114
- }
1115
- else if( x2 >= x1 && x2 > xaxis.max ) {
1116
- if( x1 > xaxis.max ) continue;
1117
- y2 = ( xaxis.max - x1 ) / ( x2 - x1 ) * ( y2 - y1 ) + y1;
1118
- x2 = xaxis.max;
1119
- }
1120
-
1121
- if( !areaOpen ) {
1122
- // open area
1123
- ctx.beginPath();
1124
- ctx.moveTo( tHoz( x1 ), tVert( bottom ) );
1125
- areaOpen = true;
1126
- }
1127
-
1128
- // now first check the case where both is outside
1129
- if( y1 >= yaxis.max && y2 >= yaxis.max ) {
1130
- ctx.lineTo( tHoz( x1 ), tVert( yaxis.max ) );
1131
- ctx.lineTo( tHoz( x2 ), tVert( yaxis.max ) );
1132
- continue;
1133
- }
1134
- else if( y1 <= yaxis.min && y2 <= yaxis.min ) {
1135
- ctx.lineTo( tHoz( x1 ), tVert( yaxis.min ) );
1136
- ctx.lineTo( tHoz( x2 ), tVert( yaxis.min ) );
1137
- continue;
1138
- }
1139
-
1140
- // else it's a bit more complicated, there might
1141
- // be two rectangles and two triangles we need to fill
1142
- // in; to find these keep track of the current x values
1143
- var x1old = x1,
1144
- x2old = x2;
1145
-
1146
- // and clip the y values, without shortcutting
1147
-
1148
- // clip with ymin
1149
- if( y1 <= y2 && y1 < yaxis.min && y2 >= yaxis.min ) {
1150
- x1 = ( yaxis.min - y1 ) / ( y2 - y1 ) * ( x2 - x1 ) + x1;
1151
- y1 = yaxis.min;
1152
- }
1153
- else if( y2 <= y1 && y2 < yaxis.min && y1 >= yaxis.min ) {
1154
- x2 = ( yaxis.min - y1 ) / ( y2 - y1 ) * ( x2 - x1 ) + x1;
1155
- y2 = yaxis.min;
1156
- }
1157
-
1158
- // clip with ymax
1159
- if( y1 >= y2 && y1 > yaxis.max && y2 <= yaxis.max ) {
1160
- x1 = ( yaxis.max - y1 ) / ( y2 - y1 ) * ( x2 - x1 ) + x1;
1161
- y1 = yaxis.max;
1162
- }
1163
- else if( y2 >= y1 && y2 > yaxis.max && y1 <= yaxis.max ) {
1164
- x2 = ( yaxis.max - y1 ) / ( y2 - y1 ) * ( x2 - x1 ) + x1;
1165
- y2 = yaxis.max;
1166
- }
1167
-
1168
- // if the x value was changed we got a rectangle to fill
1169
- if( x1 != x1old ) {
1170
- top = y1 <= yaxis.min ? yaxis.min : yaxis.max;
1171
- ctx.lineTo( tHoz( x1old ), tVert( top ) );
1172
- ctx.lineTo( tHoz( x1 ), tVert( top ) );
1173
- }
1174
-
1175
- // fill the triangles
1176
- ctx.lineTo( tHoz( x1 ), tVert( y1 ) );
1177
- ctx.lineTo( tHoz( x2 ), tVert( y2 ) );
1178
-
1179
- // fill the other rectangle if it's there
1180
- if( x2 != x2old ) {
1181
- top = y2 <= yaxis.min ? yaxis.min : yaxis.max;
1182
- ctx.lineTo( tHoz( x2old ), tVert( top ) );
1183
- ctx.lineTo( tHoz( x2 ), tVert( top ) );
1184
- }
1185
-
1186
- lastX = Math.max( x2, x2old );
1187
- }
1188
-
1189
- if( areaOpen ) {
1190
- ctx.lineTo( tHoz( lastX ), tVert( bottom ) );
1191
- ctx.fill();
1192
- }
1193
- }
1194
-
1195
- ctx.save();
1196
- ctx.translate( plotOffset.left, plotOffset.top );
1197
- ctx.lineJoin = 'round';
1198
-
1199
- var lw = series.lines.lineWidth;
1200
- var sw = series.shadowSize;
1201
-
1202
- // FIXME: consider another form of shadow when filling is turned on
1203
- if( sw > 0 ) {
1204
- // draw shadow in two steps
1205
- ctx.lineWidth = sw / 2;
1206
- ctx.strokeStyle = 'rgba(0,0,0,0.1)';
1207
- plotLine( series.data, lw / 2 + sw / 2 + ctx.lineWidth / 2 );
1208
-
1209
- ctx.lineWidth = sw / 2;
1210
- ctx.strokeStyle = 'rgba(0,0,0,0.2)';
1211
- plotLine( series.data, lw / 2 + ctx.lineWidth / 2 );
1212
- }
1213
-
1214
- ctx.lineWidth = lw;
1215
- ctx.strokeStyle = series.color;
1216
- setFillStyle( series.lines, series.color );
1217
- if( series.lines.fill ) plotLineArea( series.data, 0 );
1218
- plotLine( series.data, 0 );
1219
- ctx.restore();
1220
- }
1221
-
1222
- function drawSeriesPoints( series ) {
1223
- function plotPoints( data, radius, fill ) {
1224
- for( var i = 0; i < data.length; ++i ) {
1225
- if( !data[i] ) continue;
1226
-
1227
- var x = data[i].x,
1228
- y = data[i].y;
1229
-
1230
- if( x < xaxis.min || x > xaxis.max ||
1231
- y < yaxis.min || y > yaxis.max ) { continue; }
1232
-
1233
- ctx.beginPath();
1234
- ctx.arc( tHoz(x), tVert( y ), radius, 0, 2 * Math.PI, true );
1235
- if( fill ) ctx.fill();
1236
- ctx.stroke();
1237
- }
1238
- }
1239
-
1240
- function plotPointShadows( data, offset, radius ) {
1241
- for( var i = 0; i < data.length; ++i ) {
1242
- if( !data[i] ) continue;
1243
-
1244
- var x = data[i].x,
1245
- y = data[i].y;
1246
- if( x < xaxis.min || x > xaxis.max ||
1247
- y < yaxis.min || y > yaxis.max ) { continue; }
1248
-
1249
- ctx.beginPath();
1250
- ctx.arc( tHoz( x ), tVert( y ) + offset, radius, 0, Math.PI, false );
1251
- ctx.stroke();
1252
- }
1253
- }
1254
-
1255
- ctx.save();
1256
- ctx.translate( plotOffset.left, plotOffset.top );
1257
-
1258
- var lw = series.lines.lineWidth;
1259
- var sw = series.shadowSize;
1260
- if( sw > 0 ) {
1261
- // draw shadow in two steps
1262
- ctx.lineWidth = sw / 2;
1263
- ctx.strokeStyle = 'rgba(0,0,0,0.1)';
1264
- plotPointShadows( series.data, sw / 2 + ctx.lineWidth / 2, series.points.radius );
1265
-
1266
- ctx.lineWidth = sw / 2;
1267
- ctx.strokeStyle = 'rgba(0,0,0,0.2)';
1268
- plotPointShadows( series.data, ctx.lineWidth / 2, series.points.radius );
1269
- }
1270
-
1271
- ctx.lineWidth = series.points.lineWidth;
1272
- ctx.strokeStyle = series.color;
1273
- setFillStyle( series.points, series.color );
1274
- plotPoints( series.data, series.points.radius, series.points.fill );
1275
- ctx.restore();
1276
- }
1277
-
1278
- function drawSeriesDeltas( series ) {
1279
- function plotPoints( data, radius, fill ) {
1280
- for( var i = 0; i < data.length; ++i ) {
1281
- if( !data[i] ) continue;
1282
-
1283
- var x = data[i].x,
1284
- y = data[i].y,
1285
- d = data[i].delta;
1286
-
1287
- if( x < xaxis.min || x > xaxis.max ||
1288
- y < yaxis.min || y > yaxis.max ||
1289
- d < yaxis.min || d > yaxis.max ) { continue; }
1290
-
1291
- ctx.beginPath();
1292
- ctx.arc( tHoz( x ), tVert( y ), radius, 0, 2 * Math.PI, true );
1293
- if( fill ) ctx.fill();
1294
- ctx.stroke();
1295
- }
1296
- }
1297
-
1298
- function plotDeltas( data, settings ) {
1299
- for( var i = 0; i < data.length; ++i ) {
1300
- if( !data[i] ) { continue; }
1301
-
1302
- var x = data[i].x,
1303
- y = data[i].y,
1304
- d = data[i].delta;
1305
-
1306
- if( x < xaxis.min || x > xaxis.max ||
1307
- y < yaxis.min || y > yaxis.max ||
1308
- d < yaxis.min || d > yaxis.max ) { continue; }
1309
-
1310
- if( y < d ) ctx.strokeStyle = settings.color.below;
1311
- else if( y > d ) ctx.strokeStyle = settings.color.above;
1312
- else ctx.strokeStyle = settings.color.equal;
1313
-
1314
- ctx.beginPath();
1315
- ctx.moveTo( tHoz( x ), tVert( y ) );
1316
- ctx.lineTo( tHoz( x ), tVert( d ) );
1317
- ctx.stroke();
1318
-
1319
- // draw the markers for the deltas (horizontal line)
1320
- var markerLeft = tHoz( x ) - ( ctx.lineWidth * settings.markerWidth );
1321
- var markerRight = tHoz( x ) + ( ctx.lineWidth * settings.markerWidth );
1322
-
1323
- ctx.beginPath();
1324
- ctx.moveTo( markerLeft, tVert( d ) );
1325
- ctx.lineTo( markerRight, tVert( d ) );
1326
- ctx.stroke();
1327
- }
1328
- }
1329
-
1330
- function plotPointShadows( data, offset, radius ) {
1331
- for( var i = 0; i < data.length; ++i ) {
1332
- if( !data[i] ) continue;
1333
-
1334
- var x = data[i].x,
1335
- y = data[i].y,
1336
- d = data[i].delta;
1337
-
1338
- if( x < xaxis.min || x > xaxis.max ||
1339
- y < yaxis.min || y > yaxis.max ||
1340
- d < yaxis.min || d > yaxis.max ) { continue; }
1341
-
1342
- ctx.beginPath();
1343
- ctx.arc( tHoz( x ), tVert( y ) + offset, radius, 0, Math.PI, false );
1344
- ctx.stroke();
1345
- }
1346
- }
1347
-
1348
- ctx.save();
1349
- ctx.translate( plotOffset.left, plotOffset.top );
1350
-
1351
- var lw = series.lines.lineWidth;
1352
- var sw = series.shadowSize;
1353
- if( sw > 0 ) {
1354
- // draw shadow in two steps
1355
- ctx.lineWidth = sw / 2;
1356
- ctx.strokeStyle = 'rgba(0,0,0,0.1)';
1357
- plotPointShadows( series.data, sw / 2 + ctx.lineWidth / 2, series.points.radius );
1358
-
1359
- ctx.lineWidth = sw / 2;
1360
- ctx.strokeStyle = 'rgba(0,0,0,0.2)';
1361
- plotPointShadows( series.data, ctx.lineWidth / 2, series.points.radius );
1362
- }
1363
-
1364
- ctx.lineWidth = series.points.lineWidth;
1365
-
1366
- // draw the delta lines and markers
1367
- plotDeltas( series.data, series.deltas );
1368
-
1369
- // draw the actual datapoints
1370
- ctx.strokeStyle = series.color;
1371
- setFillStyle( series.points, series.color );
1372
- plotPoints( series.data, series.points.radius, series.points.fill );
1373
-
1374
- ctx.restore();
1375
- }
1376
-
1377
- function drawSeriesBars( series ) {
1378
- function plotBars( data, barWidth, offset, fill ) {
1379
- for( var i = 0; i < data.length; i++ ) {
1380
- if( !data[i] ) continue;
1381
-
1382
- var x = data[i].x,
1383
- y = data[i].y,
1384
- drawLeft = true,
1385
- drawTop = true,
1386
- drawRight = true;
1387
-
1388
- // determine the co-ordinates of the bar, account for negative bars having
1389
- // flipped top/bottom and draw/don't draw accordingly
1390
- var halfBar = barWidth / 2;
1391
- var left = x - halfBar,
1392
- right = x + halfBar,
1393
- bottom = ( y < 0 ? y : 0 ),
1394
- top = ( y < 0 ? 0 : y );
1395
-
1396
- if( right < xaxis.min || left > xaxis.max ||
1397
- top < yaxis.min || bottom > yaxis.max ) { continue; }
1398
-
1399
- // clip
1400
- if( left < xaxis.min ) {
1401
- left = xaxis.min;
1402
- drawLeft = false;
1403
- }
1404
-
1405
- if ( right > xaxis.max ) {
1406
- right = xaxis.max;
1407
- drawRight = false;
1408
- }
1409
-
1410
- if( bottom < yaxis.min ) {
1411
- bottom = yaxis.min;
1412
- }
1413
-
1414
- if (top > yaxis.max) {
1415
- top = yaxis.max;
1416
- drawTop = false;
1417
- }
1418
-
1419
- // fill the bar
1420
- if( fill ) {
1421
- ctx.beginPath();
1422
- ctx.moveTo( tHoz( left ), tVert( bottom) + offset );
1423
- ctx.lineTo( tHoz( left ), tVert( top) + offset );
1424
- ctx.lineTo( tHoz( right ), tVert( top) + offset );
1425
- ctx.lineTo( tHoz( right ), tVert( bottom) + offset );
1426
- ctx.fill();
1427
- }
1428
-
1429
- // draw outline
1430
- if( drawLeft || drawRight || drawTop ) {
1431
- ctx.beginPath();
1432
- ctx.moveTo( tHoz( left ), tVert( bottom ) + offset );
1433
- if( drawLeft ) ctx.lineTo( tHoz( left ), tVert( top) + offset );
1434
- else ctx.moveTo( tHoz( left ), tVert( top) + offset );
1435
- if( drawTop ) ctx.lineTo( tHoz( right ), tVert( top ) + offset );
1436
- else ctx.moveTo( tHoz( right ), tVert( top ) + offset );
1437
- if( drawRight ) ctx.lineTo( tHoz( right ), tVert( bottom ) + offset );
1438
- else ctx.moveTo( tHoz( right ), tVert( bottom ) + offset );
1439
- ctx.stroke();
1440
- }
1441
- }
1442
- }
1443
-
1444
- ctx.save();
1445
- ctx.translate( plotOffset.left, plotOffset.top );
1446
- ctx.lineJoin = 'round';
1447
-
1448
- var bw = series.bars.barWidth;
1449
- var lw = Math.min( series.bars.lineWidth, bw );
1450
-
1451
- ctx.lineWidth = lw;
1452
- ctx.strokeStyle = series.color;
1453
- setFillStyle( series.bars, series.color );
1454
- plotBars( series.data, bw, 0, series.bars.fill );
1455
- ctx.restore();
1456
- }
1457
-
1458
- function setFillStyle( obj, seriesColor ) {
1459
- var opacity = obj.fillOpacity;
1460
- if( obj.fill ) {
1461
- if( obj.fillColor ) {
1462
- ctx.fillStyle = obj.fillColor;
1463
- }
1464
- else {
1465
- var c = parseColor( seriesColor );
1466
- c.a = typeof fill == 'number' ? obj.fill : ( opacity ? opacity : 0.4 );
1467
- c.normalize();
1468
- ctx.fillStyle = c.toString();
1469
- }
1470
- }
1471
- }
1472
-
1473
- function drawMarkers() {
1474
- if( !options.grid.markers.length ) return;
1475
-
1476
- for( var i = 0; i < options.grid.markers.length; i++ ) {
1477
- marker = options.grid.markers[i];
1478
- if( marker.value < yaxis.max && marker.value > yaxis.min ) {
1479
- ctx.lineWidth = marker.width;
1480
- ctx.strokeStyle = marker.color;
1481
- ctx.beginPath();
1482
-
1483
- if( marker.axis == 'x' ) {
1484
- ctx.moveTo( tHoz( xaxis.min ) + plotOffset.left,
1485
- tVert( marker.value ) + plotOffset.top );
1486
- ctx.lineTo( tHoz( xaxis.max ) + plotOffset.left,
1487
- tVert( marker.value ) + plotOffset.top );
1488
- }
1489
- else if( marker.axis == 'y' ) {
1490
- ctx.moveTo( tHoz( marker.value ) + plotOffset.left,
1491
- tVert( yaxis.min ) + plotOffset.top );
1492
- ctx.lineTo( tHoz( marker.value ) + plotOffset.left,
1493
- tVert( yaxis.max ) + plotOffset.top );
1494
- }
1495
-
1496
- ctx.stroke();
1497
- }
1498
- }
1499
- }
1500
-
1501
- function insertLegend() {
1502
- // remove legends from the appropriate container
1503
- if( options.legend.container ) {
1504
- options.legend.container.find('table.legend_table').remove();
1505
- }
1506
- else {
1507
- target.find('.legend').remove();
1508
- }
1509
-
1510
- if( !options.legend.show ) { return; }
1511
-
1512
- var fragments = [];
1513
- var rowStarted = false;
1514
- for( i = 0; i < series.length; ++i ) {
1515
- if( !series[i].label ) continue;
1516
-
1517
- if( i % options.legend.noColumns == 0 ) {
1518
- if( rowStarted ) fragments.push( '</tr>' );
1519
- fragments.push( '<tr>' );
1520
- rowStarted = true;
1521
- }
1522
-
1523
- var label = series[i].label;
1524
- if( options.legend.labelFormatter ) label = options.legend.labelFormatter( label );
1525
-
1526
- fragments.push(
1527
- '<td class="legendColorBox"><div style="border:1px solid ' +
1528
- options.legend.labelBoxBorderColor +
1529
- ';padding:1px"><div style="width:14px;height:10px;background-color:' +
1530
- series[i].color + ';overflow:hidden"></div></div></td>' +
1531
- '<td class="legendLabel">' + label + '</td>'
1532
- );
1533
- }
1534
-
1535
- if( rowStarted ) fragments.push( '</tr>' );
1536
-
1537
- if( fragments.length > 0 ) {
1538
- var table = '<table class="legend_table" style="font-size:smaller;color:' +
1539
- options.grid.color + '">' + fragments.join('') + '</table>';
1540
-
1541
- if( options.legend.container ) {
1542
- options.legend.container.append( table );
1543
- }
1544
- else {
1545
- var pos = '';
1546
- var p = options.legend.position,
1547
- m = options.legend.margin;
1548
-
1549
- if( p.charAt( 0 ) == 'n' ) {
1550
- pos += 'top:' + ( m + plotOffset.top ) + 'px;';
1551
- }
1552
- else if( p.charAt( 0 ) == 's' ) {
1553
- pos += 'bottom:' + ( m + plotOffset.bottom + BOTTOM_SIDE_BUFFER ) + 'px;';
1554
- }
1555
- if( p.charAt( 1 ) == 'e' ) {
1556
- pos += 'right:' + ( m + plotOffset.right + RIGHT_SIDE_BUFFER ) + 'px;';
1557
- }
1558
- else if( p.charAt( 1 ) == 'w' ) {
1559
- pos += 'left:' + ( m + plotOffset.left ) + 'px;';
1560
- }
1561
-
1562
- var legend = $('<div class="legend">' +
1563
- table.replace(
1564
- 'style="', 'style="position:absolute;' + pos +';'
1565
- ) + '</div>').appendTo( target );
1566
-
1567
- if( options.legend.backgroundOpacity != 0.0 ) {
1568
- // put in the transparent background
1569
- // separately to avoid blended labels and
1570
- // label boxes
1571
- var c = options.legend.backgroundColor;
1572
- if( !c ) {
1573
- tmp = options.grid.backgroundColor ?
1574
- options.grid.backgroundColor :
1575
- extractColor( legend );
1576
- c = parseColor( tmp ).adjust( null, null, null, 1 ).toString();
1577
- }
1578
- var div = legend.children();
1579
- $('<div style="position:absolute;width:' + div.width() +
1580
- 'px;height:' + div.height() + 'px;' + pos +'background-color:' +
1581
- c + ';"> </div>')
1582
- .prependTo( legend )
1583
- .css( 'opacity', options.legend.backgroundOpacity );
1584
- }
1585
- }
1586
- }
1587
- }
1588
-
1589
- var lastMousePos = { pageX: null, pageY: null },
1590
- selection = { first: { x: -1, y: -1 }, second: { x: -1, y: -1 } },
1591
- prevSelection = null,
1592
- selectionInterval = null,
1593
- ignoreClick = false;
1594
-
1595
- // Returns the data item the mouse is over, or null if none is found
1596
- function findSelectedItem( mouseX, mouseY ) {
1597
- // How close do we need to be to an item in order to select it?
1598
- // The clickCatchingArea parameter is the radius of the circle, in pixels.
1599
- var lowestDistance = Math.pow( options.grid.mouseCatchingArea, 2 );
1600
- selectedItem = null;
1601
-
1602
- for( var i = 0; i < series.length; ++i ) {
1603
- var data = series[i].data;
1604
-
1605
- if( options.sortData && data.length > 1 ) {
1606
- var half = tHoz( data[( data.length / 2 ).toFixed(0)].x ).toFixed( 0 );
1607
- if( mouseX < half ) {
1608
- start = 0;
1609
- end = ( data.length / 2 ).toFixed( 0 ) + 5;
1610
- }
1611
- else {
1612
- start = ( data.length / 2 ).toFixed( 0 ) - 5;
1613
- end = data.length;
1614
- }
1615
- }
1616
- else {
1617
- // either we haven't sorted the data (and so we can't split it for
1618
- // searching) or there's only 1 data point, so it doesn't matter
1619
- start = 0;
1620
- end = data.length;
1621
- }
1622
-
1623
- for( var j = start; j < end; ++j ) {
1624
- if( !data[j] ) continue;
1625
-
1626
- // We have to calculate distances in pixels, not in data units, because
1627
- // the scale of the axes may be different
1628
- var x = data[j].x,
1629
- y = data[j].y;
1630
-
1631
- xDistance = Math.abs( tHoz( x ) - mouseX );
1632
- yDistance = Math.abs(tVert(y)-mouseY);
1633
- if( xDistance > options.grid.mouseCatchingArea ) continue;
1634
- if( yDistance > options.grid.mouseCatchingArea ) continue;
1635
-
1636
- sqrDistance = Math.pow( xDistance, 2 ) + Math.pow( yDistance, 2 );
1637
- if( sqrDistance < lowestDistance ) {
1638
- selectedItem = data[j];
1639
- selectedItem._data = series[i];
1640
- lowestDistance = sqrDistance;
1641
- }
1642
- }
1643
- }
1644
-
1645
- return selectedItem;
1646
- }
1647
-
1648
- function onMouseMove( ev ) {
1649
- // FIXME: temp. work-around until jQuery bug 1871 is fixed
1650
- var e = ev || window.event;
1651
- if( !e.pageX && e.clientX ) {
1652
- var de = document.documentElement,
1653
- b = document.body;
1654
- lastMousePos.pageX = e.clientX + ( de && de.scrollLeft || b.scrollLeft || 0 );
1655
- lastMousePos.pageY = e.clientY + ( de && de.scrollTop || b.scrollTop || 0 );
1656
- }
1657
- else {
1658
- lastMousePos.pageX = e.pageX;
1659
- lastMousePos.pageY = e.pageY;
1660
- }
1661
-
1662
- if( options.grid.hoverable ) {
1663
- var offset = eventHolder.offset();
1664
- result = { raw: {
1665
- x: lastMousePos.pageX - offset.left - plotOffset.left,
1666
- y: lastMousePos.pageY - offset.top - plotOffset.top
1667
- } };
1668
- result.selected = findSelectedItem( result.raw.x, result.raw.y );
1669
-
1670
- // display the tooltip/hint if requested
1671
- if( !$.browser.msie && result.selected && result.selected._data.hints.show ) {
1672
- showHintDiv( result.selected );
1673
- }
1674
-
1675
- if( !result.selected ) cleanup();
1676
- target.trigger( 'plotmousemove', [ result ] );
1677
- }
1678
- }
1679
-
1680
- function onMouseDown( e ) {
1681
- if( e.which != 1 ) return; // left click
1682
-
1683
- // cancel out any text selections
1684
- document.body.focus();
1685
-
1686
- // prevent text selection and drag in old-school browsers
1687
- if( document.onselectstart !== undefined && !workarounds.onselectstart ) {
1688
- workarounds.onselectstart = document.onselectstart;
1689
- document.onselectstart = function() { return false; };
1690
- }
1691
- if( document.ondrag !== undefined && !workarounds.ondrag ) {
1692
- workarounds.ondrag = document.ondrag;
1693
- document.ondrag = function() { return false; };
1694
- }
1695
-
1696
- setSelectionPos( selection.first, e );
1697
- clearInterval( selectionInterval );
1698
- lastMousePos.pageX = null;
1699
- selectionInterval = setInterval( updateSelectionOnMouseMove, 200 );
1700
- $(document).one( 'mouseup', onSelectionMouseUp );
1701
- }
1702
-
1703
- function onClick( e ) {
1704
- if( ignoreClick ) {
1705
- ignoreClick = false;
1706
- return;
1707
- }
1708
-
1709
- var offset = eventHolder.offset();
1710
- var canvasX = e.pageX - offset.left - plotOffset.left;
1711
- var canvasY = e.pageY - offset.top - plotOffset.top;
1712
-
1713
- var result = { raw: {
1714
- x: xaxis.min + canvasX / hozScale,
1715
- y: yaxis.max - canvasY / vertScale
1716
- } };
1717
- result.selected = findSelectedItem( canvasX, canvasY );
1718
-
1719
- target.trigger( 'plotclick', [ result ] );
1720
- }
1721
-
1722
- function triggerSelectedEvent() {
1723
- var x1, x2, y1, y2;
1724
- if( selection.first.x <= selection.second.x ) {
1725
- x1 = selection.first.x;
1726
- x2 = selection.second.x;
1727
- }
1728
- else {
1729
- x1 = selection.second.x;
1730
- x2 = selection.first.x;
1731
- }
1732
-
1733
- if( selection.first.y >= selection.second.y ) {
1734
- y1 = selection.first.y;
1735
- y2 = selection.second.y;
1736
- }
1737
- else {
1738
- y1 = selection.second.y;
1739
- y2 = selection.first.y;
1740
- }
1741
-
1742
- x1 = xaxis.min + x1 / hozScale;
1743
- x2 = xaxis.min + x2 / hozScale;
1744
-
1745
- y1 = yaxis.max - y1 / vertScale;
1746
- y2 = yaxis.max - y2 / vertScale;
1747
-
1748
- target.trigger( 'plotselected', [ { x1: x1, y1: y1, x2: x2, y2: y2 } ] );
1749
- }
1750
-
1751
- function onSelectionMouseUp( e ) {
1752
- if( document.onselectstart !== undefined ) document.onselectstart = workarounds.onselectstart;
1753
- if( document.ondrag !== undefined ) document.ondrag = workarounds.ondrag;
1754
-
1755
- if( selectionInterval ) {
1756
- clearInterval( selectionInterval );
1757
- selectionInterval = null;
1758
- }
1759
-
1760
- setSelectionPos( selection.second, e );
1761
- clearSelection();
1762
- if( !selectionIsSane() || e.which != 1 ) return false;
1763
-
1764
- drawSelection();
1765
- triggerSelectedEvent();
1766
- ignoreClick = true;
1767
-
1768
- return false;
1769
- }
1770
-
1771
- function setSelectionPos( pos, e ) {
1772
- var offset = $(overlay).offset();
1773
- if( options.selection.mode == 'y' ) {
1774
- pos.x = ( pos == selection.first ) ? 0 : plotWidth;
1775
- }
1776
- else {
1777
- pos.x = e.pageX - offset.left - plotOffset.left;
1778
- pos.x = Math.min( Math.max( 0, pos.x ), plotWidth );
1779
-
1780
- if( options.selection.snapToTicks ) {
1781
- // find our current location in terms of the xaxis
1782
- var x = xaxis.min + pos.x / hozScale;
1783
-
1784
- // determine if we're moving left or right on the xaxis
1785
- if( selection.first.x - selection.second.x < 0 ||
1786
- selection.first.x == -1 ) {
1787
- // to the right
1788
- idx = pos == selection.first ? -1 : 0
1789
- for( var i = 0; i < xaxis.ticks.length; i++ ) {
1790
- if( x <= xaxis.ticks[i].v ) {
1791
- pos.x = Math.floor( ( xaxis.ticks[i+idx].v - xaxis.min ) *
1792
- hozScale);
1793
- break;
1794
- }
1795
- }
1796
- }
1797
- else {
1798
- // to the left
1799
- idx = pos == selection.first ? 1 : 0
1800
- for( var i = xaxis.ticks.length - 1; i >= 0; i-- ) {
1801
- if( x >= xaxis.ticks[i].v ) {
1802
- pos.x = Math.floor( ( xaxis.ticks[i+idx].v - xaxis.min ) *
1803
- hozScale);
1804
- break;
1805
- }
1806
- }
1807
- }
1808
- }
1809
- }
1810
-
1811
- if( options.selection.mode == 'x' ) {
1812
- pos.y = ( pos == selection.first ) ? 0 : plotHeight;
1813
- }
1814
- else {
1815
- pos.y = e.pageY - offset.top - plotOffset.top;
1816
- pos.y = Math.min( Math.max( 0, pos.y ), plotHeight );
1817
- }
1818
- }
1819
-
1820
- function updateSelectionOnMouseMove() {
1821
- if( !lastMousePos.pageX ) { return; }
1822
- setSelectionPos( selection.second, lastMousePos );
1823
- clearSelection();
1824
- if( selectionIsSane() ) { drawSelection(); }
1825
- }
1826
-
1827
- function clearSelection() {
1828
- if( !prevSelection ) { return; }
1829
-
1830
- var x = Math.min( prevSelection.first.x, prevSelection.second.x ),
1831
- y = Math.min( prevSelection.first.y, prevSelection.second.y ),
1832
- w = Math.abs( prevSelection.second.x - prevSelection.first.x ),
1833
- h = Math.abs( prevSelection.second.y - prevSelection.first.y );
1834
-
1835
- octx.clearRect( x + plotOffset.left - octx.lineWidth,
1836
- y + plotOffset.top - octx.lineWidth,
1837
- w + octx.lineWidth * 2,
1838
- h + octx.lineWidth * 2 );
1839
-
1840
- prevSelection = null;
1841
- }
1842
-
1843
- function setSelection( area ) {
1844
- clearSelection();
1845
-
1846
- if( options.selection.mode == 'x' ) {
1847
- selection.first.y = 0;
1848
- selection.second.y = plotHeight;
1849
- }
1850
- else {
1851
- selection.first.y = ( yaxis.max - area.y1 ) * vertScale;
1852
- selection.second.y = ( yaxis.max - area.y2 ) * vertScale;
1853
- }
1854
-
1855
- if( options.selection.mode == 'y' ) {
1856
- selection.first.x = 0;
1857
- selection.second.x = plotWidth;
1858
- }
1859
- else {
1860
- selection.first.x = ( area.x1 - xaxis.min ) * hozScale;
1861
- selection.second.x = ( area.x2 - xaxis.min ) * hozScale;
1862
- }
1863
-
1864
- drawSelection();
1865
- triggerSelectedEvent();
1866
- }
1867
-
1868
- function highlight( marker ) {
1869
- // prevent unnecessary work
1870
- if( marker == lastMarker ) { return; }
1871
- else { lastMarker = marker; }
1872
-
1873
- // draw a marker on the graph over the point that the mouse is hovering over
1874
- if( marker ) {
1875
- var color = options.grid.hoverColor ? options.grid.hoverColor : marker._data.color;
1876
- var fill = options.grid.hoverFill ? options.grid.hoverFill : 'white';
1877
- var radius = options.grid.hoverRadius ? options.grid.hoverRadius : marker._data.points.radius;
1878
-
1879
- var temp_series = {
1880
- shadowSize: options.shadowSize,
1881
- lines: { show: false },
1882
- points: $.extend( true, options.points,
1883
- { fillColor: fill,
1884
- radius: radius } ),
1885
- color: color,
1886
- data: [ { x: marker.x, y: marker.y } ]
1887
- };
1888
- draw();
1889
- drawSeriesPoints( temp_series );
1890
- }
1891
- else {
1892
- draw();
1893
- }
1894
- }
1895
-
1896
- function drawSelection() {
1897
- if( prevSelection &&
1898
- selection.first.x == prevSelection.first.x &&
1899
- selection.first.y == prevSelection.first.y &&
1900
- selection.second.x == prevSelection.second.x &&
1901
- selection.second.y == prevSelection.second.y ) { return; }
1902
-
1903
- octx.strokeStyle = parseColor( options.selection.color ).scale( null, null, null, 0.8 ).toString();
1904
- octx.lineWidth = 1;
1905
- ctx.lineJoin = 'round';
1906
- octx.fillStyle = parseColor( options.selection.color ).scale( null, null, null, 0.4 ).toString();
1907
-
1908
- prevSelection = { first: { x: selection.first.x,
1909
- y: selection.first.y },
1910
- second: { x: selection.second.x,
1911
- y: selection.second.y } };
1912
-
1913
- var x = Math.min( selection.first.x, selection.second.x ),
1914
- y = Math.min( selection.first.y, selection.second.y ),
1915
- w = Math.abs( selection.second.x - selection.first.x ),
1916
- h = Math.abs( selection.second.y - selection.first.y );
1917
-
1918
- octx.fillRect( x + plotOffset.left, y + plotOffset.top, w, h );
1919
- octx.strokeRect( x + plotOffset.left, y + plotOffset.top, w, h );
1920
- }
1921
-
1922
- function selectionIsSane() {
1923
- var minSize = 5;
1924
- return Math.abs( selection.second.x - selection.first.x ) >= minSize &&
1925
- Math.abs( selection.second.y - selection.first.y ) >= minSize;
1926
- }
1927
-
1928
- function showHintDiv(selected) {
1929
- var offset = $(overlay).offset();
1930
- if( $('.hint-wrapper').length > 0 &&
1931
- $('.hint-wrapper:first').attr( 'name' ) == selected.x + ":" + selected.y ) {
1932
- var hintDiv = $('div.plot-hint');
1933
- var hintBackground = $('div.hint-background');
1934
- }
1935
- else {
1936
- cleanup();
1937
- var fragments = [];
1938
- var hintWrapper = $('<div class="hint-wrapper" name="' +
1939
- selected.x + ':' + selected.y + '"></div>');
1940
- hintWrapper.appendTo( target );
1941
-
1942
- fragments.push( '<tbody><tr>' );
1943
- if( selected._data.hints.showColorBox ) {
1944
- fragments.push( '<td class="legendColorBox"><div style="border:1px solid ' +
1945
- options.legend.labelBoxBorderColor +
1946
- ';padding:1px"><div style="width:14px;height:10px;background-color:' +
1947
- selected._data.color + '"></div></div></td>' );
1948
- }
1949
-
1950
- if( selected._data.hints.showSeriesLabel && selected._data.label ) {
1951
- var label = selected._data.hints.labelFormatter( selected._data.label );
1952
- fragments.push( '<td class="legendLabel" style="padding: 0px 4px">' +
1953
- label + '</td>');
1954
- }
1955
- fragments.push( '<td class="hintData" style="padding-left: 4px;"></td>' );
1956
- fragments.push( '</tr></tbody>' );
1957
-
1958
- hintDiv = $('<div class="plot-hint" style="border: 1px solid ' + options.hints.borderColor +
1959
- ';padding: 1px;z-index:5;position:absolute;top:1px;left:1px;display:none;"></div>')
1960
- .appendTo(hintWrapper);
1961
-
1962
- var table = $('<table style="font-size:smaller;white-space: nowrap;color:' +
1963
- options.grid.color + '">' + fragments.join('') + '</table>');
1964
- hintDiv.append( table );
1965
-
1966
- if( selected._data.hints.backgroundOpacity != 0.0 ) {
1967
- var c = selected._data.hints.backgroundColor;
1968
- if( !c ){
1969
- tmp = options.grid.backgroundColor ? options.grid.backgroundColor : extractColor( hintDiv );
1970
- c = parseColor( tmp ).adjust( null, null, null, 1 ).toString();
1971
- }
1972
- hintBackground = $('<div class="hint-background" style="padding: 2px;' +
1973
- 'z-index:4;position:absolute;display:none;background-color:' +
1974
- c + ';"> </div>')
1975
- .appendTo( hintWrapper )
1976
- .css( 'opacity', selected._data.hints.backgroundOpacity );
1977
- }
1978
-
1979
- var hintDataContainer = hintDiv.find('.hintData');
1980
- $(hintDataContainer).html( selected._data.hints.hintFormatter( selected ) );
1981
- }
1982
-
1983
- leftEdge = lastMousePos.pageX - offset.left + 15;
1984
- if( hintDiv.width() + leftEdge > target.width() ) {
1985
- leftEdge = leftEdge - 30 - hintDiv.width();
1986
- }
1987
- hintDiv.css( { left: leftEdge,
1988
- top: lastMousePos.pageY - offset.top + 15 } ).show();
1989
- hintBackground.css( { left: leftEdge,
1990
- top: lastMousePos.pageY - offset.top + 15,
1991
- width: hintDiv.width(),
1992
- height: hintDiv.height() } ).show();
1993
- }
1994
-
1995
- function cleanup() {
1996
- $('.hint-wrapper').remove();
1997
- draw();
1998
- }
1999
-
2000
- function defaultHintFormatter( datapoint ) {
2001
- hintStr = '';
2002
- for( var key in datapoint ) {
2003
- if( key[0] == '_' ) { continue; } // skip internal members
2004
- hintStr += "<strong>" + key + ":</strong> " + datapoint[key] + "<br/>";
2005
- }
2006
- return hintStr;
2007
- }
2008
-
2009
- function defaultLabelFormatter( label ) {
2010
- return "<span style='font-size:1.2em;'>" + label + "</span>";
2011
- }
2012
- }
2013
-
2014
- $.plot = function( target, data, options ) {
2015
- var plot = new Plot( target, data, options );
2016
- /*var t0 = new Date();
2017
- var t1 = new Date();
2018
- var tstr = "time used (msecs): " + (t1.getTime() - t0.getTime())
2019
- if (window.console)
2020
- console.log(tstr);
2021
- else
2022
- alert(tstr);*/
2023
- return plot;
2024
- };
2025
-
2026
- // round to nearby lower multiple of base
2027
- function floorInBase( n, base ) {
2028
- return base * Math.floor( n / base );
2029
- }
2030
-
2031
- // color helpers, inspiration from the jquery color animation
2032
- // plugin by John Resig
2033
- function Color( r, g, b, a ) {
2034
- var rgba = [ 'r', 'g', 'b', 'a' ];
2035
- var x = 4; //rgba.length
2036
-
2037
- while( -1 < --x ) {
2038
- this[rgba[x]] = arguments[x] || ( ( x == 3 ) ? 1.0 : 0 );
2039
- }
2040
-
2041
- this.toString = function() {
2042
- if( this.a >= 1.0 ) {
2043
- return "rgb(" + [ this.r, this.g, this.b ].join( ',' ) + ")";
2044
- }
2045
- else {
2046
- return "rgba(" + [ this.r, this.g, this.b, this.a ].join( ',' ) + ")";
2047
- }
2048
- };
2049
-
2050
- this.scale = function( rf, gf, bf, af ) {
2051
- x = 4; //rgba.length
2052
- while( -1 < --x ) {
2053
- if( arguments[x] ) this[rgba[x]] *= arguments[x];
2054
- }
2055
- return this.normalize();
2056
- };
2057
-
2058
- this.adjust = function( rd, gd, bd, ad ) {
2059
- x = 4; //rgba.length
2060
- while( -1 < --x ) {
2061
- if( arguments[x] ) this[rgba[x]] += arguments[x];
2062
- }
2063
- return this.normalize();
2064
- };
2065
-
2066
- this.clone = function() {
2067
- return new Color( this.r, this.b, this.g, this.a );
2068
- };
2069
-
2070
- var limit = function( val, minVal, maxVal ) {
2071
- return Math.max( Math.min( val, maxVal ), minVal );
2072
- };
2073
-
2074
- this.normalize = function() {
2075
- this.r = limit( parseInt( this.r ), 0, 255 );
2076
- this.g = limit( parseInt( this.g ), 0, 255 );
2077
- this.b = limit( parseInt( this.b ), 0, 255 );
2078
- this.a = limit( this.a, 0, 1 );
2079
- return this;
2080
- };
2081
-
2082
- this.normalize();
2083
- }
2084
-
2085
- var lookupColors = {
2086
- aqua: [ 0, 255, 255 ],
2087
- azure: [ 240, 255, 255 ],
2088
- beige: [ 245, 245, 220 ],
2089
- black: [ 0, 0, 0 ],
2090
- blue: [ 0, 0, 255 ],
2091
- brown: [ 165, 42, 42 ],
2092
- cyan: [ 0, 255, 255 ],
2093
- darkblue: [ 0, 0, 139 ],
2094
- darkcyan: [ 0, 139, 139 ],
2095
- darkgrey: [ 169, 169, 169 ],
2096
- darkgreen: [ 0, 100, 0 ],
2097
- darkkhaki: [ 189, 183, 107 ],
2098
- darkmagenta: [ 139, 0, 139 ],
2099
- darkolivegreen: [ 85, 107, 47 ],
2100
- darkorange: [ 255, 140, 0 ],
2101
- darkorchid: [ 153, 50, 204 ],
2102
- darkred: [ 139, 0, 0 ],
2103
- darksalmon: [ 233, 150, 122 ],
2104
- darkviolet: [ 148, 0, 211 ],
2105
- fuchsia: [ 255, 0, 255 ],
2106
- gold: [ 255, 215, 0 ],
2107
- green: [ 0, 128, 0 ],
2108
- indigo: [ 75, 0, 130 ],
2109
- khaki: [ 240, 230, 140 ],
2110
- lightblue: [ 173, 216, 230 ],
2111
- lightcyan: [ 224, 255, 255 ],
2112
- lightgreen: [ 144, 238, 144 ],
2113
- lightgrey: [ 211, 211, 211 ],
2114
- lightpink: [ 255, 182, 193 ],
2115
- lightyellow: [ 255, 255, 224 ],
2116
- lime: [ 0, 255, 0 ],
2117
- magenta: [ 255, 0, 255 ],
2118
- maroon: [ 128, 0, 0 ],
2119
- navy: [ 0, 0, 128 ],
2120
- olive: [ 128, 128, 0 ],
2121
- orange: [ 255, 165, 0 ],
2122
- pink: [ 255, 192, 203 ],
2123
- purple: [ 128, 0, 128 ],
2124
- violet: [ 128, 0, 128 ],
2125
- red: [ 255, 0, 0 ],
2126
- silver: [ 192, 192, 192 ],
2127
- white: [ 255, 255, 255 ],
2128
- yellow: [ 255, 255, 0 ]
2129
- };
2130
-
2131
- function extractColor( element ) {
2132
- var color,
2133
- elem = element;
2134
-
2135
- do {
2136
- color = elem.css( 'background-color' ).toLowerCase();
2137
- // keep going until we find an element that has color, or
2138
- // we hit the body
2139
- if( color != '' && color != 'transparent' ) break;
2140
- elem = elem.parent();
2141
- } while( !$.nodeName( elem.get( 0 ), 'body' ) );
2142
-
2143
- // catch Safari's way of signalling transparent
2144
- if( color == 'rgba(0, 0, 0, 0)' ) return 'transparent';
2145
- return color;
2146
- }
2147
-
2148
- // parse string, returns Color
2149
- function parseColor( str ) {
2150
- var result;
2151
-
2152
- // Try to lookup the color first before going mad with regexes
2153
- var name = $.trim( str ).toLowerCase();
2154
- if (name == 'transparent') {
2155
- return new Color( 255, 255, 255, 0 );
2156
- }
2157
- else if( !name.match( /^(rgb|#)/ ) ) {
2158
- result = lookupColors[name];
2159
- return new Color( result[0], result[1], result[2] );
2160
- }
2161
-
2162
- // Look for rgb(num,num,num)
2163
- if( result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec( str ) ) {
2164
- return new Color( parseInt( result[1], 10 ), parseInt( result[2], 10 ), parseInt( result[3], 10 ) );
2165
- }
2166
- // Look for rgba(num,num,num,num)
2167
- if( result = /rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec( str ) ) {
2168
- return new Color( parseInt( result[1], 10 ), parseInt( result[2], 10 ), parseInt( result[3], 10 ), parseFloat( result[4] ) );
2169
- }
2170
- // Look for rgb(num%,num%,num%)
2171
- if( result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec( str ) ) {
2172
- return new Color( parseFloat( result[1] ) * 2.55, parseFloat( result[2] ) * 2.55, parseFloat( result[3] ) * 2.55 );
2173
- }
2174
- // Look for rgba(num%,num%,num%,num)
2175
- if( result = /rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec( str ) ) {
2176
- return new Color( parseFloat( result[1] ) * 2.55, parseFloat( result[2]) * 2.55, parseFloat( result[3] ) * 2.55, parseFloat( result[4] ) );
2177
- }
2178
- // Look for #a0b1c2
2179
- if( result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec( str ) ) {
2180
- return new Color( parseInt( result[1], 16 ), parseInt( result[2], 16 ), parseInt( result[3], 16 ) );
2181
- }
2182
- // Look for #fff
2183
- if( result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec( str ) ) {
2184
- return new Color( parseInt( result[1] + result[1], 16 ), parseInt( result[2] + result[2], 16 ), parseInt( result[3] + result[3], 16 ) );
2185
- }
2186
- }
2187
- } )( jQuery );