var table = d3.select('#chart'); // { environments: [ { name, date, version?, percent, scores, maxscore } ] // , tests: [ { id, name[], url? } ] // , results: [[ {status: pass|fail, column: #, message?, stacktrace?, raw?} ]] // } d3.json('tap.json', function(json) { window.json = json; function score(browser, i) { var res = json.results , max = res.length , n = res.filter(function(t) { return t[i].status == 'pass'; }).length; browser.percent = 100 * (n / max) + '%'; browser.maxscore = max; browser.score = n; return browser; } // d3.selectAll('#chart thead th').style('background-image',function(d,i){console.log(i);if(!d)return;return '-webkit-linear-gradient(bottom, #'+(i&1?'f0f0f0':'fff')+' '+d.percent+',rgba(255, 0, 0, 0.3) '+d.percent+')';}) // expand the "0" short form to { status: 'pass' } (and set the column # info) var results = json.results.map(function(row) { return row.map(function(d,i) { if (d) d.status = 'fail'; else d = { status:'pass' }; d.column = i + 1; return d; }); }) /* [ { key: "Firefox" , values: [ { its different versions } ] } , ... the other renderers ] */ , browsers = d3.nest() .key(function(d) { return d.name.toLowerCase(); }) .entries(json.environments = json.environments.map(score)) , colgroups = [{ key: "test-names", values:[] }].concat(browsers) ; json.results = results; // ease debugging table.attr('cols', 1 + json.environments.length); // grouping together all shown browsers by browser family var cg = table.selectAll('colgroup') .data(colgroups); cg.enter().append('colgroup') .attr('class', function(d) { return d.key; }) //.attr('span', function(d) { return d.values.length || undefined; }) .selectAll('col') .data(function(d) { return d.values; }) .enter().append('col') .attr('title', function(d) { return d.date; }) .text(function(d) { return d.version; }) // NOP, when no version given ; var thead = table.select('thead') , tr = thead.select('tr') , legend = thead.select('th'); table.node().appendChild(thead.node()); // needs to be after the <colgroups> // the browser name headers var ths = window.ths = thead.select('tr').selectAll('th') .data([null].concat(json.environments)) .enter().append('th') .attr('class', function(d) { return d && d.type; }) // NOP ; // name + date ths.append('div').text(function(d, i) { return d && d.name + ' '; }) .append('span').text(function(d) { return d && d.date; }) ; tr.node().appendChild(legend.node()); // put our legend at the far right function hovered_header(d) { var col = d3.event.target.__data__.column, th; if ((th = ths[0][col])) if ('classList' in th) th.classList.add('hover'); else d3.select(th).classed('hover', true); } function leaving_header(d) { var col = d3.event.target.__data__.column, th; if ((th = ths[0][col])) if ('classList' in th) th.classList.remove('hover'); else d3.select(th).classed('hover', false); } // all table rows on the main grid, and the default hover title for that line tr = window.tr = table .append('tbody').attr('class', 'data') .on('mouseover', hovered_header) .on('mouseout', leaving_header) .selectAll('tr').data(json.tests) .enter().append('tr') .attr('title', function(d) { return d.name.join(' -> '); }) ; // grid columns: data from the tests tr.selectAll('td') .data(function(d, i) { return results[i]; }) .enter().append('td') .attr('class', function(d) { return d.status; }) .attr('title', function(d) { return d.message; }) //.classed('died', function(d) { return d.crash; }) ; // last column: the test id and name var group; tr.append('th') .classed('group', function(d) { var my_group = d.name.slice(0, 1).join('\t') // FIXME: 1 -> -1, perhaps? , was_same = my_group === group; group = my_group; return !was_same; }) .append('a') .style('margin-left', function(d) { return (d.name.length - 1)*5 + 'px';}) .attr('name', function(d) { return 'test-'+ d.id; }) .attr('href', function(d) { return d && d.url || '#test-'+ d.id; }) .text(function(d, i) { return d.name[d.name.length - 1]; }) .attr('title', function(d, i) { return d.name.slice(0,-1).join(' -> '); }) ; table.selectAll('td.pass, td.warn, td.fail').on('click', function(d) { if (d.message) { var msg = 'Message: '+ d.message; if (d.stacktrace) msg += '\n\nstack trace:\n'+ d.stacktrace; alert(msg); } }); });