diff --git a/lib/accessibility-element-view.js b/lib/accessibility-element-view.js index b8af706..f4b5581 100644 --- a/lib/accessibility-element-view.js +++ b/lib/accessibility-element-view.js @@ -1,12 +1,14 @@ 'use strict' const SelectableView = require('./selectable-view') +const Eval = require('./eval') class AccessibilityElementView extends SelectableView { constructor (element, parent) { super('element-row') - this.path = element + this.path = element.selector + this.pathId = element.id parent.appendChild(this.element) this.render() this.handleEvents(parent) @@ -19,6 +21,13 @@ class AccessibilityElementView extends SelectableView { render () { this.selectorPath.textContent = this.path + + // Add a click-handler that will select the element. + // Uses the `accessibilityAuditMap` defined in accessibility.js + this.selectorPath.onclick = (evt) => { + evt.stopPropagation() + Eval.execute(`inspect(window.__devtron.accessibilityAuditMap.get(${this.pathId}))`) + } } filter (searchText) { diff --git a/lib/accessibility.js b/lib/accessibility.js index 80b6f0f..fe78e6b 100644 --- a/lib/accessibility.js +++ b/lib/accessibility.js @@ -2,9 +2,23 @@ const Eval = require('./eval') exports.audit = () => { return Eval.execute(function () { - const {axs} = window.__devtron + const {axs} = window.__devtron // defined in browser-globals.js const config = new axs.AuditConfiguration({showUnsupportedRulesWarning: false}) - return axs.Audit.run(config).map(function (result) { + const results = axs.Audit.run(config) + + // Create a lookup map so users can click on an element to inspect it + let idCounter = 0 + window.__devtron.accessibilityAuditMap = new Map() + results.forEach(function (result) { + const elements = result.elements || [] + elements.forEach(function (element) { + const id = idCounter++ + element.__accessibilityAuditId = id + window.__devtron.accessibilityAuditMap.set(id, element) + }) + }) + + return results.map(function (result) { const elements = result.elements || [] let status = 'N/A' if (result.result === 'PASS') { @@ -19,7 +33,14 @@ exports.audit = () => { title: result.rule.heading, url: result.rule.url, elements: elements.map(function (element) { - return window.__devtron.axs.utils.getQuerySelectorText(element) + let selector = element.tagName.toLowerCase() + if (element.className) { + selector += '.' + element.className.split(' ').join('.') + } + return { + selector: selector, + id: element.__accessibilityAuditId + } }) } }).sort(function (resultA, resultB) { diff --git a/lib/browser-globals.js b/lib/browser-globals.js new file mode 100644 index 0000000..b56f52e --- /dev/null +++ b/lib/browser-globals.js @@ -0,0 +1,8 @@ +// This defines globals that will be used in the browser context +// (via the content_scripts definition in manifest.json) +// +// It is generated via `npm run-script prepublish` +const axs = require('accessibility-developer-tools') + +window.__devtron = window.__devtron || {} +window.__devtron.axs = axs diff --git a/manifest.json b/manifest.json index ef4c62a..24613a4 100644 --- a/manifest.json +++ b/manifest.json @@ -5,7 +5,7 @@ "content_scripts": [ { "matches": ["*"], - "js": ["vendor/axs.js"] + "js": ["out/browser-globals.js"] } ] } diff --git a/package.json b/package.json index 63c7dd0..cdee958 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,8 @@ "description": "Electron DevTools Extension", "main": "./api.js", "scripts": { - "prepublish": "browserify lib/*.js -o out/index.js --ignore-missing --entry lib/index.js", - "start": "watchify lib/*.js -o out/index.js --ignore-missing --entry lib/index.js --verbose", + "prepublish": "browserify lib/*.js -o out/index.js --ignore-missing --entry lib/index.js && browserify lib/browser-globals.js -o out/browser-globals.js", + "start": "browserify lib/browser-globals.js -o out/browser-globals.js && watchify lib/*.js -o out/index.js --ignore-missing --entry lib/index.js --verbose", "test": "mocha test/unit/*-test.js test/integration/*-test.js && standard" }, "repository": { @@ -39,6 +39,7 @@ "watchify": "^3.7.0" }, "dependencies": { + "accessibility-developer-tools": "^2.11.0", "highlight.js": "^9.3.0", "humanize-plus": "^1.8.1" }, diff --git a/static/devtron.css b/static/devtron.css index 5c9d1de..8284887 100644 --- a/static/devtron.css +++ b/static/devtron.css @@ -130,6 +130,13 @@ tbody:focus, padding-left: 45px; } +.row-element-path a { + cursor: pointer; +} +tr.active .row-element-path a { + color: white; +} + .active .sidebar-icon { background-color: white; } diff --git a/static/index.html b/static/index.html index 8e12752..d4fd3ee 100644 --- a/static/index.html +++ b/static/index.html @@ -90,7 +90,7 @@ - + diff --git a/vendor/axs.js b/vendor/axs.js deleted file mode 100644 index 2df078e..0000000 --- a/vendor/axs.js +++ /dev/null @@ -1,2284 +0,0 @@ -/* - * Copyright 2016 Google Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Generated from http://github.com/GoogleChrome/accessibility-developer-tools/tree/582ba71c124ed834d640ce2109dcd91b7864922f - * - * See project README for build steps. - */ - -// AUTO-GENERATED CONTENT BELOW: DO NOT EDIT! See above for details. - -window.__devtron = window.__devtron || {} - -window.__devtron.axs = (function() { - var COMPILED = !0, goog = goog || {}; -goog.global = this; -goog.isDef = function(a) { - return void 0 !== a; -}; -goog.exportPath_ = function(a, b, c) { - a = a.split("."); - c = c || goog.global; - a[0] in c || !c.execScript || c.execScript("var " + a[0]); - for (var d;a.length && (d = a.shift());) { - !a.length && goog.isDef(b) ? c[d] = b : c = c[d] ? c[d] : c[d] = {}; - } -}; -goog.define = function(a, b) { - var c = b; - COMPILED || (goog.global.CLOSURE_UNCOMPILED_DEFINES && Object.prototype.hasOwnProperty.call(goog.global.CLOSURE_UNCOMPILED_DEFINES, a) ? c = goog.global.CLOSURE_UNCOMPILED_DEFINES[a] : goog.global.CLOSURE_DEFINES && Object.prototype.hasOwnProperty.call(goog.global.CLOSURE_DEFINES, a) && (c = goog.global.CLOSURE_DEFINES[a])); - goog.exportPath_(a, c); -}; -goog.DEBUG = !0; -goog.LOCALE = "en"; -goog.TRUSTED_SITE = !0; -goog.STRICT_MODE_COMPATIBLE = !1; -goog.provide = function(a) { - if (!COMPILED) { - if (goog.isProvided_(a)) { - throw Error('Namespace "' + a + '" already declared.'); - } - delete goog.implicitNamespaces_[a]; - for (var b = a;(b = b.substring(0, b.lastIndexOf("."))) && !goog.getObjectByName(b);) { - goog.implicitNamespaces_[b] = !0; - } - } - goog.exportPath_(a); -}; -goog.setTestOnly = function(a) { - if (COMPILED && !goog.DEBUG) { - throw a = a || "", Error("Importing test-only code into non-debug environment" + a ? ": " + a : "."); - } -}; -goog.forwardDeclare = function(a) { -}; -COMPILED || (goog.isProvided_ = function(a) { - return !goog.implicitNamespaces_[a] && goog.isDefAndNotNull(goog.getObjectByName(a)); -}, goog.implicitNamespaces_ = {}); -goog.getObjectByName = function(a, b) { - for (var c = a.split("."), d = b || goog.global, e;e = c.shift();) { - if (goog.isDefAndNotNull(d[e])) { - d = d[e]; - } else { - return null; - } - } - return d; -}; -goog.globalize = function(a, b) { - var c = b || goog.global, d; - for (d in a) { - c[d] = a[d]; - } -}; -goog.addDependency = function(a, b, c) { - if (goog.DEPENDENCIES_ENABLED) { - var d; - a = a.replace(/\\/g, "/"); - for (var e = goog.dependencies_, f = 0;d = b[f];f++) { - e.nameToPath[d] = a, a in e.pathToNames || (e.pathToNames[a] = {}), e.pathToNames[a][d] = !0; - } - for (d = 0;b = c[d];d++) { - a in e.requires || (e.requires[a] = {}), e.requires[a][b] = !0; - } - } -}; -goog.ENABLE_DEBUG_LOADER = !0; -goog.require = function(a) { - if (!COMPILED && !goog.isProvided_(a)) { - if (goog.ENABLE_DEBUG_LOADER) { - var b = goog.getPathFromDeps_(a); - if (b) { - goog.included_[b] = !0; - goog.writeScripts_(); - return; - } - } - a = "goog.require could not find: " + a; - goog.global.console && goog.global.console.error(a); - throw Error(a); - } -}; -goog.basePath = ""; -goog.nullFunction = function() { -}; -goog.identityFunction = function(a, b) { - return a; -}; -goog.abstractMethod = function() { - throw Error("unimplemented abstract method"); -}; -goog.addSingletonGetter = function(a) { - a.getInstance = function() { - if (a.instance_) { - return a.instance_; - } - goog.DEBUG && (goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = a); - return a.instance_ = new a; - }; -}; -goog.instantiatedSingletons_ = []; -goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; -goog.DEPENDENCIES_ENABLED && (goog.included_ = {}, goog.dependencies_ = {pathToNames:{}, nameToPath:{}, requires:{}, visited:{}, written:{}}, goog.inHtmlDocument_ = function() { - var a = goog.global.document; - return "undefined" != typeof a && "write" in a; -}, goog.findBasePath_ = function() { - if (goog.global.CLOSURE_BASE_PATH) { - goog.basePath = goog.global.CLOSURE_BASE_PATH; - } else { - if (goog.inHtmlDocument_()) { - for (var a = goog.global.document.getElementsByTagName("script"), b = a.length - 1;0 <= b;--b) { - var c = a[b].src, d = c.lastIndexOf("?"), d = -1 == d ? c.length : d; - if ("base.js" == c.substr(d - 7, 7)) { - goog.basePath = c.substr(0, d - 7); - break; - } - } - } - } -}, goog.importScript_ = function(a) { - var b = goog.global.CLOSURE_IMPORT_SCRIPT || goog.writeScriptTag_; - !goog.dependencies_.written[a] && b(a) && (goog.dependencies_.written[a] = !0); -}, goog.writeScriptTag_ = function(a) { - if (goog.inHtmlDocument_()) { - var b = goog.global.document; - if ("complete" == b.readyState) { - if (/\bdeps.js$/.test(a)) { - return !1; - } - throw Error('Cannot write "' + a + '" after document load'); - } - b.write('