├── LICENSE.md ├── README.md ├── angular-aria.js ├── angular-aria.min.js ├── angular-aria.min.js.map ├── bower.json ├── index.js └── package.json /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Angular 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # packaged angular-aria 2 | 3 | **This package contains the legacy AngularJS (version 1.x). AngularJS support has officially ended 4 | as of January 2022. 5 | [See what ending support means](https://docs.angularjs.org/misc/version-support-status) and 6 | [read the end of life announcement](https://goo.gle/angularjs-end-of-life).** 7 | 8 | **[See `@angular/core` for the actively supported Angular](https://npmjs.com/@angular/core).** 9 | 10 | ## Install 11 | 12 | You can install this package either with `npm` or with `bower`. 13 | 14 | ### npm 15 | 16 | ```shell 17 | npm install angular-aria 18 | ``` 19 | Then add `ngAria` as a dependency for your app: 20 | 21 | ```javascript 22 | angular.module('myApp', [require('angular-aria')]); 23 | ``` 24 | 25 | ### bower 26 | 27 | ```shell 28 | bower install angular-aria 29 | ``` 30 | 31 | Add a ` 35 | ``` 36 | 37 | Then add `ngAria` as a dependency for your app: 38 | 39 | ```javascript 40 | angular.module('myApp', ['ngAria']); 41 | ``` 42 | 43 | ## Documentation 44 | 45 | Documentation is available on the 46 | [AngularJS docs site](http://docs.angularjs.org/api/ngAria). 47 | 48 | ## License 49 | 50 | The MIT License 51 | 52 | Copyright (c) 2022 Google LLC 53 | 54 | Permission is hereby granted, free of charge, to any person obtaining a copy 55 | of this software and associated documentation files (the "Software"), to deal 56 | in the Software without restriction, including without limitation the rights 57 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 58 | copies of the Software, and to permit persons to whom the Software is 59 | furnished to do so, subject to the following conditions: 60 | 61 | The above copyright notice and this permission notice shall be included in 62 | all copies or substantial portions of the Software. 63 | 64 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 65 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 66 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 67 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 68 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 69 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 70 | THE SOFTWARE. 71 | -------------------------------------------------------------------------------- /angular-aria.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.8.3 3 | * (c) 2010-2020 Google LLC. http://angularjs.org 4 | * License: MIT 5 | */ 6 | (function(window, angular) {'use strict'; 7 | 8 | /** 9 | * @ngdoc module 10 | * @name ngAria 11 | * @description 12 | * 13 | * The `ngAria` module provides support for common 14 | * [ARIA](http://www.w3.org/TR/wai-aria/) 15 | * attributes that convey state or semantic information about the application for users 16 | * of assistive technologies, such as screen readers. 17 | * 18 | * ## Usage 19 | * 20 | * For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following 21 | * directives are supported: 22 | * `ngModel`, `ngChecked`, `ngReadonly`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, 23 | * `ngClick`, `ngDblClick`, and `ngMessages`. 24 | * 25 | * Below is a more detailed breakdown of the attributes handled by ngAria: 26 | * 27 | * | Directive | Supported Attributes | 28 | * |---------------------------------------------|-----------------------------------------------------------------------------------------------------| 29 | * | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles | 30 | * | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled | 31 | * | {@link ng.directive:ngRequired ngRequired} | aria-required | 32 | * | {@link ng.directive:ngChecked ngChecked} | aria-checked | 33 | * | {@link ng.directive:ngReadonly ngReadonly} | aria-readonly | 34 | * | {@link ng.directive:ngValue ngValue} | aria-checked | 35 | * | {@link ng.directive:ngShow ngShow} | aria-hidden | 36 | * | {@link ng.directive:ngHide ngHide} | aria-hidden | 37 | * | {@link ng.directive:ngDblclick ngDblclick} | tabindex | 38 | * | {@link module:ngMessages ngMessages} | aria-live | 39 | * | {@link ng.directive:ngClick ngClick} | tabindex, keydown event, button role | 40 | * 41 | * Find out more information about each directive by reading the 42 | * {@link guide/accessibility ngAria Developer Guide}. 43 | * 44 | * ## Example 45 | * Using ngDisabled with ngAria: 46 | * ```html 47 | * 48 | * ``` 49 | * Becomes: 50 | * ```html 51 | * 52 | * ``` 53 | * 54 | * ## Disabling Specific Attributes 55 | * It is possible to disable individual attributes added by ngAria with the 56 | * {@link ngAria.$ariaProvider#config config} method. For more details, see the 57 | * {@link guide/accessibility Developer Guide}. 58 | * 59 | * ## Disabling `ngAria` on Specific Elements 60 | * It is possible to make `ngAria` ignore a specific element, by adding the `ng-aria-disable` 61 | * attribute on it. Note that only the element itself (and not its child elements) will be ignored. 62 | */ 63 | var ARIA_DISABLE_ATTR = 'ngAriaDisable'; 64 | 65 | var ngAriaModule = angular.module('ngAria', ['ng']). 66 | info({ angularVersion: '1.8.3' }). 67 | provider('$aria', $AriaProvider); 68 | 69 | /** 70 | * Internal Utilities 71 | */ 72 | var nativeAriaNodeNames = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY']; 73 | 74 | var isNodeOneOf = function(elem, nodeTypeArray) { 75 | if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) { 76 | return true; 77 | } 78 | }; 79 | /** 80 | * @ngdoc provider 81 | * @name $ariaProvider 82 | * @this 83 | * 84 | * @description 85 | * 86 | * Used for configuring the ARIA attributes injected and managed by ngAria. 87 | * 88 | * ```js 89 | * angular.module('myApp', ['ngAria'], function config($ariaProvider) { 90 | * $ariaProvider.config({ 91 | * ariaValue: true, 92 | * tabindex: false 93 | * }); 94 | * }); 95 | *``` 96 | * 97 | * ## Dependencies 98 | * Requires the {@link ngAria} module to be installed. 99 | * 100 | */ 101 | function $AriaProvider() { 102 | var config = { 103 | ariaHidden: true, 104 | ariaChecked: true, 105 | ariaReadonly: true, 106 | ariaDisabled: true, 107 | ariaRequired: true, 108 | ariaInvalid: true, 109 | ariaValue: true, 110 | tabindex: true, 111 | bindKeydown: true, 112 | bindRoleForClick: true 113 | }; 114 | 115 | /** 116 | * @ngdoc method 117 | * @name $ariaProvider#config 118 | * 119 | * @param {object} config object to enable/disable specific ARIA attributes 120 | * 121 | * - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags 122 | * - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags 123 | * - **ariaReadonly** – `{boolean}` – Enables/disables aria-readonly tags 124 | * - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags 125 | * - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags 126 | * - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags 127 | * - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and 128 | * aria-valuenow tags 129 | * - **tabindex** – `{boolean}` – Enables/disables tabindex tags 130 | * - **bindKeydown** – `{boolean}` – Enables/disables keyboard event binding on non-interactive 131 | * elements (such as `div` or `li`) using ng-click, making them more accessible to users of 132 | * assistive technologies 133 | * - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements (such as 134 | * `div` or `li`) using ng-click, making them more accessible to users of assistive 135 | * technologies 136 | * 137 | * @description 138 | * Enables/disables various ARIA attributes 139 | */ 140 | this.config = function(newConfig) { 141 | config = angular.extend(config, newConfig); 142 | }; 143 | 144 | function watchExpr(attrName, ariaAttr, nativeAriaNodeNames, negate) { 145 | return function(scope, elem, attr) { 146 | if (attr.hasOwnProperty(ARIA_DISABLE_ATTR)) return; 147 | 148 | var ariaCamelName = attr.$normalize(ariaAttr); 149 | if (config[ariaCamelName] && !isNodeOneOf(elem, nativeAriaNodeNames) && !attr[ariaCamelName]) { 150 | scope.$watch(attr[attrName], function(boolVal) { 151 | // ensure boolean value 152 | boolVal = negate ? !boolVal : !!boolVal; 153 | elem.attr(ariaAttr, boolVal); 154 | }); 155 | } 156 | }; 157 | } 158 | /** 159 | * @ngdoc service 160 | * @name $aria 161 | * 162 | * @description 163 | * 164 | * The $aria service contains helper methods for applying common 165 | * [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives. 166 | * 167 | * ngAria injects common accessibility attributes that tell assistive technologies when HTML 168 | * elements are enabled, selected, hidden, and more. To see how this is performed with ngAria, 169 | * let's review a code snippet from ngAria itself: 170 | * 171 | *```js 172 | * ngAriaModule.directive('ngDisabled', ['$aria', function($aria) { 173 | * return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nativeAriaNodeNames, false); 174 | * }]) 175 | *``` 176 | * Shown above, the ngAria module creates a directive with the same signature as the 177 | * traditional `ng-disabled` directive. But this ngAria version is dedicated to 178 | * solely managing accessibility attributes on custom elements. The internal `$aria` service is 179 | * used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the 180 | * developer, `aria-disabled` is injected as an attribute with its value synchronized to the 181 | * value in `ngDisabled`. 182 | * 183 | * Because ngAria hooks into the `ng-disabled` directive, developers do not have to do 184 | * anything to enable this feature. The `aria-disabled` attribute is automatically managed 185 | * simply as a silent side-effect of using `ng-disabled` with the ngAria module. 186 | * 187 | * The full list of directives that interface with ngAria: 188 | * * **ngModel** 189 | * * **ngChecked** 190 | * * **ngReadonly** 191 | * * **ngRequired** 192 | * * **ngDisabled** 193 | * * **ngValue** 194 | * * **ngShow** 195 | * * **ngHide** 196 | * * **ngClick** 197 | * * **ngDblclick** 198 | * * **ngMessages** 199 | * 200 | * Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each 201 | * directive. 202 | * 203 | * 204 | * ## Dependencies 205 | * Requires the {@link ngAria} module to be installed. 206 | */ 207 | this.$get = function() { 208 | return { 209 | config: function(key) { 210 | return config[key]; 211 | }, 212 | $$watchExpr: watchExpr 213 | }; 214 | }; 215 | } 216 | 217 | 218 | ngAriaModule.directive('ngShow', ['$aria', function($aria) { 219 | return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true); 220 | }]) 221 | .directive('ngHide', ['$aria', function($aria) { 222 | return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false); 223 | }]) 224 | .directive('ngValue', ['$aria', function($aria) { 225 | return $aria.$$watchExpr('ngValue', 'aria-checked', nativeAriaNodeNames, false); 226 | }]) 227 | .directive('ngChecked', ['$aria', function($aria) { 228 | return $aria.$$watchExpr('ngChecked', 'aria-checked', nativeAriaNodeNames, false); 229 | }]) 230 | .directive('ngReadonly', ['$aria', function($aria) { 231 | return $aria.$$watchExpr('ngReadonly', 'aria-readonly', nativeAriaNodeNames, false); 232 | }]) 233 | .directive('ngRequired', ['$aria', function($aria) { 234 | return $aria.$$watchExpr('ngRequired', 'aria-required', nativeAriaNodeNames, false); 235 | }]) 236 | .directive('ngModel', ['$aria', function($aria) { 237 | 238 | function shouldAttachAttr(attr, normalizedAttr, elem, allowNonAriaNodes) { 239 | return $aria.config(normalizedAttr) && 240 | !elem.attr(attr) && 241 | (allowNonAriaNodes || !isNodeOneOf(elem, nativeAriaNodeNames)) && 242 | (elem.attr('type') !== 'hidden' || elem[0].nodeName !== 'INPUT'); 243 | } 244 | 245 | function shouldAttachRole(role, elem) { 246 | // if element does not have role attribute 247 | // AND element type is equal to role (if custom element has a type equaling shape) <-- remove? 248 | // AND element is not in nativeAriaNodeNames 249 | return !elem.attr('role') && (elem.attr('type') === role) && !isNodeOneOf(elem, nativeAriaNodeNames); 250 | } 251 | 252 | function getShape(attr, elem) { 253 | var type = attr.type, 254 | role = attr.role; 255 | 256 | return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' : 257 | ((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' : 258 | (type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' : ''; 259 | } 260 | 261 | return { 262 | restrict: 'A', 263 | require: 'ngModel', 264 | priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value 265 | compile: function(elem, attr) { 266 | if (attr.hasOwnProperty(ARIA_DISABLE_ATTR)) return; 267 | 268 | var shape = getShape(attr, elem); 269 | 270 | return { 271 | post: function(scope, elem, attr, ngModel) { 272 | var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false); 273 | 274 | function ngAriaWatchModelValue() { 275 | return ngModel.$modelValue; 276 | } 277 | 278 | function getRadioReaction(newVal) { 279 | // Strict comparison would cause a BC 280 | // eslint-disable-next-line eqeqeq 281 | var boolVal = (attr.value == ngModel.$viewValue); 282 | elem.attr('aria-checked', boolVal); 283 | } 284 | 285 | function getCheckboxReaction() { 286 | elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue)); 287 | } 288 | 289 | switch (shape) { 290 | case 'radio': 291 | case 'checkbox': 292 | if (shouldAttachRole(shape, elem)) { 293 | elem.attr('role', shape); 294 | } 295 | if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) { 296 | scope.$watch(ngAriaWatchModelValue, shape === 'radio' ? 297 | getRadioReaction : getCheckboxReaction); 298 | } 299 | if (needsTabIndex) { 300 | elem.attr('tabindex', 0); 301 | } 302 | break; 303 | case 'range': 304 | if (shouldAttachRole(shape, elem)) { 305 | elem.attr('role', 'slider'); 306 | } 307 | if ($aria.config('ariaValue')) { 308 | var needsAriaValuemin = !elem.attr('aria-valuemin') && 309 | (attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin')); 310 | var needsAriaValuemax = !elem.attr('aria-valuemax') && 311 | (attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax')); 312 | var needsAriaValuenow = !elem.attr('aria-valuenow'); 313 | 314 | if (needsAriaValuemin) { 315 | attr.$observe('min', function ngAriaValueMinReaction(newVal) { 316 | elem.attr('aria-valuemin', newVal); 317 | }); 318 | } 319 | if (needsAriaValuemax) { 320 | attr.$observe('max', function ngAriaValueMinReaction(newVal) { 321 | elem.attr('aria-valuemax', newVal); 322 | }); 323 | } 324 | if (needsAriaValuenow) { 325 | scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) { 326 | elem.attr('aria-valuenow', newVal); 327 | }); 328 | } 329 | } 330 | if (needsTabIndex) { 331 | elem.attr('tabindex', 0); 332 | } 333 | break; 334 | } 335 | 336 | if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required 337 | && shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) { 338 | // ngModel.$error.required is undefined on custom controls 339 | attr.$observe('required', function() { 340 | elem.attr('aria-required', !!attr['required']); 341 | }); 342 | } 343 | 344 | if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) { 345 | scope.$watch(function ngAriaInvalidWatch() { 346 | return ngModel.$invalid; 347 | }, function ngAriaInvalidReaction(newVal) { 348 | elem.attr('aria-invalid', !!newVal); 349 | }); 350 | } 351 | } 352 | }; 353 | } 354 | }; 355 | }]) 356 | .directive('ngDisabled', ['$aria', function($aria) { 357 | return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nativeAriaNodeNames, false); 358 | }]) 359 | .directive('ngMessages', function() { 360 | return { 361 | restrict: 'A', 362 | require: '?ngMessages', 363 | link: function(scope, elem, attr, ngMessages) { 364 | if (attr.hasOwnProperty(ARIA_DISABLE_ATTR)) return; 365 | 366 | if (!elem.attr('aria-live')) { 367 | elem.attr('aria-live', 'assertive'); 368 | } 369 | } 370 | }; 371 | }) 372 | .directive('ngClick',['$aria', '$parse', function($aria, $parse) { 373 | return { 374 | restrict: 'A', 375 | compile: function(elem, attr) { 376 | if (attr.hasOwnProperty(ARIA_DISABLE_ATTR)) return; 377 | 378 | var fn = $parse(attr.ngClick); 379 | return function(scope, elem, attr) { 380 | 381 | if (!isNodeOneOf(elem, nativeAriaNodeNames)) { 382 | 383 | if ($aria.config('bindRoleForClick') && !elem.attr('role')) { 384 | elem.attr('role', 'button'); 385 | } 386 | 387 | if ($aria.config('tabindex') && !elem.attr('tabindex')) { 388 | elem.attr('tabindex', 0); 389 | } 390 | 391 | if ($aria.config('bindKeydown') && !attr.ngKeydown && !attr.ngKeypress && !attr.ngKeyup) { 392 | elem.on('keydown', function(event) { 393 | var keyCode = event.which || event.keyCode; 394 | 395 | if (keyCode === 13 || keyCode === 32) { 396 | // If the event is triggered on a non-interactive element ... 397 | if (nativeAriaNodeNames.indexOf(event.target.nodeName) === -1 && !event.target.isContentEditable) { 398 | // ... prevent the default browser behavior (e.g. scrolling when pressing spacebar) 399 | // See https://github.com/angular/angular.js/issues/16664 400 | event.preventDefault(); 401 | } 402 | scope.$apply(callback); 403 | } 404 | 405 | function callback() { 406 | fn(scope, { $event: event }); 407 | } 408 | }); 409 | } 410 | } 411 | }; 412 | } 413 | }; 414 | }]) 415 | .directive('ngDblclick', ['$aria', function($aria) { 416 | return function(scope, elem, attr) { 417 | if (attr.hasOwnProperty(ARIA_DISABLE_ATTR)) return; 418 | 419 | if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nativeAriaNodeNames)) { 420 | elem.attr('tabindex', 0); 421 | } 422 | }; 423 | }]); 424 | 425 | 426 | })(window, window.angular); 427 | -------------------------------------------------------------------------------- /angular-aria.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.8.3 3 | (c) 2010-2020 Google LLC. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(t,l){'use strict';var c="BUTTON A INPUT TEXTAREA SELECT DETAILS SUMMARY".split(" "),m=function(a,e){if(-1!==e.indexOf(a[0].nodeName))return!0};l.module("ngAria",["ng"]).info({angularVersion:"1.8.3"}).provider("$aria",function(){function a(a,c,n,g){return function(d,f,b){if(!b.hasOwnProperty("ngAriaDisable")){var p=b.$normalize(c);!e[p]||m(f,n)||b[p]||d.$watch(b[a],function(b){b=g?!b:!!b;f.attr(c,b)})}}}var e={ariaHidden:!0,ariaChecked:!0,ariaReadonly:!0,ariaDisabled:!0,ariaRequired:!0,ariaInvalid:!0, 7 | ariaValue:!0,tabindex:!0,bindKeydown:!0,bindRoleForClick:!0};this.config=function(a){e=l.extend(e,a)};this.$get=function(){return{config:function(a){return e[a]},$$watchExpr:a}}}).directive("ngShow",["$aria",function(a){return a.$$watchExpr("ngShow","aria-hidden",[],!0)}]).directive("ngHide",["$aria",function(a){return a.$$watchExpr("ngHide","aria-hidden",[],!1)}]).directive("ngValue",["$aria",function(a){return a.$$watchExpr("ngValue","aria-checked",c,!1)}]).directive("ngChecked",["$aria",function(a){return a.$$watchExpr("ngChecked", 8 | "aria-checked",c,!1)}]).directive("ngReadonly",["$aria",function(a){return a.$$watchExpr("ngReadonly","aria-readonly",c,!1)}]).directive("ngRequired",["$aria",function(a){return a.$$watchExpr("ngRequired","aria-required",c,!1)}]).directive("ngModel",["$aria",function(a){function e(e,g,d,f){return a.config(g)&&!d.attr(e)&&(f||!m(d,c))&&("hidden"!==d.attr("type")||"INPUT"!==d[0].nodeName)}function k(a,e){return!e.attr("role")&&e.attr("type")===a&&!m(e,c)}function h(a,e){var d=a.type,f=a.role;return"checkbox"=== 9 | (d||f)||"menuitemcheckbox"===f?"checkbox":"radio"===(d||f)||"menuitemradio"===f?"radio":"range"===d||"progressbar"===f||"slider"===f?"range":""}return{restrict:"A",require:"ngModel",priority:200,compile:function(c,g){if(!g.hasOwnProperty("ngAriaDisable")){var d=h(g,c);return{post:function(f,b,c,g){function h(){return g.$modelValue}function m(a){b.attr("aria-checked",c.value==g.$viewValue)}function n(){b.attr("aria-checked",!g.$isEmpty(g.$viewValue))}var l=e("tabindex","tabindex",b,!1);switch(d){case "radio":case "checkbox":k(d, 10 | b)&&b.attr("role",d);e("aria-checked","ariaChecked",b,!1)&&f.$watch(h,"radio"===d?m:n);l&&b.attr("tabindex",0);break;case "range":k(d,b)&&b.attr("role","slider");if(a.config("ariaValue")){var q=!b.attr("aria-valuemin")&&(c.hasOwnProperty("min")||c.hasOwnProperty("ngMin")),r=!b.attr("aria-valuemax")&&(c.hasOwnProperty("max")||c.hasOwnProperty("ngMax")),s=!b.attr("aria-valuenow");q&&c.$observe("min",function(a){b.attr("aria-valuemin",a)});r&&c.$observe("max",function(a){b.attr("aria-valuemax",a)}); 11 | s&&f.$watch(h,function(a){b.attr("aria-valuenow",a)})}l&&b.attr("tabindex",0)}!c.hasOwnProperty("ngRequired")&&g.$validators.required&&e("aria-required","ariaRequired",b,!1)&&c.$observe("required",function(){b.attr("aria-required",!!c.required)});e("aria-invalid","ariaInvalid",b,!0)&&f.$watch(function(){return g.$invalid},function(a){b.attr("aria-invalid",!!a)})}}}}}}]).directive("ngDisabled",["$aria",function(a){return a.$$watchExpr("ngDisabled","aria-disabled",c,!1)}]).directive("ngMessages",function(){return{restrict:"A", 12 | require:"?ngMessages",link:function(a,c,k,h){k.hasOwnProperty("ngAriaDisable")||c.attr("aria-live")||c.attr("aria-live","assertive")}}}).directive("ngClick",["$aria","$parse",function(a,e){return{restrict:"A",compile:function(k,h){if(!h.hasOwnProperty("ngAriaDisable")){var l=e(h.ngClick);return function(e,d,f){if(!m(d,c)&&(a.config("bindRoleForClick")&&!d.attr("role")&&d.attr("role","button"),a.config("tabindex")&&!d.attr("tabindex")&&d.attr("tabindex",0),a.config("bindKeydown")&&!f.ngKeydown&&!f.ngKeypress&& 13 | !f.ngKeyup))d.on("keydown",function(a){function d(){l(e,{$event:a})}var f=a.which||a.keyCode;if(13===f||32===f)-1!==c.indexOf(a.target.nodeName)||a.target.isContentEditable||a.preventDefault(),e.$apply(d)})}}}}}]).directive("ngDblclick",["$aria",function(a){return function(e,k,h){h.hasOwnProperty("ngAriaDisable")||!a.config("tabindex")||k.attr("tabindex")||m(k,c)||k.attr("tabindex",0)}}])})(window,window.angular); 14 | //# sourceMappingURL=angular-aria.min.js.map 15 | -------------------------------------------------------------------------------- /angular-aria.min.js.map: -------------------------------------------------------------------------------- 1 | { 2 | "version":3, 3 | "file":"angular-aria.min.js", 4 | "lineCount":13, 5 | "mappings":"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", 6 | "sources":["angular-aria.js"], 7 | "names":["window","angular","nativeAriaNodeNames","isNodeOneOf","elem","nodeTypeArray","indexOf","nodeName","module","info","angularVersion","provider","ngAriaModule","$AriaProvider","watchExpr","attrName","ariaAttr","negate","scope","attr","hasOwnProperty","ARIA_DISABLE_ATTR","ariaCamelName","$normalize","config","$watch","boolVal","ariaHidden","ariaChecked","ariaReadonly","ariaDisabled","ariaRequired","ariaInvalid","ariaValue","tabindex","bindKeydown","bindRoleForClick","this.config","newConfig","extend","$get","this.$get","key","$$watchExpr","directive","$aria","shouldAttachAttr","normalizedAttr","allowNonAriaNodes","shouldAttachRole","role","getShape","type","restrict","require","priority","compile","shape","post","ngModel","ngAriaWatchModelValue","$modelValue","getRadioReaction","newVal","value","$viewValue","getCheckboxReaction","$isEmpty","needsTabIndex","needsAriaValuemin","needsAriaValuemax","needsAriaValuenow","$observe","ngAriaValueMinReaction","ngAriaValueNowReaction","$validators","required","ngAriaInvalidWatch","$invalid","ngAriaInvalidReaction","link","ngMessages","$parse","fn","ngClick","ngKeydown","ngKeypress","ngKeyup","on","event","callback","$event","keyCode","which","target","isContentEditable","preventDefault","$apply"] 8 | } 9 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-aria", 3 | "version": "1.8.3", 4 | "license": "MIT", 5 | "main": "./angular-aria.js", 6 | "ignore": [], 7 | "dependencies": { 8 | "angular": "1.8.3" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | require('./angular-aria'); 2 | module.exports = 'ngAria'; 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-aria", 3 | "version": "1.8.3", 4 | "description": "AngularJS module for making accessibility easy", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "accessibility", 18 | "a11y", 19 | "client-side" 20 | ], 21 | "author": "Angular Core Team ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/angular/angular.js/issues" 25 | }, 26 | "homepage": "http://angularjs.org", 27 | "jspm": { 28 | "shim": { 29 | "angular-aria": { 30 | "deps": ["angular"] 31 | } 32 | } 33 | } 34 | } 35 | --------------------------------------------------------------------------------