mirror of
https://github.com/pa11y/pa11y-dashboard.git
synced 2025-09-24 14:21:13 +00:00
Merge pull request #94 from nature/enhance-graph
Enhance graph for accessibility
This commit is contained in:
@@ -94,6 +94,7 @@ module.exports = function (grunt) {
|
||||
'public/js/vendor/bootstrap/js/transition.js',
|
||||
'public/js/vendor/bootstrap/js/collapse.js',
|
||||
'public/js/vendor/flot/jquery.flot.js',
|
||||
'public/js/vendor/flot/jquery.flot.dashes.js',
|
||||
'public/js/vendor/flot/jquery.flot.time.js',
|
||||
'public/js/vendor/flot/jquery.flot.selection.js',
|
||||
'public/js/vendor/flot/jquery.flot.resize.js',
|
||||
|
2
public/css/site.min.css
vendored
2
public/css/site.min.css
vendored
File diff suppressed because one or more lines are too long
@@ -24,9 +24,11 @@ $(document).ready(function(){
|
||||
var zoomResetButton = $('[data-role="zoom-reset"]');
|
||||
var graphContainer = $('[data-role="graph"]');
|
||||
var dateSelectDropdownMenu = $('[data-role="date-select-dropdown-menu"]');
|
||||
var legend = graphContainer.parent('.graph-container').find('.dashedLegend');
|
||||
|
||||
var graphOptions = {
|
||||
series: {
|
||||
dashes: { show: false, lineWidth: 3 },
|
||||
lines: { show: true },
|
||||
points: { show: true },
|
||||
hoverable: true
|
||||
@@ -43,6 +45,12 @@ $(document).ready(function(){
|
||||
lines: {
|
||||
lineWidth: 3
|
||||
},
|
||||
points: {
|
||||
fill: true,
|
||||
radius:4,
|
||||
lineWidth:3
|
||||
},
|
||||
shadowSize: 0,
|
||||
grid: {
|
||||
backgroundColor: '#fff',
|
||||
borderColor: '#808080',
|
||||
@@ -60,6 +68,11 @@ $(document).ready(function(){
|
||||
}
|
||||
};
|
||||
|
||||
// have we declared a custom legend
|
||||
if (legend.length === 1) {
|
||||
$('body').addClass('custom-legend');
|
||||
}
|
||||
|
||||
// Toggle appearance of lists of error/warnings/notices
|
||||
expandLink.click( function(){
|
||||
$(this).next().slideToggle('slow', function(){});
|
||||
@@ -168,9 +181,25 @@ $(document).ready(function(){
|
||||
|
||||
function getData() {
|
||||
return [
|
||||
{ color: 'rgb(216, 61, 45)', label: 'Errors', data: data.error },
|
||||
{ color: 'rgb(168, 103, 0)', label: 'Warnings', data: data.warning },
|
||||
{ color: 'rgb(23, 123, 190)', label: 'Notices', data: data.notice }
|
||||
{
|
||||
color: 'rgb(216, 61, 45)',
|
||||
label: 'Errors',
|
||||
data: data.error
|
||||
},
|
||||
{
|
||||
color: 'rgb(168, 103, 0)',
|
||||
label: 'Warnings',
|
||||
data: data.warning,
|
||||
lines: { show: false },
|
||||
dashes: { show: true, dashLength: [10, 5] }
|
||||
},
|
||||
{
|
||||
color: 'rgb(23, 123, 190)',
|
||||
label: 'Notices',
|
||||
data: data.notice,
|
||||
lines: { show: false },
|
||||
dashes: { show: true, dashLength: 5 }
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -225,13 +254,25 @@ $(document).ready(function(){
|
||||
|
||||
function plotAccordingToChoices() {
|
||||
var data = [];
|
||||
var labels = [];
|
||||
choiceContainer.find('input:checked').each(function () {
|
||||
var key = $(this).attr('name');
|
||||
if (key && datasets[key]) {
|
||||
labels.push(datasets[key].label);
|
||||
data.push(datasets[key]);
|
||||
}
|
||||
});
|
||||
|
||||
if (labels.length && legend.length === 1) {
|
||||
legend.find('tr').hide();
|
||||
$.each(labels, function (index, value) {
|
||||
$('.legend' + value).parents('tr').show();
|
||||
});
|
||||
legend.show();
|
||||
} else {
|
||||
legend.hide();
|
||||
}
|
||||
|
||||
if (data.length > -1) {
|
||||
$.plot(graphContainer, data, graphOptions);
|
||||
}
|
||||
|
4
public/js/site.min.js
vendored
4
public/js/site.min.js
vendored
File diff suppressed because one or more lines are too long
228
public/js/vendor/flot/jquery.flot.dashes.js
vendored
Normal file
228
public/js/vendor/flot/jquery.flot.dashes.js
vendored
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
* jQuery.flot.dashes
|
||||
*
|
||||
* options = {
|
||||
* series: {
|
||||
* dashes: {
|
||||
*
|
||||
* // show
|
||||
* // default: false
|
||||
* // Whether to show dashes for the series.
|
||||
* show: <boolean>,
|
||||
*
|
||||
* // lineWidth
|
||||
* // default: 2
|
||||
* // The width of the dashed line in pixels.
|
||||
* lineWidth: <number>,
|
||||
*
|
||||
* // dashLength
|
||||
* // default: 10
|
||||
* // Controls the length of the individual dashes and the amount of
|
||||
* // space between them.
|
||||
* // If this is a number, the dashes and spaces will have that length.
|
||||
* // If this is an array, it is read as [ dashLength, spaceLength ]
|
||||
* dashLength: <number> or <array[2]>
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
(function($){
|
||||
|
||||
function init(plot) {
|
||||
plot.hooks.drawSeries.push(function(plot, ctx, series) {
|
||||
if (!series.dashes.show) return;
|
||||
|
||||
var plotOffset = plot.getPlotOffset(),
|
||||
axisx = series.xaxis,
|
||||
axisy = series.yaxis;
|
||||
|
||||
function plotDashes(xoffset, yoffset) {
|
||||
|
||||
var points = series.datapoints.points,
|
||||
ps = series.datapoints.pointsize,
|
||||
prevx = null,
|
||||
prevy = null,
|
||||
dashRemainder = 0,
|
||||
dashOn = true,
|
||||
dashOnLength,
|
||||
dashOffLength;
|
||||
|
||||
if (series.dashes.dashLength[0]) {
|
||||
dashOnLength = series.dashes.dashLength[0];
|
||||
if (series.dashes.dashLength[1]) {
|
||||
dashOffLength = series.dashes.dashLength[1];
|
||||
} else {
|
||||
dashOffLength = dashOnLength;
|
||||
}
|
||||
} else {
|
||||
dashOffLength = dashOnLength = series.dashes.dashLength;
|
||||
}
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
for (var i = ps; i < points.length; i += ps) {
|
||||
|
||||
var x1 = points[i - ps],
|
||||
y1 = points[i - ps + 1],
|
||||
x2 = points[i],
|
||||
y2 = points[i + 1];
|
||||
|
||||
if (x1 == null || x2 == null) continue;
|
||||
|
||||
// clip with ymin
|
||||
if (y1 <= y2 && y1 < axisy.min) {
|
||||
if (y2 < axisy.min) continue; // line segment is outside
|
||||
// compute new intersection point
|
||||
x1 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
|
||||
y1 = axisy.min;
|
||||
} else if (y2 <= y1 && y2 < axisy.min) {
|
||||
if (y1 < axisy.min) continue;
|
||||
x2 = (axisy.min - y1) / (y2 - y1) * (x2 - x1) + x1;
|
||||
y2 = axisy.min;
|
||||
}
|
||||
|
||||
// clip with ymax
|
||||
if (y1 >= y2 && y1 > axisy.max) {
|
||||
if (y2 > axisy.max) continue;
|
||||
x1 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
|
||||
y1 = axisy.max;
|
||||
} else if (y2 >= y1 && y2 > axisy.max) {
|
||||
if (y1 > axisy.max) continue;
|
||||
x2 = (axisy.max - y1) / (y2 - y1) * (x2 - x1) + x1;
|
||||
y2 = axisy.max;
|
||||
}
|
||||
|
||||
// clip with xmin
|
||||
if (x1 <= x2 && x1 < axisx.min) {
|
||||
if (x2 < axisx.min) continue;
|
||||
y1 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
|
||||
x1 = axisx.min;
|
||||
} else if (x2 <= x1 && x2 < axisx.min) {
|
||||
if (x1 < axisx.min) continue;
|
||||
y2 = (axisx.min - x1) / (x2 - x1) * (y2 - y1) + y1;
|
||||
x2 = axisx.min;
|
||||
}
|
||||
|
||||
// clip with xmax
|
||||
if (x1 >= x2 && x1 > axisx.max) {
|
||||
if (x2 > axisx.max) continue;
|
||||
y1 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
|
||||
x1 = axisx.max;
|
||||
} else if (x2 >= x1 && x2 > axisx.max) {
|
||||
if (x1 > axisx.max) continue;
|
||||
y2 = (axisx.max - x1) / (x2 - x1) * (y2 - y1) + y1;
|
||||
x2 = axisx.max;
|
||||
}
|
||||
|
||||
if (x1 != prevx || y1 != prevy) {
|
||||
ctx.moveTo(axisx.p2c(x1) + xoffset, axisy.p2c(y1) + yoffset);
|
||||
}
|
||||
|
||||
var ax1 = axisx.p2c(x1) + xoffset,
|
||||
ay1 = axisy.p2c(y1) + yoffset,
|
||||
ax2 = axisx.p2c(x2) + xoffset,
|
||||
ay2 = axisy.p2c(y2) + yoffset,
|
||||
dashOffset;
|
||||
|
||||
function lineSegmentOffset(segmentLength) {
|
||||
|
||||
var c = Math.sqrt(Math.pow(ax2 - ax1, 2) + Math.pow(ay2 - ay1, 2));
|
||||
|
||||
if (c <= segmentLength) {
|
||||
return {
|
||||
deltaX: ax2 - ax1,
|
||||
deltaY: ay2 - ay1,
|
||||
distance: c,
|
||||
remainder: segmentLength - c
|
||||
}
|
||||
} else {
|
||||
var xsign = ax2 > ax1 ? 1 : -1,
|
||||
ysign = ay2 > ay1 ? 1 : -1;
|
||||
return {
|
||||
deltaX: xsign * Math.sqrt(Math.pow(segmentLength, 2) / (1 + Math.pow((ay2 - ay1)/(ax2 - ax1), 2))),
|
||||
deltaY: ysign * Math.sqrt(Math.pow(segmentLength, 2) - Math.pow(segmentLength, 2) / (1 + Math.pow((ay2 - ay1)/(ax2 - ax1), 2))),
|
||||
distance: segmentLength,
|
||||
remainder: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
//-end lineSegmentOffset
|
||||
|
||||
do {
|
||||
|
||||
dashOffset = lineSegmentOffset(
|
||||
dashRemainder > 0 ? dashRemainder :
|
||||
dashOn ? dashOnLength : dashOffLength);
|
||||
|
||||
if (dashOffset.deltaX != 0 || dashOffset.deltaY != 0) {
|
||||
if (dashOn) {
|
||||
ctx.lineTo(ax1 + dashOffset.deltaX, ay1 + dashOffset.deltaY);
|
||||
} else {
|
||||
ctx.moveTo(ax1 + dashOffset.deltaX, ay1 + dashOffset.deltaY);
|
||||
}
|
||||
}
|
||||
|
||||
dashOn = !dashOn;
|
||||
dashRemainder = dashOffset.remainder;
|
||||
ax1 += dashOffset.deltaX;
|
||||
ay1 += dashOffset.deltaY;
|
||||
|
||||
} while (dashOffset.distance > 0);
|
||||
|
||||
prevx = x2;
|
||||
prevy = y2;
|
||||
}
|
||||
|
||||
ctx.stroke();
|
||||
}
|
||||
//-end plotDashes
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(plotOffset.left, plotOffset.top);
|
||||
ctx.lineJoin = 'round';
|
||||
|
||||
var lw = series.dashes.lineWidth,
|
||||
sw = series.shadowSize;
|
||||
|
||||
// FIXME: consider another form of shadow when filling is turned on
|
||||
if (lw > 0 && sw > 0) {
|
||||
// draw shadow as a thick and thin line with transparency
|
||||
ctx.lineWidth = sw;
|
||||
ctx.strokeStyle = "rgba(0,0,0,0.1)";
|
||||
// position shadow at angle from the mid of line
|
||||
var angle = Math.PI/18;
|
||||
plotDashes(Math.sin(angle) * (lw/2 + sw/2), Math.cos(angle) * (lw/2 + sw/2));
|
||||
ctx.lineWidth = sw/2;
|
||||
plotDashes(Math.sin(angle) * (lw/2 + sw/4), Math.cos(angle) * (lw/2 + sw/4));
|
||||
}
|
||||
|
||||
ctx.lineWidth = lw;
|
||||
ctx.strokeStyle = series.color;
|
||||
|
||||
if (lw > 0) {
|
||||
plotDashes(0, 0);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
|
||||
});
|
||||
//-end draw hook
|
||||
}
|
||||
//-end init
|
||||
|
||||
$.plot.plugins.push({
|
||||
init: init,
|
||||
options: {
|
||||
series: {
|
||||
dashes: {
|
||||
show: false,
|
||||
lineWidth: 2,
|
||||
dashLength: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
name: 'dashes',
|
||||
version: '0.1'
|
||||
});
|
||||
|
||||
})(jQuery)
|
@@ -429,8 +429,7 @@ ul.date-links {
|
||||
}
|
||||
}
|
||||
.btn-reset {
|
||||
margin-top:-24px;
|
||||
margin-right:35px
|
||||
margin-top:12px;
|
||||
}
|
||||
.flot-x-axis {
|
||||
.flot-tick-label {
|
||||
@@ -440,6 +439,68 @@ ul.date-links {
|
||||
.tooltip-graph {
|
||||
font-size:12px;
|
||||
}
|
||||
.custom-legend .legend {
|
||||
display:none !important;
|
||||
}
|
||||
.dashedLegend {
|
||||
position:absolute;
|
||||
top:17px;
|
||||
right:40px;
|
||||
font-size:smaller;
|
||||
color:#545454;
|
||||
background-color: #fff;
|
||||
background-color: rgba(255, 255, 255, 0.75);
|
||||
display:none;
|
||||
}
|
||||
.dashedContainer {
|
||||
background: #fff;
|
||||
border: 1px solid #808080;
|
||||
margin: 5px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
.dashedLegend tr {
|
||||
display: none;
|
||||
}
|
||||
.dashedLegend .legendColorBox > div:first-child {
|
||||
border: 1px solid rgb(204, 204, 204);
|
||||
padding: 3px;
|
||||
}
|
||||
.dashedLegend .legendIcon div {
|
||||
height: 0px;
|
||||
border-width: 3px 0px 0px;
|
||||
border-top-style: solid;
|
||||
overflow: hidden;
|
||||
}
|
||||
.dashedLegend .legendErrors div {
|
||||
width: 25px;
|
||||
border-top-color: rgb(216, 61, 45);
|
||||
}
|
||||
.dashedLegend .legendWarnings div {
|
||||
width: 10px;
|
||||
border-top-color: rgb(168, 103, 0);
|
||||
float: left;
|
||||
}
|
||||
.dashedLegend .legendWarnings div:first-child {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.dashedLegend .legendNotices div {
|
||||
width: 5px;
|
||||
border-top-color: rgb(23, 123, 190);
|
||||
float: left;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.dashedLegend .legendNotices div:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
.dashedLegend td.legendColorBox {
|
||||
padding-right: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.dashedLegend td.legendLabel {
|
||||
padding-right: 10px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
/* New task page */
|
||||
.standards-lists {
|
||||
|
@@ -15,11 +15,43 @@ You should have received a copy of the GNU General Public License
|
||||
along with pa11y-dashboard. If not, see <http://www.gnu.org/licenses/>.
|
||||
}}
|
||||
<div class="col-md-12 clearfix">
|
||||
<div class="graph-container graph-spacer ruled">
|
||||
<div class="graph-container graph-spacer ruled clearfix">
|
||||
<div data-role="graph" class="graph"></div>
|
||||
<div class="row">
|
||||
<ul class="list-unstyled floated-list series-checkboxes clearfix crunch-bottom col-md-3 col-sm-6 col-xs-12 pull-right" data-role="series-checkboxes"></ul>
|
||||
</div>
|
||||
<div class="dashedLegend">
|
||||
<div class="dashedContainer">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="legendColorBox">
|
||||
<div class="clearfix legendIcon legendErrors">
|
||||
<div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="legendLabel">Errors</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="legendColorBox">
|
||||
<div class="clearfix legendIcon legendWarnings">
|
||||
<div></div><div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="legendLabel">Warnings</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="legendColorBox">
|
||||
<div class="clearfix legendIcon legendNotices">
|
||||
<div></div><div></div><div></div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="legendLabel">Notices</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<button data-role='zoom-reset' class="btn btn-xs btn-primary pull-right btn-reset hidden">Reset Zoom <i class="glyphicon glyphicon-zoom-out"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user