capybara-accessible 0.1.9 → 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 01fb309e03c6d938d6ef7b42cb937ff39c61716f
4
- data.tar.gz: c7d6487f9c68c51986ed954558529df1a6bfa0f3
3
+ metadata.gz: 7ff9e45488ea2958d4b85cd7d49dd4cdaa5dafd3
4
+ data.tar.gz: ca5d1c374682e24af3fd5762fd9af8486420d163
5
5
  SHA512:
6
- metadata.gz: 534b30513f7afe122e9c78f9e900e798830f798477dd202c4d823db5fbe0adb939a5b891ddd98680bc97c6675f82cb8bc2f649de62d3c6ab89091b765fe8d68d
7
- data.tar.gz: f324fa20ecb8b46c9940175266bd532b58beaa49766bb821603931c923e17606bb52ff063a9d168e1d7a81f60fc614a515e604222dd98d8426b64b7618b02219
6
+ metadata.gz: 4bdc8ffa7b1d86ce045f804938b804b579e404b662a812e33e06d7064c3664215fd1d2fb98b326c9eca2d3a7bbc09c9e2830cf9b8bdd81b3f2f5c6feb366180a
7
+ data.tar.gz: 9350403adcb506559e44ab58d716b526ed3607ce90c128cc466787454f649b9f792728566e8df290812afe9cfb345cb58c698aa9a072a726a16c585b02e62895
data/.gitignore CHANGED
@@ -2,6 +2,7 @@
2
2
  *.rbc
3
3
  .bundle
4
4
  .config
5
+ .rspec
5
6
  .yardoc
6
7
  Gemfile.lock
7
8
  InstalledFiles
data/README.md CHANGED
@@ -87,6 +87,14 @@ You can disable audits on individual tests by tagging the example or group with
87
87
  Then I should see the inaccessible image # this assertion will still be executed
88
88
 
89
89
 
90
+ ### Changing the severity of audit rules
91
+
92
+ If you'd like to enforce certain rules and raise errors instead of showing them as warnings,
93
+ for example images should never have alt attributes, you can configure it as follows:
94
+
95
+ Capybara::Accessible::Auditor.severe_rules = ['AX_TEXT_02']
96
+
97
+
90
98
  ## Support
91
99
 
92
100
  If you think you've found a bug, or have installation questions or feature requests, please send a message
data/Rakefile CHANGED
@@ -5,7 +5,6 @@ Bundler::GemHelper.install_tasks
5
5
 
6
6
  RSpec::Core::RakeTask.new do |t|
7
7
  t.pattern = "spec/**/*_spec.rb"
8
- t.rspec_opts = "--format progress"
9
8
  end
10
9
 
11
10
  desc "Default: run all specs"
@@ -2,6 +2,7 @@ require 'capybara'
2
2
  require 'capybara/accessible/auditor'
3
3
  require 'capybara/accessible/element'
4
4
  require "capybara/accessible/version"
5
+ require "capybara/accessible/railtie" if defined?(Rails)
5
6
 
6
7
  module Capybara
7
8
  module Accessible
@@ -27,6 +27,14 @@ module Capybara::Accessible
27
27
  @@log_level ||= :error
28
28
  end
29
29
 
30
+ def self.severe_rules=(rules)
31
+ @@severe_rules = rules
32
+ end
33
+
34
+ def self.severe_rules
35
+ @@severe_rules ||= []
36
+ end
37
+
30
38
  def audit_rules
31
39
  File.read(File.expand_path("../../../vendor/google/accessibility-developer-tools/axs_testing.js", __FILE__))
32
40
  end
@@ -49,14 +57,30 @@ module Capybara::Accessible
49
57
  <<-JAVASCRIPT
50
58
  #{audit_rules}
51
59
  var config = new axs.AuditConfiguration();
60
+ var severe_rules = #{severe_rules.to_json};
61
+ var rule;
62
+
63
+ for(rule in severe_rules) {
64
+ config.setSeverity(severe_rules[rule], axs.constants.Severity.SEVERE);
65
+ }
52
66
  config.auditRulesToIgnore = #{excluded_rules.to_json};
67
+
53
68
  var results = axs.Audit.run(config);
54
69
  JAVASCRIPT
55
70
  end
56
71
 
57
72
  def excluded_rules
58
73
  codes = Capybara::Accessible::Auditor.exclusions
59
- mapping = {
74
+ codes.map { |code| mapping[code]}
75
+ end
76
+
77
+ def severe_rules
78
+ codes = Capybara::Accessible::Auditor.severe_rules
79
+ codes.map { |code| mapping[code]}
80
+ end
81
+
82
+ def mapping
83
+ @mapping ||= {
60
84
  'AX_ARIA_01' => 'badAriaRole',
61
85
  'AX_ARIA_02' => 'nonExistentAriaLabelledbyElement',
62
86
  'AX_ARIA_03' => 'requiredAriaAttributeMissing',
@@ -73,8 +97,6 @@ module Capybara::Accessible
73
97
  # 'AX_TITLE_01' => 'linkWithUnclearPurpose', # This has a duplicate name
74
98
  # 'AX_ARIA_05' => '', # This has no rule associated with it
75
99
  }
76
-
77
- codes.map { |code| mapping[code]}
78
100
  end
79
101
 
80
102
  def page_url
@@ -0,0 +1,11 @@
1
+ require 'rails/railtie'
2
+
3
+ module Capybara
4
+ module Accessible
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ load 'capybara/accessible/tasks.rb'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ require 'csv'
2
+ require 'rake'
3
+ require 'capybara/accessible'
4
+
5
+ namespace :capybara_accessible do
6
+
7
+ desc 'Report total number of integration tests that are accessible/inaccessible'
8
+ task :report_inaccessible_tests do
9
+ directories_to_exclude = ['features/support']
10
+ directories = Dir['features/**'].concat(Dir['spec/features/**']) - directories_to_exclude
11
+ directories.map! { |d| [d.split('/').last, d]}
12
+ directories.sort!
13
+
14
+ total_inaccessible_tests = 0
15
+ total_tests = 0
16
+ CSV.open("output_#{DateTime.now.strftime("%Y%m%dT%H%M")}.csv", 'w') do |csv|
17
+ csv<<['Module', 'Test Folder Path', 'Inaccessible Tests', 'Accessible Tests', 'Total Tests']
18
+
19
+ directories.each do |key, directory|
20
+ if directory.split('/').first == 'features'
21
+ inaccessible_count = `git grep "@inaccessible" #{directory} | wc -l`
22
+ total_count = `git grep "Scenario" #{directory} | wc -l`
23
+ else
24
+ inaccessible_count = `git grep "inaccessible.*true" #{directory} | wc -l`
25
+ total_count = `git grep "scenario.*do" #{directory} | wc -l`
26
+ end
27
+ total_inaccessible_tests += inaccessible_count.to_i
28
+ total_tests += total_count.to_i
29
+
30
+ csv << ["#{key.upcase}", "#{directory}", inaccessible_count.to_i, (total_count.to_i - inaccessible_count.to_i), total_count.to_i]
31
+ end
32
+ csv << ['TOTALS', '', total_inaccessible_tests, total_tests - total_inaccessible_tests, total_tests]
33
+ end
34
+ end
35
+ end
@@ -1,5 +1,5 @@
1
1
  module Capybara
2
2
  module Accessible
3
- VERSION = "0.1.9"
3
+ VERSION = '0.1.10'.freeze
4
4
  end
5
5
  end
@@ -1,3 +1,25 @@
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/6e426d3e4e38b611bc013c14c2ade77674397627
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) {
@@ -387,6 +409,10 @@ goog.scope = function(a) {
387
409
  a.call(goog.global)
388
410
  };
389
411
  var axs = {};
412
+ axs.browserUtils = {};
413
+ axs.browserUtils.matchSelector = function(a, b) {
414
+ return a.webkitMatchesSelector ? a.webkitMatchesSelector(b) : a.mozMatchesSelector ? a.mozMatchesSelector(b) : !1
415
+ };
390
416
  axs.constants = {};
391
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",
392
418
  "author"], namerequired:!0, parent:["input"], requiredProperties:["aria-checked"], properties:["aria-checked"]}, columnheader:{namefrom:["contents", "author"], namerequired:!0, parent:["gridcell", "sectionhead", "widget"], properties:["aria-sort"]}, combobox:{mustcontain:["listbox", "textbox"], namefrom:["author"], namerequired:!0, parent:["select"], requiredProperties:["aria-expanded"], properties:["aria-expanded", "aria-autocomplete", "aria-required"]}, command:{"abstract":!0, namefrom:["author"],
@@ -458,17 +484,20 @@ submit:"input_type_submit", tel:"input_type_tel", text:"input_type_text", url:"i
458
484
  axs.constants.TAG_TO_INFORMATION_TABLE_VERBOSE_MSG = {A:"tag_link", BUTTON:"tag_button", H1:"tag_h1", H2:"tag_h2", H3:"tag_h3", H4:"tag_h4", H5:"tag_h5", H6:"tag_h6", LI:"tag_li", OL:"tag_ol", SELECT:"tag_select", TEXTAREA:"tag_textarea", UL:"tag_ul", SECTION:"tag_section", NAV:"tag_nav", ARTICLE:"tag_article", ASIDE:"tag_aside", HGROUP:"tag_hgroup", HEADER:"tag_header", FOOTER:"tag_footer", TIME:"tag_time", MARK:"tag_mark"};
459
485
  axs.constants.TAG_TO_INFORMATION_TABLE_BRIEF_MSG = {BUTTON:"tag_button", SELECT:"tag_select", TEXTAREA:"tag_textarea"};
460
486
  axs.constants.MIXED_VALUES = {"true":!0, "false":!0, mixed:!0};
461
- for(var propertyName in axs.constants.ARIA_PROPERTIES) {
462
- var propertyDetails = axs.constants.ARIA_PROPERTIES[propertyName];
463
- if(propertyDetails.values) {
464
- for(var valuesSet = {}, i = 0;i < propertyDetails.values.length;i++) {
465
- valuesSet[propertyDetails.values[i]] = !0
487
+ (function() {
488
+ for(var a in axs.constants.ARIA_PROPERTIES) {
489
+ var b = axs.constants.ARIA_PROPERTIES[a];
490
+ if(b.values) {
491
+ for(var c = {}, d = 0;d < b.values.length;d++) {
492
+ c[b.values[d]] = !0
493
+ }
494
+ b.valuesSet = c
466
495
  }
467
- propertyDetails.valuesSet = valuesSet
468
496
  }
469
- }
497
+ })();
470
498
  axs.constants.Severity = {INFO:"Info", WARNING:"Warning", SEVERE:"Severe"};
471
499
  axs.constants.AuditResult = {PASS:"PASS", FAIL:"FAIL", NA:"NA"};
500
+ axs.constants.InlineElements = {TT:!0, I:!0, B:!0, BIG:!0, SMALL:!0, EM:!0, STRONG:!0, DFN:!0, CODE:!0, SAMP:!0, KBD:!0, VAR:!0, CITE:!0, ABBR:!0, ACRONYM:!0, A:!0, IMG:!0, OBJECT:!0, BR:!0, SCRIPT:!0, MAP:!0, Q:!0, SUB:!0, SUP:!0, SPAN:!0, BDO:!0, INPUT:!0, SELECT:!0, TEXTAREA:!0, LABEL:!0, BUTTON:!0};
472
501
  axs.utils = {};
473
502
  axs.utils.FOCUSABLE_ELEMENTS_SELECTOR = "input:not([type=hidden]):not([disabled]),select:not([disabled]),textarea:not([disabled]),button:not([disabled]),a[href],iframe,[tabindex]";
474
503
  axs.utils.Color = function(a, b, c, d) {
@@ -485,6 +514,26 @@ axs.utils.calculateContrastRatio = function(a, b) {
485
514
  var c = axs.utils.calculateLuminance(a), d = axs.utils.calculateLuminance(b);
486
515
  return(Math.max(c, d) + 0.05) / (Math.min(c, d) + 0.05)
487
516
  };
517
+ axs.utils.luminanceRatio = function(a, b) {
518
+ return(Math.max(a, b) + 0.05) / (Math.min(a, b) + 0.05)
519
+ };
520
+ axs.utils.asElement = function(a) {
521
+ switch(a.nodeType) {
522
+ case Node.COMMENT_NODE:
523
+ return null;
524
+ case Node.ELEMENT_NODE:
525
+ if("script" == a.tagName.toLowerCase()) {
526
+ return null
527
+ }
528
+ break;
529
+ case Node.TEXT_NODE:
530
+ a = a.parentElement;
531
+ break;
532
+ default:
533
+ return console.warn("Unhandled node type: ", a.nodeType), null
534
+ }
535
+ return a
536
+ };
488
537
  axs.utils.elementIsTransparent = function(a) {
489
538
  return"0" == a.style.opacity
490
539
  };
@@ -498,15 +547,21 @@ axs.utils.elementIsOutsideScrollArea = function(a) {
498
547
  var b = document.body.scrollWidth, c = document.body.scrollTop, d = document.body.scrollLeft;
499
548
  return a.top >= document.body.scrollHeight || a.bottom <= -c || a.left >= b || a.right <= -d ? !0 : !1
500
549
  };
501
- axs.utils.overlappingElement = function(a) {
502
- function b(a, c) {
503
- return null == c ? !1 : c === a ? !0 : b(a, c.parentNode)
504
- }
550
+ axs.utils.isAncestor = function(a, b) {
551
+ return null == b ? !1 : b === a ? !0 : axs.utils.isAncestor(a, b.parentNode)
552
+ };
553
+ axs.utils.overlappingElements = function(a) {
505
554
  if(axs.utils.elementHasZeroArea(a)) {
506
555
  return null
507
556
  }
508
- var c = a.getBoundingClientRect(), c = document.elementFromPoint((c.left + c.right) / 2, (c.top + c.bottom) / 2);
509
- return null == c || c == a || b(c, a) ? null : c
557
+ for(var b = [], c = a.getClientRects(), d = 0;d < c.length;d++) {
558
+ var e = c[d], e = document.elementFromPoint((e.left + e.right) / 2, (e.top + e.bottom) / 2);
559
+ if(null != e && e != a && !axs.utils.isAncestor(e, a) && !axs.utils.isAncestor(a, e)) {
560
+ var f = window.getComputedStyle(e, null);
561
+ f && (f = axs.utils.getBgColor(f, e)) && 0 < f.alpha && 0 > b.indexOf(e) && b.push(e)
562
+ }
563
+ }
564
+ return b
510
565
  };
511
566
  axs.utils.elementIsHtmlControl = function(a) {
512
567
  var b = a.ownerDocument.defaultView;
@@ -516,23 +571,20 @@ axs.utils.elementIsAriaWidget = function(a) {
516
571
  return a.hasAttribute("role") && (a = a.getAttribute("role")) && (a = axs.constants.ARIA_ROLES[a]) && "widget" in a.allParentRolesSet ? !0 : !1
517
572
  };
518
573
  axs.utils.elementIsVisible = function(a) {
519
- if(axs.utils.elementIsTransparent(a) || axs.utils.elementHasZeroArea(a) || axs.utils.elementIsOutsideScrollArea(a)) {
520
- return!1
521
- }
522
- if(a = axs.utils.overlappingElement(a)) {
523
- var b = window.getComputedStyle(a, null);
524
- if(b && (a = axs.utils.getBgColor(b, a)) && 0 < a.alpha) {
525
- return!1
526
- }
527
- }
528
- return!0
574
+ return axs.utils.elementIsTransparent(a) || axs.utils.elementHasZeroArea(a) || axs.utils.elementIsOutsideScrollArea(a) || axs.utils.overlappingElements(a).length ? !1 : !0
529
575
  };
530
576
  axs.utils.isLargeFont = function(a) {
531
577
  var b = a.fontSize;
532
578
  a = "bold" == a.fontWeight;
533
579
  var c = b.match(/(\d+)px/);
534
580
  if(c) {
535
- return b = parseInt(c[1], 10), a && 19.2 <= b || 24 <= b ? !0 : !1
581
+ b = parseInt(c[1], 10);
582
+ if(c = window.getComputedStyle(document.body, null).fontSize.match(/(\d+)px/)) {
583
+ var d = parseInt(c[1], 10), c = 1.2 * d, d = 1.5 * d
584
+ }else {
585
+ c = 19.2, d = 24
586
+ }
587
+ return a && b >= c || b >= d
536
588
  }
537
589
  if(c = b.match(/(\d+)em/)) {
538
590
  return b = parseInt(c[1], 10), a && 1.2 <= b || 1.5 <= b ? !0 : !1
@@ -541,7 +593,7 @@ axs.utils.isLargeFont = function(a) {
541
593
  return b = parseInt(c[1], 10), a && 120 <= b || 150 <= b ? !0 : !1
542
594
  }
543
595
  if(c = b.match(/(\d+)pt/)) {
544
- if(b = parseInt(c[1], 10), a && 14 <= b || 14 <= b) {
596
+ if(b = parseInt(c[1], 10), a && 14 <= b || 18 <= b) {
545
597
  return!0
546
598
  }
547
599
  }
@@ -549,39 +601,46 @@ axs.utils.isLargeFont = function(a) {
549
601
  };
550
602
  axs.utils.getBgColor = function(a, b) {
551
603
  var c = axs.utils.parseColor(a.backgroundColor);
552
- if(!c || a.backgroundImage && "none" != a.backgroundImage) {
604
+ if(!c) {
553
605
  return null
554
606
  }
607
+ 1 > a.opacity && (c.alpha *= a.opacity);
555
608
  if(1 > c.alpha) {
556
- var d = b, e = [];
557
- e.push(c);
558
- for(c = null;d = d.parentElement;) {
559
- var f = window.getComputedStyle(d, null);
560
- if(f) {
561
- if(f.backgroundImage && "none" != f.backgroundImage) {
562
- return null
563
- }
564
- if((f = axs.utils.parseColor(f.backgroundColor)) && 0 != f.alpha && (e.push(f), 1 == f.alpha)) {
565
- c = null;
566
- break
567
- }
568
- }
569
- }
570
- c || e.push(new axs.utils.Color(255, 255, 255, 1));
571
- for(d = e.pop();e.length;) {
572
- c = e.pop(), d = axs.utils.flattenColors(c, d)
609
+ var d = axs.utils.getParentBgColor(b);
610
+ if(null == d) {
611
+ return null
573
612
  }
574
- c = d
613
+ c = axs.utils.flattenColors(c, d)
575
614
  }
576
615
  return c
577
616
  };
578
- axs.utils.getFgColor = function(a, b) {
579
- var c = axs.utils.parseColor(a.color);
580
- if(!c) {
617
+ axs.utils.getParentBgColor = function(a) {
618
+ var b = a;
619
+ a = [];
620
+ for(var c = null;b = b.parentElement;) {
621
+ var d = window.getComputedStyle(b, null);
622
+ if(d) {
623
+ var e = axs.utils.parseColor(d.backgroundColor);
624
+ if(e && (1 > d.opacity && (e.alpha *= d.opacity), 0 != e.alpha && (a.push(e), 1 == e.alpha))) {
625
+ c = !0;
626
+ break
627
+ }
628
+ }
629
+ }
630
+ c || a.push(new axs.utils.Color(255, 255, 255, 1));
631
+ for(b = a.pop();a.length;) {
632
+ c = a.pop(), b = axs.utils.flattenColors(c, b)
633
+ }
634
+ return b
635
+ };
636
+ axs.utils.getFgColor = function(a, b, c) {
637
+ var d = axs.utils.parseColor(a.color);
638
+ if(!d) {
581
639
  return null
582
640
  }
583
- 1 > c.alpha && (c = axs.utils.flattenColors(c, b));
584
- return c
641
+ 1 > d.alpha && (d = axs.utils.flattenColors(d, c));
642
+ 1 > a.opacity && (b = axs.utils.getParentBgColor(b), d.alpha *= a.opacity, d = axs.utils.flattenColors(d, b));
643
+ return d
585
644
  };
586
645
  axs.utils.parseColor = function(a) {
587
646
  var b = a.match(/^rgb\((\d+), (\d+), (\d+)\)$/);
@@ -592,34 +651,130 @@ axs.utils.parseColor = function(a) {
592
651
  }
593
652
  return(b = a.match(/^rgba\((\d+), (\d+), (\d+), (\d+(\.\d+)?)\)/)) ? (d = parseInt(b[4], 10), a = parseInt(b[1], 10), c = parseInt(b[2], 10), b = parseInt(b[3], 10), new axs.utils.Color(a, c, b, d)) : null
594
653
  };
654
+ axs.utils.colorChannelToString = function(a) {
655
+ a = Math.round(a);
656
+ return 15 >= a ? "0" + a.toString(16) : a.toString(16)
657
+ };
595
658
  axs.utils.colorToString = function(a) {
596
- return"rgba(" + [a.red, a.green, a.blue, a.alpha].join() + ")"
659
+ return 1 == a.alpha ? "#" + axs.utils.colorChannelToString(a.red) + axs.utils.colorChannelToString(a.green) + axs.utils.colorChannelToString(a.blue) : "rgba(" + [a.red, a.green, a.blue, a.alpha].join() + ")"
660
+ };
661
+ axs.utils.luminanceFromContrastRatio = function(a, b, c) {
662
+ return c ? (a + 0.05) * b - 0.05 : (a + 0.05) / b - 0.05
663
+ };
664
+ axs.utils.translateColor = function(a, b) {
665
+ var c = a[0], c = (b - c) / ((c > b ? 0 : 1) - c);
666
+ return axs.utils.fromYCC([b, a[1] - a[1] * c, a[2] - a[2] * c])
667
+ };
668
+ axs.utils.suggestColors = function(a, b, c, d) {
669
+ if(!axs.utils.isLowContrast(c, d, !0)) {
670
+ return null
671
+ }
672
+ var e = {}, f = axs.utils.calculateLuminance(a), g = axs.utils.calculateLuminance(b), h = axs.utils.isLargeFont(d) ? 3 : 4.5, k = axs.utils.isLargeFont(d) ? 4.5 : 7, m = g > f, l = axs.utils.luminanceFromContrastRatio(f, h + 0.02, m), n = axs.utils.luminanceFromContrastRatio(f, k + 0.02, m), q = axs.utils.toYCC(b);
673
+ if(axs.utils.isLowContrast(c, d, !1) && 1 >= l && 0 <= l) {
674
+ var p = axs.utils.translateColor(q, l), l = axs.utils.calculateContrastRatio(p, a);
675
+ axs.utils.calculateLuminance(p);
676
+ f = {};
677
+ f.fg = axs.utils.colorToString(p);
678
+ f.bg = axs.utils.colorToString(a);
679
+ f.contrast = l.toFixed(2);
680
+ e.AA = f
681
+ }
682
+ 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);
683
+ h = axs.utils.luminanceFromContrastRatio(g, h + 0.02, !m);
684
+ g = axs.utils.luminanceFromContrastRatio(g, k + 0.02, !m);
685
+ a = axs.utils.toYCC(a);
686
+ !("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);
687
+ !("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);
688
+ return e
597
689
  };
598
690
  axs.utils.flattenColors = function(a, b) {
599
691
  var c = a.alpha;
600
- return new axs.utils.Color((1 - c) * b.red + c * a.red, (1 - c) * b.green + c * a.green, (1 - c) * b.blue + c * a.blue, 1)
692
+ return new axs.utils.Color((1 - c) * b.red + c * a.red, (1 - c) * b.green + c * a.green, (1 - c) * b.blue + c * a.blue, a.alpha + b.alpha * (1 - a.alpha))
601
693
  };
602
694
  axs.utils.calculateLuminance = function(a) {
695
+ return axs.utils.toYCC(a)[0]
696
+ };
697
+ axs.utils.RGBToYCCMatrix = function(a, b) {
698
+ return[[a, 1 - a - b, b], [-a / (2 - 2 * b), (a + b - 1) / (2 - 2 * b), (1 - b) / (2 - 2 * b)], [(1 - a) / (2 - 2 * a), (a + b - 1) / (2 - 2 * a), -b / (2 - 2 * a)]]
699
+ };
700
+ axs.utils.invert3x3Matrix = function(a) {
701
+ var b = a[0][0], c = a[0][1], d = a[0][2], e = a[1][0], f = a[1][1], g = a[1][2], h = a[2][0], k = a[2][1];
702
+ a = a[2][2];
703
+ return axs.utils.scalarMultiplyMatrix([[f * a - g * k, d * k - c * a, c * g - d * f], [g * h - e * a, b * a - d * h, d * e - b * g], [e * k - f * h, h * c - b * k, b * f - c * e]], 1 / (b * (f * a - g * k) - c * (a * e - g * h) + d * (e * k - f * h)))
704
+ };
705
+ axs.utils.scalarMultiplyMatrix = function(a, b) {
706
+ for(var c = [[], [], []], d = 0;3 > d;d++) {
707
+ for(var e = 0;3 > e;e++) {
708
+ c[d][e] = a[d][e] * b
709
+ }
710
+ }
711
+ return c
712
+ };
713
+ axs.utils.kR = 0.2126;
714
+ axs.utils.kB = 0.0722;
715
+ axs.utils.YCC_MATRIX = axs.utils.RGBToYCCMatrix(axs.utils.kR, axs.utils.kB);
716
+ axs.utils.INVERTED_YCC_MATRIX = axs.utils.invert3x3Matrix(axs.utils.YCC_MATRIX);
717
+ axs.utils.convertColor = function(a, b) {
718
+ var c = b[0], d = b[1], e = b[2];
719
+ return[a[0][0] * c + a[0][1] * d + a[0][2] * e, a[1][0] * c + a[1][1] * d + a[1][2] * e, a[2][0] * c + a[2][1] * d + a[2][2] * e]
720
+ };
721
+ axs.utils.multiplyMatrices = function(a, b) {
722
+ for(var c = [[], [], []], d = 0;3 > d;d++) {
723
+ for(var e = 0;3 > e;e++) {
724
+ c[d][e] = a[d][0] * b[0][e] + a[d][1] * b[1][e] + a[d][2] * b[2][e]
725
+ }
726
+ }
727
+ return c
728
+ };
729
+ axs.utils.toYCC = function(a) {
603
730
  var b = a.red / 255, c = a.green / 255;
604
731
  a = a.blue / 255;
605
732
  b = 0.03928 >= b ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4);
606
733
  c = 0.03928 >= c ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
607
734
  a = 0.03928 >= a ? a / 12.92 : Math.pow((a + 0.055) / 1.055, 2.4);
608
- return 0.2126 * b + 0.7152 * c + 0.0722 * a
735
+ return axs.utils.convertColor(axs.utils.YCC_MATRIX, [b, c, a])
736
+ };
737
+ axs.utils.fromYCC = function(a) {
738
+ var b = axs.utils.convertColor(axs.utils.INVERTED_YCC_MATRIX, a), c = b[0];
739
+ a = b[1];
740
+ b = b[2];
741
+ c = 0.00303949 >= c ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055;
742
+ a = 0.00303949 >= a ? 12.92 * a : 1.055 * Math.pow(a, 1 / 2.4) - 0.055;
743
+ b = 0.00303949 >= b ? 12.92 * b : 1.055 * Math.pow(b, 1 / 2.4) - 0.055;
744
+ c = Math.min(Math.max(Math.round(255 * c), 0), 255);
745
+ a = Math.min(Math.max(Math.round(255 * a), 0), 255);
746
+ b = Math.min(Math.max(Math.round(255 * b), 0), 255);
747
+ return new axs.utils.Color(c, a, b, 1)
748
+ };
749
+ axs.utils.scalarMultiplyMatrix = function(a, b) {
750
+ for(var c = [[], [], []], d = 0;3 > d;d++) {
751
+ for(var e = 0;3 > e;e++) {
752
+ c[d][e] = a[d][e] * b
753
+ }
754
+ }
755
+ return c
756
+ };
757
+ axs.utils.multiplyMatrices = function(a, b) {
758
+ for(var c = [[], [], []], d = 0;3 > d;d++) {
759
+ for(var e = 0;3 > e;e++) {
760
+ c[d][e] = a[d][0] * b[0][e] + a[d][1] * b[1][e] + a[d][2] * b[2][e]
761
+ }
762
+ }
763
+ return c
609
764
  };
610
765
  axs.utils.getContrastRatioForElement = function(a) {
611
766
  var b = window.getComputedStyle(a, null);
612
767
  return axs.utils.getContrastRatioForElementWithComputedStyle(b, a)
613
768
  };
614
769
  axs.utils.getContrastRatioForElementWithComputedStyle = function(a, b) {
615
- if(!axs.utils.elementIsVisible(b)) {
770
+ if(axs.utils.isElementHidden(b)) {
616
771
  return null
617
772
  }
618
773
  var c = axs.utils.getBgColor(a, b);
619
774
  if(!c) {
620
775
  return null
621
776
  }
622
- var d = axs.utils.getFgColor(a, c);
777
+ var d = axs.utils.getFgColor(a, b, c);
623
778
  return d ? axs.utils.calculateContrastRatio(d, c) : null
624
779
  };
625
780
  axs.utils.isNativeTextElement = function(a) {
@@ -652,12 +807,13 @@ axs.utils.isNativeTextElement = function(a) {
652
807
  return!1
653
808
  }
654
809
  };
655
- axs.utils.isLowContrast = function(a, b) {
656
- return 3 > a || !axs.utils.isLargeFont(b) && 4.5 > a
810
+ axs.utils.isLowContrast = function(a, b, c) {
811
+ a = Math.round(10 * a) / 10;
812
+ return c ? 4.5 > a || !axs.utils.isLargeFont(b) && 7 > a : 3 > a || !axs.utils.isLargeFont(b) && 4.5 > a
657
813
  };
658
814
  axs.utils.hasLabel = function(a) {
659
815
  var b = a.tagName.toLowerCase(), c = a.type ? a.type.toLowerCase() : "";
660
- if(a.hasAttribute("aria-label") || a.hasAttribute("title") || "img" == b && a.hasAttribute("alt") || "input" == b && "image" == c && a.hasAttribute("alt") || "input" == b && ("submit" == c || "reset" == c) || a.hasAttribute("aria-labelledby") || axs.utils.isNativeTextElement(a) && a.hasAttribute("placeholder") || a.hasAttribute("id") && 0 < document.querySelectorAll("label[for=" + a.id + "]").length) {
816
+ if(a.hasAttribute("aria-label") || a.hasAttribute("title") || "img" == b && a.hasAttribute("alt") || "input" == b && "image" == c && a.hasAttribute("alt") || "input" == b && ("submit" == c || "reset" == c) || a.hasAttribute("aria-labelledby") || axs.utils.isNativeTextElement(a) && a.hasAttribute("placeholder") || a.hasAttribute("id") && 0 < document.querySelectorAll('label[for="' + a.id + '"]').length) {
661
817
  return!0
662
818
  }
663
819
  for(b = a.parentElement;b;) {
@@ -681,12 +837,20 @@ axs.utils.isElementHidden = function(a) {
681
837
  axs.utils.isElementOrAncestorHidden = function(a) {
682
838
  return axs.utils.isElementHidden(a) ? !0 : a.parentElement ? axs.utils.isElementOrAncestorHidden(a.parentElement) : !1
683
839
  };
684
- axs.utils.getRole = function(a) {
840
+ axs.utils.isInlineElement = function(a) {
841
+ a = a.tagName.toUpperCase();
842
+ return axs.constants.InlineElements[a]
843
+ };
844
+ axs.utils.getRoles = function(a) {
685
845
  if(!a.hasAttribute("role")) {
686
846
  return!1
687
847
  }
688
- a = a.getAttribute("role");
689
- return axs.constants.ARIA_ROLES[a] ? {name:a, details:axs.constants.ARIA_ROLES[a], valid:!0} : {name:a, valid:!1}
848
+ a = a.getAttribute("role").split(" ");
849
+ for(var b = [], c = !0, d = 0;d < a.length;d++) {
850
+ var e = a[d];
851
+ axs.constants.ARIA_ROLES[e] ? b.push({name:e, details:axs.constants.ARIA_ROLES[e], valid:!0}) : (b.push({name:e, valid:!1}), c = !1)
852
+ }
853
+ return{roles:b, valid:c}
690
854
  };
691
855
  axs.utils.getAriaPropertyValue = function(a, b, c) {
692
856
  var d = a.replace(/^aria-/, ""), e = axs.constants.ARIA_PROPERTIES[d], d = {name:a, rawValue:b};
@@ -708,6 +872,8 @@ axs.utils.getAriaPropertyValue = function(a, b, c) {
708
872
  }
709
873
  return d;
710
874
  case "integer":
875
+ ;
876
+ case "decimal":
711
877
  c = axs.utils.isValidNumber(b);
712
878
  if(!c.valid) {
713
879
  return d.valid = !1, d.reason = c.reason, d
@@ -752,10 +918,14 @@ axs.utils.isValidBoolean = function(a) {
752
918
  return"boolean" != typeof b ? {valid:!1, value:a, reason:'"' + a + '" is not a true/false value'} : {valid:!0, value:b}
753
919
  };
754
920
  axs.utils.isValidIDRefValue = function(a, b) {
755
- return b.ownerDocument.getElementById(a) ? {valid:!0, idref:a} : {valid:!1, idref:a, reason:'No element with ID "' + a + '"'}
921
+ 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 + '"'}
756
922
  };
757
923
  axs.utils.isValidNumber = function(a) {
758
- var b = JSON.parse(a);
924
+ try {
925
+ var b = JSON.parse(a)
926
+ }catch(c) {
927
+ return{valid:!1, value:a, reason:'"' + a + '" is not a number'}
928
+ }
759
929
  return"number" != typeof b ? {valid:!1, value:a, reason:'"' + a + '" is not a number'} : {valid:!0, value:b}
760
930
  };
761
931
  axs.utils.isElementImplicitlyFocusable = function(a) {
@@ -822,6 +992,304 @@ axs.utils.getQuerySelectorText = function(a) {
822
992
  }
823
993
  return""
824
994
  };
995
+ axs.properties = {};
996
+ axs.properties.TEXT_CONTENT_XPATH = './/text()[normalize-space(.)!=""]/parent::*[name()!="script"]';
997
+ axs.properties.getFocusProperties = function(a) {
998
+ var b = {}, c = a.getAttribute("tabindex");
999
+ void 0 != c ? b.tabindex = {value:c, valid:!0} : axs.utils.isElementImplicitlyFocusable(a) && (b.implicitlyFocusable = {value:!0, valid:!0});
1000
+ if(0 == Object.keys(b).length) {
1001
+ return null
1002
+ }
1003
+ var d = axs.utils.elementIsTransparent(a), e = axs.utils.elementHasZeroArea(a), f = axs.utils.elementIsOutsideScrollArea(a), g = axs.utils.overlappingElements(a);
1004
+ if(d || e || f || 0 < g.length) {
1005
+ var c = axs.utils.isElementOrAncestorHidden(a), h = {value:!1, valid:c};
1006
+ d && (h.transparent = !0);
1007
+ e && (h.zeroArea = !0);
1008
+ f && (h.outsideScrollArea = !0);
1009
+ 0 < g.length && (h.overlappingElements = g);
1010
+ d = {value:c, valid:c};
1011
+ c && (d.reason = axs.properties.getHiddenReason(a));
1012
+ h.hidden = d;
1013
+ b.visible = h
1014
+ }else {
1015
+ b.visible = {value:!0, valid:!0}
1016
+ }
1017
+ return b
1018
+ };
1019
+ axs.properties.getHiddenReason = function(a) {
1020
+ if(!(a && a instanceof a.ownerDocument.defaultView.HTMLElement)) {
1021
+ return null
1022
+ }
1023
+ if(a.hasAttribute("chromevoxignoreariahidden")) {
1024
+ var b = !0
1025
+ }
1026
+ var c = window.getComputedStyle(a, null);
1027
+ return"none" == c.display ? {property:"display: none", on:a} : "hidden" == c.visibility ? {property:"visibility: hidden", on:a} : a.hasAttribute("aria-hidden") && "true" == a.getAttribute("aria-hidden").toLowerCase() && !b ? {property:"aria-hidden", on:a} : axs.properties.getHiddenReason(a.parentElement)
1028
+ };
1029
+ axs.properties.getColorProperties = function(a) {
1030
+ var b = {};
1031
+ (a = axs.properties.getContrastRatioProperties(a)) && (b.contrastRatio = a);
1032
+ return 0 == Object.keys(b).length ? null : b
1033
+ };
1034
+ axs.properties.hasDirectTextDescendant = function(a) {
1035
+ 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()) {
1036
+ if(d === a) {
1037
+ c = !0;
1038
+ break
1039
+ }
1040
+ }
1041
+ return c
1042
+ };
1043
+ axs.properties.getContrastRatioProperties = function(a) {
1044
+ if(!axs.properties.hasDirectTextDescendant(a)) {
1045
+ return null
1046
+ }
1047
+ var b = {}, c = window.getComputedStyle(a, null), d = axs.utils.getBgColor(c, a);
1048
+ if(!d) {
1049
+ return null
1050
+ }
1051
+ b.backgroundColor = axs.utils.colorToString(d);
1052
+ var e = axs.utils.getFgColor(c, a, d);
1053
+ b.foregroundColor = axs.utils.colorToString(e);
1054
+ a = axs.utils.getContrastRatioForElementWithComputedStyle(c, a);
1055
+ if(!a) {
1056
+ return null
1057
+ }
1058
+ b.value = a.toFixed(2);
1059
+ axs.utils.isLowContrast(a, c) && (b.alert = !0);
1060
+ (c = axs.utils.suggestColors(d, e, a, c)) && Object.keys(c).length && (b.suggestedColors = c);
1061
+ return b
1062
+ };
1063
+ axs.properties.findTextAlternatives = function(a, b, c) {
1064
+ var d = c || !1;
1065
+ c = axs.utils.asElement(a);
1066
+ if(!c || !d && axs.utils.isElementOrAncestorHidden(c)) {
1067
+ return null
1068
+ }
1069
+ if(a.nodeType == Node.TEXT_NODE) {
1070
+ return c = {type:"text"}, c.text = a.textContent, c.lastWord = axs.properties.getLastWord(c.text), b.content = c, a.textContent
1071
+ }
1072
+ a = null;
1073
+ d || (a = axs.properties.getTextFromAriaLabelledby(c, b));
1074
+ if(c.hasAttribute("aria-label")) {
1075
+ var e = {type:"text"};
1076
+ e.text = c.getAttribute("aria-label");
1077
+ e.lastWord = axs.properties.getLastWord(e.text);
1078
+ a ? e.unused = !0 : d && axs.utils.elementIsHtmlControl(c) || (a = e.text);
1079
+ b.ariaLabel = e
1080
+ }
1081
+ c.hasAttribute("role") && "presentation" == c.getAttribute("role") || (a = axs.properties.getTextFromHostLanguageAttributes(c, b, a, d));
1082
+ if(d && axs.utils.elementIsHtmlControl(c)) {
1083
+ e = c.ownerDocument.defaultView;
1084
+ if(c instanceof e.HTMLInputElement) {
1085
+ var f = c;
1086
+ "text" == f.type && f.value && 0 < f.value.length && (b.controlValue = {text:f.value});
1087
+ "range" == f.type && (b.controlValue = {text:f.value})
1088
+ }
1089
+ c instanceof e.HTMLSelectElement && (b.controlValue = {text:c.value});
1090
+ b.controlValue && (e = b.controlValue, a ? e.unused = !0 : a = e.text)
1091
+ }
1092
+ if(d && axs.utils.elementIsAriaWidget(c)) {
1093
+ d = c.getAttribute("role");
1094
+ "textbox" == d && c.textContent && 0 < c.textContent.length && (b.controlValue = {text:c.textContent});
1095
+ if("slider" == d || "spinbutton" == d) {
1096
+ c.hasAttribute("aria-valuetext") ? b.controlValue = {text:c.getAttribute("aria-valuetext")} : c.hasAttribute("aria-valuenow") && (b.controlValue = {value:c.getAttribute("aria-valuenow"), text:"" + c.getAttribute("aria-valuenow")})
1097
+ }
1098
+ if("menu" == d) {
1099
+ for(var g = c.querySelectorAll("[role=menuitemcheckbox], [role=menuitemradio]"), e = [], f = 0;f < g.length;f++) {
1100
+ "true" == g[f].getAttribute("aria-checked") && e.push(g[f])
1101
+ }
1102
+ if(0 < e.length) {
1103
+ g = "";
1104
+ for(f = 0;f < e.length;f++) {
1105
+ g += axs.properties.findTextAlternatives(e[f], {}, !0), f < e.length - 1 && (g += ", ")
1106
+ }
1107
+ b.controlValue = {text:g}
1108
+ }
1109
+ }
1110
+ if("combobox" == d || "select" == d) {
1111
+ b.controlValue = {text:"TODO"}
1112
+ }
1113
+ b.controlValue && (e = b.controlValue, a ? e.unused = !0 : a = e.text)
1114
+ }
1115
+ e = !0;
1116
+ c.hasAttribute("role") && (d = c.getAttribute("role"), (d = axs.constants.ARIA_ROLES[d]) && (!d.namefrom || 0 > d.namefrom.indexOf("contents")) && (e = !1));
1117
+ (d = axs.properties.getTextFromDescendantContent(c)) && e && (e = {type:"text"}, e.text = d, e.lastWord = axs.properties.getLastWord(e.text), a ? e.unused = !0 : a = d, b.content = e);
1118
+ c.hasAttribute("title") && (d = {type:"string", valid:!0}, d.text = c.getAttribute("title"), d.lastWord = axs.properties.getLastWord(d.lastWord), a ? d.unused = !0 : a = d.text, b.title = d);
1119
+ return 0 == Object.keys(b).length && null == a ? null : a
1120
+ };
1121
+ axs.properties.getTextFromDescendantContent = function(a) {
1122
+ var b = a.childNodes;
1123
+ a = [];
1124
+ for(var c = 0;c < b.length;c++) {
1125
+ var d = axs.properties.findTextAlternatives(b[c], {}, !0);
1126
+ d && a.push(d.trim())
1127
+ }
1128
+ if(a.length) {
1129
+ b = "";
1130
+ for(c = 0;c < a.length;c++) {
1131
+ b = [b, a[c]].join(" ").trim()
1132
+ }
1133
+ return b
1134
+ }
1135
+ return null
1136
+ };
1137
+ axs.properties.getTextFromAriaLabelledby = function(a, b) {
1138
+ var c = null;
1139
+ if(!a.hasAttribute("aria-labelledby")) {
1140
+ return c
1141
+ }
1142
+ for(var d = a.getAttribute("aria-labelledby").split(/\s+/), e = {valid:!0}, f = [], g = [], h = 0;h < d.length;h++) {
1143
+ var k = {type:"element"}, m = d[h];
1144
+ k.value = m;
1145
+ var l = document.getElementById(m);
1146
+ l ? (k.valid = !0, k.text = axs.properties.findTextAlternatives(l, {}, !0), k.lastWord = axs.properties.getLastWord(k.text), f.push(l.textContent.trim()), k.element = l) : (k.valid = !1, e.valid = !1, k.errorMessage = {messageKey:"noElementWithId", args:[m]});
1147
+ g.push(k)
1148
+ }
1149
+ 0 < g.length && (g[g.length - 1].last = !0, e.values = g, e.text = f.join(" "), e.lastWord = axs.properties.getLastWord(e.text), c = e.text, b.ariaLabelledby = e);
1150
+ return c
1151
+ };
1152
+ axs.properties.getTextFromHostLanguageAttributes = function(a, b, c, d) {
1153
+ if(axs.browserUtils.matchSelector(a, "img")) {
1154
+ if(a.hasAttribute("alt")) {
1155
+ var e = {type:"string", valid:!0};
1156
+ e.text = a.getAttribute("alt");
1157
+ c ? e.unused = !0 : c = e.text;
1158
+ b.alt = e
1159
+ }else {
1160
+ e = {valid:!1, errorMessage:"No alt value provided"}, b.alt = e, e = a.src, "string" == typeof e && (c = e.split("/").pop(), b.filename = {text:c})
1161
+ }
1162
+ }
1163
+ if(axs.browserUtils.matchSelector(a, 'input:not([type="hidden"]):not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), video:not([disabled])') && !d) {
1164
+ if(a.hasAttribute("id")) {
1165
+ d = document.querySelectorAll('label[for="' + a.id + '"]');
1166
+ for(var e = {}, f = [], g = [], h = 0;h < d.length;h++) {
1167
+ var k = {type:"element"}, m = d[h], l = axs.properties.findTextAlternatives(m, {}, !0);
1168
+ l && 0 < l.trim().length && (k.text = l.trim(), g.push(l.trim()));
1169
+ k.element = m;
1170
+ f.push(k)
1171
+ }
1172
+ 0 < f.length && (f[f.length - 1].last = !0, e.values = f, e.text = g.join(" "), e.lastWord = axs.properties.getLastWord(e.text), c ? e.unused = !0 : c = e.text, b.labelFor = e)
1173
+ }
1174
+ d = a.parentElement;
1175
+ for(e = {};d;) {
1176
+ if("label" == d.tagName.toLowerCase() && (f = d, f.control == a)) {
1177
+ e.type = "element";
1178
+ e.text = axs.properties.findTextAlternatives(f, {}, !0);
1179
+ e.lastWord = axs.properties.getLastWord(e.text);
1180
+ e.element = f;
1181
+ break
1182
+ }
1183
+ d = d.parentElement
1184
+ }
1185
+ e.text && (c ? e.unused = !0 : c = e.text, b.labelWrapped = e);
1186
+ Object.keys(b).length || (b.noLabel = !0)
1187
+ }
1188
+ return c
1189
+ };
1190
+ axs.properties.getLastWord = function(a) {
1191
+ if(!a) {
1192
+ return null
1193
+ }
1194
+ var b = a.lastIndexOf(" ") + 1, c = a.length - 10;
1195
+ return a.substring(b > c ? b : c)
1196
+ };
1197
+ axs.properties.getTextProperties = function(a) {
1198
+ var b = {};
1199
+ a = axs.properties.findTextAlternatives(a, b);
1200
+ if(0 == Object.keys(b).length) {
1201
+ if(!a) {
1202
+ return null
1203
+ }
1204
+ b.hasProperties = !1
1205
+ }else {
1206
+ b.hasProperties = !0
1207
+ }
1208
+ b.computedText = a;
1209
+ b.lastWord = axs.properties.getLastWord(a);
1210
+ return b
1211
+ };
1212
+ axs.properties.getAriaProperties = function(a) {
1213
+ var b = {}, c = axs.properties.getGlobalAriaProperties(a), d;
1214
+ for(d in axs.constants.ARIA_PROPERTIES) {
1215
+ var e = "aria-" + d;
1216
+ if(a.hasAttribute(e)) {
1217
+ var f = a.getAttribute(e);
1218
+ c[e] = axs.utils.getAriaPropertyValue(e, f, a)
1219
+ }
1220
+ }
1221
+ 0 < Object.keys(c).length && (b.properties = axs.utils.values(c));
1222
+ f = axs.utils.getRoles(a);
1223
+ if(!f) {
1224
+ return Object.keys(b).length ? b : null
1225
+ }
1226
+ b.roles = f;
1227
+ if(!f.valid || !f.roles) {
1228
+ return b
1229
+ }
1230
+ for(var e = f.roles, g = 0;g < e.length;g++) {
1231
+ var h = e[g];
1232
+ if(h.details && h.details.propertiesSet) {
1233
+ for(d in h.details.propertiesSet) {
1234
+ d in c || (a.hasAttribute(d) ? (f = a.getAttribute(d), c[d] = axs.utils.getAriaPropertyValue(d, f, a), "values" in c[d] && (f = c[d].values, f[f.length - 1].isLast = !0)) : h.details.requiredPropertiesSet[d] && (c[d] = {name:d, valid:!1, reason:"Required property not set"}))
1235
+ }
1236
+ }
1237
+ }
1238
+ 0 < Object.keys(c).length && (b.properties = axs.utils.values(c));
1239
+ return 0 < Object.keys(b).length ? b : null
1240
+ };
1241
+ axs.properties.getGlobalAriaProperties = function(a) {
1242
+ for(var b = {}, c = 0;c < axs.constants.GLOBAL_PROPERTIES.length;c++) {
1243
+ var d = axs.constants.GLOBAL_PROPERTIES[c];
1244
+ if(a.hasAttribute(d)) {
1245
+ var e = a.getAttribute(d);
1246
+ b[d] = axs.utils.getAriaPropertyValue(d, e, a)
1247
+ }
1248
+ }
1249
+ return b
1250
+ };
1251
+ axs.properties.getVideoProperties = function(a) {
1252
+ if(!axs.browserUtils.matchSelector(a, "video")) {
1253
+ return null
1254
+ }
1255
+ var b = {};
1256
+ b.captionTracks = axs.properties.getTrackElements(a, "captions");
1257
+ b.descriptionTracks = axs.properties.getTrackElements(a, "descriptions");
1258
+ b.chapterTracks = axs.properties.getTrackElements(a, "chapters");
1259
+ return b
1260
+ };
1261
+ axs.properties.getTrackElements = function(a, b) {
1262
+ var c = a.querySelectorAll("track[kind=" + b + "]"), d = {};
1263
+ if(!c.length) {
1264
+ return d.valid = !1, d.reason = {messageKey:"noTracksProvided", args:[[b]]}, d
1265
+ }
1266
+ d.valid = !0;
1267
+ for(var e = [], f = 0;f < c.length;f++) {
1268
+ var g = {}, h = c[f].getAttribute("src"), k = c[f].getAttribute("srcLang"), m = c[f].getAttribute("label");
1269
+ h ? (g.valid = !0, g.src = h) : (g.valid = !1, g.reason = {messageKey:"noSrcProvided"});
1270
+ h = "";
1271
+ m && (h += m, k && (h += " "));
1272
+ k && (h += "(" + k + ")");
1273
+ "" == h && (h = "[[object Object]]");
1274
+ g.name = h;
1275
+ e.push(g)
1276
+ }
1277
+ d.values = e;
1278
+ return d
1279
+ };
1280
+ axs.properties.getAllProperties = function(a) {
1281
+ var b = axs.utils.asElement(a);
1282
+ if(!b) {
1283
+ return{}
1284
+ }
1285
+ var c = {};
1286
+ c.ariaProperties = axs.properties.getAriaProperties(b);
1287
+ c.colorProperties = axs.properties.getColorProperties(b);
1288
+ c.focusProperties = axs.properties.getFocusProperties(b);
1289
+ c.textProperties = axs.properties.getTextProperties(a);
1290
+ c.videoProperties = axs.properties.getVideoProperties(b);
1291
+ return c
1292
+ };
825
1293
  axs.AuditRule = function(a) {
826
1294
  for(var b = !0, c = [], d = 0;d < axs.AuditRule.requiredFields.length;d++) {
827
1295
  var e = axs.AuditRule.requiredFields[d];
@@ -832,49 +1300,52 @@ axs.AuditRule = function(a) {
832
1300
  }
833
1301
  this.name = a.name;
834
1302
  this.severity = a.severity;
835
- this.relevantNodesSelector_ = a.relevantNodesSelector;
1303
+ this.relevantElementMatcher_ = a.relevantElementMatcher;
836
1304
  this.test_ = a.test;
837
1305
  this.code = a.code;
838
1306
  this.heading = a.heading || "";
839
1307
  this.url = a.url || "";
840
1308
  this.requiresConsoleAPI = !!a.opt_requiresConsoleAPI
841
1309
  };
842
- axs.AuditRule.requiredFields = ["name", "severity", "relevantNodesSelector", "test", "code"];
1310
+ axs.AuditRule.requiredFields = "name severity relevantElementMatcher test code heading".split(" ");
843
1311
  axs.AuditRule.NOT_APPLICABLE = {result:axs.constants.AuditResult.NA};
844
- axs.AuditRule.prototype.addNode = function(a, b) {
1312
+ axs.AuditRule.prototype.addElement = function(a, b) {
845
1313
  a.push(b)
846
1314
  };
847
- axs.AuditRule.prototype.run = function(a, b) {
848
- function c(a) {
849
- for(var b = 0;b < d.length;b++) {
850
- if(axs.browserUtils.matchSelector(a, d[b])) {
851
- return!0
852
- }
853
- }
854
- return!1
855
- }
856
- var d = a || [], e = this.relevantNodesSelector_(b || document), f = [];
857
- if(e instanceof XPathResult) {
858
- if(e.resultType == XPathResult.ORDERED_NODE_SNAPSHOT_TYPE) {
859
- if(!e.snapshotLength) {
860
- return axs.AuditRule.NOT_APPLICABLE
861
- }
862
- for(var g = 0;g < e.snapshotLength;g++) {
863
- var h = e.snapshotItem(g);
864
- this.test_(h) && !c(h) && this.addNode(f, h)
1315
+ axs.AuditRule.collectMatchingElements = function(a, b, c) {
1316
+ if(a.nodeType == Node.ELEMENT_NODE) {
1317
+ var d = a
1318
+ }
1319
+ d && b.call(null, d) && c.push(d);
1320
+ for(a = a.firstChild;null != a;) {
1321
+ axs.AuditRule.collectMatchingElements(a, b, c), a = a.nextSibling
1322
+ }
1323
+ };
1324
+ axs.AuditRule.prototype.run = function(a) {
1325
+ a = a || {};
1326
+ var b = "ignoreSelectors" in a ? a.ignoreSelectors : [], c = "maxResults" in a ? a.maxResults : null, d = [];
1327
+ axs.AuditRule.collectMatchingElements("scope" in a ? a.scope : document, this.relevantElementMatcher_, d);
1328
+ var e = [];
1329
+ if(!d.length) {
1330
+ return{result:axs.constants.AuditResult.NA}
1331
+ }
1332
+ for(a = 0;a < d.length && !(null != c && e.length >= c);a++) {
1333
+ var f = d[a], g;
1334
+ a: {
1335
+ g = f;
1336
+ for(var h = 0;h < b.length;h++) {
1337
+ if(axs.browserUtils.matchSelector(g, b[h])) {
1338
+ g = !0;
1339
+ break a
1340
+ }
865
1341
  }
866
- }else {
867
- return console.warn("Unknown XPath result type", e), null
868
- }
869
- }else {
870
- if(!e.length) {
871
- return{result:axs.constants.AuditResult.NA}
872
- }
873
- for(g = 0;g < e.length;g++) {
874
- h = e[g], this.test_(h) && !c(h) && this.addNode(f, h)
1342
+ g = !1
875
1343
  }
1344
+ !g && this.test_(f) && this.addElement(e, f)
876
1345
  }
877
- return{result:f.length ? axs.constants.AuditResult.FAIL : axs.constants.AuditResult.PASS, elements:f}
1346
+ b = {result:e.length ? axs.constants.AuditResult.FAIL : axs.constants.AuditResult.PASS, elements:e};
1347
+ a < d.length && (b.resultsTruncated = !0);
1348
+ return b
878
1349
  };
879
1350
  axs.AuditRule.specs = {};
880
1351
  axs.AuditRules = {};
@@ -931,7 +1402,7 @@ goog.exportProperty(axs.AuditResults.prototype, "toString", axs.AuditResults.pro
931
1402
  axs.Audit = {};
932
1403
  axs.AuditConfiguration = function() {
933
1404
  this.rules_ = {};
934
- this.auditRulesToIgnore = this.auditRulesToRun = this.scope = null;
1405
+ this.maxResults = this.auditRulesToIgnore = this.auditRulesToRun = this.scope = null;
935
1406
  this.withConsoleApi = !1;
936
1407
  goog.exportProperty(this, "scope", this.scope);
937
1408
  goog.exportProperty(this, "auditRulesToRun", this.auditRulesToRun);
@@ -945,6 +1416,11 @@ axs.AuditConfiguration.prototype = {ignoreSelectors:function(a, b) {
945
1416
  Array.prototype.push.call(this.rules_[a].ignore, b)
946
1417
  }, getIgnoreSelectors:function(a) {
947
1418
  return a in this.rules_ && "ignore" in this.rules_[a] ? this.rules_[a].ignore : []
1419
+ }, setSeverity:function(a, b) {
1420
+ a in this.rules_ || (this.rules_[a] = {});
1421
+ this.rules_[a].severity = b
1422
+ }, getSeverity:function(a) {
1423
+ return a in this.rules_ && "severity" in this.rules_[a] ? this.rules_[a].severity : null
948
1424
  }};
949
1425
  goog.exportProperty(axs.AuditConfiguration.prototype, "ignoreSelectors", axs.AuditConfiguration.prototype.ignoreSelectors);
950
1426
  goog.exportProperty(axs.AuditConfiguration.prototype, "getIgnoreSelectors", axs.AuditConfiguration.prototype.getIgnoreSelectors);
@@ -959,13 +1435,19 @@ axs.Audit.run = function(a) {
959
1435
  }
960
1436
  }
961
1437
  for(e = 0;e < d.length;e++) {
962
- if((f = axs.AuditRules.getRule(d[e])) && !f.disabled && (b || !f.requiresConsoleAPI)) {
963
- var g = [], h = a.getIgnoreSelectors(f.name);
964
- (0 < h.length || a.scope) && g.push(h);
965
- a.scope && g.push(a.scope);
966
- g = f.run.apply(f, g);
967
- g.rule = axs.utils.namedValues(f);
968
- c.push(g)
1438
+ var f = d[e], g = axs.AuditRules.getRule(f);
1439
+ if(g && !g.disabled && (b || !g.requiresConsoleAPI)) {
1440
+ var h = {}, k = a.getIgnoreSelectors(g.name);
1441
+ if(0 < k.length || a.scope) {
1442
+ h.ignoreSelectors = k
1443
+ }
1444
+ a.scope && (h.scope = a.scope);
1445
+ a.maxResults && (h.maxResults = a.maxResults);
1446
+ h = g.run.call(g, h);
1447
+ g = axs.utils.namedValues(g);
1448
+ g.severity = a.getSeverity(f) || g.severity;
1449
+ h.rule = g;
1450
+ c.push(h)
969
1451
  }
970
1452
  }
971
1453
  return c
@@ -999,22 +1481,18 @@ axs.Audit.accessibilityErrorMessage = function(a) {
999
1481
  return b
1000
1482
  };
1001
1483
  goog.exportSymbol("axs.Audit.accessibilityErrorMessage", axs.Audit.accessibilityErrorMessage);
1002
- axs.browserUtils = {};
1003
- axs.browserUtils.matchSelector = function(a, b) {
1004
- return a.webkitMatchesSelector ? a.webkitMatchesSelector(b) : a.mozMatchesSelector(b)
1005
- };
1006
- axs.AuditRule.specs.audioWithoutControls = {name:"audioWithoutControls", heading:"Audio elements should have controls", url:"", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
1007
- return a.querySelectorAll("audio[autoplay]")
1484
+ axs.AuditRule.specs.audioWithoutControls = {name:"audioWithoutControls", heading:"Audio elements should have controls", url:"", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
1485
+ return axs.browserUtils.matchSelector(a, "audio[autoplay]")
1008
1486
  }, test:function(a) {
1009
1487
  return!a.querySelectorAll("[controls]").length && 3 < a.duration
1010
1488
  }, code:"AX_AUDIO_01"};
1011
- axs.AuditRule.specs.badAriaAttributeValue = {name:"badAriaAttributeValue", heading:"ARIA state and property values must be valid", url:"", severity:axs.constants.Severity.SEVERE, relevantNodesSelector:function(a) {
1489
+ axs.AuditRule.specs.badAriaAttributeValue = {name:"badAriaAttributeValue", heading:"ARIA state and property values must be valid", url:"", severity:axs.constants.Severity.SEVERE, relevantElementMatcher:function(a) {
1012
1490
  var b = "", c;
1013
1491
  for(c in axs.constants.ARIA_PROPERTIES) {
1014
1492
  b += "[aria-" + c + "],"
1015
1493
  }
1016
1494
  b = b.substring(0, b.length - 1);
1017
- return a.querySelectorAll(b)
1495
+ return axs.browserUtils.matchSelector(a, b)
1018
1496
  }, test:function(a) {
1019
1497
  for(var b in axs.constants.ARIA_PROPERTIES) {
1020
1498
  var c = "aria-" + b;
@@ -1027,64 +1505,85 @@ axs.AuditRule.specs.badAriaAttributeValue = {name:"badAriaAttributeValue", headi
1027
1505
  }
1028
1506
  return!1
1029
1507
  }, code:"AX_ARIA_04"};
1030
- axs.AuditRule.specs.badAriaRole = {name:"badAriaRole", heading:"Elements with ARIA roles must use a valid, non-abstract ARIA role", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_ARIA_01:_Elements_with_ARIA_roles_must_use_a_valid,_non-abstr", severity:axs.constants.Severity.SEVERE, relevantNodesSelector:function(a) {
1031
- return a.querySelectorAll("[role]")
1508
+ 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) {
1509
+ return axs.browserUtils.matchSelector(a, "[role]")
1032
1510
  }, test:function(a) {
1033
- return!axs.utils.getRole(a).valid
1511
+ return!axs.utils.getRoles(a).valid
1034
1512
  }, code:"AX_ARIA_01"};
1035
- axs.AuditRule.specs.controlsWithoutLabel = {name:"controlsWithoutLabel", heading:"Controls and media elements should have labels", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_TEXT_01:_Controls_and_media_elements_should_have_labels", severity:axs.constants.Severity.SEVERE, relevantNodesSelector:function(a) {
1036
- return a.querySelectorAll('input:not([type="hidden"]):not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), video:not([disabled])')
1513
+ 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) {
1514
+ if(!axs.browserUtils.matchSelector(a, 'input:not([type="hidden"]):not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), video:not([disabled])')) {
1515
+ return!1
1516
+ }
1517
+ if(0 <= a.tabIndex) {
1518
+ return!0
1519
+ }
1520
+ for(a = a.parentElement;null != a;a = a.parentElement) {
1521
+ if(axs.utils.elementIsAriaWidget(a)) {
1522
+ return!1
1523
+ }
1524
+ }
1525
+ return!0
1037
1526
  }, test:function(a) {
1038
- return axs.utils.isElementOrAncestorHidden(a) || "button" == a.tagName.toLowerCase() && a.textContent.replace(/^\s+|\s+$/g, "").length ? !1 : axs.utils.hasLabel(a) ? !1 : !0
1527
+ 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
1039
1528
  }, code:"AX_TEXT_01", ruleName:"Controls and media elements should have labels"};
1040
- axs.AuditRule.specs.focusableElementNotVisibleAndNotAriaHidden = {name:"focusableElementNotVisibleAndNotAriaHidden", heading:"These elements are focusable but either invisible or obscured by another element", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_FOCUS_01:_These_elements_are_focusable_but_either_invisible_o", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
1041
- return a.querySelectorAll(axs.utils.FOCUSABLE_ELEMENTS_SELECTOR)
1529
+ 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) {
1530
+ if(!axs.browserUtils.matchSelector(a, axs.utils.FOCUSABLE_ELEMENTS_SELECTOR)) {
1531
+ return!1
1532
+ }
1533
+ if(0 <= a.tabIndex) {
1534
+ return!0
1535
+ }
1536
+ for(a = a.parentElement;null != a;a = a.parentElement) {
1537
+ if(axs.utils.elementIsAriaWidget(a)) {
1538
+ return!1
1539
+ }
1540
+ }
1541
+ return!0
1042
1542
  }, test:function(a) {
1043
1543
  return axs.utils.isElementOrAncestorHidden(a) ? !1 : !axs.utils.elementIsVisible(a)
1044
1544
  }, code:"AX_FOCUS_01"};
1045
- axs.AuditRule.specs.imagesWithoutAltText = {name:"imagesWithoutAltText", heading:"Images should have an alt attribute", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_TEXT_02:_Images_should_have_an_alt_attribute,_unless_they_hav", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
1046
- a = a.querySelectorAll("img");
1047
- for(var b = [], c = 0;c < a.length;c++) {
1048
- var d = a[c];
1049
- axs.utils.isElementOrAncestorHidden(d) || b.push(d)
1050
- }
1051
- return b
1545
+ 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) {
1546
+ return axs.browserUtils.matchSelector(a, "img") && !axs.utils.isElementOrAncestorHidden(a)
1052
1547
  }, test:function(a) {
1053
1548
  return!a.hasAttribute("alt") && "presentation" != a.getAttribute("role")
1054
1549
  }, code:"AX_TEXT_02"};
1055
- 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, relevantNodesSelector:function(a) {
1056
- return a.querySelectorAll("a")
1550
+ 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) {
1551
+ return axs.browserUtils.matchSelector(a, "a")
1057
1552
  }, test:function(a) {
1058
1553
  return/^\s*click\s*here\s*[^a-z]?$/i.test(a.textContent)
1059
1554
  }, code:"AX_TITLE_01"};
1060
- axs.AuditRule.specs.lowContrastElements = {name:"lowContrastElements", heading:"Text elements should have a reasonable contrast ratio", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_COLOR_01:_Text_elements_should_have_a_reasonable_contrast_rat", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
1061
- return document.evaluate('/html/body//text()[normalize-space(.)!=""]/parent::*[name()!="script"]', a, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null)
1555
+ 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) {
1556
+ return axs.properties.hasDirectTextDescendant(a)
1062
1557
  }, test:function(a) {
1063
1558
  var b = window.getComputedStyle(a, null);
1064
1559
  return(a = axs.utils.getContrastRatioForElementWithComputedStyle(b, a)) && axs.utils.isLowContrast(a, b)
1065
1560
  }, code:"AX_COLOR_01"};
1066
- axs.AuditRule.specs.elementsWithMeaningfulBackgroundImage = {name:"elementsWithMeaningfulBackgroundImage", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
1067
- a = a.querySelectorAll("*");
1068
- for(var b = [], c = 0;c < a.length;c++) {
1069
- var d = a[c];
1070
- axs.utils.isElementOrAncestorHidden(d) || b.push(d)
1561
+ axs.AuditRule.specs.mainRoleOnInappropriateElement = {name:"mainRoleOnInappropriateElement", heading:"role=main should only appear on significant elements", url:"", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
1562
+ return axs.browserUtils.matchSelector(a, "[role~=main]")
1563
+ }, test:function(a) {
1564
+ if(axs.utils.isInlineElement(a)) {
1565
+ return!0
1071
1566
  }
1072
- return b
1073
- }, heading:"Meaningful images should not be used in element backgrounds", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules?ts=1368336558&updated=AuditRules#AX_IMAGE_01:_Meaningful_images_should_not_be_used_in_element_bac", test:function(a) {
1567
+ a = axs.properties.getTextFromDescendantContent(a);
1568
+ return!a || 50 > a.length ? !0 : !1
1569
+ }, code:"AX_ARIA_04"};
1570
+ axs.AuditRule.specs.elementsWithMeaningfulBackgroundImage = {name:"elementsWithMeaningfulBackgroundImage", severity:axs.constants.Severity.WARNING, relevantElementMatcher:function(a) {
1571
+ return!axs.utils.isElementOrAncestorHidden(a)
1572
+ }, 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) {
1074
1573
  if(a.textContent && 0 < a.textContent.length) {
1075
1574
  return!1
1076
1575
  }
1077
1576
  a = window.getComputedStyle(a, null);
1078
1577
  var b = a.backgroundImage;
1079
- if(!b || "undefined" === b || "none" === b) {
1578
+ if(!b || "undefined" === b || "none" === b || 0 != b.indexOf("url")) {
1080
1579
  return!1
1081
1580
  }
1082
1581
  b = parseInt(a.width, 10);
1083
1582
  a = parseInt(a.height, 10);
1084
1583
  return 150 > b && 150 > a
1085
1584
  }, code:"AX_IMAGE_01"};
1086
- axs.AuditRule.specs.nonExistentAriaLabelledbyElement = {name:"nonExistentAriaLabelledbyElement", heading:"aria-labelledby attributes should refer to an element which exists in the DOM", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_ARIA_02:__aria-labelledby_attributes_should_refer_to_an_eleme", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
1087
- return a.querySelectorAll("[aria-labelledby]")
1585
+ 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) {
1586
+ return axs.browserUtils.matchSelector(a, "[aria-labelledby]")
1088
1587
  }, test:function(a) {
1089
1588
  a = a.getAttribute("aria-labelledby").split(/\s+/);
1090
1589
  for(var b = 0;b < a.length;b++) {
@@ -1094,47 +1593,37 @@ axs.AuditRule.specs.nonExistentAriaLabelledbyElement = {name:"nonExistentAriaLab
1094
1593
  }
1095
1594
  return!1
1096
1595
  }, code:"AX_ARIA_02"};
1097
- 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, relevantNodesSelector:function(a) {
1098
- return a
1596
+ 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) {
1597
+ return"html" == a.tagName.toLowerCase()
1099
1598
  }, test:function(a) {
1100
1599
  a = a.querySelector("head");
1101
- if(!a) {
1102
- return!0
1103
- }
1104
- a = a.querySelector("title");
1105
- return!a.length || !a[0].textContent
1600
+ return a ? (a = a.querySelector("title")) ? !a.textContent : !0 : !0
1106
1601
  }, code:"AX_TITLE_01"};
1107
- axs.AuditRule.specs.requiredAriaAttributeMissing = {name:"requiredAriaAttributeMissing", heading:"Elements with ARIA roles must have all required attributes for that role", url:"", severity:axs.constants.Severity.SEVERE, relevantNodesSelector:function(a) {
1108
- return a.querySelectorAll("[role]")
1602
+ 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) {
1603
+ return axs.browserUtils.matchSelector(a, "[role]")
1109
1604
  }, test:function(a) {
1110
- var b = axs.utils.getRole(a);
1605
+ var b = axs.utils.getRoles(a);
1111
1606
  if(!b.valid) {
1112
1607
  return!1
1113
1608
  }
1114
- var b = b.details.requiredPropertiesSet, c;
1115
- for(c in b) {
1116
- b = c.replace(/^aria-/, "");
1117
- if("defaultValue" in axs.constants.ARIA_PROPERTIES[b]) {
1118
- return!1
1119
- }
1120
- if(!a.hasAttribute(c)) {
1121
- return!0
1609
+ for(var c = 0;c < b.roles.length;c++) {
1610
+ var d = b.roles[c].details.requiredPropertiesSet, e;
1611
+ for(e in d) {
1612
+ if(d = e.replace(/^aria-/, ""), !("defaultValue" in axs.constants.ARIA_PROPERTIES[d] || a.hasAttribute(e))) {
1613
+ return!0
1614
+ }
1122
1615
  }
1123
1616
  }
1124
1617
  }, code:"AX_ARIA_03"};
1125
- axs.AuditRule.specs.unfocusableElementsWithOnClick = {name:"unfocusableElementsWithOnClick", heading:"Elements with onclick handlers must be focusable", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_FOCUS_02:_Elements_with_onclick_handlers_must_be_focusable", severity:axs.constants.Severity.WARNING, opt_requiresConsoleAPI:!0, relevantNodesSelector:function(a) {
1126
- a = a.querySelectorAll("*");
1127
- for(var b = [], c = 0;c < a.length;c++) {
1128
- var d = a[c];
1129
- d instanceof d.ownerDocument.defaultView.HTMLBodyElement || axs.utils.isElementOrAncestorHidden(d) || "click" in getEventListeners(d) && b.push(d)
1130
- }
1131
- return b
1618
+ 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) {
1619
+ return a instanceof a.ownerDocument.defaultView.HTMLBodyElement || axs.utils.isElementOrAncestorHidden(a) ? !1 : "click" in getEventListeners(a) ? !0 : !1
1132
1620
  }, test:function(a) {
1133
1621
  return!a.hasAttribute("tabindex") && !axs.utils.isElementImplicitlyFocusable(a)
1134
1622
  }, code:"AX_FOCUS_02"};
1135
- axs.AuditRule.specs.videoWithoutCaptions = {name:"videoWithoutCaptions", heading:"Video elements should use <track> elements to provide captions", url:"https://code.google.com/p/accessibility-developer-tools/wiki/AuditRules#AX_VIDEO_01:_Video_elements_should_use_<track>_elements_to", severity:axs.constants.Severity.WARNING, relevantNodesSelector:function(a) {
1136
- return a.querySelectorAll("video")
1623
+ 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) {
1624
+ return axs.browserUtils.matchSelector(a, "video")
1137
1625
  }, test:function(a) {
1138
1626
  return!a.querySelectorAll("track[kind=captions]").length
1139
1627
  }, code:"AX_VIDEO_01"};
1140
1628
 
1629
+