@@ -308,6 +308,15 @@ document.addEventListener('DOMContentLoaded', function () {
308308
309309 // CHARTING AND PAGE UPDATES
310310
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+
311320 function prepareBreakdownChartData ( metricTimeSeries , breakdownSourceSelector , averageSelector ) {
312321 // this finds the dataset for this date, for example a breakdown by author
313322 // and then collects all the keys from that dataset (i.e. all authors)
@@ -393,15 +402,29 @@ document.addEventListener('DOMContentLoaded', function () {
393402 }
394403
395404 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 ;
400420
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 ;
405428
406429 return `hsl(${ hue } , ${ saturation } %, ${ lightness } %)` ;
407430 }
@@ -410,33 +433,27 @@ document.addEventListener('DOMContentLoaded', function () {
410433 var gridLine ;
411434 var titleColor ;
412435
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+ ) {
414446 var chartElement = document . getElementById ( chartId ) ;
415447 var chartParentElement = document . getElementById ( chartParentId ) ;
416448 var canvas = chartElement . getContext ( '2d' ) ;
417449
418450 var chartData = prepareBreakdownChartData ( metricTimeSeries , breakdownSourceSelector , averageSelector ) ;
419451 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 ;
423454 var hasAverages = chartData . averages . some ( item => item !== null ) ;
424-
425455 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+
440457 if ( hasAverages ) {
441458 datasets . push ( {
442459 label : 'Average' ,
@@ -452,6 +469,21 @@ document.addEventListener('DOMContentLoaded', function () {
452469 } ) ;
453470 }
454471
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+
455487 var chartConfig = new Chart ( canvas , {
456488 type : 'line' ,
457489 data : {
@@ -816,11 +848,11 @@ document.addEventListener('DOMContentLoaded', function () {
816848 var perRepositorySelector = ( metricOnDate ) => metricOnDate . perRepository ;
817849 var repositoriesAverageSelector = ( metricOnDate ) => metricOnDate . averagePerRepository ;
818850
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 ) ;
824856 } )
825857 . catch ( function ( error ) {
826858 console . error ( 'Error fetching data: ' , error ) ;
0 commit comments