1/** 2 * :focus-within Polyfill 3 * @return {boolean} 4 */ 5function polyfill() { 6 var CLASS_NAME = 'focus-within'; 7 var WHITE_SPACE = ['\n', '\t', ' ', '\r']; 8 9 /** 10 * Calculate the entire event path. 11 * 12 * @param {Element} node 13 * @return {Array} computedPath 14 */ 15 function computeEventPath(node) { 16 var path = []; 17 var parent = null; 18 19 while ((parent = node.parentNode || node.host || node.defaultView)) { 20 path.push(parent); 21 node = parent; 22 } 23 24 return path; 25 } 26 27 /** 28 * Add user defined attribute to element retaining any previously 29 * applied attributes. Attribute can be the 'class' attribute for 30 * compatibility reasons. 31 * 32 * @param {string} value 33 * @return {function(Element)} callback 34 */ 35 function addClass(value) { 36 return function(el) { 37 var attributes = (el && typeof el.getAttribute !== 'undefined') ? 38 el.getAttribute('class') || '' : 39 undefined; 40 41 if (typeof attributes !== 'undefined' && 42 attributes.indexOf(value) === -1) { 43 el.setAttribute('class', attributes.concat(' ', value).trim()); 44 } 45 }; 46 } 47 48 /** 49 * Remove user defined attribute value or entire attribute if last one. 50 * Attribute can be the 'class' attribute for compatibility reasons. 51 * 52 * @param {string} value 53 * @return {function(Element)} callback 54 */ 55 function removeClass(value) { 56 return function(el) { 57 var attributes = (el && typeof el.getAttribute !== 'undefined') ? 58 el.getAttribute('class') || '' : 59 undefined; 60 61 if (attributes) { 62 var index = attributes.indexOf(value); 63 // Check if `value` exists in `attributes` and it is either 64 // at the start or after a whitespace character. This stops 65 // "focus-within" being remove from "js-focus-within". 66 if (index >= 0 && 67 (index === 0 || 68 WHITE_SPACE.indexOf(attributes.charAt(index - 1)) >= 0)) { 69 var newAttributes = attributes.replace(value, '').trim(); 70 (newAttributes === '') ? 71 el.removeAttribute('class') : 72 el.setAttribute('class', newAttributes); 73 } 74 } 75 }; 76 } 77 78 /** 79 * Attach event listerns to initiate polyfill 80 * @return {boolean} 81 */ 82 function load() { 83 var handler = function(e) { 84 var running; 85 86 /** 87 * Request animation frame callback. 88 * Remove previously applied attributes. 89 * Add new attributes. 90 */ 91 function action() { 92 running = false; 93 94 if ('blur' === e.type) { 95 Array.prototype.slice 96 .call(computeEventPath(e.target)) 97 .forEach(removeClass(CLASS_NAME)); 98 } 99 100 if ('focus' === e.type) { 101 Array.prototype.slice 102 .call(computeEventPath(e.target)) 103 .forEach(addClass(CLASS_NAME)); 104 } 105 } 106 107 if (!running) { 108 window.requestAnimationFrame(action); 109 running = true; 110 } 111 }; 112 113 document.addEventListener('focus', handler, true); 114 document.addEventListener('blur', handler, true); 115 addClass('js-focus-within')(document.body); 116 return true; 117 } 118 119 try { 120 return !document.querySelector(':' + CLASS_NAME); 121 } catch (error) { 122 return load(); 123 } 124} 125 126polyfill();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.