<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Axe Report</title>
<style>
@import url('https://rsms.me/inter/inter.css');
* {
box-sizing: border-box;
}
html {
font-family: 'Inter', sans-serif;
}
@supports (font-variation-settings: normal) {
html {
font-family: 'Inter var', sans-serif;
}
}
body {
background-color: #f4f5f7;
overflow-x: hidden;
}
p {
margin: 0;
}
.report_main {
margin: 0 auto;
max-width: 56rem;
padding-left: 2rem;
padding-right: 2rem;
}
.report_header {
text-align: center;
}
.report_pages {
margin-top: 2rem;
}
.report_error_wrap {
text-align: center;
}
.form-input {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
border-color: #d2d6dc;
border-width: 1px;
border-radius: .375rem;
padding-top: .5rem;
padding-right: .75rem;
padding-bottom: .5rem;
padding-left: .75rem;
font-size: 1rem;
line-height: 1.5
}
label,
.label {
cursor: pointer;
font-size: .875rem;
}
label span {
display: block;
}
label input {
width: 100%;
margin-top: 0.5rem;
}
.report_filters_row {
display: flex;
flex-flow: row wrap;
margin: 0 -15px;
}
.report_filters_row>* {
flex: 1;
margin-bottom: 15px;
padding: 0 15px;
}
.checkboxes label {
align-items: center;
display: flex;
}
.checkboxes .label {
display: inline-block;
margin-bottom: 0.5rem;
}
.checkboxes label input {
margin-top: 0;
width: auto;
}
.checkboxes label span {
display: inline-block;
margin-left: 10px;
}
.report_pages {
margin-top: 1rem;
}
.report_page {
background-color: #fff;
border-radius: 0.5rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px 0 rgba(0, 0, 0, .06);
margin-bottom: 2rem;
padding: 1rem;
}
.report_item_context {
background-color: rgba(0, 0, 0, .05);
border-radius: 0.5rem;
margin: 10px 0 0;
overflow-x: auto;
padding: 20px;
}
.font-mono {
font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
.report_item_title_link {
color: #DA2315;
}
.report_items_wrap {
border-top: solid #000 1px;
margin-top: 30px;
padding-top: 10px;
}
.report_item_label {
margin-top: 10px;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="axe-app">
<main class="report_main">
<header class="report_header">
<h1 class="report_title" v-text="title"></h1>
</header>
<div class="report_pages">
<div v-if="!results.length">
<div class="report_error_wrap">
<h2 class="report_error_title">Report not yet generated.</h2>
<pre class="report_error_code">
\`*-.
) _`-.
. : `. .
: _ ' \
; *` _. `*-._
`-.-' `-.
; ` `.
:. . \
. \ . : .-' .
' `+.; ; ' :
: ' | ; ;-.
; ' : :`-: _.`* ;
[bug] .*' / .*' ; .*`- +' `*'
`*-* `*-* `*-*'
</pre>
</div>
</div>
<div v-else>
<h2>Filters</h2>
<div class="report_filters">
<div class="report_filters_row">
<label>
<span>Page</span>
<input class="form-input" type="text" v-model="filters.page" />
</label>
<label>
<span>Violation</span>
<input class="form-input" type="text" v-model="filters.violation" />
</label>
</div>
<div class="report_filters_row">
<div class="checkboxes">
<span class="label">Impact</span>
<label v-for="impact in impacts" :key="impact">
<input type="checkbox" :id="`impact_${impact}`" :value="impact" v-model="filters.impact" />
<span v-text="impact"></span>
</label>
</div>
<div class="checkboxes">
<span class="label">Tags</span>
<label v-for="tag in tags" :key="tag">
<input type="checkbox" :id="`tag_${tag}`" :value="tag" v-model="filters.tag" />
<span v-text="tag"></span>
</label>
</div>
</div>
</div>
<pre>Page Count: {{ filteredResults.length }}</pre>
<div class="report_pages">
<div class="report_page" v-for="(page, pageIndex) in filteredResults" :key="`page_${pageIndex}`">
<h2 class="report_page_title" v-text="page.url"></h2>
<pre class="font-mono">Violation Count: {{ page.violations.length }}</pre>
<div class="report_page_body">
<div class="report_page_body">
<div class="report_item" v-for="(violation, violationIndex) in page.violations" :key="`${pageIndex}_${violationIndex}`">
<h3 class="report_item_title">
<a class="report_item_title_link" target="_blank" rel="nofollow" :href="violation.helpUrl" v-text="violation.description"></a>
</h3>
<div class="report_items_wrap">
<p class="report_item_label"><strong>Help</strong></p>
<p class="report_item_context" v-text="violation.help"></p>
<p class="report_item_label"><strong>Context (escaped)</strong></p>
<p class="report_item_context font-mono" v-text="violation.html"></p>
<p class="report_item_label"><strong>Context (raw)</strong></p>
<p class="report_item_context font-mono" v-html="violation.html"></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
</div>
<script>
new Vue({
el: '#axe-app',
data: {
title: 'Axe Report',
filters: {},
results: [],
tags: [],
impacts: [],
ignored: {
pages: ['page-404.html', 'page-form-builder.html'],
violations: [
// 'buttons',
],
},
storage: {
filtersKey: 'fastspot.axe-app.filters',
},
axeJsonFile: {
path: '/axe.json',
},
},
mounted() {
fetch(this.axeJsonFile.path)
.then((data) => data.json())
.then((data) => {
data = this.getNormalizedData(data);
this.results = data.results;
this.tags = data.tags;
this.impacts = data.impacts;
})
.catch((error) => {
console.log(error);
});
this.filters = window.localStorage.getItem(this.storage.filtersKey) ?
JSON.parse(localStorage.getItem(this.storage.filtersKey)) :
this.getFiltersTemplate();
},
watch: {
filters: {
handler() {
window.localStorage.setItem(
this.storage.filtersKey,
JSON.stringify(this.filters),
);
},
deep: true,
},
},
computed: {
filteredResults() {
let results = JSON.parse(JSON.stringify(this.results));
return results
.map((result) => {
result.violations = result.violations.filter(
this.filterViolation,
);
return result;
})
.filter(this.filterResult);
},
},
methods: {
getFiltersTemplate() {
return {
page: '',
violation: '',
impact: [],
tag: [],
};
},
getNormalizedData(results) {
let obj = {
results: [],
tags: [],
impacts: [],
};
results.forEach((result) => {
let url = result.url.split('/')[
result.url.split('/').length - 1
];
let violations = result.violations.map((violation) => ({
help: violation.help,
helpUrl: violation.helpUrl,
description: violation.description,
html: violation.nodes[0].html,
target: violation.nodes[0].target,
impact: violation.impact,
tags: violation.tags,
}));
violations.forEach((violation) => {
obj.tags.push(...violation.tags);
obj.impacts.push(violation.impact);
});
obj.results.push({
url,
fullUrl: result.url,
count: result.violations.length,
violations,
});
});
obj.tags = [...new Set(obj.tags)];
obj.impacts = [...new Set(obj.impacts)];
return obj;
},
resetFilters() {
this.filters = this.getFiltersTemplate();
},
filterViolation(violation) {
if (this.ignored.violations.length) {
let test = this.ignored.violations.some((v) =>
violation.description.includes(v),
);
if (test) return false;
}
let tests = [{
check: this.filters.violation.length,
fn: () =>
violation.description.includes(this.filters.violation),
},
{
check: this.filters.impact.length,
fn: () => this.filters.impact.includes(violation.impact),
},
{
check: this.filters.tag.length,
fn: () =>
this.filters.tag.some((tag) =>
violation.tags.includes(tag),
),
},
].filter((test) => test.check);
return !tests.length ? true : tests.some((test) => test['fn']());
},
filterResult(result) {
if (this.ignored.pages.length) {
if (this.ignored.pages.includes(result.url)) return false;
}
if (!result.violations.length) return false;
let tests = [{
check: this.filters.page.length,
fn: () => result.url.includes(this.filters.page),
}, ].filter((test) => test.check);
return !tests.length ? true : tests.some((test) => test['fn']());
},
},
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Axe Report</title>
<style>
@import url('https://rsms.me/inter/inter.css');
* {
box-sizing: border-box;
}
html { font-family: 'Inter', sans-serif; }
@supports (font-variation-settings: normal) {
html { font-family: 'Inter var', sans-serif; }
}
body {
background-color: #f4f5f7;
overflow-x: hidden;
}
p {
margin: 0;
}
.report_main {
margin: 0 auto;
max-width: 56rem;
padding-left: 2rem;
padding-right: 2rem;
}
.report_header {
text-align: center;
}
.report_pages {
margin-top: 2rem;
}
.report_error_wrap {
text-align: center;
}
.form-input {
-webkit-appearance:none;
-moz-appearance:none;
appearance:none;
background-color:#fff;
border-color:#d2d6dc;
border-width:1px;
border-radius:.375rem;
padding-top:.5rem;
padding-right:.75rem;
padding-bottom:.5rem;
padding-left:.75rem;
font-size:1rem;
line-height:1.5
}
label,
.label {
cursor: pointer;
font-size: .875rem;
}
label span {
display: block;
}
label input {
width: 100%;
margin-top: 0.5rem;
}
.report_filters_row {
display: flex;
flex-flow: row wrap;
margin: 0 -15px;
}
.report_filters_row > * {
flex: 1;
margin-bottom: 15px;
padding: 0 15px;
}
.checkboxes label {
align-items: center;
display: flex;
}
.checkboxes .label {
display: inline-block;
margin-bottom: 0.5rem;
}
.checkboxes label input {
margin-top: 0;
width: auto;
}
.checkboxes label span {
display: inline-block;
margin-left: 10px;
}
.report_pages {
margin-top: 1rem;
}
.report_page {
background-color: #fff;
border-radius: 0.5rem;
box-shadow: 0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px 0 rgba(0,0,0,.06);
margin-bottom: 2rem;
padding: 1rem;
}
.report_item_context {
background-color: rgba(0, 0, 0, .05);
border-radius: 0.5rem;
margin: 10px 0 0;
overflow-x: auto;
padding: 20px;
}
.font-mono {
font-family: Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
}
.report_item_title_link {
color: #DA2315;
}
.report_items_wrap {
border-top: solid #000 1px;
margin-top: 30px;
padding-top: 10px;
}
.report_item_label {
margin-top: 10px;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="axe-app">
{% raw %}
<main class="report_main">
<header class="report_header">
<h1 class="report_title" v-text="title"></h1>
</header>
<div class="report_pages">
<div v-if="!results.length">
<div class="report_error_wrap">
<h2 class="report_error_title">Report not yet generated.</h2>
<pre class="report_error_code">
\`*-.
) _`-.
. : `. .
: _ ' \
; *` _. `*-._
`-.-' `-.
; ` `.
:. . \
. \ . : .-' .
' `+.; ; ' :
: ' | ; ;-.
; ' : :`-: _.`* ;
[bug] .*' / .*' ; .*`- +' `*'
`*-* `*-* `*-*'
</pre>
</div>
</div>
<div v-else>
<h2>Filters</h2>
<div class="report_filters">
<div class="report_filters_row">
<label>
<span>Page</span>
<input class="form-input" type="text" v-model="filters.page" />
</label>
<label>
<span>Violation</span>
<input class="form-input" type="text" v-model="filters.violation" />
</label>
</div>
<div class="report_filters_row">
<div class="checkboxes">
<span class="label">Impact</span>
<label v-for="impact in impacts" :key="impact">
<input type="checkbox" :id="`impact_${impact}`" :value="impact" v-model="filters.impact" />
<span v-text="impact"></span>
</label>
</div>
<div class="checkboxes">
<span class="label">Tags</span>
<label v-for="tag in tags" :key="tag">
<input type="checkbox" :id="`tag_${tag}`" :value="tag" v-model="filters.tag" />
<span v-text="tag"></span>
</label>
</div>
</div>
</div>
<pre>Page Count: {{ filteredResults.length }}</pre>
<div class="report_pages">
<div
class="report_page"
v-for="(page, pageIndex) in filteredResults"
:key="`page_${pageIndex}`"
>
<h2 class="report_page_title" v-text="page.url"></h2>
<pre class="font-mono">Violation Count: {{ page.violations.length }}</pre>
<div class="report_page_body">
<div class="report_page_body">
<div
class="report_item"
v-for="(violation, violationIndex) in page.violations"
:key="`${pageIndex}_${violationIndex}`"
>
<h3 class="report_item_title">
<a
class="report_item_title_link"
target="_blank"
rel="nofollow"
:href="violation.helpUrl"
v-text="violation.description"
></a>
</h3>
<div class="report_items_wrap">
<p class="report_item_label"><strong>Help</strong></p>
<p class="report_item_context" v-text="violation.help"></p>
<p class="report_item_label"><strong>Context (escaped)</strong></p>
<p class="report_item_context font-mono" v-text="violation.html"></p>
<p class="report_item_label"><strong>Context (raw)</strong></p>
<p class="report_item_context font-mono" v-html="violation.html"></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
{% endraw %}
</div>
<script>
new Vue({
el: '#axe-app',
data: {
title: 'Axe Report',
filters: {},
results: [],
tags: [],
impacts: [],
ignored: {
pages: ['page-404.html', 'page-form-builder.html'],
violations: [
// 'buttons',
],
},
storage: {
filtersKey: 'fastspot.axe-app.filters',
},
axeJsonFile: {
path: '/axe.json',
},
},
mounted() {
fetch(this.axeJsonFile.path)
.then((data) => data.json())
.then((data) => {
data = this.getNormalizedData(data);
this.results = data.results;
this.tags = data.tags;
this.impacts = data.impacts;
})
.catch((error) => {
console.log(error);
});
this.filters = window.localStorage.getItem(this.storage.filtersKey)
? JSON.parse(localStorage.getItem(this.storage.filtersKey))
: this.getFiltersTemplate();
},
watch: {
filters: {
handler() {
window.localStorage.setItem(
this.storage.filtersKey,
JSON.stringify(this.filters),
);
},
deep: true,
},
},
computed: {
filteredResults() {
let results = JSON.parse(JSON.stringify(this.results));
return results
.map((result) => {
result.violations = result.violations.filter(
this.filterViolation,
);
return result;
})
.filter(this.filterResult);
},
},
methods: {
getFiltersTemplate() {
return {
page: '',
violation: '',
impact: [],
tag: [],
};
},
getNormalizedData(results) {
let obj = {
results: [],
tags: [],
impacts: [],
};
results.forEach((result) => {
let url = result.url.split('/')[
result.url.split('/').length - 1
];
let violations = result.violations.map((violation) => ({
help: violation.help,
helpUrl: violation.helpUrl,
description: violation.description,
html: violation.nodes[0].html,
target: violation.nodes[0].target,
impact: violation.impact,
tags: violation.tags,
}));
violations.forEach((violation) => {
obj.tags.push(...violation.tags);
obj.impacts.push(violation.impact);
});
obj.results.push({
url,
fullUrl: result.url,
count: result.violations.length,
violations,
});
});
obj.tags = [...new Set(obj.tags)];
obj.impacts = [...new Set(obj.impacts)];
return obj;
},
resetFilters() {
this.filters = this.getFiltersTemplate();
},
filterViolation(violation) {
if (this.ignored.violations.length) {
let test = this.ignored.violations.some((v) =>
violation.description.includes(v),
);
if (test) return false;
}
let tests = [
{
check: this.filters.violation.length,
fn: () =>
violation.description.includes(this.filters.violation),
},
{
check: this.filters.impact.length,
fn: () => this.filters.impact.includes(violation.impact),
},
{
check: this.filters.tag.length,
fn: () =>
this.filters.tag.some((tag) =>
violation.tags.includes(tag),
),
},
].filter((test) => test.check);
return !tests.length ? true : tests.some((test) => test['fn']());
},
filterResult(result) {
if (this.ignored.pages.length) {
if (this.ignored.pages.includes(result.url)) return false;
}
if (!result.violations.length) return false;
let tests = [
{
check: this.filters.page.length,
fn: () => result.url.includes(this.filters.page),
},
].filter((test) => test.check);
return !tests.length ? true : tests.some((test) => test['fn']());
},
},
});
</script>
</body>
</html>
No notes defined.