UNPKG

jobiqo-cl

Version:

[![CircleCI](https://circleci.com/gh/jobiqo/jobiqo-cl.svg?style=svg&circle-token=5a24efa5b8bbc4879276123e77d0d3f35ca7144c)](https://circleci.com/gh/jobiqo/jobiqo-cl)

207 lines (168 loc) 6.04 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var candidateSelectors = [ 'input', 'select', 'textarea', 'a[href]', 'button', '[tabindex]', 'audio[controls]', 'video[controls]', '[contenteditable]:not([contenteditable="false"])', ]; var candidateSelector = candidateSelectors.join(','); var matches = typeof Element === 'undefined' ? function () {} : Element.prototype.matches || Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; function tabbable(el, options) { options = options || {}; var elementDocument = el.ownerDocument || el; var regularTabbables = []; var orderedTabbables = []; var untouchabilityChecker = new UntouchabilityChecker(elementDocument); var candidates = el.querySelectorAll(candidateSelector); if (options.includeContainer) { if (matches.call(el, candidateSelector)) { candidates = Array.prototype.slice.apply(candidates); candidates.unshift(el); } } var i, candidate, candidateTabindex; for (i = 0; i < candidates.length; i++) { candidate = candidates[i]; if (!isNodeMatchingSelectorTabbable(candidate, untouchabilityChecker)) continue; candidateTabindex = getTabindex(candidate); if (candidateTabindex === 0) { regularTabbables.push(candidate); } else { orderedTabbables.push({ documentOrder: i, tabIndex: candidateTabindex, node: candidate, }); } } var tabbableNodes = orderedTabbables .sort(sortOrderedTabbables) .map(function(a) { return a.node }) .concat(regularTabbables); return tabbableNodes; } tabbable.isTabbable = isTabbable; tabbable.isFocusable = isFocusable; function isNodeMatchingSelectorTabbable(node, untouchabilityChecker) { if ( !isNodeMatchingSelectorFocusable(node, untouchabilityChecker) || isNonTabbableRadio(node) || getTabindex(node) < 0 ) { return false; } return true; } function isTabbable(node, untouchabilityChecker) { if (!node) throw new Error('No node provided'); if (matches.call(node, candidateSelector) === false) return false; return isNodeMatchingSelectorTabbable(node, untouchabilityChecker); } function isNodeMatchingSelectorFocusable(node, untouchabilityChecker) { untouchabilityChecker = untouchabilityChecker || new UntouchabilityChecker(node.ownerDocument || node); if ( node.disabled || isHiddenInput(node) || untouchabilityChecker.isUntouchable(node) ) { return false; } return true; } var focusableCandidateSelector = candidateSelectors.concat('iframe').join(','); function isFocusable(node, untouchabilityChecker) { if (!node) throw new Error('No node provided'); if (matches.call(node, focusableCandidateSelector) === false) return false; return isNodeMatchingSelectorFocusable(node, untouchabilityChecker); } function getTabindex(node) { var tabindexAttr = parseInt(node.getAttribute('tabindex'), 10); if (!isNaN(tabindexAttr)) return tabindexAttr; // Browsers do not return `tabIndex` correctly for contentEditable nodes; // so if they don't have a tabindex attribute specifically set, assume it's 0. if (isContentEditable(node)) return 0; return node.tabIndex; } function sortOrderedTabbables(a, b) { return a.tabIndex === b.tabIndex ? a.documentOrder - b.documentOrder : a.tabIndex - b.tabIndex; } // Array.prototype.find not available in IE. function find(list, predicate) { for (var i = 0, length = list.length; i < length; i++) { if (predicate(list[i])) return list[i]; } } function isContentEditable(node) { return node.contentEditable === 'true'; } function isInput(node) { return node.tagName === 'INPUT'; } function isHiddenInput(node) { return isInput(node) && node.type === 'hidden'; } function isRadio(node) { return isInput(node) && node.type === 'radio'; } function isNonTabbableRadio(node) { return isRadio(node) && !isTabbableRadio(node); } function getCheckedRadio(nodes) { for (var i = 0; i < nodes.length; i++) { if (nodes[i].checked) { return nodes[i]; } } } function isTabbableRadio(node) { if (!node.name) return true; // This won't account for the edge case where you have radio groups with the same // in separate forms on the same page. var radioSet = node.ownerDocument.querySelectorAll('input[type="radio"][name="' + node.name + '"]'); var checked = getCheckedRadio(radioSet); return !checked || checked === node; } // An element is "untouchable" if *it or one of its ancestors* has // `visibility: hidden` or `display: none`. function UntouchabilityChecker(elementDocument) { this.doc = elementDocument; // Node cache must be refreshed on every check, in case // the content of the element has changed. The cache contains tuples // mapping nodes to their boolean result. this.cache = []; } // getComputedStyle accurately reflects `visibility: hidden` of ancestors // but not `display: none`, so we need to recursively check parents. UntouchabilityChecker.prototype.hasDisplayNone = function hasDisplayNone(node, nodeComputedStyle) { if (node.nodeType !== Node.ELEMENT_NODE) return false; // Search for a cached result. var cached = find(this.cache, function(item) { return item === node; }); if (cached) return cached[1]; nodeComputedStyle = nodeComputedStyle || this.doc.defaultView.getComputedStyle(node); var result = false; if (nodeComputedStyle.display === 'none') { result = true; } else if (node.parentNode) { result = this.hasDisplayNone(node.parentNode); } this.cache.push([node, result]); return result; }; UntouchabilityChecker.prototype.isUntouchable = function isUntouchable(node) { if (node === this.doc.documentElement) return false; var computedStyle = this.doc.defaultView.getComputedStyle(node); if (this.hasDisplayNone(node, computedStyle)) return true; return computedStyle.visibility === 'hidden'; }; var tabbable_1 = tabbable; exports.__moduleExports = tabbable_1;