Initial OpenECOMP policy/engine commit
[policy/engine.git] / ecomp-sdk-app / src / main / webapp / app / fusion / external / angular-1.5 / angular-aria.js
1 /**
2  * @license AngularJS v1.5.0
3  * (c) 2010-2016 Google, Inc. http://angularjs.org
4  * License: MIT
5  */
6 (function(window, angular, undefined) {'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  * <div doc-module-components="ngAria"></div>
19  *
20  * ## Usage
21  *
22  * For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following
23  * directives are supported:
24  * `ngModel`, `ngChecked`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`,
25  * `ngDblClick`, and `ngMessages`.
26  *
27  * Below is a more detailed breakdown of the attributes handled by ngAria:
28  *
29  * | Directive                                   | Supported Attributes                                                                   |
30  * |---------------------------------------------|----------------------------------------------------------------------------------------|
31  * | {@link ng.directive:ngModel ngModel}        | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles |
32  * | {@link ng.directive:ngDisabled ngDisabled}  | aria-disabled                                                                          |
33  * | {@link ng.directive:ngRequired ngRequired}  | aria-required                                                                          |
34  * | {@link ng.directive:ngChecked ngChecked}    | aria-checked                                                                           |
35  * | {@link ng.directive:ngValue ngValue}        | aria-checked                                                                           |
36  * | {@link ng.directive:ngShow ngShow}          | aria-hidden                                                                            |
37  * | {@link ng.directive:ngHide ngHide}          | aria-hidden                                                                            |
38  * | {@link ng.directive:ngDblclick ngDblclick}  | tabindex                                                                               |
39  * | {@link module:ngMessages ngMessages}        | aria-live                                                                              |
40  * | {@link ng.directive:ngClick ngClick}        | tabindex, keypress event, button role                                                  |
41  *
42  * Find out more information about each directive by reading the
43  * {@link guide/accessibility ngAria Developer Guide}.
44  *
45  * ##Example
46  * Using ngDisabled with ngAria:
47  * ```html
48  * <md-checkbox ng-disabled="disabled">
49  * ```
50  * Becomes:
51  * ```html
52  * <md-checkbox ng-disabled="disabled" aria-disabled="true">
53  * ```
54  *
55  * ##Disabling Attributes
56  * It's possible to disable individual attributes added by ngAria with the
57  * {@link ngAria.$ariaProvider#config config} method. For more details, see the
58  * {@link guide/accessibility Developer Guide}.
59  */
60  /* global -ngAriaModule */
61 var ngAriaModule = angular.module('ngAria', ['ng']).
62                         provider('$aria', $AriaProvider);
63
64 /**
65 * Internal Utilities
66 */
67 var nodeBlackList = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY'];
68
69 var isNodeOneOf = function(elem, nodeTypeArray) {
70   if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) {
71     return true;
72   }
73 };
74 /**
75  * @ngdoc provider
76  * @name $ariaProvider
77  *
78  * @description
79  *
80  * Used for configuring the ARIA attributes injected and managed by ngAria.
81  *
82  * ```js
83  * angular.module('myApp', ['ngAria'], function config($ariaProvider) {
84  *   $ariaProvider.config({
85  *     ariaValue: true,
86  *     tabindex: false
87  *   });
88  * });
89  *```
90  *
91  * ## Dependencies
92  * Requires the {@link ngAria} module to be installed.
93  *
94  */
95 function $AriaProvider() {
96   var config = {
97     ariaHidden: true,
98     ariaChecked: true,
99     ariaDisabled: true,
100     ariaRequired: true,
101     ariaInvalid: true,
102     ariaValue: true,
103     tabindex: true,
104     bindKeypress: true,
105     bindRoleForClick: true
106   };
107
108   /**
109    * @ngdoc method
110    * @name $ariaProvider#config
111    *
112    * @param {object} config object to enable/disable specific ARIA attributes
113    *
114    *  - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags
115    *  - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags
116    *  - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags
117    *  - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags
118    *  - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags
119    *  - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags
120    *  - **tabindex** – `{boolean}` – Enables/disables tabindex tags
121    *  - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `div` and
122    *    `li` elements with ng-click
123    *  - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div`
124    *    using ng-click, making them more accessible to users of assistive technologies
125    *
126    * @description
127    * Enables/disables various ARIA attributes
128    */
129   this.config = function(newConfig) {
130     config = angular.extend(config, newConfig);
131   };
132
133   function watchExpr(attrName, ariaAttr, nodeBlackList, negate) {
134     return function(scope, elem, attr) {
135       var ariaCamelName = attr.$normalize(ariaAttr);
136       if (config[ariaCamelName] && !isNodeOneOf(elem, nodeBlackList) && !attr[ariaCamelName]) {
137         scope.$watch(attr[attrName], function(boolVal) {
138           // ensure boolean value
139           boolVal = negate ? !boolVal : !!boolVal;
140           elem.attr(ariaAttr, boolVal);
141         });
142       }
143     };
144   }
145   /**
146    * @ngdoc service
147    * @name $aria
148    *
149    * @description
150    * @priority 200
151    *
152    * The $aria service contains helper methods for applying common
153    * [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives.
154    *
155    * ngAria injects common accessibility attributes that tell assistive technologies when HTML
156    * elements are enabled, selected, hidden, and more. To see how this is performed with ngAria,
157    * let's review a code snippet from ngAria itself:
158    *
159    *```js
160    * ngAriaModule.directive('ngDisabled', ['$aria', function($aria) {
161    *   return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
162    * }])
163    *```
164    * Shown above, the ngAria module creates a directive with the same signature as the
165    * traditional `ng-disabled` directive. But this ngAria version is dedicated to
166    * solely managing accessibility attributes on custom elements. The internal `$aria` service is
167    * used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the
168    * developer, `aria-disabled` is injected as an attribute with its value synchronized to the
169    * value in `ngDisabled`.
170    *
171    * Because ngAria hooks into the `ng-disabled` directive, developers do not have to do
172    * anything to enable this feature. The `aria-disabled` attribute is automatically managed
173    * simply as a silent side-effect of using `ng-disabled` with the ngAria module.
174    *
175    * The full list of directives that interface with ngAria:
176    * * **ngModel**
177    * * **ngChecked**
178    * * **ngRequired**
179    * * **ngDisabled**
180    * * **ngValue**
181    * * **ngShow**
182    * * **ngHide**
183    * * **ngClick**
184    * * **ngDblclick**
185    * * **ngMessages**
186    *
187    * Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each
188    * directive.
189    *
190    *
191    * ## Dependencies
192    * Requires the {@link ngAria} module to be installed.
193    */
194   this.$get = function() {
195     return {
196       config: function(key) {
197         return config[key];
198       },
199       $$watchExpr: watchExpr
200     };
201   };
202 }
203
204
205 ngAriaModule.directive('ngShow', ['$aria', function($aria) {
206   return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true);
207 }])
208 .directive('ngHide', ['$aria', function($aria) {
209   return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false);
210 }])
211 .directive('ngValue', ['$aria', function($aria) {
212   return $aria.$$watchExpr('ngValue', 'aria-checked', nodeBlackList, false);
213 }])
214 .directive('ngChecked', ['$aria', function($aria) {
215   return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false);
216 }])
217 .directive('ngRequired', ['$aria', function($aria) {
218   return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false);
219 }])
220 .directive('ngModel', ['$aria', function($aria) {
221
222   function shouldAttachAttr(attr, normalizedAttr, elem, allowBlacklistEls) {
223     return $aria.config(normalizedAttr) && !elem.attr(attr) && (allowBlacklistEls || !isNodeOneOf(elem, nodeBlackList));
224   }
225
226   function shouldAttachRole(role, elem) {
227     // if element does not have role attribute
228     // AND element type is equal to role (if custom element has a type equaling shape) <-- remove?
229     // AND element is not INPUT
230     return !elem.attr('role') && (elem.attr('type') === role) && (elem[0].nodeName !== 'INPUT');
231   }
232
233   function getShape(attr, elem) {
234     var type = attr.type,
235         role = attr.role;
236
237     return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' :
238            ((type || role) === 'radio'    || role === 'menuitemradio') ? 'radio' :
239            (type === 'range'              || role === 'progressbar' || role === 'slider') ? 'range' : '';
240   }
241
242   return {
243     restrict: 'A',
244     require: 'ngModel',
245     priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value
246     compile: function(elem, attr) {
247       var shape = getShape(attr, elem);
248
249       return {
250         pre: function(scope, elem, attr, ngModel) {
251           if (shape === 'checkbox') {
252             //Use the input[checkbox] $isEmpty implementation for elements with checkbox roles
253             ngModel.$isEmpty = function(value) {
254               return value === false;
255             };
256           }
257         },
258         post: function(scope, elem, attr, ngModel) {
259           var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false);
260
261           function ngAriaWatchModelValue() {
262             return ngModel.$modelValue;
263           }
264
265           function getRadioReaction(newVal) {
266             var boolVal = (attr.value == ngModel.$viewValue);
267             elem.attr('aria-checked', boolVal);
268           }
269
270           function getCheckboxReaction() {
271             elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue));
272           }
273
274           switch (shape) {
275             case 'radio':
276             case 'checkbox':
277               if (shouldAttachRole(shape, elem)) {
278                 elem.attr('role', shape);
279               }
280               if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) {
281                 scope.$watch(ngAriaWatchModelValue, shape === 'radio' ?
282                     getRadioReaction : getCheckboxReaction);
283               }
284               if (needsTabIndex) {
285                 elem.attr('tabindex', 0);
286               }
287               break;
288             case 'range':
289               if (shouldAttachRole(shape, elem)) {
290                 elem.attr('role', 'slider');
291               }
292               if ($aria.config('ariaValue')) {
293                 var needsAriaValuemin = !elem.attr('aria-valuemin') &&
294                     (attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin'));
295                 var needsAriaValuemax = !elem.attr('aria-valuemax') &&
296                     (attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax'));
297                 var needsAriaValuenow = !elem.attr('aria-valuenow');
298
299                 if (needsAriaValuemin) {
300                   attr.$observe('min', function ngAriaValueMinReaction(newVal) {
301                     elem.attr('aria-valuemin', newVal);
302                   });
303                 }
304                 if (needsAriaValuemax) {
305                   attr.$observe('max', function ngAriaValueMinReaction(newVal) {
306                     elem.attr('aria-valuemax', newVal);
307                   });
308                 }
309                 if (needsAriaValuenow) {
310                   scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) {
311                     elem.attr('aria-valuenow', newVal);
312                   });
313                 }
314               }
315               if (needsTabIndex) {
316                 elem.attr('tabindex', 0);
317               }
318               break;
319           }
320
321           if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required
322             && shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) {
323             // ngModel.$error.required is undefined on custom controls
324             attr.$observe('required', function() {
325               elem.attr('aria-required', !!attr['required']);
326             });
327           }
328
329           if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) {
330             scope.$watch(function ngAriaInvalidWatch() {
331               return ngModel.$invalid;
332             }, function ngAriaInvalidReaction(newVal) {
333               elem.attr('aria-invalid', !!newVal);
334             });
335           }
336         }
337       };
338     }
339   };
340 }])
341 .directive('ngDisabled', ['$aria', function($aria) {
342   return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false);
343 }])
344 .directive('ngMessages', function() {
345   return {
346     restrict: 'A',
347     require: '?ngMessages',
348     link: function(scope, elem, attr, ngMessages) {
349       if (!elem.attr('aria-live')) {
350         elem.attr('aria-live', 'assertive');
351       }
352     }
353   };
354 })
355 .directive('ngClick',['$aria', '$parse', function($aria, $parse) {
356   return {
357     restrict: 'A',
358     compile: function(elem, attr) {
359       var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true);
360       return function(scope, elem, attr) {
361
362         if (!isNodeOneOf(elem, nodeBlackList)) {
363
364           if ($aria.config('bindRoleForClick') && !elem.attr('role')) {
365             elem.attr('role', 'button');
366           }
367
368           if ($aria.config('tabindex') && !elem.attr('tabindex')) {
369             elem.attr('tabindex', 0);
370           }
371
372           if ($aria.config('bindKeypress') && !attr.ngKeypress) {
373             elem.on('keypress', function(event) {
374               var keyCode = event.which || event.keyCode;
375               if (keyCode === 32 || keyCode === 13) {
376                 scope.$apply(callback);
377               }
378
379               function callback() {
380                 fn(scope, { $event: event });
381               }
382             });
383           }
384         }
385       };
386     }
387   };
388 }])
389 .directive('ngDblclick', ['$aria', function($aria) {
390   return function(scope, elem, attr) {
391     if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nodeBlackList)) {
392       elem.attr('tabindex', 0);
393     }
394   };
395 }]);
396
397
398 })(window, window.angular);