Issues (1588)

myems-admin/js/angular/angular-aria.js (2 issues)

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
 * [<abbr title="Accessible Rich Internet Applications">ARIA</abbr>](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
 * <md-checkbox ng-disabled="disabled">
48
 * ```
49
 * Becomes:
50
 * ```html
51
 * <md-checkbox ng-disabled="disabled" aria-disabled="true">
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;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
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;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
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