chartist-rails 0.0.1 → 0.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +3 -2
- data/chartist-rails.gemspec +2 -2
- data/generators/install_settings.rb +11 -0
- data/lib/chartist-rails/version.rb +1 -1
- data/vendor/assets/javascripts/chartist.js +1261 -601
- data/vendor/assets/javascripts/chartist.min.js +4 -3
- data/vendor/assets/javascripts/chartist.min.js.map +1 -1
- data/vendor/assets/stylesheets/chartist.min.css +1 -1
- data/vendor/assets/stylesheets/chartist.scss +107 -26
- data/vendor/assets/stylesheets/settings/_chartist-settings.scss +9 -5
- metadata +6 -5
@@ -14,7 +14,7 @@
|
|
14
14
|
}
|
15
15
|
}(this, function () {
|
16
16
|
|
17
|
-
/* Chartist.js 0.
|
17
|
+
/* Chartist.js 0.9.4
|
18
18
|
* Copyright © 2015 Gion Kunz
|
19
19
|
* Free to use under the WTFPL license.
|
20
20
|
* http://www.wtfpl.net/
|
@@ -25,7 +25,7 @@
|
|
25
25
|
* @module Chartist.Core
|
26
26
|
*/
|
27
27
|
var Chartist = {
|
28
|
-
version: '0.
|
28
|
+
version: '0.9.4'
|
29
29
|
};
|
30
30
|
|
31
31
|
(function (window, document, Chartist) {
|
@@ -68,7 +68,7 @@ var Chartist = {
|
|
68
68
|
var sources = Array.prototype.slice.call(arguments, 1);
|
69
69
|
sources.forEach(function(source) {
|
70
70
|
for (var prop in source) {
|
71
|
-
if (typeof source[prop] === 'object' && !(source[prop] instanceof Array)) {
|
71
|
+
if (typeof source[prop] === 'object' && source[prop] !== null && !(source[prop] instanceof Array)) {
|
72
72
|
target[prop] = Chartist.extend({}, target[prop], source[prop]);
|
73
73
|
} else {
|
74
74
|
target[prop] = source[prop];
|
@@ -154,7 +154,33 @@ var Chartist = {
|
|
154
154
|
* @return {*}
|
155
155
|
*/
|
156
156
|
Chartist.sum = function(previous, current) {
|
157
|
-
return previous + current;
|
157
|
+
return previous + (current ? current : 0);
|
158
|
+
};
|
159
|
+
|
160
|
+
/**
|
161
|
+
* Multiply helper to be used in `Array.map` for multiplying each value of an array with a factor.
|
162
|
+
*
|
163
|
+
* @memberof Chartist.Core
|
164
|
+
* @param {Number} factor
|
165
|
+
* @returns {Function} Function that can be used in `Array.map` to multiply each value in an array
|
166
|
+
*/
|
167
|
+
Chartist.mapMultiply = function(factor) {
|
168
|
+
return function(num) {
|
169
|
+
return num * factor;
|
170
|
+
};
|
171
|
+
};
|
172
|
+
|
173
|
+
/**
|
174
|
+
* Add helper to be used in `Array.map` for adding a addend to each value of an array.
|
175
|
+
*
|
176
|
+
* @memberof Chartist.Core
|
177
|
+
* @param {Number} addend
|
178
|
+
* @returns {Function} Function that can be used in `Array.map` to add a addend to each value in an array
|
179
|
+
*/
|
180
|
+
Chartist.mapAdd = function(addend) {
|
181
|
+
return function(num) {
|
182
|
+
return num + addend;
|
183
|
+
};
|
158
184
|
};
|
159
185
|
|
160
186
|
/**
|
@@ -282,7 +308,7 @@ var Chartist = {
|
|
282
308
|
// Check if there is a previous SVG element in the container that contains the Chartist XML namespace and remove it
|
283
309
|
// Since the DOM API does not support namespaces we need to manually search the returned list http://www.w3.org/TR/selectors-api/
|
284
310
|
Array.prototype.slice.call(container.querySelectorAll('svg')).filter(function filterChartistSvgObjects(svg) {
|
285
|
-
return svg.
|
311
|
+
return svg.getAttributeNS('http://www.w3.org/2000/xmlns/', Chartist.xmlNs.prefix);
|
286
312
|
}).forEach(function removePreviousElement(svg) {
|
287
313
|
container.removeChild(svg);
|
288
314
|
});
|
@@ -314,7 +340,7 @@ var Chartist = {
|
|
314
340
|
for (var i = 0; i < data.series.length; i++) {
|
315
341
|
if(typeof(data.series[i]) === 'object' && data.series[i].data !== undefined) {
|
316
342
|
data.series[i].data.reverse();
|
317
|
-
} else {
|
343
|
+
} else if(data.series[i] instanceof Array) {
|
318
344
|
data.series[i].reverse();
|
319
345
|
}
|
320
346
|
}
|
@@ -326,13 +352,10 @@ var Chartist = {
|
|
326
352
|
* @memberof Chartist.Core
|
327
353
|
* @param {Object} data The series object that contains the data to be visualized in the chart
|
328
354
|
* @param {Boolean} reverse If true the whole data is reversed by the getDataArray call. This will modify the data object passed as first parameter. The labels as well as the series order is reversed. The whole series data arrays are reversed too.
|
355
|
+
* @param {Boolean} multi Create a multi dimensional array from a series data array where a value object with `x` and `y` values will be created.
|
329
356
|
* @return {Array} A plain array that contains the data to be visualized in the chart
|
330
357
|
*/
|
331
|
-
Chartist.getDataArray = function (data, reverse) {
|
332
|
-
var array = [],
|
333
|
-
value,
|
334
|
-
localData;
|
335
|
-
|
358
|
+
Chartist.getDataArray = function (data, reverse, multi) {
|
336
359
|
// If the data should be reversed but isn't we need to reverse it
|
337
360
|
// If it's reversed but it shouldn't we need to reverse it back
|
338
361
|
// That's required to handle data updates correctly and to reflect the responsive configurations
|
@@ -341,27 +364,41 @@ var Chartist = {
|
|
341
364
|
data.reversed = !data.reversed;
|
342
365
|
}
|
343
366
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
367
|
+
// Recursively walks through nested arrays and convert string values to numbers and objects with value properties
|
368
|
+
// to values. Check the tests in data core -> data normalization for a detailed specification of expected values
|
369
|
+
function recursiveConvert(value) {
|
370
|
+
if(Chartist.isFalseyButZero(value)) {
|
371
|
+
// This is a hole in data and we should return undefined
|
372
|
+
return undefined;
|
373
|
+
} else if((value.data || value) instanceof Array) {
|
374
|
+
return (value.data || value).map(recursiveConvert);
|
375
|
+
} else if(value.hasOwnProperty('value')) {
|
376
|
+
return recursiveConvert(value.value);
|
352
377
|
} else {
|
353
|
-
|
354
|
-
|
378
|
+
if(multi) {
|
379
|
+
var multiValue = {};
|
380
|
+
|
381
|
+
// Single series value arrays are assumed to specify the Y-Axis value
|
382
|
+
// For example: [1, 2] => [{x: undefined, y: 1}, {x: undefined, y: 2}]
|
383
|
+
// If multi is a string then it's assumed that it specified which dimension should be filled as default
|
384
|
+
if(typeof multi === 'string') {
|
385
|
+
multiValue[multi] = Chartist.getNumberOrUndefined(value);
|
386
|
+
} else {
|
387
|
+
multiValue.y = Chartist.getNumberOrUndefined(value);
|
388
|
+
}
|
355
389
|
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
390
|
+
multiValue.x = value.hasOwnProperty('x') ? Chartist.getNumberOrUndefined(value.x) : multiValue.x;
|
391
|
+
multiValue.y = value.hasOwnProperty('y') ? Chartist.getNumberOrUndefined(value.y) : multiValue.y;
|
392
|
+
|
393
|
+
return multiValue;
|
394
|
+
|
395
|
+
} else {
|
396
|
+
return Chartist.getNumberOrUndefined(value);
|
397
|
+
}
|
361
398
|
}
|
362
399
|
}
|
363
400
|
|
364
|
-
return
|
401
|
+
return data.series.map(recursiveConvert);
|
365
402
|
};
|
366
403
|
|
367
404
|
/**
|
@@ -388,28 +425,6 @@ var Chartist = {
|
|
388
425
|
};
|
389
426
|
};
|
390
427
|
|
391
|
-
/**
|
392
|
-
* Adds missing values at the end of the array. This array contains the data, that will be visualized in the chart
|
393
|
-
*
|
394
|
-
* @memberof Chartist.Core
|
395
|
-
* @param {Array} dataArray The array that contains the data to be visualized in the chart. The array in this parameter will be modified by function.
|
396
|
-
* @param {Number} length The length of the x-axis data array.
|
397
|
-
* @return {Array} The array that got updated with missing values.
|
398
|
-
*/
|
399
|
-
Chartist.normalizeDataArray = function (dataArray, length) {
|
400
|
-
for (var i = 0; i < dataArray.length; i++) {
|
401
|
-
if (dataArray[i].length === length) {
|
402
|
-
continue;
|
403
|
-
}
|
404
|
-
|
405
|
-
for (var j = dataArray[i].length; j < length; j++) {
|
406
|
-
dataArray[i][j] = 0;
|
407
|
-
}
|
408
|
-
}
|
409
|
-
|
410
|
-
return dataArray;
|
411
|
-
};
|
412
|
-
|
413
428
|
Chartist.getMetaData = function(series, index) {
|
414
429
|
var value = series.data ? series.data[index] : series[index];
|
415
430
|
return value ? Chartist.serialize(value.meta) : undefined;
|
@@ -455,32 +470,162 @@ var Chartist = {
|
|
455
470
|
* Get highest and lowest value of data array. This Array contains the data that will be visualized in the chart.
|
456
471
|
*
|
457
472
|
* @memberof Chartist.Core
|
458
|
-
* @param {Array}
|
473
|
+
* @param {Array} data The array that contains the data to be visualized in the chart
|
474
|
+
* @param {Object} options The Object that contains the chart options
|
475
|
+
* @param {String} dimension Axis dimension 'x' or 'y' used to access the correct value and high / low configuration
|
459
476
|
* @return {Object} An object that contains the highest and lowest value that will be visualized on the chart.
|
460
477
|
*/
|
461
|
-
Chartist.getHighLow = function (
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
478
|
+
Chartist.getHighLow = function (data, options, dimension) {
|
479
|
+
// TODO: Remove workaround for deprecated global high / low config. Axis high / low configuration is preferred
|
480
|
+
options = Chartist.extend({}, options, dimension ? options['axis' + dimension.toUpperCase()] : {});
|
481
|
+
|
482
|
+
var highLow = {
|
483
|
+
high: options.high === undefined ? -Number.MAX_VALUE : +options.high,
|
484
|
+
low: options.low === undefined ? Number.MAX_VALUE : +options.low
|
467
485
|
};
|
486
|
+
var findHigh = options.high === undefined;
|
487
|
+
var findLow = options.low === undefined;
|
488
|
+
|
489
|
+
// Function to recursively walk through arrays and find highest and lowest number
|
490
|
+
function recursiveHighLow(data) {
|
491
|
+
if(data === undefined) {
|
492
|
+
return undefined;
|
493
|
+
} else if(data instanceof Array) {
|
494
|
+
for (var i = 0; i < data.length; i++) {
|
495
|
+
recursiveHighLow(data[i]);
|
496
|
+
}
|
497
|
+
} else {
|
498
|
+
var value = dimension ? +data[dimension] : +data;
|
468
499
|
|
469
|
-
|
470
|
-
|
471
|
-
if (dataArray[i][j] > highLow.high) {
|
472
|
-
highLow.high = dataArray[i][j];
|
500
|
+
if (findHigh && value > highLow.high) {
|
501
|
+
highLow.high = value;
|
473
502
|
}
|
474
503
|
|
475
|
-
if (
|
476
|
-
highLow.low =
|
504
|
+
if (findLow && value < highLow.low) {
|
505
|
+
highLow.low = value;
|
477
506
|
}
|
478
507
|
}
|
479
508
|
}
|
480
509
|
|
510
|
+
// Start to find highest and lowest number recursively
|
511
|
+
if(findHigh || findLow) {
|
512
|
+
recursiveHighLow(data);
|
513
|
+
}
|
514
|
+
|
515
|
+
// Overrides of high / low based on reference value, it will make sure that the invisible reference value is
|
516
|
+
// used to generate the chart. This is useful when the chart always needs to contain the position of the
|
517
|
+
// invisible reference value in the view i.e. for bipolar scales.
|
518
|
+
if (options.referenceValue || options.referenceValue === 0) {
|
519
|
+
highLow.high = Math.max(options.referenceValue, highLow.high);
|
520
|
+
highLow.low = Math.min(options.referenceValue, highLow.low);
|
521
|
+
}
|
522
|
+
|
523
|
+
// If high and low are the same because of misconfiguration or flat data (only the same value) we need
|
524
|
+
// to set the high or low to 0 depending on the polarity
|
525
|
+
if (highLow.high <= highLow.low) {
|
526
|
+
// If both values are 0 we set high to 1
|
527
|
+
if (highLow.low === 0) {
|
528
|
+
highLow.high = 1;
|
529
|
+
} else if (highLow.low < 0) {
|
530
|
+
// If we have the same negative value for the bounds we set bounds.high to 0
|
531
|
+
highLow.high = 0;
|
532
|
+
} else {
|
533
|
+
// If we have the same positive value for the bounds we set bounds.low to 0
|
534
|
+
highLow.low = 0;
|
535
|
+
}
|
536
|
+
}
|
537
|
+
|
481
538
|
return highLow;
|
482
539
|
};
|
483
540
|
|
541
|
+
/**
|
542
|
+
* Checks if the value is a valid number or string with a number.
|
543
|
+
*
|
544
|
+
* @memberof Chartist.Core
|
545
|
+
* @param value
|
546
|
+
* @returns {Boolean}
|
547
|
+
*/
|
548
|
+
Chartist.isNum = function(value) {
|
549
|
+
return !isNaN(value) && isFinite(value);
|
550
|
+
};
|
551
|
+
|
552
|
+
/**
|
553
|
+
* Returns true on all falsey values except the numeric value 0.
|
554
|
+
*
|
555
|
+
* @memberof Chartist.Core
|
556
|
+
* @param value
|
557
|
+
* @returns {boolean}
|
558
|
+
*/
|
559
|
+
Chartist.isFalseyButZero = function(value) {
|
560
|
+
return !value && value !== 0;
|
561
|
+
};
|
562
|
+
|
563
|
+
/**
|
564
|
+
* Returns a number if the passed parameter is a valid number or the function will return undefined. On all other values than a valid number, this function will return undefined.
|
565
|
+
*
|
566
|
+
* @memberof Chartist.Core
|
567
|
+
* @param value
|
568
|
+
* @returns {*}
|
569
|
+
*/
|
570
|
+
Chartist.getNumberOrUndefined = function(value) {
|
571
|
+
return isNaN(+value) ? undefined : +value;
|
572
|
+
};
|
573
|
+
|
574
|
+
/**
|
575
|
+
* Gets a value from a dimension `value.x` or `value.y` while returning value directly if it's a valid numeric value. If the value is not numeric and it's falsey this function will return undefined.
|
576
|
+
*
|
577
|
+
* @param value
|
578
|
+
* @param dimension
|
579
|
+
* @returns {*}
|
580
|
+
*/
|
581
|
+
Chartist.getMultiValue = function(value, dimension) {
|
582
|
+
if(Chartist.isNum(value)) {
|
583
|
+
return +value;
|
584
|
+
} else if(value) {
|
585
|
+
return value[dimension || 'y'] || 0;
|
586
|
+
} else {
|
587
|
+
return 0;
|
588
|
+
}
|
589
|
+
};
|
590
|
+
|
591
|
+
/**
|
592
|
+
* Pollard Rho Algorithm to find smallest factor of an integer value. There are more efficient algorithms for factorization, but this one is quite efficient and not so complex.
|
593
|
+
*
|
594
|
+
* @memberof Chartist.Core
|
595
|
+
* @param {Number} num An integer number where the smallest factor should be searched for
|
596
|
+
* @returns {Number} The smallest integer factor of the parameter num.
|
597
|
+
*/
|
598
|
+
Chartist.rho = function(num) {
|
599
|
+
if(num === 1) {
|
600
|
+
return num;
|
601
|
+
}
|
602
|
+
|
603
|
+
function gcd(p, q) {
|
604
|
+
if (p % q === 0) {
|
605
|
+
return q;
|
606
|
+
} else {
|
607
|
+
return gcd(q, p % q);
|
608
|
+
}
|
609
|
+
}
|
610
|
+
|
611
|
+
function f(x) {
|
612
|
+
return x * x + 1;
|
613
|
+
}
|
614
|
+
|
615
|
+
var x1 = 2, x2 = 2, divisor;
|
616
|
+
if (num % 2 === 0) {
|
617
|
+
return 2;
|
618
|
+
}
|
619
|
+
|
620
|
+
do {
|
621
|
+
x1 = f(x1) % num;
|
622
|
+
x2 = f(f(x2)) % num;
|
623
|
+
divisor = gcd(Math.abs(x1 - x2), num);
|
624
|
+
} while (divisor === 1);
|
625
|
+
|
626
|
+
return divisor;
|
627
|
+
};
|
628
|
+
|
484
629
|
/**
|
485
630
|
* Calculate and retrieve all the bounds for the chart and return them in one array
|
486
631
|
*
|
@@ -488,11 +633,12 @@ var Chartist = {
|
|
488
633
|
* @param {Number} axisLength The length of the Axis used for
|
489
634
|
* @param {Object} highLow An object containing a high and low property indicating the value range of the chart.
|
490
635
|
* @param {Number} scaleMinSpace The minimum projected length a step should result in
|
491
|
-
* @param {
|
636
|
+
* @param {Boolean} onlyInteger
|
492
637
|
* @return {Object} All the values to set the bounds of the chart
|
493
638
|
*/
|
494
|
-
Chartist.getBounds = function (axisLength, highLow, scaleMinSpace,
|
639
|
+
Chartist.getBounds = function (axisLength, highLow, scaleMinSpace, onlyInteger) {
|
495
640
|
var i,
|
641
|
+
optimizationCounter = 0,
|
496
642
|
newMin,
|
497
643
|
newMax,
|
498
644
|
bounds = {
|
@@ -500,63 +646,57 @@ var Chartist = {
|
|
500
646
|
low: highLow.low
|
501
647
|
};
|
502
648
|
|
503
|
-
// If high and low are the same because of misconfiguration or flat data (only the same value) we need
|
504
|
-
// to set the high or low to 0 depending on the polarity
|
505
|
-
if(bounds.high === bounds.low) {
|
506
|
-
// If both values are 0 we set high to 1
|
507
|
-
if(bounds.low === 0) {
|
508
|
-
bounds.high = 1;
|
509
|
-
} else if(bounds.low < 0) {
|
510
|
-
// If we have the same negative value for the bounds we set bounds.high to 0
|
511
|
-
bounds.high = 0;
|
512
|
-
} else {
|
513
|
-
// If we have the same positive value for the bounds we set bounds.low to 0
|
514
|
-
bounds.low = 0;
|
515
|
-
}
|
516
|
-
}
|
517
|
-
|
518
|
-
// Overrides of high / low based on reference value, it will make sure that the invisible reference value is
|
519
|
-
// used to generate the chart. This is useful when the chart always needs to contain the position of the
|
520
|
-
// invisible reference value in the view i.e. for bipolar scales.
|
521
|
-
if (referenceValue || referenceValue === 0) {
|
522
|
-
bounds.high = Math.max(referenceValue, bounds.high);
|
523
|
-
bounds.low = Math.min(referenceValue, bounds.low);
|
524
|
-
}
|
525
|
-
|
526
649
|
bounds.valueRange = bounds.high - bounds.low;
|
527
650
|
bounds.oom = Chartist.orderOfMagnitude(bounds.valueRange);
|
528
|
-
bounds.min = Math.floor(bounds.low / Math.pow(10, bounds.oom)) * Math.pow(10, bounds.oom);
|
529
|
-
bounds.max = Math.ceil(bounds.high / Math.pow(10, bounds.oom)) * Math.pow(10, bounds.oom);
|
530
|
-
bounds.range = bounds.max - bounds.min;
|
531
651
|
bounds.step = Math.pow(10, bounds.oom);
|
652
|
+
bounds.min = Math.floor(bounds.low / bounds.step) * bounds.step;
|
653
|
+
bounds.max = Math.ceil(bounds.high / bounds.step) * bounds.step;
|
654
|
+
bounds.range = bounds.max - bounds.min;
|
532
655
|
bounds.numberOfSteps = Math.round(bounds.range / bounds.step);
|
533
656
|
|
534
657
|
// Optimize scale step by checking if subdivision is possible based on horizontalGridMinSpace
|
535
658
|
// If we are already below the scaleMinSpace value we will scale up
|
536
|
-
var length = Chartist.projectLength(axisLength, bounds.step, bounds)
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
659
|
+
var length = Chartist.projectLength(axisLength, bounds.step, bounds);
|
660
|
+
var scaleUp = length < scaleMinSpace;
|
661
|
+
var smallestFactor = onlyInteger ? Chartist.rho(bounds.range) : 0;
|
662
|
+
|
663
|
+
// First check if we should only use integer steps and if step 1 is still larger than scaleMinSpace so we can use 1
|
664
|
+
if(onlyInteger && Chartist.projectLength(axisLength, 1, bounds) >= scaleMinSpace) {
|
665
|
+
bounds.step = 1;
|
666
|
+
} else if(onlyInteger && smallestFactor < bounds.step && Chartist.projectLength(axisLength, smallestFactor, bounds) >= scaleMinSpace) {
|
667
|
+
// If step 1 was too small, we can try the smallest factor of range
|
668
|
+
// If the smallest factor is smaller than the current bounds.step and the projected length of smallest factor
|
669
|
+
// is larger than the scaleMinSpace we should go for it.
|
670
|
+
bounds.step = smallestFactor;
|
671
|
+
} else {
|
672
|
+
// Trying to divide or multiply by 2 and find the best step value
|
673
|
+
while (true) {
|
674
|
+
if (scaleUp && Chartist.projectLength(axisLength, bounds.step, bounds) <= scaleMinSpace) {
|
675
|
+
bounds.step *= 2;
|
676
|
+
} else if (!scaleUp && Chartist.projectLength(axisLength, bounds.step / 2, bounds) >= scaleMinSpace) {
|
677
|
+
bounds.step /= 2;
|
678
|
+
if(onlyInteger && bounds.step % 1 !== 0) {
|
679
|
+
bounds.step *= 2;
|
680
|
+
break;
|
681
|
+
}
|
682
|
+
} else {
|
683
|
+
break;
|
684
|
+
}
|
685
|
+
|
686
|
+
if(optimizationCounter++ > 1000) {
|
687
|
+
throw new Error('Exceeded maximum number of iterations while optimizing scale step!');
|
688
|
+
}
|
546
689
|
}
|
547
690
|
}
|
548
691
|
|
549
692
|
// Narrow min and max based on new step
|
550
693
|
newMin = bounds.min;
|
551
694
|
newMax = bounds.max;
|
552
|
-
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
557
|
-
if (i - bounds.step >= bounds.high) {
|
558
|
-
newMax -= bounds.step;
|
559
|
-
}
|
695
|
+
while(newMin + bounds.step <= bounds.low) {
|
696
|
+
newMin += bounds.step;
|
697
|
+
}
|
698
|
+
while(newMax - bounds.step >= bounds.high) {
|
699
|
+
newMax -= bounds.step;
|
560
700
|
}
|
561
701
|
bounds.min = newMin;
|
562
702
|
bounds.max = newMax;
|
@@ -599,17 +739,20 @@ var Chartist = {
|
|
599
739
|
* @return {Object} The chart rectangles coordinates inside the svg element plus the rectangles measurements
|
600
740
|
*/
|
601
741
|
Chartist.createChartRect = function (svg, options, fallbackPadding) {
|
602
|
-
var
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
742
|
+
var hasAxis = !!(options.axisX || options.axisY);
|
743
|
+
var yAxisOffset = hasAxis ? options.axisY.offset : 0;
|
744
|
+
var xAxisOffset = hasAxis ? options.axisX.offset : 0;
|
745
|
+
// If width or height results in invalid value (including 0) we fallback to the unitless settings or even 0
|
746
|
+
var width = svg.width() || Chartist.stripUnit(options.width) || 0;
|
747
|
+
var height = svg.height() || Chartist.stripUnit(options.height) || 0;
|
748
|
+
var normalizedPadding = Chartist.normalizePadding(options.chartPadding, fallbackPadding);
|
749
|
+
|
750
|
+
// If settings were to small to cope with offset (legacy) and padding, we'll adjust
|
751
|
+
width = Math.max(width, yAxisOffset + normalizedPadding.left + normalizedPadding.right);
|
752
|
+
height = Math.max(height, xAxisOffset + normalizedPadding.top + normalizedPadding.bottom);
|
753
|
+
|
754
|
+
var chartRect = {
|
755
|
+
padding: normalizedPadding,
|
613
756
|
width: function () {
|
614
757
|
return this.x2 - this.x1;
|
615
758
|
},
|
@@ -617,13 +760,38 @@ var Chartist = {
|
|
617
760
|
return this.y1 - this.y2;
|
618
761
|
}
|
619
762
|
};
|
763
|
+
|
764
|
+
if(hasAxis) {
|
765
|
+
if (options.axisX.position === 'start') {
|
766
|
+
chartRect.y2 = normalizedPadding.top + xAxisOffset;
|
767
|
+
chartRect.y1 = Math.max(height - normalizedPadding.bottom, chartRect.y2 + 1);
|
768
|
+
} else {
|
769
|
+
chartRect.y2 = normalizedPadding.top;
|
770
|
+
chartRect.y1 = Math.max(height - normalizedPadding.bottom - xAxisOffset, chartRect.y2 + 1);
|
771
|
+
}
|
772
|
+
|
773
|
+
if (options.axisY.position === 'start') {
|
774
|
+
chartRect.x1 = normalizedPadding.left + yAxisOffset;
|
775
|
+
chartRect.x2 = Math.max(width - normalizedPadding.right, chartRect.x1 + 1);
|
776
|
+
} else {
|
777
|
+
chartRect.x1 = normalizedPadding.left;
|
778
|
+
chartRect.x2 = Math.max(width - normalizedPadding.right - yAxisOffset, chartRect.x1 + 1);
|
779
|
+
}
|
780
|
+
} else {
|
781
|
+
chartRect.x1 = normalizedPadding.left;
|
782
|
+
chartRect.x2 = Math.max(width - normalizedPadding.right, chartRect.x1 + 1);
|
783
|
+
chartRect.y2 = normalizedPadding.top;
|
784
|
+
chartRect.y1 = Math.max(height - normalizedPadding.bottom, chartRect.y2 + 1);
|
785
|
+
}
|
786
|
+
|
787
|
+
return chartRect;
|
620
788
|
};
|
621
789
|
|
622
790
|
/**
|
623
791
|
* Creates a grid line based on a projected value.
|
624
792
|
*
|
625
793
|
* @memberof Chartist.Core
|
626
|
-
* @param
|
794
|
+
* @param position
|
627
795
|
* @param index
|
628
796
|
* @param axis
|
629
797
|
* @param offset
|
@@ -632,10 +800,10 @@ var Chartist = {
|
|
632
800
|
* @param classes
|
633
801
|
* @param eventEmitter
|
634
802
|
*/
|
635
|
-
Chartist.createGrid = function(
|
803
|
+
Chartist.createGrid = function(position, index, axis, offset, length, group, classes, eventEmitter) {
|
636
804
|
var positionalData = {};
|
637
|
-
positionalData[axis.units.pos + '1'] =
|
638
|
-
positionalData[axis.units.pos + '2'] =
|
805
|
+
positionalData[axis.units.pos + '1'] = position;
|
806
|
+
positionalData[axis.units.pos + '2'] = position;
|
639
807
|
positionalData[axis.counterUnits.pos + '1'] = offset;
|
640
808
|
positionalData[axis.counterUnits.pos + '2'] = offset + length;
|
641
809
|
|
@@ -657,7 +825,8 @@ var Chartist = {
|
|
657
825
|
* Creates a label based on a projected value and an axis.
|
658
826
|
*
|
659
827
|
* @memberof Chartist.Core
|
660
|
-
* @param
|
828
|
+
* @param position
|
829
|
+
* @param length
|
661
830
|
* @param index
|
662
831
|
* @param labels
|
663
832
|
* @param axis
|
@@ -668,16 +837,23 @@ var Chartist = {
|
|
668
837
|
* @param useForeignObject
|
669
838
|
* @param eventEmitter
|
670
839
|
*/
|
671
|
-
Chartist.createLabel = function(
|
672
|
-
var labelElement
|
673
|
-
|
674
|
-
|
840
|
+
Chartist.createLabel = function(position, length, index, labels, axis, axisOffset, labelOffset, group, classes, useForeignObject, eventEmitter) {
|
841
|
+
var labelElement;
|
842
|
+
var positionalData = {};
|
843
|
+
|
844
|
+
positionalData[axis.units.pos] = position + labelOffset[axis.units.pos];
|
675
845
|
positionalData[axis.counterUnits.pos] = labelOffset[axis.counterUnits.pos];
|
676
|
-
positionalData[axis.units.len] =
|
677
|
-
positionalData[axis.counterUnits.len] = axisOffset;
|
846
|
+
positionalData[axis.units.len] = length;
|
847
|
+
positionalData[axis.counterUnits.len] = axisOffset - 10;
|
678
848
|
|
679
849
|
if(useForeignObject) {
|
680
|
-
|
850
|
+
// We need to set width and height explicitly to px as span will not expand with width and height being
|
851
|
+
// 100% in all browsers
|
852
|
+
var content = '<span class="' + classes.join(' ') + '" style="' +
|
853
|
+
axis.units.len + ': ' + Math.round(positionalData[axis.units.len]) + 'px; ' +
|
854
|
+
axis.counterUnits.len + ': ' + Math.round(positionalData[axis.counterUnits.len]) + 'px">' +
|
855
|
+
labels[index] + '</span>';
|
856
|
+
|
681
857
|
labelElement = group.foreignObject(content, Chartist.extend({
|
682
858
|
style: 'overflow: visible;'
|
683
859
|
}, positionalData));
|
@@ -696,43 +872,21 @@ var Chartist = {
|
|
696
872
|
};
|
697
873
|
|
698
874
|
/**
|
699
|
-
*
|
875
|
+
* Helper to read series specific options from options object. It automatically falls back to the global option if
|
876
|
+
* there is no option in the series options.
|
700
877
|
*
|
701
|
-
* @
|
702
|
-
* @param
|
703
|
-
* @param
|
704
|
-
* @
|
705
|
-
* @param gridGroup
|
706
|
-
* @param labelGroup
|
707
|
-
* @param useForeignObject
|
708
|
-
* @param options
|
709
|
-
* @param eventEmitter
|
878
|
+
* @param {Object} series Series object
|
879
|
+
* @param {Object} options Chartist options object
|
880
|
+
* @param {string} key The options key that should be used to obtain the options
|
881
|
+
* @returns {*}
|
710
882
|
*/
|
711
|
-
Chartist.
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
if(!labelValues[index] && labelValues[index] !== 0) {
|
719
|
-
return;
|
720
|
-
}
|
721
|
-
|
722
|
-
if(axisOptions.showGrid) {
|
723
|
-
Chartist.createGrid(projectedValue, index, axis, axis.gridOffset, chartRect[axis.counterUnits.len](), gridGroup, [
|
724
|
-
options.classNames.grid,
|
725
|
-
options.classNames[axis.units.dir]
|
726
|
-
], eventEmitter);
|
727
|
-
}
|
728
|
-
|
729
|
-
if(axisOptions.showLabel) {
|
730
|
-
Chartist.createLabel(projectedValue, index, labelValues, axis, axisOptions.offset, axis.labelOffset, labelGroup, [
|
731
|
-
options.classNames.label,
|
732
|
-
options.classNames[axis.units.dir]
|
733
|
-
], useForeignObject, eventEmitter);
|
734
|
-
}
|
735
|
-
});
|
883
|
+
Chartist.getSeriesOption = function(series, options, key) {
|
884
|
+
if(series.name && options.series && options.series[series.name]) {
|
885
|
+
var seriesOptions = options.series[series.name];
|
886
|
+
return seriesOptions.hasOwnProperty(key) ? seriesOptions[key] : options[key];
|
887
|
+
} else {
|
888
|
+
return options[key];
|
889
|
+
}
|
736
890
|
};
|
737
891
|
|
738
892
|
/**
|
@@ -791,10 +945,10 @@ var Chartist = {
|
|
791
945
|
updateCurrentOptions(true);
|
792
946
|
|
793
947
|
return {
|
794
|
-
|
948
|
+
removeMediaQueryListeners: removeMediaQueryListeners,
|
949
|
+
getCurrentOptions: function getCurrentOptions() {
|
795
950
|
return Chartist.extend({}, currentOptions);
|
796
|
-
}
|
797
|
-
removeMediaQueryListeners: removeMediaQueryListeners
|
951
|
+
}
|
798
952
|
};
|
799
953
|
};
|
800
954
|
|
@@ -817,11 +971,27 @@ var Chartist = {
|
|
817
971
|
* @return {Function}
|
818
972
|
*/
|
819
973
|
Chartist.Interpolation.none = function() {
|
820
|
-
return function
|
821
|
-
var path = new Chartist.Svg.Path()
|
974
|
+
return function none(pathCoordinates, valueData) {
|
975
|
+
var path = new Chartist.Svg.Path();
|
976
|
+
// We need to assume that the first value is a "hole"
|
977
|
+
var hole = true;
|
822
978
|
|
823
|
-
for(var i =
|
824
|
-
|
979
|
+
for(var i = 1; i < pathCoordinates.length; i += 2) {
|
980
|
+
var data = valueData[(i - 1) / 2];
|
981
|
+
|
982
|
+
// If the current value is undefined we should treat it as a hole start
|
983
|
+
if(data.value === undefined) {
|
984
|
+
hole = true;
|
985
|
+
} else {
|
986
|
+
// If this value is valid we need to check if we're coming out of a hole
|
987
|
+
if(hole) {
|
988
|
+
// If we are coming out of a hole we should first make a move and also reset the hole flag
|
989
|
+
path.move(pathCoordinates[i - 1], pathCoordinates[i], false, data);
|
990
|
+
hole = false;
|
991
|
+
} else {
|
992
|
+
path.line(pathCoordinates[i - 1], pathCoordinates[i], false, data);
|
993
|
+
}
|
994
|
+
}
|
825
995
|
}
|
826
996
|
|
827
997
|
return path;
|
@@ -858,24 +1028,42 @@ var Chartist = {
|
|
858
1028
|
|
859
1029
|
var d = 1 / Math.max(1, options.divisor);
|
860
1030
|
|
861
|
-
return function simple(pathCoordinates) {
|
862
|
-
var path = new Chartist.Svg.Path()
|
1031
|
+
return function simple(pathCoordinates, valueData) {
|
1032
|
+
var path = new Chartist.Svg.Path();
|
1033
|
+
var hole = true;
|
863
1034
|
|
864
1035
|
for(var i = 2; i < pathCoordinates.length; i += 2) {
|
865
|
-
var prevX = pathCoordinates[i - 2]
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
876
|
-
|
877
|
-
|
878
|
-
|
1036
|
+
var prevX = pathCoordinates[i - 2];
|
1037
|
+
var prevY = pathCoordinates[i - 1];
|
1038
|
+
var currX = pathCoordinates[i];
|
1039
|
+
var currY = pathCoordinates[i + 1];
|
1040
|
+
var length = (currX - prevX) * d;
|
1041
|
+
var prevData = valueData[(i / 2) - 1];
|
1042
|
+
var currData = valueData[i / 2];
|
1043
|
+
|
1044
|
+
if(prevData.value === undefined) {
|
1045
|
+
hole = true;
|
1046
|
+
} else {
|
1047
|
+
|
1048
|
+
if(hole) {
|
1049
|
+
path.move(prevX, prevY, false, prevData);
|
1050
|
+
}
|
1051
|
+
|
1052
|
+
if(currData.value !== undefined) {
|
1053
|
+
path.curve(
|
1054
|
+
prevX + length,
|
1055
|
+
prevY,
|
1056
|
+
currX - length,
|
1057
|
+
currY,
|
1058
|
+
currX,
|
1059
|
+
currY,
|
1060
|
+
false,
|
1061
|
+
currData
|
1062
|
+
);
|
1063
|
+
|
1064
|
+
hole = false;
|
1065
|
+
}
|
1066
|
+
}
|
879
1067
|
}
|
880
1068
|
|
881
1069
|
return path;
|
@@ -913,47 +1101,169 @@ var Chartist = {
|
|
913
1101
|
var t = Math.min(1, Math.max(0, options.tension)),
|
914
1102
|
c = 1 - t;
|
915
1103
|
|
916
|
-
|
917
|
-
|
918
|
-
|
919
|
-
|
1104
|
+
// This function will help us to split pathCoordinates and valueData into segments that also contain pathCoordinates
|
1105
|
+
// and valueData. This way the existing functions can be reused and the segment paths can be joined afterwards.
|
1106
|
+
// This functionality is necessary to treat "holes" in the line charts
|
1107
|
+
function splitIntoSegments(pathCoordinates, valueData) {
|
1108
|
+
var segments = [];
|
1109
|
+
var hole = true;
|
1110
|
+
|
1111
|
+
for(var i = 0; i < pathCoordinates.length; i += 2) {
|
1112
|
+
// If this value is a "hole" we set the hole flag
|
1113
|
+
if(valueData[i / 2].value === undefined) {
|
1114
|
+
hole = true;
|
1115
|
+
} else {
|
1116
|
+
// If it's a valid value we need to check if we're coming out of a hole and create a new empty segment
|
1117
|
+
if(hole) {
|
1118
|
+
segments.push({
|
1119
|
+
pathCoordinates: [],
|
1120
|
+
valueData: []
|
1121
|
+
});
|
1122
|
+
// As we have a valid value now, we are not in a "hole" anymore
|
1123
|
+
hole = false;
|
1124
|
+
}
|
1125
|
+
|
1126
|
+
// Add to the segment pathCoordinates and valueData
|
1127
|
+
segments[segments.length - 1].pathCoordinates.push(pathCoordinates[i], pathCoordinates[i + 1]);
|
1128
|
+
segments[segments.length - 1].valueData.push(valueData[i / 2]);
|
1129
|
+
}
|
920
1130
|
}
|
921
1131
|
|
922
|
-
|
923
|
-
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
931
|
-
|
932
|
-
|
933
|
-
|
934
|
-
|
935
|
-
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
1132
|
+
return segments;
|
1133
|
+
}
|
1134
|
+
|
1135
|
+
return function cardinal(pathCoordinates, valueData) {
|
1136
|
+
// First we try to split the coordinates into segments
|
1137
|
+
// This is necessary to treat "holes" in line charts
|
1138
|
+
var segments = splitIntoSegments(pathCoordinates, valueData);
|
1139
|
+
|
1140
|
+
// If the split resulted in more that one segment we need to interpolate each segment individually and join them
|
1141
|
+
// afterwards together into a single path.
|
1142
|
+
if(segments.length > 1) {
|
1143
|
+
var paths = [];
|
1144
|
+
// For each segment we will recurse the cardinal function
|
1145
|
+
segments.forEach(function(segment) {
|
1146
|
+
paths.push(cardinal(segment.pathCoordinates, segment.valueData));
|
1147
|
+
});
|
1148
|
+
// Join the segment path data into a single path and return
|
1149
|
+
return Chartist.Svg.Path.join(paths);
|
1150
|
+
} else {
|
1151
|
+
// If there was only one segment we can proceed regularly by using pathCoordinates and valueData from the first
|
1152
|
+
// segment
|
1153
|
+
pathCoordinates = segments[0].pathCoordinates;
|
1154
|
+
valueData = segments[0].valueData;
|
1155
|
+
|
1156
|
+
// If less than two points we need to fallback to no smoothing
|
1157
|
+
if(pathCoordinates.length <= 4) {
|
1158
|
+
return Chartist.Interpolation.none()(pathCoordinates, valueData);
|
1159
|
+
}
|
1160
|
+
|
1161
|
+
var path = new Chartist.Svg.Path().move(pathCoordinates[0], pathCoordinates[1], false, valueData[0]),
|
1162
|
+
z;
|
1163
|
+
|
1164
|
+
for (var i = 0, iLen = pathCoordinates.length; iLen - 2 * !z > i; i += 2) {
|
1165
|
+
var p = [
|
1166
|
+
{x: +pathCoordinates[i - 2], y: +pathCoordinates[i - 1]},
|
1167
|
+
{x: +pathCoordinates[i], y: +pathCoordinates[i + 1]},
|
1168
|
+
{x: +pathCoordinates[i + 2], y: +pathCoordinates[i + 3]},
|
1169
|
+
{x: +pathCoordinates[i + 4], y: +pathCoordinates[i + 5]}
|
1170
|
+
];
|
1171
|
+
if (z) {
|
1172
|
+
if (!i) {
|
1173
|
+
p[0] = {x: +pathCoordinates[iLen - 2], y: +pathCoordinates[iLen - 1]};
|
1174
|
+
} else if (iLen - 4 === i) {
|
1175
|
+
p[3] = {x: +pathCoordinates[0], y: +pathCoordinates[1]};
|
1176
|
+
} else if (iLen - 2 === i) {
|
1177
|
+
p[2] = {x: +pathCoordinates[0], y: +pathCoordinates[1]};
|
1178
|
+
p[3] = {x: +pathCoordinates[2], y: +pathCoordinates[3]};
|
1179
|
+
}
|
1180
|
+
} else {
|
1181
|
+
if (iLen - 4 === i) {
|
1182
|
+
p[3] = p[2];
|
1183
|
+
} else if (!i) {
|
1184
|
+
p[0] = {x: +pathCoordinates[i], y: +pathCoordinates[i + 1]};
|
1185
|
+
}
|
940
1186
|
}
|
1187
|
+
|
1188
|
+
path.curve(
|
1189
|
+
(t * (-p[0].x + 6 * p[1].x + p[2].x) / 6) + (c * p[2].x),
|
1190
|
+
(t * (-p[0].y + 6 * p[1].y + p[2].y) / 6) + (c * p[2].y),
|
1191
|
+
(t * (p[1].x + 6 * p[2].x - p[3].x) / 6) + (c * p[2].x),
|
1192
|
+
(t * (p[1].y + 6 * p[2].y - p[3].y) / 6) + (c * p[2].y),
|
1193
|
+
p[2].x,
|
1194
|
+
p[2].y,
|
1195
|
+
false,
|
1196
|
+
valueData[(i + 2) / 2]
|
1197
|
+
);
|
1198
|
+
}
|
1199
|
+
|
1200
|
+
return path;
|
1201
|
+
}
|
1202
|
+
};
|
1203
|
+
};
|
1204
|
+
|
1205
|
+
/**
|
1206
|
+
* Step interpolation will cause the line chart to move in steps rather than diagonal or smoothed lines. This interpolation will create additional points that will also be drawn when the `showPoint` option is enabled.
|
1207
|
+
*
|
1208
|
+
* All smoothing functions within Chartist are factory functions that accept an options parameter. The step interpolation function accepts one configuration parameter `postpone`, that can be `true` or `false`. The default value is `true` and will cause the step to occur where the value actually changes. If a different behaviour is needed where the step is shifted to the left and happens before the actual value, this option can be set to `false`.
|
1209
|
+
*
|
1210
|
+
* @example
|
1211
|
+
* var chart = new Chartist.Line('.ct-chart', {
|
1212
|
+
* labels: [1, 2, 3, 4, 5],
|
1213
|
+
* series: [[1, 2, 8, 1, 7]]
|
1214
|
+
* }, {
|
1215
|
+
* lineSmooth: Chartist.Interpolation.step({
|
1216
|
+
* postpone: true
|
1217
|
+
* })
|
1218
|
+
* });
|
1219
|
+
*
|
1220
|
+
* @memberof Chartist.Interpolation
|
1221
|
+
* @param options
|
1222
|
+
* @returns {Function}
|
1223
|
+
*/
|
1224
|
+
Chartist.Interpolation.step = function(options) {
|
1225
|
+
var defaultOptions = {
|
1226
|
+
postpone: true
|
1227
|
+
};
|
1228
|
+
|
1229
|
+
options = Chartist.extend({}, defaultOptions, options);
|
1230
|
+
|
1231
|
+
return function step(pathCoordinates, valueData) {
|
1232
|
+
var path = new Chartist.Svg.Path();
|
1233
|
+
var hole = true;
|
1234
|
+
|
1235
|
+
for (var i = 2; i < pathCoordinates.length; i += 2) {
|
1236
|
+
var prevX = pathCoordinates[i - 2];
|
1237
|
+
var prevY = pathCoordinates[i - 1];
|
1238
|
+
var currX = pathCoordinates[i];
|
1239
|
+
var currY = pathCoordinates[i + 1];
|
1240
|
+
var prevData = valueData[(i / 2) - 1];
|
1241
|
+
var currData = valueData[i / 2];
|
1242
|
+
|
1243
|
+
// If last point is a "hole"
|
1244
|
+
if(prevData.value === undefined) {
|
1245
|
+
hole = true;
|
941
1246
|
} else {
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
p[0] = {x: +pathCoordinates[i], y: +pathCoordinates[i + 1]};
|
1247
|
+
// If last point is not a "hole" but we just came back out of a "hole" we need to move first
|
1248
|
+
if(hole) {
|
1249
|
+
path.move(prevX, prevY, false, prevData);
|
946
1250
|
}
|
947
|
-
}
|
948
1251
|
|
949
|
-
|
950
|
-
(
|
951
|
-
|
952
|
-
|
953
|
-
|
954
|
-
|
955
|
-
|
956
|
-
|
1252
|
+
// If the current point is also not a hole we can draw the step lines
|
1253
|
+
if(currData.value !== undefined) {
|
1254
|
+
if(options.postpone) {
|
1255
|
+
// If postponed we should draw the step line with the value of the previous value
|
1256
|
+
path.line(currX, prevY, false, prevData);
|
1257
|
+
} else {
|
1258
|
+
// If not postponed we should draw the step line with the value of the current value
|
1259
|
+
path.line(prevX, currY, false, currData);
|
1260
|
+
}
|
1261
|
+
// Line to the actual point (this should only be a Y-Axis movement
|
1262
|
+
path.line(currX, currY, false, currData);
|
1263
|
+
// Reset the "hole" flag as previous and current point have valid values
|
1264
|
+
hole = false;
|
1265
|
+
}
|
1266
|
+
}
|
957
1267
|
}
|
958
1268
|
|
959
1269
|
return path;
|
@@ -1196,7 +1506,7 @@ var Chartist = {
|
|
1196
1506
|
|
1197
1507
|
// Only re-created the chart if it has been initialized yet
|
1198
1508
|
if(!this.initializeTimeoutId) {
|
1199
|
-
this.createChart(this.optionsProvider.
|
1509
|
+
this.createChart(this.optionsProvider.getCurrentOptions());
|
1200
1510
|
}
|
1201
1511
|
|
1202
1512
|
// Return a reference to the chart object to chain up calls
|
@@ -1209,8 +1519,15 @@ var Chartist = {
|
|
1209
1519
|
* @memberof Chartist.Base
|
1210
1520
|
*/
|
1211
1521
|
function detach() {
|
1212
|
-
|
1213
|
-
|
1522
|
+
// Only detach if initialization already occurred on this chart. If this chart still hasn't initialized (therefore
|
1523
|
+
// the initializationTimeoutId is still a valid timeout reference, we will clear the timeout
|
1524
|
+
if(!this.initializeTimeoutId) {
|
1525
|
+
window.removeEventListener('resize', this.resizeListener);
|
1526
|
+
this.optionsProvider.removeMediaQueryListeners();
|
1527
|
+
} else {
|
1528
|
+
window.clearTimeout(this.initializeTimeoutId);
|
1529
|
+
}
|
1530
|
+
|
1214
1531
|
return this;
|
1215
1532
|
}
|
1216
1533
|
|
@@ -1269,7 +1586,7 @@ var Chartist = {
|
|
1269
1586
|
});
|
1270
1587
|
|
1271
1588
|
// Create the first chart
|
1272
|
-
this.createChart(this.optionsProvider.
|
1589
|
+
this.createChart(this.optionsProvider.getCurrentOptions());
|
1273
1590
|
|
1274
1591
|
// As chart is initialized from the event loop now we can reset our timeout reference
|
1275
1592
|
// This is important if the chart gets initialized on the same element twice
|
@@ -1302,14 +1619,7 @@ var Chartist = {
|
|
1302
1619
|
if(this.container) {
|
1303
1620
|
// If chartist was already initialized in this container we are detaching all event listeners first
|
1304
1621
|
if(this.container.__chartist__) {
|
1305
|
-
|
1306
|
-
// If the initializeTimeoutId is still set we can safely assume that the initialization function has not
|
1307
|
-
// been called yet from the event loop. Therefore we should cancel the timeout and don't need to detach
|
1308
|
-
window.clearTimeout(this.container.__chartist__.initializeTimeoutId);
|
1309
|
-
} else {
|
1310
|
-
// The timeout reference has already been reset which means we need to detach the old chart first
|
1311
|
-
this.container.__chartist__.detach();
|
1312
|
-
}
|
1622
|
+
this.container.__chartist__.detach();
|
1313
1623
|
}
|
1314
1624
|
|
1315
1625
|
this.container.__chartist__ = this;
|
@@ -1363,7 +1673,7 @@ var Chartist = {
|
|
1363
1673
|
*
|
1364
1674
|
* @memberof Chartist.Svg
|
1365
1675
|
* @constructor
|
1366
|
-
* @param {String|
|
1676
|
+
* @param {String|Element} name The name of the SVG element to create or an SVG dom element which should be wrapped into Chartist.Svg
|
1367
1677
|
* @param {Object} attributes An object with properties that will be added as attributes to the SVG element that is created. Attributes with undefined values will not be added.
|
1368
1678
|
* @param {String} className This class or class list will be added to the SVG element
|
1369
1679
|
* @param {Object} parent The parent SVG wrapper object where this newly created wrapper and it's element will be attached to as child
|
@@ -1371,7 +1681,7 @@ var Chartist = {
|
|
1371
1681
|
*/
|
1372
1682
|
function Svg(name, attributes, className, parent, insertFirst) {
|
1373
1683
|
// If Svg is getting called with an SVG element we just return the wrapper
|
1374
|
-
if(name instanceof
|
1684
|
+
if(name instanceof Element) {
|
1375
1685
|
this._node = name;
|
1376
1686
|
} else {
|
1377
1687
|
this._node = document.createElementNS(svgNs, name);
|
@@ -1380,21 +1690,21 @@ var Chartist = {
|
|
1380
1690
|
if(name === 'svg') {
|
1381
1691
|
this._node.setAttributeNS(xmlNs, Chartist.xmlNs.qualifiedName, Chartist.xmlNs.uri);
|
1382
1692
|
}
|
1693
|
+
}
|
1383
1694
|
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1695
|
+
if(attributes) {
|
1696
|
+
this.attr(attributes);
|
1697
|
+
}
|
1387
1698
|
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1699
|
+
if(className) {
|
1700
|
+
this.addClass(className);
|
1701
|
+
}
|
1391
1702
|
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
}
|
1703
|
+
if(parent) {
|
1704
|
+
if (insertFirst && parent._node.firstChild) {
|
1705
|
+
parent._node.insertBefore(this._node, parent._node.firstChild);
|
1706
|
+
} else {
|
1707
|
+
parent._node.appendChild(this._node);
|
1398
1708
|
}
|
1399
1709
|
}
|
1400
1710
|
}
|
@@ -1449,6 +1759,7 @@ var Chartist = {
|
|
1449
1759
|
/**
|
1450
1760
|
* Returns the parent Chartist.SVG wrapper object
|
1451
1761
|
*
|
1762
|
+
* @memberof Chartist.Svg
|
1452
1763
|
* @return {Chartist.Svg} Returns a Chartist.Svg wrapper around the parent node of the current node. If the parent node is not existing or it's not an SVG node then this function will return null.
|
1453
1764
|
*/
|
1454
1765
|
function parent() {
|
@@ -1458,6 +1769,7 @@ var Chartist = {
|
|
1458
1769
|
/**
|
1459
1770
|
* This method returns a Chartist.Svg wrapper around the root SVG element of the current tree.
|
1460
1771
|
*
|
1772
|
+
* @memberof Chartist.Svg
|
1461
1773
|
* @return {Chartist.Svg} The root SVG element wrapped in a Chartist.Svg element
|
1462
1774
|
*/
|
1463
1775
|
function root() {
|
@@ -1471,6 +1783,7 @@ var Chartist = {
|
|
1471
1783
|
/**
|
1472
1784
|
* Find the first child SVG element of the current element that matches a CSS selector. The returned object is a Chartist.Svg wrapper.
|
1473
1785
|
*
|
1786
|
+
* @memberof Chartist.Svg
|
1474
1787
|
* @param {String} selector A CSS selector that is used to query for child SVG elements
|
1475
1788
|
* @return {Chartist.Svg} The SVG wrapper for the element found or null if no element was found
|
1476
1789
|
*/
|
@@ -1482,6 +1795,7 @@ var Chartist = {
|
|
1482
1795
|
/**
|
1483
1796
|
* Find the all child SVG elements of the current element that match a CSS selector. The returned object is a Chartist.Svg.List wrapper.
|
1484
1797
|
*
|
1798
|
+
* @memberof Chartist.Svg
|
1485
1799
|
* @param {String} selector A CSS selector that is used to query for child SVG elements
|
1486
1800
|
* @return {Chartist.Svg.List} The SVG wrapper list for the element found or null if no element was found
|
1487
1801
|
*/
|
@@ -1647,6 +1961,24 @@ var Chartist = {
|
|
1647
1961
|
return this;
|
1648
1962
|
}
|
1649
1963
|
|
1964
|
+
/**
|
1965
|
+
* "Save" way to get property value from svg BoundingBox.
|
1966
|
+
* This is a workaround. Firefox throws an NS_ERROR_FAILURE error if getBBox() is called on an invisible node.
|
1967
|
+
* See [NS_ERROR_FAILURE: Component returned failure code: 0x80004005](http://jsfiddle.net/sym3tri/kWWDK/)
|
1968
|
+
*
|
1969
|
+
* @memberof Chartist.Svg
|
1970
|
+
* @param {SVGElement} node The svg node to
|
1971
|
+
* @param {String} prop The property to fetch (ex.: height, width, ...)
|
1972
|
+
* @returns {Number} The value of the given bbox property
|
1973
|
+
*/
|
1974
|
+
function getBBoxProperty(node, prop) {
|
1975
|
+
try {
|
1976
|
+
return node.getBBox()[prop];
|
1977
|
+
} catch(e) {}
|
1978
|
+
|
1979
|
+
return 0;
|
1980
|
+
}
|
1981
|
+
|
1650
1982
|
/**
|
1651
1983
|
* Get element height with fallback to svg BoundingBox or parent container dimensions:
|
1652
1984
|
* See [bugzilla.mozilla.org](https://bugzilla.mozilla.org/show_bug.cgi?id=530985)
|
@@ -1655,7 +1987,7 @@ var Chartist = {
|
|
1655
1987
|
* @return {Number} The elements height in pixels
|
1656
1988
|
*/
|
1657
1989
|
function height() {
|
1658
|
-
return this._node.clientHeight || Math.round(this._node
|
1990
|
+
return this._node.clientHeight || Math.round(getBBoxProperty(this._node, 'height')) || this._node.parentNode.clientHeight;
|
1659
1991
|
}
|
1660
1992
|
|
1661
1993
|
/**
|
@@ -1666,7 +1998,7 @@ var Chartist = {
|
|
1666
1998
|
* @return {Number} The elements width in pixels
|
1667
1999
|
*/
|
1668
2000
|
function width() {
|
1669
|
-
return this._node.clientWidth || Math.round(this._node
|
2001
|
+
return this._node.clientWidth || Math.round(getBBoxProperty(this._node, 'width')) || this._node.parentNode.clientWidth;
|
1670
2002
|
}
|
1671
2003
|
|
1672
2004
|
/**
|
@@ -1852,7 +2184,7 @@ var Chartist = {
|
|
1852
2184
|
* @return {Boolean} True of false if the feature is supported or not
|
1853
2185
|
*/
|
1854
2186
|
Chartist.Svg.isSupported = function(feature) {
|
1855
|
-
return document.implementation.hasFeature('www.
|
2187
|
+
return document.implementation.hasFeature('http://www.w3.org/TR/SVG11/feature#' + feature, '1.1');
|
1856
2188
|
};
|
1857
2189
|
|
1858
2190
|
/**
|
@@ -1964,10 +2296,12 @@ var Chartist = {
|
|
1964
2296
|
accuracy: 3
|
1965
2297
|
};
|
1966
2298
|
|
1967
|
-
function element(command, params, pathElements, pos, relative) {
|
1968
|
-
|
2299
|
+
function element(command, params, pathElements, pos, relative, data) {
|
2300
|
+
var pathElement = Chartist.extend({
|
1969
2301
|
command: relative ? command.toLowerCase() : command.toUpperCase()
|
1970
|
-
}, params)
|
2302
|
+
}, params, data ? { data: data } : {} );
|
2303
|
+
|
2304
|
+
pathElements.splice(pos, 0, pathElement);
|
1971
2305
|
}
|
1972
2306
|
|
1973
2307
|
function forEachParam(pathElements, cb) {
|
@@ -2028,13 +2362,14 @@ var Chartist = {
|
|
2028
2362
|
* @param {Number} x The x coordinate for the move element.
|
2029
2363
|
* @param {Number} y The y coordinate for the move element.
|
2030
2364
|
* @param {Boolean} [relative] If set to true the move element will be created with relative coordinates (lowercase letter)
|
2365
|
+
* @param {*} [data] Any data that should be stored with the element object that will be accessible in pathElement
|
2031
2366
|
* @return {Chartist.Svg.Path} The current path object for easy call chaining.
|
2032
2367
|
*/
|
2033
|
-
function move(x, y, relative) {
|
2368
|
+
function move(x, y, relative, data) {
|
2034
2369
|
element('M', {
|
2035
2370
|
x: +x,
|
2036
2371
|
y: +y
|
2037
|
-
}, this.pathElements, this.pos++, relative);
|
2372
|
+
}, this.pathElements, this.pos++, relative, data);
|
2038
2373
|
return this;
|
2039
2374
|
}
|
2040
2375
|
|
@@ -2045,13 +2380,14 @@ var Chartist = {
|
|
2045
2380
|
* @param {Number} x The x coordinate for the line element.
|
2046
2381
|
* @param {Number} y The y coordinate for the line element.
|
2047
2382
|
* @param {Boolean} [relative] If set to true the line element will be created with relative coordinates (lowercase letter)
|
2383
|
+
* @param {*} [data] Any data that should be stored with the element object that will be accessible in pathElement
|
2048
2384
|
* @return {Chartist.Svg.Path} The current path object for easy call chaining.
|
2049
2385
|
*/
|
2050
|
-
function line(x, y, relative) {
|
2386
|
+
function line(x, y, relative, data) {
|
2051
2387
|
element('L', {
|
2052
2388
|
x: +x,
|
2053
2389
|
y: +y
|
2054
|
-
}, this.pathElements, this.pos++, relative);
|
2390
|
+
}, this.pathElements, this.pos++, relative, data);
|
2055
2391
|
return this;
|
2056
2392
|
}
|
2057
2393
|
|
@@ -2066,9 +2402,10 @@ var Chartist = {
|
|
2066
2402
|
* @param {Number} x The x coordinate for the target point of the curve element.
|
2067
2403
|
* @param {Number} y The y coordinate for the target point of the curve element.
|
2068
2404
|
* @param {Boolean} [relative] If set to true the curve element will be created with relative coordinates (lowercase letter)
|
2405
|
+
* @param {*} [data] Any data that should be stored with the element object that will be accessible in pathElement
|
2069
2406
|
* @return {Chartist.Svg.Path} The current path object for easy call chaining.
|
2070
2407
|
*/
|
2071
|
-
function curve(x1, y1, x2, y2, x, y, relative) {
|
2408
|
+
function curve(x1, y1, x2, y2, x, y, relative, data) {
|
2072
2409
|
element('C', {
|
2073
2410
|
x1: +x1,
|
2074
2411
|
y1: +y1,
|
@@ -2076,7 +2413,7 @@ var Chartist = {
|
|
2076
2413
|
y2: +y2,
|
2077
2414
|
x: +x,
|
2078
2415
|
y: +y
|
2079
|
-
}, this.pathElements, this.pos++, relative);
|
2416
|
+
}, this.pathElements, this.pos++, relative, data);
|
2080
2417
|
return this;
|
2081
2418
|
}
|
2082
2419
|
|
@@ -2092,9 +2429,10 @@ var Chartist = {
|
|
2092
2429
|
* @param {Number} x The x coordinate for the target point of the curve element.
|
2093
2430
|
* @param {Number} y The y coordinate for the target point of the curve element.
|
2094
2431
|
* @param {Boolean} [relative] If set to true the curve element will be created with relative coordinates (lowercase letter)
|
2432
|
+
* @param {*} [data] Any data that should be stored with the element object that will be accessible in pathElement
|
2095
2433
|
* @return {Chartist.Svg.Path} The current path object for easy call chaining.
|
2096
2434
|
*/
|
2097
|
-
function arc(rx, ry, xAr, lAf, sf, x, y, relative) {
|
2435
|
+
function arc(rx, ry, xAr, lAf, sf, x, y, relative, data) {
|
2098
2436
|
element('A', {
|
2099
2437
|
rx: +rx,
|
2100
2438
|
ry: +ry,
|
@@ -2103,7 +2441,7 @@ var Chartist = {
|
|
2103
2441
|
sf: +sf,
|
2104
2442
|
x: +x,
|
2105
2443
|
y: +y
|
2106
|
-
}, this.pathElements, this.pos++, relative);
|
2444
|
+
}, this.pathElements, this.pos++, relative, data);
|
2107
2445
|
return this;
|
2108
2446
|
}
|
2109
2447
|
|
@@ -2233,10 +2571,11 @@ var Chartist = {
|
|
2233
2571
|
* This function clones a whole path object with all its properties. This is a deep clone and path element objects will also be cloned.
|
2234
2572
|
*
|
2235
2573
|
* @memberof Chartist.Svg.Path
|
2574
|
+
* @param {Boolean} [close] Optional option to set the new cloned path to closed. If not specified or false, the original path close option will be used.
|
2236
2575
|
* @return {Chartist.Svg.Path}
|
2237
2576
|
*/
|
2238
|
-
function clone() {
|
2239
|
-
var c = new Chartist.Svg.Path(this.close);
|
2577
|
+
function clone(close) {
|
2578
|
+
var c = new Chartist.Svg.Path(close || this.close);
|
2240
2579
|
c.pos = this.pos;
|
2241
2580
|
c.pathElements = this.pathElements.slice().map(function cloneElements(pathElement) {
|
2242
2581
|
return Chartist.extend({}, pathElement);
|
@@ -2245,6 +2584,50 @@ var Chartist = {
|
|
2245
2584
|
return c;
|
2246
2585
|
}
|
2247
2586
|
|
2587
|
+
/**
|
2588
|
+
* Split a Svg.Path object by a specific command in the path chain. The path chain will be split and an array of newly created paths objects will be returned. This is useful if you'd like to split an SVG path by it's move commands, for example, in order to isolate chunks of drawings.
|
2589
|
+
*
|
2590
|
+
* @memberof Chartist.Svg.Path
|
2591
|
+
* @param {String} command The command you'd like to use to split the path
|
2592
|
+
* @return {Array<Chartist.Svg.Path>}
|
2593
|
+
*/
|
2594
|
+
function splitByCommand(command) {
|
2595
|
+
var split = [
|
2596
|
+
new Chartist.Svg.Path()
|
2597
|
+
];
|
2598
|
+
|
2599
|
+
this.pathElements.forEach(function(pathElement) {
|
2600
|
+
if(pathElement.command === command.toUpperCase() && split[split.length - 1].pathElements.length !== 0) {
|
2601
|
+
split.push(new Chartist.Svg.Path());
|
2602
|
+
}
|
2603
|
+
|
2604
|
+
split[split.length - 1].pathElements.push(pathElement);
|
2605
|
+
});
|
2606
|
+
|
2607
|
+
return split;
|
2608
|
+
}
|
2609
|
+
|
2610
|
+
/**
|
2611
|
+
* This static function on `Chartist.Svg.Path` is joining multiple paths together into one paths.
|
2612
|
+
*
|
2613
|
+
* @memberof Chartist.Svg.Path
|
2614
|
+
* @param {Array<Chartist.Svg.Path>} paths A list of paths to be joined together. The order is important.
|
2615
|
+
* @param {boolean} close If the newly created path should be a closed path
|
2616
|
+
* @param {Object} options Path options for the newly created path.
|
2617
|
+
* @return {Chartist.Svg.Path}
|
2618
|
+
*/
|
2619
|
+
|
2620
|
+
function join(paths, close, options) {
|
2621
|
+
var joinedPath = new Chartist.Svg.Path(close, options);
|
2622
|
+
for(var i = 0; i < paths.length; i++) {
|
2623
|
+
var path = paths[i];
|
2624
|
+
for(var j = 0; j < path.pathElements.length; j++) {
|
2625
|
+
joinedPath.pathElements.push(path.pathElements[j]);
|
2626
|
+
}
|
2627
|
+
}
|
2628
|
+
return joinedPath;
|
2629
|
+
}
|
2630
|
+
|
2248
2631
|
Chartist.Svg.Path = Chartist.Class.extend({
|
2249
2632
|
constructor: SvgPath,
|
2250
2633
|
position: position,
|
@@ -2258,17 +2641,14 @@ var Chartist = {
|
|
2258
2641
|
transform: transform,
|
2259
2642
|
parse: parse,
|
2260
2643
|
stringify: stringify,
|
2261
|
-
clone: clone
|
2644
|
+
clone: clone,
|
2645
|
+
splitByCommand: splitByCommand
|
2262
2646
|
});
|
2263
2647
|
|
2264
2648
|
Chartist.Svg.Path.elementDescriptions = elementDescriptions;
|
2649
|
+
Chartist.Svg.Path.join = join;
|
2265
2650
|
}(window, document, Chartist));
|
2266
|
-
|
2267
|
-
* Axis base class used to implement different axis types
|
2268
|
-
*
|
2269
|
-
* @module Chartist.Axis
|
2270
|
-
*/
|
2271
|
-
/* global Chartist */
|
2651
|
+
;/* global Chartist */
|
2272
2652
|
(function (window, document, Chartist) {
|
2273
2653
|
'use strict';
|
2274
2654
|
|
@@ -2291,19 +2671,91 @@ var Chartist = {
|
|
2291
2671
|
}
|
2292
2672
|
};
|
2293
2673
|
|
2294
|
-
function Axis(units, chartRect,
|
2674
|
+
function Axis(units, chartRect, ticks, options) {
|
2295
2675
|
this.units = units;
|
2296
2676
|
this.counterUnits = units === axisUnits.x ? axisUnits.y : axisUnits.x;
|
2297
2677
|
this.chartRect = chartRect;
|
2298
2678
|
this.axisLength = chartRect[units.rectEnd] - chartRect[units.rectStart];
|
2299
2679
|
this.gridOffset = chartRect[units.rectOffset];
|
2300
|
-
this.
|
2301
|
-
this.labelOffset = labelOffset;
|
2680
|
+
this.ticks = ticks;
|
2302
2681
|
this.options = options;
|
2303
2682
|
}
|
2304
2683
|
|
2684
|
+
function createGridAndLabels(gridGroup, labelGroup, useForeignObject, chartOptions, eventEmitter) {
|
2685
|
+
var axisOptions = chartOptions['axis' + this.units.pos.toUpperCase()];
|
2686
|
+
var projectedValues = this.ticks.map(this.projectValue.bind(this));
|
2687
|
+
var labelValues = this.ticks.map(axisOptions.labelInterpolationFnc);
|
2688
|
+
|
2689
|
+
projectedValues.forEach(function(projectedValue, index) {
|
2690
|
+
var labelOffset = {
|
2691
|
+
x: 0,
|
2692
|
+
y: 0
|
2693
|
+
};
|
2694
|
+
|
2695
|
+
// TODO: Find better solution for solving this problem
|
2696
|
+
// Calculate how much space we have available for the label
|
2697
|
+
var labelLength;
|
2698
|
+
if(projectedValues[index + 1]) {
|
2699
|
+
// If we still have one label ahead, we can calculate the distance to the next tick / label
|
2700
|
+
labelLength = projectedValues[index + 1] - projectedValue;
|
2701
|
+
} else {
|
2702
|
+
// If we don't have a label ahead and we have only two labels in total, we just take the remaining distance to
|
2703
|
+
// on the whole axis length. We limit that to a minimum of 30 pixel, so that labels close to the border will
|
2704
|
+
// still be visible inside of the chart padding.
|
2705
|
+
labelLength = Math.max(this.axisLength - projectedValue, 30);
|
2706
|
+
}
|
2707
|
+
|
2708
|
+
// Skip grid lines and labels where interpolated label values are falsey (execpt for 0)
|
2709
|
+
if(!labelValues[index] && labelValues[index] !== 0) {
|
2710
|
+
return;
|
2711
|
+
}
|
2712
|
+
|
2713
|
+
// Transform to global coordinates using the chartRect
|
2714
|
+
// We also need to set the label offset for the createLabel function
|
2715
|
+
if(this.units.pos === 'x') {
|
2716
|
+
projectedValue = this.chartRect.x1 + projectedValue;
|
2717
|
+
labelOffset.x = chartOptions.axisX.labelOffset.x;
|
2718
|
+
|
2719
|
+
// If the labels should be positioned in start position (top side for vertical axis) we need to set a
|
2720
|
+
// different offset as for positioned with end (bottom)
|
2721
|
+
if(chartOptions.axisX.position === 'start') {
|
2722
|
+
labelOffset.y = this.chartRect.padding.top + chartOptions.axisX.labelOffset.y + (useForeignObject ? 5 : 20);
|
2723
|
+
} else {
|
2724
|
+
labelOffset.y = this.chartRect.y1 + chartOptions.axisX.labelOffset.y + (useForeignObject ? 5 : 20);
|
2725
|
+
}
|
2726
|
+
} else {
|
2727
|
+
projectedValue = this.chartRect.y1 - projectedValue;
|
2728
|
+
labelOffset.y = chartOptions.axisY.labelOffset.y - (useForeignObject ? labelLength : 0);
|
2729
|
+
|
2730
|
+
// If the labels should be positioned in start position (left side for horizontal axis) we need to set a
|
2731
|
+
// different offset as for positioned with end (right side)
|
2732
|
+
if(chartOptions.axisY.position === 'start') {
|
2733
|
+
labelOffset.x = useForeignObject ? this.chartRect.padding.left + chartOptions.axisY.labelOffset.x : this.chartRect.x1 - 10;
|
2734
|
+
} else {
|
2735
|
+
labelOffset.x = this.chartRect.x2 + chartOptions.axisY.labelOffset.x + 10;
|
2736
|
+
}
|
2737
|
+
}
|
2738
|
+
|
2739
|
+
if(axisOptions.showGrid) {
|
2740
|
+
Chartist.createGrid(projectedValue, index, this, this.gridOffset, this.chartRect[this.counterUnits.len](), gridGroup, [
|
2741
|
+
chartOptions.classNames.grid,
|
2742
|
+
chartOptions.classNames[this.units.dir]
|
2743
|
+
], eventEmitter);
|
2744
|
+
}
|
2745
|
+
|
2746
|
+
if(axisOptions.showLabel) {
|
2747
|
+
Chartist.createLabel(projectedValue, labelLength, index, labelValues, this, axisOptions.offset, labelOffset, labelGroup, [
|
2748
|
+
chartOptions.classNames.label,
|
2749
|
+
chartOptions.classNames[this.units.dir],
|
2750
|
+
chartOptions.classNames[axisOptions.position]
|
2751
|
+
], useForeignObject, eventEmitter);
|
2752
|
+
}
|
2753
|
+
}.bind(this));
|
2754
|
+
}
|
2755
|
+
|
2305
2756
|
Chartist.Axis = Chartist.Class.extend({
|
2306
2757
|
constructor: Axis,
|
2758
|
+
createGridAndLabels: createGridAndLabels,
|
2307
2759
|
projectValue: function(value, index, data) {
|
2308
2760
|
throw new Error('Base axis can\'t be instantiated!');
|
2309
2761
|
}
|
@@ -2313,40 +2765,121 @@ var Chartist = {
|
|
2313
2765
|
|
2314
2766
|
}(window, document, Chartist));
|
2315
2767
|
;/**
|
2316
|
-
* The
|
2768
|
+
* The auto scale axis uses standard linear scale projection of values along an axis. It uses order of magnitude to find a scale automatically and evaluates the available space in order to find the perfect amount of ticks for your chart.
|
2769
|
+
* **Options**
|
2770
|
+
* The following options are used by this axis in addition to the default axis options outlined in the axis configuration of the chart default settings.
|
2771
|
+
* ```javascript
|
2772
|
+
* var options = {
|
2773
|
+
* // If high is specified then the axis will display values explicitly up to this value and the computed maximum from the data is ignored
|
2774
|
+
* high: 100,
|
2775
|
+
* // If low is specified then the axis will display values explicitly down to this value and the computed minimum from the data is ignored
|
2776
|
+
* low: 0,
|
2777
|
+
* // This option will be used when finding the right scale division settings. The amount of ticks on the scale will be determined so that as many ticks as possible will be displayed, while not violating this minimum required space (in pixel).
|
2778
|
+
* scaleMinSpace: 20,
|
2779
|
+
* // Can be set to true or false. If set to true, the scale will be generated with whole numbers only.
|
2780
|
+
* onlyInteger: true,
|
2781
|
+
* // The reference value can be used to make sure that this value will always be on the chart. This is especially useful on bipolar charts where the bipolar center always needs to be part of the chart.
|
2782
|
+
* referenceValue: 5
|
2783
|
+
* };
|
2784
|
+
* ```
|
2317
2785
|
*
|
2318
|
-
* @module Chartist.
|
2786
|
+
* @module Chartist.AutoScaleAxis
|
2319
2787
|
*/
|
2320
2788
|
/* global Chartist */
|
2321
2789
|
(function (window, document, Chartist) {
|
2322
2790
|
'use strict';
|
2323
2791
|
|
2324
|
-
function
|
2325
|
-
|
2792
|
+
function AutoScaleAxis(axisUnit, data, chartRect, options) {
|
2793
|
+
// Usually we calculate highLow based on the data but this can be overriden by a highLow object in the options
|
2794
|
+
var highLow = options.highLow || Chartist.getHighLow(data.normalized, options, axisUnit.pos);
|
2795
|
+
this.bounds = Chartist.getBounds(chartRect[axisUnit.rectEnd] - chartRect[axisUnit.rectStart], highLow, options.scaleMinSpace || 20, options.onlyInteger);
|
2796
|
+
this.range = {
|
2797
|
+
min: this.bounds.min,
|
2798
|
+
max: this.bounds.max
|
2799
|
+
};
|
2800
|
+
|
2801
|
+
Chartist.AutoScaleAxis.super.constructor.call(this,
|
2326
2802
|
axisUnit,
|
2327
2803
|
chartRect,
|
2328
|
-
|
2329
|
-
labelOffset,
|
2804
|
+
this.bounds.values,
|
2330
2805
|
options);
|
2331
|
-
|
2332
|
-
this.bounds = Chartist.getBounds(this.axisLength, options.highLow, options.scaleMinSpace, options.referenceValue);
|
2333
2806
|
}
|
2334
2807
|
|
2335
2808
|
function projectValue(value) {
|
2336
|
-
return
|
2337
|
-
|
2338
|
-
|
2809
|
+
return this.axisLength * (+Chartist.getMultiValue(value, this.units.pos) - this.bounds.min) / this.bounds.range;
|
2810
|
+
}
|
2811
|
+
|
2812
|
+
Chartist.AutoScaleAxis = Chartist.Axis.extend({
|
2813
|
+
constructor: AutoScaleAxis,
|
2814
|
+
projectValue: projectValue
|
2815
|
+
});
|
2816
|
+
|
2817
|
+
}(window, document, Chartist));
|
2818
|
+
;/**
|
2819
|
+
* The fixed scale axis uses standard linear projection of values along an axis. It makes use of a divisor option to divide the range provided from the minimum and maximum value or the options high and low that will override the computed minimum and maximum.
|
2820
|
+
* **Options**
|
2821
|
+
* The following options are used by this axis in addition to the default axis options outlined in the axis configuration of the chart default settings.
|
2822
|
+
* ```javascript
|
2823
|
+
* var options = {
|
2824
|
+
* // If high is specified then the axis will display values explicitly up to this value and the computed maximum from the data is ignored
|
2825
|
+
* high: 100,
|
2826
|
+
* // If low is specified then the axis will display values explicitly down to this value and the computed minimum from the data is ignored
|
2827
|
+
* low: 0,
|
2828
|
+
* // If specified then the value range determined from minimum to maximum (or low and high) will be divided by this number and ticks will be generated at those division points. The default divisor is 1.
|
2829
|
+
* divisor: 4,
|
2830
|
+
* // If ticks is explicitly set, then the axis will not compute the ticks with the divisor, but directly use the data in ticks to determine at what points on the axis a tick need to be generated.
|
2831
|
+
* ticks: [1, 10, 20, 30]
|
2832
|
+
* };
|
2833
|
+
* ```
|
2834
|
+
*
|
2835
|
+
* @module Chartist.FixedScaleAxis
|
2836
|
+
*/
|
2837
|
+
/* global Chartist */
|
2838
|
+
(function (window, document, Chartist) {
|
2839
|
+
'use strict';
|
2840
|
+
|
2841
|
+
function FixedScaleAxis(axisUnit, data, chartRect, options) {
|
2842
|
+
var highLow = options.highLow || Chartist.getHighLow(data.normalized, options, axisUnit.pos);
|
2843
|
+
this.divisor = options.divisor || 1;
|
2844
|
+
this.ticks = options.ticks || Chartist.times(this.divisor).map(function(value, index) {
|
2845
|
+
return highLow.low + (highLow.high - highLow.low) / this.divisor * index;
|
2846
|
+
}.bind(this));
|
2847
|
+
this.range = {
|
2848
|
+
min: highLow.low,
|
2849
|
+
max: highLow.high
|
2339
2850
|
};
|
2851
|
+
|
2852
|
+
Chartist.FixedScaleAxis.super.constructor.call(this,
|
2853
|
+
axisUnit,
|
2854
|
+
chartRect,
|
2855
|
+
this.ticks,
|
2856
|
+
options);
|
2857
|
+
|
2858
|
+
this.stepLength = this.axisLength / this.divisor;
|
2340
2859
|
}
|
2341
2860
|
|
2342
|
-
|
2343
|
-
|
2861
|
+
function projectValue(value) {
|
2862
|
+
return this.axisLength * (+Chartist.getMultiValue(value, this.units.pos) - this.range.min) / (this.range.max - this.range.min);
|
2863
|
+
}
|
2864
|
+
|
2865
|
+
Chartist.FixedScaleAxis = Chartist.Axis.extend({
|
2866
|
+
constructor: FixedScaleAxis,
|
2344
2867
|
projectValue: projectValue
|
2345
2868
|
});
|
2346
2869
|
|
2347
2870
|
}(window, document, Chartist));
|
2348
2871
|
;/**
|
2349
|
-
*
|
2872
|
+
* The step axis for step based charts like bar chart or step based line charts. It uses a fixed amount of ticks that will be equally distributed across the whole axis length. The projection is done using the index of the data value rather than the value itself and therefore it's only useful for distribution purpose.
|
2873
|
+
* **Options**
|
2874
|
+
* The following options are used by this axis in addition to the default axis options outlined in the axis configuration of the chart default settings.
|
2875
|
+
* ```javascript
|
2876
|
+
* var options = {
|
2877
|
+
* // Ticks to be used to distribute across the axis length. As this axis type relies on the index of the value rather than the value, arbitrary data that can be converted to a string can be used as ticks.
|
2878
|
+
* ticks: ['One', 'Two', 'Three'],
|
2879
|
+
* // If set to true the full width will be used to distribute the values where the last value will be at the maximum of the axis length. If false the spaces between the ticks will be evenly distributed instead.
|
2880
|
+
* stretch: true
|
2881
|
+
* };
|
2882
|
+
* ```
|
2350
2883
|
*
|
2351
2884
|
* @module Chartist.StepAxis
|
2352
2885
|
*/
|
@@ -2354,22 +2887,18 @@ var Chartist = {
|
|
2354
2887
|
(function (window, document, Chartist) {
|
2355
2888
|
'use strict';
|
2356
2889
|
|
2357
|
-
function StepAxis(axisUnit,
|
2890
|
+
function StepAxis(axisUnit, data, chartRect, options) {
|
2358
2891
|
Chartist.StepAxis.super.constructor.call(this,
|
2359
2892
|
axisUnit,
|
2360
2893
|
chartRect,
|
2361
|
-
|
2362
|
-
labelOffset,
|
2894
|
+
options.ticks,
|
2363
2895
|
options);
|
2364
2896
|
|
2365
|
-
this.stepLength = this.axisLength / (options.
|
2897
|
+
this.stepLength = this.axisLength / (options.ticks.length - (options.stretch ? 1 : 0));
|
2366
2898
|
}
|
2367
2899
|
|
2368
2900
|
function projectValue(value, index) {
|
2369
|
-
return
|
2370
|
-
pos: this.stepLength * index,
|
2371
|
-
len: this.stepLength
|
2372
|
-
};
|
2901
|
+
return this.stepLength * index;
|
2373
2902
|
}
|
2374
2903
|
|
2375
2904
|
Chartist.StepAxis = Chartist.Axis.extend({
|
@@ -2399,6 +2928,8 @@ var Chartist = {
|
|
2399
2928
|
axisX: {
|
2400
2929
|
// The offset of the labels to the chart area
|
2401
2930
|
offset: 30,
|
2931
|
+
// Position where labels are placed. Can be set to `start` or `end` where `start` is equivalent to left or top on vertical axis and `end` is equivalent to right or bottom on horizontal axis.
|
2932
|
+
position: 'end',
|
2402
2933
|
// Allows you to correct label positioning on this axis by positive or negative x and y offset.
|
2403
2934
|
labelOffset: {
|
2404
2935
|
x: 0,
|
@@ -2409,12 +2940,16 @@ var Chartist = {
|
|
2409
2940
|
// If the axis grid should be drawn or not
|
2410
2941
|
showGrid: true,
|
2411
2942
|
// Interpolation function that allows you to intercept the value from the axis label
|
2412
|
-
labelInterpolationFnc: Chartist.noop
|
2943
|
+
labelInterpolationFnc: Chartist.noop,
|
2944
|
+
// Set the axis type to be used to project values on this axis. If not defined, Chartist.StepAxis will be used for the X-Axis, where the ticks option will be set to the labels in the data and the stretch option will be set to the global fullWidth option. This type can be changed to any axis constructor available (e.g. Chartist.FixedScaleAxis), where all axis options should be present here.
|
2945
|
+
type: undefined
|
2413
2946
|
},
|
2414
2947
|
// Options for Y-Axis
|
2415
2948
|
axisY: {
|
2416
2949
|
// The offset of the labels to the chart area
|
2417
2950
|
offset: 40,
|
2951
|
+
// Position where labels are placed. Can be set to `start` or `end` where `start` is equivalent to left or top on vertical axis and `end` is equivalent to right or bottom on horizontal axis.
|
2952
|
+
position: 'start',
|
2418
2953
|
// Allows you to correct label positioning on this axis by positive or negative x and y offset.
|
2419
2954
|
labelOffset: {
|
2420
2955
|
x: 0,
|
@@ -2426,8 +2961,12 @@ var Chartist = {
|
|
2426
2961
|
showGrid: true,
|
2427
2962
|
// Interpolation function that allows you to intercept the value from the axis label
|
2428
2963
|
labelInterpolationFnc: Chartist.noop,
|
2964
|
+
// Set the axis type to be used to project values on this axis. If not defined, Chartist.AutoScaleAxis will be used for the Y-Axis, where the high and low options will be set to the global high and low options. This type can be changed to any axis constructor available (e.g. Chartist.FixedScaleAxis), where all axis options should be present here.
|
2965
|
+
type: undefined,
|
2429
2966
|
// This value specifies the minimum height in pixel of the scale steps
|
2430
|
-
scaleMinSpace: 20
|
2967
|
+
scaleMinSpace: 20,
|
2968
|
+
// Use only integer values (whole numbers) for the scale steps
|
2969
|
+
onlyInteger: false
|
2431
2970
|
},
|
2432
2971
|
// Specify a fixed width for the chart as a string (i.e. '100px' or '50%')
|
2433
2972
|
width: undefined,
|
@@ -2448,7 +2987,12 @@ var Chartist = {
|
|
2448
2987
|
// Overriding the natural high of the chart allows you to zoom in or limit the charts highest displayed value
|
2449
2988
|
high: undefined,
|
2450
2989
|
// Padding of the chart drawing area to the container element and labels as a number or padding object {top: 5, right: 5, bottom: 5, left: 5}
|
2451
|
-
chartPadding:
|
2990
|
+
chartPadding: {
|
2991
|
+
top: 15,
|
2992
|
+
right: 15,
|
2993
|
+
bottom: 5,
|
2994
|
+
left: 10
|
2995
|
+
},
|
2452
2996
|
// When set to true, the last grid line on the x-axis is not drawn and the chart elements will expand to the full available width of the chart. For the last label to be drawn correctly you might need to add chart padding or offset the last label with a draw event handler.
|
2453
2997
|
fullWidth: false,
|
2454
2998
|
// If true the whole data is reversed including labels, the series order as well as the whole series data arrays.
|
@@ -2465,7 +3009,9 @@ var Chartist = {
|
|
2465
3009
|
grid: 'ct-grid',
|
2466
3010
|
gridGroup: 'ct-grids',
|
2467
3011
|
vertical: 'ct-vertical',
|
2468
|
-
horizontal: 'ct-horizontal'
|
3012
|
+
horizontal: 'ct-horizontal',
|
3013
|
+
start: 'ct-start',
|
3014
|
+
end: 'ct-end'
|
2469
3015
|
}
|
2470
3016
|
};
|
2471
3017
|
|
@@ -2474,189 +3020,197 @@ var Chartist = {
|
|
2474
3020
|
*
|
2475
3021
|
*/
|
2476
3022
|
function createChart(options) {
|
2477
|
-
var
|
2478
|
-
|
2479
|
-
|
3023
|
+
var data = {
|
3024
|
+
raw: this.data,
|
3025
|
+
normalized: Chartist.getDataArray(this.data, options.reverseData, true)
|
3026
|
+
};
|
2480
3027
|
|
2481
3028
|
// Create new svg object
|
2482
3029
|
this.svg = Chartist.createSvg(this.container, options.width, options.height, options.classNames.chart);
|
3030
|
+
// Create groups for labels, grid and series
|
3031
|
+
var gridGroup = this.svg.elem('g').addClass(options.classNames.gridGroup);
|
3032
|
+
var seriesGroup = this.svg.elem('g');
|
3033
|
+
var labelGroup = this.svg.elem('g').addClass(options.classNames.labelGroup);
|
2483
3034
|
|
2484
3035
|
var chartRect = Chartist.createChartRect(this.svg, options, defaultOptions.padding);
|
3036
|
+
var axisX, axisY;
|
2485
3037
|
|
2486
|
-
|
2487
|
-
|
2488
|
-
|
2489
|
-
highLow.low = +options.low || (options.low === 0 ? 0 : highLow.low);
|
2490
|
-
|
2491
|
-
var axisX = new Chartist.StepAxis(
|
2492
|
-
Chartist.Axis.units.x,
|
2493
|
-
chartRect,
|
2494
|
-
function xAxisTransform(projectedValue) {
|
2495
|
-
projectedValue.pos = chartRect.x1 + projectedValue.pos;
|
2496
|
-
return projectedValue;
|
2497
|
-
},
|
2498
|
-
{
|
2499
|
-
x: options.axisX.labelOffset.x,
|
2500
|
-
y: chartRect.y1 + options.axisX.labelOffset.y + (this.supportsForeignObject ? 5 : 20)
|
2501
|
-
},
|
2502
|
-
{
|
2503
|
-
stepCount: this.data.labels.length,
|
3038
|
+
if(options.axisX.type === undefined) {
|
3039
|
+
axisX = new Chartist.StepAxis(Chartist.Axis.units.x, data, chartRect, Chartist.extend({}, options.axisX, {
|
3040
|
+
ticks: data.raw.labels,
|
2504
3041
|
stretch: options.fullWidth
|
2505
|
-
}
|
2506
|
-
|
2507
|
-
|
2508
|
-
|
2509
|
-
Chartist.Axis.units.y,
|
2510
|
-
chartRect,
|
2511
|
-
function yAxisTransform(projectedValue) {
|
2512
|
-
projectedValue.pos = chartRect.y1 - projectedValue.pos;
|
2513
|
-
return projectedValue;
|
2514
|
-
},
|
2515
|
-
{
|
2516
|
-
x: normalizedPadding.left + options.axisY.labelOffset.x + (this.supportsForeignObject ? -10 : 0),
|
2517
|
-
y: options.axisY.labelOffset.y + (this.supportsForeignObject ? -15 : 0)
|
2518
|
-
},
|
2519
|
-
{
|
2520
|
-
highLow: highLow,
|
2521
|
-
scaleMinSpace: options.axisY.scaleMinSpace
|
2522
|
-
}
|
2523
|
-
);
|
2524
|
-
|
2525
|
-
// Start drawing
|
2526
|
-
var labelGroup = this.svg.elem('g').addClass(options.classNames.labelGroup),
|
2527
|
-
gridGroup = this.svg.elem('g').addClass(options.classNames.gridGroup);
|
3042
|
+
}));
|
3043
|
+
} else {
|
3044
|
+
axisX = options.axisX.type.call(Chartist, Chartist.Axis.units.x, data, chartRect, options.axisX);
|
3045
|
+
}
|
2528
3046
|
|
2529
|
-
|
2530
|
-
|
2531
|
-
|
2532
|
-
|
2533
|
-
|
2534
|
-
|
2535
|
-
|
2536
|
-
|
2537
|
-
this.eventEmitter
|
2538
|
-
);
|
3047
|
+
if(options.axisY.type === undefined) {
|
3048
|
+
axisY = new Chartist.AutoScaleAxis(Chartist.Axis.units.y, data, chartRect, Chartist.extend({}, options.axisY, {
|
3049
|
+
high: Chartist.isNum(options.high) ? options.high : options.axisY.high,
|
3050
|
+
low: Chartist.isNum(options.low) ? options.low : options.axisY.low
|
3051
|
+
}));
|
3052
|
+
} else {
|
3053
|
+
axisY = options.axisY.type.call(Chartist, Chartist.Axis.units.y, data, chartRect, options.axisY);
|
3054
|
+
}
|
2539
3055
|
|
2540
|
-
|
2541
|
-
|
2542
|
-
axisY.bounds.values,
|
2543
|
-
chartRect,
|
2544
|
-
gridGroup,
|
2545
|
-
labelGroup,
|
2546
|
-
this.supportsForeignObject,
|
2547
|
-
options,
|
2548
|
-
this.eventEmitter
|
2549
|
-
);
|
3056
|
+
axisX.createGridAndLabels(gridGroup, labelGroup, this.supportsForeignObject, options, this.eventEmitter);
|
3057
|
+
axisY.createGridAndLabels(gridGroup, labelGroup, this.supportsForeignObject, options, this.eventEmitter);
|
2550
3058
|
|
2551
3059
|
// Draw the series
|
2552
|
-
|
2553
|
-
|
3060
|
+
data.raw.series.forEach(function(series, seriesIndex) {
|
3061
|
+
var seriesElement = seriesGroup.elem('g');
|
2554
3062
|
|
2555
3063
|
// Write attributes to series group element. If series name or meta is undefined the attributes will not be written
|
2556
|
-
|
3064
|
+
seriesElement.attr({
|
2557
3065
|
'series-name': series.name,
|
2558
3066
|
'meta': Chartist.serialize(series.meta)
|
2559
3067
|
}, Chartist.xmlNs.uri);
|
2560
3068
|
|
2561
3069
|
// Use series class from series data or if not set generate one
|
2562
|
-
|
3070
|
+
seriesElement.addClass([
|
2563
3071
|
options.classNames.series,
|
2564
3072
|
(series.className || options.classNames.series + '-' + Chartist.alphaNumerate(seriesIndex))
|
2565
3073
|
].join(' '));
|
2566
3074
|
|
2567
|
-
var pathCoordinates = []
|
3075
|
+
var pathCoordinates = [],
|
3076
|
+
pathData = [];
|
2568
3077
|
|
2569
|
-
|
3078
|
+
data.normalized[seriesIndex].forEach(function(value, valueIndex) {
|
2570
3079
|
var p = {
|
2571
|
-
x: chartRect.x1 + axisX.projectValue(value, valueIndex,
|
2572
|
-
y: chartRect.y1 - axisY.projectValue(value, valueIndex,
|
3080
|
+
x: chartRect.x1 + axisX.projectValue(value, valueIndex, data.normalized[seriesIndex]),
|
3081
|
+
y: chartRect.y1 - axisY.projectValue(value, valueIndex, data.normalized[seriesIndex])
|
2573
3082
|
};
|
2574
3083
|
pathCoordinates.push(p.x, p.y);
|
3084
|
+
pathData.push({
|
3085
|
+
value: value,
|
3086
|
+
valueIndex: valueIndex,
|
3087
|
+
meta: Chartist.getMetaData(series, valueIndex)
|
3088
|
+
});
|
3089
|
+
}.bind(this));
|
3090
|
+
|
3091
|
+
var seriesOptions = {
|
3092
|
+
lineSmooth: Chartist.getSeriesOption(series, options, 'lineSmooth'),
|
3093
|
+
showPoint: Chartist.getSeriesOption(series, options, 'showPoint'),
|
3094
|
+
showLine: Chartist.getSeriesOption(series, options, 'showLine'),
|
3095
|
+
showArea: Chartist.getSeriesOption(series, options, 'showArea'),
|
3096
|
+
areaBase: Chartist.getSeriesOption(series, options, 'areaBase')
|
3097
|
+
};
|
2575
3098
|
|
2576
|
-
|
2577
|
-
|
2578
|
-
|
2579
|
-
|
2580
|
-
|
2581
|
-
|
2582
|
-
|
2583
|
-
|
3099
|
+
var smoothing = typeof seriesOptions.lineSmooth === 'function' ?
|
3100
|
+
seriesOptions.lineSmooth : (seriesOptions.lineSmooth ? Chartist.Interpolation.cardinal() : Chartist.Interpolation.none());
|
3101
|
+
// Interpolating path where pathData will be used to annotate each path element so we can trace back the original
|
3102
|
+
// index, value and meta data
|
3103
|
+
var path = smoothing(pathCoordinates, pathData);
|
3104
|
+
|
3105
|
+
// If we should show points we need to create them now to avoid secondary loop
|
3106
|
+
// Points are drawn from the pathElements returned by the interpolation function
|
3107
|
+
// Small offset for Firefox to render squares correctly
|
3108
|
+
if (seriesOptions.showPoint) {
|
3109
|
+
|
3110
|
+
path.pathElements.forEach(function(pathElement) {
|
3111
|
+
var point = seriesElement.elem('line', {
|
3112
|
+
x1: pathElement.x,
|
3113
|
+
y1: pathElement.y,
|
3114
|
+
x2: pathElement.x + 0.01,
|
3115
|
+
y2: pathElement.y
|
2584
3116
|
}, options.classNames.point).attr({
|
2585
|
-
'value': value,
|
2586
|
-
|
3117
|
+
'value': [pathElement.data.value.x, pathElement.data.value.y].filter(function(v) {
|
3118
|
+
return v;
|
3119
|
+
}).join(','),
|
3120
|
+
'meta': pathElement.data.meta
|
2587
3121
|
}, Chartist.xmlNs.uri);
|
2588
3122
|
|
2589
3123
|
this.eventEmitter.emit('draw', {
|
2590
3124
|
type: 'point',
|
2591
|
-
value: value,
|
2592
|
-
index: valueIndex,
|
2593
|
-
|
3125
|
+
value: pathElement.data.value,
|
3126
|
+
index: pathElement.data.valueIndex,
|
3127
|
+
meta: pathElement.data.meta,
|
3128
|
+
series: series,
|
3129
|
+
seriesIndex: seriesIndex,
|
3130
|
+
axisX: axisX,
|
3131
|
+
axisY: axisY,
|
3132
|
+
group: seriesElement,
|
2594
3133
|
element: point,
|
2595
|
-
x:
|
2596
|
-
y:
|
3134
|
+
x: pathElement.x,
|
3135
|
+
y: pathElement.y
|
2597
3136
|
});
|
2598
|
-
}
|
2599
|
-
}
|
2600
|
-
|
2601
|
-
// TODO: Nicer handling of conditions, maybe composition?
|
2602
|
-
if (options.showLine || options.showArea) {
|
2603
|
-
var smoothing = typeof options.lineSmooth === 'function' ?
|
2604
|
-
options.lineSmooth : (options.lineSmooth ? Chartist.Interpolation.cardinal() : Chartist.Interpolation.none()),
|
2605
|
-
path = smoothing(pathCoordinates);
|
2606
|
-
|
2607
|
-
if(options.showLine) {
|
2608
|
-
var line = seriesGroups[seriesIndex].elem('path', {
|
2609
|
-
d: path.stringify()
|
2610
|
-
}, options.classNames.line, true).attr({
|
2611
|
-
'values': normalizedData[seriesIndex]
|
2612
|
-
}, Chartist.xmlNs.uri);
|
2613
|
-
|
2614
|
-
this.eventEmitter.emit('draw', {
|
2615
|
-
type: 'line',
|
2616
|
-
values: normalizedData[seriesIndex],
|
2617
|
-
path: path.clone(),
|
2618
|
-
chartRect: chartRect,
|
2619
|
-
index: seriesIndex,
|
2620
|
-
group: seriesGroups[seriesIndex],
|
2621
|
-
element: line
|
2622
|
-
});
|
2623
|
-
}
|
3137
|
+
}.bind(this));
|
3138
|
+
}
|
2624
3139
|
|
2625
|
-
|
2626
|
-
|
2627
|
-
|
2628
|
-
|
3140
|
+
if(seriesOptions.showLine) {
|
3141
|
+
var line = seriesElement.elem('path', {
|
3142
|
+
d: path.stringify()
|
3143
|
+
}, options.classNames.line, true);
|
2629
3144
|
|
2630
|
-
|
2631
|
-
|
3145
|
+
this.eventEmitter.emit('draw', {
|
3146
|
+
type: 'line',
|
3147
|
+
values: data.normalized[seriesIndex],
|
3148
|
+
path: path.clone(),
|
3149
|
+
chartRect: chartRect,
|
3150
|
+
index: seriesIndex,
|
3151
|
+
series: series,
|
3152
|
+
seriesIndex: seriesIndex,
|
3153
|
+
axisX: axisX,
|
3154
|
+
axisY: axisY,
|
3155
|
+
group: seriesElement,
|
3156
|
+
element: line
|
3157
|
+
});
|
3158
|
+
}
|
2632
3159
|
|
2633
|
-
|
2634
|
-
|
2635
|
-
|
2636
|
-
|
3160
|
+
// Area currently only works with axes that support a range!
|
3161
|
+
if(seriesOptions.showArea && axisY.range) {
|
3162
|
+
// If areaBase is outside the chart area (< min or > max) we need to set it respectively so that
|
3163
|
+
// the area is not drawn outside the chart area.
|
3164
|
+
var areaBase = Math.max(Math.min(seriesOptions.areaBase, axisY.range.max), axisY.range.min);
|
3165
|
+
|
3166
|
+
// We project the areaBase value into screen coordinates
|
3167
|
+
var areaBaseProjected = chartRect.y1 - axisY.projectValue(areaBase);
|
3168
|
+
|
3169
|
+
// In order to form the area we'll first split the path by move commands so we can chunk it up into segments
|
3170
|
+
path.splitByCommand('M').filter(function onlySolidSegments(pathSegment) {
|
3171
|
+
// We filter only "solid" segments that contain more than one point. Otherwise there's no need for an area
|
3172
|
+
return pathSegment.pathElements.length > 1;
|
3173
|
+
}).map(function convertToArea(solidPathSegments) {
|
3174
|
+
// Receiving the filtered solid path segments we can now convert those segments into fill areas
|
3175
|
+
var firstElement = solidPathSegments.pathElements[0];
|
3176
|
+
var lastElement = solidPathSegments.pathElements[solidPathSegments.pathElements.length - 1];
|
3177
|
+
|
3178
|
+
// Cloning the solid path segment with closing option and removing the first move command from the clone
|
3179
|
+
// We then insert a new move that should start at the area base and draw a straight line up or down
|
3180
|
+
// at the end of the path we add an additional straight line to the projected area base value
|
3181
|
+
// As the closing option is set our path will be automatically closed
|
3182
|
+
return solidPathSegments.clone(true)
|
3183
|
+
.position(0)
|
2637
3184
|
.remove(1)
|
2638
|
-
.move(
|
2639
|
-
.line(
|
2640
|
-
.position(
|
2641
|
-
.line(
|
2642
|
-
|
2643
|
-
|
2644
|
-
|
3185
|
+
.move(firstElement.x, areaBaseProjected)
|
3186
|
+
.line(firstElement.x, firstElement.y)
|
3187
|
+
.position(solidPathSegments.pathElements.length + 1)
|
3188
|
+
.line(lastElement.x, areaBaseProjected);
|
3189
|
+
|
3190
|
+
}).forEach(function createArea(areaPath) {
|
3191
|
+
// For each of our newly created area paths, we'll now create path elements by stringifying our path objects
|
3192
|
+
// and adding the created DOM elements to the correct series group
|
3193
|
+
var area = seriesElement.elem('path', {
|
2645
3194
|
d: areaPath.stringify()
|
2646
3195
|
}, options.classNames.area, true).attr({
|
2647
|
-
'values':
|
3196
|
+
'values': data.normalized[seriesIndex]
|
2648
3197
|
}, Chartist.xmlNs.uri);
|
2649
3198
|
|
3199
|
+
// Emit an event for each area that was drawn
|
2650
3200
|
this.eventEmitter.emit('draw', {
|
2651
3201
|
type: 'area',
|
2652
|
-
values:
|
3202
|
+
values: data.normalized[seriesIndex],
|
2653
3203
|
path: areaPath.clone(),
|
3204
|
+
series: series,
|
3205
|
+
seriesIndex: seriesIndex,
|
3206
|
+
axisX: axisX,
|
3207
|
+
axisY: axisY,
|
2654
3208
|
chartRect: chartRect,
|
2655
3209
|
index: seriesIndex,
|
2656
|
-
group:
|
3210
|
+
group: seriesElement,
|
2657
3211
|
element: area
|
2658
3212
|
});
|
2659
|
-
}
|
3213
|
+
}.bind(this));
|
2660
3214
|
}
|
2661
3215
|
}.bind(this));
|
2662
3216
|
|
@@ -2786,6 +3340,8 @@ var Chartist = {
|
|
2786
3340
|
axisX: {
|
2787
3341
|
// The offset of the chart drawing area to the border of the container
|
2788
3342
|
offset: 30,
|
3343
|
+
// Position where labels are placed. Can be set to `start` or `end` where `start` is equivalent to left or top on vertical axis and `end` is equivalent to right or bottom on horizontal axis.
|
3344
|
+
position: 'end',
|
2789
3345
|
// Allows you to correct label positioning on this axis by positive or negative x and y offset.
|
2790
3346
|
labelOffset: {
|
2791
3347
|
x: 0,
|
@@ -2798,12 +3354,16 @@ var Chartist = {
|
|
2798
3354
|
// Interpolation function that allows you to intercept the value from the axis label
|
2799
3355
|
labelInterpolationFnc: Chartist.noop,
|
2800
3356
|
// This value specifies the minimum width in pixel of the scale steps
|
2801
|
-
scaleMinSpace:
|
3357
|
+
scaleMinSpace: 30,
|
3358
|
+
// Use only integer values (whole numbers) for the scale steps
|
3359
|
+
onlyInteger: false
|
2802
3360
|
},
|
2803
3361
|
// Options for Y-Axis
|
2804
3362
|
axisY: {
|
2805
3363
|
// The offset of the chart drawing area to the border of the container
|
2806
3364
|
offset: 40,
|
3365
|
+
// Position where labels are placed. Can be set to `start` or `end` where `start` is equivalent to left or top on vertical axis and `end` is equivalent to right or bottom on horizontal axis.
|
3366
|
+
position: 'start',
|
2807
3367
|
// Allows you to correct label positioning on this axis by positive or negative x and y offset.
|
2808
3368
|
labelOffset: {
|
2809
3369
|
x: 0,
|
@@ -2816,7 +3376,9 @@ var Chartist = {
|
|
2816
3376
|
// Interpolation function that allows you to intercept the value from the axis label
|
2817
3377
|
labelInterpolationFnc: Chartist.noop,
|
2818
3378
|
// This value specifies the minimum height in pixel of the scale steps
|
2819
|
-
scaleMinSpace: 20
|
3379
|
+
scaleMinSpace: 20,
|
3380
|
+
// Use only integer values (whole numbers) for the scale steps
|
3381
|
+
onlyInteger: false
|
2820
3382
|
},
|
2821
3383
|
// Specify a fixed width for the chart as a string (i.e. '100px' or '50%')
|
2822
3384
|
width: undefined,
|
@@ -2826,19 +3388,29 @@ var Chartist = {
|
|
2826
3388
|
high: undefined,
|
2827
3389
|
// Overriding the natural low of the chart allows you to zoom in or limit the charts lowest displayed value
|
2828
3390
|
low: undefined,
|
3391
|
+
// Use only integer values (whole numbers) for the scale steps
|
3392
|
+
onlyInteger: false,
|
2829
3393
|
// Padding of the chart drawing area to the container element and labels as a number or padding object {top: 5, right: 5, bottom: 5, left: 5}
|
2830
|
-
chartPadding:
|
3394
|
+
chartPadding: {
|
3395
|
+
top: 15,
|
3396
|
+
right: 15,
|
3397
|
+
bottom: 5,
|
3398
|
+
left: 10
|
3399
|
+
},
|
2831
3400
|
// Specify the distance in pixel of bars in a group
|
2832
3401
|
seriesBarDistance: 15,
|
2833
3402
|
// If set to true this property will cause the series bars to be stacked and form a total for each series point. This will also influence the y-axis and the overall bounds of the chart. In stacked mode the seriesBarDistance property will have no effect.
|
2834
3403
|
stackBars: false,
|
2835
3404
|
// Inverts the axes of the bar chart in order to draw a horizontal bar chart. Be aware that you also need to invert your axis settings as the Y Axis will now display the labels and the X Axis the values.
|
2836
3405
|
horizontalBars: false,
|
3406
|
+
// If set to true then each bar will represent a series and the data array is expected to be a one dimensional array of data values rather than a series array of series. This is useful if the bar chart should represent a profile rather than some data over time.
|
3407
|
+
distributeSeries: false,
|
2837
3408
|
// If true the whole data is reversed including labels, the series order as well as the whole series data arrays.
|
2838
3409
|
reverseData: false,
|
2839
3410
|
// Override the class names that get used to generate the SVG structure of the chart
|
2840
3411
|
classNames: {
|
2841
3412
|
chart: 'ct-chart-bar',
|
3413
|
+
horizontalBars: 'ct-horizontal-bars',
|
2842
3414
|
label: 'ct-label',
|
2843
3415
|
labelGroup: 'ct-labels',
|
2844
3416
|
series: 'ct-series',
|
@@ -2846,7 +3418,9 @@ var Chartist = {
|
|
2846
3418
|
grid: 'ct-grid',
|
2847
3419
|
gridGroup: 'ct-grids',
|
2848
3420
|
vertical: 'ct-vertical',
|
2849
|
-
horizontal: 'ct-horizontal'
|
3421
|
+
horizontal: 'ct-horizontal',
|
3422
|
+
start: 'ct-start',
|
3423
|
+
end: 'ct-end'
|
2850
3424
|
}
|
2851
3425
|
};
|
2852
3426
|
|
@@ -2855,23 +3429,48 @@ var Chartist = {
|
|
2855
3429
|
*
|
2856
3430
|
*/
|
2857
3431
|
function createChart(options) {
|
2858
|
-
var
|
2859
|
-
|
2860
|
-
|
2861
|
-
|
3432
|
+
var data = {
|
3433
|
+
raw: this.data,
|
3434
|
+
normalized: options.distributeSeries ? Chartist.getDataArray(this.data, options.reverseData, options.horizontalBars ? 'x' : 'y').map(function(value) {
|
3435
|
+
return [value];
|
3436
|
+
}) : Chartist.getDataArray(this.data, options.reverseData, options.horizontalBars ? 'x' : 'y')
|
3437
|
+
};
|
3438
|
+
|
3439
|
+
var highLow;
|
2862
3440
|
|
2863
3441
|
// Create new svg element
|
2864
|
-
this.svg = Chartist.createSvg(
|
3442
|
+
this.svg = Chartist.createSvg(
|
3443
|
+
this.container,
|
3444
|
+
options.width,
|
3445
|
+
options.height,
|
3446
|
+
options.classNames.chart + (options.horizontalBars ? ' ' + options.classNames.horizontalBars : '')
|
3447
|
+
);
|
3448
|
+
|
3449
|
+
// Drawing groups in correct order
|
3450
|
+
var gridGroup = this.svg.elem('g').addClass(options.classNames.gridGroup);
|
3451
|
+
var seriesGroup = this.svg.elem('g');
|
3452
|
+
var labelGroup = this.svg.elem('g').addClass(options.classNames.labelGroup);
|
2865
3453
|
|
2866
3454
|
if(options.stackBars) {
|
2867
3455
|
// If stacked bars we need to calculate the high low from stacked values from each series
|
2868
|
-
var serialSums = Chartist.serialMap(
|
2869
|
-
return Array.prototype.slice.call(arguments).
|
3456
|
+
var serialSums = Chartist.serialMap(data.normalized, function serialSums() {
|
3457
|
+
return Array.prototype.slice.call(arguments).map(function(value) {
|
3458
|
+
return value;
|
3459
|
+
}).reduce(function(prev, curr) {
|
3460
|
+
return {
|
3461
|
+
x: prev.x + curr.x || 0,
|
3462
|
+
y: prev.y + curr.y || 0
|
3463
|
+
};
|
3464
|
+
}, {x: 0, y: 0});
|
2870
3465
|
});
|
2871
3466
|
|
2872
|
-
highLow = Chartist.getHighLow([serialSums]
|
3467
|
+
highLow = Chartist.getHighLow([serialSums], Chartist.extend({}, options, {
|
3468
|
+
referenceValue: 0
|
3469
|
+
}), options.horizontalBars ? 'x' : 'y');
|
2873
3470
|
} else {
|
2874
|
-
highLow = Chartist.getHighLow(
|
3471
|
+
highLow = Chartist.getHighLow(data.normalized, Chartist.extend({}, options, {
|
3472
|
+
referenceValue: 0
|
3473
|
+
}), options.horizontalBars ? 'x' : 'y');
|
2875
3474
|
}
|
2876
3475
|
// Overrides of high / low from settings
|
2877
3476
|
highLow.high = +options.high || (options.high === 0 ? 0 : highLow.high);
|
@@ -2880,149 +3479,167 @@ var Chartist = {
|
|
2880
3479
|
var chartRect = Chartist.createChartRect(this.svg, options, defaultOptions.padding);
|
2881
3480
|
|
2882
3481
|
var valueAxis,
|
3482
|
+
labelAxisTicks,
|
2883
3483
|
labelAxis,
|
2884
3484
|
axisX,
|
2885
3485
|
axisY;
|
2886
3486
|
|
3487
|
+
// We need to set step count based on some options combinations
|
3488
|
+
if(options.distributeSeries && options.stackBars) {
|
3489
|
+
// If distributed series are enabled and bars need to be stacked, we'll only have one bar and therefore should
|
3490
|
+
// use only the first label for the step axis
|
3491
|
+
labelAxisTicks = data.raw.labels.slice(0, 1);
|
3492
|
+
} else {
|
3493
|
+
// If distributed series are enabled but stacked bars aren't, we should use the series labels
|
3494
|
+
// If we are drawing a regular bar chart with two dimensional series data, we just use the labels array
|
3495
|
+
// as the bars are normalized
|
3496
|
+
labelAxisTicks = data.raw.labels;
|
3497
|
+
}
|
3498
|
+
|
3499
|
+
// Set labelAxis and valueAxis based on the horizontalBars setting. This setting will flip the axes if necessary.
|
2887
3500
|
if(options.horizontalBars) {
|
2888
|
-
|
2889
|
-
Chartist.Axis.units.
|
2890
|
-
chartRect,
|
2891
|
-
function timeAxisTransform(projectedValue) {
|
2892
|
-
projectedValue.pos = chartRect.y1 - projectedValue.pos;
|
2893
|
-
return projectedValue;
|
2894
|
-
},
|
2895
|
-
{
|
2896
|
-
x: normalizedPadding.left + options.axisY.labelOffset.x + (this.supportsForeignObject ? -10 : 0),
|
2897
|
-
y: options.axisY.labelOffset.y - chartRect.height() / this.data.labels.length
|
2898
|
-
},
|
2899
|
-
{
|
2900
|
-
stepCount: this.data.labels.length,
|
2901
|
-
stretch: options.fullHeight
|
2902
|
-
}
|
2903
|
-
);
|
2904
|
-
|
2905
|
-
valueAxis = axisX = new Chartist.LinearScaleAxis(
|
2906
|
-
Chartist.Axis.units.x,
|
2907
|
-
chartRect,
|
2908
|
-
function valueAxisTransform(projectedValue) {
|
2909
|
-
projectedValue.pos = chartRect.x1 + projectedValue.pos;
|
2910
|
-
return projectedValue;
|
2911
|
-
},
|
2912
|
-
{
|
2913
|
-
x: options.axisX.labelOffset.x,
|
2914
|
-
y: chartRect.y1 + options.axisX.labelOffset.y + (this.supportsForeignObject ? 5 : 20)
|
2915
|
-
},
|
2916
|
-
{
|
3501
|
+
if(options.axisX.type === undefined) {
|
3502
|
+
valueAxis = axisX = new Chartist.AutoScaleAxis(Chartist.Axis.units.x, data, chartRect, Chartist.extend({}, options.axisX, {
|
2917
3503
|
highLow: highLow,
|
2918
|
-
scaleMinSpace: options.axisX.scaleMinSpace,
|
2919
3504
|
referenceValue: 0
|
2920
|
-
}
|
2921
|
-
|
3505
|
+
}));
|
3506
|
+
} else {
|
3507
|
+
valueAxis = axisX = options.axisX.type.call(Chartist, Chartist.Axis.units.x, data, chartRect, Chartist.extend({}, options.axisX, {
|
3508
|
+
highLow: highLow,
|
3509
|
+
referenceValue: 0
|
3510
|
+
}));
|
3511
|
+
}
|
3512
|
+
|
3513
|
+
if(options.axisY.type === undefined) {
|
3514
|
+
labelAxis = axisY = new Chartist.StepAxis(Chartist.Axis.units.y, data, chartRect, {
|
3515
|
+
ticks: labelAxisTicks
|
3516
|
+
});
|
3517
|
+
} else {
|
3518
|
+
labelAxis = axisY = options.axisY.type.call(Chartist, Chartist.Axis.units.y, data, chartRect, options.axisY);
|
3519
|
+
}
|
2922
3520
|
} else {
|
2923
|
-
|
2924
|
-
Chartist.Axis.units.x,
|
2925
|
-
|
2926
|
-
|
2927
|
-
|
2928
|
-
|
2929
|
-
|
2930
|
-
|
2931
|
-
|
2932
|
-
|
2933
|
-
},
|
2934
|
-
{
|
2935
|
-
stepCount: this.data.labels.length
|
2936
|
-
}
|
2937
|
-
);
|
2938
|
-
|
2939
|
-
valueAxis = axisY = new Chartist.LinearScaleAxis(
|
2940
|
-
Chartist.Axis.units.y,
|
2941
|
-
chartRect,
|
2942
|
-
function valueAxisTransform(projectedValue) {
|
2943
|
-
projectedValue.pos = chartRect.y1 - projectedValue.pos;
|
2944
|
-
return projectedValue;
|
2945
|
-
},
|
2946
|
-
{
|
2947
|
-
x: normalizedPadding.left + options.axisY.labelOffset.x + (this.supportsForeignObject ? -10 : 0),
|
2948
|
-
y: options.axisY.labelOffset.y + (this.supportsForeignObject ? -15 : 0)
|
2949
|
-
},
|
2950
|
-
{
|
3521
|
+
if(options.axisX.type === undefined) {
|
3522
|
+
labelAxis = axisX = new Chartist.StepAxis(Chartist.Axis.units.x, data, chartRect, {
|
3523
|
+
ticks: labelAxisTicks
|
3524
|
+
});
|
3525
|
+
} else {
|
3526
|
+
labelAxis = axisX = options.axisX.type.call(Chartist, Chartist.Axis.units.x, data, chartRect, options.axisX);
|
3527
|
+
}
|
3528
|
+
|
3529
|
+
if(options.axisY.type === undefined) {
|
3530
|
+
valueAxis = axisY = new Chartist.AutoScaleAxis(Chartist.Axis.units.y, data, chartRect, Chartist.extend({}, options.axisY, {
|
2951
3531
|
highLow: highLow,
|
2952
|
-
scaleMinSpace: options.axisY.scaleMinSpace,
|
2953
3532
|
referenceValue: 0
|
2954
|
-
}
|
2955
|
-
|
3533
|
+
}));
|
3534
|
+
} else {
|
3535
|
+
valueAxis = axisY = options.axisY.type.call(Chartist, Chartist.Axis.units.y, data, chartRect, Chartist.extend({}, options.axisY, {
|
3536
|
+
highLow: highLow,
|
3537
|
+
referenceValue: 0
|
3538
|
+
}));
|
3539
|
+
}
|
2956
3540
|
}
|
2957
3541
|
|
2958
|
-
//
|
2959
|
-
var
|
2960
|
-
|
2961
|
-
|
2962
|
-
zeroPoint = options.horizontalBars ? (chartRect.x1 + valueAxis.projectValue(0).pos) : (chartRect.y1 - valueAxis.projectValue(0).pos),
|
2963
|
-
// Used to track the screen coordinates of stacked bars
|
2964
|
-
stackedBarValues = [];
|
3542
|
+
// Projected 0 point
|
3543
|
+
var zeroPoint = options.horizontalBars ? (chartRect.x1 + valueAxis.projectValue(0)) : (chartRect.y1 - valueAxis.projectValue(0));
|
3544
|
+
// Used to track the screen coordinates of stacked bars
|
3545
|
+
var stackedBarValues = [];
|
2965
3546
|
|
2966
|
-
|
2967
|
-
|
2968
|
-
this.data.labels,
|
2969
|
-
chartRect,
|
2970
|
-
gridGroup,
|
2971
|
-
labelGroup,
|
2972
|
-
this.supportsForeignObject,
|
2973
|
-
options,
|
2974
|
-
this.eventEmitter
|
2975
|
-
);
|
2976
|
-
|
2977
|
-
Chartist.createAxis(
|
2978
|
-
valueAxis,
|
2979
|
-
valueAxis.bounds.values,
|
2980
|
-
chartRect,
|
2981
|
-
gridGroup,
|
2982
|
-
labelGroup,
|
2983
|
-
this.supportsForeignObject,
|
2984
|
-
options,
|
2985
|
-
this.eventEmitter
|
2986
|
-
);
|
3547
|
+
labelAxis.createGridAndLabels(gridGroup, labelGroup, this.supportsForeignObject, options, this.eventEmitter);
|
3548
|
+
valueAxis.createGridAndLabels(gridGroup, labelGroup, this.supportsForeignObject, options, this.eventEmitter);
|
2987
3549
|
|
2988
3550
|
// Draw the series
|
2989
|
-
|
3551
|
+
data.raw.series.forEach(function(series, seriesIndex) {
|
2990
3552
|
// Calculating bi-polar value of index for seriesOffset. For i = 0..4 biPol will be -1.5, -0.5, 0.5, 1.5 etc.
|
2991
|
-
var biPol = seriesIndex - (
|
3553
|
+
var biPol = seriesIndex - (data.raw.series.length - 1) / 2;
|
2992
3554
|
// Half of the period width between vertical grid lines used to position bars
|
2993
|
-
|
3555
|
+
var periodHalfLength;
|
3556
|
+
// Current series SVG element
|
3557
|
+
var seriesElement;
|
3558
|
+
|
3559
|
+
// We need to set periodHalfLength based on some options combinations
|
3560
|
+
if(options.distributeSeries && !options.stackBars) {
|
3561
|
+
// If distributed series are enabled but stacked bars aren't, we need to use the length of the normaizedData array
|
3562
|
+
// which is the series count and divide by 2
|
3563
|
+
periodHalfLength = labelAxis.axisLength / data.normalized.length / 2;
|
3564
|
+
} else if(options.distributeSeries && options.stackBars) {
|
3565
|
+
// If distributed series and stacked bars are enabled we'll only get one bar so we should just divide the axis
|
3566
|
+
// length by 2
|
3567
|
+
periodHalfLength = labelAxis.axisLength / 2;
|
3568
|
+
} else {
|
3569
|
+
// On regular bar charts we should just use the series length
|
3570
|
+
periodHalfLength = labelAxis.axisLength / data.normalized[seriesIndex].length / 2;
|
3571
|
+
}
|
2994
3572
|
|
2995
|
-
|
3573
|
+
// Adding the series group to the series element
|
3574
|
+
seriesElement = seriesGroup.elem('g');
|
2996
3575
|
|
2997
3576
|
// Write attributes to series group element. If series name or meta is undefined the attributes will not be written
|
2998
|
-
|
3577
|
+
seriesElement.attr({
|
2999
3578
|
'series-name': series.name,
|
3000
3579
|
'meta': Chartist.serialize(series.meta)
|
3001
3580
|
}, Chartist.xmlNs.uri);
|
3002
3581
|
|
3003
3582
|
// Use series class from series data or if not set generate one
|
3004
|
-
|
3583
|
+
seriesElement.addClass([
|
3005
3584
|
options.classNames.series,
|
3006
3585
|
(series.className || options.classNames.series + '-' + Chartist.alphaNumerate(seriesIndex))
|
3007
3586
|
].join(' '));
|
3008
3587
|
|
3009
|
-
|
3010
|
-
var projected
|
3011
|
-
x: chartRect.x1 + (options.horizontalBars ? valueAxis : labelAxis).projectValue(value, valueIndex, normalizedData[seriesIndex]).pos,
|
3012
|
-
y: chartRect.y1 - (options.horizontalBars ? labelAxis : valueAxis).projectValue(value, valueIndex, normalizedData[seriesIndex]).pos
|
3013
|
-
},
|
3588
|
+
data.normalized[seriesIndex].forEach(function(value, valueIndex) {
|
3589
|
+
var projected,
|
3014
3590
|
bar,
|
3015
|
-
previousStack
|
3591
|
+
previousStack,
|
3592
|
+
labelAxisValueIndex;
|
3593
|
+
|
3594
|
+
// We need to set labelAxisValueIndex based on some options combinations
|
3595
|
+
if(options.distributeSeries && !options.stackBars) {
|
3596
|
+
// If distributed series are enabled but stacked bars aren't, we can use the seriesIndex for later projection
|
3597
|
+
// on the step axis for label positioning
|
3598
|
+
labelAxisValueIndex = seriesIndex;
|
3599
|
+
} else if(options.distributeSeries && options.stackBars) {
|
3600
|
+
// If distributed series and stacked bars are enabled, we will only get one bar and therefore always use
|
3601
|
+
// 0 for projection on the label step axis
|
3602
|
+
labelAxisValueIndex = 0;
|
3603
|
+
} else {
|
3604
|
+
// On regular bar charts we just use the value index to project on the label step axis
|
3605
|
+
labelAxisValueIndex = valueIndex;
|
3606
|
+
}
|
3607
|
+
|
3608
|
+
// We need to transform coordinates differently based on the chart layout
|
3609
|
+
if(options.horizontalBars) {
|
3610
|
+
projected = {
|
3611
|
+
x: chartRect.x1 + valueAxis.projectValue(value && value.x ? value.x : 0, valueIndex, data.normalized[seriesIndex]),
|
3612
|
+
y: chartRect.y1 - labelAxis.projectValue(value && value.y ? value.y : 0, labelAxisValueIndex, data.normalized[seriesIndex])
|
3613
|
+
};
|
3614
|
+
} else {
|
3615
|
+
projected = {
|
3616
|
+
x: chartRect.x1 + labelAxis.projectValue(value && value.x ? value.x : 0, labelAxisValueIndex, data.normalized[seriesIndex]),
|
3617
|
+
y: chartRect.y1 - valueAxis.projectValue(value && value.y ? value.y : 0, valueIndex, data.normalized[seriesIndex])
|
3618
|
+
}
|
3619
|
+
}
|
3016
3620
|
|
3017
|
-
//
|
3018
|
-
|
3019
|
-
//
|
3020
|
-
|
3621
|
+
// If the label axis is a step based axis we will offset the bar into the middle of between two steps using
|
3622
|
+
// the periodHalfLength value. Also we do arrange the different series so that they align up to each other using
|
3623
|
+
// the seriesBarDistance. If we don't have a step axis, the bar positions can be chosen freely so we should not
|
3624
|
+
// add any automated positioning.
|
3625
|
+
if(labelAxis instanceof Chartist.StepAxis) {
|
3626
|
+
// Offset to center bar between grid lines, but only if the step axis is not stretched
|
3627
|
+
if(!labelAxis.options.stretch) {
|
3628
|
+
projected[labelAxis.units.pos] += periodHalfLength * (options.horizontalBars ? -1 : 1);
|
3629
|
+
}
|
3630
|
+
// Using bi-polar offset for multiple series if no stacked bars or series distribution is used
|
3631
|
+
projected[labelAxis.units.pos] += (options.stackBars || options.distributeSeries) ? 0 : biPol * options.seriesBarDistance * (options.horizontalBars ? -1 : 1);
|
3632
|
+
}
|
3021
3633
|
|
3022
3634
|
// Enter value in stacked bar values used to remember previous screen value for stacking up bars
|
3023
3635
|
previousStack = stackedBarValues[valueIndex] || zeroPoint;
|
3024
3636
|
stackedBarValues[valueIndex] = previousStack - (zeroPoint - projected[labelAxis.counterUnits.pos]);
|
3025
3637
|
|
3638
|
+
// Skip if value is undefined
|
3639
|
+
if(value === undefined) {
|
3640
|
+
return;
|
3641
|
+
}
|
3642
|
+
|
3026
3643
|
var positions = {};
|
3027
3644
|
positions[labelAxis.units.pos + '1'] = projected[labelAxis.units.pos];
|
3028
3645
|
positions[labelAxis.units.pos + '2'] = projected[labelAxis.units.pos];
|
@@ -3030,8 +3647,17 @@ var Chartist = {
|
|
3030
3647
|
positions[labelAxis.counterUnits.pos + '1'] = options.stackBars ? previousStack : zeroPoint;
|
3031
3648
|
positions[labelAxis.counterUnits.pos + '2'] = options.stackBars ? stackedBarValues[valueIndex] : projected[labelAxis.counterUnits.pos];
|
3032
3649
|
|
3033
|
-
|
3034
|
-
|
3650
|
+
// Limit x and y so that they are within the chart rect
|
3651
|
+
positions.x1 = Math.min(Math.max(positions.x1, chartRect.x1), chartRect.x2);
|
3652
|
+
positions.x2 = Math.min(Math.max(positions.x2, chartRect.x1), chartRect.x2);
|
3653
|
+
positions.y1 = Math.min(Math.max(positions.y1, chartRect.y2), chartRect.y1);
|
3654
|
+
positions.y2 = Math.min(Math.max(positions.y2, chartRect.y2), chartRect.y1);
|
3655
|
+
|
3656
|
+
// Create bar element
|
3657
|
+
bar = seriesElement.elem('line', positions, options.classNames.bar).attr({
|
3658
|
+
'value': [value.x, value.y].filter(function(v) {
|
3659
|
+
return v;
|
3660
|
+
}).join(','),
|
3035
3661
|
'meta': Chartist.getMetaData(series, valueIndex)
|
3036
3662
|
}, Chartist.xmlNs.uri);
|
3037
3663
|
|
@@ -3039,8 +3665,13 @@ var Chartist = {
|
|
3039
3665
|
type: 'bar',
|
3040
3666
|
value: value,
|
3041
3667
|
index: valueIndex,
|
3668
|
+
meta: Chartist.getMetaData(series, valueIndex),
|
3669
|
+
series: series,
|
3670
|
+
seriesIndex: seriesIndex,
|
3671
|
+
axisX: axisX,
|
3672
|
+
axisY: axisY,
|
3042
3673
|
chartRect: chartRect,
|
3043
|
-
group:
|
3674
|
+
group: seriesElement,
|
3044
3675
|
element: bar
|
3045
3676
|
}, positions));
|
3046
3677
|
}.bind(this));
|
@@ -3130,12 +3761,13 @@ var Chartist = {
|
|
3130
3761
|
height: undefined,
|
3131
3762
|
// Padding of the chart drawing area to the container element and labels as a number or padding object {top: 5, right: 5, bottom: 5, left: 5}
|
3132
3763
|
chartPadding: 5,
|
3133
|
-
// Override the class names that
|
3764
|
+
// Override the class names that are used to generate the SVG structure of the chart
|
3134
3765
|
classNames: {
|
3135
|
-
|
3766
|
+
chartPie: 'ct-chart-pie',
|
3767
|
+
chartDonut: 'ct-chart-donut',
|
3136
3768
|
series: 'ct-series',
|
3137
|
-
|
3138
|
-
|
3769
|
+
slicePie: 'ct-slice-pie',
|
3770
|
+
sliceDonut: 'ct-slice-donut',
|
3139
3771
|
label: 'ct-label'
|
3140
3772
|
},
|
3141
3773
|
// The start angle of the pie chart in degrees where 0 points north. A higher value offsets the start angle clockwise.
|
@@ -3150,6 +3782,8 @@ var Chartist = {
|
|
3150
3782
|
showLabel: true,
|
3151
3783
|
// Label position offset from the standard position which is half distance of the radius. This value can be either positive or negative. Positive values will position the label away from the center.
|
3152
3784
|
labelOffset: 0,
|
3785
|
+
// This option can be set to 'inside', 'outside' or 'center'. Positioned with 'inside' the labels will be placed on half the distance of the radius to the border of the Pie by respecting the 'labelOffset'. The 'outside' option will place the labels at the border of the pie and 'center' will place the labels in the absolute center point of the chart. The 'center' option only makes sense in conjunction with the 'labelOffset' option.
|
3786
|
+
labelPosition: 'inside',
|
3153
3787
|
// An interpolation function for the label value
|
3154
3788
|
labelInterpolationFnc: Chartist.noop,
|
3155
3789
|
// Label direction can be 'neutral', 'explode' or 'implode'. The labels anchor will be positioned based on those settings as well as the fact if the labels are on the right or left side of the center of the chart. Usually explode is useful when labels are positioned far away from the center.
|
@@ -3187,6 +3821,7 @@ var Chartist = {
|
|
3187
3821
|
*/
|
3188
3822
|
function createChart(options) {
|
3189
3823
|
var seriesGroups = [],
|
3824
|
+
labelsGroup,
|
3190
3825
|
chartRect,
|
3191
3826
|
radius,
|
3192
3827
|
labelRadius,
|
@@ -3195,7 +3830,7 @@ var Chartist = {
|
|
3195
3830
|
dataArray = Chartist.getDataArray(this.data, options.reverseData);
|
3196
3831
|
|
3197
3832
|
// Create SVG.js draw
|
3198
|
-
this.svg = Chartist.createSvg(this.container, options.width, options.height, options.classNames.
|
3833
|
+
this.svg = Chartist.createSvg(this.container, options.width, options.height,options.donut ? options.classNames.chartDonut : options.classNames.chartPie);
|
3199
3834
|
// Calculate charting rect
|
3200
3835
|
chartRect = Chartist.createChartRect(this.svg, options, defaultOptions.padding);
|
3201
3836
|
// Get biggest circle radius possible within chartRect
|
@@ -3210,9 +3845,18 @@ var Chartist = {
|
|
3210
3845
|
// See this proposal for more details: http://lists.w3.org/Archives/Public/www-svg/2003Oct/0000.html
|
3211
3846
|
radius -= options.donut ? options.donutWidth / 2 : 0;
|
3212
3847
|
|
3213
|
-
// If a donut chart then the label position is at the radius,
|
3214
|
-
//
|
3215
|
-
|
3848
|
+
// If labelPosition is set to `outside` or a donut chart is drawn then the label position is at the radius,
|
3849
|
+
// if regular pie chart it's half of the radius
|
3850
|
+
if(options.labelPosition === 'outside' || options.donut) {
|
3851
|
+
labelRadius = radius;
|
3852
|
+
} else if(options.labelPosition === 'center') {
|
3853
|
+
// If labelPosition is center we start with 0 and will later wait for the labelOffset
|
3854
|
+
labelRadius = 0;
|
3855
|
+
} else {
|
3856
|
+
// Default option is 'inside' where we use half the radius so the label will be placed in the center of the pie
|
3857
|
+
// slice
|
3858
|
+
labelRadius = radius / 2;
|
3859
|
+
}
|
3216
3860
|
// Add the offset to the labelRadius where a negative offset means closed to the center of the chart
|
3217
3861
|
labelRadius += options.labelOffset;
|
3218
3862
|
|
@@ -3224,26 +3868,29 @@ var Chartist = {
|
|
3224
3868
|
|
3225
3869
|
// Check if there is only one non-zero value in the series array.
|
3226
3870
|
var hasSingleValInSeries = this.data.series.filter(function(val) {
|
3227
|
-
return val !== 0;
|
3871
|
+
return val.hasOwnProperty('value') ? val.value !== 0 : val !== 0;
|
3228
3872
|
}).length === 1;
|
3229
3873
|
|
3874
|
+
//if we need to show labels we create the label group now
|
3875
|
+
if(options.showLabel) {
|
3876
|
+
labelsGroup = this.svg.elem('g', null, null, true);
|
3877
|
+
}
|
3878
|
+
|
3230
3879
|
// Draw the series
|
3231
3880
|
// initialize series groups
|
3232
3881
|
for (var i = 0; i < this.data.series.length; i++) {
|
3882
|
+
var series = this.data.series[i];
|
3233
3883
|
seriesGroups[i] = this.svg.elem('g', null, null, true);
|
3234
3884
|
|
3235
|
-
// If the series is an object and contains a name we add a custom attribute
|
3236
|
-
|
3237
|
-
|
3238
|
-
|
3239
|
-
'meta': Chartist.serialize(this.data.series[i].meta)
|
3240
|
-
}, Chartist.xmlNs.uri);
|
3241
|
-
}
|
3885
|
+
// If the series is an object and contains a name or meta data we add a custom attribute
|
3886
|
+
seriesGroups[i].attr({
|
3887
|
+
'series-name': series.name
|
3888
|
+
}, Chartist.xmlNs.uri);
|
3242
3889
|
|
3243
3890
|
// Use series class from series data or if not set generate one
|
3244
3891
|
seriesGroups[i].addClass([
|
3245
3892
|
options.classNames.series,
|
3246
|
-
(
|
3893
|
+
(series.className || options.classNames.series + '-' + Chartist.alphaNumerate(i))
|
3247
3894
|
].join(' '));
|
3248
3895
|
|
3249
3896
|
var endAngle = startAngle + dataArray[i] / totalDataSum * 360;
|
@@ -3270,11 +3917,12 @@ var Chartist = {
|
|
3270
3917
|
// If this is a donut chart we add the donut class, otherwise just a regular slice
|
3271
3918
|
var pathElement = seriesGroups[i].elem('path', {
|
3272
3919
|
d: path.stringify()
|
3273
|
-
}, options.
|
3920
|
+
}, options.donut ? options.classNames.sliceDonut : options.classNames.slicePie);
|
3274
3921
|
|
3275
3922
|
// Adding the pie series value to the path
|
3276
3923
|
pathElement.attr({
|
3277
|
-
'value': dataArray[i]
|
3924
|
+
'value': dataArray[i],
|
3925
|
+
'meta': Chartist.serialize(series.meta)
|
3278
3926
|
}, Chartist.xmlNs.uri);
|
3279
3927
|
|
3280
3928
|
// If this is a donut, we add the stroke-width as style attribute
|
@@ -3290,6 +3938,8 @@ var Chartist = {
|
|
3290
3938
|
value: dataArray[i],
|
3291
3939
|
totalDataSum: totalDataSum,
|
3292
3940
|
index: i,
|
3941
|
+
meta: series.meta,
|
3942
|
+
series: series,
|
3293
3943
|
group: seriesGroups[i],
|
3294
3944
|
element: pathElement,
|
3295
3945
|
path: path.clone(),
|
@@ -3305,22 +3955,24 @@ var Chartist = {
|
|
3305
3955
|
var labelPosition = Chartist.polarToCartesian(center.x, center.y, labelRadius, startAngle + (endAngle - startAngle) / 2),
|
3306
3956
|
interpolatedValue = options.labelInterpolationFnc(this.data.labels ? this.data.labels[i] : dataArray[i], i);
|
3307
3957
|
|
3308
|
-
|
3309
|
-
|
3310
|
-
|
3311
|
-
|
3312
|
-
|
3958
|
+
if(interpolatedValue || interpolatedValue === 0) {
|
3959
|
+
var labelElement = labelsGroup.elem('text', {
|
3960
|
+
dx: labelPosition.x,
|
3961
|
+
dy: labelPosition.y,
|
3962
|
+
'text-anchor': determineAnchorPosition(center, labelPosition, options.labelDirection)
|
3963
|
+
}, options.classNames.label).text('' + interpolatedValue);
|
3313
3964
|
|
3314
|
-
|
3315
|
-
|
3316
|
-
|
3317
|
-
|
3318
|
-
|
3319
|
-
|
3320
|
-
|
3321
|
-
|
3322
|
-
|
3323
|
-
|
3965
|
+
// Fire off draw event
|
3966
|
+
this.eventEmitter.emit('draw', {
|
3967
|
+
type: 'label',
|
3968
|
+
index: i,
|
3969
|
+
group: labelsGroup,
|
3970
|
+
element: labelElement,
|
3971
|
+
text: '' + interpolatedValue,
|
3972
|
+
x: labelPosition.x,
|
3973
|
+
y: labelPosition.y
|
3974
|
+
});
|
3975
|
+
}
|
3324
3976
|
}
|
3325
3977
|
|
3326
3978
|
// Set next startAngle to current endAngle. Use slight offset so there are no transparent hairline issues
|
@@ -3340,7 +3992,7 @@ var Chartist = {
|
|
3340
3992
|
*
|
3341
3993
|
* @memberof Chartist.Pie
|
3342
3994
|
* @param {String|Node} query A selector query string or directly a DOM element
|
3343
|
-
* @param {Object} data The data object in the pie chart needs to have a series property with a one dimensional data array. The values will be normalized against each other and don't necessarily need to be in percentage. The series property can also be an array of objects that contain a
|
3995
|
+
* @param {Object} data The data object in the pie chart needs to have a series property with a one dimensional data array. The values will be normalized against each other and don't necessarily need to be in percentage. The series property can also be an array of value objects that contain a value property and a className property to override the CSS class name for the series group.
|
3344
3996
|
* @param {Object} [options] The options object with options that override the default options. Check the examples for a detailed list.
|
3345
3997
|
* @param {Array} [responsiveOptions] Specify an array of responsive option arrays which are a media query and options object pair => [[mediaQueryString, optionsObject],[more...]]
|
3346
3998
|
* @return {Object} An object with a version and an update method to manually redraw the chart
|
@@ -3381,17 +4033,25 @@ var Chartist = {
|
|
3381
4033
|
* });
|
3382
4034
|
*
|
3383
4035
|
* @example
|
3384
|
-
* // Overriding the class names for individual series
|
4036
|
+
* // Overriding the class names for individual series as well as a name and meta data.
|
4037
|
+
* // The name will be written as ct:series-name attribute and the meta data will be serialized and written
|
4038
|
+
* // to a ct:meta attribute.
|
3385
4039
|
* new Chartist.Pie('.ct-chart', {
|
3386
4040
|
* series: [{
|
3387
|
-
*
|
3388
|
-
*
|
4041
|
+
* value: 20,
|
4042
|
+
* name: 'Series 1',
|
4043
|
+
* className: 'my-custom-class-one',
|
4044
|
+
* meta: 'Meta One'
|
3389
4045
|
* }, {
|
3390
|
-
*
|
3391
|
-
*
|
4046
|
+
* value: 10,
|
4047
|
+
* name: 'Series 2',
|
4048
|
+
* className: 'my-custom-class-two',
|
4049
|
+
* meta: 'Meta Two'
|
3392
4050
|
* }, {
|
3393
|
-
*
|
3394
|
-
*
|
4051
|
+
* value: 70,
|
4052
|
+
* name: 'Series 3',
|
4053
|
+
* className: 'my-custom-class-three',
|
4054
|
+
* meta: 'Meta Three'
|
3395
4055
|
* }]
|
3396
4056
|
* });
|
3397
4057
|
*/
|