@@ -308,6 +308,15 @@ document.addEventListener('DOMContentLoaded', function () {
308
308
309
309
// CHARTING AND PAGE UPDATES
310
310
311
+ String . prototype . hashCode = function ( ) {
312
+ let hash = 0 ;
313
+ for ( let i = 0 ; i < this . length ; i ++ ) {
314
+ const char = this . charCodeAt ( i ) ;
315
+ hash = ( hash << 5 ) - hash + char ;
316
+ }
317
+ return hash ;
318
+ } ;
319
+
311
320
function prepareBreakdownChartData ( metricTimeSeries , breakdownSourceSelector , averageSelector ) {
312
321
// this finds the dataset for this date, for example a breakdown by author
313
322
// and then collects all the keys from that dataset (i.e. all authors)
@@ -393,15 +402,29 @@ document.addEventListener('DOMContentLoaded', function () {
393
402
}
394
403
395
404
function getColorFromKey ( key ) {
396
- let hash = 0 ;
397
- for ( let i = 0 ; i < key . length ; i ++ ) {
398
- hash = key . charCodeAt ( i ) + ( ( hash << 5 ) - hash ) ;
399
- }
405
+ // Use hash to ensure consistent random variations for the same key
406
+ const randomSeed = Math . abs ( key . hashCode ( ) ) ;
407
+
408
+ // Define acceptable hue ranges (avoiding red)
409
+ const hueRanges = [
410
+ [ 30 , 60 ] , // Orange
411
+ [ 240 , 300 ] , // Purple
412
+ ] ;
413
+
414
+ const baseHue = randomSeed % 300 ; // Using a larger range to account for avoided red
415
+ const baseSaturation = ( randomSeed * 2654435761 ) % 31 + 70 ; // Between 70% and 100%
416
+ const baseLightness = ( randomSeed * 2654435761 ) % 61 + 20 ; // Between 20% and 80%
417
+
418
+ // Calculate hue variation within acceptable ranges
419
+ const hueVariation = ( Math . random ( ) * 61 - 30 + randomSeed ) % 300 ;
400
420
401
- // adjust hue to generate predominantly blue and green colors
402
- const hue = Math . floor ( ( Math . sin ( hash ++ ) + 1 ) / 2 * 120 + 150 ) ;
403
- const saturation = Math . floor ( ( Math . sin ( hash ++ ) + 1 ) / 2 * 50 + 30 ) ;
404
- const lightness = Math . floor ( ( Math . sin ( hash ++ ) + 1 ) / 2 * 30 + 50 ) ;
421
+ // Find the acceptable hue range
422
+ const acceptableRange = hueRanges . find ( range => baseHue + hueVariation >= range [ 0 ] && baseHue + hueVariation <= range [ 1 ] ) ;
423
+
424
+ // Calculate final hue within the chosen range
425
+ const hue = ( baseHue + hueVariation + ( acceptableRange ? acceptableRange [ 1 ] : 0 ) - 30 ) % 360 ;
426
+ const saturation = Math . min ( Math . max ( baseSaturation , 0 ) , 100 ) ;
427
+ const lightness = baseLightness ;
405
428
406
429
return `hsl(${ hue } , ${ saturation } %, ${ lightness } %)` ;
407
430
}
@@ -410,33 +433,27 @@ document.addEventListener('DOMContentLoaded', function () {
410
433
var gridLine ;
411
434
var titleColor ;
412
435
413
- function setUpChart ( metricTimeSeries , breakdownSourceSelector , averageSelector , chartId , chartTitle , chartParentId ) {
436
+ function setUpChart (
437
+ metricTimeSeries ,
438
+ breakdownSourceSelector ,
439
+ averageSelector ,
440
+ chartId ,
441
+ chartTitle ,
442
+ chartParentId ,
443
+ showLegend ,
444
+ forceLabels = false ,
445
+ ) {
414
446
var chartElement = document . getElementById ( chartId ) ;
415
447
var chartParentElement = document . getElementById ( chartParentId ) ;
416
448
var canvas = chartElement . getContext ( '2d' ) ;
417
449
418
450
var chartData = prepareBreakdownChartData ( metricTimeSeries , breakdownSourceSelector , averageSelector ) ;
419
451
var labelToTimeSeriesList = Object . entries ( chartData . yValuesMap ) ;
420
- // usually useless after 50 labels, while hover still works. large datasets are dots so they're ok to display
421
- var showLegend = labelToTimeSeriesList . length < 200 ;
422
- var disableLabelsDueToSize = showLegend && ( labelToTimeSeriesList . length > 7 && labelToTimeSeriesList . length < 50 || labelToTimeSeriesList . length > 200 ) ;
452
+ // usually useless when there are a lot of labels; hide by default when too many
453
+ var disableLabelsDueToSize = labelToTimeSeriesList . length > 15 ;
423
454
var hasAverages = chartData . averages . some ( item => item !== null ) ;
424
-
425
455
var datasets = [ ] ;
426
- for ( const [ sourceKey , timeSeries ] of labelToTimeSeriesList ) {
427
- var disableLabelsDueToLackOfData = timeSeries . every ( item => item === null ) ;
428
- datasets . push ( {
429
- label : sourceKey ,
430
- data : timeSeries ,
431
- cubicInterpolationMode : 'monotone' ,
432
- tension : 0.4 ,
433
- backgroundColor : [ getColorFromKey ( sourceKey ) ] ,
434
- borderColor : [ getColorFromKey ( sourceKey ) ] ,
435
- borderWidth : 2.5 ,
436
- spanGaps : true ,
437
- hidden : disableLabelsDueToSize || disableLabelsDueToLackOfData ,
438
- } ) ;
439
- } ;
456
+
440
457
if ( hasAverages ) {
441
458
datasets . push ( {
442
459
label : 'Average' ,
@@ -452,6 +469,21 @@ document.addEventListener('DOMContentLoaded', function () {
452
469
} ) ;
453
470
}
454
471
472
+ for ( const [ sourceKey , timeSeries ] of labelToTimeSeriesList ) {
473
+ var disableLabelsDueToLackOfData = timeSeries . every ( item => item === null ) ;
474
+ datasets . push ( {
475
+ label : sourceKey ,
476
+ data : timeSeries ,
477
+ cubicInterpolationMode : 'monotone' ,
478
+ tension : 0.4 ,
479
+ backgroundColor : [ getColorFromKey ( sourceKey ) ] ,
480
+ borderColor : [ getColorFromKey ( sourceKey ) ] ,
481
+ borderWidth : 2.5 ,
482
+ spanGaps : true ,
483
+ hidden : ! forceLabels && ( disableLabelsDueToSize || disableLabelsDueToLackOfData ) ,
484
+ } ) ;
485
+ } ;
486
+
455
487
var chartConfig = new Chart ( canvas , {
456
488
type : 'line' ,
457
489
data : {
@@ -816,11 +848,11 @@ document.addEventListener('DOMContentLoaded', function () {
816
848
var perRepositorySelector = ( metricOnDate ) => metricOnDate . perRepository ;
817
849
var repositoriesAverageSelector = ( metricOnDate ) => metricOnDate . averagePerRepository ;
818
850
819
- setUpChart ( data , perAuthorSelector , authorsAverageSelector , 'chart-per-author' , 'Authors breakdown' , 'chart-per-author-wrapper' ) ;
820
- setUpChart ( data , perReviewerSelector , reviewersAverageSelector , 'chart-per-reviewer' , 'Reviewers breakdown' , 'chart-per-reviewer-wrapper' ) ;
821
- setUpChart ( data , perCodeReviewSelector , codeReviewsAverageSelector , 'chart-per-code-review' , 'Code reviews breakdown' , 'chart-per-code-review-wrapper' ) ;
822
- setUpChart ( data , perDiscussionSelector , discussionsAverageSelector , 'chart-per-discussion' , 'Discussions breakdown' , 'chart-per-discussion-wrapper' ) ;
823
- setUpChart ( data , perRepositorySelector , repositoriesAverageSelector , 'chart-per-repository' , 'Repositories breakdown' , 'chart-per-repository-wrapper' ) ;
851
+ setUpChart ( data , perAuthorSelector , authorsAverageSelector , 'chart-per-author' , 'Authors breakdown' , 'chart-per-author-wrapper' , true ) ;
852
+ setUpChart ( data , perReviewerSelector , reviewersAverageSelector , 'chart-per-reviewer' , 'Reviewers breakdown' , 'chart-per-reviewer-wrapper' , true ) ;
853
+ setUpChart ( data , perCodeReviewSelector , codeReviewsAverageSelector , 'chart-per-code-review' , 'Code reviews breakdown' , 'chart-per-code-review-wrapper' , false , true ) ;
854
+ setUpChart ( data , perDiscussionSelector , discussionsAverageSelector , 'chart-per-discussion' , 'Discussions breakdown' , 'chart-per-discussion-wrapper' , true ) ;
855
+ setUpChart ( data , perRepositorySelector , repositoriesAverageSelector , 'chart-per-repository' , 'Repositories breakdown' , 'chart-per-repository-wrapper' , true ) ;
824
856
} )
825
857
. catch ( function ( error ) {
826
858
console . error ( 'Error fetching data: ' , error ) ;
0 commit comments