capybara-accessible 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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
+