access_lint 0.0.4 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Vendorfile +1 -4
- data/access_lint.gemspec +1 -0
- data/lib/access_lint/audit.rb +5 -7
- data/lib/access_lint/cli.rb +3 -4
- data/lib/access_lint/version.rb +1 -1
- data/spec/access_lint/audit_spec.rb +7 -5
- data/vendor/access-lint/bin/auditor.js +30 -9
- data/vendor/google-chrome/accessibility-developer-tools/gen/axs_testing.js +179 -162
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7ae823b09a9888e551139aa7b9601c3e4a57dc0
|
4
|
+
data.tar.gz: 18648a27bf348a584f520c7adeefea99097b6dda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5da54a7b154e263273922ff4ce69e1e328b9a5b59a1e8f21eee1d004b4d082b82ea548e6b9b487c26af82fb014533252b66f5473898d4e4aa58dc44b755d68d8
|
7
|
+
data.tar.gz: 77dbf7dd4963f49683fdd5ca18c4124787adf57464ebd978c4eab3044d4107321301fd54293f6c4ba0ba0ab92b09af205d7a10521f5370097d1534c0205c593d
|
data/Vendorfile
CHANGED
@@ -1,6 +1,3 @@
|
|
1
1
|
folder 'vendor/google-chrome/accessibility-developer-tools' do
|
2
|
-
|
3
|
-
file 'README.md'
|
4
|
-
folder 'gen'
|
5
|
-
end
|
2
|
+
file 'gen/axs_testing.js', 'https://raw.github.com/GoogleChrome/accessibility-developer-tools/stable/dist/js/axs_testing.js'
|
6
3
|
end
|
data/access_lint.gemspec
CHANGED
data/lib/access_lint/audit.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'access_lint'
|
2
|
+
require 'json'
|
2
3
|
|
3
4
|
module AccessLint
|
4
5
|
class Audit
|
@@ -9,13 +10,10 @@ module AccessLint
|
|
9
10
|
end
|
10
11
|
|
11
12
|
def run
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def create_report
|
18
|
-
%x( phantomjs #{RUNNER_PATH} #{@target})
|
13
|
+
result = `phantomjs #{RUNNER_PATH} #{@target}`
|
14
|
+
if !result.nil?
|
15
|
+
JSON.parse(result)
|
16
|
+
end
|
19
17
|
end
|
20
18
|
end
|
21
19
|
end
|
data/lib/access_lint/cli.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
require 'thor'
|
2
2
|
|
3
3
|
module AccessLint
|
4
|
-
class CLI < Thor
|
5
|
-
desc '
|
6
|
-
|
7
|
-
def audit
|
4
|
+
class CLI < Thor
|
5
|
+
desc 'audit', 'audit TARGET'
|
6
|
+
def audit(target)
|
8
7
|
puts Audit.new(target).run
|
9
8
|
end
|
10
9
|
end
|
data/lib/access_lint/version.rb
CHANGED
@@ -2,12 +2,14 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module AccessLint
|
4
4
|
describe Audit do
|
5
|
+
let(:target) { double(:target) }
|
6
|
+
|
5
7
|
describe '#run' do
|
6
|
-
let(:
|
7
|
-
|
8
|
-
|
9
|
-
audit.
|
10
|
-
audit.
|
8
|
+
let(:audit) { Audit.new(target) }
|
9
|
+
|
10
|
+
it 'runs phantomjs' do
|
11
|
+
audit.should_receive(:`).with(/^phantomjs.*/)
|
12
|
+
audit.run
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
// do stuff
|
2
1
|
var page = require('webpage').create(),
|
3
2
|
system = require('system'),
|
4
3
|
url;
|
@@ -11,18 +10,40 @@ if (system.args.length !== 2) {
|
|
11
10
|
page.open(url, function (status) {
|
12
11
|
if (status === 'success') {
|
13
12
|
page.injectJs('../../google-chrome/accessibility-developer-tools/gen/axs_testing.js');
|
13
|
+
|
14
14
|
var report = page.evaluate(function() {
|
15
|
-
var
|
16
|
-
|
15
|
+
var raw_results = axs.Audit.run();
|
16
|
+
var results = [];
|
17
|
+
|
18
|
+
for(var i = 0; i < raw_results.length; i++) {
|
19
|
+
var element_names = [];
|
20
|
+
var elements = raw_results[i]['elements'];
|
21
|
+
|
22
|
+
if(elements !== undefined) {
|
23
|
+
for(var j = 0; j < elements.length; j++) {
|
24
|
+
var element = elements[j];
|
25
|
+
var tmp = document.createElement("div");
|
26
|
+
tmp.appendChild(element);
|
27
|
+
element_names.push(tmp.innerHTML);
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
results.push({
|
32
|
+
status: raw_results[i]['result'],
|
33
|
+
title: raw_results[i]['rule']['heading'],
|
34
|
+
severity: raw_results[i]['rule']['severity'],
|
35
|
+
element_names: element_names,
|
36
|
+
elements: raw_results[i]['elements']
|
37
|
+
});
|
38
|
+
}
|
39
|
+
|
40
|
+
return results;
|
17
41
|
});
|
18
|
-
|
42
|
+
|
43
|
+
console.log(JSON.stringify(report));
|
19
44
|
phantom.exit();
|
20
45
|
} else {
|
21
|
-
console.log('Failed to load the page at ' +
|
22
|
-
url +
|
23
|
-
". Status was: " +
|
24
|
-
status
|
25
|
-
);
|
46
|
+
console.log('Failed to load the page at ' + url + ".");
|
26
47
|
phantom.exit();
|
27
48
|
}
|
28
49
|
});
|
@@ -1,11 +1,33 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 2013 Google Inc.
|
3
|
+
*
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
* you may not use this file except in compliance with the License.
|
6
|
+
* You may obtain a copy of the License at
|
7
|
+
*
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
*
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
* See the License for the specific language governing permissions and
|
14
|
+
* limitations under the License.
|
15
|
+
*
|
16
|
+
* Generated from http://github.com/GoogleChrome/accessibility-developer-tools/tree/33515adf3dcd6e17d48f8a9dde27c60865545b70
|
17
|
+
*
|
18
|
+
* See project README for build steps.
|
19
|
+
*/
|
20
|
+
|
21
|
+
// AUTO-GENERATED CONTENT BELOW: DO NOT EDIT! See above for details.
|
22
|
+
|
1
23
|
var COMPILED = !0, goog = goog || {};
|
2
24
|
goog.global = this;
|
3
25
|
goog.exportPath_ = function(a, b, c) {
|
4
26
|
a = a.split(".");
|
5
27
|
c = c || goog.global;
|
6
|
-
|
28
|
+
a[0] in c || !c.execScript || c.execScript("var " + a[0]);
|
7
29
|
for(var d;a.length && (d = a.shift());) {
|
8
|
-
|
30
|
+
a.length || void 0 === b ? c = c[d] ? c[d] : c[d] = {} : c[d] = b
|
9
31
|
}
|
10
32
|
};
|
11
33
|
goog.define = function(a, b) {
|
@@ -389,7 +411,7 @@ goog.scope = function(a) {
|
|
389
411
|
var axs = {};
|
390
412
|
axs.browserUtils = {};
|
391
413
|
axs.browserUtils.matchSelector = function(a, b) {
|
392
|
-
return a.webkitMatchesSelector ? a.webkitMatchesSelector(b) : a.mozMatchesSelector(b)
|
414
|
+
return a.webkitMatchesSelector ? a.webkitMatchesSelector(b) : a.mozMatchesSelector ? a.mozMatchesSelector(b) : !1
|
393
415
|
};
|
394
416
|
axs.constants = {};
|
395
417
|
axs.constants.ARIA_ROLES = {alert:{namefrom:["author"], parent:["region"]}, alertdialog:{namefrom:["author"], namerequired:!0, parent:["alert", "dialog"]}, application:{namefrom:["author"], namerequired:!0, parent:["landmark"]}, article:{namefrom:["author"], parent:["document", "region"]}, banner:{namefrom:["author"], parent:["landmark"]}, button:{childpresentational:!0, namefrom:["contents", "author"], namerequired:!0, parent:["command"], properties:["aria-expanded", "aria-pressed"]}, checkbox:{namefrom:["contents",
|
@@ -425,8 +447,7 @@ axs.constants.addAllPropertiesToSet_ = function(a, b, c) {
|
|
425
447
|
}
|
426
448
|
}
|
427
449
|
if(a.parent) {
|
428
|
-
a = a.parent;
|
429
|
-
for(d = 0;d < a.length;d++) {
|
450
|
+
for(a = a.parent, d = 0;d < a.length;d++) {
|
430
451
|
axs.constants.addAllPropertiesToSet_(axs.constants.ARIA_ROLES[a[d]], b, c)
|
431
452
|
}
|
432
453
|
}
|
@@ -519,7 +540,7 @@ axs.utils.elementIsTransparent = function(a) {
|
|
519
540
|
axs.utils.elementHasZeroArea = function(a) {
|
520
541
|
a = a.getBoundingClientRect();
|
521
542
|
var b = a.top - a.bottom;
|
522
|
-
return
|
543
|
+
return a.right - a.left && b ? !1 : !0
|
523
544
|
};
|
524
545
|
axs.utils.elementIsOutsideScrollArea = function(a) {
|
525
546
|
a = a.getBoundingClientRect();
|
@@ -534,19 +555,14 @@ axs.utils.overlappingElement = function(a) {
|
|
534
555
|
return null
|
535
556
|
}
|
536
557
|
var b = a.getBoundingClientRect(), b = document.elementFromPoint((b.left + b.right) / 2, (b.top + b.bottom) / 2);
|
537
|
-
return null
|
558
|
+
return null == b || b == a || axs.utils.isAncestor(b, a) || axs.utils.isAncestor(a, b) ? null : b
|
538
559
|
};
|
539
560
|
axs.utils.elementIsHtmlControl = function(a) {
|
540
561
|
var b = a.ownerDocument.defaultView;
|
541
562
|
return a instanceof b.HTMLButtonElement || a instanceof b.HTMLInputElement || a instanceof b.HTMLSelectElement || a instanceof b.HTMLTextAreaElement ? !0 : !1
|
542
563
|
};
|
543
564
|
axs.utils.elementIsAriaWidget = function(a) {
|
544
|
-
|
545
|
-
if((a = axs.constants.ARIA_ROLES[a]) && "widget" in a.allParentRolesSet) {
|
546
|
-
return!0
|
547
|
-
}
|
548
|
-
}
|
549
|
-
return!1
|
565
|
+
return a.hasAttribute("role") && (a = a.getAttribute("role")) && (a = axs.constants.ARIA_ROLES[a]) && "widget" in a.allParentRolesSet ? !0 : !1
|
550
566
|
};
|
551
567
|
axs.utils.elementIsVisible = function(a) {
|
552
568
|
if(axs.utils.elementIsTransparent(a) || axs.utils.elementHasZeroArea(a) || axs.utils.elementIsOutsideScrollArea(a)) {
|
@@ -666,12 +682,12 @@ axs.utils.suggestColors = function(a, b, c, d) {
|
|
666
682
|
f.contrast = l.toFixed(2);
|
667
683
|
e.AA = f
|
668
684
|
}
|
669
|
-
axs.utils.isLowContrast(c, d, !0) &&
|
685
|
+
axs.utils.isLowContrast(c, d, !0) && 1 >= n && 0 <= n && (n = axs.utils.translateColor(q, n), l = axs.utils.calculateContrastRatio(n, a), f = {}, f.fg = axs.utils.colorToString(n), f.bg = axs.utils.colorToString(a), f.contrast = l.toFixed(2), e.AAA = f);
|
670
686
|
h = axs.utils.luminanceFromContrastRatio(g, h + 0.02, !m);
|
671
687
|
g = axs.utils.luminanceFromContrastRatio(g, k + 0.02, !m);
|
672
688
|
a = axs.utils.toYCC(a);
|
673
|
-
!("AA" in e) &&
|
674
|
-
!("AAA" in e) &&
|
689
|
+
!("AA" in e) && axs.utils.isLowContrast(c, d, !1) && 1 >= h && 0 <= h && (k = axs.utils.translateColor(a, h), l = axs.utils.calculateContrastRatio(b, k), f = {}, f.bg = axs.utils.colorToString(k), f.fg = axs.utils.colorToString(b), f.contrast = l.toFixed(2), e.AA = f);
|
690
|
+
!("AAA" in e) && axs.utils.isLowContrast(c, d, !0) && 1 >= g && 0 <= g && (c = axs.utils.translateColor(a, g), l = axs.utils.calculateContrastRatio(b, c), f = {}, f.bg = axs.utils.colorToString(c), f.fg = axs.utils.colorToString(b), f.contrast = l.toFixed(2), e.AAA = f);
|
675
691
|
return e
|
676
692
|
};
|
677
693
|
axs.utils.flattenColors = function(a, b) {
|
@@ -762,7 +778,7 @@ axs.utils.getContrastRatioForElementWithComputedStyle = function(a, b) {
|
|
762
778
|
return null
|
763
779
|
}
|
764
780
|
var d = axs.utils.getFgColor(a, b, c);
|
765
|
-
return
|
781
|
+
return d ? axs.utils.calculateContrastRatio(d, c) : null
|
766
782
|
};
|
767
783
|
axs.utils.isNativeTextElement = function(a) {
|
768
784
|
var b = a.tagName.toLowerCase();
|
@@ -893,7 +909,7 @@ axs.utils.isValidTokenValue = function(a, b) {
|
|
893
909
|
return axs.utils.isPossibleValue(b, axs.constants.ARIA_PROPERTIES[c].valuesSet, a)
|
894
910
|
};
|
895
911
|
axs.utils.isPossibleValue = function(a, b, c) {
|
896
|
-
return
|
912
|
+
return b[a] ? {valid:!0, value:a} : {valid:!1, value:a, reason:'"' + a + '" is not a valid value for ' + c, possibleValues:Object.keys(b)}
|
897
913
|
};
|
898
914
|
axs.utils.isValidBoolean = function(a) {
|
899
915
|
try {
|
@@ -904,7 +920,7 @@ axs.utils.isValidBoolean = function(a) {
|
|
904
920
|
return"boolean" != typeof b ? {valid:!1, value:a, reason:'"' + a + '" is not a true/false value'} : {valid:!0, value:b}
|
905
921
|
};
|
906
922
|
axs.utils.isValidIDRefValue = function(a, b) {
|
907
|
-
return 0 == a.length ? {valid:!0, idref:a} :
|
923
|
+
return 0 == a.length ? {valid:!0, idref:a} : b.ownerDocument.getElementById(a) ? {valid:!0, idref:a} : {valid:!1, idref:a, reason:'No element with ID "' + a + '"'}
|
908
924
|
};
|
909
925
|
axs.utils.isValidNumber = function(a) {
|
910
926
|
try {
|
@@ -989,19 +1005,20 @@ axs.properties.getColorProperties = function(a) {
|
|
989
1005
|
(a = axs.properties.getContrastRatioProperties(a)) && (b.contrastRatio = a);
|
990
1006
|
return 0 == Object.keys(b).length ? null : b
|
991
1007
|
};
|
992
|
-
axs.properties.
|
993
|
-
for(var b =
|
1008
|
+
axs.properties.hasDirectTextDescendant = function(a) {
|
1009
|
+
for(var b = (a.nodeType == Node.DOCUMENT_NODE ? a : a.ownerDocument).evaluate(axs.properties.TEXT_CONTENT_XPATH, a, null, XPathResult.ANY_TYPE, null), c = !1, d = b.iterateNext();null != d;d = b.iterateNext()) {
|
994
1010
|
if(d === a) {
|
995
1011
|
c = !0;
|
996
1012
|
break
|
997
1013
|
}
|
998
1014
|
}
|
999
|
-
|
1015
|
+
return c
|
1016
|
+
};
|
1017
|
+
axs.properties.getContrastRatioProperties = function(a) {
|
1018
|
+
if(!axs.properties.hasDirectTextDescendant(a)) {
|
1000
1019
|
return null
|
1001
1020
|
}
|
1002
|
-
b = {};
|
1003
|
-
c = window.getComputedStyle(a, null);
|
1004
|
-
d = axs.utils.getBgColor(c, a);
|
1021
|
+
var b = {}, c = window.getComputedStyle(a, null), d = axs.utils.getBgColor(c, a);
|
1005
1022
|
if(!d) {
|
1006
1023
|
return null
|
1007
1024
|
}
|
@@ -1014,7 +1031,7 @@ axs.properties.getContrastRatioProperties = function(a) {
|
|
1014
1031
|
}
|
1015
1032
|
b.value = a.toFixed(2);
|
1016
1033
|
axs.utils.isLowContrast(a, c) && (b.alert = !0);
|
1017
|
-
(
|
1034
|
+
(c = axs.utils.suggestColors(d, e, a, c)) && Object.keys(c).length && (b.suggestedColors = c);
|
1018
1035
|
return b
|
1019
1036
|
};
|
1020
1037
|
axs.properties.findTextAlternatives = function(a, b, c) {
|
@@ -1032,18 +1049,10 @@ axs.properties.findTextAlternatives = function(a, b, c) {
|
|
1032
1049
|
var e = {type:"text"};
|
1033
1050
|
e.text = c.getAttribute("aria-label");
|
1034
1051
|
e.lastWord = axs.properties.getLastWord(e.text);
|
1035
|
-
|
1036
|
-
e.unused = !0
|
1037
|
-
}else {
|
1038
|
-
if(!d || !axs.utils.elementIsHtmlControl(c)) {
|
1039
|
-
a = e.text
|
1040
|
-
}
|
1041
|
-
}
|
1052
|
+
a ? e.unused = !0 : d && axs.utils.elementIsHtmlControl(c) || (a = e.text);
|
1042
1053
|
b.ariaLabel = e
|
1043
1054
|
}
|
1044
|
-
|
1045
|
-
a = axs.properties.getTextFromHostLangaugeAttributes(c, b, a)
|
1046
|
-
}
|
1055
|
+
c.hasAttribute("role") && "presentation" == c.getAttribute("role") || (a = axs.properties.getTextFromHostLangaugeAttributes(c, b, a));
|
1047
1056
|
if(d && axs.utils.elementIsHtmlControl(c)) {
|
1048
1057
|
e = c.ownerDocument.defaultView;
|
1049
1058
|
if(c instanceof e.HTMLInputElement) {
|
@@ -1262,49 +1271,49 @@ axs.AuditRule = function(a) {
|
|
1262
1271
|
}
|
1263
1272
|
this.name = a.name;
|
1264
1273
|
this.severity = a.severity;
|
1265
|
-
this.
|
1274
|
+
this.relevantElementMatcher_ = a.relevantElementMatcher;
|
1266
1275
|
this.test_ = a.test;
|
1267
1276
|
this.code = a.code;
|
1268
1277
|
this.heading = a.heading || "";
|
1269
1278
|
this.url = a.url || "";
|
1270
1279
|
this.requiresConsoleAPI = !!a.opt_requiresConsoleAPI
|
1271
1280
|
};
|
1272
|
-
axs.AuditRule.requiredFields = "name severity
|
1281
|
+
axs.AuditRule.requiredFields = "name severity relevantElementMatcher test code heading".split(" ");
|
1273
1282
|
axs.AuditRule.NOT_APPLICABLE = {result:axs.constants.AuditResult.NA};
|
1274
|
-
axs.AuditRule.prototype.
|
1283
|
+
axs.AuditRule.prototype.addElement = function(a, b) {
|
1275
1284
|
a.push(b)
|
1276
1285
|
};
|
1277
|
-
axs.AuditRule.
|
1278
|
-
|
1279
|
-
|
1280
|
-
if(axs.browserUtils.matchSelector(a, d[b])) {
|
1281
|
-
return!0
|
1282
|
-
}
|
1283
|
-
}
|
1284
|
-
return!1
|
1286
|
+
axs.AuditRule.collectMatchingElements = function(a, b, c) {
|
1287
|
+
if(a.nodeType == Node.ELEMENT_NODE) {
|
1288
|
+
var d = a
|
1285
1289
|
}
|
1286
|
-
|
1287
|
-
|
1288
|
-
|
1289
|
-
|
1290
|
-
|
1291
|
-
|
1292
|
-
|
1293
|
-
|
1294
|
-
|
1290
|
+
d && b.call(null, d) && c.push(d);
|
1291
|
+
for(a = a.firstChild;null != a;) {
|
1292
|
+
axs.AuditRule.collectMatchingElements(a, b, c), a = a.nextSibling
|
1293
|
+
}
|
1294
|
+
};
|
1295
|
+
axs.AuditRule.prototype.run = function(a, b) {
|
1296
|
+
var c = a || [], d = [];
|
1297
|
+
axs.AuditRule.collectMatchingElements(b || document, this.relevantElementMatcher_, d);
|
1298
|
+
var e = [];
|
1299
|
+
if(!d.length) {
|
1300
|
+
return{result:axs.constants.AuditResult.NA}
|
1301
|
+
}
|
1302
|
+
for(var f = 0;f < d.length;f++) {
|
1303
|
+
var g = d[f], h;
|
1304
|
+
a: {
|
1305
|
+
h = g;
|
1306
|
+
for(var k = 0;k < c.length;k++) {
|
1307
|
+
if(axs.browserUtils.matchSelector(h, c[k])) {
|
1308
|
+
h = !0;
|
1309
|
+
break a
|
1310
|
+
}
|
1295
1311
|
}
|
1296
|
-
|
1297
|
-
return console.warn("Unknown XPath result type", e), null
|
1298
|
-
}
|
1299
|
-
}else {
|
1300
|
-
if(!e.length) {
|
1301
|
-
return{result:axs.constants.AuditResult.NA}
|
1302
|
-
}
|
1303
|
-
for(g = 0;g < e.length;g++) {
|
1304
|
-
h = e[g], this.test_(h) && !c(h) && this.addNode(f, h)
|
1312
|
+
h = !1
|
1305
1313
|
}
|
1314
|
+
!h && this.test_(g) && this.addElement(e, g)
|
1306
1315
|
}
|
1307
|
-
return{result:
|
1316
|
+
return{result:e.length ? axs.constants.AuditResult.FAIL : axs.constants.AuditResult.PASS, elements:e}
|
1308
1317
|
};
|
1309
1318
|
axs.AuditRule.specs = {};
|
1310
1319
|
axs.AuditRules = {};
|
@@ -1429,71 +1438,94 @@ axs.Audit.accessibilityErrorMessage = function(a) {
|
|
1429
1438
|
return b
|
1430
1439
|
};
|
1431
1440
|
goog.exportSymbol("axs.Audit.accessibilityErrorMessage", axs.Audit.accessibilityErrorMessage);
|
1432
|
-
axs.AuditRule.specs.
|
1433
|
-
return
|
1434
|
-
}, test:function(a) {
|
1435
|
-
return axs.utils.isInlineElement(a) || 50 > axs.properties.findTextAlternatives(a, {}).length ? !0 : !1
|
1436
|
-
}, code:"AX_ARIA_04"};
|
1437
|
-
axs.AuditRule.specs.audioWithoutControls = {name:"audioWithoutControls", heading:"Audio elements should have controls", url:"", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
|
1438
|
-
return a.querySelectorAll("audio[autoplay]")
|
1441
|
+
axs.AuditRule.specs.audioWithoutControls = {name:"audioWithoutControls", heading:"Audio elements should have controls", url:"", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
|
1442
|
+
return axs.browserUtils.matchSelector(a, "audio[autoplay]")
|
1439
1443
|
}, test:function(a) {
|
1440
1444
|
return!a.querySelectorAll("[controls]").length && 3 < a.duration
|
1441
1445
|
}, code:"AX_AUDIO_01"};
|
1442
|
-
axs.AuditRule.specs.
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
if(!a) {
|
1447
|
-
return!0
|
1446
|
+
axs.AuditRule.specs.badAriaAttributeValue = {name:"badAriaAttributeValue", heading:"ARIA state and property values must be valid", url:"", severity:axs.constants.Severity.SEVERE, relevantElementMatcher:function(a) {
|
1447
|
+
var b = "", c;
|
1448
|
+
for(c in axs.constants.ARIA_PROPERTIES) {
|
1449
|
+
b += "[aria-" + c + "],"
|
1448
1450
|
}
|
1449
|
-
|
1450
|
-
return
|
1451
|
-
}, code:"AX_TITLE_01"};
|
1452
|
-
axs.AuditRule.specs.lowContrastElements = {name:"lowContrastElements", heading:"Text elements should have a reasonable contrast ratio", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_color_01--text-elements-should-have-a-reasonable-contrast-ratio", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
|
1453
|
-
return document.evaluate('.//text()[normalize-space(.)!=""]/parent::*[name()!="script"]', a, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
|
1454
|
-
}, test:function(a) {
|
1455
|
-
var b = window.getComputedStyle(a, null);
|
1456
|
-
return(a = axs.utils.getContrastRatioForElementWithComputedStyle(b, a)) && axs.utils.isLowContrast(a, b)
|
1457
|
-
}, code:"AX_COLOR_01"};
|
1458
|
-
axs.AuditRule.specs.nonExistentAriaLabelledbyElement = {name:"nonExistentAriaLabelledbyElement", heading:"aria-labelledby attributes should refer to an element which exists in the DOM", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_aria_02--aria-labelledby-attributes-should-refer-to-an-element-which-exists-in-the-dom", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
|
1459
|
-
return a.querySelectorAll("[aria-labelledby]")
|
1451
|
+
b = b.substring(0, b.length - 1);
|
1452
|
+
return axs.browserUtils.matchSelector(a, b)
|
1460
1453
|
}, test:function(a) {
|
1461
|
-
|
1462
|
-
|
1463
|
-
if(
|
1464
|
-
|
1454
|
+
for(var b in axs.constants.ARIA_PROPERTIES) {
|
1455
|
+
var c = "aria-" + b;
|
1456
|
+
if(a.hasAttribute(c)) {
|
1457
|
+
var d = a.getAttribute(c);
|
1458
|
+
if(!axs.utils.getAriaPropertyValue(c, d, a).valid) {
|
1459
|
+
return!0
|
1460
|
+
}
|
1465
1461
|
}
|
1466
1462
|
}
|
1467
1463
|
return!1
|
1468
|
-
}, code:"
|
1469
|
-
axs.AuditRule.specs.
|
1470
|
-
return
|
1464
|
+
}, code:"AX_ARIA_04"};
|
1465
|
+
axs.AuditRule.specs.badAriaRole = {name:"badAriaRole", heading:"Elements with ARIA roles must use a valid, non-abstract ARIA role", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_aria_01--elements-with-aria-roles-must-use-a-valid-non-abstract-aria-role", severity:axs.constants.Severity.SEVERE, relevantElementMatcher:function(a) {
|
1466
|
+
return axs.browserUtils.matchSelector(a, "[role]")
|
1471
1467
|
}, test:function(a) {
|
1472
|
-
|
1473
|
-
|
1468
|
+
return!axs.utils.getRoles(a).valid
|
1469
|
+
}, code:"AX_ARIA_01"};
|
1470
|
+
axs.AuditRule.specs.controlsWithoutLabel = {name:"controlsWithoutLabel", heading:"Controls and media elements should have labels", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_text_01--controls-and-media-elements-should-have-labels", severity:axs.constants.Severity.SEVERE, relevantElementMatcher:function(a) {
|
1471
|
+
if(!axs.browserUtils.matchSelector(a, 'input:not([type="hidden"]):not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), video:not([disabled])')) {
|
1474
1472
|
return!1
|
1475
1473
|
}
|
1476
|
-
|
1477
|
-
|
1478
|
-
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1474
|
+
if(0 <= a.tabIndex) {
|
1475
|
+
return!0
|
1476
|
+
}
|
1477
|
+
for(a = a.parentElement;null != a;a = a.parentElement) {
|
1478
|
+
if(axs.utils.elementIsAriaWidget(a)) {
|
1479
|
+
return!1
|
1482
1480
|
}
|
1483
1481
|
}
|
1484
|
-
|
1485
|
-
|
1486
|
-
return a.
|
1482
|
+
return!0
|
1483
|
+
}, test:function(a) {
|
1484
|
+
return axs.utils.isElementOrAncestorHidden(a) || "input" == a.tagName.toLowerCase() && "button" == a.type && a.value.length || "button" == a.tagName.toLowerCase() && a.textContent.replace(/^\s+|\s+$/g, "").length ? !1 : axs.utils.hasLabel(a) ? !1 : !0
|
1485
|
+
}, code:"AX_TEXT_01", ruleName:"Controls and media elements should have labels"};
|
1486
|
+
axs.AuditRule.specs.focusableElementNotVisibleAndNotAriaHidden = {name:"focusableElementNotVisibleAndNotAriaHidden", heading:"These elements are focusable but either invisible or obscured by another element", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_focus_01--these-elements-are-focusable-but-either-invisible-or-obscured-by-another-element", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
|
1487
|
+
if(!axs.browserUtils.matchSelector(a, axs.utils.FOCUSABLE_ELEMENTS_SELECTOR)) {
|
1488
|
+
return!1
|
1489
|
+
}
|
1490
|
+
if(0 <= a.tabIndex) {
|
1491
|
+
return!0
|
1492
|
+
}
|
1493
|
+
for(a = a.parentElement;null != a;a = a.parentElement) {
|
1494
|
+
if(axs.utils.elementIsAriaWidget(a)) {
|
1495
|
+
return!1
|
1496
|
+
}
|
1497
|
+
}
|
1498
|
+
return!0
|
1487
1499
|
}, test:function(a) {
|
1488
1500
|
return axs.utils.isElementOrAncestorHidden(a) ? !1 : !axs.utils.elementIsVisible(a)
|
1489
1501
|
}, code:"AX_FOCUS_01"};
|
1490
|
-
axs.AuditRule.specs.
|
1491
|
-
|
1492
|
-
|
1493
|
-
|
1494
|
-
|
1502
|
+
axs.AuditRule.specs.imagesWithoutAltText = {name:"imagesWithoutAltText", heading:"Images should have an alt attribute", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_text_02--images-should-have-an-alt-attribute-unless-they-have-an-aria-role-of-presentation", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
|
1503
|
+
return axs.browserUtils.matchSelector(a, "img") && !axs.utils.isElementOrAncestorHidden(a)
|
1504
|
+
}, test:function(a) {
|
1505
|
+
return!a.hasAttribute("alt") && "presentation" != a.getAttribute("role")
|
1506
|
+
}, code:"AX_TEXT_02"};
|
1507
|
+
axs.AuditRule.specs.linkWithUnclearPurpose = {name:"linkWithUnclearPurpose", heading:"The purpose of each link should be clear from the link text", url:"", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
|
1508
|
+
return axs.browserUtils.matchSelector(a, "a")
|
1509
|
+
}, test:function(a) {
|
1510
|
+
return/^\s*click\s*here\s*[^a-z]?$/i.test(a.textContent)
|
1511
|
+
}, code:"AX_TITLE_01"};
|
1512
|
+
axs.AuditRule.specs.lowContrastElements = {name:"lowContrastElements", heading:"Text elements should have a reasonable contrast ratio", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_color_01--text-elements-should-have-a-reasonable-contrast-ratio", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
|
1513
|
+
return axs.properties.hasDirectTextDescendant(a)
|
1514
|
+
}, test:function(a) {
|
1515
|
+
var b = window.getComputedStyle(a, null);
|
1516
|
+
return(a = axs.utils.getContrastRatioForElementWithComputedStyle(b, a)) && axs.utils.isLowContrast(a, b)
|
1517
|
+
}, code:"AX_COLOR_01"};
|
1518
|
+
axs.AuditRule.specs.mainRoleOnInappropriateElement = {name:"mainRoleOnInappropriateElement", heading:"role=main should only appear on significant elements", url:"", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
|
1519
|
+
return axs.browserUtils.matchSelector(a, "[role~=main]")
|
1520
|
+
}, test:function(a) {
|
1521
|
+
if(axs.utils.isInlineElement(a)) {
|
1522
|
+
return!0
|
1495
1523
|
}
|
1496
|
-
|
1524
|
+
a = axs.properties.findTextAlternatives(a, {});
|
1525
|
+
return!a || 50 > a.length ? !0 : !1
|
1526
|
+
}, code:"AX_ARIA_04"};
|
1527
|
+
axs.AuditRule.specs.elementsWithMeaningfulBackgroundImage = {name:"elementsWithMeaningfulBackgroundImage", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
|
1528
|
+
return!axs.utils.isElementOrAncestorHidden(a)
|
1497
1529
|
}, heading:"Meaningful images should not be used in element backgrounds", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_image_01--meaningful-images-should-not-be-used-in-element-backgrounds", test:function(a) {
|
1498
1530
|
if(a.textContent && 0 < a.textContent.length) {
|
1499
1531
|
return!1
|
@@ -1507,63 +1539,48 @@ axs.AuditRule.specs.elementsWithMeaningfulBackgroundImage = {name:"elementsWithM
|
|
1507
1539
|
a = parseInt(a.height, 10);
|
1508
1540
|
return 150 > b && 150 > a
|
1509
1541
|
}, code:"AX_IMAGE_01"};
|
1510
|
-
axs.AuditRule.specs.
|
1511
|
-
return
|
1542
|
+
axs.AuditRule.specs.nonExistentAriaLabelledbyElement = {name:"nonExistentAriaLabelledbyElement", heading:"aria-labelledby attributes should refer to an element which exists in the DOM", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_aria_02--aria-labelledby-attributes-should-refer-to-an-element-which-exists-in-the-dom", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
|
1543
|
+
return axs.browserUtils.matchSelector(a, "[aria-labelledby]")
|
1512
1544
|
}, test:function(a) {
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1545
|
+
a = a.getAttribute("aria-labelledby").split(/\s+/);
|
1546
|
+
for(var b = 0;b < a.length;b++) {
|
1547
|
+
if(!document.getElementById(a[b])) {
|
1548
|
+
return!0
|
1549
|
+
}
|
1550
|
+
}
|
1551
|
+
return!1
|
1552
|
+
}, code:"AX_ARIA_02"};
|
1553
|
+
axs.AuditRule.specs.pageWithoutTitle = {name:"pageWithoutTitle", heading:"The web page should have a title that describes topic or purpose", url:"", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
|
1554
|
+
return"html" == a.tagName.toLowerCase()
|
1517
1555
|
}, test:function(a) {
|
1518
|
-
|
1519
|
-
|
1520
|
-
|
1521
|
-
|
1556
|
+
a = a.querySelector("head");
|
1557
|
+
return a ? (a = a.querySelector("title")) ? !a.textContent : !0 : !0
|
1558
|
+
}, code:"AX_TITLE_01"};
|
1559
|
+
axs.AuditRule.specs.requiredAriaAttributeMissing = {name:"requiredAriaAttributeMissing", heading:"Elements with ARIA roles must have all required attributes for that role", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_aria_03--elements-with-aria-roles-must-have-all-required-attributes-for-that-role", severity:axs.constants.Severity.SEVERE, relevantElementMatcher:function(a) {
|
1560
|
+
return axs.browserUtils.matchSelector(a, "[role]")
|
1522
1561
|
}, test:function(a) {
|
1523
|
-
|
1524
|
-
|
1525
|
-
|
1526
|
-
var b = "", c;
|
1527
|
-
for(c in axs.constants.ARIA_PROPERTIES) {
|
1528
|
-
b += "[aria-" + c + "],"
|
1562
|
+
var b = axs.utils.getRoles(a);
|
1563
|
+
if(!b.valid) {
|
1564
|
+
return!1
|
1529
1565
|
}
|
1530
|
-
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
var c = "aria-" + b;
|
1535
|
-
if(a.hasAttribute(c)) {
|
1536
|
-
var d = a.getAttribute(c);
|
1537
|
-
if(!axs.utils.getAriaPropertyValue(c, d, a).valid) {
|
1566
|
+
for(var c = 0;c < b.roles.length;c++) {
|
1567
|
+
var d = b.roles[c].details.requiredPropertiesSet, e;
|
1568
|
+
for(e in d) {
|
1569
|
+
if(d = e.replace(/^aria-/, ""), !("defaultValue" in axs.constants.ARIA_PROPERTIES[d] || a.hasAttribute(e))) {
|
1538
1570
|
return!0
|
1539
1571
|
}
|
1540
1572
|
}
|
1541
1573
|
}
|
1542
|
-
|
1543
|
-
|
1544
|
-
|
1545
|
-
return a.querySelectorAll("[role]")
|
1546
|
-
}, test:function(a) {
|
1547
|
-
return!axs.utils.getRoles(a).valid
|
1548
|
-
}, code:"AX_ARIA_01"};
|
1549
|
-
axs.AuditRule.specs.imagesWithoutAltText = {name:"imagesWithoutAltText", heading:"Images should have an alt attribute", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_text_02--images-should-have-an-alt-attribute-unless-they-have-an-aria-role-of-presentation", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
|
1550
|
-
a = a.querySelectorAll("img");
|
1551
|
-
for(var b = [], c = 0;c < a.length;c++) {
|
1552
|
-
var d = a[c];
|
1553
|
-
axs.utils.isElementOrAncestorHidden(d) || b.push(d)
|
1554
|
-
}
|
1555
|
-
return b
|
1556
|
-
}, test:function(a) {
|
1557
|
-
return!a.hasAttribute("alt") && "presentation" != a.getAttribute("role")
|
1558
|
-
}, code:"AX_TEXT_02"};
|
1559
|
-
axs.AuditRule.specs.unfocusableElementsWithOnClick = {name:"unfocusableElementsWithOnClick", heading:"Elements with onclick handlers must be focusable", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_focus_02--elements-with-onclick-handlers-must-be-focusable", severity:axs.constants.Severity.WARNING, opt_requiresConsoleAPI:!0, relevantNodesSelector:function(a) {
|
1560
|
-
a = a.querySelectorAll("*");
|
1561
|
-
for(var b = [], c = 0;c < a.length;c++) {
|
1562
|
-
var d = a[c];
|
1563
|
-
d instanceof d.ownerDocument.defaultView.HTMLBodyElement || axs.utils.isElementOrAncestorHidden(d) || "click" in getEventListeners(d) && b.push(d)
|
1564
|
-
}
|
1565
|
-
return b
|
1574
|
+
}, code:"AX_ARIA_03"};
|
1575
|
+
axs.AuditRule.specs.unfocusableElementsWithOnClick = {name:"unfocusableElementsWithOnClick", heading:"Elements with onclick handlers must be focusable", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_focus_02--elements-with-onclick-handlers-must-be-focusable", severity:axs.constants.Severity.WARNING, opt_requiresConsoleAPI:!0, relevantElementMatcher:function(a) {
|
1576
|
+
return a instanceof a.ownerDocument.defaultView.HTMLBodyElement || axs.utils.isElementOrAncestorHidden(a) && "selected" == a.className ? !1 : "click" in getEventListeners(a) ? !0 : !1
|
1566
1577
|
}, test:function(a) {
|
1567
1578
|
return!a.hasAttribute("tabindex") && !axs.utils.isElementImplicitlyFocusable(a)
|
1568
1579
|
}, code:"AX_FOCUS_02"};
|
1580
|
+
axs.AuditRule.specs.videoWithoutCaptions = {name:"videoWithoutCaptions", heading:"Video elements should use <track> elements to provide captions", url:"https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#-ax_video_01--video-elements-should-use-track-elements-to-provide-captions", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
|
1581
|
+
return axs.browserUtils.matchSelector(a, "video")
|
1582
|
+
}, test:function(a) {
|
1583
|
+
return!a.querySelectorAll("track[kind=captions]").length
|
1584
|
+
}, code:"AX_VIDEO_01"};
|
1585
|
+
|
1569
1586
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: access_lint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Cameron Cundiff
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-11-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: json
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description: Run an accessibility audit on a file or URL from the command line.
|
56
70
|
email:
|
57
71
|
- ckundo@gmail.com
|