access_lint 0.0.4 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|