var BASE_Z = 999999; var WARNING_COUNT = 0; var idToWarningsMap = {}; var STYLE = ``; var CHROME_LENS_BASE_ID = 'chrome-lens-base' var CHROME_LENS_WARNING_CLASS = 'chrome-lens-warning' var WARNING_ATTR_NAME = 'chromelens-warning-id'; function initDom() { if (!document.getElementById(CHROME_LENS_BASE_ID)) { const div = document.createElement('div'); div.id = CHROME_LENS_BASE_ID; var root = div.attachShadow({mode: 'open'}); root.innerHTML = STYLE; document.body.appendChild(div); } } function tooltipHeader(severity, code, url) { const el = document.createElement('p'); const severityEl = document.createElement('span'); if (severity === 'Severe') { severityEl.className = 'severe' } else if (severity === 'Warning') { severityEl.className = 'warning' } severityEl.innerText = severity; const codeLink = document.createElement('a'); codeLink.href = url; codeLink.innerText = code; codeLink.target = '_blank'; el.appendChild(severityEl); el.appendChild(document.createTextNode(' ')); el.appendChild(codeLink); return el; } function positionTooltip(tooltipText, el_top, el_left, el_height) { const {width, height} = tooltipText.getBoundingClientRect(); if (height < el_top) { // tooltip cannot fit above, make it below tooltipText.style.bottom = el_height + 'px'; } else { // tooltip goes above tooltipText.style.top = el_height + 'px'; } if (el_left + width > document.documentElement.clientWidth) { // tooltip is wider than the el, right align el and tooltip tooltipText.style.right = 0 + 'px'; } } function tooltipTextNode(rule_violated) { const {code, heading, name, severity, url} = rule_violated; const tooltipText = document.createElement('div'); tooltipText.className = 'tooltip-text'; tooltipText.appendChild(tooltipHeader(severity, code, url)); tooltipText.appendChild(document.createTextNode(heading)); suggestFix(rule_violated); return tooltipText; } function getCoordinates(elem) { var box = elem.getBoundingClientRect(); var body = document.body; var docEl = document.documentElement; var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop; var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft; var clientTop = docEl.clientTop || body.clientTop || 0; var clientLeft = docEl.clientLeft || body.clientLeft || 0; var top = box.top + scrollTop - clientTop; var left = box.left + scrollLeft - clientLeft; return { top, left, height: box.height, width: box.width } } function tooltipNode(offendingEl) { const { top, left, height, width} = getCoordinates(offendingEl); const div = document.createElement('div'); div.className = CHROME_LENS_WARNING_CLASS; div.classList.add('tooltip'); div.style.left = left + 'px'; div.style.top = top + 'px'; div.style.width = width + 'px'; div.style.height = height + 'px'; // this id will be useful if we want to reference specific warnings emitted div.id = 'chrome-lens-warning-' + WARNING_COUNT; div.onmouseover = function() { chrome.runtime.sendMessage({ type: 'HIGHLIGHT_REPORT', data: { warningId: div.id } }) } div.onmouseout = function() { chrome.runtime.sendMessage({ type: 'UNHIGHLIGHT_REPORT', data: { warningId: div.id } }) } return div; } function suggestFix(ruleViolated) { const {code, heading, name, severity, url} = ruleViolated; if (code === 'AX_ARIA_10') { } } function highlightElementForRuleViolation(frag, el, rule_violated) { const warningId = CHROME_LENS_WARNING_CLASS + '-' + (WARNING_COUNT++); idToWarningsMap[warningId] = {el: el, rule: rule_violated}; el.setAttribute(WARNING_ATTR_NAME, warningId); const { top, left, height, width } = getCoordinates(el); // if this isn't visible let's not warn about if (top < 0 || left < 0) return; const tooltipText = tooltipTextNode(rule_violated); const toolTip = tooltipNode(el); toolTip.appendChild(tooltipText); frag.appendChild(toolTip); // we can only position after we append, because tooltipText has no // bounding client rect before it gets added to the DOM positionTooltip(tooltipText, top, left, height); } function run() { const base = document.getElementById(CHROME_LENS_BASE_ID); if (base) { base.remove() } var run_result = axs.Audit.run(); initDom() var frag = document.createDocumentFragment(); // we only want to highlight failures const failures = run_result.filter(v => v.result === 'FAIL'); failures.forEach(function(v) { v.elements.forEach(function(el) { highlightElementForRuleViolation(frag, el, v.rule); }) }) var root = document.getElementById(CHROME_LENS_BASE_ID).shadowRoot; root.appendChild(frag); chrome.runtime.sendMessage({ type: 'AXS_COMPLETE', data: { result: failures, idToWarningsMap, } }) } run(); chrome.runtime.onMessage.addListener(function(message) { switch (message.type) { case 'HIGHLIGHT_WARNING': { const { warningId } = message.data; const warningTooltip = document.querySelector('#' + warningId + ' .tooltip-text'); if (!warningTooltip) { return; } warningTooltip.style.visibility = 'visible'; break; } case 'UNHIGHLIGHT_WARNING': { const { warningId } = message.data; const warningTooltip = document.querySelector('#' + warningId + ' .tooltip-text'); if (!warningTooltip) { return; } // we must set to null so that CSS can take over warningTooltip.style.visibility = null; break; } default:{ break; } } })