-
Notifications
You must be signed in to change notification settings - Fork 745
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4150e55
commit 798fbf3
Showing
2,647 changed files
with
328,703 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,398 @@ | ||
/** | ||
* @license AngularJS v1.5.0 | ||
* (c) 2010-2016 Google, Inc. http://angularjs.org | ||
* License: MIT | ||
*/ | ||
(function(window, angular, undefined) {'use strict'; | ||
|
||
/** | ||
* @ngdoc module | ||
* @name ngAria | ||
* @description | ||
* | ||
* The `ngAria` module provides support for common | ||
* [<abbr title="Accessible Rich Internet Applications">ARIA</abbr>](http://www.w3.org/TR/wai-aria/) | ||
* attributes that convey state or semantic information about the application for users | ||
* of assistive technologies, such as screen readers. | ||
* | ||
* <div doc-module-components="ngAria"></div> | ||
* | ||
* ## Usage | ||
* | ||
* For ngAria to do its magic, simply include the module `ngAria` as a dependency. The following | ||
* directives are supported: | ||
* `ngModel`, `ngChecked`, `ngRequired`, `ngValue`, `ngDisabled`, `ngShow`, `ngHide`, `ngClick`, | ||
* `ngDblClick`, and `ngMessages`. | ||
* | ||
* Below is a more detailed breakdown of the attributes handled by ngAria: | ||
* | ||
* | Directive | Supported Attributes | | ||
* |---------------------------------------------|----------------------------------------------------------------------------------------| | ||
* | {@link ng.directive:ngModel ngModel} | aria-checked, aria-valuemin, aria-valuemax, aria-valuenow, aria-invalid, aria-required, input roles | | ||
* | {@link ng.directive:ngDisabled ngDisabled} | aria-disabled | | ||
* | {@link ng.directive:ngRequired ngRequired} | aria-required | | ||
* | {@link ng.directive:ngChecked ngChecked} | aria-checked | | ||
* | {@link ng.directive:ngValue ngValue} | aria-checked | | ||
* | {@link ng.directive:ngShow ngShow} | aria-hidden | | ||
* | {@link ng.directive:ngHide ngHide} | aria-hidden | | ||
* | {@link ng.directive:ngDblclick ngDblclick} | tabindex | | ||
* | {@link module:ngMessages ngMessages} | aria-live | | ||
* | {@link ng.directive:ngClick ngClick} | tabindex, keypress event, button role | | ||
* | ||
* Find out more information about each directive by reading the | ||
* {@link guide/accessibility ngAria Developer Guide}. | ||
* | ||
* ##Example | ||
* Using ngDisabled with ngAria: | ||
* ```html | ||
* <md-checkbox ng-disabled="disabled"> | ||
* ``` | ||
* Becomes: | ||
* ```html | ||
* <md-checkbox ng-disabled="disabled" aria-disabled="true"> | ||
* ``` | ||
* | ||
* ##Disabling Attributes | ||
* It's possible to disable individual attributes added by ngAria with the | ||
* {@link ngAria.$ariaProvider#config config} method. For more details, see the | ||
* {@link guide/accessibility Developer Guide}. | ||
*/ | ||
/* global -ngAriaModule */ | ||
var ngAriaModule = angular.module('ngAria', ['ng']). | ||
provider('$aria', $AriaProvider); | ||
|
||
/** | ||
* Internal Utilities | ||
*/ | ||
var nodeBlackList = ['BUTTON', 'A', 'INPUT', 'TEXTAREA', 'SELECT', 'DETAILS', 'SUMMARY']; | ||
|
||
var isNodeOneOf = function(elem, nodeTypeArray) { | ||
if (nodeTypeArray.indexOf(elem[0].nodeName) !== -1) { | ||
return true; | ||
} | ||
}; | ||
/** | ||
* @ngdoc provider | ||
* @name $ariaProvider | ||
* | ||
* @description | ||
* | ||
* Used for configuring the ARIA attributes injected and managed by ngAria. | ||
* | ||
* ```js | ||
* angular.module('myApp', ['ngAria'], function config($ariaProvider) { | ||
* $ariaProvider.config({ | ||
* ariaValue: true, | ||
* tabindex: false | ||
* }); | ||
* }); | ||
*``` | ||
* | ||
* ## Dependencies | ||
* Requires the {@link ngAria} module to be installed. | ||
* | ||
*/ | ||
function $AriaProvider() { | ||
var config = { | ||
ariaHidden: true, | ||
ariaChecked: true, | ||
ariaDisabled: true, | ||
ariaRequired: true, | ||
ariaInvalid: true, | ||
ariaValue: true, | ||
tabindex: true, | ||
bindKeypress: true, | ||
bindRoleForClick: true | ||
}; | ||
|
||
/** | ||
* @ngdoc method | ||
* @name $ariaProvider#config | ||
* | ||
* @param {object} config object to enable/disable specific ARIA attributes | ||
* | ||
* - **ariaHidden** – `{boolean}` – Enables/disables aria-hidden tags | ||
* - **ariaChecked** – `{boolean}` – Enables/disables aria-checked tags | ||
* - **ariaDisabled** – `{boolean}` – Enables/disables aria-disabled tags | ||
* - **ariaRequired** – `{boolean}` – Enables/disables aria-required tags | ||
* - **ariaInvalid** – `{boolean}` – Enables/disables aria-invalid tags | ||
* - **ariaValue** – `{boolean}` – Enables/disables aria-valuemin, aria-valuemax and aria-valuenow tags | ||
* - **tabindex** – `{boolean}` – Enables/disables tabindex tags | ||
* - **bindKeypress** – `{boolean}` – Enables/disables keypress event binding on `div` and | ||
* `li` elements with ng-click | ||
* - **bindRoleForClick** – `{boolean}` – Adds role=button to non-interactive elements like `div` | ||
* using ng-click, making them more accessible to users of assistive technologies | ||
* | ||
* @description | ||
* Enables/disables various ARIA attributes | ||
*/ | ||
this.config = function(newConfig) { | ||
config = angular.extend(config, newConfig); | ||
}; | ||
|
||
function watchExpr(attrName, ariaAttr, nodeBlackList, negate) { | ||
return function(scope, elem, attr) { | ||
var ariaCamelName = attr.$normalize(ariaAttr); | ||
if (config[ariaCamelName] && !isNodeOneOf(elem, nodeBlackList) && !attr[ariaCamelName]) { | ||
scope.$watch(attr[attrName], function(boolVal) { | ||
// ensure boolean value | ||
boolVal = negate ? !boolVal : !!boolVal; | ||
elem.attr(ariaAttr, boolVal); | ||
}); | ||
} | ||
}; | ||
} | ||
/** | ||
* @ngdoc service | ||
* @name $aria | ||
* | ||
* @description | ||
* @priority 200 | ||
* | ||
* The $aria service contains helper methods for applying common | ||
* [ARIA](http://www.w3.org/TR/wai-aria/) attributes to HTML directives. | ||
* | ||
* ngAria injects common accessibility attributes that tell assistive technologies when HTML | ||
* elements are enabled, selected, hidden, and more. To see how this is performed with ngAria, | ||
* let's review a code snippet from ngAria itself: | ||
* | ||
*```js | ||
* ngAriaModule.directive('ngDisabled', ['$aria', function($aria) { | ||
* return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false); | ||
* }]) | ||
*``` | ||
* Shown above, the ngAria module creates a directive with the same signature as the | ||
* traditional `ng-disabled` directive. But this ngAria version is dedicated to | ||
* solely managing accessibility attributes on custom elements. The internal `$aria` service is | ||
* used to watch the boolean attribute `ngDisabled`. If it has not been explicitly set by the | ||
* developer, `aria-disabled` is injected as an attribute with its value synchronized to the | ||
* value in `ngDisabled`. | ||
* | ||
* Because ngAria hooks into the `ng-disabled` directive, developers do not have to do | ||
* anything to enable this feature. The `aria-disabled` attribute is automatically managed | ||
* simply as a silent side-effect of using `ng-disabled` with the ngAria module. | ||
* | ||
* The full list of directives that interface with ngAria: | ||
* * **ngModel** | ||
* * **ngChecked** | ||
* * **ngRequired** | ||
* * **ngDisabled** | ||
* * **ngValue** | ||
* * **ngShow** | ||
* * **ngHide** | ||
* * **ngClick** | ||
* * **ngDblclick** | ||
* * **ngMessages** | ||
* | ||
* Read the {@link guide/accessibility ngAria Developer Guide} for a thorough explanation of each | ||
* directive. | ||
* | ||
* | ||
* ## Dependencies | ||
* Requires the {@link ngAria} module to be installed. | ||
*/ | ||
this.$get = function() { | ||
return { | ||
config: function(key) { | ||
return config[key]; | ||
}, | ||
$$watchExpr: watchExpr | ||
}; | ||
}; | ||
} | ||
|
||
|
||
ngAriaModule.directive('ngShow', ['$aria', function($aria) { | ||
return $aria.$$watchExpr('ngShow', 'aria-hidden', [], true); | ||
}]) | ||
.directive('ngHide', ['$aria', function($aria) { | ||
return $aria.$$watchExpr('ngHide', 'aria-hidden', [], false); | ||
}]) | ||
.directive('ngValue', ['$aria', function($aria) { | ||
return $aria.$$watchExpr('ngValue', 'aria-checked', nodeBlackList, false); | ||
}]) | ||
.directive('ngChecked', ['$aria', function($aria) { | ||
return $aria.$$watchExpr('ngChecked', 'aria-checked', nodeBlackList, false); | ||
}]) | ||
.directive('ngRequired', ['$aria', function($aria) { | ||
return $aria.$$watchExpr('ngRequired', 'aria-required', nodeBlackList, false); | ||
}]) | ||
.directive('ngModel', ['$aria', function($aria) { | ||
|
||
function shouldAttachAttr(attr, normalizedAttr, elem, allowBlacklistEls) { | ||
return $aria.config(normalizedAttr) && !elem.attr(attr) && (allowBlacklistEls || !isNodeOneOf(elem, nodeBlackList)); | ||
} | ||
|
||
function shouldAttachRole(role, elem) { | ||
// if element does not have role attribute | ||
// AND element type is equal to role (if custom element has a type equaling shape) <-- remove? | ||
// AND element is not INPUT | ||
return !elem.attr('role') && (elem.attr('type') === role) && (elem[0].nodeName !== 'INPUT'); | ||
} | ||
|
||
function getShape(attr, elem) { | ||
var type = attr.type, | ||
role = attr.role; | ||
|
||
return ((type || role) === 'checkbox' || role === 'menuitemcheckbox') ? 'checkbox' : | ||
((type || role) === 'radio' || role === 'menuitemradio') ? 'radio' : | ||
(type === 'range' || role === 'progressbar' || role === 'slider') ? 'range' : ''; | ||
} | ||
|
||
return { | ||
restrict: 'A', | ||
require: 'ngModel', | ||
priority: 200, //Make sure watches are fired after any other directives that affect the ngModel value | ||
compile: function(elem, attr) { | ||
var shape = getShape(attr, elem); | ||
|
||
return { | ||
pre: function(scope, elem, attr, ngModel) { | ||
if (shape === 'checkbox') { | ||
//Use the input[checkbox] $isEmpty implementation for elements with checkbox roles | ||
ngModel.$isEmpty = function(value) { | ||
return value === false; | ||
}; | ||
} | ||
}, | ||
post: function(scope, elem, attr, ngModel) { | ||
var needsTabIndex = shouldAttachAttr('tabindex', 'tabindex', elem, false); | ||
|
||
function ngAriaWatchModelValue() { | ||
return ngModel.$modelValue; | ||
} | ||
|
||
function getRadioReaction(newVal) { | ||
var boolVal = (attr.value == ngModel.$viewValue); | ||
elem.attr('aria-checked', boolVal); | ||
} | ||
|
||
function getCheckboxReaction() { | ||
elem.attr('aria-checked', !ngModel.$isEmpty(ngModel.$viewValue)); | ||
} | ||
|
||
switch (shape) { | ||
case 'radio': | ||
case 'checkbox': | ||
if (shouldAttachRole(shape, elem)) { | ||
elem.attr('role', shape); | ||
} | ||
if (shouldAttachAttr('aria-checked', 'ariaChecked', elem, false)) { | ||
scope.$watch(ngAriaWatchModelValue, shape === 'radio' ? | ||
getRadioReaction : getCheckboxReaction); | ||
} | ||
if (needsTabIndex) { | ||
elem.attr('tabindex', 0); | ||
} | ||
break; | ||
case 'range': | ||
if (shouldAttachRole(shape, elem)) { | ||
elem.attr('role', 'slider'); | ||
} | ||
if ($aria.config('ariaValue')) { | ||
var needsAriaValuemin = !elem.attr('aria-valuemin') && | ||
(attr.hasOwnProperty('min') || attr.hasOwnProperty('ngMin')); | ||
var needsAriaValuemax = !elem.attr('aria-valuemax') && | ||
(attr.hasOwnProperty('max') || attr.hasOwnProperty('ngMax')); | ||
var needsAriaValuenow = !elem.attr('aria-valuenow'); | ||
|
||
if (needsAriaValuemin) { | ||
attr.$observe('min', function ngAriaValueMinReaction(newVal) { | ||
elem.attr('aria-valuemin', newVal); | ||
}); | ||
} | ||
if (needsAriaValuemax) { | ||
attr.$observe('max', function ngAriaValueMinReaction(newVal) { | ||
elem.attr('aria-valuemax', newVal); | ||
}); | ||
} | ||
if (needsAriaValuenow) { | ||
scope.$watch(ngAriaWatchModelValue, function ngAriaValueNowReaction(newVal) { | ||
elem.attr('aria-valuenow', newVal); | ||
}); | ||
} | ||
} | ||
if (needsTabIndex) { | ||
elem.attr('tabindex', 0); | ||
} | ||
break; | ||
} | ||
|
||
if (!attr.hasOwnProperty('ngRequired') && ngModel.$validators.required | ||
&& shouldAttachAttr('aria-required', 'ariaRequired', elem, false)) { | ||
// ngModel.$error.required is undefined on custom controls | ||
attr.$observe('required', function() { | ||
elem.attr('aria-required', !!attr['required']); | ||
}); | ||
} | ||
|
||
if (shouldAttachAttr('aria-invalid', 'ariaInvalid', elem, true)) { | ||
scope.$watch(function ngAriaInvalidWatch() { | ||
return ngModel.$invalid; | ||
}, function ngAriaInvalidReaction(newVal) { | ||
elem.attr('aria-invalid', !!newVal); | ||
}); | ||
} | ||
} | ||
}; | ||
} | ||
}; | ||
}]) | ||
.directive('ngDisabled', ['$aria', function($aria) { | ||
return $aria.$$watchExpr('ngDisabled', 'aria-disabled', nodeBlackList, false); | ||
}]) | ||
.directive('ngMessages', function() { | ||
return { | ||
restrict: 'A', | ||
require: '?ngMessages', | ||
link: function(scope, elem, attr, ngMessages) { | ||
if (!elem.attr('aria-live')) { | ||
elem.attr('aria-live', 'assertive'); | ||
} | ||
} | ||
}; | ||
}) | ||
.directive('ngClick',['$aria', '$parse', function($aria, $parse) { | ||
return { | ||
restrict: 'A', | ||
compile: function(elem, attr) { | ||
var fn = $parse(attr.ngClick, /* interceptorFn */ null, /* expensiveChecks */ true); | ||
return function(scope, elem, attr) { | ||
|
||
if (!isNodeOneOf(elem, nodeBlackList)) { | ||
|
||
if ($aria.config('bindRoleForClick') && !elem.attr('role')) { | ||
elem.attr('role', 'button'); | ||
} | ||
|
||
if ($aria.config('tabindex') && !elem.attr('tabindex')) { | ||
elem.attr('tabindex', 0); | ||
} | ||
|
||
if ($aria.config('bindKeypress') && !attr.ngKeypress) { | ||
elem.on('keypress', function(event) { | ||
var keyCode = event.which || event.keyCode; | ||
if (keyCode === 32 || keyCode === 13) { | ||
scope.$apply(callback); | ||
} | ||
|
||
function callback() { | ||
fn(scope, { $event: event }); | ||
} | ||
}); | ||
} | ||
} | ||
}; | ||
} | ||
}; | ||
}]) | ||
.directive('ngDblclick', ['$aria', function($aria) { | ||
return function(scope, elem, attr) { | ||
if ($aria.config('tabindex') && !elem.attr('tabindex') && !isNodeOneOf(elem, nodeBlackList)) { | ||
elem.attr('tabindex', 0); | ||
} | ||
}; | ||
}]); | ||
|
||
|
||
})(window, window.angular); |
Oops, something went wrong.