├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE.md ├── README.md ├── app.js ├── axe-phantom.js ├── config ├── config.json └── package.json ├── cookies └── .keep ├── loadtest.js ├── module.js ├── package-lock.json ├── package.json ├── ptest1.js ├── ptest2.js ├── public ├── css │ ├── HTMLCS.css │ ├── home-print.css │ ├── home.css │ └── lib │ │ ├── bootstrap-accessibility.css │ │ ├── bootstrap-responsive.css │ │ └── bootstrap.min.css ├── img │ ├── AATT_a11y-edge_sticker.png │ ├── HTMLCS-tools.png │ ├── PayPal_logo.png │ ├── asc.gif │ ├── bg.gif │ ├── bgTexture1.gif │ ├── desc.gif │ ├── external-link-hover.png │ ├── external-link-table.png │ ├── glyphicons-halflings-white.png │ ├── glyphicons-halflings.png │ ├── icon_load_roundcorner_lock1_186x42_withlock.gif │ ├── logo_347x50_PPa11y.png │ ├── summaryLoader-error.gif │ ├── summaryLoader-notice.gif │ ├── summaryLoader-warning.gif │ └── test-area-bg.png └── js │ ├── lib │ ├── __jquery.tablesorter.js │ ├── bootstrap-accessibility.min.js │ ├── bootstrap.min.js │ ├── calltable2CSV.js │ ├── jquery-1.7.1.min.js │ ├── jquery.js │ └── table2CSV.js │ └── validation.js ├── screenshots └── .keep ├── src ├── HTMLCS_Run.js ├── PAET.js ├── axe.js ├── axe │ ├── axe.js │ └── axe.min.js ├── axe_url.js ├── chrome.js ├── chrome │ └── axs_testing.js ├── chrome_url.js ├── home.js ├── htmlcs │ ├── .jshintrc │ ├── HTMLCS.js │ └── HTMLCS.min.js ├── login.js ├── runner_html.js ├── runner_json.js └── screenshot.js ├── test ├── AATT_API.html ├── Accessibility Fails_files │ ├── animated-tree.gif │ ├── bbc-blocks-dark.png │ ├── decoration.png │ ├── demo.html │ ├── demo_002.html │ ├── foo.html │ ├── jquery-1.js │ ├── main.css │ ├── main.js │ ├── normalize.css │ ├── red_panda.jpg │ ├── spacer.gif │ └── submit.png ├── Fails.html ├── bs_modal_dynamic │ ├── bootstrap.min.css │ ├── bootstrap.min.js │ ├── donate.html │ └── jquery.js ├── css │ └── home.css ├── imgs │ ├── asc.gif │ ├── bg.gif │ ├── desc.gif │ └── screenshots │ │ └── Home_ScreenShot.png ├── index.html ├── js │ ├── main.js │ ├── sorttable.js │ └── util.js └── tooltest │ ├── allTests.html │ ├── lowcontrast.gif │ └── paypal-logo1x.svg ├── tmp ├── .keep ├── bootstrap.min.css ├── bootstrap.min.js └── jquery.js └── views ├── bookmark.html ├── help.html ├── home.html └── index.html /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | ./node_modules -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | 16 | node_modules 17 | 18 | .DS_Store 19 | .idea 20 | screenshots/* 21 | 22 | tmp/*.html 23 | cookies/*.txt 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.9.4 2 | 3 | RUN apt-get update && apt-get install -y openssh-client git && rm -rf /var/lib/apt/lists/* 4 | COPY . /usr/src/app 5 | WORKDIR /usr/src/app 6 | 7 | RUN npm install 8 | RUN git submodule init 9 | RUN git submodule update 10 | 11 | EXPOSE 3000 12 | ENTRYPOINT DEBUG=AATT* http_port=3000 node app.js -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, PayPal 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, this 11 | list of conditions and the following disclaimer in the documentation and/or 12 | other materials provided with the distribution. 13 | 14 | * Neither the name of the PayPal nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 25 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automated Accessibility Testing Tool (AATT) 2 | 3 | Browser-based accessibility testing tools and plugins require manually testing each page, one at a time. Tools that can crawl a website can only scan pages that do not require login credentials, and that are not behind a firewall. Instead of developing, testing, and using a separate accessibility test suite, you can now integrate accessibility testing into your existing automation test suite using AATT. 4 | 5 | AATT tests web applications regarding conformance to the Web Content Accessibility Guidelines (WCAG) 2.1 (for axe engine). 6 | 7 | AATT provides an accessibility API and custom web application for [HTML CodeSniffer](http://squizlabs.github.io/HTML_CodeSniffer/), [Axe](https://github.com/dequelabs/axe-core) and [Chrome developer tool](https://github.com/GoogleChrome/accessibility-developer-tools). Using the AATT web application, you can configure test server configurations inside the firewall, and test individual pages. 8 | 9 | AATT includes [HTML CodeSniffer](http://squizlabs.github.io/HTML_CodeSniffer/), [Axe](https://github.com/dequelabs/axe-core) and [Chrome developer tool](https://github.com/GoogleChrome/accessibility-developer-tools) with Express and PhantomJS, which runs on Node. 10 | 11 | For example, it can be used to test Java web applications using [SeLion](https://github.com/paypal/selion/) automation test frameworks. 12 | 13 | For node applications, it can be integrated into [NemoJS testing framework](https://github.com/paypal/nemo) to run accessibility testing during automated unit testing .For Nemo framework use [Nemo-Accessibility plugin] (https://github.com/paypal/nemo-accessibility) 14 | 15 | 16 | ## Set up 17 | 18 | ```sh 19 | $ git clone https://github.com/paypal/AATT.git 20 | $ cd AATT 21 | $ npm i 22 | $ DEBUG=AATT* http_port=3000 node app.js . (If you want to run in Debug mode printing logs) 23 | ``` 24 | $sudo node app.js will run in default port 80 without printing log information 25 | 26 | You can now access the running instance of AATT from http://localhost:3000 27 | 28 | ## Integration with AATT API 29 | AATT provides an API for evaluating HTML Source code from other servers. The API EndPoint is: https://your_nodejs_server/evaluate 30 | 31 | * Accepts the following OPTIONAL parameters: 32 | 1. "source" to send the HTML source of the page. Can be a whole page or partial page source. Defaults to document 33 | 2. "engine" E.g. engine=htmlcs. This is the engine which will scan the code. It accepts a single value of "axe", chrome" or "htmlcs". Defaults to axe 34 | 3. "ouput" to get the jsonified string. E.g. output=json. If this parameter is not set or left empty, it will return a string with table data that can be parsed or appended directly into your page. Defaults to json. 35 | 36 | 4. "errLevel" Error level like Error, Warning or Notices . Mapped to 1, 2 and 3 respectively. E.g. "1,2,3" . (For HTMLCS engine) 37 | 5. "level" This option applies only for the default htmlcs evaluation engine. Options can be either of the following WCAG2AA, WCAG2A, WCAG2AAA, Section508 . Defaults to "WCAG2AA" (For HTMLCS engine) 38 | 39 | 40 | * Set the Request Header Content-type as application/x-www-form-urlencoded 41 | 42 | ## Example 43 | 44 | Here is a sample ajax script which would initiate the request: 45 | 46 | ``` html 47 | var xmlhttp = new XMLHttpRequest(); 48 | xmlhttp.open("POST","http://your_nodejs_server/evaluate",true); 49 | xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 50 | xmlhttp.send("source=" + document.getElementById('source').value + "&priority=" + document.getElementById('priority').value); 51 | 52 | ``` 53 | ## How to Use with nemo-accessibility as a plugin 54 | 55 | [Nemo](https://github.com/paypal/nemo) is a node.js based automation framework for browser automation. It's plugin-architecture helps switch on/off different capabilities. The [nemo-accessibility](https://github.com/paypal/nemo-accessibility) plugin performs accessibility scanning while running browser automation using [Nemo framework](https://github.com/paypal/nemo). 56 | 57 | [Learn more about nemo](https://github.com/paypal/nemo) 58 | 59 | `nemo-accessibility` plugin uses the AATT accessibility API to evaluate HTML source. Therefore you must specify the API url under as a plugin argument like below. 60 | 61 | ```json 62 | "nemo-accessibility":{ 63 | "module":"nemo-accessibility", 64 | "arguments": ["https://your_nodejs_accessibility_server/evaluate"] 65 | } 66 | ``` 67 | 68 | ## How to Use with nightwatchJS 69 | [Nightwatch JS](http://nightwatchjs.org ) is another UI automated testing framework powered by Node.js and uses the Selenium WebDriver API. To call AATT, you need to use the [request module](https://github.com/request/request). NightwatchJs has call back functions like before and after hooks that would be called before or after executing a test case. Request to AATT API should be done in after hook passing the source code of the page to the API. Here is an [example commit](https://github.com/mpnkhan/nightwatch/commit/a377e860e0bfbd21d9e365e86fb3e6c4ec0e63f0) on how to do this with Nightwatch. 70 | 71 | ## How to use as a node module 72 | 73 | The AATT evaluate function can be used directly as a node module, without the 74 | need for using a web API. 75 | 76 | ### Installation 77 | 78 | Add the module to your project 79 | 80 | ```sh 81 | npm install --save aatt 82 | ``` 83 | 84 | ### Usage Example 85 | 86 | This takes the same options as the web `/evaluate` HTTP endpoint. 87 | 88 | ```javascript 89 | const { evaluate } = require('aatt'); 90 | 91 | evaluate({ 92 | source: "Foo

Bar

", 93 | output: "json", 94 | engine: "htmlcs", 95 | level: "WCAG2A" 96 | }).then(result => { 97 | console.log('Results', JSON.parse(result)); 98 | }); 99 | ``` 100 | 101 | ## Copyright and License 102 | 103 | Copyright 2021, PayPal under [the BSD license](LICENSE.md). 104 | 105 | [1]: https://yourhostname/evaluate "AATT api" 106 | 107 | ## Contributors 108 | - Prem Nawaz Khan, developer || [https://github.com/mpnkhan](https://github.com/mpnkhan) || [@mpnkhan](https://twitter.com/mpnkhan) 109 | - Cathy O'Connor, design || [@cagocon](https://twitter.com/cagocon) 110 | - Srinivasu Chakravarthula, user interaction, testing || @csrinivasu 111 | 112 | ## Feedback 113 | We welcome your feedback. Please file issues and/or enhancement requests. 114 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | //R E Q U I R E S 2 | var express = require('express') 3 | var app = express(); 4 | var http = require('http'); 5 | var https = require('https'); 6 | var cons = require('consolidate') 7 | var childProcess = require('child_process'); 8 | var path =require('path'); 9 | var phantomjs = require('phantomjs-prebuilt'); 10 | var binPath = phantomjs.path 11 | var fs = require('fs'); 12 | var bodyParser = require('body-parser'); 13 | var session = require('express-session'); 14 | var debug = require('debug'); 15 | var log = debug('AATT:log'); 16 | var error = debug('AATT:error'); 17 | var nconf = require('nconf'); 18 | var { evaluate } = require('./module.js'); 19 | 20 | nconf.env().argv(); 21 | 22 | // B A S I C C O N F I G 23 | var http_port = nconf.get('http_port') || 80; // Start with Sudo for starting in port 80 or 443 24 | var https_port = nconf.get('https_port') || 443; 25 | var ssl_path= 'cert/ssl.key'; 26 | var cert_file = 'cert/abc.cer'; 27 | 28 | 29 | app.set('views', __dirname + '/views'); 30 | app.engine('html', cons.handlebars); 31 | app.set('view engine', 'html'); 32 | 33 | app.use(bodyParser.json({limit: '100mb'})); 34 | app.use(bodyParser.urlencoded({limit: '100mb', extended: true})); 35 | 36 | app.use(session({ resave: true, 37 | saveUninitialized: true, 38 | secret: 'uwotm8' })); 39 | 40 | app.use(express.static(path.join(__dirname, 'public'))); 41 | app.use(express.static(path.join(__dirname, 'src'))); 42 | app.use('/test', express.static(__dirname + '/test')); 43 | app.use('/src', express.static(__dirname + '/src')); 44 | 45 | app.use('/screenshots', express.static(__dirname + '/screenshots')); 46 | app.use('/Auditor',express.static(path.join(__dirname, 'src/HTML_CodeSniffer/Auditor'))); 47 | app.use('/Images',express.static(path.join(__dirname, 'src/HTML_CodeSniffer/Auditor/Images'))); 48 | 49 | 50 | if (fs.existsSync(ssl_path)) { 51 | var hskey = fs.readFileSync(ssl_path); 52 | var hscert = fs.readFileSync(cert_file) ; 53 | var options = { 54 | key: hskey, 55 | cert: hscert 56 | }; 57 | var httpsServer = https.createServer(options, app); 58 | httpsServer.listen(https_port); 59 | log('Express started on port ' , https_port); 60 | 61 | } else { 62 | var server = http.createServer(app); 63 | app.listen(http_port); 64 | log('Express started on port ', http_port); 65 | } 66 | 67 | app.get('/', function(req, res) { 68 | res.render('index.html',{data:''}); 69 | }); 70 | 71 | app.get('/help', function(req, res) { 72 | res.render('help.html'); 73 | }); 74 | 75 | app.get('/getURL', function(req, res) { 76 | res.render('index.html',{data:''}); 77 | }); 78 | 79 | app.post('/sniffURL', function(req, res) { 80 | var childArgs 81 | , userName = '' 82 | , d = new Date() 83 | , customDateString = d.getHours() +'_' + d.getMinutes() + '_' + d.getSeconds() +'_'+ d.getMonth() + '_' + d.getDate() + '_' + d.getFullYear() 84 | , dirName = "screenshots/" + customDateString 85 | , scrshot = req.body.scrshot 86 | , msgErr = req.body.msgErr 87 | , msgWarn = req.body.msgWarn 88 | , msgNotice = req.body.msgNotice 89 | , eLevel=[] 90 | , engine = req.body.engine 91 | , output ='string' 92 | 93 | if(typeof engine === 'undefined' || engine ==='') engine = 'htmlcs'; 94 | if(typeof output === 'undefined' || output ==='') output = 'string'; 95 | 96 | log('E N G I N E ', engine); 97 | 98 | if (typeof req.session.userName !== 'undefined') { 99 | userName = req.session.userName; 100 | log('Testing logged in session: -> ', userName) 101 | } 102 | if(engine === 'htmlcs'){ 103 | if(typeof msgErr !== 'undefined' && msgErr=='true') eLevel.push(1); 104 | if(typeof msgWarn !== 'undefined' && msgWarn=='true') eLevel.push(2); 105 | if(typeof msgNotice !== 'undefined' && msgNotice=='true') eLevel.push(3); 106 | 107 | //Default to Error 108 | if(typeof msgErr === 'undefined' && typeof msgWarn === 'undefined' && typeof msgNotice === 'undefined') eLevel.push(1); 109 | 110 | if(typeof scrshot !== 'undefined' && scrshot === 'true') fs.mkdirSync(dirName); //Create SCREEN SHOT DIRECTORY 111 | 112 | childArgs = ['--config=config/config.json', path.join(__dirname, 'src/PAET.js') 113 | , req.body.textURL 114 | , 'WCAG2AA' 115 | , userName 116 | , dirName 117 | , scrshot 118 | , eLevel 119 | ]; 120 | } 121 | if(engine === 'axe'){ 122 | childArgs = ['--config=config/config.json', path.join(__dirname, 'src/axe_url.js'), req.body.textURL, output]; 123 | } 124 | if(engine === 'chrome'){ 125 | childArgs = ['--config=config/config.json', path.join(__dirname, 'src/chrome_url.js'), req.body.textURL, output]; 126 | } 127 | childProcess.execFile(binPath, childArgs, function(err, stdout, stderr) { 128 | res.json({ userName: userName, data: stdout }); 129 | log(stdout); 130 | }); 131 | }); 132 | 133 | app.post('/sniffHTML', function(req, res) { 134 | var childArgs 135 | , userName = '' 136 | , d = new Date() 137 | , tempFilename = 'tmp/'+ new Date().getTime() + '.html' 138 | , engine = req.body.engine 139 | , output ='string' 140 | 141 | if(typeof engine === 'undefined' || engine ==='') engine = 'htmlcs'; 142 | if(typeof output === 'undefined' || output ==='') output = 'string'; 143 | 144 | var source = req.body.source; 145 | source = source.replace(/)<[^<]*)*<\/script>/gi,''); //replaces script tags 146 | 147 | log('sniffHTML, E N G I N E ', engine, output); 148 | 149 | var source = 'Home - PayPal Accessibility Tool' 150 | + req.body.source 151 | + ''; 152 | 153 | fs.writeFile(tempFilename, source , function (err,data) { 154 | if (err) throw err; 155 | if(engine === 'htmlcs'){ 156 | var childArgs = ['--config=config/config.json', path.join(__dirname, 'src/HTMLCS_Run.js'), tempFilename, 'WCAG2AA', '1,2,3', output]; 157 | } 158 | if(engine === 'axe'){ 159 | var childArgs = ['--config=config/config.json', path.join(__dirname, 'src/axe_url.js'), tempFilename, output]; 160 | } 161 | if(engine === 'chrome'){ 162 | var childArgs = ['--config=config/config.json', path.join(__dirname, 'src/chrome_url.js'), tempFilename, output] 163 | } 164 | 165 | childProcess.execFile(binPath, childArgs, function(err, stdout, stderr) { 166 | res.json(stdout); 167 | log(stdout); 168 | fs.unlink(tempFilename, (err) => { 169 | if (err) { 170 | console.log("failed to delete : "+ err); 171 | } else { 172 | console.log('successfully deleted ' + tempFilename); 173 | } 174 | }); 175 | }); 176 | }); //fs.writeFile 177 | }); 178 | 179 | app.post('/login', function(req, res) { 180 | var userName= req.body.userName, 181 | password =req.body.password, 182 | stageName = req.body.stageName, 183 | server = req.body.server 184 | 185 | log(userName, password, stageName, server); 186 | req.session.userName = userName; 187 | 188 | var childArgs1 = ['--config=config/config.json', path.join(__dirname, 'src/login.js'), userName, password, stageName, server] 189 | childProcess.execFile(binPath, childArgs1, function(err, stdout, stderr) { 190 | res.json({ userName: userName, data: stdout }); 191 | log(stdout); 192 | }); 193 | }); 194 | 195 | app.get('/logout', function(req, res) { 196 | if (typeof req.session.userName !== 'undefined'){ 197 | var fs = require('fs'); 198 | fs.unlink(req.session.userName+'.txt', function (err) { 199 | if (err) throw err; 200 | log('successfully deleted cookies'); 201 | }); 202 | delete req.session.userName; 203 | } 204 | res.redirect('/'); 205 | }); 206 | 207 | app.post('/evaluate', function(req, res) { 208 | evaluate(req.body).then(function(stdout) { 209 | res.writeHead(200, { 'Content-Type': 'text/plain', "Access-Control-Allow-Origin":"*" }); 210 | res.write(stdout); 211 | res.end(); 212 | log(stdout); 213 | }); 214 | }); 215 | -------------------------------------------------------------------------------- /axe-phantom.js: -------------------------------------------------------------------------------- 1 | var PATH_TO_AXE = './src/axe/axe.min.js'; 2 | var fs = require('fs'); 3 | var page = require('webpage').create(); 4 | 5 | page.open('tmp/1453970269469.html', function (status) { 6 | // Check for page load success 7 | if (status !== 'success') { 8 | console.log('Unable to access network'); 9 | return; 10 | } 11 | 12 | page.injectJs(PATH_TO_AXE); 13 | page.framesName.forEach(function (name) { 14 | page.switchToFrame(name); 15 | page.injectJs(PATH_TO_AXE); 16 | }); 17 | page.switchToMainFrame(); 18 | page.evaluateAsync(function () { 19 | /*global window, axe */ 20 | axe.a11yCheck(window.document, null, function (results) { 21 | window.callPhantom(results); 22 | }); 23 | }); 24 | 25 | page.onCallback = function (msg) { 26 | console.log(JSON.stringify(msg, null, ' ')); 27 | phantom.exit(); 28 | }; 29 | }); -------------------------------------------------------------------------------- /config/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "loadImages": true, 3 | "loadPlugins": false, 4 | "proxy": null, 5 | "diskCache": false, 6 | "ignoreSslErrors": true, 7 | "localAccessRemote": false, 8 | "cookies": null, 9 | "outputEncoding": "utf8", 10 | "scriptEncoding": "utf8" 11 | } 12 | -------------------------------------------------------------------------------- /config/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Accessbility", 3 | "version": "1.0", 4 | "keywords": ["phantomjs", "Accessibility", "headless", "webkit"], 5 | "description": "Headless WebKit with JS API", 6 | 7 | "Contributor": [ { 8 | "name": "Tsaran Victor", 9 | "email": "vtsaran@paypal.com" 10 | } ], 11 | 12 | 13 | "main": "lib/phantomjs", 14 | "bin": { 15 | "phantomjs": "./bin/phantomjs" 16 | }, 17 | "dependencies": { 18 | "express" : "3.0", 19 | "handlebars" : "4.5.3", 20 | "nodemon" : "0.7.2", 21 | "consolidate" : "0.4.0", 22 | "phantomjs" : "*" 23 | 24 | }, 25 | "devDependencies": { 26 | "nodeunit": "~0.7.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /cookies/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/cookies/.keep -------------------------------------------------------------------------------- /loadtest.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(); 2 | page.open('http://www.yahoo.com', function(status) { 3 | console.log("Status: " + status); 4 | if(status === "success") { 5 | page.render('example.png'); 6 | } 7 | phantom.exit(); 8 | }); 9 | 10 | -------------------------------------------------------------------------------- /module.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var path =require('path'); 3 | var childProcess = require('child_process'); 4 | var phantomjs = require('phantomjs-prebuilt'); 5 | var binPath = phantomjs.path; 6 | 7 | var debug = require('debug'); 8 | var log = debug('AATT:log'); 9 | var error = debug('AATT:error'); 10 | 11 | function evaluate(options) { 12 | var engine = options.engine; //Eg htmlcs, chrome, axe default:htmlcs 13 | var output = options.output; // Eg. json, string default: string 14 | var level = options.level; //E.g. WCAG2AA, WCAG2A, WCAG2AAA, Section508 default:WCAG2AA 15 | var errLevel = options.errLevel; // Eg. 1,2,3 1 means Error, 2 means Warning, 3 means Notice default:1,2,3 16 | var source = options.source; 17 | var tempFilename = path.join(__dirname, 'tmp', new Date().getTime() + '.html'); 18 | 19 | if(typeof engine === 'undefined' || engine ==='') engine = 'htmlcs'; 20 | if(typeof output === 'undefined' || output ==='') output = 'string'; 21 | if(typeof level === 'undefined' || level ==='') level = 'WCAG2AA'; 22 | if(typeof errLevel === 'undefined' || errLevel ==='') errLevel = '1,2,3'; 23 | 24 | source = source.replace(/)<[^<]*)*<\/script>/gi,''); //replaces script tags 25 | 26 | return new Promise(function(resolve) { 27 | fs.writeFile(tempFilename, source , function (err,data) { 28 | if (err) throw err; 29 | var config = '--config=' + path.join(__dirname, 'config', 'config.json'); 30 | 31 | if(engine === 'htmlcs'){ 32 | var childArgs = [config, path.join(__dirname, 'src/HTMLCS_Run.js'), tempFilename, level, errLevel, output]; 33 | } 34 | if(engine === 'axe'){ 35 | var childArgs = [config, path.join(__dirname, 'src/axe_url.js'), tempFilename, output]; 36 | } 37 | if(engine === 'chrome'){ 38 | var childArgs = [config, path.join(__dirname, 'src/chrome_url.js'), tempFilename, output] 39 | } 40 | log('E N G I N E ' , engine, binPath, childArgs); 41 | 42 | childProcess.execFile(binPath, childArgs, { cwd: __dirname }, function(err, stdout, stderr) { 43 | stdout = stdout.replace('done',''); 44 | 45 | resolve(stdout); 46 | 47 | fs.unlink(tempFilename, (err) => { 48 | if (err) { 49 | log("failed to delete : "+ err); 50 | } else { 51 | log('successfully deleted ' + tempFilename); 52 | } 53 | }); 54 | }) 55 | }) 56 | }); 57 | } 58 | 59 | module.exports = { evaluate }; 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aatt", 3 | "version": "1.0.2", 4 | "description": "Automated Accessibility Testing Tool", 5 | "main": "module.js", 6 | "dependencies": { 7 | "body-parser": "^1.10.2", 8 | "consolidate": "^0.10.0", 9 | "debug": "^2.2.0", 10 | "express": "^4.17.1", 11 | "express-session": "^1.16.2", 12 | "handlebars": ">=4.7.7", 13 | "nconf": "^0.7.2", 14 | "phantomjs-prebuilt": "^2.1.16" 15 | }, 16 | "devDependencies": {}, 17 | "scripts": { 18 | "test": "echo \"Error: no test specified\" && exit 1" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/paypal/AATT.git" 23 | }, 24 | "keywords": [ 25 | "Selenium", 26 | "NodeJS", 27 | "accessibility", 28 | "testing" 29 | ], 30 | "author": "Nawaz Khan", 31 | "license": "BSD-2-Clause" 32 | } 33 | -------------------------------------------------------------------------------- /ptest1.js: -------------------------------------------------------------------------------- 1 | 2 | var path = require('path') 3 | var childProcess = require('child_process') 4 | var phantomjs = require('phantomjs-prebuilt') 5 | var binPath = phantomjs.path 6 | 7 | 8 | var source = 'Home - PayPal Accessibility Tool' 9 | + '
Test div :
' 10 | + ''; 11 | 12 | var childArgs = [ 13 | path.join(__dirname, 'ptest2.js'), 14 | source 15 | ] 16 | 17 | childProcess.execFile(binPath, childArgs, function(err, stdout, stderr) { 18 | // handle results 19 | console.log('Error', JSON.stringify(stderr)); 20 | console.log('Output', JSON.stringify(stdout)); 21 | }) 22 | 23 | 24 | 25 | 26 | /* 27 | if ( typeof(phantom) !== "undefined" ) { 28 | var page = require('webpage').create(); 29 | 30 | // Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") 31 | page.onConsoleMessage = function(msg) { 32 | console.log(msg); 33 | }; 34 | 35 | page.onAlert = function(msg) { 36 | console.log(msg); 37 | }; 38 | 39 | console.log("* Script running in the Phantom context."); 40 | console.log("* Script will 'inject' itself in a page..."); 41 | page.open("about:blank", function(status) { 42 | if ( status === "success" ) { 43 | console.log(page.injectJs("injectme.js") ? "... done injecting itself!" : "... fail! Check the $PWD?!"); 44 | } 45 | phantom.exit(); 46 | }); 47 | } else { 48 | console.log("* Script running in the Page context."); 49 | } 50 | */ -------------------------------------------------------------------------------- /ptest2.js: -------------------------------------------------------------------------------- 1 | var args = require('system').args; 2 | var source= args[1]; 3 | var page = require('webpage').create(); 4 | var PATH_TO_AXE = './src/axe/axe.js'; 5 | 6 | page.settings.webSecurityEnabled = false; 7 | page.content = source; 8 | 9 | page.injectJs(PATH_TO_AXE); 10 | page.framesName.forEach(function (name) { 11 | page.switchToFrame(name); 12 | page.injectJs(PATH_TO_AXE); 13 | }); 14 | page.switchToMainFrame(); 15 | page.evaluate(function () { 16 | axe.run(document.body, function(err, results) { 17 | window.callPhantom(results); 18 | }); 19 | }); 20 | 21 | page.onCallback = function (results) { 22 | console.log(JSON.stringify(results)) 23 | phantom.exit(); 24 | } -------------------------------------------------------------------------------- /public/css/HTMLCS.css: -------------------------------------------------------------------------------- 1 | #HTMLCS-wrapper *{margin:0;padding:0;float:none;height:inherit;height:auto;width:auto;font-size:inherit;line-height:inherit;box-sizing:content-box;-moz-box-sizing:content-box;}#HTMLCS-wrapper p,#HTMLCS-wrapper div,#HTMLCS-wrapper ul,#HTMLCS-wrapper ol,#HTMLCS-wrapper li,#HTMLCS-wrapper table{background:transparent;color:black;font-family:Arial,Sans Serif;}#HTMLCS-wrapper a,#HTMLCS-wrapper a:visited{border:none;background:none;}#HTMLCS-wrapper a:hover{background:none;}#HTMLCS-wrapper{-moz-transition-timing-function:ease;-webkit-transition-timing-function:ease;position:fixed;top:36px;right:3em;width:300px;overflow:hidden;background-color:#2b2b2b;border:1px solid #2b2b2b;box-shadow:0 0 15px rgba(0,0,0,0.7),0 1px 0 #555 inset;color:#2b2b2b;text-shadow:0 1px 0 #fff;border-radius:.8em .8em .8em .8em;font-family:Arial,Sans Serif;font-size:9px;z-index:100000;}#HTMLCS-wrapper.HTMLCS-translucent{opacity:.5;filter:alpha(opacity=50);}#HTMLCS-wrapper strong{font-weight:bold;}#HTMLCS-wrapper .HTMLCS-header{color:#aaa;text-shadow:0 -1px 0 #000;text-transform:uppercase;position:relative;width:100%;font-size:1.2em;line-height:2em;padding-top:.2em;text-align:center;border-bottom:1px solid transparent;cursor:move;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;}#HTMLCS-wrapper .HTMLCS-close{position:absolute;cursor:pointer;height:2.2em;opacity:.5;filter:alpha(opacity=50);right:0;top:0;width:2.2em;}#HTMLCS-wrapper .HTMLCS-close:hover{opacity:1;filter:none;}#HTMLCS-wrapper .HTMLCS-close:after{position:absolute;background:transparent url(./Images/1.png) 0 -61px no-repeat;content:"";display:block;height:10px;left:50%;margin:-5px 0 0 -5px;top:50%;width:10px;}#HTMLCS-wrapper .HTMLCS-summary,#HTMLCS-wrapper .HTMLCS-summary-detail{text-align:center;padding:.6em .5em .7em;line-height:1.3em;filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr=#919097,endColorstr=#727179);background:-webkit-gradient(linear,left top,left bottom,from(#919097),to(#727179));background:-moz-linear-gradient(center top,#919097,#727179) repeat scroll 0 0 transparent;border-top:1px solid #a2a1a8;display:block;}#HTMLCS-wrapper .HTMLCS-summary:after,#HTMLCS-wrapper .HTMLCS-summary-detail:after{content:"";display:block;clear:both;}#HTMLCS-wrapper .HTMLCS-summary-left{color:#000;float:left;font-size:1.35em;line-height:2.1em;max-width:240px;padding-left:.2em;text-overflow:ellipsis;text-shadow:0 1px 0 rgba(255,255,255,0.3);white-space:nowrap;}#HTMLCS-wrapper .HTMLCS-summary .HTMLCS-summary-left{color:#2b2b2b;}#HTMLCS-wrapper .HTMLCS-summary strong{color:#000;}#HTMLCS-wrapper .HTMLCS-lineage{list-style:none;color:#000;}#HTMLCS-wrapper .HTMLCS-lineage .HTMLCS-lineage-item{position:relative;display:inline-block;height:26px;padding:0 .6em 0 17px;}#HTMLCS-wrapper .HTMLCS-lineage .HTMLCS-lineage-link{color:#000;text-decoration:none;}#HTMLCS-wrapper .HTMLCS-lineage .HTMLCS-lineage-item:first-child{padding-left:0;padding-right:.3em;}#HTMLCS-wrapper .HTMLCS-lineage .HTMLCS-lineage-item:first-child .HTMLCS-lineage-link{display:inline-block;overflow:hidden;width:1.6em;height:1.4em;line-height:1em;}#HTMLCS-wrapper .HTMLCS-lineage .HTMLCS-lineage-item:first-child span{opacity:0;filter:alpha(opacity=0);position:absolute;left:-9999px;width:0;height:0;}#HTMLCS-wrapper .HTMLCS-lineage .HTMLCS-lineage-item:before{background:url(./Images/1.png) no-repeat scroll 0 -74px transparent;content:"";position:absolute;left:0;top:40%;margin-top:-14px;height:32px;width:10px;}#HTMLCS-wrapper .HTMLCS-lineage .HTMLCS-lineage-item:first-child:before{display:none;}#HTMLCS-wrapper .HTMLCS-lineage .HTMLCS-lineage-item:first-child .HTMLCS-lineage-link:after{background:url(./Images/1.png) no-repeat scroll -40px -281px transparent;content:"";display:block;position:absolute;left:46%;top:50%;margin:-6px 0 0 -6px;height:13px;width:12px;overflow:hidden;}#HTMLCS-wrapper .HTMLCS-summary-right{text-align:right;}#HTMLCS-wrapper .HTMLCS-outer-wrapper{width:70em;overflow:hidden;text-align:left;}#HTMLCS-wrapper .HTMLCS-inner-wrapper{background:url(./Images/2.gif) repeat scroll 0 0 #54535a;border-radius:0 0 .75em .75em;text-align:left;display:none;vertical-align:top;width:300px;overflow:hidden;-moz-transition:margin .2s ease-in 0s;-webkit-transition:margin .2s ease-in 0s;}#HTMLCS-wrapper .HTMLCS-details{display:none;line-height:2em;text-align:left;width:300px;overflow:hidden;border-top:1px solid #2b2b2b;background-color:rgba(0,0,0,0.2);border-bottom:1px solid #656565;}#HTMLCS-wrapper .HTMLCS-issue-list,#HTMLCS-wrapper .HTMLCS-issue-detail-list{display:inline-block;list-style:none outside none;margin:0;padding:0;vertical-align:top;-moz-transition:margin .2s ease-in 0s;-webkit-transition:margin .2s ease-in 0s;}#HTMLCS-wrapper .HTMLCS-issue-list{width:300px;z-index:1;}#HTMLCS-wrapper .HTMLCS-issue-list>li{font-size:1.38em;background-color:#2b2b2b;border-bottom:1px solid #000;border-top:1px solid #4b4b4b;line-height:1.3em;min-height:4em;padding:.8em 2em .6em 2.2em;position:relative;text-shadow:0 -1px 0 #000;margin:0;}#HTMLCS-wrapper .HTMLCS-issue-list>li>.HTMLCS-issue-type,#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-type{height:100%;left:0;position:absolute;top:-1px;border-top:1px solid black;border-bottom:1px solid black;width:1.6em;}#HTMLCS-wrapper .HTMLCS-issue-type:before{background:url(./Images/1.png) no-repeat scroll 0 0 transparent;content:"";height:14px;left:50%;margin:-7px 0 0 -7px;position:absolute;top:50%;width:14px;}#HTMLCS-wrapper .HTMLCS-issue-list>li:after{background:url(./Images/1.png) no-repeat scroll 0 -108px transparent;content:"";height:14px;margin-top:-7px;position:absolute;right:.7em;top:50%;width:9px;}#HTMLCS-wrapper .HTMLCS-issue-list>li>.HTMLCS-issue-type.HTMLCS-warning,#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-type.HTMLCS-warning{background-color:#c5a00c;border-top-color:#f2d560;}#HTMLCS-wrapper .HTMLCS-issue-type.HTMLCS-warning:before{background-position:0 -16px;}#HTMLCS-wrapper .HTMLCS-issue-list>li>.HTMLCS-issue-type.HTMLCS-error,#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-type.HTMLCS-error{background-color:#952424;border-top-color:#c76161;}#HTMLCS-wrapper .HTMLCS-issue-list>li>.HTMLCS-issue-type.HTMLCS-notice,#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-type.HTMLCS-notice{background-color:#6c6b72;border-top-color:#828188;}#HTMLCS-wrapper .HTMLCS-issue-type.HTMLCS-notice:before{background-position:0 -32px;}#HTMLCS-wrapper .HTMLCS-issue-list>li>.HTMLCS-issue-title,#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-title{color:#fff;display:block;}#HTMLCS-wrapper .HTMLCS-issue-list>li>.HTMLCS-issue-title{line-height:1.2em;text-align:left;}#HTMLCS-wrapper .HTMLCS-details>.HTMLCS-issue-list>li:hover{background-color:#3b3b3b;border-top-color:#5b5b5c;cursor:pointer;}#HTMLCS-wrapper .HTMLCS-details>.HTMLCS-issue-detail-list>li{display:inline-block;vertical-align:top;}#HTMLCS-wrapper .HTMLCS-issue-detail-list>li{background-color:#2b2b2b;border-top:1px solid #4b4b4b;color:#fff;width:300px;max-height:10em;position:relative;text-shadow:none;font-size:1.4em;-moz-transition:margin .2s ease-in 0s;-webkit-transition:margin .2s ease-in 0s;}#HTMLCS-wrapper .HTMLCS-issue-detail-list>li.HTMLCS-current{max-height:inherit;}#HTMLCS-wrapper .HTMLCS-issue-detail-list .HTMLCS-issue-details{position:relative;display:block;border-bottom:1px solid #2b2b2b;}#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-title{font-size:1.07em;border-bottom:1px solid #000;padding:.8em 0 1.1em;margin:0 1.4em 0 2.8em;}#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-wcag-ref{border-top:1px solid #5b5b5b;padding-top:1em;text-shadow:none;color:#fff;padding:.7em 0 1.5em;margin:0 1.4em 0 2.8em;}#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-wcag-ref>em{color:#8f8f94;background:none;display:inline-block;font-style:normal;font-weight:bold;margin-top:.5em;min-width:5.5em;}#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-wcag-ref>a,#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-wcag-ref>a:visited{border-bottom:1px solid #777;color:#dbdbe1;display:inline-block;font-style:normal;text-decoration:none;line-height:1.2em;margin-right:.3em;}#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-wcag-ref>a:hover{border-bottom-color:#87878e;}#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-wcag-ref>a:after{background:url(./Images/1.png) no-repeat scroll 0 -48px transparent;content:"";display:inline-block;opacity:.6;filter:alpha(opacity=60);height:11px;margin:0 0 0 .5em;width:12px;}#HTMLCS-wrapper .HTMLCS-issue-detail-list>li .HTMLCS-issue-wcag-ref>a:hover:after{opacity:1;filter:alpha(opacity=100);}#HTMLCS-wrapper .HTMLCS-issue-source{position:relative;display:block;background:url(./Images/2.gif) repeat scroll 0 0 #54535a;border-top:1px solid #6c6c6d;font-size:1em;padding:.5em;}#HTMLCS-wrapper .HTMLCS-issue-source .HTMLCS-issue-source-header{text-align:right;font-size:.7em;margin:0 0 .7em 0;}#HTMLCS-wrapper .HTMLCS-issue-source .HTMLCS-issue-source-header strong{color:#9b99a5;display:inline-block;float:left;font-size:1.49em;line-height:1.8em;padding-left:.2em;text-shadow:0 -1px 0 rgba(0,0,0,0.6);}#HTMLCS-wrapper .HTMLCS-issue-source-inner{background-color:rgba(0,0,0,0.3);border-radius:.4em;box-shadow:0 0 3px rgba(0,0,0,0.4) inset,0 1px 0 rgba(255,255,255,0.15);color:#787878;font-family:Monaco,monospace;font-size:.9em;line-height:1.4em;margin:0;padding:.6em;overflow:auto;}#HTMLCS-wrapper .HTMLCS-issue-source-inner-u2p{padding:.35em;clear:both;color:white;}#HTMLCS-wrapper .HTMLCS-issue-source-not-supported{color:white;font-size:1em;line-height:1.4em;margin:0 0 .5em .2em;overflow:auto;}#HTMLCS-wrapper .HTMLCS-issue-source-inner strong{font-weight:normal;color:#FFF;}#HTMLCS-wrapper .HTMLCS-navigation{font-size:1.5em;line-height:2em;padding:.5em 0 .6em;text-align:center;display:none;}#HTMLCS-wrapper .HTMLCS-page-number{color:#eee;display:inline-block;font-size:.9em;font-weight:bold;padding:0 1em;text-shadow:none;vertical-align:middle;}#HTMLCS-wrapper .HTMLCS-nav-button{background-color:rgba(0,0,0,0.3);border-radius:.3em;box-shadow:0 1px 0 rgba(255,255,255,0.3);cursor:pointer;display:inline-block;height:2.2em;position:relative;width:2.5em;}#HTMLCS-wrapper .HTMLCS-nav-button.HTMLCS-previous:after,#HTMLCS-wrapper .HTMLCS-nav-button.HTMLCS-next:after{background:url(./Images/1.png) no-repeat scroll 0 -237px transparent;content:"";height:14px;left:50%;margin:-7px 0 0 -6px;position:absolute;top:50%;width:11px;}#HTMLCS-wrapper .HTMLCS-nav-button.HTMLCS-next:after{background-position:0 -253px;margin-left:-5px;}#HTMLCS-wrapper .HTMLCS-nav-button.HTMLCS-disabled{cursor:default;filter:alpha(opacity=30);opacity:.3;}#HTMLCS-wrapper .HTMLCS-button{border:none;box-shadow:0 0 2px 0 rgba(0,0,0,0.5),0 1px 0 rgba(255,255,255,0.8) inset,0 -14px 16px -8px rgba(0,0,0,0.3) inset;color:#2b2b2b;background-color:#e9e9eb;text-shadow:0 1px 0 #fff;border-radius:.25em;cursor:pointer;display:inline-block;font-size:1.6em;line-height:1.7em;margin-left:.3em;min-width:1em;padding:0 .5em;position:relative;text-align:center;}#HTMLCS-wrapper .HTMLCS-button-group{display:inline-block;margin-left:.6em;}#HTMLCS-wrapper .HTMLCS-button-group:first-child{margin-left:0;}#HTMLCS-wrapper .HTMLCS-button-group .HTMLCS-button{border-radius:.1em .1em .1em .1em;margin:0;}#HTMLCS-wrapper .HTMLCS-button-group .HTMLCS-button.active,.HTMLCS-button-group .HTMLCS-button.selected{border-radius:0;}#HTMLCS-wrapper .HTMLCS-button-group .HTMLCS-button:first-child{border-bottom-left-radius:.3em;border-left:medium none!important;border-top-left-radius:.3em;}#HTMLCS-wrapper .HTMLCS-button-group .HTMLCS-button:last-child{border-bottom-right-radius:.3em;border-top-right-radius:.3em;}#HTMLCS-wrapper .HTMLCS-button-icon{background-image:url(./Images/1.png);height:20px;left:50%;margin:-10px 0 0 -12.5px;position:absolute;top:50%;width:25px;}#HTMLCS-wrapper .HTMLCS-button-icon.HTMLCS-button-next{background-position:-13px -259px;}#HTMLCS-wrapper .HTMLCS-button-icon.HTMLCS-button-previous{background-position:-40px -259px;}#HTMLCS-wrapper .HTMLCS-button-icon.HTMLCS-button-pointer{background-position:-13px -281px;}#HTMLCS-wrapper .HTMLCS-button-icon.HTMLCS-button-copy{background-position:-13px -304px;}#HTMLCS-wrapper .HTMLCS-settings{display:none;background:url(./Images/2.gif) repeat scroll 0 0 #54535a;color:#eee;text-shadow:none;border-radius:0 0 .75em .75em;padding:5px;position:relative;text-align:center;z-index:1;}#HTMLCS-settings-recheck{text-align:center;}#HTMLCS-wrapper .HTMLCS-settings h1{color:#fff;font-size:16px;font-weight:normal;margin:.1em 0 .5em;}#HTMLCS-wrapper .HTMLCS-settings p{color:#666;font-size:12px;margin:.5em 0 1.5em;}#HTMLCS-wrapper .HTMLCS-settings button{font-family:Arial,Sans Serif;}#HTMLCS-wrapper .HTMLCS-checkbox{display:block;position:relative;clear:both;margin-bottom:.6em;text-align:left;text-shadow:none;color:#FFF;background-color:rgba(255,255,255,0.06);border:1px solid #393939;border-radius:.4em .4em .4em .4em;box-shadow:0 1px 0 rgba(255,255,255,0.2) inset,0 1px 0 rgba(255,255,255,0.1);padding:0 .8em;font-size:1.2em;cursor:pointer;}#HTMLCS-wrapper .HTMLCS-checkbox.disabled{opacity:.4;filter:alpha(opacity=40);cursor:default;}#HTMLCS-wrapper .HTMLCS-checkbox-title{text-shadow:0 -1px 0 rgba(0,0,0,0.4);color:#ddd;white-space:nowrap;font-size:1.1em;line-height:2.5em;}#HTMLCS-wrapper .HTMLCS-checkbox-switch{font-size:1.2em;position:absolute;overflow:hidden;right:.6em;width:3.2em;height:1.6em;top:50%;margin-top:-0.8em;cursor:pointer;background-color:#2b2b2b;border-radius:.8em;box-shadow:0 0 1px 1px rgba(0,0,0,0.6) inset,0 1px 0 rgba(255,255,255,0.2);-moz-transition:background-color .2s ease;-webkit-transition:background-color .2s ease;box-shadow:0 2px 2px -1px rgba(0,0,0,0.8) inset,0 -2px 2px -2px #000 inset,0 1px 0 rgba(255,255,255,0.2),1px 0 0 rgba(0,0,0,0.3) inset,-1px 0 0 rgba(0,0,0,0.3) inset;}#HTMLCS-wrapper .HTMLCS-checkbox.active .HTMLCS-checkbox-switch{background-color:#205caf;}#HTMLCS-wrapper .HTMLCS-checkbox.disabled *{cursor:default;}#HTMLCS-wrapper .HTMLCS-checkbox-slider{-moz-transition:left .2s ease-out;-webkit-transition:left .2s ease-out;content:"";position:absolute;width:1.3em;height:1.3em;border-radius:.65em;left:.15em;top:50%;margin-top:-0.65em;background-color:#65636b;box-shadow:0 0 2px #000,0 1px 0 rgba(255,255,255,0.3) inset;}#HTMLCS-wrapper .HTMLCS-checkbox.active .HTMLCS-checkbox-slider{left:1.7em;background-color:#d9d9de;box-shadow:0 0 2px 1px rgba(0,0,0,0.6),0 1px 0 rgba(255,255,255,0.3) inset;background:-webkit-gradient(linear,left top,left bottom,from(#e9e9eb),to(#c2c2cb));background:-moz-linear-gradient(top,#e9e9eb,#c2c2cb);}#HTMLCS-wrapper .HTMLCS-checkbox-switch:before{content:"";display:block;opacity:0;filter:alpha(opacity=0);position:absolute;width:10px;height:7px;background:transparent url(./Images/1.png) 0 -124px no-repeat;left:.5em;top:51%;margin-top:-3px;-moz-transition:.2s opacity ease-in;-webkit-transition:.2s opacity ease-in;}#HTMLCS-wrapper .HTMLCS-checkbox.active .HTMLCS-checkbox-switch:before{opacity:1;}#HTMLCS-wrapper .HTMLCS-checkbox input{visibility:hidden;}#HTMLCS-wrapper.showing-issue-list .HTMLCS-inner-wrapper{display:inline-block;}#HTMLCS-wrapper.showing-issue-list .HTMLCS-details{display:block;}#HTMLCS-wrapper.showing-issue-list .HTMLCS-navigation{display:block;}#HTMLCS-wrapper.showing-settings .HTMLCS-settings{display:block;}#HTMLCS-wrapper .HTMLCS-summary-detail{display:none;}#HTMLCS-wrapper .HTMLCS-summary-detail .HTMLCS-summary-left{font-weight:bold;}#HTMLCS-wrapper .HTMLCS-issue-detail-list.HTMLCS-transition-disabled>li{-moz-transition:margin 0s ease 0s;-webkit-transition:margin 0s ease 0s;}.HTMLCS-pointer{height:62px;left:0;position:absolute;top:0;width:50px;z-index:9999;background:transparent url(./Images/1.png) -16px -64px no-repeat;}.HTMLCS-pointer-up{background-position:-16px 0;}.HTMLCS-pointer-left{width:62px;height:50px;background-position:0 -133px;}.HTMLCS-pointer-right{width:62px;height:50px;background-position:0 -185px;}.HTMLCS-pointer-hidden{opacity:0;filter:alpha(opacity=0);display:none;}.HTMLCS-pointer.HTMLCS-pointer-hidden-block{opacity:0;filter:alpha(opacity=0);display:block;}#HTMLCS-wrapper #HTMLCS-settings-use-standard label{color:#FFF;display:inline;}#HTMLCS-wrapper #HTMLCS-settings-use-standard>select{float:right;margin-top:-0.1em;}#HTMLCS-wrapper #HTMLCS-settings-use-standard:after{clear:both;display:block;}#HTMLCS-wrapper #HTMLCS-settings-use-standard{-moz-box-sizing:border-box;border:1px solid #393939;border-radius:.4em .4em .4em .4em;box-shadow:0 1px 2px rgba(0,0,0,0.2) inset,0 1px 0 rgba(255,255,255,0.1);color:#fff;font-size:1.3em;margin:0 0 1em;padding:.7em .7em .8em;text-align:left;text-shadow:0 -1px rgba(0,0,0,0.3);}#HTMLCS-wrapper #HTMLCS-settings-view-report{background-color:#3a3940;border:1px solid #2b2b2b;border-radius:.3em .3em .3em .3em;box-shadow:0 1px 0 rgba(255,255,255,0.2) inset,0 1px 0 rgba(255,255,255,0.15),0 -1.4em 1.6em rgba(0,0,0,0.4) inset;color:#FFF;cursor:pointer;font-size:1.9em;line-height:3em;position:relative;text-shadow:0 -1px 0 #000;-moz-transition:opacity .3s ease;-webkit-transition:opacity .3s ease;opacity:1;text-align:center;}#HTMLCS-wrapper #HTMLCS-settings-view-report:after{background:url(./Images/1.png) no-repeat scroll 0 -108px transparent;content:"";height:14px;margin-top:-7px;position:absolute;right:1em;top:50%;width:9px;}#HTMLCS-wrapper #HTMLCS-settings-view-report:hover{background-color:#2b2b2b;}#HTMLCS-wrapper.HTMLCS-processing #HTMLCS-settings-view-report,#HTMLCS-wrapper #HTMLCS-settings-view-report.disabled{cursor:default;filter:alpha(opacity=40);opacity:.4;}#HTMLCS-wrapper .HTMLCS-button.disabled{cursor:default;filter:alpha(opacity=30);opacity:.3;}#HTMLCS-wrapper #HTMLCS-settings-issue-count{text-align:center;}#HTMLCS-wrapper.HTMLCS-processing .HTMLCS-tile-text>strong{visibility:hidden;}#HTMLCS-wrapper.HTMLCS-processing .HTMLCS-tile-text:before{background:url(./Images/3.gif) no-repeat scroll 50% 50% transparent;content:"";display:block;height:30px;left:50%;margin-left:-15px;position:absolute;top:.5em;width:30px;}#HTMLCS-wrapper.HTMLCS-processing .HTMLCS-issue-tile.HTMLCS-warning .HTMLCS-tile-text:before{background-image:url(./Images/4.gif);}#HTMLCS-wrapper.HTMLCS-processing .HTMLCS-issue-tile.HTMLCS-notice .HTMLCS-tile-text:before{background-image:url(./Images/5.gif);}#HTMLCS-wrapper #HTMLCS-settings-issue-count-help{clear:both;color:#AAA;display:block;font-size:1.3em;text-align:center;padding:1em 0;text-shadow:0 -1px rgba(0,0,0,0.3);}#HTMLCS-wrapper #HTMLCS-settings-updated-notification{color:#FFF;display:block;font-size:1.3em;text-align:left;padding:1em;}#HTMLCS-wrapper #HTMLCS-settings-updated-notification a,#HTMLCS-wrapper #HTMLCS-settings-updated-notification a:visited{border-bottom:1px solid #777;color:#dbdbe1;display:inline-block;font-style:normal;text-decoration:none;line-height:1.2em;margin-right:.3em;}#HTMLCS-wrapper #HTMLCS-settings-updated-notification a:hover{border-bottom-color:#87878e;}#HTMLCS-wrapper #HTMLCS-settings-updated-notification a:after{background:url(./Images/1.png) no-repeat scroll 0 -48px transparent;content:"";display:inline-block;opacity:.6;filter:alpha(opacity=60);height:11px;margin:0 0 0 .5em;width:12px;}#HTMLCS-wrapper #HTMLCS-settings-updated-notification a:hover:after{opacity:1;filter:alpha(opacity=100);}#HTMLCS-wrapper .HTMLCS-issue-tile{-moz-box-sizing:border-box;display:inline-block;margin-left:.5em;width:31%;text-align:center;}#HTMLCS-wrapper .HTMLCS-issue-tile:first-child{margin-left:0;}#HTMLCS-wrapper .HTMLCS-issue-tile.HTMLCS-error>.HTMLCS-tile-text{background-color:#8b2222;}#HTMLCS-wrapper .HTMLCS-issue-tile.HTMLCS-warning>.HTMLCS-tile-text{background-color:#b3913a;}#HTMLCS-wrapper .HTMLCS-issue-tile.HTMLCS-notice>.HTMLCS-tile-text{background-color:#66656b;}#HTMLCS-wrapper .HTMLCS-issue-tile>.HTMLCS-tile-text:after{background:url(./Images/1.png) no-repeat scroll 0 0 transparent;bottom:.6em;content:"";height:14px;left:50%;margin:-7px 0 0 -7px;position:absolute;width:14px;}#HTMLCS-wrapper .HTMLCS-issue-tile.HTMLCS-warning>.HTMLCS-tile-text:after{background-position:0 -16px;}#HTMLCS-wrapper .HTMLCS-issue-tile.HTMLCS-notice>.HTMLCS-tile-text:after{background-position:0 -32px;}#HTMLCS-wrapper .HTMLCS-tile-text>strong{font-size:1.7em;color:#FFF;display:block;text-align:center;}#HTMLCS-wrapper .HTMLCS-issue-tile>.HTMLCS-tile-text{border:1px solid #393939;border-bottom:none;border-radius:.3em .3em 0 0;box-shadow:0 1px 0 rgba(255,255,255,0.2) inset;color:#fff;display:block;font-size:1.7em;line-height:1.5em;padding:.9em 0 1.8em;position:relative;text-shadow:0 -1px rgba(0,0,0,0.3);text-align:center;}#HTMLCS-wrapper .HTMLCS-issue-tile>.HTMLCS-checkbox{border-radius:0 0 .4em .4em;margin:0;min-height:2.7em;}#HTMLCS-wrapper .HTMLCS-issue-tile>.HTMLCS-checkbox:after{clear:both;content:"";display:block;}#HTMLCS-wrapper .HTMLCS-issue-tile .HTMLCS-checkbox-switch{margin-right:-1.6em;right:50%;} -------------------------------------------------------------------------------- /public/css/home-print.css: -------------------------------------------------------------------------------- 1 | a:link { 2 | text-decoration:underline; color:green; 3 | } 4 | 5 | a:link:after, a:visited:after { 6 | content:" [" attr(href) "] "; color:#305093; 7 | } 8 | 9 | h1 { 10 | top:0; 11 | left:0; 12 | color:#000000; 13 | } 14 | 15 | h1:after { 16 | content: url(../images/html-code-sniffer-print.png); 17 | width:100px; 18 | position:absolute; 19 | top:10px; 20 | left:0; 21 | } 22 | 23 | h2, h3, h4, h5, h6 { 24 | color:#000000; 25 | text-shadow:none; 26 | } 27 | 28 | h2,h3 { 29 | border-bottom: 2px solid #000000; 30 | } 31 | 32 | #test-area h3 { 33 | text-shadow:none; 34 | color:#000000; 35 | } 36 | 37 | p { 38 | text-shadow:none; 39 | font-size:17px; 40 | color:#333333; 41 | } 42 | 43 | #back-to-top { 44 | display:none; 45 | } 46 | 47 | /* GIT HUB BANNER */ 48 | 49 | #git { 50 | display:none; 51 | } 52 | 53 | /* HEADER */ 54 | 55 | #header { 56 | margin:10px 0 20px 0; 57 | } 58 | 59 | #header:after { 60 | content: url(../images/squiz-logo.png); 61 | position:absolute; 62 | top:40px; 63 | right:15px; 64 | } 65 | 66 | #sprites { 67 | display:none; 68 | } 69 | 70 | #introduction { 71 | border:none; 72 | box-shadow:none; 73 | text-shadow:none; 74 | min-height:0px; 75 | padding:0; 76 | margin:0; 77 | } 78 | 79 | #introduction #content { 80 | width:100%; 81 | text-shadow:none; 82 | font-size:18px; 83 | } 84 | 85 | .introduction-divider { 86 | display:none; 87 | } 88 | 89 | #standards-list { 90 | display:none; 91 | } 92 | 93 | #introduction #bookmarklet { 94 | display:none; 95 | } 96 | #code-input { 97 | border-radius:5px; 98 | border:none; 99 | width:300px; 100 | overflow:hidden; 101 | box-shadow:none; 102 | } 103 | 104 | #source { 105 | display:none; 106 | } 107 | 108 | #run-button { 109 | display:none; 110 | } 111 | 112 | button { 113 | display:none; 114 | } 115 | 116 | #code-overlay { 117 | display:none; 118 | } 119 | 120 | #test-area { 121 | border:none; 122 | box-shadow:none; 123 | border-radius:0; 124 | } 125 | 126 | #test-area p { 127 | display:none; 128 | } 129 | 130 | #test-area table { 131 | border-collapse: collapse; 132 | border:1px solid #000000; 133 | font-size:14px; 134 | margin:20px 0 0px 14px; 135 | text-align:left; 136 | box-shadow: none; 137 | width:98%; 138 | text-shadow: none; 139 | color:#333333; 140 | background:#ffffff; 141 | } 142 | 143 | #test-area tr { 144 | border-bottom:none; 145 | } 146 | 147 | #test-area tr:last-child { 148 | border-bottom:none; 149 | } 150 | 151 | #test-area th{ 152 | background:#ffffff; 153 | color:#ffffff; 154 | font-weight:bold; 155 | padding:9px 10px; 156 | font-weight:bold; 157 | font-size:15px; 158 | text-shadow: none; 159 | border-left:1px solid #d8d7d7; 160 | box-shadow: none; 161 | border-top:1px solid #e0e0e0; 162 | } 163 | 164 | #test-area th:first-child { 165 | text-align:center; 166 | } 167 | 168 | #test-area td:last-child { 169 | box-shadow: none; 170 | } 171 | 172 | #test-area td:first-child { 173 | box-shadow: none; 174 | border-left:none; 175 | text-align:center; 176 | } 177 | 178 | #test-area th{ 179 | background:#ffffff; 180 | color:#000000; 181 | font-weight:bold; 182 | padding:9px 10px; 183 | font-weight:bold; 184 | font-size:18px; 185 | text-shadow: none; 186 | border-left:1px solid #d8d7d7; 187 | box-shadow: none; 188 | border:1px solid #000000; 189 | } 190 | 191 | #test-area td{ 192 | padding:7px 15px; 193 | text-align:left; 194 | text-shadow: none; 195 | border:1px solid #000000; 196 | color:#1c283f; 197 | vertical-align:top; 198 | box-shadow: none; 199 | background:#ffffff; 200 | color:#333333; 201 | page-break-inside: avoid; 202 | } 203 | 204 | #resultsWrapper { 205 | border:none; 206 | box-shadow:none; 207 | } 208 | 209 | .radio-gen { 210 | box-shadow:none; 211 | border:1px solid #000000; 212 | } 213 | 214 | .radio-gen.radio-on { 215 | box-shadow:none; 216 | border:1px solid #000000; 217 | } 218 | 219 | .radio-gen.radio-on:after { 220 | border:5px solid #000000; 221 | width:2px; 222 | height:2px; 223 | box-shadow:none; 224 | } 225 | 226 | #test-options { 227 | position:relative; 228 | width:960px; 229 | padding:10px 0; 230 | color:#1c283f; 231 | border:none; 232 | position:relative; 233 | } 234 | 235 | #test-options ul { 236 | margin:0; 237 | padding:0 0 0 15px; 238 | width:700px; 239 | position:relative; 240 | top:8px; 241 | } 242 | 243 | #test-options li { 244 | display:inline-block; 245 | font-size:13px; 246 | font-weight:normal; 247 | text-shadow: none; 248 | padding-right:15px; 249 | } 250 | 251 | #test-results-heading { 252 | position:relative; 253 | top:-70px; 254 | } 255 | 256 | #results-overview { 257 | display:inline-block; 258 | background: #ffffff; 259 | box-shadow:none; 260 | border-radius:0px; 261 | font-size:13px; 262 | position:absolute; 263 | top:0px; 264 | right:10px; 265 | padding:0 0 0px 0; 266 | border:1px solid #000000; 267 | text-shadow: none; 268 | overflow:hidden; 269 | 270 | } 271 | 272 | #results-overview li { 273 | display:inline-block; 274 | border-right:1px solid #000000; 275 | box-shadow: none; 276 | background:#ffffff; 277 | } 278 | 279 | #results-overview li:last-child { 280 | box-shadow:none; 281 | } 282 | 283 | #results-overview li a{ 284 | display:inline-block; 285 | padding:5px 20px; 286 | text-decoration:none; 287 | color:#18243b; 288 | } 289 | 290 | #results-overview li:last-child { 291 | border-right:none; 292 | } 293 | 294 | #test-results td.number { 295 | font-size:15px; 296 | font-weight:bold; 297 | text-shadow: none; 298 | padding-top:8px; 299 | padding:8px 5px; 300 | } 301 | 302 | #resultsWrapper { 303 | position:relative; 304 | top:-60px; 305 | background:none; 306 | } 307 | 308 | .footnote { 309 | display:none; 310 | } 311 | 312 | #footer { 313 | margin-top:0; 314 | padding-top:0px; 315 | text-decoration:none; 316 | color:#333333; 317 | } 318 | -------------------------------------------------------------------------------- /public/css/lib/bootstrap-accessibility.css: -------------------------------------------------------------------------------- 1 | .btn:focus { 2 | outline: dotted 2px black; 3 | } 4 | div.active:focus { 5 | outline: dotted 1px black; 6 | } 7 | a:focus { 8 | outline: dotted 1px black; 9 | } 10 | .close:hover, 11 | .close:focus { 12 | outline: dotted 1px black; 13 | } 14 | 15 | .nav > li > a:hover, 16 | .nav > li > a:focus { 17 | outline: dotted 1px black; 18 | } 19 | 20 | .carousel-inner > .item { 21 | position: absolute; 22 | top: -999999em; 23 | display: block; 24 | -webkit-transition: 0.6s ease-in-out left; 25 | transition: 0.6s ease-in-out left; 26 | } 27 | .carousel-inner > .active { 28 | top: 0; 29 | } 30 | .carousel-inner > .active, 31 | .carousel-inner > .next, 32 | .carousel-inner > .prev { 33 | position: relative; 34 | } 35 | .carousel-inner > .next, 36 | .carousel-inner > .prev { 37 | position: absolute; 38 | top: 0; 39 | width: 100%; 40 | } 41 | 42 | .alert-success { 43 | color: #2d4821; 44 | } 45 | .alert-info { 46 | color: #214c62; 47 | } 48 | .alert-warning { 49 | color: #6c4a00; 50 | background-color: #f9f1c6; 51 | } 52 | .alert-danger { 53 | color: #d2322d; 54 | } 55 | .alert-danger:hover { 56 | color: #c12f2a; 57 | } -------------------------------------------------------------------------------- /public/img/AATT_a11y-edge_sticker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/AATT_a11y-edge_sticker.png -------------------------------------------------------------------------------- /public/img/HTMLCS-tools.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/HTMLCS-tools.png -------------------------------------------------------------------------------- /public/img/PayPal_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/PayPal_logo.png -------------------------------------------------------------------------------- /public/img/asc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/asc.gif -------------------------------------------------------------------------------- /public/img/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/bg.gif -------------------------------------------------------------------------------- /public/img/bgTexture1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/bgTexture1.gif -------------------------------------------------------------------------------- /public/img/desc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/desc.gif -------------------------------------------------------------------------------- /public/img/external-link-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/external-link-hover.png -------------------------------------------------------------------------------- /public/img/external-link-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/external-link-table.png -------------------------------------------------------------------------------- /public/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /public/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /public/img/icon_load_roundcorner_lock1_186x42_withlock.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/icon_load_roundcorner_lock1_186x42_withlock.gif -------------------------------------------------------------------------------- /public/img/logo_347x50_PPa11y.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/logo_347x50_PPa11y.png -------------------------------------------------------------------------------- /public/img/summaryLoader-error.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/summaryLoader-error.gif -------------------------------------------------------------------------------- /public/img/summaryLoader-notice.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/summaryLoader-notice.gif -------------------------------------------------------------------------------- /public/img/summaryLoader-warning.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/summaryLoader-warning.gif -------------------------------------------------------------------------------- /public/img/test-area-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/public/img/test-area-bg.png -------------------------------------------------------------------------------- /public/js/lib/bootstrap-accessibility.min.js: -------------------------------------------------------------------------------- 1 | /*! bootstrap-accessibility-plugin - v1.0.2 - 2014-03-18 2 | * https://github.com/paypal/bootstrap-accessibility-plugin 3 | * Copyright (c) 2014 PayPal Accessibility Team; Licensed BSD */ 4 | !function($){"use strict";var uniqueId=function(prefix){return(prefix||"ui-id")+"-"+Math.floor(1e3*Math.random()+1)};$(".alert").attr("role","alert"),$(".close").removeAttr("aria-hidden").wrapInner('').append('Close');var showTooltip=$.fn.tooltip.Constructor.prototype.show,hideTooltip=$.fn.tooltip.Constructor.prototype.hide;$.fn.tooltip.Constructor.prototype.show=function(){showTooltip.apply(this,arguments);var $tip=this.tip(),tooltipID=$tip.attr("id")||uniqueId("ui-tooltip");$tip.attr({role:"tooltip",id:tooltipID}),this.$element.attr("aria-describedby",tooltipID)},$.fn.tooltip.Constructor.prototype.hide=function(){hideTooltip.apply(this,arguments),removeMultiValAttributes(this.$element,"aria-describedby",this.tip().attr("id"))};{var showPopover=$.fn.popover.Constructor.prototype.setContent;$.fn.popover.Constructor.prototype.hide}$.fn.popover.Constructor.prototype.setContent=function(){showPopover.apply(this,arguments);var $tip=this.tip(),tooltipID=$tip.attr("id")||uniqueId("ui-tooltip");$tip.attr({role:"alert",id:tooltipID}),this.$element.attr("aria-describedby",tooltipID),this.$element.focus()},$.fn.popover.Constructor.prototype.hide=function(){hideTooltip.apply(this,arguments),removeMultiValAttributes(this.$element,"aria-describedby",this.tip().attr("id"))},$(".modal-dialog").attr({role:"document"});var modalhide=$.fn.modal.Constructor.prototype.hide;$.fn.modal.Constructor.prototype.hide=function(){var modalOpener=this.$element.parent().find('[data-target="#'+this.$element.attr("id")+'"]');modalhide.apply(this,arguments),modalOpener.focus()};var $par,firstItem,toggle="[data-toggle=dropdown]",focusDelay=200,menus=$(toggle).parent().find("ul").attr("role","menu"),lis=menus.find("li").attr("role","presentation");lis.find("a").attr({role:"menuitem",tabIndex:"-1"}),$(toggle).attr({"aria-haspopup":"true","aria-expanded":"false"}),$(toggle).parent().on("shown.bs.dropdown",function(){$par=$(this);var $toggle=$par.find(toggle);$toggle.attr("aria-expanded","true"),setTimeout(function(){firstItem=$(".dropdown-menu [role=menuitem]:visible",$par)[0];try{firstItem.focus()}catch(ex){}},focusDelay)}),$(toggle).parent().on("hidden.bs.dropdown",function(){$par=$(this);var $toggle=$par.find(toggle);$toggle.attr("aria-expanded","false")}),$.fn.dropdown.Constructor.prototype.keydown=function(e){var $par;/(32)/.test(e.keyCode)&&($par=$(this).parent(),$(this).trigger("click"),e.preventDefault()&&e.stopPropagation())},$(document).on("focusout.dropdown.data-api",".dropdown-menu",function(){var $this=$(this),that=this;setTimeout(function(){$.contains(that,document.activeElement)||($this.parent().removeClass("open"),$this.parent().find("[data-toggle=dropdown]").attr("aria-expanded","false"))},150)}).on("keydown.bs.dropdown.data-api",toggle+", [role=menu]",$.fn.dropdown.Constructor.prototype.keydown);var $tablist=$(".nav-tabs"),$lis=$tablist.children("li"),$tabs=$tablist.find('[data-toggle="tab"], [data-toggle="pill"]');$tablist.attr("role","tablist"),$lis.attr("role","presentation"),$tabs.attr("role","tab"),$tabs.each(function(){var tabpanel=$($(this).attr("href")),tab=$(this),tabid=tab.attr("id")||uniqueId("ui-tab");tab.attr("id",tabid),tab.parent().hasClass("active")?(tab.attr({tabIndex:"0","aria-expanded":"true","aria-selected":"true","aria-controls":tab.attr("href").substr(1)}),tabpanel.attr({role:"tabpanel",tabIndex:"0","aria-hidden":"false","aria-labelledby":tabid})):(tab.attr({tabIndex:"-1","aria-expanded":"false","aria-selected":"false","aria-controls":tab.attr("href").substr(1)}),tabpanel.attr({role:"tabpanel",tabIndex:"-1","aria-hidden":"true","aria-labelledby":tabid}))}),$.fn.tab.Constructor.prototype.keydown=function(e){var $items,index,$this=$(this),$ul=$this.closest("ul[role=tablist] "),k=e.which||e.keyCode;if($this=$(this),/(37|38|39|40)/.test(k)){$items=$ul.find("[role=tab]:visible"),index=$items.index($items.filter(":focus")),(38==k||37==k)&&index--,(39==k||40==k)&&index++,0>index&&(index=$items.length-1),index==$items.length&&(index=0);var nextTab=$items.eq(index);"tab"===nextTab.attr("role")&&nextTab.tab("show").focus(),e.preventDefault(),e.stopPropagation()}},$(document).on("keydown.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',$.fn.tab.Constructor.prototype.keydown);var tabactivate=$.fn.tab.Constructor.prototype.activate;$.fn.tab.Constructor.prototype.activate=function(element,container){var $active=container.find("> .active");$active.find("[data-toggle=tab]").attr({tabIndex:"-1","aria-selected":!1,"aria-expanded":!1}),$active.filter(".tab-pane").attr({"aria-hidden":!0,tabIndex:"-1"}),tabactivate.apply(this,arguments),element.find("[data-toggle=tab]").attr({tabIndex:"0","aria-selected":!0,"aria-expanded":!0}),element.filter(".tab-pane").attr({"aria-hidden":!1,tabIndex:"0"})};var $colltabs=$('[data-toggle="collapse"]');$colltabs.attr({role:"tab","aria-selected":"false","aria-expanded":"false"}),$colltabs.each(function(){var colltab=$(this),collpanel=$(colltab.attr("href")),parent=colltab.attr("data-parent"),collparent=parent&&$(parent),collid=colltab.attr("id")||uniqueId("ui-collapse");$(collparent).find("div:not(.collapse,.panel-body), h4").attr("role","presentation"),colltab.attr("id",collid),collparent&&(collparent.attr({role:"tablist","aria-multiselectable":"true"}),collpanel.hasClass("in")?(colltab.attr({"aria-controls":colltab.attr("href").substr(1),"aria-selected":"true","aria-expanded":"true",tabindex:"0"}),collpanel.attr({role:"tabpanel",tabindex:"0","aria-labelledby":collid,"aria-hidden":"false"})):(colltab.attr({"aria-controls":colltab.attr("href").substr(1),tabindex:"-1"}),collpanel.attr({role:"tabpanel",tabindex:"-1","aria-labelledby":collid,"aria-hidden":"true"})))});var collToggle=$.fn.collapse.Constructor.prototype.toggle;$.fn.collapse.Constructor.prototype.toggle=function(){var href,prevTab=this.$parent&&this.$parent.find('[aria-expanded="true"]');if(prevTab){{var curTab,prevPanel=prevTab.attr("data-target")||(href=prevTab.attr("href"))&&href.replace(/.*(?=#[^\s]+$)/,""),$prevPanel=$(prevPanel),$curPanel=this.$element;this.$parent}this.$parent&&(curTab=this.$parent.find('[data-toggle=collapse][href="#'+this.$element.attr("id")+'"]')),collToggle.apply(this,arguments),$.support.transition&&this.$element.one($.support.transition.end,function(){prevTab.attr({"aria-selected":"false","aria-expanded":"false",tabIndex:"-1"}),$prevPanel.attr({"aria-hidden":"true",tabIndex:"-1"}),curTab.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:"0"}),$curPanel.hasClass("in")?$curPanel.attr({"aria-hidden":"false",tabIndex:"0"}):(curTab.attr({"aria-selected":"false","aria-expanded":"false"}),$curPanel.attr({"aria-hidden":"true",tabIndex:"-1"}))})}else collToggle.apply(this,arguments)},$.fn.collapse.Constructor.prototype.keydown=function(e){var $items,index,$this=$(this),$tablist=$this.closest("div[role=tablist] "),k=e.which||e.keyCode;$this=$(this),/(32|37|38|39|40)/.test(k)&&(32==k&&$this.click(),$items=$tablist.find("[role=tab]"),index=$items.index($items.filter(":focus")),(38==k||37==k)&&index--,(39==k||40==k)&&index++,0>index&&(index=$items.length-1),index==$items.length&&(index=0),$items.eq(index).focus(),e.preventDefault(),e.stopPropagation())},$(document).on("keydown.collapse.data-api",'[data-toggle="collapse"]',$.fn.collapse.Constructor.prototype.keydown),$(".carousel").each(function(){var $this=$(this),prev=$this.find('[data-slide="prev"]'),next=$this.find('[data-slide="next"]'),$options=$this.find(".item"),$listbox=$options.parent();$this.attr({"data-interval":"false","data-wrap":"false"}),$listbox.attr("role","listbox"),$options.attr("role","option");var spanPrev=document.createElement("span");spanPrev.setAttribute("class","sr-only"),spanPrev.innerHTML="Previous";var spanNext=document.createElement("span");spanNext.setAttribute("class","sr-only"),spanNext.innerHTML="Next",prev.attr("role","button"),next.attr("role","button"),prev.append(spanPrev),next.append(spanNext),$options.each(function(){var item=$(this);item.attr(item.hasClass("active")?{"aria-selected":"true",tabindex:"0"}:{"aria-selected":"false",tabindex:"-1"})})});var slideCarousel=$.fn.carousel.Constructor.prototype.slide;$.fn.carousel.Constructor.prototype.slide=function(type,next){var $active=this.$element.find(".item.active"),$next=next||$active[type]();slideCarousel.apply(this,arguments),$active.one($.support.transition.end,function(){$active.attr({"aria-selected":!1,tabIndex:"-1"}),$next.attr({"aria-selected":!0,tabIndex:"0"})})},$.fn.carousel.Constructor.prototype.keydown=function(e){var index,$this=$(this),$ul=$this.closest("div[role=listbox]"),$items=$ul.find("[role=option]"),$parent=$ul.parent(),k=e.which||e.keyCode;/(37|38|39|40)/.test(k)&&(index=$items.index($items.filter(".active")),(37==k||38==k)&&($parent.carousel("prev"),index--,0>index?index=$items.length-1:$this.prev().focus()),(39==k||40==k)&&($parent.carousel("next"),index++,index==$items.length?index=0:$this.one($.support.transition.end,function(){$this.next().focus()})),e.preventDefault(),e.stopPropagation())},$(document).on("keydown.carousel.data-api","div[role=option]",$.fn.carousel.Constructor.prototype.keydown);var removeMultiValAttributes=function(el,attr,val){var describedby=(el.attr(attr)||"").split(/\s+/),index=$.inArray(val,describedby);-1!==index&&describedby.splice(index,1),describedby=$.trim(describedby.join(" ")),describedby?el.attr(attr,describedby):el.removeAttr(attr)}}(jQuery); -------------------------------------------------------------------------------- /public/js/lib/calltable2CSV.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | var constructOrig = $.fn.tablesorter; 3 | $.fn.extend({ 4 | tablesorter: function(options) { 5 | var $headers =$($.find('th')); 6 | if ( $headers.length>0 ){ 7 | $headers[0].setAttribute('tabIndex','0'); 8 | $headers[0].setAttribute('aria-sort','ascending') 9 | 10 | $headers.keydown(function(e) { 11 | var k = e.which || e.keyCode 12 | , $this= $(this) 13 | 14 | if (!/(13|32|37|39)/.test(k)) return 15 | 16 | index = $headers.index(this) 17 | if (k == 37) index-- 18 | if (k == 39) index++ 19 | 20 | if(index < 0) index = $headers.length -1 21 | if(index == $headers.length) index = 0 22 | if(k == 37 || k === 39){ 23 | this.setAttribute('tabIndex','-1'); 24 | $headers[index].setAttribute('tabIndex','0'); 25 | $headers[index].focus(); 26 | } 27 | //console.log(this, $headers, index); 28 | if( k === 32 || k === 13) { 29 | $this.click() 30 | setTimeout(function(){ 31 | if($this.hasClass('headerSortUp')) $this.attr('aria-sort','descending') 32 | if($this.hasClass('headerSortDown')) $this.attr('aria-sort','ascending') 33 | } , 200) 34 | } 35 | }); 36 | } 37 | constructOrig.apply(this, arguments); 38 | } 39 | }) 40 | })(jQuery); 41 | 42 | 43 | $(function() { 44 | $("#test-results-table").tablesorter({sortList: [[0,0]], headers: {2:{sorter: false}, 3:{sorter: false}, 4:{sorter: false}}}); 45 | }); 46 | 47 | 48 | $("#export").on('click', function (event) { 49 | $('#test-results-table').table2CSV({delivery:'download'}, event); 50 | }); 51 | -------------------------------------------------------------------------------- /public/js/lib/table2CSV.js: -------------------------------------------------------------------------------- 1 | jQuery.fn.table2CSV = function(options ,e) { 2 | var options = jQuery.extend({ 3 | separator: ',', 4 | header: [], 5 | delivery: 'popup' // popup, value 6 | }, 7 | options); 8 | 9 | var csvData = []; 10 | var headerArr = []; 11 | var el = this; 12 | 13 | //header 14 | var numCols = options.header.length; 15 | var tmpRow = []; // construct header avalible array 16 | 17 | if (numCols > 0) { 18 | for (var i = 0; i < numCols; i++) { 19 | tmpRow[tmpRow.length] = formatData(options.header[i]); 20 | } 21 | } else { 22 | $(el).filter(':visible').find('th').each(function() { 23 | if ($(this).css('display') != 'none') tmpRow[tmpRow.length] = formatData($(this).html()); 24 | }); 25 | } 26 | 27 | row2CSV(tmpRow); 28 | 29 | // actual data 30 | $(el).find('tr').each(function() { 31 | var tmpRow = []; 32 | $(this).filter(':visible').find('td').each(function(index) { 33 | if ($(this).css('display') != 'none') { 34 | if(index==2) { 35 | var t = $(this).html() 36 | .replace(/\n/g,"") 37 | .replace(/&/g, '&') 38 | .replace(/</g, '<') 39 | .replace(/>/g, '>'); 40 | // .replace(/["]/g,"“") 41 | //.replace(/[\s]/g,"") 42 | tmpRow[tmpRow.length] =t; 43 | } 44 | else { 45 | tmpRow[tmpRow.length] = formatData($(this).html()); 46 | } 47 | } 48 | }); 49 | row2CSV(tmpRow); 50 | }); 51 | if (options.delivery == 'popup') { 52 | var mydata = csvData.join('\n'); 53 | return popup(mydata); 54 | } 55 | else if(options.delivery == 'download'){ 56 | var mydata = csvData.join('\n'); 57 | // window.open("data:text/csv;charset=utf-8," + escape(mydata)) 58 | download(mydata); 59 | }else { 60 | var mydata = csvData.join('\n'); 61 | return mydata; 62 | } 63 | 64 | function row2CSV(tmpRow) { 65 | var tmp = tmpRow.join('') // to remove any blank rows 66 | // alert(tmp); 67 | if (tmpRow.length > 0 && tmp != '') { 68 | var mystr = tmpRow.join(options.separator); 69 | csvData[csvData.length] = mystr; 70 | } 71 | } 72 | function formatData(input) { 73 | // replace " with “ 74 | var regexp = new RegExp(/["]/g); 75 | var output = input.replace(regexp, "“"); 76 | //HTML 77 | var regexp = new RegExp(/\<[^\<]+\>/g); 78 | var output = output.replace(regexp, ""); 79 | if (output == "") return ''; 80 | return '"' + output + '"'; 81 | } 82 | function popup(data) { 83 | var generator = window.open('', 'csv', 'height=400,width=600'); 84 | generator.document.write('CSV'); 85 | generator.document.write(''); 86 | generator.document.write(''); 89 | generator.document.write(''); 90 | generator.document.close(); 91 | return true; 92 | } 93 | function download(data){ 94 | // e.target.download = 'hello.csv'; 95 | e.target.download = new Date().getTime() + '.csv'; 96 | 97 | // $(e.target).attr( 'href' ,"data:text/csv;charset=utf-8," + data.replace(/&/g,'&').replace(//g,'>') ); 98 | 99 | data = escape(data) 100 | .replace(/%u2018/g,'"') 101 | .replace(/%u2019/g,'"') 102 | .replace(/%u201C/g,'"') 103 | .replace(/%u201D/g,'"') 104 | 105 | $(e.target).attr('href' ,"data:text/csv;charset=utf-8," + data); 106 | // console.log(e.target); 107 | } 108 | }; -------------------------------------------------------------------------------- /public/js/validation.js: -------------------------------------------------------------------------------- 1 | //Added to check to append http:// 2 | 3 | 4 | $('#saveSubmit').click(function() { 5 | if(!checkUrl($("#stageName").val())) { 6 | if($("#stageName").val().indexOf("http") == -1){ 7 | $("#stageName").attr('value',"http://" + $("#stageName").val()); 8 | } 9 | return true; 10 | } 11 | return true; 12 | }); 13 | 14 | 15 | $('#requestURL').submit(function() { 16 | if(!checkUrl($("#textURL").val())) { 17 | if($("#textURL").val().indexOf("http") == -1){ 18 | $("#textURL").attr('value',"http://" + $("#textURL").val()); 19 | } 20 | return true; 21 | } 22 | return true; 23 | }); 24 | function checkUrl(url){ 25 | var urlregex = new RegExp("^(http:\/\/www.|https:\/\/www.|ftp:\/\/www.|www.){1}([0-9A-Za-z]+\.)"); 26 | return urlregex.test(url); 27 | } 28 | 29 | if(typeof($("#user") !== 'undefined')){ 30 | if($("#user").val() === ""){ 31 | $("#loginButton").addClass("showLoginButton"); 32 | $("#logoutButton").addClass("hideLogoutButton"); 33 | } 34 | 35 | if($("#user").val() !== ""){ 36 | $("#loginButton").addClass("hideLoginButton"); 37 | $("#logoutButton").addClass("showLogoutButton"); 38 | 39 | } 40 | 41 | } -------------------------------------------------------------------------------- /screenshots/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/screenshots/.keep -------------------------------------------------------------------------------- /src/HTMLCS_Run.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(); 2 | var fs = require('fs'); 3 | var args = require('system').args; 4 | 5 | var url = args[1]; 6 | var standard = args[2]; 7 | var errLevel = args[3]; 8 | var output = args[4]; 9 | var screenshot_url =''; 10 | 11 | phantom.silent = true; 12 | 13 | // console.log(url, standard, errLevel, output, screenshot_url) 14 | // phantom.exit(); 15 | 16 | page.open(url, function (status) { 17 | if (status !== 'success') { 18 | console.log('Unable to load the address!'); 19 | phantom.exit(); 20 | } else { 21 | window.setTimeout(function () { 22 | // Override onConsoleMessage function for outputting. 23 | page.onConsoleMessage = function (msg) { 24 | if (msg === 'done') { 25 | setTimeout(function(){ 26 | phantom.exit(); 27 | }, 0); 28 | } 29 | console.log(msg); 30 | }; 31 | 32 | page.onError = function (msg) { 33 | console.log(msg); 34 | phantom.exit(); 35 | }; 36 | 37 | page.injectJs('./src/htmlcs/HTMLCS.js'); 38 | 39 | // console.log('O U T P U T ' , output); 40 | // console.log('errLevel' , errLevel); 41 | 42 | // page.injectJs('runner_json.js'); 43 | if(output==='json'){ 44 | page.injectJs('runner_json.js'); 45 | } else{ 46 | page.injectJs('runner_html.js'); 47 | } 48 | 49 | var data = { 50 | standard : standard, 51 | screenshot_url : screenshot_url, 52 | errLevel: errLevel 53 | }; 54 | 55 | switch (standard) { 56 | case 'WCAG2A': 57 | case 'WCAG2AA': 58 | case 'WCAG2AAA': 59 | case 'Section508': 60 | page.evaluate(function(data) { 61 | var screenshot_url = data.screenshot_url; 62 | var standard = data.standard; 63 | var errLevel = data.errLevel; 64 | 65 | HTMLCS_RUNNER.run(standard, screenshot_url, errLevel); 66 | 67 | }, data); 68 | break; 69 | default: 70 | console.log('Unknown standard.'); 71 | // phantom.exit(); 72 | setTimeout(function(){ 73 | phantom.exit(); 74 | }, 0); 75 | break; 76 | } //switch 77 | }, 200); //E N D window.setTimeout 78 | }//end if else 79 | });//end -------------------------------------------------------------------------------- /src/PAET.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(), 2 | system = require('system'), 3 | fs = require('fs'), 4 | content='', 5 | address = system.args[1], 6 | standard = system.args[2], 7 | userName = system.args[3], 8 | scanDir = system.args[4], 9 | scrshot = system.args[5], 10 | eLevel = system.args[6], 11 | screenshot_url ; 12 | 13 | if(userName !== "") { 14 | var cookieFile = 'cookies/' + userName + ".txt"; 15 | } 16 | phantom.silent = false; 17 | page.onConsoleMessage = function (msg) { 18 | console.log(msg); 19 | }; 20 | page.onError = function (msg) { 21 | console.log(msg); 22 | phantom.exit(); 23 | }; 24 | 25 | /***************** H E L P E R F U N C T I O N S *******************/ 26 | function restoreCookies(cookieFile) { 27 | var fs = require("fs"); 28 | if (fs.exists(cookieFile)) { 29 | var cookies = fs.read(cookieFile); 30 | cookies = JSON.parse(cookies); 31 | //console.log("Restored cookies: " + JSON.stringify(cookies)); 32 | if (cookies.length > 0) { 33 | for(var i=0; i'; 66 | content += ''; 67 | content += '' + principles[principle] + ''; 68 | content += ''; 69 | 70 | // content += '' + priority + ''; 71 | content += ' ' + msg.msg + ''; 72 | content += '' + msg.element + ''; 73 | content += '
    '; 74 | for (var j = 0; j < techniques.length; j++) { 75 | content += '
  • ' + techniques[j] + '
  • '; 76 | } 77 | content += '
'; 78 | 79 | var rect = eval("(" + msg.rect + ')'); 80 | 81 | if(rect.width!== 0 && rect.height!== 0){ 82 | 83 | if(scrshot =='false'){ 84 | content += ' '; 85 | }else { 86 | var screenshot =Math.floor((Math.random()*1000)+1) +'.png'; 87 | screenshot_url= scanDir + '/' + screenshot ; 88 | // page.clipRect = rect; 89 | page.clipRect = { left: rect.left, top: rect.top, width: rect.width + 100 , height: rect.height + 100 }; 90 | page.render(screenshot_url); 91 | content += ''; 92 | } 93 | }else{ 94 | content += '  No scr'; 95 | } 96 | 97 | content += ''; 98 | } 99 | 100 | function outputToHtml(msgs){ 101 | var heading ='' 102 | , msg 103 | , errors = 0 104 | , warnings = 0 105 | , notices = 0 106 | , count=1 107 | 108 | if (msgs.length === 0) { 109 | content += 'No violations found'; 110 | content += ''; 111 | return; 112 | } 113 | 114 | content += ''; 115 | content += ''; 116 | 117 | // console.log('Debug eLevel -> ', eLevel); 118 | 119 | for (var key in msgs) { 120 | msg = msgs[key]; 121 | 122 | if(eLevel.indexOf(msg.type) !=-1 ) { 123 | if(msg.type==1) errors++; 124 | if(msg.type==2) warnings++; 125 | if(msg.type==3) notices++; 126 | 127 | formRows(msg); 128 | } //if loop 129 | } //for loop 130 | 131 | heading += '

' + msgs['title'] +'

'; 132 | 133 | if(address.indexOf("file:") === -1){ 134 | heading += '

' + msgs['address'] + '

'; 135 | } 136 | count = errors + warnings + notices; 137 | heading += '
  • '; 138 | heading += '' + errors + 'errors
  • '; 139 | heading += '' + warnings + 'warnings'; 140 | heading += '' + notices + 'notices'; 141 | 142 | heading += '
'; 143 | 144 | if(address.indexOf("file:") === -1){ 145 | heading +='Export as CSV'; 146 | } 147 | if(errors === 0) { 148 | content += ''; 149 | } 150 | 151 | content += '
Error LevelPrincipleMessageCode snippetTechniquesScreenshot
---- No Errors ---
'; 152 | 153 | content ='
' + heading + content +'
'; 154 | 155 | return content; 156 | } 157 | 158 | /***************** E N D H E L P E R F U N C T I O N S *******************/ 159 | 160 | 161 | 162 | if(userName != "") { 163 | restoreCookies(cookieFile); 164 | } 165 | 166 | page.open(address, function (status) { 167 | // F O R T E S T I N G 168 | // page.open('about:blank', function (status) { 169 | // page.content = ' PLAIN TEXT'; 170 | 171 | if (status !== 'success') { 172 | console.log('Unable to load the address!'); 173 | phantom.exit(); 174 | } else { 175 | window.setTimeout(function () { 176 | page.onConsoleMessage = function (msg) { 177 | if (msg === 'done') phantom.exit(); 178 | console.log(msg); 179 | }; 180 | 181 | var fs = require('fs'); 182 | page.injectJs('./src/htmlcs/HTMLCS.js'); 183 | 184 | var data = { 185 | standard : standard 186 | }; 187 | 188 | switch (standard) { 189 | case 'WCAG2A': 190 | case 'WCAG2AA': 191 | case 'WCAG2AAA': 192 | var results = page.evaluate(function(data) { 193 | var standard = data.standard 194 | , messages, msgJson ={} 195 | , splitLine = function (s, n) { //Break the code snippet into multiple lines 196 | var b = ''; 197 | while (s.length > n) { 198 | var c = s.substring(0,n); 199 | var d = c.lastIndexOf(' '); 200 | var e =c.lastIndexOf('\n'); 201 | if (e != -1) d = e; 202 | if (d == -1) d = n; 203 | b += c.substring(0,d) + '\n'; 204 | s = s.substring(d+1); 205 | } 206 | return b+s; 207 | } 208 | 209 | HTMLCS.process(standard, document, function() { 210 | messages = HTMLCS.getMessages(); 211 | for (var i = 0 ,l = messages.length ; i < l ; i++) { 212 | var msg = messages[i]; 213 | 214 | if (msg.element.innerHTML && msg.element.innerHTML.length > 50) { 215 | var outerHTML = msg.element.innerHTML.replace(msg.element.innerHTML, msg.element.innerHTML.substr(0, 50) + '...'); 216 | } else { 217 | var outerHTML = msg.element.outerHTML; 218 | } 219 | 220 | msgJson[i] = { 221 | "type" : msg.type 222 | , "element" : outerHTML 223 | , "msg" : msg.msg 224 | , "code" : msg.code 225 | // , "rect" : JSON.stringify( msg.element.getBoundingClientRect() ) 226 | } 227 | } //End for loop 228 | 229 | msgJson['title'] = document.title; 230 | msgJson['address'] = document.location.href 231 | 232 | }) //End HTMLCS.process 233 | 234 | //console.log(msgJson); 235 | return msgJson; 236 | 237 | }, data); //End evaluate 238 | 239 | break; 240 | default: 241 | console.log('Unknown standard.'); 242 | phantom.exit(); 243 | break; 244 | } 245 | 246 | // console.log(JSON.stringify(results)); 247 | var htmlStr = outputToHtml(results); 248 | console.log(htmlStr); 249 | phantom.exit(); 250 | }, 200); 251 | }//end if 252 | });//end 253 | -------------------------------------------------------------------------------- /src/axe.js: -------------------------------------------------------------------------------- 1 | var PATH_TO_AXE = './src/axe/axe.min.js'; 2 | var args = require('system').args; 3 | var page = require('webpage').create(); 4 | page.content = args[2]; 5 | 6 | var contentType= args[1]; 7 | var url=''; 8 | var jsonOp = []; 9 | 10 | var output = args[3]; 11 | 12 | phantom.silent = true; 13 | page.settings.webSecurityEnabled = false; 14 | 15 | page.injectJs(PATH_TO_AXE); //source from https://github.com/dequelabs/axe-core/blob/master/doc/examples/phantomjs/axe-phantom.js 16 | page.framesName.forEach(function (name) { 17 | page.switchToFrame(name); 18 | page.injectJs(PATH_TO_AXE); 19 | }); 20 | page.switchToMainFrame(); 21 | page.evaluateAsync(function () { 22 | axe.run(document.body, function(err, results) { 23 | window.callPhantom(results); 24 | }); 25 | }); 26 | 27 | page.onCallback = function (msg) { 28 | var violations = msg.violations 29 | for (var i=violations.length;i--;){ 30 | delete violations[i].helpUrl; 31 | delete violations[i].tags; 32 | delete violations[i].nodes; 33 | } 34 | // console.log(JSON.stringify(msg, null, ' ')); 35 | if(output==='string'){ 36 | var htmlStr = buildHtmlTable( violations ,'Axe Accessibility Plugin' ); 37 | }else { 38 | var htmlStr = buildJsonObj( violations ,'Axe Accessibility Plugin' ); 39 | } 40 | 41 | console.log(htmlStr); 42 | phantom.exit(); 43 | }; 44 | 45 | 46 | 47 | /***************** H E L P E R F U N C T I O N S *******************/ 48 | function buildHtmlTable(arr) { 49 | var heading ='' 50 | , msg 51 | , content = 'Axe Results' 52 | 53 | if (arr.length === 0) { 54 | content += 'No violations found'; 55 | content += ''; 56 | return; 57 | } 58 | content += ''; 59 | content += ''; 60 | for (var key in arr) { 61 | msg = arr[key]; 62 | content += ''; 63 | content += ''; 64 | content += ''; 65 | content += ''; 66 | content += ''; 67 | content += ''; 68 | } 69 | content += '
IdDescriptionHelpImpact
' + msg.id + '' + msg.description + '' + msg.help + '' + msg.impact + '
'; 70 | content += ''; 71 | return content; 72 | } 73 | 74 | function buildJsonObj(arr){ 75 | if (arr.length === 0) { 76 | jsonOp.push({'message':'No violations found'}); 77 | return; 78 | } 79 | for (var key in arr) { 80 | msg = arr[key]; 81 | var temp_obj = {}; 82 | temp_obj["id"] = msg.id; 83 | temp_obj["description"] = msg.description; 84 | temp_obj["help"] = msg.help; 85 | temp_obj["impact"] = msg.impact; 86 | jsonOp.push(temp_obj); 87 | } 88 | return JSON.stringify(jsonOp); 89 | } 90 | /***************** E N D H E L P E R F U N C T I O N S *******************/ -------------------------------------------------------------------------------- /src/axe_url.js: -------------------------------------------------------------------------------- 1 | var PATH_TO_AXE = './src/axe/axe.min.js'; 2 | var args = require('system').args; 3 | var page = require('webpage').create(); 4 | var url= args[1]; 5 | var jsonOp = []; 6 | var output = args[2]; 7 | 8 | phantom.silent = true; 9 | page.settings.webSecurityEnabled = false; 10 | page.open(url, function (status) { 11 | if (status !== 'success') { 12 | console.log('Unable to access network'); 13 | return; 14 | } 15 | page.injectJs(PATH_TO_AXE); //source from https://github.com/dequelabs/axe-core/blob/master/doc/examples/phantomjs/axe-phantom.js 16 | page.framesName.forEach(function (name) { 17 | page.switchToFrame(name); 18 | page.injectJs(PATH_TO_AXE); 19 | }); 20 | page.switchToMainFrame(); 21 | page.evaluateAsync(function () { 22 | axe.run(document.body, function(err, results) { 23 | window.callPhantom(results); 24 | }); 25 | }); 26 | }); 27 | 28 | page.onCallback = function (msg) { 29 | var violations = msg.violations 30 | for (var i=violations.length;i--;){ 31 | delete violations[i].helpUrl; 32 | delete violations[i].tags; 33 | delete violations[i].nodes; 34 | } 35 | // console.log(JSON.stringify(msg, null, ' ')); 36 | if(output==='string'){ 37 | var htmlStr = buildHtmlTable( violations ,'Axe Accessibility Plugin' ); 38 | }else { 39 | var htmlStr = buildJsonObj( violations ,'Axe Accessibility Plugin' ); 40 | } 41 | 42 | console.log(htmlStr); 43 | phantom.exit(); 44 | }; 45 | 46 | 47 | 48 | /***************** H E L P E R F U N C T I O N S *******************/ 49 | function buildHtmlTable(arr) { 50 | var heading ='' 51 | , msg 52 | , content = 'Axe Results' 53 | 54 | if (arr.length === 0) { 55 | content += 'No violations found'; 56 | content += ''; 57 | return; 58 | } 59 | content += ''; 60 | content += ''; 61 | for (var key in arr) { 62 | msg = arr[key]; 63 | content += ''; 64 | content += ''; 65 | content += ''; 66 | content += ''; 67 | content += ''; 68 | content += ''; 69 | } 70 | content += '
IdDescriptionHelpImpact
' + msg.id + '' + msg.description + '' + msg.help + '' + msg.impact + '
'; 71 | content += ''; 72 | return content; 73 | } 74 | 75 | function buildJsonObj(arr){ 76 | if (arr.length === 0) { 77 | jsonOp.push({'message':'No violations found'}); 78 | return; 79 | } 80 | for (var key in arr) { 81 | msg = arr[key]; 82 | var temp_obj = {}; 83 | temp_obj["id"] = msg.id; 84 | temp_obj["description"] = msg.description; 85 | temp_obj["help"] = msg.help; 86 | temp_obj["impact"] = msg.impact; 87 | jsonOp.push(temp_obj); 88 | } 89 | return JSON.stringify(jsonOp); 90 | } 91 | /***************** E N D H E L P E R F U N C T I O N S *******************/ -------------------------------------------------------------------------------- /src/chrome.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(); 2 | var args = require('system').args; 3 | var PATH_TO_CHROME = './src/chrome/axs_testing.js'; 4 | 5 | var contentType= args[1]; 6 | var url=''; 7 | var jsonOp = []; 8 | 9 | page.content = args[2]; 10 | var output = args[3]; 11 | 12 | phantom.silent = true; 13 | page.settings.webSecurityEnabled = false; 14 | page.injectJs(PATH_TO_CHROME); 15 | 16 | // console.log('O U T P U T ' , output); 17 | 18 | var screenshot_url= 'screenshots/' + Math.floor((Math.random()*1000)+1) +'.png'; 19 | // page.render(screenshot_url); 20 | 21 | var data = { 22 | screenshot_url : screenshot_url 23 | }; 24 | 25 | var evalData = page.evaluate(function(data) { 26 | var screenshot_url = data.screenshot_url; 27 | 28 | var configuration = new axs.AuditConfiguration(); 29 | configuration.showUnsupportedRulesWarning = false; 30 | configuration.scope = document.body; 31 | var results = axs.Audit.run(configuration); 32 | 33 | // console.log(JSON.stringify(document.body)); 34 | 35 | var audit = results.map(function(result) { 36 | var DOMElements = result.elements; 37 | var message = ''; 38 | if(result.result ==='FAIL'){ 39 | if (DOMElements !== undefined) { 40 | var maxElements = Math.min(DOMElements.length, 5); 41 | 42 | for (var i = 0; i < maxElements; i++) { 43 | var el = DOMElements[i]; 44 | message += '\n'; 45 | try { 46 | message += axs.utils.getQuerySelectorText(el); 47 | } catch (err) { 48 | message += ' tagName:' + el.tagName; 49 | message += ' id:' + el.id; 50 | } 51 | } 52 | } 53 | return { 54 | heading: result.rule.heading, 55 | result: result.result, 56 | severity: result.rule.severity, 57 | elements: message 58 | }; 59 | } //Return Failures only 60 | }); 61 | 62 | // var report = axs.Audit.createReport(results); 63 | for (var i=audit.length;i--;){ 64 | if (audit[i] == null) audit.splice(i,1); 65 | } 66 | return audit; 67 | }, data); 68 | if(output==='string'){ 69 | var htmlStr = buildHtmlTable(evalData ,'Chrome Accessibility Plugin'); 70 | }else { 71 | var htmlStr = buildJsonObj(evalData ,'Chrome Accessibility Plugin' ); 72 | } 73 | console.log(htmlStr); 74 | phantom.exit(); 75 | 76 | 77 | /***************** H E L P E R F U N C T I O N S *******************/ 78 | // Builds the HTML Table out of myList json data from Ivy restful service. 79 | function buildHtmlTable(arr) { 80 | var heading ='' 81 | , msg 82 | , content = 'Google Chrome a11y tool Results' 83 | 84 | if (arr.length === 0) { 85 | content += 'No violations found'; 86 | content += ''; 87 | return; 88 | } 89 | content += ''; 90 | content += ''; 91 | for (var key in arr) { 92 | msg = arr[key]; 93 | content += ''; 94 | content += ''; 95 | content += ''; 96 | content += ''; 97 | content += ''; 98 | } 99 | content += '
HeadingResultseverityElements
' + msg.heading + '' + msg.result + '' + msg.severity + '' + msg.elements + '
'; 100 | content += ''; 101 | return content; 102 | } 103 | function buildJsonObj(arr){ 104 | if (arr.length === 0) { 105 | jsonOp.push({'message':'No violations found'}); 106 | return; 107 | } 108 | for (var key in arr) { 109 | msg = arr[key]; 110 | var temp_obj = {}; 111 | temp_obj["heading"] = msg.heading; 112 | temp_obj["result"] = msg.result; 113 | temp_obj["severity"] = msg.severity; 114 | temp_obj["elements"] = msg.elements; 115 | jsonOp.push(temp_obj); 116 | } 117 | return JSON.stringify(jsonOp); 118 | } 119 | /***************** E N D H E L P E R F U N C T I O N S *******************/ 120 | -------------------------------------------------------------------------------- /src/chrome_url.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(); 2 | var args = require('system').args; 3 | var PATH_TO_CHROME = './src/chrome/axs_testing.js'; 4 | var url= args[1]; 5 | var jsonOp = []; 6 | var output = args[2]; 7 | 8 | phantom.silent = false; 9 | page.settings.webSecurityEnabled = false; 10 | 11 | page.open(url, function (status) { 12 | if (status !== 'success') { 13 | console.log('Unable to access network'); 14 | return; 15 | } 16 | page.injectJs(PATH_TO_CHROME); 17 | 18 | // console.log('O U T P U T ' , output); 19 | 20 | var screenshot_url= 'screenshots/' + Math.floor((Math.random()*1000)+1) +'.png'; 21 | // page.render(screenshot_url); 22 | 23 | var data = { 24 | screenshot_url : screenshot_url 25 | }; 26 | 27 | var evalData = page.evaluate(function(data) { 28 | var screenshot_url = data.screenshot_url; 29 | 30 | var configuration = new axs.AuditConfiguration(); 31 | configuration.showUnsupportedRulesWarning = false; 32 | configuration.scope = document.body; 33 | var results = axs.Audit.run(configuration); 34 | 35 | // console.log(JSON.stringify(document.body)); 36 | 37 | var audit = results.map(function(result) { 38 | var DOMElements = result.elements; 39 | var message = ''; 40 | if(result.result ==='FAIL'){ 41 | if (DOMElements !== undefined) { 42 | var maxElements = Math.min(DOMElements.length, 5); 43 | 44 | for (var i = 0; i < maxElements; i++) { 45 | var el = DOMElements[i]; 46 | message += '\n'; 47 | try { 48 | message += axs.utils.getQuerySelectorText(el); 49 | } catch (err) { 50 | message += ' tagName:' + el.tagName; 51 | message += ' id:' + el.id; 52 | } 53 | } 54 | } 55 | return { 56 | heading: result.rule.heading, 57 | result: result.result, 58 | severity: result.rule.severity, 59 | elements: message 60 | }; 61 | } //Return Failures only 62 | }); 63 | 64 | // var report = axs.Audit.createReport(results); 65 | for (var i=audit.length;i--;){ 66 | if (audit[i] == null) audit.splice(i,1); 67 | } 68 | return audit; 69 | }, data); 70 | 71 | if(output==='string'){ 72 | var htmlStr = buildHtmlTable(evalData ,'Chrome Accessibility Plugin'); 73 | }else { 74 | var htmlStr = buildJsonObj(evalData ,'Chrome Accessibility Plugin' ); 75 | } 76 | console.log(htmlStr); 77 | phantom.exit(); 78 | }) 79 | 80 | /***************** H E L P E R F U N C T I O N S *******************/ 81 | // Builds the HTML Table out of myList json data from Ivy restful service. 82 | function buildHtmlTable(arr) { 83 | var heading ='' 84 | , msg 85 | , content = 'Google Chrome a11y tool Results' 86 | 87 | if (arr.length === 0) { 88 | content += 'No violations found'; 89 | content += ''; 90 | return; 91 | } 92 | content += ''; 93 | content += ''; 94 | for (var key in arr) { 95 | msg = arr[key]; 96 | content += ''; 97 | content += ''; 98 | content += ''; 99 | content += ''; 100 | content += ''; 101 | } 102 | content += '
HeadingResultseverityElements
' + msg.heading + '' + msg.result + '' + msg.severity + '' + msg.elements + '
'; 103 | content += ''; 104 | return content; 105 | } 106 | function buildJsonObj(arr){ 107 | if (arr.length === 0) { 108 | jsonOp.push({'message':'No violations found'}); 109 | return; 110 | } 111 | for (var key in arr) { 112 | msg = arr[key]; 113 | var temp_obj = {}; 114 | temp_obj["heading"] = msg.heading; 115 | temp_obj["result"] = msg.result; 116 | temp_obj["severity"] = msg.severity; 117 | temp_obj["elements"] = msg.elements; 118 | jsonOp.push(temp_obj); 119 | } 120 | return JSON.stringify(jsonOp); 121 | } 122 | /***************** E N D H E L P E R F U N C T I O N S *******************/ 123 | -------------------------------------------------------------------------------- /src/home.js: -------------------------------------------------------------------------------- 1 | function runHTMLCS(standard, source, resultsDiv, callback) { 2 | if (/resultsWrapperActive/.test(resultsDiv) === false) { 3 | resultsDiv.className += ' resultsWrapperActive'; 4 | } 5 | 6 | resultsDiv.innerHTML = 'Loading Sniffing...'; 7 | 8 | HTMLCS.process(standard, source, function() { 9 | if (standard === 'Section508') { 10 | updateResults508(resultsDiv); 11 | } else { 12 | updateResults(resultsDiv); 13 | } 14 | 15 | if (callback instanceof Function === true) { 16 | callback.call(); 17 | } 18 | }); 19 | } 20 | 21 | function updateResults(resultsWrapper) { 22 | resultsWrapper.innerHTML = ''; 23 | 24 | var principles = { 25 | 'Principle1': 'Perceivable', 26 | 'Principle2': 'Operable', 27 | 'Principle3': 'Understandable', 28 | 'Principle4': 'Robust' 29 | }; 30 | 31 | var msgs = HTMLCS.getMessages(); 32 | if (msgs.length === 0) { 33 | resultsWrapper.innerHTML = 'No violations found'; 34 | return; 35 | } 36 | 37 | var content = ''; 38 | content += ''; 39 | 40 | var errors = 0; 41 | var warnings = 0; 42 | var notices = 0; 43 | //console.log("Messages" + msgs); 44 | for (var i = 0; i < msgs.length; i++) { 45 | var msg = msgs[i]; 46 | var type = ''; 47 | switch (msg.type) { 48 | case HTMLCS.ERROR: 49 | type = 'Error'; 50 | errors++; 51 | break; 52 | 53 | case HTMLCS.WARNING: 54 | type = 'Warning'; 55 | warnings++; 56 | break; 57 | 58 | case HTMLCS.NOTICE: 59 | type = 'Notice'; 60 | notices++; 61 | break; 62 | 63 | default: 64 | type = 'Unknown'; 65 | break; 66 | } 67 | 68 | // Get the success criterion so we can provide a link. 69 | var msgParts = msg.code.split('.'); 70 | var principle = msgParts[1]; 71 | var sc = msgParts[3].split('_').slice(0, 3).join('_'); 72 | var techniques = msgParts[4]; 73 | techniques = techniques.split(','); 74 | 75 | // Build a message code without the standard name. 76 | msgParts.shift(); 77 | msgParts.unshift('[Standard]'); 78 | var noStdMsgParts = msgParts.join('.'); 79 | 80 | content += ''; 81 | content += ''; 82 | content += ''; 83 | content += ''; 86 | content += ''; 89 | content += ''; 94 | 95 | content += ''; 98 | 99 | content += ''; 100 | } 101 | 102 | 103 | var heading = '

Test results

'; 104 | 105 | var noticeActive = ''; 106 | var testResultsClass = 'hide-notice'; 107 | if ((errors === 0) && (warnings === 0)) { 108 | noticeActive = ' class="active"'; 109 | testResultsClass = ''; 110 | } 111 | 112 | heading += ''; 117 | heading += '
'; 118 | 119 | content = heading + content; 120 | content += '
#MessagePrincipleSCTechniquesCode
' + type + ': ' + msg.msg + ''; 84 | content += '' + principles[principle] + ''; 85 | content += ''; 87 | content += '' + sc.replace(new RegExp('_', 'g'), '.') + ''; 88 | content += '
    '; 90 | for (var j = 0; j < techniques.length; j++) { 91 | content += '
  • ' + techniques[j] + '
  • '; 92 | } 93 | content += '
'; 96 | content += msg.element.outerHTML.replace(/&/g,'&').replace(//g,'>'); 97 | content += '
'; 121 | content += '
No messages matched the types you selected
'; 122 | content += 'Add the Accessibility Auditor bookmarklet to your browser to run this test on any web page.'; 123 | resultsWrapper.innerHTML = content; 124 | 125 | reorderResults(); 126 | } 127 | 128 | function updateResults508(resultsWrapper) { 129 | 130 | console.log("resultsWrapper : " + resultsWrapper); 131 | resultsWrapper.innerHTML = ''; 132 | 133 | var msgs = HTMLCS.getMessages(); 134 | console.info(msgs); 135 | if (msgs.length === 0) { 136 | resultsWrapper.innerHTML = 'No violations found'; 137 | return; 138 | } 139 | 140 | var content = ''; 141 | content += ''; 142 | 143 | var errors = 0; 144 | var warnings = 0; 145 | var notices = 0; 146 | 147 | for (var i = 0; i < msgs.length; i++) { 148 | var msg = msgs[i]; 149 | var type = ''; 150 | switch (msg.type) { 151 | case HTMLCS.ERROR: 152 | type = 'Error'; 153 | errors++; 154 | break; 155 | 156 | case HTMLCS.WARNING: 157 | type = 'Warning'; 158 | warnings++; 159 | break; 160 | 161 | case HTMLCS.NOTICE: 162 | type = 'Notice'; 163 | notices++; 164 | break; 165 | 166 | default: 167 | type = 'Unknown'; 168 | break; 169 | } 170 | 171 | // Get the success criterion so we can provide a link. 172 | var msgParts = msg.code.split('.'); 173 | var section = msgParts[1]; 174 | 175 | // Build a message code without the standard name. 176 | msgParts.shift(); 177 | msgParts.unshift('[Standard]'); 178 | var noStdMsgParts = msgParts.join('.'); 179 | 180 | content += ''; 181 | content += ''; 182 | content += ''; 183 | content += ''; 186 | content += ''; 187 | } 188 | 189 | 190 | var heading = '

Test results

'; 191 | 192 | var noticeActive = ''; 193 | var testResultsClass = 'hide-notice'; 194 | if ((errors === 0) && (warnings === 0)) { 195 | noticeActive = ' class="active"'; 196 | testResultsClass = ''; 197 | } 198 | 199 | heading += ''; 204 | heading += '
'; 205 | 206 | content = heading + content; 207 | content += '
#MessageRule
' + type + ': ' + msg.msg + ''; 184 | content += '1194.22 (' + section.toLowerCase() + ')'; 185 | content += '
'; 208 | content += '
No messages matched the types you selected
'; 209 | content += 'Add the Accessibility Auditor bookmarklet to your browser to run this test on any web page.'; 210 | resultsWrapper.innerHTML = content; 211 | 212 | reorderResults(); 213 | } 214 | 215 | function runHTMLCSTest() { 216 | var source = document.getElementById('source').value; 217 | if (source !== '') { 218 | var level = ''; 219 | for (var i = 0; i < document.getElementById('runHTMLCS').level.length; i++) { 220 | var option = document.getElementById('runHTMLCS').level[i]; 221 | if (option.checked === true) { 222 | level = option.value; 223 | break; 224 | } 225 | } 226 | 227 | runHTMLCS(level, source, document.getElementById('resultsWrapper'), function() { 228 | scrollToElement(document.getElementById('test-area')); 229 | }); 230 | 231 | //var runBtn = document.getElementById('run-button'); 232 | // runBtn.className = 'test-options-disabled'; 233 | } 234 | } 235 | 236 | function activateHTMLCS() { 237 | var runBtn = document.getElementById('run-button'); 238 | runBtn.className = 'test-options-active'; 239 | } 240 | 241 | function hideDiv() { 242 | document.getElementById('source').focus(); 243 | var overlayDiv = document.getElementById('code-overlay'); 244 | 245 | if (overlayDiv.style.opacity !== undefined) { 246 | overlayDiv.style.opacity = 0; 247 | setTimeout(function() { 248 | overlayDiv.style.visibility = "hidden"; 249 | }, 400); 250 | } else { 251 | overlayDiv.style.visibility = "hidden"; 252 | } 253 | } 254 | 255 | function scrollToElement(element) { 256 | var currScrollY = null; 257 | 258 | var targetScrollY = 0; 259 | var op = element; 260 | while (op.offsetParent !== null) { 261 | targetScrollY += op.offsetTop; 262 | op = op.offsetParent; 263 | } 264 | 265 | if (window.pageYOffset !== undefined) { 266 | currScrollY = window.pageYOffset; 267 | } else if (document.documentElement.scrollTop !== undefined) { 268 | currScrollY = document.documentElement.scrollTop; 269 | } else if (document.body.scrollTop !== undefined) { 270 | currScrollY = document.body.scrollTop; 271 | } 272 | 273 | 274 | if (currScrollY !== targetScrollY) { 275 | var maxTick = 1; 276 | var interval = setInterval(function() { 277 | var sign = 1; 278 | if (currScrollY > targetScrollY) { 279 | sign = -1; 280 | } 281 | var scrollBy = sign * Math.ceil(Math.max(1, Math.min(maxTick, (Math.abs(targetScrollY - currScrollY) * 0.25)))); 282 | currScrollY += scrollBy; 283 | window.scrollBy(0, scrollBy); 284 | 285 | if (currScrollY === targetScrollY) { 286 | clearInterval(interval); 287 | } else { 288 | maxTick = Math.min(maxTick + 0.5, Math.abs(targetScrollY - currScrollY)); 289 | } 290 | }, 20); 291 | }//end if 292 | } 293 | 294 | function toggleMsgTypes(type) { 295 | if (this.parentNode.className === 'active') { 296 | this.parentNode.className = ''; 297 | } else { 298 | this.parentNode.className = 'active'; 299 | } 300 | 301 | var testResultsDiv = document.getElementById('test-results'); 302 | var className = 'hide-' + type; 303 | 304 | if (new RegExp(className).test(testResultsDiv.className) === true) { 305 | testResultsDiv.className = testResultsDiv.className.replace(className, ''); 306 | } else { 307 | testResultsDiv.className += ' ' + className; 308 | } 309 | 310 | reorderResults(); 311 | return false; 312 | } 313 | 314 | function reorderResults() { 315 | var testResultsDiv = document.getElementById('test-results'); 316 | var numberCells = testResultsDiv.querySelectorAll('tr td.number'); 317 | var currRow = 0; 318 | 319 | for (var i = 0; i < numberCells.length; i++) { 320 | if (window.getComputedStyle) { 321 | var display = window.getComputedStyle(numberCells[i].parentNode).display; 322 | } else { 323 | var display = numberCells[i].parentNode.currentStyle.display; 324 | } 325 | 326 | if (display !== 'none') { 327 | currRow++; 328 | numberCells[i].innerHTML = currRow; 329 | } else { 330 | numberCells[i].innerHTML = ''; 331 | } 332 | } 333 | 334 | if (currRow === 0) { 335 | document.getElementById('test-results-noMessages').style.display = 'block'; 336 | } else { 337 | document.getElementById('test-results-noMessages').style.display = 'none'; 338 | } 339 | } 340 | 341 | // HTMLCSMeter. 342 | function loadHTMLCSStats(callback) { 343 | var feed = 'list'; 344 | var key = '0ArD0TOS0OvHkdEdLQ0pRbkgzRUp5T2JvRHRYQkZfS0E'; 345 | var worksheet = 'od8'; 346 | $.getJSON('http://spreadsheets.google.com/feeds/' + feed + '/' + key + '/' + worksheet + '/public/values?alt=json-in-script&single=true&callback=?', null, function(data) { 347 | var stats = {}; 348 | var entry = data.feed.entry[0]; 349 | var sec = data.feed.entry[1]; 350 | 351 | stats.errors = parseInt(entry.gsx$errors.$t); 352 | stats.warnings = parseInt(entry.gsx$warnings.$t); 353 | stats.notices = parseInt(entry.gsx$notices.$t); 354 | stats.errorSeconds = parseInt(sec.gsx$errors.$t); 355 | stats.warningSeconds = parseInt(sec.gsx$warnings.$t); 356 | stats.noticesSeconds = parseInt(sec.gsx$notices.$t); 357 | 358 | callback.call(this, stats); 359 | }); 360 | } 361 | 362 | window.onload = function() { 363 | var radios = document.querySelectorAll('.radio-gen'); 364 | var source = document.getElementById('source'); 365 | 366 | for (var i = 0; i < radios.length; i++) { 367 | radios[i].onclick = function(event) { 368 | event.target.previousSibling.click(); 369 | } 370 | } 371 | 372 | var inputs = document.querySelectorAll('.radio-input'); 373 | for (var i = 0; i < inputs.length; i++) { 374 | inputs[i].onclick = function(event) { 375 | var radios = document.querySelectorAll('.radio-gen'); 376 | for (var j = 0; j < radios.length; j++) { 377 | radios[j].className = radios[j].className.replace(/ radio-on/, ''); 378 | } 379 | 380 | event.target.nextSibling.className += ' radio-on'; 381 | 382 | if (source.value !== '') { 383 | activateHTMLCS(); 384 | } 385 | } 386 | } 387 | 388 | source.onkeypress = function() { 389 | activateHTMLCS(); 390 | }; 391 | 392 | source.onpaste = function() { 393 | activateHTMLCS(); 394 | }; 395 | 396 | // Set the back-to-top div to appear only when a certain amount of pixels down. 397 | var topDiv = document.getElementById('back-to-top'); 398 | window.onscroll = function() { 399 | var offset = window.pageYOffset || document.documentElement.scrollTop; 400 | 401 | if (offset >= 1200) { 402 | topDiv.className = 'on'; 403 | } else { 404 | topDiv.className = 'off'; 405 | } 406 | } 407 | 408 | window.onscroll(); 409 | } 410 | -------------------------------------------------------------------------------- /src/htmlcs/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "browser": true, 3 | "camelcase": true 4 | } -------------------------------------------------------------------------------- /src/login.js: -------------------------------------------------------------------------------- 1 | var page = new WebPage(), 2 | testindex = 0, 3 | loadInProgress = false, 4 | system = require('system'), 5 | userName = system.args[1], 6 | password= system.args[2], 7 | stageName = system.args[3], 8 | server = system.args[4], 9 | fs = require('fs'), 10 | cookieFile = 'cookies/' + userName+".txt"; 11 | 12 | var data = {stageName: stageName, userName: userName, password: password, server:server }; 13 | 14 | page.onLoadStarted = function() { 15 | loadInProgress = true; 16 | console.log("load started"); 17 | }; 18 | 19 | page.onLoadFinished = function() { 20 | loadInProgress = false; 21 | console.log("load finished"); 22 | }; 23 | 24 | function saveCookies(cookieFile) { 25 | var fs = require("fs"); 26 | fs.write(cookieFile, JSON.stringify(phantom.cookies)); 27 | console.log("Saving cookies: " + JSON.stringify(phantom.cookies)); 28 | } 29 | 30 | var steps = [ 31 | function() { 32 | //Load Login Page 33 | if(server === 'paypal'){ 34 | stageName = 'https://' + stageName +'/login'; 35 | } 36 | 37 | page.open(stageName, function (status) { 38 | if (status !== 'success') { 39 | console.log('Unable to load the address!', stageName); 40 | phantom.exit(); 41 | } else { 42 | page.render('login.jpg'); 43 | } 44 | }) 45 | }, 46 | 47 | function() { 48 | page.evaluate(function(data) { 49 | var userName = data.userName 50 | , password = data.password 51 | 52 | // inputElements = document.getElementsByTagName("input"); 53 | // inputElements.login_email.value = userName; 54 | // inputElements.login_password.value = password; 55 | 56 | var loginip = document.getElementById('login_email'); 57 | var passwordip = document.getElementById('login_password'); 58 | loginip.value = userName; 59 | passwordip.value = password; 60 | // console.log(loginip, passwordip); 61 | 62 | }, data); 63 | }, 64 | function() { 65 | //Login 66 | page.evaluate(function() { 67 | var loginForm = document.forms['login_form']; 68 | loginForm.submit(); 69 | }); 70 | }, 71 | function() { 72 | page.evaluate(function() { 73 | // var tagElement = document.getElementById("headline"); 74 | // console.log(tagElement.innerHTML); 75 | }); 76 | saveCookies(cookieFile); 77 | } 78 | ]; 79 | 80 | 81 | interval = setInterval(function() { 82 | if (!loadInProgress && typeof steps[testindex] == "function") { 83 | console.log("step " + (testindex + 1)); 84 | steps[testindex](); 85 | testindex++; 86 | } 87 | if (typeof steps[testindex] != "function") { 88 | console.log("test complete!"); 89 | phantom.exit(); 90 | } 91 | }, 50); 92 | -------------------------------------------------------------------------------- /src/runner_html.js: -------------------------------------------------------------------------------- 1 | var HTMLCS_RUNNER = new function() { 2 | this.run = function(standard, screenshot_url, errLevel) { 3 | 4 | HTMLCS.process(standard, document, function() { 5 | var msgs = HTMLCS.getMessages(); 6 | var content = ""; 7 | var heading = ""; 8 | var type = ''; 9 | var outerHTML = ''; 10 | 11 | // console.log('errLevel' , errLevel); 12 | 13 | try { 14 | var principles = { 15 | 'Principle1': 'Perceivable', 16 | 'Principle2': 'Operable', 17 | 'Principle3': 'Understandable', 18 | 'Principle4': 'Robust' 19 | }; 20 | content += 'HTMLCodeSniffer Results'; 21 | 22 | if (msgs.length === 0) { 23 | // resultsWrapper.innerHTML = 'No violations found'; 24 | console.log('No violations found'); 25 | return; 26 | } 27 | 28 | content += ''; 29 | if(standard=='Section508') content += ''; 30 | else content += ''; 31 | content += ''; 32 | 33 | var errors = 0; 34 | var warnings = 0; 35 | var notices = 0; 36 | var count=1; 37 | 38 | for (var i = 0; i < msgs.length; i++) { 39 | var msg = msgs[i]; 40 | if(errLevel.indexOf(msg.type) !=-1 ) { 41 | switch (msg.type) { 42 | case HTMLCS.ERROR: 43 | type = 'Error'; 44 | errors += 1; 45 | break; 46 | case HTMLCS.WARNING: 47 | type = 'Warning'; 48 | warnings++; 49 | break; 50 | 51 | case HTMLCS.NOTICE: 52 | type = 'Notice'; 53 | notices++; 54 | break; 55 | 56 | default: 57 | type = 'Unknown'; 58 | break; 59 | 60 | } 61 | 62 | if (msg.element.innerHTML && msg.element.innerHTML.length > 50) { 63 | var outerHTML = msg.element.outerHTML.replace(msg.element.innerHTML, msg.element.innerHTML.substr(0, 50) + '...'); 64 | } else { 65 | var outerHTML = msg.element.outerHTML; 66 | } 67 | // Get the success criterion so we can provide a link. 68 | var msgParts = msg.code.split('.'); 69 | if(standard=='Section508'){ 70 | // var msgParts = code.split('.', 3); 71 | var paragraph = msgParts[1].toLowerCase(); 72 | var principle = [ 73 | ['Section', '1194.22 (' + paragraph + ')'] 74 | ]; 75 | }else{ 76 | 77 | var principle = msgParts[1]; 78 | var sc = msgParts[3].split('_').slice(0, 3).join('_'); 79 | var techniques = msgParts[4]; 80 | techniques = techniques.split(','); 81 | 82 | msgParts.shift(); 83 | msgParts.unshift('[Standard]'); 84 | } 85 | var noStdMsgParts = msgParts.join('.'); 86 | 87 | content += ''; 88 | 89 | if(standard !=='Section508'){ 90 | content += ''; 91 | 92 | content += ''; 96 | 97 | content += ''; 98 | content += ''; 99 | content += ''; 106 | 107 | count++ 108 | } 109 | 110 | if(standard=='Section508'){ 111 | errors += 1; 112 | content += ''; 113 | content += ''; 114 | } 115 | 116 | content += ''; 117 | 118 | } //if errLevel 119 | } //Closing for loop 120 | 121 | 122 | if(errors === 0) { 123 | content += ''; 124 | } 125 | 126 | var address = document.location.href; 127 | if(address.indexOf("file:") === -1){ 128 | heading += '

' + address +'

'; 129 | heading += '
Screenshot:   ' + screenshot_url + '
'; 130 | } 131 | 132 | heading += ''; 134 | 135 | heading +='Export as CSV

'; 136 | 137 | content = content + heading; 138 | content += '
MessageRuleError LevelPrincipleMessageCode snippetTechniques
' + type.toLowerCase() + ''; 93 | 94 | content += '' + principles[principle] + ''; 95 | content += ' ' + msg.msg + '' + splitLine(outerHTML.replace(/&/g,'&').replace(//g,'>'), 30) + '
    '; 100 | 101 | for (var j = 0; j < techniques.length; j++) { 102 | content += '
  • ' + techniques[j] + '
  • '; 103 | } 104 | 105 | content += '
' + msg.msg + '' + principle + '
---- No Errors ---
'; 139 | content += ''; 140 | 141 | } catch (e) { 142 | console.log('Error:', e.toString()); 143 | } 144 | 145 | console.log(content); 146 | console.log('done'); 147 | 148 | }); 149 | }; 150 | 151 | }; 152 | 153 | //Break the code snippet into multiple lines. 154 | function splitLine(st,n) { 155 | var b = ''; 156 | var s = st;while (s.length > n) { 157 | var c = s.substring(0,n); 158 | var d = c.lastIndexOf(' '); 159 | var e =c.lastIndexOf('\n'); 160 | if (e != -1) 161 | d = e; 162 | if (d == -1) 163 | d = n; 164 | b += c.substring(0,d) + '\n'; 165 | s = s.substring(d+1); 166 | } 167 | 168 | return b+s; 169 | } -------------------------------------------------------------------------------- /src/runner_json.js: -------------------------------------------------------------------------------- 1 | var HTMLCS_RUNNER = new function() { 2 | 3 | this.run = function(standard, screenshot_url, errLevel) { 4 | 5 | HTMLCS.process(standard, document, function() { 6 | var msgs = HTMLCS.getMessages(); 7 | var content = []; 8 | var heading = ""; 9 | var type = ''; 10 | var outerHTML = ''; 11 | 12 | console.log(msgs) 13 | 14 | try { 15 | var principles = { 16 | 'Principle1': 'Perceivable', 17 | 'Principle2': 'Operable', 18 | 'Principle3': 'Understandable', 19 | 'Principle4': 'Robust' 20 | }; 21 | 22 | if (msgs.length === 0) { 23 | content.push({'message':'No violations found'}); 24 | } 25 | 26 | var errors = 0; 27 | var warnings = 0; 28 | var notices = 0; 29 | var count=1; 30 | 31 | for (var i = 0; i < msgs.length; i++) { 32 | var msg = msgs[i]; 33 | 34 | if(errLevel.indexOf(msg.type) !=-1 ) { 35 | 36 | var temp_obj = {}; 37 | 38 | switch (msg.type) { 39 | case HTMLCS.ERROR: 40 | type = 'Error'; 41 | errors += 1; 42 | break; 43 | case HTMLCS.WARNING: 44 | type = 'Warning'; 45 | warnings++; 46 | break; 47 | 48 | case HTMLCS.NOTICE: 49 | type = 'Notice'; 50 | notices++; 51 | break; 52 | 53 | default: 54 | type = 'Unknown:' + msg.type +' '; 55 | break; 56 | 57 | } 58 | 59 | if (msg.element.innerHTML && msg.element.innerHTML.length > 50) { 60 | var outerHTML = msg.element.outerHTML.replace(msg.element.innerHTML, msg.element.innerHTML.substr(0, 50) + '...'); 61 | } else { 62 | var outerHTML = msg.element.outerHTML; 63 | } 64 | // Get the success criterion so we can provide a link. 65 | var msgParts = msg.code.split('.'); 66 | var principle = msgParts[1]; 67 | var sc = msgParts[3].split('_').slice(0, 3).join('_'); 68 | var techniques = msgParts[4]; 69 | techniques = techniques.split(','); 70 | 71 | var code = ''; 72 | if (outerHTML !== undefined) { 73 | code = outerHTML.replace(/&/g,'&').replace(//g,'>'); 74 | } 75 | msgParts.shift(); 76 | msgParts.unshift('[Standard]'); 77 | var noStdMsgParts = msgParts.join('.'); 78 | 79 | temp_obj["type"] = type.toLowerCase(); 80 | temp_obj["msg"] = msg.msg; 81 | temp_obj["code"] = splitLine(code, 30); 82 | temp_obj["principle"] = '' + principles[principle] + ''; 83 | var technique=''; 84 | for (var j = 0; j < techniques.length; j++) { 85 | technique += '' + techniques[j] + ''; 86 | 87 | } 88 | temp_obj["techniques"] = technique; 89 | count++; 90 | content.push(temp_obj); 91 | 92 | } //if Closing 93 | } //Closing for loop 94 | 95 | if (errors === 0) { 96 | content.push({'message':'---- No Errors ---'}); 97 | } 98 | 99 | var address = document.location.href; 100 | 101 | if(address.indexOf("file:") === -1){ 102 | heading += '

' + address +'

'; 103 | heading += '
Screenshot:   ' + screenshot_url + '
'; 104 | } 105 | 106 | content.push( {'errorcount':errors} ); 107 | 108 | } catch (e) { 109 | console.log('Error:', e.toString()); 110 | } 111 | console.log(JSON.stringify(content)); 112 | console.log('done'); 113 | }); 114 | }; 115 | }; 116 | 117 | //Break the code snippet into multiple lines. 118 | function splitLine(st,n) { 119 | var b = ''; 120 | var s = st;while (s.length > n) { 121 | var c = s.substring(0,n); 122 | var d = c.lastIndexOf(' '); 123 | var e =c.lastIndexOf('\n'); 124 | if (e != -1) 125 | d = e; 126 | if (d == -1) 127 | d = n; 128 | b += c.substring(0,d) + '\n'; 129 | s = s.substring(d+1); 130 | } 131 | return b+s; 132 | } 133 | -------------------------------------------------------------------------------- /src/screenshot.js: -------------------------------------------------------------------------------- 1 | var page = require('webpage').create(), 2 | system = require('system'), 3 | fs = require('fs'), 4 | 5 | address = system.args[1], 6 | userName = system.args[2], 7 | scanDir = system.args[3], 8 | left = parseFloat(system.args[4]), 9 | top = parseFloat(system.args[5]), 10 | width = parseFloat(system.args[6]), 11 | height = parseFloat(system.args[7]), 12 | 13 | screenshot_url ; 14 | 15 | if(userName !== "") { 16 | var cookieFile = 'cookies/' + userName+".txt"; 17 | } 18 | phantom.silent = true; 19 | page.onConsoleMessage = function (msg) { 20 | // console.log(msg); 21 | }; 22 | page.onError = function (msg) { 23 | // console.log(msg); 24 | }; 25 | 26 | /***************** H E L P E R F U N C T I O N S *******************/ 27 | function restoreCookies(cookieFile) { 28 | var fs = require("fs"); 29 | 30 | if (fs.exists(cookieFile)) { 31 | var cookies = fs.read(cookieFile); 32 | cookies = JSON.parse(cookies); 33 | //console.log("Restored cookies: " + JSON.stringify(cookies)); 34 | if (cookies.length > 0) { 35 | for(var i=0; i'; 80 | console.log(htmlStr); 81 | phantom.exit(); 82 | 83 | }, 200); 84 | }//end if 85 | });//end page.open -------------------------------------------------------------------------------- /test/AATT_API.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Home - PayPal Accessibility Tool 6 | 7 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 |
Paste your code here and test results will be shown underneath the code block: 16 |
17 | 39 |
40 | 41 |

42 | 43 | E.g. htmlcs, chrome, axe default:htmlcs 44 | 45 |

46 | 47 | Eg. json, string 48 | 49 |

50 | 51 |   Eg. 1,2,3 1 means Error, 2 means Warning, 3 means Notice default:1,2,3 52 | 53 |
54 | Standard (htmlcs): 55 |
    56 |
  • 57 |
  • 58 |
  • 59 |
  • 60 |
61 |
62 |

63 | 64 | 65 | 66 | 67 |

68 | 69 | 70 |
71 |
72 |
73 |
74 |
75 |
76 | 77 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /test/Accessibility Fails_files/animated-tree.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/Accessibility Fails_files/animated-tree.gif -------------------------------------------------------------------------------- /test/Accessibility Fails_files/bbc-blocks-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/Accessibility Fails_files/bbc-blocks-dark.png -------------------------------------------------------------------------------- /test/Accessibility Fails_files/decoration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/Accessibility Fails_files/decoration.png -------------------------------------------------------------------------------- /test/Accessibility Fails_files/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sandbox for testing fails 5 | 6 | 7 | 8 | 9 |

Demo page

10 | 11 |

This is a basic page to use as a demo page for iframes and webchat etc

12 | 13 | 14 | -------------------------------------------------------------------------------- /test/Accessibility Fails_files/demo_002.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sandbox for testing fails 5 | 6 | 7 | 8 | 9 |

Demo page

10 | 11 |

This is a basic page to use as a demo page for iframes and webchat etc

12 | 13 | 14 | -------------------------------------------------------------------------------- /test/Accessibility Fails_files/foo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Page not found · GitHub Pages 7 | 50 | 51 | 52 | 53 |
87 | 88 | 89 | -------------------------------------------------------------------------------- /test/Accessibility Fails_files/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 20px; 3 | line-height: 150%; 4 | } 5 | 6 | .h1 { 7 | font-size: 2em; 8 | margin: 0.67em 0; 9 | font-weight: bold; 10 | } 11 | 12 | h2 { 13 | margin: 2em 0 0; 14 | padding-bottom: 0.2em; 15 | border-bottom: 1px solid #aaa; 16 | } 17 | 18 | h5 { 19 | font-size: 1em; 20 | } 21 | 22 | .fake-heading { 23 | font-size: 1.5em; 24 | font-weight: bold; 25 | margin: 0.67em 0; 26 | } 27 | 28 | table { 29 | border-collapse: collapse; 30 | border-spacing: 0; 31 | margin: 0px; 32 | padding: 0px; 33 | } 34 | 35 | th { 36 | font-weight: bold; 37 | } 38 | 39 | th, td { 40 | border: 1px solid #999; 41 | padding: 4px; 42 | } 43 | 44 | tbody th { 45 | text-align: left; 46 | font-weight: normal; 47 | } 48 | 49 | .table--tool { 50 | width: 100%; 51 | } 52 | 53 | .table--tool tbody th { 54 | width: 20%; 55 | } 56 | 57 | .table--tool td { 58 | width: 4%; 59 | } 60 | 61 | .error { 62 | background: #a8ff26; 63 | } 64 | 65 | .warning { 66 | background: #fba300; 67 | } 68 | 69 | .different { 70 | background: #980f0f; 71 | color: #fff; 72 | } 73 | 74 | .notfound { 75 | background: #ff7c7c; 76 | } 77 | 78 | .identified { 79 | background: #A5D9FF; 80 | } 81 | 82 | .manual { 83 | background: #e685ff; 84 | } 85 | 86 | .wrong { 87 | background: #ffcccc; 88 | } 89 | 90 | .false-positive { 91 | background: #981389; 92 | color: #fff; 93 | } 94 | 95 | .container { 96 | width: 800px; 97 | } 98 | 99 | .example { 100 | border: 1px solid #000; 101 | padding: 10px; 102 | } 103 | 104 | .example--contain { 105 | overflow: hidden; 106 | } 107 | 108 | .example--wide { 109 | width: 200%; 110 | overflow: hidden; 111 | } 112 | 113 | .constrained { 114 | width: 50em; 115 | } 116 | 117 | .red { 118 | color: #B31212; 119 | } 120 | .green { 121 | color: green; 122 | } 123 | 124 | .no-outine { 125 | outline:none; 126 | } 127 | 128 | .small-text { 129 | font-size: 8px; 130 | } 131 | .italic { 132 | font-style: italic; 133 | } 134 | .all-caps { 135 | text-transform: uppercase; 136 | } 137 | .justify { 138 | text-align: justify; 139 | } 140 | .line-height { 141 | line-height: 90%; 142 | } 143 | 144 | .low-contrast-small-aa, 145 | a.low-contrast-small-aa:active, 146 | a.low-contrast-small-aa:focus, 147 | a.low-contrast-small-aa:visited { 148 | font-size: 12px; 149 | color: #757980; 150 | } 151 | 152 | .low-contrast-large-aa, 153 | a.low-contrast-large-aa:active, 154 | a.low-contrast-large-aa:focus, 155 | a.low-contrast-large-aa:visited { 156 | font-size: 20px; 157 | color: #878c8c; 158 | } 159 | 160 | .low-contrast-small-aaa, 161 | a.low-contrast-small-aaa:active, 162 | a.low-contrast-small-aaa:focus, 163 | a.low-contrast-small-aaa:visited { 164 | font-size: 12px; 165 | color: #9499a1; 166 | } 167 | 168 | .low-contrast-large-aaa, 169 | a.low-contrast-large-aaa:active, 170 | a.low-contrast-large-aaa:focus, 171 | a.low-contrast-large-aaa:visited { 172 | font-size: 20px; 173 | color: #81858c; 174 | } 175 | 176 | input.low-contrast { 177 | border: 1px #dddddd solid; 178 | } 179 | 180 | .resize { 181 | font-size:1.5em; 182 | line-height: 24px; 183 | max-height: 64px; 184 | overflow: hidden; 185 | } 186 | 187 | #css-generated-text:after { 188 | content: 'Pizza'; 189 | } 190 | 191 | .poisonous { 192 | color: #a1150b; 193 | } 194 | 195 | .safe { 196 | color: #0a6703; 197 | } 198 | 199 | .button { 200 | display: inline-block; 201 | padding: 10px 20px; 202 | color: #fff; 203 | background: #00823b; 204 | border-bottom: 2px solid #000; 205 | text-decoration: none; 206 | cursor: pointer; 207 | } 208 | 209 | .warning-icon { 210 | background-image: url("../images/important.png"); 211 | background-size: 34px 34px; 212 | background-repeat: no-repeat; 213 | background-position: 0 4px; 214 | min-height: 40px; 215 | padding-left: 40px; 216 | font-weight: bold; 217 | width: 30em; 218 | } 219 | 220 | .display-table { 221 | display:table; 222 | height: 100px; 223 | width: 100%; 224 | text-align: center; 225 | } 226 | .display-table-cell { 227 | display:table-cell; 228 | vertical-align:middle; 229 | } 230 | 231 | .unobvious-link { 232 | font-weight: bold; 233 | color: #000; 234 | } 235 | 236 | .unobvious-link a, 237 | .unobvious-link a:visited, 238 | .unobvious-link a:active, 239 | .unobvious-link a:hover { 240 | font-weight: bold; 241 | color: #000; 242 | text-decoration: none; 243 | } 244 | 245 | .unobvious-link a:hover { 246 | text-decoration: underline; 247 | } 248 | 249 | 250 | .form-label { 251 | display: block; 252 | } 253 | .form-hint { 254 | color: #6f777b; 255 | margin: 0; 256 | } 257 | 258 | .quail-result { 259 | outline: 4px solid grey; 260 | } 261 | 262 | .quail-result.severe { 263 | outline: 4px solid red; 264 | } 265 | 266 | .quail-result.suggestion { 267 | outline: 4px solid blue; 268 | } -------------------------------------------------------------------------------- /test/Accessibility Fails_files/main.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('#webchat').click(function(){ 3 | window.open('demo.html','webchat', 'width=200, height=200'); 4 | }); 5 | 6 | $('a.trap').keydown(function(event){ 7 | event.preventDefault(); 8 | var href = $(this).attr('href'); 9 | var text = $(this).text(); 10 | window.open(href, text); 11 | }); 12 | 13 | }); -------------------------------------------------------------------------------- /test/Accessibility Fails_files/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.2 | MIT License | git.io/normalize */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS text size adjust after orientation change, without disabling 6 | * user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability when focused and also mouse hovered in all browsers. 95 | */ 96 | 97 | a:active, 98 | a:hover { 99 | outline: 0; 100 | } 101 | 102 | /* Text-level semantics 103 | ========================================================================== */ 104 | 105 | /** 106 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 107 | */ 108 | 109 | abbr[title] { 110 | border-bottom: 1px dotted; 111 | } 112 | 113 | /** 114 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 115 | */ 116 | 117 | b, 118 | strong { 119 | font-weight: bold; 120 | } 121 | 122 | /** 123 | * Address styling not present in Safari and Chrome. 124 | */ 125 | 126 | dfn { 127 | font-style: italic; 128 | } 129 | 130 | /** 131 | * Address variable `h1` font-size and margin within `section` and `article` 132 | * contexts in Firefox 4+, Safari, and Chrome. 133 | */ 134 | 135 | h1 { 136 | font-size: 2em; 137 | margin: 0.67em 0; 138 | } 139 | 140 | /** 141 | * Address styling not present in IE 8/9. 142 | */ 143 | 144 | mark { 145 | background: #ff0; 146 | color: #000; 147 | } 148 | 149 | /** 150 | * Address inconsistent and variable font size in all browsers. 151 | */ 152 | 153 | small { 154 | font-size: 80%; 155 | } 156 | 157 | /** 158 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 159 | */ 160 | 161 | sub, 162 | sup { 163 | font-size: 75%; 164 | line-height: 0; 165 | position: relative; 166 | vertical-align: baseline; 167 | } 168 | 169 | sup { 170 | top: -0.5em; 171 | } 172 | 173 | sub { 174 | bottom: -0.25em; 175 | } 176 | 177 | /* Embedded content 178 | ========================================================================== */ 179 | 180 | /** 181 | * Remove border when inside `a` element in IE 8/9/10. 182 | */ 183 | 184 | img { 185 | border: 0; 186 | } 187 | 188 | /** 189 | * Correct overflow not hidden in IE 9/10/11. 190 | */ 191 | 192 | svg:not(:root) { 193 | overflow: hidden; 194 | } 195 | 196 | /* Grouping content 197 | ========================================================================== */ 198 | 199 | /** 200 | * Address margin not present in IE 8/9 and Safari. 201 | */ 202 | 203 | figure { 204 | margin: 1em 40px; 205 | } 206 | 207 | /** 208 | * Address differences between Firefox and other browsers. 209 | */ 210 | 211 | hr { 212 | -moz-box-sizing: content-box; 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome 354 | * (include `-moz` to future-proof). 355 | */ 356 | 357 | input[type="search"] { 358 | -webkit-appearance: textfield; /* 1 */ 359 | -moz-box-sizing: content-box; 360 | -webkit-box-sizing: content-box; /* 2 */ 361 | box-sizing: content-box; 362 | } 363 | 364 | /** 365 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 366 | * Safari (but not Chrome) clips the cancel button when the search input has 367 | * padding (and `textfield` appearance). 368 | */ 369 | 370 | input[type="search"]::-webkit-search-cancel-button, 371 | input[type="search"]::-webkit-search-decoration { 372 | -webkit-appearance: none; 373 | } 374 | 375 | /** 376 | * Define consistent border, margin, and padding. 377 | */ 378 | 379 | fieldset { 380 | border: 1px solid #c0c0c0; 381 | margin: 0 2px; 382 | padding: 0.35em 0.625em 0.75em; 383 | } 384 | 385 | /** 386 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 387 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 388 | */ 389 | 390 | legend { 391 | border: 0; /* 1 */ 392 | padding: 0; /* 2 */ 393 | } 394 | 395 | /** 396 | * Remove default vertical scrollbar in IE 8/9/10/11. 397 | */ 398 | 399 | textarea { 400 | overflow: auto; 401 | } 402 | 403 | /** 404 | * Don't inherit the `font-weight` (applied by a rule above). 405 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 406 | */ 407 | 408 | optgroup { 409 | font-weight: bold; 410 | } 411 | 412 | /* Tables 413 | ========================================================================== */ 414 | 415 | /** 416 | * Remove most spacing between table cells. 417 | */ 418 | 419 | table { 420 | border-collapse: collapse; 421 | border-spacing: 0; 422 | } 423 | 424 | td, 425 | th { 426 | padding: 0; 427 | } -------------------------------------------------------------------------------- /test/Accessibility Fails_files/red_panda.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/Accessibility Fails_files/red_panda.jpg -------------------------------------------------------------------------------- /test/Accessibility Fails_files/spacer.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/Accessibility Fails_files/spacer.gif -------------------------------------------------------------------------------- /test/Accessibility Fails_files/submit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/Accessibility Fails_files/submit.png -------------------------------------------------------------------------------- /test/bs_modal_dynamic/donate.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Donate 5 | 6 | 7 | 8 | 9 | 10 | 23 | 24 | 25 |
26 | 27 | 30 | 31 | 32 | 33 | 51 |
52 | 53 | 54 | 55 | 82 | 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /test/css/home.css: -------------------------------------------------------------------------------- 1 | 2 | #container { 3 | margin: 20px auto; 4 | text-align: left; 5 | } 6 | 7 | .results{ margin: 20px;} 8 | 9 | table { 10 | border-collapse: collapse; 11 | } 12 | 13 | table, th, td { 14 | border: 1px solid green; 15 | } 16 | th { 17 | text-transform: capitalize; 18 | background-color: green; 19 | color: white; 20 | } 21 | td { 22 | padding: 15px; 23 | vertical-align: bottom; 24 | } 25 | 26 | #container h2 { 27 | font-size: 24px; 28 | color: #3a5180; 29 | text-shadow: 0 1px 0 rgba(255, 255, 255, 1); 30 | } 31 | #container p { 32 | font-size: 18px; 33 | color: #3a5180; 34 | text-shadow: 0 1px 0 rgba(255, 255, 255, 1); 35 | } 36 | 37 | .button { 38 | border-top: 1px solid #ea97f7; 39 | background: #63f70d; 40 | background: -webkit-gradient(linear, left top, left bottom, from(#09eb58), to(#63f70d)); 41 | background: -webkit-linear-gradient(top, #09eb58, #63f70d); 42 | background: -moz-linear-gradient(top, #09eb58, #63f70d); 43 | background: -ms-linear-gradient(top, #09eb58, #63f70d); 44 | background: -o-linear-gradient(top, #09eb58, #63f70d); 45 | 46 | padding: 5px 65px; 47 | -webkit-border-radius: 12px; 48 | -moz-border-radius: 12px; 49 | border-radius: 12px; 50 | -webkit-box-shadow: rgba(0,0,0,1) 0 1px 0; 51 | -moz-box-shadow: rgba(0,0,0,1) 0 1px 0; 52 | box-shadow: rgba(0,0,0,1) 0 1px 0; 53 | text-shadow: rgba(0,0,0,.4) 0 1px 0; 54 | color: #000000; 55 | font-size: 20px; 56 | margin-left: 10px; 57 | font-family: Georgia, serif; 58 | text-decoration: none; 59 | vertical-align: middle; 60 | } 61 | .button:hover { 62 | border-top-color: #285908; 63 | background: #285908; 64 | color: #ccc; 65 | } 66 | .button:active { 67 | border-top-color: #1b5c3f; 68 | background: #1b5c3f; 69 | } 70 | 71 | #source { 72 | width:920px; 73 | position:relative; 74 | top:14px; 75 | left:14px; 76 | height:243px; 77 | z-index:5; 78 | padding:10px; 79 | margin:10; 80 | clear:both; 81 | background: #26252b; 82 | border:none; 83 | font-family: "menlo", monaco, monospace; 84 | font-size:12px; 85 | display:block; 86 | color:#ffffff; 87 | text-shadow: 0 -1px 0 rgba(0,0,0,1); 88 | border-radius:3px; 89 | resize: none 90 | } 91 | #source::-webkit-scrollbar { 92 | -webkit-appearance: none; 93 | width: 8px; 94 | } 95 | #source::-webkit-scrollbar-thumb { 96 | border-radius: 8px; 97 | background-color: #454545; 98 | } 99 | 100 | #test-options { 101 | position:relative; 102 | width:960px; 103 | padding:18px 0; 104 | color:#1c283f; 105 | } 106 | #code-input { 107 | position:relative; 108 | width:968px; 109 | padding-bottom:12px; 110 | background: #ffffff; 111 | margin:20px 0; 112 | border:1px solid #c6c6c6; 113 | clear:both; 114 | border-radius:5px; 115 | background: #f9f8f8; 116 | box-shadow:0 1px rgba(255,255,255,1), 0 0px 40px rgba(255,255,255,.5), 0 0 6px rgba(0,0,0,.2) inset; 117 | border-radius:14px; 118 | font-size:13px; 119 | } 120 | #code-input fieldset{ 121 | display: table; 122 | border-collapse: collapse; 123 | } 124 | #code-input input[type=checkbox] { 125 | outline: 1px solid blue; 126 | } 127 | .row { 128 | display: table-row; 129 | height: 20px; 130 | } 131 | fieldset .row { 132 | border-bottom:1pt solid black; 133 | } 134 | #code-input .left, .right, .middle { 135 | display: table-cell; 136 | } 137 | #code-input li{ display: inline;} 138 | 139 | table.sortable thead tr .header { 140 | background-image: url('../imgs/bg.gif'); 141 | background-repeat: no-repeat; 142 | background-position: center right; 143 | cursor: pointer; 144 | } 145 | table.sortable tbody td { 146 | color: #3D3D3D; 147 | background-color: #FFF; 148 | vertical-align: top; 149 | } 150 | table.sortable tbody tr.odd td { 151 | background-color:#F0F0F6; 152 | } 153 | table.sortable thead tr .headerSortUp { 154 | background-image: url('../imgs/asc.gif'); 155 | } 156 | table.sortable thead tr .headerSortDown { 157 | background-image: url('../imgs/desc.gif'); 158 | } 159 | table.sortable thead tr .headerSortDown, table.sortable thead tr .headerSortUp { 160 | background-color: #8dbdd8; 161 | } -------------------------------------------------------------------------------- /test/imgs/asc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/imgs/asc.gif -------------------------------------------------------------------------------- /test/imgs/bg.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/imgs/bg.gif -------------------------------------------------------------------------------- /test/imgs/desc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/imgs/desc.gif -------------------------------------------------------------------------------- /test/imgs/screenshots/Home_ScreenShot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/imgs/screenshots/Home_ScreenShot.png -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Quickly Check for Accessibility errors 5 | 6 | 7 | 8 | 9 |
10 |

Run your code through Chrome Developer Tool, Axe Engine and HTML Code Sniffer

11 |

12 | Test results will be shown underneath the code block. 13 |

14 |
15 | 16 |
17 |
18 | Engines 19 |
20 |
21 | 22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 | 33 |
34 |
35 |
36 |
37 | 38 |
39 |
40 | 41 |
42 |
43 |
    44 |
  • 45 | 46 |
  • 47 | 48 |
  • 49 | 50 |
  • 51 |
52 |
53 |
54 | 55 |
56 | 57 |
58 |
59 | 60 | 61 | 62 |
63 |
64 |
65 |
66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /test/js/main.js: -------------------------------------------------------------------------------- 1 | function chromeDevTool(ifrDoc){ 2 | //Chrome Developer Tools 3 | //https://github.com/GoogleChrome/accessibility-developer-tools 4 | var configuration = new axs.AuditConfiguration(); 5 | configuration.showUnsupportedRulesWarning = false; 6 | configuration.scope = ifrDoc.body; 7 | var results = axs.Audit.run(configuration); 8 | var audit = results.map(function (result) { 9 | var DOMElements = result.elements; 10 | var message = ''; 11 | if(result.result ==='FAIL'){ 12 | if (DOMElements !== undefined) { 13 | var maxElements = Math.min(DOMElements.length, 5); 14 | 15 | for (var i = 0; i < maxElements; i++) { 16 | var el = DOMElements[i]; 17 | message += '\n'; 18 | try { 19 | message += axs.utils.getQuerySelectorText(el); 20 | } catch (err) { 21 | message += ' tagName:' + el.tagName; 22 | message += ' id:' + el.id; 23 | } 24 | } 25 | } 26 | return { 27 | heading: result.rule.heading, 28 | result: result.result, 29 | severity: result.rule.severity, 30 | elements: message 31 | }; 32 | } //Return Failures only 33 | }); 34 | // var report = axs.Audit.createReport(results); 35 | for (var i=audit.length;i--;){ 36 | if (audit[i] == null) audit.splice(i,1); 37 | } 38 | var chromeDiv = document.getElementById('chromeres'); 39 | var chromeres = document.createElement("div"); 40 | chromeres.setAttribute("id", "chromeres"); 41 | chromeres.appendChild( buildHtmlTable( audit ,'Chrome Accessibility Plugin','yes') ); 42 | chromeDiv.parentNode.replaceChild(chromeres, chromeDiv); 43 | } 44 | 45 | //Axe-core https://github.com/dequelabs/axe-core 46 | function axeTool(ifrDoc){ 47 | axe.run(ifrDoc.body, function (err, results) { 48 | // console.log(ifrDoc.body, results); 49 | var violations = results.violations 50 | for (var i=violations.length;i--;){ 51 | delete violations[i].helpUrl; 52 | delete violations[i].tags; 53 | console.log(violations[i].nodes) 54 | var nodes= violations[i].nodes 55 | for (var j=nodes.length;j--;){ 56 | violations[i].target = JSON.stringify(nodes[j].target) +'
'+violations[i].target; 57 | } 58 | delete violations[i].nodes; 59 | } 60 | var axeDiv = document.getElementById('axeres'); 61 | var axeres = document.createElement("div"); 62 | axeres.setAttribute("id", "axeres"); 63 | axeres.appendChild( buildHtmlTable( results.violations ,'Axe validator from Deque','no' ) ); 64 | axeDiv.parentNode.replaceChild(axeres, axeDiv); 65 | // window.stop(); 66 | }); 67 | } 68 | 69 | //https://github.com/squizlabs/HTML_CodeSniffer.git 70 | function htmlcsTool(ifrDoc, options){ 71 | var standard = options.standard || 'WCAG2A'; 72 | var source = ifrDoc.body; 73 | // console.log(options.standard, source) 74 | 75 | HTMLCS.process(standard, source, function() { 76 | var msgs = HTMLCS.getMessages(); 77 | var content = []; 78 | var heading = ""; 79 | var type = ''; 80 | var outerHTML = ''; 81 | 82 | var htmlcsDiv = document.getElementById('htmlcsres'); 83 | var htmlcsres = document.createElement("div"); 84 | htmlcsres.setAttribute("id", "htmlcsres"); 85 | htmlcsDiv.innerHTML=''; 86 | 87 | try { 88 | var principles = { 89 | 'Principle1': 'Perceivable', 90 | 'Principle2': 'Operable', 91 | 'Principle3': 'Understandable', 92 | 'Principle4': 'Robust' 93 | }; 94 | 95 | if (msgs.length === 0) { 96 | content.push({'message':'No violations found'}); 97 | return; 98 | } 99 | 100 | var errors = 0; 101 | var warnings = 0; 102 | var notices = 0; 103 | var count=1; 104 | 105 | for (var i = 0; i < msgs.length; i++) { 106 | var msg = msgs[i]; 107 | var temp_obj = {}; 108 | 109 | switch (msg.type) { 110 | case HTMLCS.ERROR: 111 | type = 'Error'; 112 | break; 113 | case HTMLCS.WARNING: 114 | type = 'Warning'; 115 | warnings++; 116 | break; 117 | case HTMLCS.NOTICE: 118 | type = 'Notice'; 119 | notices++; 120 | break; 121 | default: 122 | type = 'Unknown:' + msg.type +' '; 123 | break; 124 | } 125 | 126 | if (msg.element.innerHTML && msg.element.innerHTML.length > 50) { 127 | var outerHTML = msg.element.outerHTML.replace(msg.element.innerHTML, msg.element.innerHTML.substr(0, 50) + '...'); 128 | } else { 129 | var outerHTML = msg.element.outerHTML; 130 | } 131 | 132 | // Get the success criterion so we can provide a link. 133 | var msgParts = msg.code.split('.'); 134 | var principle = msgParts[1]; 135 | var sc = msgParts[3].split('_').slice(0, 3).join('_'); 136 | var techniques = msgParts[4]; 137 | techniques = techniques.split(','); 138 | 139 | msgParts.shift(); 140 | msgParts.unshift('[Standard]'); 141 | var noStdMsgParts = msgParts.join('.'); 142 | 143 | // if(type !== "Warning" && type !== "Notice" && prtyArr.indexOf(priority) !=-1 ) { //&& prtyArr.indexOf(priority) === 0 144 | errors += 1; 145 | temp_obj["type"] = type.toLowerCase(); 146 | temp_obj["msg"] = msg.msg; 147 | temp_obj["code"] = splitLine(outerHTML.replace(/&/g,'&').replace(//g,'>'), 30); 148 | temp_obj["principle"] = '' + principles[principle] + ''; 149 | var technique=''; 150 | for (var j = 0; j < techniques.length; j++) { 151 | technique += '' + techniques[j] + ''; 152 | 153 | } 154 | temp_obj["techniques"] = technique; 155 | count++; 156 | content.push(temp_obj); 157 | // } 158 | 159 | } //Closing for loop 160 | 161 | } catch (e) { 162 | console.log('Error:', e.toString()); 163 | } 164 | htmlcsres.appendChild(buildHtmlTable(content , 'HTML CodeSniffer from SquizLabs','yes' ) ); 165 | htmlcsDiv.parentNode.replaceChild(htmlcsres, htmlcsDiv); 166 | window.stop(); 167 | }); 168 | 169 | } //End htmlcsTool 170 | -------------------------------------------------------------------------------- /test/js/sorttable.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/js/sorttable.js -------------------------------------------------------------------------------- /test/js/util.js: -------------------------------------------------------------------------------- 1 | 2 | var _table_ = document.createElement('table'), 3 | _tr_ = document.createElement('tr'), 4 | _th_ = document.createElement('th'), 5 | _td_ = document.createElement('td'); 6 | _th_.setAttribute('scope','col'); 7 | _td_.setAttribute('scope','row'); 8 | 9 | // Builds the HTML Table out of myList json data from Ivy restful service. 10 | function buildHtmlTable(arr , captionTxt ,sort) { 11 | 12 | var table = _table_.cloneNode(false); 13 | if(sort==='yes') table.setAttribute("class", "sortable"); 14 | var caption = document.createElement('caption'); 15 | caption.appendChild(document.createTextNode(captionTxt || '')); 16 | table.appendChild(caption); 17 | 18 | var thead = document.createElement('thead'); 19 | var columns = addAllColumnHeaders(arr, table, thead); 20 | var tbody = document.createElement('tbody'); 21 | 22 | for (var i=0, maxi=arr.length; i < maxi; ++i) { 23 | var tr = _tr_.cloneNode(false); 24 | for (var j=0, maxj=columns.length; j < maxj ; ++j) { 25 | var td = _td_.cloneNode(false); 26 | if(typeof arr[i] !='undefined'){ 27 | cellValue = arr[i][columns[j]]; 28 | // td.appendChild(document.createTextNode(arr[i][columns[j]] || '')); 29 | td.innerHTML = arr[i][columns[j]] || ' '; 30 | tr.appendChild(td); 31 | } 32 | } 33 | tbody.appendChild(tr); 34 | table.appendChild(tbody); 35 | } 36 | if(sort==='yes') sorttable.makeSortable(table); 37 | return table; 38 | } 39 | 40 | // Adds a header row to the table and returns the set of columns. 41 | // Need to do union of keys from all records as some records may not contain 42 | // all records 43 | function addAllColumnHeaders(arr, table, thead) 44 | { 45 | var columnSet = [], 46 | tr = _tr_.cloneNode(false); 47 | for (var i=0, l=arr.length; i < l; i++) { 48 | for (var key in arr[i]) { 49 | if (arr[i].hasOwnProperty(key) && columnSet.indexOf(key)===-1) { 50 | columnSet.push(key); 51 | var th = _th_.cloneNode(false); 52 | th.appendChild(document.createTextNode(key)); 53 | th.setAttribute('class','header'); 54 | tr.appendChild(th); 55 | } 56 | } 57 | } 58 | thead.appendChild(tr); 59 | table.appendChild(thead); 60 | return columnSet; 61 | } 62 | //Break the code snippet into multiple lines. 63 | function splitLine(st,n) { 64 | var b = ''; 65 | var s = st;while (s.length > n) { 66 | var c = s.substring(0,n); 67 | var d = c.lastIndexOf(' '); 68 | var e =c.lastIndexOf('\n'); 69 | if (e != -1) 70 | d = e; 71 | if (d == -1) 72 | d = n; 73 | b += c.substring(0,d) + '\n'; 74 | s = s.substring(d+1); 75 | } 76 | return b+s; 77 | } -------------------------------------------------------------------------------- /test/tooltest/allTests.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Test 6 | 27 | 28 | 29 |
30 |

Test cases for Accessibility Tools

31 | 32 |

Form input fields

33 |
34 | 35 |
36 | Form Fields 37 | Element with hidden CSS style:

38 | Element with JS hiding input :

39 | 40 | Input type = hiddden

41 | 42 |

Aria-label

43 | 44 | 45 |

Aria-labelledBy

46 |
Middle Name

47 |

Aria-describedBy

48 |
Last Name

49 | 50 |

51 | 52 |
53 | 54 |
55 |
56 |

Contrast

57 |

58 | Low contrast image 59 |

60 |

61 | Low-contrast font color and unreadable texts? visit http://contrastrebellion.com/ 62 |

63 |
64 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /test/tooltest/lowcontrast.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/test/tooltest/lowcontrast.gif -------------------------------------------------------------------------------- /test/tooltest/paypal-logo1x.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paypal/AATT/2217eb3ab8851b0381da01033bd8a30a7b104332/tmp/.keep -------------------------------------------------------------------------------- /views/bookmark.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PayPal Accessibility Tool - Bookmark 6 | 7 | 8 |

9 | PAET 10 | 11 |

12 |

13 |

    14 |
  1. Simply copy or drag this link to your browser bookmarks.
  2. 15 |
  3. Open the page you want to test in the browser which you created this bookmark.
  4. 16 |
  5. Click this bookmark.
  6. 17 |
  7. Note: This can also be used to test logged in pages.
  8. 18 |

    19 | 20 | -------------------------------------------------------------------------------- /views/help.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Help- PayPal Accessibility Tool 6 | 7 | 8 | 9 | 10 | 11 | 20 | 21 | 22 | 23 |
    24 | 25 | 26 |
    27 | PayPal Opensource 28 | 29 |
    30 | 36 |
    37 |
    38 | 39 | 43 | 44 |
    45 |

    Help

    46 |

    Use the Automated Accessibility Testing Tool to test web applications for conformance to the Web Content Accessibility Guidelines (WCAG) 2.0. 47 | 48 | AATT provides a custom UI and accessibility API for HTML CodeSniffer. Find a list of the WCAG 2.0 rules checked on the HTML CodeSniffer WCAG Standard: Summary page. 49 |

    50 |

    AATT Features

    51 |
    • Can test pages, html code snippets, and logged in experiences.
    • 52 |
    • Results are displayed as a table that can be exported as a CSV file.
    • 53 | 54 |
    • Available as an API to integrate with other quality tools.
    • 55 |
    56 | 57 |

    How to use

    58 |
      59 |
    • Test page by address: Provide a stage or live URL, then click the Test Page button.
    • 60 |
    • Test HTML: paste html code (currently supports a small code sample) then click the Test Page button.
    • 61 |
    • Test logged in pages:
    • 62 |
        63 |
      1. click the "Login" button.
      2. 64 |
      3. Provide the user name, password and Stage URL.
      4. 65 |
      5. Click the Login button. Creates a cookied experience so you can test logged in pages by entering a URL.
      6. 66 |
      67 |
    68 | 69 |

    The results show:

    70 |
      71 |
    • WCAG principle: perceivable, operable, understandable, or robust. You can use these principles to help prioritize issues.
    • 72 |
    • Error description
    • 73 |
    • Code snippet
    • 74 |
    • WCAG 2.0 techniques to help developers fix the issue.
    • 75 |
    • Optionally, captures a screen snap of the html page.
    • 76 |
    • You can configure whether or not to show only errors, or include warnings and notices. Warnings and notices require manual inspection to determine the severity of the issue noted.
    • 77 |
    78 | 79 |
    80 | 83 | 84 | 85 |
    86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /views/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HTML_CodeSniffer 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
    18 |
    19 |
    20 |
    Paste your code here 21 |
    22 | 23 | 24 | 25 |
    26 |
    27 | Standard: 28 |
      29 |
    • 30 | 31 |
    • 32 | 33 |
    • 34 | 35 |
    • 36 |
    37 |
    38 | 39 | 40 | 41 |
    42 |
    43 | 44 | 45 |
    46 |
    47 | 48 | 49 | 50 | 51 | --------------------------------------------------------------------------------