UNPKG

md2

Version:

Angular2 based Material Design components, directives and services are Accordion, Autocomplete, Chips(Tags), Collapse, Colorpicker, Data Table, Datepicker, Dialog(Modal), Menu, Multiselect, Select, Tabs, Tags(Chips), Toast and Tooltip.

214 lines 9.21 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import { Injectable } from '@angular/core'; import { Platform } from '../platform/platform'; /** * The InteractivityChecker leans heavily on the ally.js accessibility utilities. * Methods like `isTabbable` are only covering specific edge-cases for the browsers which are * supported. */ /** * Utility for checking the interactivity of an element, such as whether is is focusable or * tabbable. */ var InteractivityChecker = (function () { function InteractivityChecker(_platform) { this._platform = _platform; } /** * Gets whether an element is disabled. * * @param element Element to be checked. * @returns Whether the element is disabled. */ InteractivityChecker.prototype.isDisabled = function (element) { // This does not capture some cases, such as a non-form control with a disabled attribute or // a form control inside of a disabled form, but should capture the most common cases. return element.hasAttribute('disabled'); }; /** * Gets whether an element is visible for the purposes of interactivity. * * This will capture states like `display: none` and `visibility: hidden`, but not things like * being clipped by an `overflow: hidden` parent or being outside the viewport. * * @returns Whether the element is visible. */ InteractivityChecker.prototype.isVisible = function (element) { return hasGeometry(element) && getComputedStyle(element).visibility === 'visible'; }; /** * Gets whether an element can be reached via Tab key. * Assumes that the element has already been checked with isFocusable. * * @param element Element to be checked. * @returns Whether the element is tabbable. */ InteractivityChecker.prototype.isTabbable = function (element) { // Nothing is tabbable on the the server 😎 if (!this._platform.isBrowser) { return false; } var frameElement = getWindow(element).frameElement; if (frameElement) { var frameType = frameElement && frameElement.nodeName.toLowerCase(); // Frame elements inherit their tabindex onto all child elements. if (getTabIndexValue(frameElement) === -1) { return false; } // Webkit and Blink consider anything inside of an <object> element as non-tabbable. if ((this._platform.BLINK || this._platform.WEBKIT) && frameType === 'object') { return false; } // Webkit and Blink disable tabbing to an element inside of an invisible frame. if ((this._platform.BLINK || this._platform.WEBKIT) && !this.isVisible(frameElement)) { return false; } } var nodeName = element.nodeName.toLowerCase(); var tabIndexValue = getTabIndexValue(element); if (element.hasAttribute('contenteditable')) { return tabIndexValue !== -1; } if (nodeName === 'iframe') { // The frames may be tabbable depending on content, but it's not possibly to reliably // investigate the content of the frames. return false; } if (nodeName === 'audio') { if (!element.hasAttribute('controls')) { // By default an <audio> element without the controls enabled is not tabbable. return false; } else if (this._platform.BLINK) { // In Blink <audio controls> elements are always tabbable. return true; } } if (nodeName === 'video') { if (!element.hasAttribute('controls') && this._platform.TRIDENT) { // In Trident a <video> element without the controls enabled is not tabbable. return false; } else if (this._platform.BLINK || this._platform.FIREFOX) { // In Chrome and Firefox <video controls> elements are always tabbable. return true; } } if (nodeName === 'object' && (this._platform.BLINK || this._platform.WEBKIT)) { // In all Blink and WebKit based browsers <object> elements are never tabbable. return false; } // In iOS the browser only considers some specific elements as tabbable. if (this._platform.WEBKIT && this._platform.IOS && !isPotentiallyTabbableIOS(element)) { return false; } return element.tabIndex >= 0; }; /** * Gets whether an element can be focused by the user. * * @param element Element to be checked. * @returns Whether the element is focusable. */ InteractivityChecker.prototype.isFocusable = function (element) { // Perform checks in order of left to most expensive. // Again, naive approach that does not capture many edge cases and browser quirks. return isPotentiallyFocusable(element) && !this.isDisabled(element) && this.isVisible(element); }; return InteractivityChecker; }()); InteractivityChecker = __decorate([ Injectable(), __metadata("design:paramtypes", [Platform]) ], InteractivityChecker); export { InteractivityChecker }; /** Checks whether the specified element has any geometry / rectangles. */ function hasGeometry(element) { // Use logic from jQuery to check for an invisible element. // See https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js#L12 return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length); } /** Gets whether an element's */ function isNativeFormElement(element) { var nodeName = element.nodeName.toLowerCase(); return nodeName === 'input' || nodeName === 'select' || nodeName === 'button' || nodeName === 'textarea'; } /** Gets whether an element is an <input type="hidden">. */ function isHiddenInput(element) { return isInputElement(element) && element.type == 'hidden'; } /** Gets whether an element is an anchor that has an href attribute. */ function isAnchorWithHref(element) { return isAnchorElement(element) && element.hasAttribute('href'); } /** Gets whether an element is an input element. */ function isInputElement(element) { return element.nodeName.toLowerCase() == 'input'; } /** Gets whether an element is an anchor element. */ function isAnchorElement(element) { return element.nodeName.toLowerCase() == 'a'; } /** Gets whether an element has a valid tabindex. */ function hasValidTabIndex(element) { if (!element.hasAttribute('tabindex') || element.tabIndex === undefined) { return false; } var tabIndex = element.getAttribute('tabindex'); // IE11 parses tabindex="" as the value "-32768" if (tabIndex == '-32768') { return false; } return !!(tabIndex && !isNaN(parseInt(tabIndex, 10))); } /** * Returns the parsed tabindex from the element attributes instead of returning the * evaluated tabindex from the browsers defaults. */ function getTabIndexValue(element) { if (!hasValidTabIndex(element)) { return null; } // See browser issue in Gecko https://bugzilla.mozilla.org/show_bug.cgi?id=1128054 var tabIndex = parseInt(element.getAttribute('tabindex'), 10); return isNaN(tabIndex) ? -1 : tabIndex; } /** Checks whether the specified element is potentially tabbable on iOS */ function isPotentiallyTabbableIOS(element) { var nodeName = element.nodeName.toLowerCase(); var inputType = nodeName === 'input' && element.type; return inputType === 'text' || inputType === 'password' || nodeName === 'select' || nodeName === 'textarea'; } /** * Gets whether an element is potentially focusable without taking current visible/disabled state * into account. */ function isPotentiallyFocusable(element) { // Inputs are potentially focusable *unless* they're type="hidden". if (isHiddenInput(element)) { return false; } return isNativeFormElement(element) || isAnchorWithHref(element) || element.hasAttribute('contenteditable') || hasValidTabIndex(element); } /** Gets the parent window of a DOM node with regards of being inside of an iframe. */ function getWindow(node) { return node.ownerDocument.defaultView || window; } //# sourceMappingURL=interactivity-checker.js.map