2016-08-10 16:46:49 +03:00
let crashesFile , crashes ;
let options = {
'channel' : {
value : null ,
type : 'option' ,
} ,
'crashesType' : {
value : null ,
type : 'option' ,
} ,
2016-09-23 19:28:16 +03:00
'wontfix' : {
value : null ,
type : 'select' ,
}
2016-08-10 16:46:49 +03:00
} ;
function getOption ( name ) {
return options [ name ] . value ;
}
function getOptionType ( name ) {
return options [ name ] . type ;
}
function setOption ( name , value ) {
return options [ name ] . value = value ;
}
let onLoad = new Promise ( function ( resolve , reject ) {
window . onload = resolve ;
} ) ;
function agoString ( val , str ) {
return val + ' ' + ( val == 1 ? str : str + 's' ) + ' ago' ;
}
function prettyDate ( date ) {
date = new Date ( date ) ;
let today = new Date ( ) ;
let hoursDiff = Math . round ( ( today . getTime ( ) - date . getTime ( ) ) / 3600000 ) ;
if ( hoursDiff < 24 ) {
return agoString ( hoursDiff , 'hour' ) ;
}
let daysDiff = Math . round ( ( today . getTime ( ) - date . getTime ( ) ) / 86400000 ) ;
if ( daysDiff < 10 ) {
return agoString ( daysDiff , 'day' ) ;
}
let weeksDiff = Math . round ( ( today . getTime ( ) - date . getTime ( ) ) / ( 7 * 86400000 ) ) ;
if ( weeksDiff < 3 ) {
return agoString ( weeksDiff , 'week' ) ;
}
let monthsDiff = ( today . getMonth ( ) + 12 * today . getFullYear ( ) ) - ( date . getMonth ( ) + 12 * date . getFullYear ( ) ) ;
if ( monthsDiff < 12 ) {
return agoString ( monthsDiff , 'month' ) ;
}
return agoString ( today . getFullYear ( ) - date . getFullYear ( ) , 'year' ) ;
}
function getVersion ( ) {
return Number ( crashes . versions [ 0 ] . substring ( 0 , crashes . versions [ 0 ] . indexOf ( '.' ) ) ) ;
}
function getFixedIn ( bug ) {
let version = getVersion ( ) ;
2016-09-23 19:28:16 +03:00
let statuses = [ '' , '---' , '?' , 'fix-optional' , 'affected' ] ;
if ( getOption ( 'wontfix' ) ) {
statuses . push ( 'wontfix' ) ;
}
if ( ! statuses . includes ( bug [ 'cf_status_firefox' + version ] ) ) {
2016-08-10 16:46:49 +03:00
return [ ] ;
}
let versionEnd = version ;
if ( getOption ( 'channel' ) == 'aurora' ) {
versionEnd += 1 ;
} else if ( getOption ( 'channel' ) == 'beta' ) {
versionEnd += 2 ;
} else if ( getOption ( 'channel' ) == 'release' ) {
versionEnd += 3 ;
}
let fixedIn = [ ] ;
2016-09-23 18:43:35 +03:00
for ( version += 1 ; version <= versionEnd ; version ++ ) {
2016-08-10 16:46:49 +03:00
if ( bug [ 'cf_status_firefox' + version ] === 'fixed' ||
bug [ 'cf_status_firefox' + version ] === 'verified' ) {
fixedIn . push ( version ) ;
}
}
return fixedIn ;
}
function addRow ( signature , obj ) {
let table = document . getElementById ( 'table' ) ;
let row = table . insertRow ( table . rows . length ) ;
let rank = row . insertCell ( 0 ) ;
rank . appendChild ( document . createTextNode ( obj . tc _rank ) ) ;
let key = row . insertCell ( 1 ) ;
let startupImage = document . createElement ( 'img' ) ;
startupImage . title = ( obj . startup _percent * 100 ) . toFixed ( 2 ) + ' %' ;
startupImage . src = 'rocket_fly.png' ;
startupImage . width = 64 * obj . startup _percent ;
startupImage . height = 64 * obj . startup _percent ;
startupImage . style . paddingRight = 5 ;
key . appendChild ( startupImage ) ;
let signatureDiv = document . createElement ( 'div' ) ;
signatureDiv . className = 'tooltip' ;
let signatureLink = document . createElement ( 'a' ) ;
signatureLink . appendChild ( document . createTextNode ( signature ) ) ;
signatureLink . href = 'https://crash-stats.mozilla.com/signature/?date=<%3D' + crashes . end _date + '&date=>%3D' + crashes . start _date + '&product=Firefox&' + crashes . versions . map ( version => 'version=' + version ) . join ( '&' ) + '&signature=' + signature ;
signatureDiv . appendChild ( signatureLink ) ;
key . appendChild ( signatureDiv ) ;
let today = new Date ( ) ;
let three _days _ago = new Date ( ) . setDate ( today . getDate ( ) - 3 ) ;
let ten _days _ago = new Date ( ) . setDate ( today . getDate ( ) - 10 ) ;
let bugs = row . insertCell ( 2 ) ;
obj . bugs
. sort ( ( bug1 , bug2 ) => new Date ( bug2 . last _change _time ) - new Date ( bug1 . last _change _time ) )
. forEach ( function ( bug ) {
let fixedIn = getFixedIn ( bug ) ;
if ( fixedIn . length == 0 ) {
return ;
}
let bugLink = document . createElement ( 'a' ) ;
2016-09-23 19:18:24 +03:00
bugLink . appendChild ( document . createTextNode ( bug . id + ' - ' + 'Fixed in ' + fixedIn . join ( ', ' ) + ', \'' + bug [ 'cf_status_firefox' + getVersion ( ) ] + '\' in ' + getVersion ( ) + '.' ) ) ;
2016-08-10 16:46:49 +03:00
bugLink . title = ( bug . resolution ? bug . resolution + ' - ' : '' ) +
'Last activity: ' + prettyDate ( bug . last _change _time ) ;
bugLink . href = 'https://bugzilla.mozilla.org/show_bug.cgi?id=' + bug . id ;
let bugDate = new Date ( bug . last _change _time ) ;
if ( bugDate > three _days _ago ) {
bugLink . style . color = 'green' ;
} else if ( bugDate > ten _days _ago ) {
bugLink . style . color = 'orange' ;
} else {
bugLink . style . color = 'red' ;
}
bugs . appendChild ( bugLink ) ;
bugs . appendChild ( document . createElement ( 'br' ) ) ;
} ) ;
}
function buildTable ( ) {
let file = getOption ( 'channel' ) ;
if ( getOption ( 'crashesType' ) === 'All crashes' ) {
file += '.json' ;
} else if ( getOption ( 'crashesType' ) === 'Startup crashes' ) {
file += '-startup.json'
}
let promise ;
if ( file === crashesFile ) {
promise = Promise . resolve ( ) ;
} else {
promise = fetch ( file )
. then ( function ( response ) {
return response . json ( ) ;
} )
. then ( function ( val ) {
crashes = val ;
} ) ;
crashesFile = file ;
}
promise
. then ( function ( ) {
// Order signatures by rank change or kairo's explosiveness.
Object . keys ( crashes . signatures )
. sort ( ( signature1 , signature2 ) => crashes . signatures [ signature1 ] . tc _rank - crashes . signatures [ signature2 ] . tc _rank )
. filter ( signature => crashes . signatures [ signature ] . bugs . filter ( bug => getFixedIn ( bug ) . length > 0 ) . length > 0 )
. forEach ( function ( signature ) {
addRow ( signature , crashes . signatures [ signature ] ) ;
} ) ;
} )
. catch ( function ( err ) {
console . error ( err ) ;
} ) ;
}
function rebuildTable ( ) {
while ( table . rows . length > 1 ) {
table . deleteRow ( table . rows . length - 1 ) ;
}
buildTable ( ) ;
}
onLoad
. then ( function ( ) {
Object . keys ( options )
. forEach ( function ( optionName ) {
let optionType = getOptionType ( optionName ) ;
let elem = document . getElementById ( optionName ) ;
if ( optionType === 'select' ) {
setOption ( optionName , elem . checked ) ;
elem . onchange = function ( ) {
setOption ( optionName , elem . checked ) ;
rebuildTable ( ) ;
} ;
} else if ( optionType === 'option' ) {
setOption ( optionName , elem . options [ elem . selectedIndex ] . value ) ;
elem . onchange = function ( ) {
setOption ( optionName , elem . options [ elem . selectedIndex ] . value ) ;
rebuildTable ( ) ;
} ;
} else if ( optionType === 'button' ) {
setOption ( optionName , elem . value ) ;
document . getElementById ( optionName + 'Button' ) . onclick = function ( ) {
setOption ( optionName , elem . value ) ;
rebuildTable ( ) ;
} ;
} else {
throw new Error ( 'Unexpected option type.' ) ;
}
} ) ;
} )
. then ( function ( ) {
buildTable ( ) ;
} )
. catch ( function ( err ) {
console . error ( err ) ;
} ) ;