diff --git a/.gitignore b/.gitignore index a24b9c3..28ef6fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ node_modules +package-lock.json .tmp .vagrant results/* diff --git a/.travis.yml b/.travis.yml index 3718230..a9a1f81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: node_js sudo: false node_js: - - "8.9" - - "6.2" + - "12.18" + - "14.7" env: - CXX=g++-4.8 addons: diff --git a/Gruntfile.js b/Gruntfile.js index 90eb3d2..d66c14c 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -11,21 +11,6 @@ module.exports = function(grunt) { pkg: grunt.file.readJSON('package.json'), settings: grunt.file.readJSON('./server_config/settings.json'), - webfont: { - icons: { - src: 'front/src/fonts/svg-icons/*.svg', - dest: 'tmp', - destCss: 'front/src/less', - options: { - engine: 'node', - types: 'woff', - stylesheet: 'less', - embed: true, - htmlDemo: false, - syntax: 'bootstrap' - } - } - }, less: { all: { files: [ @@ -71,7 +56,10 @@ module.exports = function(grunt) { 'test/core/*.js', 'test/fixtures/*.js', 'front/src/js/**/*.js' - ] + ], + options: { + esversion: 6 + } }, clean: { tmp: { @@ -103,7 +91,7 @@ module.exports = function(grunt) { options: { reporter: 'spec', }, - src: ['test/api/apiTest.js'] + src: ['test/core/mediaQueriesCheckerTest.js'] } }, env: { @@ -222,12 +210,6 @@ module.exports = function(grunt) { process.env.IS_TEST = true; }); - grunt.registerTask('icons', [ - 'webfont:icons', - 'less', - 'clean:tmp' - ]); - grunt.registerTask('build', [ 'jshint', 'clean:build', diff --git a/README.md b/README.md index 902385d..1144f3e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Analyzes a webpage and detects **performance** or **front-end code quality** iss https://yellowlab.tools - + @@ -58,9 +58,9 @@ Analyzes a webpage and detects **performance** or **front-end code quality** iss ## How it works -The tool loads the given URL via [PhantomJS](http://phantomjs.org/) (a headless browser) and collects various metrics and statistics with the help of [Phantomas](https://github.com/macbre/phantomas). These metrics are then categorized and transformed into scores. It also gives in-depth details so developpers can fix the detected issues. +The tool loads the given URL and collects various metrics and statistics with the help of [Phantomas](https://github.com/macbre/phantomas). These metrics are then categorized and transformed into scores. It also gives in-depth details so developpers can fix the detected issues. -By the way, it's free because we are geeks, not businessmen. All we want is a ★ on GitHub, it will boost our motivation to add more awesome features!!! +By the way, it's free because I am a geek, not businessmen. In return, you can add a ★ on GitHub, it will boost my motivation to add more awesome features!!! ![example dashboard screenshot](./doc/img/screenshot.png) diff --git a/bin/cli.js b/bin/cli.js index a76b83d..c03c922 100644 --- a/bin/cli.js +++ b/bin/cli.js @@ -13,20 +13,20 @@ var cli = meow({ ' yellowlabtools ', '', 'Options:', - ' --device Use "phone" or "tablet" to simulate a mobile device (by user-agent and viewport size).', + ' --device Simulates a device. Choose between phone (default), tablet, desktop and desktop-hd.', ' --screenshot Will take a screenshot and use this value as the output path. It needs to end with ".png".', //' --wait-for-selector Once the page is loaded, Phantomas will wait until the given CSS selector matches some elements.', ' --proxy Sets an HTTP proxy to pass through. Syntax is "host:port".', ' --cookie Adds a cookie on the main domain.', ' --auth-user Basic HTTP authentication username.', ' --auth-pass Basic HTTP authentication password.', - ' --block-domain Disallow requests to given (comma-separated) domains - aka blacklist.', - ' --allow-domain Only allow requests to given (comma-separated) domains - aka whitelist.', + ' --block-domain Disallow requests to given (comma-separated) domains.', + ' --allow-domain Only allow requests to given (comma-separated) domains.', ' --no-externals Block all domains except the main one.', ' --reporter The output format: "json" or "xml". Default is "json".', '' ].join('\n'), - pkg: '../package.json' + pkg: require('../package.json') }); @@ -55,7 +55,7 @@ if (screenshot) { } // Device simulation -options.device = cli.flags.device || 'desktop'; +options.device = cli.flags.device || 'mobile'; // Wait for CSS selector options.waitForSelector = cli.flags.waitForSelector || null; diff --git a/doc/img/YLT-animated.gif b/doc/img/YLT-animated.gif deleted file mode 100644 index 718d072..0000000 Binary files a/doc/img/YLT-animated.gif and /dev/null differ diff --git a/doc/img/home.png b/doc/img/home.png new file mode 100644 index 0000000..48c46ce Binary files /dev/null and b/doc/img/home.png differ diff --git a/doc/img/logo.png b/doc/img/logo.png index f8ecb4f..cc0042b 100644 Binary files a/doc/img/logo.png and b/doc/img/logo.png differ diff --git a/doc/img/screenshot.png b/doc/img/screenshot.png index 71cd7ea..459ba46 100644 Binary files a/doc/img/screenshot.png and b/doc/img/screenshot.png differ diff --git a/front/src/css/about.css b/front/src/css/about.css index 0436d82..8bf7c0d 100644 --- a/front/src/css/about.css +++ b/front/src/css/about.css @@ -6,5 +6,8 @@ margin: 2em; } .about a { - color: #FFF; + color: #fff; +} +.sponsor { + color: #ffa319; } diff --git a/front/src/css/dashboard.css b/front/src/css/dashboard.css index c5e089a..2808d86 100644 --- a/front/src/css/dashboard.css +++ b/front/src/css/dashboard.css @@ -5,15 +5,8 @@ text-align: center; } .summary .globalScore { - display: table; - width: 60%; margin: 3em auto; } -.summary .globalScore > div { - display: table-cell; - width: 50%; - vertical-align: middle; -} .summary .globalScore .globalGrade { margin: 0.5 auto; width: 2.5em; @@ -21,12 +14,10 @@ line-height: 2.5em; border-radius: 0.5em; font-size: 3em; - font-weight: bold; vertical-align: middle; } .summary .globalScore .on100 { font-size: 1.2em; - font-weight: bold; margin: 0.5em 0 1em; } .summary .globalScore .screenshotWrapper:hover { @@ -54,28 +45,63 @@ top: 1.5em; left: 0.9em; } +@media (min-width: 820px) { + .summary .globalScore { + width: 65%; + display: table; + } + .summary .globalScore > div { + display: table-cell; + width: 50%; + vertical-align: middle; + } +} .summary .notations { + width: 100%; display: table; - width: 80%; - margin: 0 10% 1.5em; - border-spacing: 1em; + margin: 0 0 1.5em; + border-spacing: 0 1em; +} +@media (min-width: 820px) { + .summary .notations { + width: 80%; + margin: 0 10% 1.5em; + border-spacing: 1em; + } } .summary .notations > div { display: table-row; } .summary .notations > div > div { - display: table-cell; - height: 2.5em; vertical-align: middle; } +@media (min-width: 820px) { + .summary .notations > div > div { + display: table-cell; + height: 2.5em; + } +} .summary .notations .category { - font-weight: bold; - text-align: center; - width: 20%; + font-size: 1.2em; + width: 50%; + float: left; + text-align: left; + margin: 0.5em 0.25em; +} +@media (min-width: 820px) { + .summary .notations .category { + width: 20%; + text-align: center; + float: none; + } } .summary .notations .criteria { font-weight: normal; - width: 75%; +} +@media (min-width: 820px) { + .summary .notations .criteria { + width: 75%; + } } .summary .notations .A.categoryScore, .summary .notations .B.categoryScore, @@ -87,10 +113,23 @@ width: 2.5em; max-width: 2.5em; min-width: 2.5em; - font-size: 2em; + margin: 0.2em; + font-size: 1.5em; text-align: center; border-radius: 0.5em; - font-weight: bold; + float: right; +} +@media (min-width: 820px) { + .summary .notations .A.categoryScore, + .summary .notations .B.categoryScore, + .summary .notations .C.categoryScore, + .summary .notations .D.categoryScore, + .summary .notations .E.categoryScore, + .summary .notations .F.categoryScore, + .summary .notations .NA.categoryScore { + float: none; + font-size: 2em; + } } .summary .notations .grade .A, .summary .notations .grade .B, @@ -114,14 +153,14 @@ color: inherit; } .summary .notations .criteria .table > a:hover > div { - background: #EBD8E2; + background: #d8ebe0; cursor: pointer; } .summary .notations .criteria .table > a:hover > div.info { background: #FFF; } -.summary .notations .criteria .table > a:hover > div.info .icon-question { - color: #EBD8E2; +.summary .notations .criteria .table > a:hover > div.info svg { + fill: #d8ebe0; } .summary .notations .criteria .grade { width: 10%; @@ -134,32 +173,48 @@ } .summary .notations .criteria .result { width: 18%; - font-weight: bold; white-space: nowrap; text-align: center; vertical-align: middle; } .summary .notations .warning .label, -.summary .notations .warning .result, -.summary .notations .icon-warning { +.summary .notations .warning .result { color: #FF1919; } -.summary .notations .criteria .info { - width: 2%; - text-align: center; - vertical-align: middle; - background: #FFF; - padding-left: 0.1em; - padding-right: 0.1em; +.summary .notations .icon-warning svg { + fill: #FF1919; + margin: -2px 0; } -.summary .notations .criteria .icon-question { - color: transparent; +.summary .notations .criteria .info { + display: none; +} +@media (min-width: 820px) { + .summary .notations .criteria .info { + display: table-cell; + width: 2%; + text-align: center; + vertical-align: middle; + background: #FFF; + padding-left: 0.1em; + padding-right: 0.1em; + } +} +.summary .notations .criteria .info svg { + fill: transparent; } .summary .fromShare { margin-bottom: 3em; } +.summary .tweet { + display: none; +} +@media (min-width: 820px) { + .summary .tweet { + display: block; + } +} .summary .tweet .tweetText { - color: #413; + color: #212240; background: #F2F2F2; border: none; width: 25em; @@ -169,7 +224,7 @@ } .summary .tweet .tweetButton, .summary .tweet .linkedinButton { - color: #413; + color: #212240; background: #F2F2F2; margin-right: 0; } @@ -184,7 +239,7 @@ .summary .sponsor { font-size: 0.9em; margin-bottom: 4em; - color: #413; + color: #ffa319; } .summary .sponsor a { color: inherit; diff --git a/front/src/css/icons.css b/front/src/css/icons.css deleted file mode 100644 index 778eca8..0000000 --- a/front/src/css/icons.css +++ /dev/null @@ -1,67 +0,0 @@ -@font-face { - font-family: "icons"; - src: url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAA+AAA8AAAAAGiQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABWAAAADMAAABCsP6z7U9TLzIAAAGMAAAAQwAAAFZAvEyrY21hcAAAAdAAAAB2AAAB7sZk9g5jdnQgAAACSAAAABQAAAAi/5YDmmZwZ20AAAJcAAAFpwAAC5fYFNvwZ2FzcAAACAQAAAAIAAAACAAAABBnbHlmAAAIDAAABNEAAAcY5gRg2WhlYWQAAAzgAAAAMAAAADYIZ7ifaGhlYQAADRAAAAAgAAAAJAPxAcZobXR4AAANMAAAABEAAAAsFAD/+mxvY2EAAA1EAAAAGAAAABgIwgpsbWF4cAAADVwAAAAgAAAAIAEMC/NuYW1lAAANfAAAAR0AAAHyFNvC+HBvc3QAAA6cAAAAYwAAAIAsEeX5cHJlcAAADwAAAAB9AAAAioyG3sR4nGNgZGBg4GKQY9BhYHRx8wlh4GBgYYAAkAxjTmZ6IlAMygPKsYBpDiBmg4gCAIojA08AeJxjYGS8yDiBgZWBg9GFMY2BgcEdSn9lkGRoYWBgYmBlZsAKAtJcUxgcPjJ+5GI88P8Agx7jQQZHoDAjSA4AzowL6wB4nO2RwRHDMAgEVxaWbcal5JmC8nItaVcVOBy4jDCzd8MN0gOAFejBKzBoFw3VJ9KWecczN945Y8qn33dok0ZvqUvMWvw42Ng58t3gX2fq9+lceytyk0uBvBe6yLRCu51roWvNUSDfCuR7gfwokHuB/wDJjRwpAAB4nGNgwAA8EMh48P9+EAYAFmoFMXicrVb5c9NGFJZ8JE5CjpKDFvVYsXGa2iuTUggGTAiSZRfcw7laCUorxU56H9Ayw9/gv+bJtDP0N/60fm9lm0CSdoZpJqP37e6nffeTyVCCjL3AD4VoPTNmtlo0tnMvoMsWrYbRoejtBZQpxn8XjILR6ch9y7bJCMnwZL1vmIYXuQ6ZikR06FBGia6g523Krdzrr5qTnt/xt+8HtrStXiCo3Q5s2gwtQVVG1TAUSUqKu7SKrcFK0BqfrzHzeTsQMKIXC5psBxF2BJ9NMlpntB5ZURiGFpnlMJRktIODMHQoqwTuyRVjGJT32gHlpUtj0oX5IZmRQzklYZfoJvl9V/BJqpyfOPc7lC3Z2PdET/Rwd7KWL8KtrSBqW/F2GMgQp5s7AY4sdmqg2aG8onGv3DcyaWjGsJSuRIilG1Nm/5DMDu6nfMmhcSXYyCmv8yxn7Au+gTajkClRXRtZUP3xKcPz3ZI9CvaEejn4k+ktZhkmePA4En5PxpwIHSnD4miSsGDk0ErKFmVcT1VMnfI6LeMtw3rh2tGXzijtUH9qMusHtiXtsGQ7NK2STManblx3aEaBKASd8e7y6wDSDWmaV9tYTWPl0CyumdMhEYhAB3ppxotELxI0g6A5NKdau0GS69bDZZo+kE8cekO1toLWTrpp2dif1/tnVWLMentBMjuL/MUuzZa5SFG6bnKGH9N4kLmETGSL7SDh4MFbt4f0stqSLfHaEFvpOb+C2uedEJ40YX8Tuy+n6pQEJoYxLxEtj4yNvmmaOlfzykiMjL8b0Kx0hU9TKMpJKI5cEUH9X3NzpjFjuG4vSs6Olelx2bqAMC3At/myQ4sqMVkuIc4sz6kky/JNleRYvqWSPMvzKhljaalknOXbKimwfEclEyw/UKJC5gOHSho8dKiswSOH3lUGTZdfw8b3YOO7uFvARpY2bGR5ATaylLCR5TJsZFmEjSxXYCPL92Ejy1XYyFIpUdOl5iionYuExyZ4Oh1oH8X1VlHklMlBJ11EETfFKZmQcVXyGPtXBkrJobVReswlulhK8uaiH2AMsYMfHo3M8eNLSlzR9n4EnukfV4IOO1E57xtLfxr8V9+Q1eSSuQiPLsN/GHyyvSjsuOrQFVU5V3No/b+oKMIO6FeREmOpKCqiyc2LUN7p9ZqyiW4PMNYxFtHR66a5uAD9VUyZJTQI/jWFJrzyQa8ihaj1cNe1F8eikt5BOdwJlqCI+31zK3iaEVlhPc2sZM+HLs/AAqap1GzZQPd5r7ZSxHMoHfYZL+pKynpxF8cZL7aAI55Br74TwyQMZtlADiU0NOAXhNaC+05QItNpl0ODI/Z5FFT+2K24kT0qaiPwbKdT7oUupPw6x0BgJ78yiIGsITQ39DYV0DxCNGSTlXG2ajpk7MAgosZuUBE1fBvZ4sGmYFuGIR8rYnXn6Nc3TdRJFTzIjOQyvjmwwBumJuLP86suDlO5oaSocNQaGMy1sJJUzAU04K3Rdvvo9ubL7BM5txVVyyde6iq6Vu5BMRcLrD3OQVoqVAHVG1XYMLpcXBKlXkGTpNfVMTQww1+jFJv/V/Wx+TxfahIj5Ei+7XBgo8/BGPrfYP9tOQjAwI+Ry024vJg2J77u6MP5Cl1GL358yv4dzFxzYZ6uAN9VdBWixVHzEVfRwKdsGKdPFJcjtQA/VX3MGYDPAEwGn6u+qXfaAHpnizk+wDZzGOwwh8EucxjsMec2wBfMYfAlcxgEzGEQMscDuMccBveZw+Ar5jB4wJwGwNfMYfANcxhEzGEQM8cF2GcOgw5zGHSZw+BA0fVRmA95QRtA32p0C+g7XU9YbGLxvaIbI/YPvNDsHzVi9k8aMfVnRbUR9RdeaOqvGjH1N42Y+lDRzRH1ES809XeNmPqHRkx9rJ5O5DLDH09umQoHlF1uPxl+U5x/ACtxTWMAAAEAAf//AA94nHVU32/bVBQ+5177XieN88t2frhpEidasi1rN/Kj6VSoLKSOLs1gCAYbmbaUSexhUhXK9m6Jpwkk+Aum8t4HpA1e8ST+i0kob7zyCg8u57rZ1g1h2edIvvfa3/nOdz7gAMchA3wOKSjDO3DNH4HGda7pM5CCCclmoIOQurgHnMPEQABzN4GM4QQQ0zhOmwirF862mt5KxbbMcrosBaQwlRR2B7tVtNPYWMP+FlrdLeyvYSONdhXbw/Vuwc5gj6LoYJFi4yK2+ghXHm1vP/pehSuX725s3D1Q4U6QdK3E6KlpWebTUcJyk0ECw8U+CtGlxUYKfwZq/arlWlfVIToJQDXA8ZzqDICDhCXIgAWb/gYYmnEAmtAOgIo9AJaQOpXJ+B5QdXCDTuItQMBrVj63uLJJsdIZeI437DnNl4/VyzUZROD7/vTlXffD6XweBOhHIX0IGGH4Df/CkJiuwcDvVpezqOmAI4E64dL5TGO0KWbW3CW+03zsum7NrTmDZq8hRblzhg963fV+q4MNYRc20eGiij2PqD0zHPScIv7xJCsTCXmoQvZJXjvXRL95Tose3/fvr6F8kEgn6H4Q/V3tVqvd2dHDh0cKm1joIDzFTxE+8z8B5BpHbUYkcU3sgy4NqRszMJLSSO5TTRwVWwJIInIPkrCUSC4Re6QSUH8aFwuOfYq8FJGXU6QRgfKtHASh4ip8K2MYTP3p4lnweII1Cx585I9NKTjDWrVka1xjoyzCThH1D4ChhkxBpwL4vmroJAYmkHN9Arqe1sf5XMUtODkv71lniOFiB9ew2RCyuKW0WZS9ATZa7WHTGRLP3fVhO434S/NO/+NvK+86X77XvrK8Ex5DnHZfvcZHzbO3R8WN7aq7E0a+iruLNwRAU/jxJ5o5xXIF6rDtv694F1wqJnVN6PeAlPBq2jTt5bQ5NoN6rbpSLtkVp0K8ZpMSMiwTTxvhdnJKFd31Qa7fUrRajVZfTZoYejkPZ3O7UrFV+D0IfBom17K+OVb6Dit2NFWLeGhXStloRy251hT9Bd7YIzghrsMqfOF/nloyONdwBFwjfmFGlkCZaar1KAD3kqjrcrKEUpIShDAmYBhpY9zwclldO9v2Vhur5VK2nqunzWRCy+gZpY1CJx6mbmEFZXMwHNDTSDO7yrpbrL/G2qf9ZO77gWmRKg6n58ebtdrm+NOTdP61hUx9rFvmlEax7l9/vUOlS6/dY6Ep5Q+HsfYv++tqJiSIPZCGzpBJ3CMtcc5uUGL8FnDGrwFk0maKTizF2k6o+cx5St2OR9A5Ua48YT5FmM4xiIJ6HOhC/xgg9l58HuugRDpQf9Xowxq/R9qEiYi7L095bS7LoLLslh07W8qVTjpvqM57J50nI4g7z9/o+o9RGHfWp/hMqqFMPI7mWH+z6VH2xBoukUdSz4//oZ4LwsZidB5892vBImg42v3Zvn7TXzOQporMQTkDKmcQOgp9Xzmt/pXE2MGI19sk5DQbL/sX//8Aud/+f07c8ku5rMbrtcrySbkkuIyWIZadDnKPd4tWQTS9RmtorRe7BUc22/11aReUOzaIuiO8eZRPvTAtN/rQtcwXqbxpJS8krXzeVTd+Hf2AYSEaF+r1Aj4rhCSTqWmFZrz+L5j5HWAAAAB4nGNgZGBgAOKHa1vvx/PbfGXgZmIAgctLbARh9P9f//czsTEeBHI5GMDSAGZADLR4nGNgZGBgPPD/AIMeE8P/X/9/MbExAEVQADcAqdcGuHicY2BgYGAiCv//BQAE3AIOAAAAAAAAAAB0ALYBBAFaAboCEgKEAr4DDAOMAAEAAAALADIABgAAAAAAAgAYACgAdwAAAGsLlwAAAAB4nF2OvU7DMBSFT/qHaBACITGbpQtS+jP2AdqZDtnTxElbJXHkuJUqMTPzFMw8Bc/FiXslKmzp+jvnHl8bwAN+EKBbAYa+dquHG6oL90l3wgPyo/AQIZ6FR1QvwmO8YiIc4glvnBAMbumMkQn3cI9auE//XXhA/hAecvqn8Ij+l/AYMb6FQ0yC0T41dbvRxbFMrGdfYm3bvanVPJp5vda1tonTmdqeVXsqFs7lKremUitTO12WRjXWHHTqop1zzXI6zcWPUlNhjxSGf26xgUaBI0oksFf+H8VMWO90WmGOCLOr/pr92mcSOJ4ZM1ucWVucOHtB1yGnzpkxqEgrf7dLl9yGTuN7Bzop/Qg7f6vBElPu/F8+8q9XvzD1U2IAAAB4nG3BQQ7CMAwEQG+bpoXwFU68yEEGRTIxOK76fQ5cmaGJfhb6r2DCjIQFGSs2nHBGwYUKu9txVXnELVX2MSvXpG1EUrN3flltKttnlxHNeh53F+k5uKrEerD31p9EX5pyGQsAeJxj8N7BcCIoYiMjY1/kBsadHAwcDMkFGxnYnDYxMDJogRibuZkYOSAsfgYwi91pFzMDUJoTyOZw2sXgAGEzM7hsVGHsCIzY4NARsZE5xWWjGoi3i6OBgZHFoSM5JAKkJBIINvMyMfJo7WD837qBpXcjE1Afa4oLAGWtJJsAAAA=") format("woff"); - font-weight: normal; - font-style: normal; -} -[class^="icon-"]:before, -[class*=" icon-"]:before { - font-family: "icons"; - display: inline-block; - vertical-align: middle; - line-height: 1; - font-weight: normal; - font-style: normal; - speak: none; - text-decoration: inherit; - text-transform: none; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.icon-arrow-left3:before, -.icon-bars:before, -.icon-lab:before, -.icon-list:before, -.icon-loop:before, -.icon-mobile:before, -.icon-question:before, -.icon-screen:before, -.icon-tablet:before, -.icon-warning:before { - font-family: "icons"; - display: inline-block; - font-weight: normal; - font-style: normal; - text-decoration: inherit; -} -.icon-arrow-left3:before { - content: "\f101"; -} -.icon-bars:before { - content: "\f102"; -} -.icon-lab:before { - content: "\f103"; -} -.icon-list:before { - content: "\f104"; -} -.icon-loop:before { - content: "\f105"; -} -.icon-mobile:before { - content: "\f106"; -} -.icon-question:before { - content: "\f107"; -} -.icon-screen:before { - content: "\f108"; -} -.icon-tablet:before { - content: "\f109"; -} -.icon-warning:before { - content: "\f10a"; -} diff --git a/front/src/css/index.css b/front/src/css/index.css index 20f1b18..54ad711 100644 --- a/front/src/css/index.css +++ b/front/src/css/index.css @@ -20,7 +20,7 @@ background: #e74c3c; } .launchBtn.disabled { - background: #deaca6; + background: #f1bd70; } .launchBtn.disabled:focus { color: #ddd; @@ -55,23 +55,28 @@ text-decoration: none; font-size: 0.8em; } +.device .item > svg { + margin: 0.6em 0 0.3em; + fill: #fff; +} .device .item.active { color: #ffa319; border: 2px solid #ffa319; padding: 0; } +.device .item.active > svg { + fill: #ffa319; +} .device .item:hover { color: #ffa319; } -.device .item > div { - margin: 0.2em 0 0.1em; - font-size: 3em; +.device .item:hover > svg { + fill: #ffa319; } .settingsTooltip { position: relative; } -.settingsTooltip span { - font-size: 0.8em; +.settingsTooltip svg { vertical-align: text-top; } .settingsTooltip div { @@ -85,6 +90,8 @@ border-radius: 1em; border: 2px solid #ffa319; white-space: normal; + word-break: break-all; + word-break: break-word; z-index: 2; } .settingsTooltip:hover div { @@ -149,7 +156,7 @@ width: 50%; margin: 6em auto 0; font-size: 0.9em; - color: #413; + color: #8abfaf; } .features > div { width: 33.3%; @@ -164,7 +171,6 @@ input.url { height: 2em; border: 0 solid; border-radius: 0.5em; - box-shadow: 0.1em 0.2em 0 0 #5e2846; outline: none; } input[type=submit]:hover { diff --git a/front/src/css/main.css b/front/src/css/main.css index add9c9c..d45ff6e 100644 --- a/front/src/css/main.css +++ b/front/src/css/main.css @@ -1,77 +1,15 @@ -@font-face { - font-family: "icons"; - src: url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); - font-weight: normal; - font-style: normal; -} -[class^="icon-"]:before, -[class*=" icon-"]:before { - font-family: "icons"; - display: inline-block; - vertical-align: middle; - line-height: 1; - font-weight: normal; - font-style: normal; - speak: none; - text-decoration: inherit; - text-transform: none; - text-rendering: auto; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.icon-arrow-left3:before, -.icon-bars:before, -.icon-lab:before, -.icon-list:before, -.icon-loop:before, -.icon-mobile:before, -.icon-question:before, -.icon-screen:before, -.icon-tablet:before, -.icon-warning:before { - font-family: "icons"; - display: inline-block; - font-weight: normal; - font-style: normal; - text-decoration: inherit; -} -.icon-arrow-left3:before { - content: "\f101"; -} -.icon-bars:before { - content: "\f102"; -} -.icon-lab:before { - content: "\f103"; -} -.icon-list:before { - content: "\f104"; -} -.icon-loop:before { - content: "\f105"; -} -.icon-mobile:before { - content: "\f106"; -} -.icon-question:before { - content: "\f107"; -} -.icon-screen:before { - content: "\f108"; -} -.icon-tablet:before { - content: "\f109"; -} -.icon-warning:before { - content: "\f10a"; -} html { - margin: 100px 50px; + margin: 35px 5px; +} +@media (min-width: 640px) { + html { + margin: 100px 50px; + } } body { margin: 0 auto; max-width: 1280px; - background: #9c4274; + background: #212240; color: #fff; font-size: 16px; text-align: center; @@ -87,43 +25,61 @@ button { input[type=submit] { cursor: pointer; } -h1 span { - display: inline-block; - height: 1em; - width: 1em; - color: #ffa319; +h1 { + font-weight: 200; } .resultsMenu { margin-top: 2em; } .resultsMenu .menuItem { + font-size: 0.8em; display: inline-block; - margin: 1em; - width: 8em; + width: 7em; height: 7em; color: #fff; - border: 3px solid #fff; - border-radius: 0.5em; cursor: pointer; text-decoration: none; } +@media (min-width: 640px) { + .resultsMenu .menuItem { + font-size: 1em; + margin: 1em; + width: 8em; + border: 2px solid #fff; + border-radius: 0.5em; + } +} +.resultsMenu .menuItem svg { + fill: #fff; +} .resultsMenu .menuItem.back, .resultsMenu .menuItem.restart { - color: #413; - border-color: #413; + color: #fff; + border-color: #fff; } .resultsMenu .menuItem div { padding-top: 0.5em; font-size: 3em; } +.resultsMenu svg { + display: block; + margin: 1.2em auto 0.2em; +} .resultsMenu .active, .resultsMenu .menuItem.active:hover { color: #ffa319; border-color: #ffa319; } +.resultsMenu .active svg, +.resultsMenu .menuItem.active:hover svg { + fill: #ffa319; +} .resultsMenu .menuItem:hover { color: #ffa319; } +.resultsMenu .menuItem:hover svg { + fill: #ffa319; +} .resultsMenu span { position: relative; top: 0.5em; @@ -204,13 +160,15 @@ a.linkButton { .screenshotWrapper .screenshotError { color: #fff; } -.screenshotWrapper.desktop { +.screenshotWrapper.desktop, +.screenshotWrapper.desktop-hd { border: 0.2em solid #AAA; padding: 0.5em; border-top-left-radius: 0.4em; border-top-right-radius: 0.4em; } -.screenshotWrapper.desktop:before { +.screenshotWrapper.desktop:before, +.screenshotWrapper.desktop-hd:before { position: absolute; width: 15em; height: 0.6em; @@ -221,7 +179,8 @@ a.linkButton { border-bottom-right-radius: 0.2em; content: " "; } -.screenshotWrapper.desktop:after { +.screenshotWrapper.desktop:after, +.screenshotWrapper.desktop-hd:after { position: absolute; width: 0.4em; height: 0.2em; @@ -230,7 +189,8 @@ a.linkButton { background: lime; content: " "; } -.screenshotWrapper.desktop > div { +.screenshotWrapper.desktop > div, +.screenshotWrapper.desktop-hd > div { width: 12em; height: 6.75em; } @@ -282,9 +242,29 @@ a.linkButton { width: 8.25em; height: 11em; } +.table { + display: table; + width: 100%; + border-spacing: 0.25em; +} +.table > div, +.table > a { + display: table-row; +} +.table > .headers > div { + font-weight: bold; + padding: 0.5em 1em; +} +.table > div > div, +.table > a > div { + padding: 0.1em 1em; + background: #f2f2f2; + display: table-cell; + text-align: left; +} .footer { padding: 3em; - color: #413; + color: #fff; } .footer a { color: inherit; @@ -298,7 +278,6 @@ a.linkButton { .footer .sponsor { font-size: 0.9em; } -[class^="icon-"]:before, -[class*=" icon-"]:before { - vertical-align: baseline; +.homeSponsor { + color: #ffa319; } diff --git a/front/src/css/queue.css b/front/src/css/queue.css index 094ee24..e6b9fa2 100644 --- a/front/src/css/queue.css +++ b/front/src/css/queue.css @@ -6,3 +6,16 @@ font-size: 0.8em; margin-bottom: 6em; } +.progressBarEmpty { + width: 90%; + max-width: 300px; + margin: 1em auto; + padding: 0.05em; + border: 1px solid #ffa319; +} +.progressBarFilled { + width: 5%; + height: 0.5em; + background: #ffa319; + transition: width 3s ease-out; +} diff --git a/front/src/css/rule.css b/front/src/css/rule.css index c097224..24e789e 100644 --- a/front/src/css/rule.css +++ b/front/src/css/rule.css @@ -2,7 +2,6 @@ text-align: center; } .rule .ruleTable { - display: table; border-spacing: 1em; width: 90%; margin: 2em auto; @@ -10,16 +9,20 @@ border: 1px dashed #666; border-radius: 0.5em; } -.rule .ruleTable > div { - display: table-cell; - vertical-align: middle; -} -.rule .ruleTable .left { - width: 33%; - font-weight: bold; -} -.rule .ruleTable .right { - width: 67%; +@media (min-width: 820px) { + .rule .ruleTable { + display: table; + } + .rule .ruleTable > div { + display: table-cell; + vertical-align: middle; + } + .rule .ruleTable .left { + width: 33%; + } + .rule .ruleTable .right { + width: 67%; + } } .rule .score { font-size: 2.5em; @@ -27,7 +30,7 @@ height: 2em; width: 2em; border-radius: 0.5em; - margin: 0 auto 0.5em; + margin: 0 auto 0.25em; } .rule h3 { margin-bottom: 0em; @@ -67,7 +70,13 @@ border-spacing: 0 0.25em; margin: 0 auto; min-width: 10%; - max-width: 90%; + font-size: 0.875em; +} +@media (min-width: 820px) { + .rule .offendersTable { + max-width: 90%; + font-size: 1em; + } } .rule .offendersTable > div { display: table-row; @@ -75,12 +84,12 @@ .rule .offendersTable > div > div { display: table-cell; background: #f2f2f2; - padding: 0 1em; + padding: 0 0.25em; word-wrap: break-word; word-break: break-all; } .rule .offendersTable > div > div:hover { - background: #EBD8E2; + background: #d8ebe0; } .rule .notFound { font-size: 1em; diff --git a/front/src/css/screenshot.css b/front/src/css/screenshot.css index 55af543..0df36d4 100644 --- a/front/src/css/screenshot.css +++ b/front/src/css/screenshot.css @@ -2,6 +2,16 @@ text-align: center; } .screenshot .screenshotWrapper { - font-size: 2.08333333333333em; + font-size: 1.2em; margin-bottom: 0.5em; } +@media (min-width: 420px) { + .screenshot .screenshotWrapper { + font-size: 1.6em; + } +} +@media (min-width: 640px) { + .screenshot .screenshotWrapper { + font-size: 2.08333333em; + } +} diff --git a/front/src/css/timeline.css b/front/src/css/timeline.css deleted file mode 100644 index 11920fb..0000000 --- a/front/src/css/timeline.css +++ /dev/null @@ -1,338 +0,0 @@ -/* Timeline colors, related to Window Performances */ -.execution { - text-align: center; -} -.selectScript { - padding-bottom: 2em; - font-size: 0.9em; -} -.selectScript select { - max-width: 30em; -} -.selectScript.empty { - font-size: 0.8em; -} -.selectScript.empty select { - width: 10em; -} -.timeline { - margin: 2em 0 5em; -} -.timeline .chart { - position: relative; - width: 100%; - border-bottom: 1px solid #000; -} -.timeline .startTime, -.timeline .endTime { - position: absolute; - bottom: 0.5em; - font-size: 0.8em; -} -.timeline .startTime { - left: 0em; -} -.timeline .endTime { - right: 0em; -} -.timeline .chartPoints { - display: table; - height: 100px; - width: 99%; - margin: 0 auto; -} -.timeline .interval { - display: table-cell; - position: relative; - height: 100px; - width: 0.5%; -} -.timeline .interval .color { - position: absolute; - bottom: 0; - width: 100%; -} -.timeline .interval .color.clickable { - cursor: pointer; -} -.timeline div.interval:hover { - background: #9C4274; -} -.timeline .interval:hover .color { - background: #F04DA7; -} -.timeline .domComplete.interval { - background: #EDE3FF; -} -.timeline .domComplete .color { - background: #C2A3FF; -} -.timeline .domContentLoadedEnd.interval { - background: #D8F0F0; -} -.timeline .domContentLoadedEnd .color { - background: #7ECCCC; -} -.timeline .domContentLoaded.interval { - background: #E0FFD1; -} -.timeline .domContentLoaded .color { - background: #A7E846; -} -.timeline .domInteractive.interval { - background: #FFFCCC; -} -.timeline .domInteractive .color { - background: #FFE433; -} -.timeline .domCreation.interval { - background: #FFE0CC; -} -.timeline .domCreation .color { - background: #FF6600; -} -.timeline .tooltip.detailsOverlay { - position: absolute; - display: none; - width: auto; - padding: 0.5em 1em; - top: -1.5em; - right: 1em; -} -.timeline .interval:hover .tooltip { - display: block; -} -.timeline .legend { - display: table; - width: 100%; - margin-top: 1em; -} -.timeline .legend > div { - display: table-row; -} -.timeline .legend > div > div { - position: relative; - display: table-cell; - margin-top: 1em; -} -.timeline .titles { - font-weight: bold; -} -.timeline .titles > div { - padding: 0 1em 0 2em; -} -.timeline .tips { - font-size: 0.7em; -} -.timeline .tips > div { - padding: 1em 1em 0 0; -} -.timeline .legend .color { - display: block; - position: absolute; - left: 0; - height: 1.5em; - width: 1.5em; - border-radius: 0.2em; -} -.filters { - margin: 1em auto; - padding: 0.5em; - min-width: 30em; - width: 30%; - border: 1px dotted #aaa; - text-align: left; -} -.subFilters { - margin-left: 3em; - font-size: 0.9em; -} -.table { - display: table; - width: 100%; - border-spacing: 0.25em; -} -.table > div, -.table > a { - display: table-row; -} -.table > .headers > div { - font-weight: bold; - padding: 0.5em 1em; -} -.table > div > div, -.table > a > div { - padding: 0.1em 1em; - background: #f2f2f2; - display: table-cell; - text-align: left; -} -.table > div.jsError > .type, -.table > div.jsError > .value { - color: #e74c3c; - font-weight: bold; -} -.table > div.windowPerformance > div, -.table > div.windowPerformance > div.startTime { - background: #EBD8E2; -} -.table > div.showingDetails > div { - background: #f1c40f; -} -.table > div.highlight > div.startTime { - background-color: #C0F090; -} -.table > div.highlight-remove { - transition: 3s; -} -.table > div.highlight-remove > div.startTime { - transition: background-color 3s ease-in; -} -.table > div > .index { - color: #bbb; - word-break: normal; -} -.table > div > .type { - white-space: nowrap; -} -.table .children { - margin-top: 0.2em; - font-size: 0.8em; - line-height: 1.6em; -} -.table .child { - margin-left: 0.5em; -} -.table .child > .child { - margin-left: 1em; -} -.table .child:before { - content: "↳"; -} -.table .child .childArgs { - display: none; -} -.table .child span { - position: relative; -} -.table .child span:hover { - background: #EBD8E2; -} -.table .child span:hover div { - display: inline-block; -} -.table .child span:hover .childArgs { - display: block; - position: absolute; - padding: 0 1em 0 2em; - left: 100%; - top: 0; - background: #EBD8E2; - line-height: 1.3em; - height: 1.3em; - z-index: 2; -} -.table .showingDetails .child span:hover { - background: inherit; -} -.table .showingDetails .child span:hover .childArgs { - display: none; -} -.table > div > .value { - width: 70%; - word-break: break-all; -} -.table > div > .details { - position: relative; -} -.table .details .icon-question { - color: #f1c40f; - cursor: pointer; -} -.table .icon-warning { - display: inline-block; - width: 0.8em; -} -.detailsOverlay { - display: none; - position: absolute; - right: 3em; - top: -3em; - width: 45em; - min-height: 1em; - padding: 0 1em 1em; - background: #fff; - border: 2px solid #f1c40f; - border-radius: 0.5em; - z-index: 2; -} -@media screen and (max-width: 1024px) { - .detailsOverlay { - width: 25em; - } -} -.showDetails .detailsOverlay { - display: block; -} -.detailsOverlay .closeBtn { - position: absolute; - top: 0.5em; - right: 0.5em; - color: #f1c40f; - cursor: pointer; -} -.detailsOverlay .advice { - color: #e74c3c; - font-weight: bold; -} -.detailsOverlay .trace { - word-break: break-all; -} -.table > div > .duration, -.table > div > .startTime { - text-align: center; - white-space: nowrap; -} -.table > div > .startTime.domComplete { - background: #EDE3FF; -} -.table > div > .startTime.domContentLoadedEnd { - background: #D8F0F0; -} -.table > div > .startTime.domContentLoaded { - background: #E0FFD1; -} -.table > div > .startTime.domInteractive { - background: #FFFCCC; -} -.table > div > .startTime.domCreation { - background: #FFE0CC; -} -.execution .icon-warning { - color: #e74c3c; - cursor: pointer; -} -.queryWithoutResultsFilterOn > div { - display: none; -} -.queryWithoutResultsFilterOn > div.queryWithoutResults { - display: table-row; -} -.jQueryCallOnEmptyObjectFilterOn > div { - display: none; -} -.jQueryCallOnEmptyObjectFilterOn > div.jQueryCallOnEmptyObject { - display: table-row; -} -.eventNotDelegatedFilterOn > div { - display: none; -} -.eventNotDelegatedFilterOn > div.eventNotDelegated { - display: table-row; -} -.jsErrorFilterOn > div { - display: none; -} -.jsErrorFilterOn > div.jsError { - display: table-row; -} diff --git a/front/src/fonts/svg-icons/arrow-left3.svg b/front/src/fonts/svg-icons/arrow-left3.svg deleted file mode 100644 index edd6f1c..0000000 --- a/front/src/fonts/svg-icons/arrow-left3.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/fonts/svg-icons/bars.svg b/front/src/fonts/svg-icons/bars.svg deleted file mode 100644 index 03dac05..0000000 --- a/front/src/fonts/svg-icons/bars.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/fonts/svg-icons/lab.svg b/front/src/fonts/svg-icons/lab.svg deleted file mode 100644 index 0cf1263..0000000 --- a/front/src/fonts/svg-icons/lab.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/fonts/svg-icons/list.svg b/front/src/fonts/svg-icons/list.svg deleted file mode 100644 index 98a4cd3..0000000 --- a/front/src/fonts/svg-icons/list.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/fonts/svg-icons/loop.svg b/front/src/fonts/svg-icons/loop.svg deleted file mode 100755 index 84d3184..0000000 --- a/front/src/fonts/svg-icons/loop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/fonts/svg-icons/mobile.svg b/front/src/fonts/svg-icons/mobile.svg deleted file mode 100755 index 5cb8089..0000000 --- a/front/src/fonts/svg-icons/mobile.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/fonts/svg-icons/question.svg b/front/src/fonts/svg-icons/question.svg deleted file mode 100644 index c78d38d..0000000 --- a/front/src/fonts/svg-icons/question.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/fonts/svg-icons/screen.svg b/front/src/fonts/svg-icons/screen.svg deleted file mode 100755 index c0cdff9..0000000 --- a/front/src/fonts/svg-icons/screen.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/fonts/svg-icons/tablet.svg b/front/src/fonts/svg-icons/tablet.svg deleted file mode 100755 index b701c87..0000000 --- a/front/src/fonts/svg-icons/tablet.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/fonts/svg-icons/warning.svg b/front/src/fonts/svg-icons/warning.svg deleted file mode 100644 index 3fd4284..0000000 --- a/front/src/fonts/svg-icons/warning.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/front/src/img/favicon-fail.png b/front/src/img/favicon-fail.png new file mode 100644 index 0000000..8192c94 Binary files /dev/null and b/front/src/img/favicon-fail.png differ diff --git a/front/src/img/favicon-success.png b/front/src/img/favicon-success.png new file mode 100644 index 0000000..09f498f Binary files /dev/null and b/front/src/img/favicon-success.png differ diff --git a/front/src/img/favicon.png b/front/src/img/favicon.png index 0a5a194..23ff5bc 100644 Binary files a/front/src/img/favicon.png and b/front/src/img/favicon.png differ diff --git a/front/src/img/logo-large.png b/front/src/img/logo-large.png index f739504..88d1971 100644 Binary files a/front/src/img/logo-large.png and b/front/src/img/logo-large.png differ diff --git a/front/src/js/app.js b/front/src/js/app.js index 6256042..4b04f1e 100644 --- a/front/src/js/app.js +++ b/front/src/js/app.js @@ -7,7 +7,6 @@ var yltApp = angular.module('YellowLabTools', [ 'queueCtrl', 'ruleCtrl', 'screenshotCtrl', - 'timelineCtrl', 'runsFactory', 'resultsFactory', 'apiService', @@ -64,10 +63,6 @@ yltApp.config(['$routeProvider', '$locationProvider', templateUrl: 'views/dashboard.html', controller: 'DashboardCtrl' }). - when('/result/:runId/timeline', { - templateUrl: 'views/timeline.html', - controller: 'TimelineCtrl' - }). when('/result/:runId/screenshot', { templateUrl: 'views/screenshot.html', controller: 'ScreenshotCtrl' diff --git a/front/src/js/controllers/queueCtrl.js b/front/src/js/controllers/queueCtrl.js index 17a3820..938e6d7 100644 --- a/front/src/js/controllers/queueCtrl.js +++ b/front/src/js/controllers/queueCtrl.js @@ -5,26 +5,50 @@ queueCtrl.controller('QueueCtrl', ['$scope', '$routeParams', '$location', 'Runs' var numberOfTries = 0; + var favicon = document.querySelector('link[rel=icon]'); + var faviconUrl = 'img/favicon.png'; + var faviconSuccessUrl = 'img/favicon-success.png'; + var faviconFailUrl = 'img/favicon-fail.png'; + var faviconInterval = null; + var faviconCounter = 0; + var faviconCanvas = null; + var faviconCanvasContext = null; + var faviconImage = null; + function getRunStatus () { Runs.get({runId: $scope.runId}, function(data) { $scope.url = data.params.url; $scope.status = data.status; + $scope.progress = data.progress; $scope.notFound = false; $scope.connectionLost = false; - if (data.status.statusCode === 'running' || data.status.statusCode === 'awaiting') { + if (data.status.statusCode === 'awaiting') { numberOfTries ++; + rotateFavicon(); - // Retrying in 2 seconds (and increasing the delay a bit more each time) + // Retrying every 2 seconds (and increasing the delay a bit more each time) setTimeout(getRunStatus, 2000 + (numberOfTries * 100)); + } else if (data.status.statusCode === 'running') { + numberOfTries ++; + rotateFavicon(); + + // Retrying every second or so + setTimeout(getRunStatus, 1000 + (numberOfTries * 10)); + } else if (data.status.statusCode === 'complete') { + stopFavicon(true); + $location.path('/result/' + $scope.runId).replace(); } else { - // Handled by the view + stopFavicon(false); + + // The rest is handled by the view } }, function(response) { if (response.status === 404) { + stopFavicon(false); $scope.notFound = true; $scope.connectionLost = false; } else if (response.status === 0) { @@ -35,6 +59,40 @@ queueCtrl.controller('QueueCtrl', ['$scope', '$routeParams', '$location', 'Runs' } }); } + + function rotateFavicon() { + if (!faviconInterval) { + faviconImage = new Image(); + faviconImage.onload = function() { + faviconCanvas = document.getElementById('faviconRotator'); + faviconCanvasContext = faviconCanvas.getContext('2d'); + faviconCanvasContext.fillStyle = '#212240'; + + if (!!faviconCanvasContext) { + faviconInterval = window.setInterval(faviconTick, 300); + } + }; + faviconImage.src = faviconUrl; + } + } + + function faviconTick() { + faviconCounter ++; + faviconCanvasContext.save(); + faviconCanvasContext.fillRect(0, 0, 32, 32); + faviconCanvasContext.translate(16, 16); + faviconCanvasContext.rotate(22.5 * faviconCounter * Math.PI / 180); + faviconCanvasContext.translate(-16, -16); + faviconCanvasContext.drawImage(faviconImage, 0, 0, 32, 32); + faviconCanvasContext.restore(); + favicon.href = faviconCanvas.toDataURL('image/png'); + } + + function stopFavicon(isSuccess) { + window.clearInterval(faviconInterval); + faviconInterval = null; + favicon.href = isSuccess ? faviconSuccessUrl : faviconFailUrl; + } getRunStatus(); }]); diff --git a/front/src/js/controllers/ruleCtrl.js b/front/src/js/controllers/ruleCtrl.js index 16c5c55..b2502c2 100644 --- a/front/src/js/controllers/ruleCtrl.js +++ b/front/src/js/controllers/ruleCtrl.js @@ -64,68 +64,6 @@ ruleCtrl.controller('RuleCtrl', ['$scope', '$rootScope', '$routeParams', '$locat } }; } - - // Init "Breakpoints" chart - if ($scope.policyName === 'cssBreakpoints' && $scope.rule.value > 0) { - - // Seek for the biggest breakpoint - var max = 0; - $scope.rule.offendersObj.forEach(function(offender) { - if (offender.pixels > max) { - max = offender.pixels; - } - }); - max = Math.max(max + 100, 1400); - - // We group offenders 10px by 10px - var GROUP_SIZE = 20; - - // Generate an empty array of values - $scope.breakpointsLabels = []; - $scope.breakpointsData = [[]]; - for (var i = 0; i <= max / GROUP_SIZE; i++) { - $scope.breakpointsLabels[i] = ''; - $scope.breakpointsData[0][i] = 0; - } - - // Fill it with results - $scope.rule.offendersObj.forEach(function(offender) { - var group = Math.floor((offender.pixels + 1) / GROUP_SIZE); - - if ($scope.breakpointsLabels[group] !== '') { - $scope.breakpointsLabels[group] += '/'; - } - $scope.breakpointsLabels[group] += offender.breakpoint; - - $scope.breakpointsData[0][group] += offender.count; - }); - - $scope.breakpointsSeries = ['Number of CSS rules per breakpoint']; - $scope.breakpointsColours = ['#9c4274']; - $scope.breakpointsOptions = { - scales: { - xAxes: [{ - gridLines: { - display:false - } - }], - yAxes: [{ - gridLines: { - display:false - } - }] - }, - tooltips: { - enabled: false - }, - - elements: { - point: { - radius: 0 - } - } - }; - } } $scope.backToDashboard = function() { diff --git a/front/src/js/controllers/timelineCtrl.js b/front/src/js/controllers/timelineCtrl.js deleted file mode 100644 index e23fee1..0000000 --- a/front/src/js/controllers/timelineCtrl.js +++ /dev/null @@ -1,190 +0,0 @@ -var timelineCtrl = angular.module('timelineCtrl', []); - -timelineCtrl.controller('TimelineCtrl', ['$scope', '$rootScope', '$routeParams', '$location', '$timeout', 'Menu', 'Results', 'API', function($scope, $rootScope, $routeParams, $location, $timeout, Menu, Results, API) { - $scope.runId = $routeParams.runId; - $scope.Menu = Menu.setCurrentPage('timeline', $scope.runId); - - function loadResults() { - // Load result if needed - if (!$rootScope.loadedResult || $rootScope.loadedResult.runId !== $routeParams.runId) { - Results.get({runId: $routeParams.runId, exclude: 'toolsResults'}, function(result) { - $rootScope.loadedResult = result; - $scope.result = result; - render(); - }); - } else { - $scope.result = $rootScope.loadedResult; - render(); - } - } - - function render() { - initFilters(); - initScriptFiltering(); - initExecutionTree(); - initTimeline(); - $timeout(initProfiler, 100); - } - - function initFilters() { - var hash = $location.hash(); - var filter = null; - - if (hash.indexOf('filter=') === 0) { - filter = hash.substr(7); - } - - $scope.warningsFilterOn = (filter !== null); - $scope.warningsFilters = { - queryWithoutResults: (filter === null || filter === 'queryWithoutResults'), - jQueryCallOnEmptyObject: (filter === null || filter === 'jQueryCallOnEmptyObject'), - eventNotDelegated: (filter === null || filter === 'eventNotDelegated'), - jsError: (filter === null || filter === 'jsError') - }; - } - - function initScriptFiltering() { - var offenders = $scope.result.rules.totalRequests.offendersObj.list.byType.js; - $scope.scripts = []; - - offenders.forEach(function(filePath) { - var shortPath = filePath; - - if (filePath.length > 100) { - shortPath = filePath.substr(0, 98) + '...'; - } - - var scriptObj = { - fullPath: filePath, - shortPath: shortPath - }; - - $scope.scripts.push(scriptObj); - }); - } - - function initExecutionTree() { - var originalExecutions = $scope.result.javascriptExecutionTree.children || []; - - // Detect the last event of all (before filtering) and read time - var lastEvent = originalExecutions[originalExecutions.length - 1]; - $scope.endTime = lastEvent.data.timestamp + (lastEvent.data.time || 0); - - // Filter - $scope.executionTree = []; - originalExecutions.forEach(function(node) { - - // Filter by script (if enabled) - if ($scope.selectedScript) { - if (node.data.backtrace && node.data.backtrace.indexOf($scope.selectedScript.fullPath + ':') === -1) { - return; - } - if (node.data.type === "jQuery loaded" || node.data.type === "jQuery version change") { - return; - } - } - - $scope.executionTree.push(node); - }); - } - - function initTimeline() { - - // Split the timeline into 200 intervals - var numberOfIntervals = 199; - $scope.timelineIntervalDuration = $scope.endTime / numberOfIntervals; - - // Pre-fill array of as many elements as there are milleseconds - var millisecondsArray = Array.apply(null, new Array($scope.endTime + 1)).map(Number.prototype.valueOf,0); - - // Create the milliseconds array from the execution tree - $scope.executionTree.forEach(function(node) { - if (node.data.time !== undefined) { - - // Ignore artefacts (durations > 100ms) - var time = Math.min(node.data.time, 100) || 1; - - for (var i=node.data.timestamp, max=node.data.timestamp + time ; i 0) { - break; - } - } - - return lineIndex; - }; - - - $scope.backToDashboard = function() { - $location.path('/result/' + $scope.runId); - }; - - $scope.testAgain = function() { - API.relaunchTest($scope.result); - }; - - loadResults(); - -}]); - -timelineCtrl.directive('scrollOnClick', ['$animate', '$timeout', function($animate, $timeout) { - return { - restrict: 'A', - link: function (scope, element, attributes) { - // When the user clicks on the timeline, find the right profiler line and scroll to it - element.on('click', function() { - var lineIndex = scope.findLineIndexByTimestamp(attributes.scrollOnClick); - var lineElement = angular.element(document.getElementById('line_' + lineIndex)); - - // Animate the background color to "flash" the row - lineElement.addClass('highlight'); - $timeout(function() { - $animate.removeClass(lineElement, 'highlight'); - scope.$digest(); - }, 50); - - - window.scrollTo(0, lineElement[0].offsetTop); - }); - } - }; -}]); diff --git a/front/src/js/directives/offendersDirectives.js b/front/src/js/directives/offendersDirectives.js index 434598a..956a9cb 100644 --- a/front/src/js/directives/offendersDirectives.js +++ b/front/src/js/directives/offendersDirectives.js @@ -93,510 +93,12 @@ }; }); - - function getJQueryContextButtonHTML(context, onASingleLine) { - if (context.length === 0) { - return 'Empty jQuery object'; - } - - if (context.length === 1) { - return getDomElementButtonHTML(context.elements[0], onASingleLine); - } - - var html = context.length + ' elements (' + getDomElementButtonHTML(context.elements[0], onASingleLine) + ', ' + getDomElementButtonHTML(context.elements[1], onASingleLine); - if (context.length === 3) { - html += ', ' + getDomElementButtonHTML(context.elements[0], onASingleLine); - } else if (context.length > 3) { - html += ' and ' + (context.length - 2) + ' more...'; - } - return html + ')'; - } - - function isJQuery(node) { - return node.data.type.indexOf('jQuery ') === 0; - } - - function getNonJQueryHTML(node, onASingleLine) { - var type = node.data.type; - - if (node.windowPerformance) { - switch (type) { - case 'documentScroll': - return '(triggering the scroll event on document)'; - - case 'windowScroll': - return '(triggering the scroll event on window)'; - - case 'window.onscroll': - return '(calling the window.onscroll function)'; - - default: - return ''; - } - } - - if (!node.data.callDetails) { - return ''; - } - - var args = node.data.callDetails.arguments; - var ctxt = node.data.callDetails.context; - - - switch (type) { - case 'getElementById': - case 'createElement': - return '' + args[0] + ''; - - case 'getElementsByClassName': - case 'getElementsByTagName': - case 'querySelector': - case 'querySelectorAll': - return '' + args[0] + ' on ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine); - - case 'appendChild': - return 'append ' + getDomElementButtonHTML(args[0], onASingleLine) + ' to ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine); - - case 'insertBefore': - return 'insert ' + getDomElementButtonHTML(args[0], onASingleLine) + ' into ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine) + ' before ' + getDomElementButtonHTML(args[1], onASingleLine); - - case 'addEventListener': - return 'bind ' + args[0] + ' to ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine); - - case 'getComputedStyle': - return getDomElementButtonHTML(args[0], onASingleLine) + (args[1] || ''); - - case 'error': - return args[0]; - - case 'jQuery - onDOMReady': - return '(function)'; - - case 'documentScroll': - return 'The scroll event just triggered on document'; - - case 'windowScroll': - return 'The scroll event just triggered on window'; - - case 'window.onscroll': - return 'The window.onscroll function just got called'; - - default: - return ''; - } - } - - function getJQueryHTML(node, onASingleLine) { - var type = node.data.type; - var unescapedArgs = node.data.callDetails.arguments; - var args = []; - var ctxt = node.data.callDetails.context; - - // escape HTML in args - for (var i = 0 ; i < 4 ; i ++) { - if (unescapedArgs[i] !== undefined) { - args[i] = escapeHTML(unescapedArgs[i]); - } - } - - if (type === 'jQuery loaded' || type === 'jQuery version change') { - return args[0]; - } - - switch (type) { - case 'jQuery - onDOMReady': - case 'jQuery - windowOnLoad': - return '(function)'; - - case 'jQuery - Sizzle call': - return '' + args[0] + ' on ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine); - - case 'jQuery - find': - if (ctxt && ctxt.length === 1 && ctxt.elements[0].type !== 'document') { - return '' + args[0] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } else { - return '' + args[0] + ''; - } - break; - - case 'jQuery - html': - if (args[0] !== undefined) { - return 'set content "' + args[0] + '" to ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } else { - return 'get content from ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - append': - return 'append ' + joinArgs(args) + ' to ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - appendTo': - return 'append ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to ' + args[0] + ''; - - case 'jQuery - prepend': - return 'prepend ' + joinArgs(args) + ' to ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - prependTo': - return 'prepend ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to ' + args[0] + ''; - - case 'jQuery - before': - return 'insert ' + joinArgs(args) + ' before ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - insertBefore': - return 'insert ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' before ' + args[0] + ''; - - case 'jQuery - after': - return 'insert ' + joinArgs(args) + ' after ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - insertAfter': - return 'insert ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' after ' + args[0] + ''; - - case 'jQuery - remove': - case 'jQuery - detach': - if (args[0]) { - return getJQueryContextButtonHTML(ctxt, onASingleLine) + ' filtered by ' + args[0] + ''; - } else { - return getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - empty': - case 'jQuery - clone': - case 'jQuery - unwrap': - case 'jQuery - show': - case 'jQuery - hide': - case 'jQuery - animate': - case 'jQuery - fadeIn': - case 'jQuery - fadeOut': - case 'jQuery - fadeTo': - case 'jQuery - fadeToggle': - case 'jQuery - slideDown': - case 'jQuery - slideUp': - case 'jQuery - slideToggle': - return getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - replaceWith': - return 'replace ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' with ' + args[0] + ''; - - case 'jQuery - replaceAll': - return 'replace ' + args[0] + ' with ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - text': - if (args[0] !== undefined) { - return 'set text "' + args[0] + '" to ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } else { - return 'get text from ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - wrap': - case 'jQuery - wrapAll': - return 'wrap ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' within ' + args[0] + ''; - - case 'jQuery - wrapInner': - return 'wrap the content of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' within ' + args[0] + ''; - - case 'jQuery - css': - case 'jQuery - attr': - case 'jQuery - prop': - if (isStringOfObject(args[0])) { - return 'set ' + args[0] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } else if (args[1]) { - return 'set ' + args[0] + ' : ' + args[1] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } else { - return 'get ' + args[0] + ' from ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - offset': - case 'jQuery - height': - case 'jQuery - innerHeight': - case 'jQuery - width': - case 'jQuery - innerWidth': - case 'jQuery - scrollLeft': - case 'jQuery - scrollTop': - case 'jQuery - position': - if (args[0]) { - return 'set ' + args[0] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } else { - return 'get from ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - outerHeight': - case 'jQuery - outerWidth': - if (args[0] && args[0] !== 'true') { - return 'set ' + args[0] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } else if (args[0] === 'true') { - return 'get from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' (with include margins option)'; - } else { - return 'get from ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - toggle': - if (args[0] === 'true') { - return getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to visible'; - } else if (args[0] === 'false') { - return getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to hidden'; - } else { - return getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - on': - case 'jQuery - one': - if (isStringOfObject(args[0])) { - return '' + args[0].replace(/"\(function\)"/g, '(function)') + ''; - } else if (args[1] && isPureString(args[1])) { - return 'bind ' + args[0] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by ' + args[1] + ''; - } else { - return 'bind ' + args[0] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - off': - if (args[0]) { - if (args[1]) { - return 'unbind ' + args[0] + ' from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by ' + args[1] + ''; - } else { - return 'unbind ' + args[0] + ' from ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - } else { - return 'unbind all events'; - } - break; - - case 'jQuery - live': - case 'jQuery - bind': - return 'bind ' + args[0] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - die': - case 'jQuery - unbind': - if (args[0]) { - return 'unbind ' + args[0] + ' from ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } else { - return 'unbind all events'; - } - break; - - case 'jQuery - delegate': - return 'bind ' + args[1] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by ' + args[0] + ''; - - case 'jQuery - undelegate': - if (args[0]) { - if (args[1]) { - return 'unbind ' + args[1] + ' from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by ' + args[0] + ''; - } else { - return 'unbind namespace ' + args[0] + ''; - } - } else { - return 'unbind all events'; - } - break; - - case 'jQuery - blur': - case 'jQuery - change': - case 'jQuery - click': - case 'jQuery - dblclick': - case 'jQuery - focus': - case 'jQuery - keydown': - case 'jQuery - keypress': - case 'jQuery - keyup': - case 'jQuery - mousedown': - case 'jQuery - mouseenter': - case 'jQuery - mouseleave': - case 'jQuery - mousemove': - case 'jQuery - mouseout': - case 'jQuery - mouseover': - case 'jQuery - mouseup': - case 'jQuery - resize': - case 'jQuery - scroll': - case 'jQuery - select': - case 'jQuery - submit': - if (args[0]) { - return 'bind on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } else { - return 'triggered on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - error': - case 'jQuery - focusin': - case 'jQuery - focusout': - case 'jQuery - hover': - case 'jQuery - load': - case 'jQuery - unload': - return 'bind on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - removeAttr': - case 'jQuery - removeProp': - return 'remove ' + args[0] + ' from ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - val': - if (args[0]) { - return 'set value ' + args[0] + ' to ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } else { - return 'get value from ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - hasClass': - case 'jQuery - addClass': - case 'jQuery - removeClass': - return '' + args[0] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - toggleClass': - if (args[0]) { - if (args[1]) { - return 'toggle ' + args[0] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to ' + args[1] + ''; - } else { - return 'toggle ' + args[0] + ' on ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - } else { - return 'magic no-argument toggleClass'; - } - break; - - case 'jQuery - children': - if (args[0]) { - return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' filtered by ' + args[0] + ''; - } else { - return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - closest': - if (args[1]) { - return 'closest ' + args[0] + ' from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' in context ' + args[1] + ''; - } else { - return 'closest ' + args[0] + ' from ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - next': - case 'jQuery - nextAll': - if (args[0]) { - return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching ' + args[0] + ''; - } else { - return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - nextUntil': - if (args[0]) { - if (args[1]) { - return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until ' + args[0] + ' and matching ' + args[1] + ''; - } else { - return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until ' + args[0] + ''; - } - } else { - return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - offsetParent': - return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - - case 'jQuery - prev': - case 'jQuery - prevAll': - if (args[0]) { - return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching ' + args[0] + ''; - } else { - return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - prevUntil': - if (args[0]) { - if (args[1]) { - return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until ' + args[0] + ' and matching ' + args[1] + ''; - } else { - return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until ' + args[0] + ''; - } - } else { - return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - parent': - case 'jQuery - parents': - if (args[0]) { - return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching ' + args[0] + ''; - } else { - return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - parentsUntil': - if (args[0]) { - if (args[1]) { - return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until ' + args[0] + ' and matching ' + args[1] + ''; - } else { - return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until ' + args[0] + ''; - } - } else { - return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - case 'jQuery - siblings': - if (args[0]) { - return 'near ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching ' + args[0] + ''; - } else { - return 'near ' + getJQueryContextButtonHTML(ctxt, onASingleLine); - } - break; - - default: - return ''; - } - } - - function escapeHTML(html) { - var entityMap = { - "&": "&", - "<": "<", - ">": ">", - '"': '"', - "'": ''', - "/": '/' + offendersDirectives.filter('lastDOMNode', function() { + return function(str) { + var splited = str.split(' > '); + return splited[splited.length - 1]; }; - - return String(html).replace(/[&<>"'\/]/g, function (s) { - return entityMap[s]; - }); - } - - function joinArgs(args) { - var html = '' + args[0] + ''; - if (args[1]) { - html += ', ' + args[1] + ''; - if (args[2]) { - html += ', ' + args[2] + ''; - if (args[3]) { - html += ', and more...'; - } - } - } - return html; - } - - function isStringOfObject(str) { - return typeof str === 'string' && str[0] === '{' && str[str.length - 1] === '}'; - } - - function isPureString(str) { - return typeof str === 'string' && str[0] !== '{' && str !== '(function)' && str !== '[Object]' && str !== '[Array]' && str !== 'true' && str !== 'false' && str !== 'undefined' && str !== 'unknown'; - } - - function getTimelineParamsHTML(node, onASingleLine) { - if (isJQuery(node)) { - return getJQueryHTML(node, onASingleLine); - } else { - return getNonJQueryHTML(node, onASingleLine); - } - } + }); function getBacktraceHTML(backtrace) { var html = ''; @@ -687,145 +189,6 @@ return out; } - function getTimelineDetailsHTML(node) { - var html = ''; - - if (node.data.type != 'jQuery loaded' && node.data.type != 'jQuery version change' && !node.windowPerformance) { - if (node.warning || node.error) { - html += '
'; - } else { - html += '
'; - } - - html += '
'; - html += '
'; - - if (node.data.callDetails.context && node.data.callDetails.context.length === 0) { - html += '

Called on 0 jQuery element

Useless function call, as the jQuery object is empty.

'; - } else if (node.eventNotDelegated) { - html += '

This binding should use Event Delegation instead of binding each element one by one.

'; - } - - if (node.data.resultsNumber === 0) { - html += '

The query returned 0 results. Could it be unused or dead code?

'; - } else if (node.data.resultsNumber > 0) { - html += '

The query returned ' + node.data.resultsNumber + ' ' + (node.data.resultsNumber > 1 ? 'results' : 'result') + '.

'; - } - - if (node.data.backtrace) { - html += '

Backtrace

'; - html += '
'; - html += getBacktraceHTML(node.data.backtrace); - html += '
'; - } - - html += '
'; - } - - return html; - } - - - offendersDirectives.directive('profilerLine', ['$filter', function($filter) { - - var numberWithCommas = $filter('number'); - - function getProfilerLineHTML(index, node) { - return '
' + (index + 1) + '
' + - '
' + node.data.type + (node.children ? '
' + recursiveChildrenHTML(node) + '
' : '') + '
' + - '
' + getTimelineParamsHTML(node, false) + '
' + - '
' + getTimelineDetailsHTML(node) + '
' + - '
' + numberWithCommas(node.data.timestamp, 0) + ' ms
'; - } - - function recursiveChildrenHTML(node) { - var html = ''; - - if (node.children) { - node.children.forEach(function(child) { - html += '
' + child.data.type + '
' + getTimelineParamsHTML(child, true) + '
' + recursiveChildrenHTML(child) + '
'; - }); - } - - return html; - } - - function onDetailsClick(row) { - // Close if it's already open - if (row.classList.contains('showDetails')) { - closeDetails(row); - return; - } - - // Close any other open details overlay - var openOnes = document.getElementsByClassName('showDetails'); - if (openOnes.length > 0) { - openOnes[0].classList.remove('showDetails'); - } - - // Make it appear - row.classList.add('showDetails'); - - // Bind the close button - row.querySelector('.closeBtn').addEventListener('click', function() { - closeDetails(row); - }); - } - - function closeDetails(row) { - row.classList.remove('showDetails'); - - // Unbind the close button - row.querySelector('.closeBtn').removeEventListener('click', closeDetails); - } - - return { - restrict: 'E', - scope: { - index: '=', - node: '=' - }, - template: '
', - replace: true, - link: function(scope, element) { - - if (scope.node.error) { - element.addClass('jsError'); - } else if (scope.node.windowPerformance) { - element.addClass('windowPerformance'); - } - - element.append(getProfilerLineHTML(scope.index, scope.node)); - element[0].id = 'line_' + scope.index; - - if (scope.node.warning) { - element[0].classList.add('warning'); - - if (scope.node.queryWithoutResults) { - element[0].classList.add('queryWithoutResults'); - } - - if (scope.node.jQueryCallOnEmptyObject) { - element[0].classList.add('jQueryCallOnEmptyObject'); - } - - if (scope.node.eventNotDelegated) { - element[0].classList.add('eventNotDelegated'); - } - } - - - // Bind click on the details icon - var detailsIcon = element[0].querySelector('.details div'); - if (detailsIcon) { - detailsIcon.addEventListener('click', function() { - onDetailsClick(this.parentNode.parentNode); - }); - } - } - }; - }]); - function shortenUrl(url, maxLength) { if (!maxLength) { maxLength = 110; @@ -935,4 +298,10 @@ }; }); + offendersDirectives.filter('roundNbr', function() { + return function(nbr) { + return Math.round(nbr); + }; + }); + })(); diff --git a/front/src/js/services/apiService.js b/front/src/js/services/apiService.js index 9162eca..01c6409 100644 --- a/front/src/js/services/apiService.js +++ b/front/src/js/services/apiService.js @@ -21,9 +21,9 @@ apiService.factory('API', ['$location', 'Runs', 'Results', function($location, R }; - if (settings.domainsBlackOrWhite === 'black') { + if (settings.domainsBlockOrAllow === 'block') { runObject.blockDomain = this.parseDomains(settings.domains); - } else if (settings.domainsBlackOrWhite === 'white') { + } else if (settings.domainsBlockOrAllow === 'allow') { var allowedDomains = this.parseDomains(settings.domains); if (allowedDomains.length > 0) { runObject.allowDomain = allowedDomains; diff --git a/front/src/js/services/menuService.js b/front/src/js/services/menuService.js index 7b56c74..9ef3c37 100644 --- a/front/src/js/services/menuService.js +++ b/front/src/js/services/menuService.js @@ -22,9 +22,6 @@ menuService.factory('Menu', ['$location', function($location) { case 'dashboard': $location.path('/result/' + currentRunId); break; - case 'timeline': - $location.path('/result/' + currentRunId + '/timeline'); - break; default: console.err('Undefined Menu.changePage() destination'); } diff --git a/front/src/js/services/settingsService.js b/front/src/js/services/settingsService.js index 81f9f5b..741280c 100644 --- a/front/src/js/services/settingsService.js +++ b/front/src/js/services/settingsService.js @@ -6,7 +6,7 @@ settingsService.factory('Settings', ['localStorageService', function(localStorag getMergedSettings: function() { var defaultSettings = { - device: 'desktop', + device: 'phone', showAdvanced: false }; diff --git a/front/src/less/about.less b/front/src/less/about.less index 67de419..3c20359 100644 --- a/front/src/less/about.less +++ b/front/src/less/about.less @@ -8,5 +8,9 @@ } .about a { - color: #FFF; + color: #fff; +} + +.sponsor { + color: #ffa319; } \ No newline at end of file diff --git a/front/src/less/dashboard.less b/front/src/less/dashboard.less index 974ec04..6fd856f 100644 --- a/front/src/less/dashboard.less +++ b/front/src/less/dashboard.less @@ -7,16 +7,8 @@ } .summary .globalScore { - display: table; - width: 60%; margin: 3em auto; - > div { - display: table-cell; - width: 50%; - vertical-align: middle; - } - .globalGrade { margin: 0.5 auto; width: 2.5em; @@ -24,12 +16,10 @@ line-height: 2.5em; border-radius: 0.5em; font-size: 3em; - font-weight: bold; vertical-align: middle; } .on100 { font-size: 1.2em; - font-weight: bold; margin: 0.5em 0 1em; } @@ -61,40 +51,77 @@ top: 1.5em; left: 0.9em; } + + @media (min-width: 820px) { + width: 65%; + display: table; + + > div { + display: table-cell; + width: 50%; + vertical-align: middle; + } + } } .summary .notations { + width: 100%; display: table; - width: 80%; - margin: 0 10% 1.5em; - border-spacing: 1em; + margin: 0 0 1.5em; + border-spacing: 0 1em; + + @media (min-width: 820px) { + width: 80%; + margin: 0 10% 1.5em; + border-spacing: 1em; + } } .summary .notations > div { display: table-row; } .summary .notations > div > div { - display: table-cell; - height: 2.5em; vertical-align: middle; + + @media (min-width: 820px) { + display: table-cell; + height: 2.5em; + } } .summary .notations .category { - font-weight: bold; - text-align: center; - width: 20%; + font-size: 1.2em; + width: 50%; + float: left; + text-align: left; + margin: 0.5em 0.25em; + + @media (min-width: 820px) { + width: 20%; + text-align: center; + float: none; + } } .summary .notations .criteria { font-weight: normal; - width: 75%; + + @media (min-width: 820px) { + width: 75%; + } } .A, .B, .C, .D, .E, .F, .NA { .summary .notations &.categoryScore { width: 2.5em; max-width: 2.5em; min-width: 2.5em; - font-size: 2em; + margin: 0.2em; + font-size: 1.5em; text-align: center; border-radius: 0.5em; - font-weight: bold; + float: right; + + @media (min-width: 820px) { + float: none; + font-size: 2em; + } } .summary .notations .grade & { width: 1em; @@ -113,12 +140,12 @@ color: inherit; } > a:hover > div { - background: #EBD8E2; + background: #d8ebe0; cursor: pointer; &.info { background: #FFF; - .icon-question { - color: #EBD8E2; + svg { + fill: #d8ebe0; } } } @@ -134,24 +161,32 @@ } .summary .notations .criteria .result { width: 18%; - font-weight: bold; white-space: nowrap; text-align: center; vertical-align: middle; } -.summary .notations .warning .label, .summary .notations .warning .result, .summary .notations .icon-warning { +.summary .notations .warning .label, .summary .notations .warning .result { color: #FF1919; } -.summary .notations .criteria .info { - width: 2%; - text-align: center; - vertical-align: middle; - background: #FFF; - padding-left: 0.1em; - padding-right: 0.1em; +.summary .notations .icon-warning svg { + fill: #FF1919; + margin: -2px 0; } -.summary .notations .criteria .icon-question { - color: transparent; +.summary .notations .criteria .info { + display: none; + + @media (min-width: 820px) { + display: table-cell; + width: 2%; + text-align: center; + vertical-align: middle; + background: #FFF; + padding-left: 0.1em; + padding-right: 0.1em; + } +} +.summary .notations .criteria .info svg { + fill: transparent; } .summary .fromShare { @@ -159,8 +194,13 @@ } .summary .tweet { + display: none; + @media (min-width: 820px) { + display: block; + } + .tweetText { - color: #413; + color: #212240; background: #F2F2F2; border: none; width: 25em; @@ -169,7 +209,7 @@ box-shadow: 0.05em 0.1em 0 0 #999; } .tweetButton, .linkedinButton { - color: #413; + color: #212240; background: #F2F2F2; margin-right: 0; &:hover { @@ -185,7 +225,7 @@ .summary .sponsor { font-size: 0.9em; margin-bottom: 4em; - color: #413; + color: #ffa319; a { color: inherit; } diff --git a/front/src/less/icons.less b/front/src/less/icons.less deleted file mode 100644 index 6d596a7..0000000 --- a/front/src/less/icons.less +++ /dev/null @@ -1,132 +0,0 @@ -// Generated by grunt-webfont -// Based on https://github.com/endtwist/fontcustom/blob/master/lib/fontcustom/templates/fontcustom.css - - -@font-face { - font-family:"icons"; - src:url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); - font-weight:normal; - font-style:normal; -} - - -// Bootstrap Overrides -[class^="icon-"]:before, -[class*=" icon-"]:before { - font-family:"icons"; - display:inline-block; - vertical-align:middle; - line-height:1; - font-weight:normal; - font-style:normal; - speak:none; - text-decoration:inherit; - text-transform:none; - text-rendering:auto; - -webkit-font-smoothing:antialiased; - -moz-osx-font-smoothing:grayscale; -} - -// Mixins - -.icon-arrow-left3, -.icon-bars, -.icon-lab, -.icon-list, -.icon-loop, -.icon-mobile, -.icon-question, -.icon-screen, -.icon-tablet, -.icon-warning { - &:before { - font-family:"icons"; - display:inline-block; - font-weight:normal; - font-style:normal; - text-decoration:inherit; - } -} - - -// Icons - -.icon-arrow-left3 { - &:before { - content:"\f101"; - } - -} - - -.icon-bars { - &:before { - content:"\f102"; - } - -} - - -.icon-lab { - &:before { - content:"\f103"; - } - -} - - -.icon-list { - &:before { - content:"\f104"; - } - -} - - -.icon-loop { - &:before { - content:"\f105"; - } - -} - - -.icon-mobile { - &:before { - content:"\f106"; - } - -} - - -.icon-question { - &:before { - content:"\f107"; - } - -} - - -.icon-screen { - &:before { - content:"\f108"; - } - -} - - -.icon-tablet { - &:before { - content:"\f109"; - } - -} - - -.icon-warning { - &:before { - content:"\f10a"; - } - -} - diff --git a/front/src/less/index.less b/front/src/less/index.less index 6892452..e212fcd 100644 --- a/front/src/less/index.less +++ b/front/src/less/index.less @@ -22,7 +22,7 @@ background: #e74c3c; } &.disabled { - background: #deaca6; + background: #f1bd70; &:focus { color: #ddd; } @@ -59,27 +59,34 @@ text-decoration: none; font-size: 0.8em; + > svg { + margin: 0.6em 0 0.3em; + fill: #fff; + } + &.active { color: #ffa319; border: 2px solid #ffa319; padding: 0; + + > svg { + fill: #ffa319; + } } &:hover { color: #ffa319; - } - > div { - margin: 0.2em 0 0.1em; - font-size: 3em; + > svg { + fill: #ffa319; + } } } } .settingsTooltip { position: relative; - span { - font-size: 0.8em; + svg { vertical-align: text-top; } @@ -94,6 +101,8 @@ border-radius: 1em; border: 2px solid #ffa319; white-space: normal; + word-break: break-all; + word-break: break-word; z-index: 2; } @@ -171,7 +180,7 @@ width: 50%; margin: 6em auto 0; font-size: 0.9em; - color: #413; + color: #8abfaf; > div { width: 33.3%; @@ -187,7 +196,6 @@ input[type=submit], input.url { height: 2em; border: 0 solid; border-radius: 0.5em; - box-shadow: 0.1em 0.2em 0 0 #5e2846; outline: none; } input[type=submit]:hover { diff --git a/front/src/less/main.less b/front/src/less/main.less index 3d5bbe3..48753a9 100644 --- a/front/src/less/main.less +++ b/front/src/less/main.less @@ -1,13 +1,14 @@ -@import "icons.less"; - html { - margin: 100px 50px; + margin: 35px 5px; + @media (min-width: 640px) { + margin: 100px 50px; + } } body { margin: 0 auto; max-width: 1280px; - background: #9c4274; + background: #212240; color: #fff; font-size: 16px; text-align: center; @@ -21,41 +22,61 @@ input[type=submit] { cursor: pointer; } -h1 span { - display: inline-block; - height: 1em; - width: 1em; - color: #ffa319; +h1 { + font-weight: 200; } .resultsMenu { margin-top: 2em; } .resultsMenu .menuItem { + font-size: 0.8em; display: inline-block; - margin: 1em; - width: 8em; + width: 7em; height: 7em; color: #fff; - border: 3px solid #fff; - border-radius: 0.5em; cursor: pointer; text-decoration: none; + + @media (min-width: 640px) { + font-size: 1em; + margin: 1em; + width: 8em; + border: 2px solid #fff; + border-radius: 0.5em; + } + + svg { + fill: #fff; + } + &.back, &.restart { - color: #413; - border-color: #413; + color: #fff; + border-color: #fff; } } .resultsMenu .menuItem div { padding-top: 0.5em; font-size: 3em; } +.resultsMenu svg { + display: block; + margin: 1.2em auto 0.2em; +} .resultsMenu .active, .resultsMenu .menuItem.active:hover { color: #ffa319; border-color: #ffa319; + + svg { + fill: #ffa319; + } } .resultsMenu .menuItem:hover { color: #ffa319; + + svg { + fill: #ffa319; + } } .resultsMenu span { position: relative; @@ -149,7 +170,7 @@ a.linkButton { } } -.screenshotWrapper.desktop { +.screenshotWrapper.desktop, .screenshotWrapper.desktop-hd { border: 0.2em solid #AAA; padding: 0.5em; border-top-left-radius: 0.4em; @@ -238,9 +259,30 @@ a.linkButton { } } +.table { + display: table; + width: 100%; + border-spacing: 0.25em; +} +.table > div, +.table > a { + display: table-row; +} +.table > .headers > div { + font-weight: bold; + padding: 0.5em 1em; +} +.table > div > div, +.table > a > div { + padding: 0.1em 1em; + background: #f2f2f2; + display: table-cell; + text-align: left; +} + .footer { padding: 3em; - color: #413; + color: #fff; a { color: inherit; } @@ -255,6 +297,6 @@ a.linkButton { } } -[class^="icon-"]:before, [class*=" icon-"]:before { - vertical-align: baseline; +.homeSponsor { + color: #ffa319; } \ No newline at end of file diff --git a/front/src/less/queue.less b/front/src/less/queue.less index f8d2cb2..266fbee 100644 --- a/front/src/less/queue.less +++ b/front/src/less/queue.less @@ -6,4 +6,19 @@ .statusSubMessage { font-size: 0.8em; margin-bottom: 6em; +} + +.progressBarEmpty { + width: 90%; + max-width: 300px; + margin: 1em auto; + padding: 0.05em; + border: 1px solid #ffa319; +} + +.progressBarFilled { + width: 5%; + height: 0.5em; + background: #ffa319; + transition: width 3s ease-out; } \ No newline at end of file diff --git a/front/src/less/rule.less b/front/src/less/rule.less index c139a34..43cae38 100644 --- a/front/src/less/rule.less +++ b/front/src/less/rule.less @@ -3,23 +3,27 @@ } .rule .ruleTable { - display: table; border-spacing: 1em; width: 90%; margin: 2em auto; background: #f2f2f2; border: 1px dashed #666; border-radius: 0.5em; - > div { - display: table-cell; - vertical-align: middle; - } - .left { - width: 33%; - font-weight: bold; - } - .right { - width: 67%; + + @media (min-width: 820px) { + display: table; + + > div { + display: table-cell; + vertical-align: middle; + } + + .left { + width: 33%; + } + .right { + width: 67%; + } } } @@ -29,7 +33,7 @@ height: 2em; width: 2em; border-radius: 0.5em; - margin: 0 auto 0.5em; + margin: 0 auto 0.25em; } .rule h3 { @@ -75,17 +79,23 @@ border-spacing: 0 0.25em; margin: 0 auto; min-width: 10%; - max-width: 90%; + font-size: 0.875em; + + @media (min-width: 820px) { + max-width: 90%; + font-size: 1em; + } + > div { display: table-row; > div { display: table-cell; background: #f2f2f2; - padding: 0 1em; + padding: 0 0.25em; word-wrap: break-word; word-break: break-all; &:hover { - background: #EBD8E2; + background: #d8ebe0; } } } diff --git a/front/src/less/screenshot.less b/front/src/less/screenshot.less index a214659..5bc58e3 100644 --- a/front/src/less/screenshot.less +++ b/front/src/less/screenshot.less @@ -3,6 +3,14 @@ } .screenshot .screenshotWrapper { - font-size: 2.08333333333333em; + font-size: 1.2em; margin-bottom: 0.5em; + + @media (min-width: 420px) { + font-size: 1.6em; + } + + @media (min-width: 640px) { + font-size: 2.08333333333333em; + } } \ No newline at end of file diff --git a/front/src/less/timeline.less b/front/src/less/timeline.less deleted file mode 100644 index d59beac..0000000 --- a/front/src/less/timeline.less +++ /dev/null @@ -1,387 +0,0 @@ -/* Timeline colors, related to Window Performances */ -@domCreationColor: #FF6600; -@domCreationBg: #FFE0CC; -@domContentLoadedColor: #A7E846; -@domContentLoadedBg: #E0FFD1; -@domContentLoadedEndColor: #7ECCCC; -@domContentLoadedEndBg: #D8F0F0; -@domCompleteColor: #C2A3FF; -@domCompleteBg: #EDE3FF; -@domInteractiveColor: #FFE433; -@domInteractiveBg: #FFFCCC; - -.execution { - text-align: center; -} - -.selectScript { - padding-bottom: 2em; - font-size: 0.9em; - - select { - max-width: 30em; - } - - &.empty { - font-size: 0.8em; - - select { - width: 10em; - } - } -} - -.timeline { - margin: 2em 0 5em; -} -.timeline .chart { - position: relative; - width: 100%; - border-bottom: 1px solid #000; -} -.timeline .startTime, .timeline .endTime { - position: absolute; - bottom: 0.5em; - font-size: 0.8em; -} -.timeline .startTime { - left: 0em; -} -.timeline .endTime { - right: 0em; -} -.timeline .chartPoints { - display: table; - height: 100px; - width: 99%; - margin: 0 auto; -} -.timeline .interval { - display: table-cell; - position: relative; - height: 100px; - width: 0.5%; -} -.timeline .interval .color { - position: absolute; - bottom: 0; - width: 100%; - - &.clickable { - cursor: pointer; - } -} -.timeline div.interval:hover { - background: #9C4274; -} -.timeline .interval:hover .color { - background: #F04DA7; -} -.timeline .domComplete.interval { - background: @domCompleteBg; -} -.timeline .domComplete .color { - background: @domCompleteColor; -} -.timeline .domContentLoadedEnd.interval { - background: @domContentLoadedEndBg; -} -.timeline .domContentLoadedEnd .color { - background: @domContentLoadedEndColor; -} -.timeline .domContentLoaded.interval { - background: @domContentLoadedBg; -} -.timeline .domContentLoaded .color { - background: @domContentLoadedColor; -} -.timeline .domInteractive.interval { - background: @domInteractiveBg; -} -.timeline .domInteractive .color { - background: @domInteractiveColor; -} -.timeline .domCreation.interval { - background: @domCreationBg; -} -.timeline .domCreation .color { - background: @domCreationColor; -} -.timeline .tooltip.detailsOverlay { - position: absolute; - display: none; - width: auto; - padding: 0.5em 1em; - top: -1.5em; - right: 1em; -} -.timeline .interval:hover .tooltip { - display: block; -} - -.timeline .legend { - display: table; - width: 100%; - margin-top: 1em; -} -.timeline .legend > div { - display: table-row; -} -.timeline .legend > div > div { - position: relative; - display: table-cell; - margin-top: 1em; -} -.timeline .titles { - font-weight: bold; -} -.timeline .titles > div { - padding: 0 1em 0 2em; -} -.timeline .tips { - font-size: 0.7em; -} -.timeline .tips > div { - padding: 1em 1em 0 0; -} -.timeline .legend .color { - display: block; - position: absolute; - left: 0; - height: 1.5em; - width: 1.5em; - border-radius: 0.2em; -} - -.filters { - margin: 1em auto; - padding: 0.5em; - min-width: 30em; - width: 30%; - border: 1px dotted #aaa; - text-align: left; -} - -.subFilters { - margin-left: 3em; - font-size: 0.9em; -} - -.table { - display: table; - width: 100%; - border-spacing: 0.25em; -} - -.table > div, -.table > a { - display: table-row; -} - -.table > .headers > div { - font-weight: bold; - padding: 0.5em 1em; -} - -.table > div > div, -.table > a > div { - padding: 0.1em 1em; - background: #f2f2f2; - display: table-cell; - text-align: left; -} -.table > div.jsError > .type, .table > div.jsError > .value { - color: #e74c3c; - font-weight: bold; -} -.table > div.windowPerformance > div, .table > div.windowPerformance > div.startTime { - background: #EBD8E2; -} -.table > div.showingDetails > div { - background: #f1c40f; -} - -.table > div.highlight { - > div.startTime { - background-color: #C0F090; - } -} -.table > div.highlight-remove { - transition: 3s; - - > div.startTime { - transition: background-color 3s ease-in; - } -} - -.table > div > .index { - color: #bbb; - word-break: normal; -} - -.table > div > .type { - white-space:nowrap; -} - -.table .children { - margin-top: 0.2em; - font-size: 0.8em; - line-height: 1.6em; -} - -.table .child { - margin-left: 0.5em; - - > .child { - margin-left: 1em; - } - - &:before { - content: "↳"; - } - - .childArgs { - display: none; - } - - span { - position: relative; - } - - span:hover { - background: #EBD8E2; - - div { - display: inline-block; - } - - .childArgs { - display: block; - position: absolute; - padding: 0 1em 0 2em; - left: 100%; - top: 0; - background: #EBD8E2; - line-height: 1.3em; - height: 1.3em; - z-index: 2; - } - } -} - -.table .showingDetails .child span:hover { - background: inherit; - - .childArgs { - display: none; - } -} - -.table > div > .value { - width: 70%; - word-break: break-all; -} - -.table > div > .details { - position: relative; -} -.table .details .icon-question { - color: #f1c40f; - cursor: pointer; -} -.table .icon-warning { - display: inline-block; - width: 0.8em; -} - -.detailsOverlay { - display: none; - position: absolute; - right: 3em; - top: -3em; - width: 45em; - min-height: 1em; - padding: 0 1em 1em; - background: #fff; - border: 2px solid #f1c40f; - border-radius: 0.5em; - z-index: 2; -} -@media screen and (max-width: 1024px) { - .detailsOverlay { - width: 25em; - } -} -.showDetails .detailsOverlay { - display: block; -} -.detailsOverlay .closeBtn { - position: absolute; - top: 0.5em; - right: 0.5em; - color: #f1c40f; - cursor: pointer; -} -.detailsOverlay .advice { - color: #e74c3c; - font-weight: bold; -} -.detailsOverlay .trace { - word-break: break-all; -} - -.table > div > .duration, .table > div > .startTime { - text-align: center; - white-space:nowrap; -} -.table > div > .startTime.domComplete { - background: @domCompleteBg; -} -.table > div > .startTime.domContentLoadedEnd { - background: @domContentLoadedEndBg; -} -.table > div > .startTime.domContentLoaded { - background: @domContentLoadedBg; -} -.table > div > .startTime.domInteractive { - background: @domInteractiveBg; -} -.table > div > .startTime.domCreation { - background: @domCreationBg; -} -.execution .icon-warning { - color: #e74c3c; - cursor: pointer; -} -.queryWithoutResultsFilterOn { - > div { - display: none; - &.queryWithoutResults { - display: table-row; - } - } -} -.jQueryCallOnEmptyObjectFilterOn { - > div { - display: none; - &.jQueryCallOnEmptyObject { - display: table-row; - } - } -} -.eventNotDelegatedFilterOn { - > div { - display: none; - &.eventNotDelegated { - display: table-row; - } - } -} -.jsErrorFilterOn { - > div { - display: none; - &.jsError { - display: table-row; - } - } -} \ No newline at end of file diff --git a/front/src/main.html b/front/src/main.html index 3748747..5959394 100644 --- a/front/src/main.html +++ b/front/src/main.html @@ -4,6 +4,7 @@ Yellow Lab Tools + @@ -14,21 +15,17 @@ - - - - - +
@@ -30,7 +42,7 @@

This rule reached the abnormality threshold, which means there is a real problem you should care about.

-

+

@@ -39,8 +51,13 @@
+
+ {{offender.url}} + an iframe without URL +
+
- {{offender.id}}: {{offender.occurrences}} occurrences + {{offender.id}}: {{offender.count}} occurrences
@@ -97,9 +114,9 @@
- inline CSS - - : + inline CSS + + :
@@ -118,7 +135,7 @@
- {{offender.property}} {{offender.message}} + {{offender.property}} {{offender.message}}
show hide @@ -132,7 +149,12 @@
-
+
+ + (offset: {{offender.offset | roundNbr}}px) +
+ +
@@ -148,7 +170,7 @@
- + cached for {{offender.ttlWithUnit}} {{offender.unit}}
@@ -158,13 +180,21 @@
- {{offender}} + {{offender.version}}
({{offender.size | bytes}})
+ +
+ {{offender.domain}} sends over {{offender.httpVersion}} +
+ +
+ {{offender.domain}} uses {{offender.tlsVersion}} and seems to be on the critical path +
@@ -173,8 +203,8 @@

in - - inline CSS + + inline CSS

@@ -229,19 +259,6 @@
-
-

The table below shows the interactions between the JavaScript and the DOM on a scroll event.

-
-
-
-
Type
-
Params
-
-
- -
-
-

This is the colors palette, sized by total occurrences:

@@ -267,6 +284,36 @@
+
+
+

+ from + getElementById() + getElementsByTagName() + getElementsByClassName() + querySelector() or querySelectorAll() + appendChild() or insertBefore() + added nodes + removed nodes + attribute changes + addEventListener() +

+
+
+
#{{access.id}}
+
{{access.tag}} on {{access.node | lastDOMNode}}
+
.{{access.class}} on {{access.node | lastDOMNode}}
+
{{access.selector}} on {{access.node | lastDOMNode}}
+
{{access.append | lastDOMNode}} added to {{access.node | lastDOMNode}}
+
{{access.node}} added to {{access.target}}
+
{{access.node}} removed from {{access.target}}
+
{{access.attribute}} changed on {{access.node}}
+
{{access.eventType}} on {{access.path | lastDOMNode}}
+
+
+
+
+

{{rule.value | bytes}} could be saved on

@@ -276,32 +323,40 @@
-

Current weight: {{image.original | bytes}}

-

Current weight: {{image.original | bytes}} ({{image.afterCompression | bytes}} gzipped)

-

With a lossless optimization:
{{image.afterOptimizationAndCompression | bytes}} gzipped (-{{image.gain | bytes}} gzipped)

-

With a lossless optimization:
{{image.lossless | bytes}} (-{{image.gain | bytes}})

-

With a lossy optimization:
{{image.afterOptimizationAndCompression | bytes}} gzipped (-{{image.gain | bytes}} gzipped)

-

With a lossy optimization:
{{image.lossy | bytes}} (-{{image.gain | bytes}})

+

Current weight: {{image.originalWeigth | bytes}}

+

Current weight: {{image.originalWeigth | bytes}} ({{image.originalCompressedWeight | bytes}} compressed)

+ +

With a lossless optimization:
{{image.afterOptimizationAndCompression | bytes}} compressed (-{{image.gain | bytes}} compressed)

+

With a lossless optimization:
{{image.lossless | bytes}} (-{{image.gain | bytes}})

+ +

With a lossy optimization:
{{image.afterOptimizationAndCompression | bytes}} compressed (-{{image.gain | bytes}} compressed)

+

With a lossy optimization:
{{image.lossy | bytes}} (-{{image.gain | bytes}})

-
+

{{rule.value | bytes}} could be saved on

File
Current weight
-
Gzipped
+
Gzip
+
Brotli
Gain
- +
-
{{file.original | bytes}}
-
{{file.gzipped | bytes}}
+
{{file.originalSize | bytes}}
+ +
already gzipped
+
{{file.gzipped | bytes}}
+ +
{{file.brotlified | bytes}}
+
-{{file.gain | bytes}}
@@ -318,12 +373,12 @@
- +
-
{{file.original | bytes}} (gzipped)
-
{{file.original | bytes}} ({{file.afterCompression | bytes}} gzipped)
-
{{file.afterOptimizationAndCompression | bytes}} (gzipped)
-
{{file.optimized | bytes}} ({{file.afterOptimizationAndCompression | bytes}} gzipped)
+
{{file.originalWeigth | bytes}} (compressed)
+
{{file.originalWeigth | bytes}} ({{file.originalCompressedWeight | bytes}} compressed)
+
{{file.afterOptimizationAndCompression | bytes}} (compressed)
+
{{file.optimized | bytes}} ({{file.afterOptimizationAndCompression | bytes}} compressed)
-{{file.gain | bytes}}
@@ -357,51 +412,8 @@ -
-
-

-

Try to inline these styles in the head of the HTML or to merge them with other files.

-

Try to inline these scripts in the HTML or merge them with other files.

-

Try to inline these images (with base64 encoding for most image types except SVG that don't need base64 encoding). You can also create sprites.

-
-
-
Preview
-
File
-
Weight (bytes)
-
-
-
-
-
{{request.size}}
-
-
-
-
- -
-

{{rule.value}} offenders

- Please open the JS timeline -
- -
-

{{rule.value}} offenders

- Please open the JS timeline, filtered by "Queries without results" -
- -
-

{{rule.value}} offenders

- Please open the JS timeline, filtered by "jQuery calls on empty object" -
- -
-

{{rule.value}} offenders

- Please open the JS timeline, filtered by "Events not delegated" -
-
-
-

Breakpoints distribution graph

- +

Breakpoints list

@@ -470,14 +482,22 @@
-
-

Protocols advertised by the server

-
-
-
{{protocol}}
+
+

{{rule.value | bytes}} could be saved on

+
+
+
File
+
Current weight
+
WOFF 2 weight
+
Gain
-
-
none
+
+
+ +
+
{{file.originalSize | bytes}}
+
{{file.woff2Size | bytes}}
+
-{{file.gain | bytes}}
diff --git a/front/src/views/screenshot.html b/front/src/views/screenshot.html index 7f4de0d..c22bc42 100644 --- a/front/src/views/screenshot.html +++ b/front/src/views/screenshot.html @@ -2,7 +2,7 @@

Screenshot

-
+
Screenshot not available diff --git a/front/src/views/timeline.html b/front/src/views/timeline.html deleted file mode 100644 index b774c9a..0000000 --- a/front/src/views/timeline.html +++ /dev/null @@ -1,100 +0,0 @@ -
-
-
- Filter timeline and profiler by script: - -
- -

JavaScript Timeline

-

This graph gives a quick view of when the JavaScript interactions with the DOM occur during the loading of the page.

- -
-
-
-
-
-
-
- Timestamp: {{$index * timelineIntervalDuration | number: 0}} ms -
-
-
-
-
0 ms
-
{{endTime | number: 0}} ms
-
-
-
-
DOM creation
-
DOM interactive
-
DOM content loaded event
-
Page completion
-
Page is complete
-
-
-
Executing JavaScript and DOM queries here is a bad practice and slows down the DOM construction.
-
Some frameworks do things here, but it's not reliable and should be avoided.
-
Also known as "document ready". This is where you should execute top-priority scripts, like binding action buttons or launch a video player.
-
Here you can execute mid-priority tasks. Loading a script with createElement('script') is one way to do so.
-
The page is considered loaded, it's time for low priority things : trackers, social plugins, easter egg...
-
-
-
- -

JavaScript Profiler

-

- The table below shows the interactions between the JavaScript and the DOM. It is useful to understand what happens while the page loads. -

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- {{queryWithoutResultsFilterOn}} -
-
-
-
-
Type
-
Params
-
-
Timestamp
-
- - - -
- - -
\ No newline at end of file diff --git a/lib/index.js b/lib/index.js index 81819f4..9064046 100644 --- a/lib/index.js +++ b/lib/index.js @@ -24,9 +24,13 @@ var yellowLabTools = function(url, options) { }; var runner = new Runner(params) + + .progress(deferred.notify) + .then(function(data) { deferred.resolve(data); }) + .fail(function(err) { deferred.reject(err); }); diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index 8ac7e24..ae6c298 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -15,9 +15,9 @@ var policies = { "tool": "phantomas", "label": "DOM max depth", "message": "

A deep DOM makes the CSS matching with DOM elements difficult.

It also slows down JavaScript modifications to the DOM because changing the dimensions of an element makes the browser re-calculate the dimensions of it's parents. Same thing for JavaScript events, that bubble up to the document root.

", - "isOkThreshold": 12, - "isBadThreshold": 22, - "isAbnormalThreshold": 30, + "isOkThreshold": 14, + "isBadThreshold": 24, + "isAbnormalThreshold": 32, "hasOffenders": true, "offendersTransformFn": function(offenders) { var domArrays = offenders.map(offendersHelpers.domPathToArray); @@ -32,89 +32,38 @@ var policies = { "tool": "phantomas", "label": "Number of iframes", "message": "

iFrames are the most complex HTML elements. They are pages, just like the main page, and the browser needs to create a new page context, which has a cost.

", - "isOkThreshold": 3, + "isOkThreshold": 4, "isBadThreshold": 15, "isAbnormalThreshold": 30, - "hasOffenders": false + "hasOffenders": true }, "DOMidDuplicated": { "tool": "phantomas", "label": "IDs duplicated", "message": "

IDs of HTML elements must be document-wide unique. This can cause problems with getElementById returning the wrong element.

", "isOkThreshold": 0, - "isBadThreshold": 5, + "isBadThreshold": 10, "isAbnormalThreshold": 50, - "hasOffenders": true, - "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var parts = /^(.*): ?(\d+) ?occurrences$/.exec(offender); - - if (!parts) { - debug('DOMidDuplicated offenders transform function error with "%s"', offender); - return { - parseError: offender - }; - } - - return { - id: parts[1], - occurrences: parseInt(parts[2], 10) - }; - }) - }; - } + "hasOffenders": true }, - "DOMaccesses": { - "tool": "jsExecutionTransformer", - "label": "DOM access", - "message": "

This metric counts the number of calls to DOM related functions (both native DOM functions and jQuery functions) on page load.

The more your JavaScript code accesses the DOM, the slower the page will load.

Try, as much as possible, to have an HTML page fully generated by the server instead of making changes with JS.

Try to reduce the number of queries by refactoring your JavaScript code.

Binding too many events also has a cost. Try to use event delegation as much as possible.

", - "isOkThreshold": 200, + "scriptDuration": { + "tool": "phantomas", + "label": "Total JS execution time", + "message": "

This is the number of milliseconds spent by the browser on JavaScript execution during page load.

For more details, try using the performance tab in Chrome DevTools. It is a bit complicated at first sight, but you'll be able to analyze exactly where this execution time is spent.

", + "isOkThreshold": 500, "isBadThreshold": 2000, "isAbnormalThreshold": 4000, - "hasOffenders": false + "hasOffenders": false, + "unit": 'ms' }, - "queriesWithoutResults": { - "tool": "jsExecutionTransformer", - "label": "Queries without result", - "message": "

Number of queries that return no result. Both native and jQuery DOM requests are counted.

It suggests the query is not used on the page, probably because it is some dead code.

Or maybe the code is trying to find an HTML block that is not always here. Look at the JS Timeline to see if the scripts correctly figures out the HTML block is not here and immediatly stops interacting further with the DOM.

", - "isOkThreshold": 0, - "isBadThreshold": 150, - "isAbnormalThreshold": 250, - "hasOffenders": false - }, - "DOMqueriesAvoidable": { - "tool": "phantomas", - "label": "Duplicated DOM queries", - "message": "

This is the number of queries that could be avoided by removing all duplicated queries.

Simply save the result of a query in a variable. Ok it is not always simple, especially with third-party scripts, but at least do it with your own code.

", - "isOkThreshold": 0, - "isBadThreshold": 300, - "isAbnormalThreshold": 600, - "hasOffenders": true, - "takeOffendersFrom": "DOMqueriesDuplicated", - "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var parts = /^[^"]* ?"(.*)" ?with ?(.*) ?\(in ?context ?(.*)\): ?(.*)\s?queries$/.exec(offender); - - if (!parts) { - debug('DOMqueriesAvoidable offenders transform function error with "%s"', offender); - return { - parseError: offender - }; - } - - return { - query: parts[1], - context: offendersHelpers.domPathToDomElementObj(parts[3]), - fn: parts[2], - count: parseInt(parts[4], 10) - }; - }) - }; - } + "DOMaccesses": { + "tool": "domAccessAgregator", + "label": "DOM access", + "message": "

This metric estimates the number of times the JavaScript reads, changes or binds the DOM.

The more your JavaScript code accesses the DOM, the slower the page will load.

Try, as much as possible, to have an HTML page fully generated by the server instead of making changes with JS.

Try to reduce the number of queries by refactoring your JavaScript code.

Binding too many events also has a cost.

", + "isOkThreshold": 500, + "isBadThreshold": 2500, + "isAbnormalThreshold": 5000, + "hasOffenders": true }, "eventsScrollBound": { "tool": "phantomas", @@ -147,25 +96,13 @@ var policies = { }; } }, - "DOMaccessesOnScroll": { - "tool": "jsExecutionTransformer", - "label": "DOM access on scroll", - "message": "

This rule counts the number of DOM-accessing functions calls on a scroll event, such as queries, readings, writings, bindings and jQuery functions.

Two scroll events are triggered quickly, one after the other, and only the second one is analyzed so throttled functions are ignored.

One of the main reasons of a poor scrolling experience is when too much JS is executed on each scroll event. Note that some devices such as smartphones and MacBooks send more scroll events than others.

Reduce the number of DOM accesses inside scroll listeners. Put DOM queries outside them when possible. Use throttling or debouncing.

", - "isOkThreshold": 1, - "isBadThreshold": 20, - "isAbnormalThreshold": 35, - "hasOffenders": true, - "offendersTransformFn": function(offenders) { - return offenders; - } - }, "jsErrors": { "tool": "phantomas", "label": "JavaScript errors", - "message": "

Just to let you know there are some errors on the page.

Please note that some errors only occur in the PhantomJS browser, so you might need to double check on other browsers.

", + "message": "

Just to let you know there are some errors on the page.

", "isOkThreshold": 0, "isBadThreshold": 1, - "isAbnormalThreshold": 4, + "isAbnormalThreshold": 5, "hasOffenders": true, "offendersTransformFn": function(offenders) { return { @@ -195,8 +132,8 @@ var policies = { "label": "document.write calls", "message": "

They slow down the page construction, especially if they are used to insert scripts in the page. Remove them ASAP.

If you cannot remove them because they come from a third-party script (such as ads), have a look at PostScribe.

", "isOkThreshold": 0, - "isBadThreshold": 3, - "isAbnormalThreshold": 8, + "isBadThreshold": 2, + "isAbnormalThreshold": 6, "hasOffenders": true, "offendersTransformFn": function(offenders) { return { @@ -250,22 +187,13 @@ var policies = { "isAbnormalThreshold": 1, "hasOffenders": true }, - "consoleMessages": { - "tool": "phantomas", - "label": "Console messages", - "message": "

Try to keep your console clean when in production. Debugging is good for development only.

Writing in the console has a cost, especially when dumping large object variables.

There is also a problem with Internet Explorer 8, not knowing the console object.

", - "isOkThreshold": 3, - "isBadThreshold": 20, - "isAbnormalThreshold": 50, - "hasOffenders": false - }, "globalVariables": { "tool": "phantomas", "label": "Global variables", "message": "

It is a bad practice because they clutter up the global namespace. If two scripts use the same variable name in the global scope, it can cause conflicts and it is generally hard to debug.

Global variables also take a (very) little bit longer to be accessed than variables in the local scope of a function.

", - "isOkThreshold": 40, - "isBadThreshold": 200, - "isAbnormalThreshold": 700, + "isOkThreshold": 20, + "isBadThreshold": 300, + "isAbnormalThreshold": 800, "hasOffenders": true, "offendersTransformFn": function(offenders) { return { @@ -347,76 +275,45 @@ var policies = { "message": "

jQuery is a heavy library. You should never load jQuery more than once on the same page.

", "isOkThreshold": 1, "isBadThreshold": 2, - "isAbnormalThreshold": 2, + "isAbnormalThreshold": 3, "hasOffenders": true }, - "jQueryCallsOnEmptyObject": { - "tool": "jsExecutionTransformer", - "label": "Calls on empty objects", - "message": "

This metric counts the number of jQuery functions called on an empty jQuery object. The call was useless.

This can be helpful to detect dead or unused code.

", - "isOkThreshold": 1, - "isBadThreshold": 100, - "isAbnormalThreshold": 180, - "hasOffenders": false - }, - "jQueryNotDelegatedEvents": { - "tool": "jsExecutionTransformer", - "label": "Events not delegated", - "message": "

This is the number of events that are bound with the .bind() or the .on() function without using event delegation.

This means jQuery binds each element contained in the object one by one. This is bad for performance.

", - "isOkThreshold": 1, - "isBadThreshold": 100, - "isAbnormalThreshold": 180, - "hasOffenders": false - }, "cssParsingErrors": { "tool": "phantomas", "label": "CSS syntax error", "message": "

Yellow Lab Tools failed to parse a CSS file. I doubt the problem comes from the css parser.

Maybe a CSS validator can help you.

", "isOkThreshold": 0, - "isBadThreshold": 1, + "isBadThreshold": 2, "isAbnormalThreshold": 20, "hasOffenders": true, "offendersTransformFn": function(offenders) { return { count: offenders.length, list: offenders.map(function(offender) { - if (offender === '[inline CSS] (Empty CSS was provided)') { + if (offender === '[inline CSS]') { return { - error: 'Empty style tag', + error: 'Empty