diff --git a/.travis.yml b/.travis.yml index 80a5f98..d3027b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: node_js sudo: false node_js: - "6.2" - - "0.12" + - "4.0" env: - CXX=g++-4.8 addons: diff --git a/front/src/css/icons.css b/front/src/css/icons.css index 8a7c99f..778eca8 100644 --- a/front/src/css/icons.css +++ b/front/src/css/icons.css @@ -1,6 +1,6 @@ @font-face { font-family: "icons"; - src: url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAA7wAA4AAAAAGUAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABRAAAAEMAAABWQLtM2mNtYXAAAAGIAAAAOgAAAUriHBC2Y3Z0IAAAAcQAAAAUAAAAIv+YA5pmcGdtAAAB2AAABacAAAuX2BTb8Gdhc3AAAAeAAAAACAAAAAgAAAAQZ2x5ZgAAB4gAAATFAAAHKo/0IxloZWFkAAAMUAAAADMAAAA2BCiQNmhoZWEAAAyEAAAAIAAAACQD/QHAaG10eAAADKQAAAAPAAAALBYAAABsb2NhAAAMtAAAABgAAAAYCMwKe21heHAAAAzMAAAAIAAAACABDAvxbmFtZQAADOwAAAEdAAAB8hTbwvhwb3N0AAAODAAAAGIAAAB/4g/aQ3ByZXAAAA5wAAAAfQAAAIqMht7EeJxjYGRiYJzAwMrAwejCmMbAwOAOpb8ySDK0MDAwMbAyM2AFAWmuKQwOHxk/cjEe+H+AQY/xIIMDUJgRJAcAixQLGgB4nGNgYGBmgGAZBkYGEHAB8hjBfBYGDSDNBqQZGZgYGD5y/f8PUvCREUTzM0DVAwEjG8OIBwB5MwbBAAB4nGNgwAA8EMh48P8BEAYAFnAFM3icrVb5c9NGFJZ8JE5CjpKDFvVYsXGa2iuTUggGTAiSZRfcw7laCUorxU56H9Ayw9/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//AA94nHWUz27bRhDGZ3bJXeofJVIUKUuyLEqJHUSGHUiWlSKBQxQN6khKG6BJ61QtKiNAcwhiqEaaa9lj0UP9BIEfwIcCSXtmgb5FgEKXPkR6oDtLWY6boAS5I5G75My3v/mAA5xGDDCCDHiwCXeDAWhc55o+BSmYkGwKOgipi0fAOYwNBMgNU8gYjgHRxFEui9C+unrJX6lWilbWy3lShwxm0sJpYx09E+UGru1gcQf79IP+1nGtv91xnTx2aRRt9GhsbuLqFsLTI86Pns7HveeMPd9LxlthulJMDV6atm2+HKSKlXSYwuh85tOjuLuYSuPfoZpxx67Zd9QyWks1wOmM6gyBg6Ra81CEG8F1MDTjEDShHQIVewgsJXUqk/F9oOrgAVB4CAh4t2hbZ0chLZbbPb/k97ul1uIqdq0WgxiCIJgszkYQTWazMMQgjuhFwJTW+Ia09mAZekGntlRATQccCNQpL51PNUaTEmVzQ9Lb5KNyubxcXna2Wt2mFEvty7zX7WxvrbaxKRz3Bpa4cD1/By/3e92Sh3+9KMhUSh6rofCieqmHQe9SHD4OHm+gfJIyU3Q+id9s7iDu/Hjy7NmJykucM/BWGw8+Dz4D5BpHbUoCcU0cgC4NqRtTMNLSSB9QPRyVUgIID7kPacik0hlSjggB9aWR55acC8JlSThLCUbiyXdiGEZKp+idiFE4CSZn10LDJNcC+PBpMMpJwRmu1MuOxjU2KCDseqh/DAw1ZCp1KoAfqM0cJ4kJ5Fwfg66b+si2ahW3ZPm2X7xM6npt3MAWAeoRqh3Xk90eNlfX+q1Sv45dukfw4m+tr3v3f1i+6X5z68rt6m50CkkYnt/G71tXcDzwrn+E9cpuFAdqHC7uKb21hINjqkEpXYMG3A4+VNoLLpWauib0R0AknHebpi26reQwaKzUl5fKTq1UI20LaQl5lk+6TeVuUcupXC3acMVlc3VL9Zro+5aP05lTRaw68/BnGAbUUDXbPjxVjEf0JJ7MZ+CxU/UK8a56WrMnGCzyPuMkT1mvw5fBF9mMwbmGA+AaaQ1TsgaKTFMYoADcT6Ouy3EGpSQqhDDGYBimMWr6VkHXrqz66831pXKhYTXMXDql5fW84sRtJ03VcZdRtnr9Hl1Nkzl11tlhWxts7YKvzIIgNG0C5HhydXhzZeXm8P48XL3gJZMAG7Y5oZZsBPfeTlHh2gUXmfOlfOI46YMPgm3VHxLEPkhDZ8gk7hNXnLMHFBh/CJzxuwB5M5elFZmE85TqU8tXpJd8Sp2T9MobZhOEyQzDOGwkAx0YnAIkHkzqKx7KxIP6qkYv1vgj4hTGIqFAXvBcq8CgVq0slZxC2SrPCTAUAf4ZAf6cAP6f3T+Ko7PNDZL4SqomTf0Uz7Dx3ubH1twtrpFlqr0//Yf2XuAfpJDK0oeff3eLlCIOhr869/aCDQOp08gwlFugcguho9APlPPq30pMHI30/YrANtmoGmz+/wJyw4P3VjwMylZB442VWnVeNoGX1/KkdqmN3Ocdr+gK6W9gv7jtddySbK1tbUvHVW7ZJAlPcO/Eyr3O2pX4k4qdfZ2zTDu9nrYtu2LTid/Fv2DkxiOvjlj38JUbETAT045y6rH9L7s+GfsAAAB4nGNgZGBgAOIZ7P+q4vltvjJwMzGAwMWWdU8g9IIr/3/9P8DExngQyOVgAEsDAHIyDgIAeJxjYGRgYDzw/wCDHhMDA8P/L0w8DEARFMANAHttBL94nGNiYGBgIhIDAAI8ABcAAAAAAAB0ALYBAgFYAboCFgKIAsIDFAOVAAEAAAALADAABgAAAAAAAgAYACgAdwAAAGsLlwAAAAB4nF2OvU7DMBSFT/qHaBACITGbpQtS+jP2AdqZDtnTxElbJXHkuJUqMTPzFMw8Bc/FiXslKmzp+jvnHl8bwAN+EKBbAYa+dquHG6oL90l3wgPyo/AQIZ6FR1QvwmO8YiIc4glvnBAMbumMkQn3cI9auE//XXhA/hAecvqn8Ij+l/AYMb6FQ0yC0T41dbvRxbFMrGdfYm3bvanVPJp5vda1tonTmdqeVXsqFs7lKremUitTO12WRjXWHHTqop1zzXI6zcWPUlNhjxSGf26xgUaBI0oksFf+H8VMWO90WmGOCLOr/pr92mcSOJ4ZM1ucWVucOHtB1yGnzpkxqEgrf7dLl9yGTuN7Bzop/Qg7f6vBElPu/F8+8q9XvzD1U2IAAAB4nG3BQQ7CMAwEQG+bpi15Cyde5CBTRTIxOEb9PgeuzNBEPwv9V4gwYUbCgowVG3ZcUAq723lVecQtVfYxK9ekbURSs1d+Wm0q2/sjI5r1PO4u0nNwVYn1ZO+tH0RflcgY/gAAeJxj8N7BcCIoYiMjY1/kBsadHAwcDMkFGxnYnDYxMDJogRibuZkYOSAsfgYwi91pFzMDUJoTyOZw2sXgAGEzM7hsVGHsCIzY4NARsZE5xWWjGoi3i6OBgZHFoSM5JAKkJBIINvMyMfJo7WD837qBpXcjE1Afa4oLAGWtJJsAAAA=") format("woff"); + src: url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); font-weight: normal; font-style: normal; } @@ -15,7 +15,7 @@ speak: none; text-decoration: inherit; text-transform: none; - text-rendering: optimizeLegibility; + text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } diff --git a/front/src/css/main.css b/front/src/css/main.css index 1d698ba..c870840 100644 --- a/front/src/css/main.css +++ b/front/src/css/main.css @@ -1,6 +1,6 @@ @font-face { font-family: "icons"; - src: url("data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAA7wAA4AAAAAGUAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABRAAAAEMAAABWQLtM2mNtYXAAAAGIAAAAOgAAAUriHBC2Y3Z0IAAAAcQAAAAUAAAAIv+YA5pmcGdtAAAB2AAABacAAAuX2BTb8Gdhc3AAAAeAAAAACAAAAAgAAAAQZ2x5ZgAAB4gAAATFAAAHKo/0IxloZWFkAAAMUAAAADMAAAA2BCiQNmhoZWEAAAyEAAAAIAAAACQD/QHAaG10eAAADKQAAAAPAAAALBYAAABsb2NhAAAMtAAAABgAAAAYCMwKe21heHAAAAzMAAAAIAAAACABDAvxbmFtZQAADOwAAAEdAAAB8hTbwvhwb3N0AAAODAAAAGIAAAB/4g/aQ3ByZXAAAA5wAAAAfQAAAIqMht7EeJxjYGRiYJzAwMrAwejCmMbAwOAOpb8ySDK0MDAwMbAyM2AFAWmuKQwOHxk/cjEe+H+AQY/xIIMDUJgRJAcAixQLGgB4nGNgYGBmgGAZBkYGEHAB8hjBfBYGDSDNBqQZGZgYGD5y/f8PUvCREUTzM0DVAwEjG8OIBwB5MwbBAAB4nGNgwAA8EMh48P8BEAYAFnAFM3icrVb5c9NGFJZ8JE5CjpKDFvVYsXGa2iuTUggGTAiSZRfcw7laCUorxU56H9Ayw9/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//AA94nHWUz27bRhDGZ3bJXeofJVIUKUuyLEqJHUSGHUiWlSKBQxQN6khKG6BJ61QtKiNAcwhiqEaaa9lj0UP9BIEfwIcCSXtmgb5FgEKXPkR6oDtLWY6boAS5I5G75My3v/mAA5xGDDCCDHiwCXeDAWhc55o+BSmYkGwKOgipi0fAOYwNBMgNU8gYjgHRxFEui9C+unrJX6lWilbWy3lShwxm0sJpYx09E+UGru1gcQf79IP+1nGtv91xnTx2aRRt9GhsbuLqFsLTI86Pns7HveeMPd9LxlthulJMDV6atm2+HKSKlXSYwuh85tOjuLuYSuPfoZpxx67Zd9QyWks1wOmM6gyBg6Ra81CEG8F1MDTjEDShHQIVewgsJXUqk/F9oOrgAVB4CAh4t2hbZ0chLZbbPb/k97ul1uIqdq0WgxiCIJgszkYQTWazMMQgjuhFwJTW+Ia09mAZekGntlRATQccCNQpL51PNUaTEmVzQ9Lb5KNyubxcXna2Wt2mFEvty7zX7WxvrbaxKRz3Bpa4cD1/By/3e92Sh3+9KMhUSh6rofCieqmHQe9SHD4OHm+gfJIyU3Q+id9s7iDu/Hjy7NmJykucM/BWGw8+Dz4D5BpHbUoCcU0cgC4NqRtTMNLSSB9QPRyVUgIID7kPacik0hlSjggB9aWR55acC8JlSThLCUbiyXdiGEZKp+idiFE4CSZn10LDJNcC+PBpMMpJwRmu1MuOxjU2KCDseqh/DAw1ZCp1KoAfqM0cJ4kJ5Fwfg66b+si2ahW3ZPm2X7xM6npt3MAWAeoRqh3Xk90eNlfX+q1Sv45dukfw4m+tr3v3f1i+6X5z68rt6m50CkkYnt/G71tXcDzwrn+E9cpuFAdqHC7uKb21hINjqkEpXYMG3A4+VNoLLpWauib0R0AknHebpi26reQwaKzUl5fKTq1UI20LaQl5lk+6TeVuUcupXC3acMVlc3VL9Zro+5aP05lTRaw68/BnGAbUUDXbPjxVjEf0JJ7MZ+CxU/UK8a56WrMnGCzyPuMkT1mvw5fBF9mMwbmGA+AaaQ1TsgaKTFMYoADcT6Ouy3EGpSQqhDDGYBimMWr6VkHXrqz66831pXKhYTXMXDql5fW84sRtJ03VcZdRtnr9Hl1Nkzl11tlhWxts7YKvzIIgNG0C5HhydXhzZeXm8P48XL3gJZMAG7Y5oZZsBPfeTlHh2gUXmfOlfOI46YMPgm3VHxLEPkhDZ8gk7hNXnLMHFBh/CJzxuwB5M5elFZmE85TqU8tXpJd8Sp2T9MobZhOEyQzDOGwkAx0YnAIkHkzqKx7KxIP6qkYv1vgj4hTGIqFAXvBcq8CgVq0slZxC2SrPCTAUAf4ZAf6cAP6f3T+Ko7PNDZL4SqomTf0Uz7Dx3ubH1twtrpFlqr0//Yf2XuAfpJDK0oeff3eLlCIOhr869/aCDQOp08gwlFugcguho9APlPPq30pMHI30/YrANtmoGmz+/wJyw4P3VjwMylZB442VWnVeNoGX1/KkdqmN3Ocdr+gK6W9gv7jtddySbK1tbUvHVW7ZJAlPcO/Eyr3O2pX4k4qdfZ2zTDu9nrYtu2LTid/Fv2DkxiOvjlj38JUbETAT045y6rH9L7s+GfsAAAB4nGNgZGBgAOIZ7P+q4vltvjJwMzGAwMWWdU8g9IIr/3/9P8DExngQyOVgAEsDAHIyDgIAeJxjYGRgYDzw/wCDHhMDA8P/L0w8DEARFMANAHttBL94nGNiYGBgIhIDAAI8ABcAAAAAAAB0ALYBAgFYAboCFgKIAsIDFAOVAAEAAAALADAABgAAAAAAAgAYACgAdwAAAGsLlwAAAAB4nF2OvU7DMBSFT/qHaBACITGbpQtS+jP2AdqZDtnTxElbJXHkuJUqMTPzFMw8Bc/FiXslKmzp+jvnHl8bwAN+EKBbAYa+dquHG6oL90l3wgPyo/AQIZ6FR1QvwmO8YiIc4glvnBAMbumMkQn3cI9auE//XXhA/hAecvqn8Ij+l/AYMb6FQ0yC0T41dbvRxbFMrGdfYm3bvanVPJp5vda1tonTmdqeVXsqFs7lKremUitTO12WRjXWHHTqop1zzXI6zcWPUlNhjxSGf26xgUaBI0oksFf+H8VMWO90WmGOCLOr/pr92mcSOJ4ZM1ucWVucOHtB1yGnzpkxqEgrf7dLl9yGTuN7Bzop/Qg7f6vBElPu/F8+8q9XvzD1U2IAAAB4nG3BQQ7CMAwEQG+bpi15Cyde5CBTRTIxOEb9PgeuzNBEPwv9V4gwYUbCgowVG3ZcUAq723lVecQtVfYxK9ekbURSs1d+Wm0q2/sjI5r1PO4u0nNwVYn1ZO+tH0RflcgY/gAAeJxj8N7BcCIoYiMjY1/kBsadHAwcDMkFGxnYnDYxMDJogRibuZkYOSAsfgYwi91pFzMDUJoTyOZw2sXgAGEzM7hsVGHsCIzY4NARsZE5xWWjGoi3i6OBgZHFoSM5JAKkJBIINvMyMfJo7WD837qBpXcjE1Afa4oLAGWtJJsAAAA=") format("woff"); + src: url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); font-weight: normal; font-style: normal; } @@ -15,7 +15,7 @@ speak: none; text-decoration: inherit; text-transform: none; - text-rendering: optimizeLegibility; + text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } diff --git a/front/src/js/app.js b/front/src/js/app.js index 2cb2875..d1decbe 100644 --- a/front/src/js/app.js +++ b/front/src/js/app.js @@ -82,3 +82,7 @@ yltApp.config(['$routeProvider', '$locationProvider', } ]); +// Disable debugging https://docs.angularjs.org/guide/production +yltApp.config(['$compileProvider', function ($compileProvider) { + $compileProvider.debugInfoEnabled(false); +}]); \ No newline at end of file diff --git a/front/src/js/directives/offendersDirectives.js b/front/src/js/directives/offendersDirectives.js index 3ed389a..ae84679 100644 --- a/front/src/js/directives/offendersDirectives.js +++ b/front/src/js/directives/offendersDirectives.js @@ -641,6 +641,11 @@ withFnResult = /^([^\s\(]+) \((.+:\d+:\d+)\)$/.exec(trace); } + if (withFnResult === null) { + // Yet another PhantomJS 2 format? + withFnResult = /^([^\s\(]+|global code)@(.+:\d+:\d+)$/.exec(trace); + } + if (withFnResult === null) { // Try the PhantomJS 2 ERROR format withFnResult = /^([^\s\(]+) (http.+:\d+)$/.exec(trace); @@ -664,12 +669,15 @@ var line = fileAndLineSplit[2]; var column = fileAndLineSplit[3]; - out.push({ - fnName: fnName, - filePath: filePath, - line: line, - column: column - }); + // Filter phantomas code + if (filePath.indexOf('phantomjs://') === -1) { + out.push({ + fnName: fnName, + filePath: filePath, + line: line, + column: column + }); + } }); } catch(e) { diff --git a/front/src/less/icons.less b/front/src/less/icons.less index b1eb7fa..6d596a7 100644 --- a/front/src/less/icons.less +++ b/front/src/less/icons.less @@ -4,7 +4,7 @@ @font-face { font-family:"icons"; - src:url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); + src:url("data:application/x-font-woff;charset=utf-8;base64,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") format("woff"); font-weight:normal; font-style:normal; } @@ -22,7 +22,7 @@ speak:none; text-decoration:inherit; text-transform:none; - text-rendering:optimizeLegibility; + text-rendering:auto; -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale; } diff --git a/front/src/views/rule.html b/front/src/views/rule.html index d6a83e8..99aca48 100644 --- a/front/src/views/rule.html +++ b/front/src/views/rule.html @@ -30,7 +30,7 @@

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

-

+

@@ -106,20 +106,14 @@
{{offender.color1}}
{{offender.color2}}
-
- {{offender.query}} for - -
-
{{offender.error}} (Check on the W3C validator)
-
- - {{offender.css}} +
+ {{offender.css}}
@@ -127,32 +121,6 @@ {{offender.rule}} (x{{offender.occurrences}})
-
- Property {{offender.property}} duplicated in {{offender.rule}} { } - -
- -
- {{offender.css}} { } - -
- -
- {{offender.rule}} {{ '{' + offender.property}}: expression({{offender.expression}})} - -
- -
- {{offender.rule}} {{ '{' + offender.property}}: {{offender.value}} !important} - -
- -
- {{offender.browser}} fix: - - -
-
{{offender.property}} {{offender.message}}
@@ -173,7 +141,7 @@
-
+
@@ -187,15 +155,64 @@ ()
-
+
{{offender}}
-
-
+
+

+ + in + + inline CSS +

+ +
+
+
+ + {{offender.css}} + @ {{offender.line}}:{{offender.column}} +
+ +
+ {{offender.query}} for + @ {{offender.line}}:{{offender.column}} +
+ +
+ Property {{offender.property}} duplicated in {{offender.rule}} { } + @ {{offender.line}}:{{offender.column}} +
+ +
+ {{offender.css}} { } + @ {{offender.line}}:{{offender.column}} +
+ +
+ {{offender.rule}} {{ '{' + offender.property}}: expression({{offender.expression}})} + @ {{offender.line}}:{{offender.column}} +
+ +
+ {{offender.rule}} {{ '{' + offender.property}}: {{offender.value}} !important} + @ {{offender.line}}:{{offender.column}} +
+ +
+ {{offender.browser}} fix: + + @ {{offender.line}}:{{offender.column}} +
+
+
+
+ +
@@ -318,6 +335,17 @@
+
+
+

A file of {{offender.weight | bytes}} is loaded {{offender.urls.length}} times:

+
+
+
+
+
+
+
+

diff --git a/lib/metadata/policies.js b/lib/metadata/policies.js index 9c7b9e2..65e7d3f 100644 --- a/lib/metadata/policies.js +++ b/lib/metadata/policies.js @@ -241,6 +241,15 @@ var policies = { }; } }, + "synchronousXHR": { + "tool": "phantomas", + "label": "Synchronous Ajax requests", + "message": "

Making an XMLHttpRequest with the async option set to false is deprecated due to the negative effect to performances. The browser's main thread needs to stop everything until the response is received.

", + "isOkThreshold": 0, + "isBadThreshold": 1, + "isAbnormalThreshold": 1, + "hasOffenders": true + }, "consoleMessages": { "tool": "phantomas", "label": "Console messages", @@ -267,7 +276,7 @@ var policies = { }, "jQueryVersion": { "label": "jQuery version", - "message": "

Current latest versions of jQuery are 1.12 (with support for old IE versions) and 2.2 (without).

Each new version of jQuery optimizes performances. Do not keep an old version of jQuery. Updating can sometimes break a few things, but it is generally quite easy to fix them up. So don't hesitate.

", + "message": "

The current latest version of jQuery is 3.0

Each new version of jQuery optimizes performances. Do not keep an old version of jQuery. Updating can sometimes break a few things, but it is generally quite easy to fix them up. So don't hesitate.

", "hasOffenders": false, "scoreFn": function(data) { var differentVersions = data.toolsResults.phantomas.metrics.jQueryVersionsLoaded; @@ -279,34 +288,31 @@ var policies = { var value = data.toolsResults.phantomas.metrics.jQueryVersion; var score; - if (value.indexOf('1.12.') === 0 || - value.indexOf('2.2.') === 0 || - value.indexOf('1.13.') === 0 || - value.indexOf('2.3.') === 0 || - value.indexOf('3.0.') === 0 || - value.indexOf('3.1.') === 0) { + if (value.indexOf('3.0.') === 0 || + value.indexOf('3.1.') === 0 || + value.indexOf('3.2.') === 0) { score = 100; + } else if (value.indexOf('1.12.') === 0 || + value.indexOf('2.2.') === 0) { + score = 90; } else if (value.indexOf('1.11.') === 0 || value.indexOf('2.1.') === 0) { - score = 90; + score = 70; } else if (value.indexOf('1.10.') === 0 || value.indexOf('2.0.') === 0) { - score = 70; - } else if (value.indexOf('1.9.') === 0) { score = 50; - } else if (value.indexOf('1.8.') === 0) { + } else if (value.indexOf('1.9.') === 0) { score = 40; - } else if (value.indexOf('1.7') === 0) { + } else if (value.indexOf('1.8.') === 0) { score = 30; - } else if (value.indexOf('1.6') === 0) { + } else if (value.indexOf('1.7') === 0) { score = 20; - } else if (value.indexOf('1.5') === 0) { + } else if (value.indexOf('1.6') === 0) { score = 10; - } else if (value.indexOf('1.4') === 0) { - score = 0; - } else if (value.indexOf('1.3') === 0) { - score = 0; - } else if (value.indexOf('1.2') === 0) { + } else if (value.indexOf('1.5') === 0 || + value.indexOf('1.4') === 0 || + value.indexOf('1.3') === 0 || + value.indexOf('1.2') === 0) { score = 0; } else { debug('Unknown jQuery version "%s"', value); @@ -462,40 +468,18 @@ var policies = { "cssComplexSelectors": { "tool": "phantomas", "label": "Complex selectors", - "message": "

Complex selectors are CSS selectors with 4 or more expressions, like \"#header ul li .foo\".

They are adding more work for the browser, and this could be avoided by simplifying selectors.

", + "message": "

Complex selectors are CSS selectors with 4 or more expressions, like \"#header ul li .foo\".

They are adding more work for the browser, and this could be avoided by simplifying selectors. The B.E.M. methodology is an useful way to simplify your CSS.

", "isOkThreshold": 0, "isBadThreshold": 600, "isAbnormalThreshold": 2000, "hasOffenders": true, "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var splittedOffender = offendersHelpers.cssOffenderPattern(offender); - return splittedOffender; - }) - }; - } - }, - "cssComplexSelectorsByAttribute": { - "tool": "phantomas", - "label": "Complex attributes selector", - "message": "

Complex attributes selectors are one of these:

  • .foo[type*=bar] (contains bar)
  • .foo[type^=bar] (starts with bar)
  • .foo[type|=bar] (starts with bar or bar-)
  • .foo[type$=bar] (ends with bar)
  • .foo[type~=bar baz] (bar or baz)

Their matching process needs more CPU and it has a cost on performances.

", - "isOkThreshold": 0, - "isBadThreshold": 75, - "isAbnormalThreshold": 150, - "hasOffenders": true, - "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var splittedOffender = offendersHelpers.cssOffenderPattern(offender); + var parsedOffenders = offenders.map(function(offender) { + var splittedOffender = offendersHelpers.cssOffenderPattern(offender); + return splittedOffender; + }); - splittedOffender.bolded = splittedOffender.css.replace(/(\[[^ ]+[~\|\^\$\*]="[^"]+"\])/g, '$1'); - - return splittedOffender; - }) - }; + return offendersHelpers.orderByFile(parsedOffenders); } }, "cssColors": { @@ -590,10 +574,7 @@ var policies = { "isAbnormalThreshold": 1000, "hasOffenders": true, "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders - }; + return offendersHelpers.orderByFile(offenders); } }, "cssImports": { @@ -652,29 +633,28 @@ var policies = { "isAbnormalThreshold": 120, "hasOffenders": true, "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var splittedOffender = offendersHelpers.cssOffenderPattern(offender); + var parsedOffenders = offenders.map(function(offender) { + var splittedOffender = offendersHelpers.cssOffenderPattern(offender); - var parts = /^([^{]+) {([^ ]+): (.+)}$/.exec(splittedOffender.css); - - if (!parts) { - debug('cssDuplicatedProperties offenders transform function error with "%s"', offender); - return { - parseError: offender - }; - } + var parts = /^([^{]+) {([^ ]+): (.+)}$/.exec(splittedOffender.css); + if (!parts) { + debug('cssDuplicatedProperties offenders transform function error with "%s"', offender); return { - property: parts[2], - rule: parts[1], - file: splittedOffender.file, - line: splittedOffender.line, - column: splittedOffender.column + parseError: offender }; - }) - }; + } + + return { + property: parts[2], + rule: parts[1], + file: splittedOffender.file, + line: splittedOffender.line, + column: splittedOffender.column + }; + }); + + return offendersHelpers.orderByFile(parsedOffenders); } }, "cssEmptyRules": { @@ -686,14 +666,8 @@ var policies = { "isAbnormalThreshold": 100, "hasOffenders": true, "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var splittedOffender = offendersHelpers.cssOffenderPattern(offender); - - return splittedOffender; - }) - }; + var parsedOffenders = offenders.map(offendersHelpers.cssOffenderPattern); + return offendersHelpers.orderByFile(parsedOffenders); } }, "cssExpressions": { @@ -705,30 +679,29 @@ var policies = { "isAbnormalThreshold": 20, "hasOffenders": true, "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var splittedOffender = offendersHelpers.cssOffenderPattern(offender); + var parsedOffenders = offenders.map(function(offender) { + var splittedOffender = offendersHelpers.cssOffenderPattern(offender); - var parts = /^(.*) {([^ ]+): expression\((.*)\)}$/.exec(splittedOffender.css); - - if (!parts) { - debug('cssExpressions offenders transform function error with "%s"', offender); - return { - parseError: offender - }; - } + var parts = /^(.*) {([^ ]+): expression\((.*)\)}$/.exec(splittedOffender.css); + if (!parts) { + debug('cssExpressions offenders transform function error with "%s"', offender); return { - rule: parts[1], - property: parts[2], - expression: parts[3], - file: splittedOffender.file, - line: splittedOffender.line, - column: splittedOffender.column + parseError: offender }; - }) - }; + } + + return { + rule: parts[1], + property: parts[2], + expression: parts[3], + file: splittedOffender.file, + line: splittedOffender.line, + column: splittedOffender.column + }; + }); + + return offendersHelpers.orderByFile(parsedOffenders); } }, "cssImportants": { @@ -740,30 +713,29 @@ var policies = { "isAbnormalThreshold": 200, "hasOffenders": true, "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var splittedOffender = offendersHelpers.cssOffenderPattern(offender); + var parsedOffenders = offenders.map(function(offender) { + var splittedOffender = offendersHelpers.cssOffenderPattern(offender); - var parts = /^(.*) {([^ ]+): (.*) ?\!important}$/.exec(splittedOffender.css); - - if (!parts) { - debug('cssImportants offenders transform function error with "%s"', offender); - return { - parseError: offender - }; - } + var parts = /^(.*) {([^ ]+): (.*) ?\!important}$/.exec(splittedOffender.css); + if (!parts) { + debug('cssImportants offenders transform function error with "%s"', offender); return { - rule: parts[1], - property: parts[2], - value: parts[3], - file: splittedOffender.file, - line: splittedOffender.line, - column: splittedOffender.column + parseError: offender }; - }) - }; + } + + return { + rule: parts[1], + property: parts[2], + value: parts[3], + file: splittedOffender.file, + line: splittedOffender.line, + column: splittedOffender.column + }; + }); + + return offendersHelpers.orderByFile(parsedOffenders); } }, "cssOldIEFixes": { @@ -775,52 +747,51 @@ var policies = { "isAbnormalThreshold": 300, "hasOffenders": true, "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var splittedOffender = offendersHelpers.cssOffenderPattern(offender); + var parsedOffenders = offenders.map(function(offender) { + var splittedOffender = offendersHelpers.cssOffenderPattern(offender); - var parts = /^([^{]*)( {([^ ]+): (.*)})?$/.exec(splittedOffender.css); + var parts = /^([^{]*)( {([^ ]+): (.*)})?$/.exec(splittedOffender.css); - if (!parts) { - debug('cssOldIEFixes offenders transform function error with "%s"', offender); - return { - parseError: offender - }; - } + if (!parts) { + debug('cssOldIEFixes offenders transform function error with "%s"', offender); + return { + parseError: offender + }; + } - var rule = parts[1]; - var property = parts[3]; - var value = parts[4]; - var browser = null; + var rule = parts[1]; + var property = parts[3]; + var value = parts[4]; + var browser = null; - if (rule.indexOf('* html') === 0) { - rule = rule.replace(/^\* html/, '* html'); - browser = 'IE6'; - } else if (rule.indexOf('html>body') === 0) { - rule = rule.replace(/^html>body/, 'html>body'); - browser = 'IE6'; - } else if (property.indexOf('*') === 0) { - property = '' + property + ''; - browser = 'IE7'; - } else if (value.match(/\!ie$/)) { - value = value.replace(/\!ie$/, '!ie'); - browser = 'IE7'; - } else if (property === '-ms-filter') { - property = '-ms-filter'; - browser = 'IE9'; - } else if (value.indexOf('progid:DXImageTransform.Microsoft') >= 0) { - value = value.replace(/progid:DXImageTransform\.Microsoft/, 'progid:DXImageTransform.Microsoft'); - browser = 'IE9'; - } + if (rule.indexOf('* html') === 0) { + rule = rule.replace(/^\* html/, '* html'); + browser = 'IE6'; + } else if (rule.indexOf('html>body') === 0) { + rule = rule.replace(/^html>body/, 'html>body'); + browser = 'IE6'; + } else if (property.indexOf('*') === 0) { + property = '' + property + ''; + browser = 'IE7'; + } else if (value.match(/\!ie$/)) { + value = value.replace(/\!ie$/, '!ie'); + browser = 'IE7'; + } else if (property === '-ms-filter') { + property = '-ms-filter'; + browser = 'IE9'; + } else if (value.indexOf('progid:DXImageTransform.Microsoft') >= 0) { + value = value.replace(/progid:DXImageTransform\.Microsoft/, 'progid:DXImageTransform.Microsoft'); + browser = 'IE9'; + } - var propertyAndValue = (property && value) ? ' {' + property + ': ' + value + '}' : ''; - splittedOffender.bolded = rule + propertyAndValue; - splittedOffender.browser = browser; + var propertyAndValue = (property && value) ? ' {' + property + ': ' + value + '}' : ''; + splittedOffender.bolded = rule + propertyAndValue; + splittedOffender.browser = browser; - return splittedOffender; - }) - }; + return splittedOffender; + }); + + return offendersHelpers.orderByFile(parsedOffenders); } }, "cssOldPropertyPrefixes": { @@ -876,24 +847,6 @@ var policies = { }; } }, - "cssUniversalSelectors": { - "tool": "phantomas", - "label": "Universal selectors", - "message": "

Universal selectors are the most expensive CSS selectors.

More informations here.

", - "isOkThreshold": 0, - "isBadThreshold": 50, - "isAbnormalThreshold": 150, - "hasOffenders": true, - "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var splittedOffender = offendersHelpers.cssOffenderPattern(offender); - return splittedOffender; - }) - }; - } - }, "cssRedundantBodySelectors": { "tool": "phantomas", "label": "Redundant body selectors", @@ -903,54 +856,50 @@ var policies = { "isAbnormalThreshold": 200, "hasOffenders": true, "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var splittedOffender = offendersHelpers.cssOffenderPattern(offender); + var parsedOffenders = offenders.map(function(offender) { + var splittedOffender = offendersHelpers.cssOffenderPattern(offender); + splittedOffender.bolded = splittedOffender.css.replace(/body/, 'body'); + return splittedOffender; + }); - splittedOffender.bolded = splittedOffender.css.replace(/body/, 'body'); - - return splittedOffender; - }) - }; + return offendersHelpers.orderByFile(parsedOffenders); } }, "cssRedundantChildNodesSelectors": { "tool": "phantomas", "label": "Redundant tags selectors", - "message": "

Some tags included inside other tags are obvious. For example, when \"ul li\" is specified in a rule, \"ul\" can be removed because the \"li\" element is always inside a \"ul\". Same thing for \"tr td\", \"select option\", ...

Lowering compexity in CSS selectors can make the page load a little faster.

", + "message": "

Some tags included inside other tags are obvious. For example, when \"ul li\" is specified in a rule, \"ul\" can be removed because the \"li\" tag is nearly always inside an \"ul\" container (the \"ol\" container is quite rare). Same thing for \"tr td\", \"select option\", ...

Lowering compexity in CSS selectors can make the page load a little faster.

", "isOkThreshold": 0, "isBadThreshold": 60, "isAbnormalThreshold": 200, "hasOffenders": true, "offendersTransformFn": function(offenders) { - return { - count: offenders.length, - list: offenders.map(function(offender) { - var splittedOffender = offendersHelpers.cssOffenderPattern(offender); + var parsedOffenders = offenders.map(function(offender) { + var splittedOffender = offendersHelpers.cssOffenderPattern(offender); - var rule = splittedOffender.css || ''; - var redundanters = [ - ['ul', 'li'], - ['ol', 'li'], - ['select', 'option'], - ['table', 'tr'], - ['table', 'th'], - ]; + var rule = splittedOffender.css || ''; + var redundanters = [ + ['ul', 'li'], + ['ol', 'li'], + ['select', 'option'], + ['table', 'tr'], + ['table', 'th'], + ]; - redundanters.forEach(function(couple) { - rule = rule.replace(new RegExp('(^| |>)' + couple[0] + '([^ >]*)?([ >]| > )' + couple[1] + '([^\\w-]|$)', 'g'), '$1' + couple[0] + '$2$3' + couple[1] + '$4'); - }); + redundanters.forEach(function(couple) { + rule = rule.replace(new RegExp('(^| |>)' + couple[0] + '([^ >]*)?([ >]| > )' + couple[1] + '([^\\w-]|$)', 'g'), '$1' + couple[0] + '$2$3' + couple[1] + '$4'); + }); - splittedOffender.bolded = rule; + splittedOffender.bolded = rule; - return splittedOffender; - }) - }; + return splittedOffender; + }); + + return offendersHelpers.orderByFile(parsedOffenders); } }, "totalWeight": { - "tool": "weightChecker", + "tool": "redownload", "label": "Total weight", "message": "

The weight is of course very important if you want the page to load fast. Try to stay under 1MB, which is alreay very long to download over a slow connection.

Please note that Yellow Lab Tools' engine (PhantomJS) is not compatible with image srcset (unless you use a polyfill). This can lead to incorrect page weight.

", "isOkThreshold": 716800, @@ -960,7 +909,7 @@ var policies = { "unit": 'bytes' }, "imageOptimization": { - "tool": "weightChecker", + "tool": "redownload", "label": "Image optimization", "message": "

This metric measures the number of bytes that could be saved by optimizing images.

Image optimization is generally one of the easiest way to reduce a page weight, and as a result, the page load time. Don't use Photoshop or other image editing tools, they're not very good for optimization. Use specialized tools such as Kraken.io or the excellent ImageOptim on Mac. For SVG images, you can use SVGOMG

The tools in use in YellowLabTools are not set to their maximum optimization power (JPEG quality 85), so you might be able to compress even more!

Please note that Yellow Lab Tools' engine (PhantomJS) is not compatible with image srcset (unless you use a polyfill). This can lead to incorrect page weight.

", "isOkThreshold": 10240, @@ -970,7 +919,7 @@ var policies = { "unit": 'bytes' }, "gzipCompression": { - "tool": "weightChecker", + "tool": "redownload", "label": "Gzip compression", "message": "

Measures the number of bytes that could be saved by compressing file transfers.

Gzip is a powerfull weight reducer and should be enabled on text-based assets in your server's configuration. Note that gzipping small files (< 1 KB) is arguable, and that some assets such as images should not be gzipped as they are already compressed. Here is a list of Content-Types that should be gzipped.

", "isOkThreshold": 5125, @@ -980,7 +929,7 @@ var policies = { "unit": 'bytes' }, "fileMinification": { - "tool": "weightChecker", + "tool": "redownload", "label": "File minification", "message": "

This is the weight that could be saved if all text resources were correctly minified.

The tools in use here are UglifyJS, clean-css and HTMLMinifier. These tools are so good that some of your minified files can be marked as unminified. Change your tool it this happens :)

The gains of minification are generally small, but the impact can be high when these text files are loaded on the critical path.

", "isOkThreshold": 5125, @@ -990,7 +939,7 @@ var policies = { "unit": 'bytes' }, "totalRequests": { - "tool": "weightChecker", + "tool": "redownload", "label": "Requests number", "message": "

This is one of the most important performance rule. Every request is slowing down the page loading.

There are several technics to reduce their number:

  • Concatenate JS files
  • Concatenate CSS files
  • Embed or inline small JS or CSS files in the HTML
  • Create sprites or icon fonts
  • Base64 encode small images in HTML or stylesheets
  • Use lazyloading for images

", "isOkThreshold": 15, @@ -1045,17 +994,29 @@ var policies = { "isAbnormalThreshold": 20, "hasOffenders": true }, - "multipleRequests": { - "tool": "phantomas", - "label": "Duplicated requests", - "message": "

This only happens when the asset has no cache and is requested more than once on the same page. Be very careful about it.

", + "identicalFiles": { + "tool": "redownload", + "label": "Identical content", + "message": "

This is the number of requests that could be avoided, because of downloaded files that have the same content but are loaded from different URLs.

Try to load them from the same URL.

", "isOkThreshold": 0, - "isBadThreshold": 3, - "isAbnormalThreshold": 10, + "isBadThreshold": 5, + "isAbnormalThreshold": 15, + "hasOffenders": true, + "offendersTransformFn": function(offenders) { + return offenders; + } + }, + "emptyRequests": { + "tool": "redownload", + "label": "Empty requests", + "message": "

List of GET requests that respond with an empty body. These are probably the easiest requests to remove.

", + "isOkThreshold": 0, + "isBadThreshold": 1, + "isAbnormalThreshold": 5, "hasOffenders": true }, "smallRequests": { - "tool": "weightChecker", + "tool": "redownload", "label": "Small requests", "message": "

List of all requests that are less than 2 KB. Try to merge them with other files.

", "isOkThreshold": 4, @@ -1075,7 +1036,7 @@ var policies = { "hiddenImages": { "tool": "phantomas", "label": "Hidden images", - "message": "

List of all images that have a display:none property, or one of their parents. These images are loaded by the browser even if they're not visible. You might be able to find a way to lazy-load them, only when they get visible.

Trackers are an exception, you'd better hide them.

", + "message": "

List of all images that have a display:none property, or one of their parents. These images are loaded by the browser even if they're not visible. You might be able to find a way to lazy-load them, only when they get visible.

As images displayed in 1x1 pixels tend to be trackers, they are excluded from this rule.

", "isOkThreshold": 1, "isBadThreshold": 12, "isAbnormalThreshold": 30, diff --git a/lib/metadata/scoreProfileGeneric.json b/lib/metadata/scoreProfileGeneric.json index a65a931..958db6a 100644 --- a/lib/metadata/scoreProfileGeneric.json +++ b/lib/metadata/scoreProfileGeneric.json @@ -15,7 +15,8 @@ "totalRequests": 5, "domains": 3, "notFound": 3, - "multipleRequests": 2, + "identicalFiles": 2, + "emptyRequests": 3, "smallRequests": 1, "lazyLoadableImagesBelowTheFold": 2, "hiddenImages": 1 @@ -50,6 +51,7 @@ "policies": { "jsErrors": 1, "documentWriteCalls": 2, + "synchronousXHR": 5, "consoleMessages": 0.5, "globalVariables": 0.5 } @@ -69,10 +71,9 @@ "policies": { "cssRules": 2, "cssComplexSelectors": 2, - "cssComplexSelectorsByAttribute": 1.5, "cssColors": 0.5, "similarColors": 0.5, - "cssBreakpoints": 1, + "cssBreakpoints": 0.5, "cssMobileFirst": 1 } }, @@ -88,7 +89,6 @@ "cssImportants": 3, "cssOldIEFixes": 1, "cssOldPropertyPrefixes": 1, - "cssUniversalSelectors": 1, "cssRedundantBodySelectors": 1, "cssRedundantChildNodesSelectors": 1 } @@ -110,7 +110,7 @@ "domComplexity": 2, "domManipulations": 2, "scroll": 1, - "badJavascript": 1, + "badJavascript": 2, "jQuery": 1, "cssSyntaxError": 1, "cssComplexity": 1, diff --git a/lib/offendersHelpers.js b/lib/offendersHelpers.js index 24fc5dc..3fe906a 100644 --- a/lib/offendersHelpers.js +++ b/lib/offendersHelpers.js @@ -113,14 +113,17 @@ var OffendersHelpers = function() { if (traceArray) { var results = []; var parts = null; + var obj; for (var i=0 ; i [%s]', url, method); + + if (async === false) { + phantomas.incrMetric('synchronousXHR'); + phantomas.addOffender('synchronousXHR', url); + phantomas.log('ajaxRequests: synchronous XMLHttpRequest call to <%s>', url); + } + }, true); + })(window.__phantomas); + }); + }); +}; \ No newline at end of file diff --git a/lib/tools/phantomas/custom_modules/modules/domHiddenYLT/domHiddenYLT.js b/lib/tools/phantomas/custom_modules/modules/domHiddenYLT/domHiddenYLT.js index 097aef7..a90e2bf 100644 --- a/lib/tools/phantomas/custom_modules/modules/domHiddenYLT/domHiddenYLT.js +++ b/lib/tools/phantomas/custom_modules/modules/domHiddenYLT/domHiddenYLT.js @@ -52,6 +52,8 @@ exports.module = function(phantomas) { if (src === '' || src.indexOf('data:image') === 0) continue; + if (images[i].width === 1 && images[i].height === 1) continue; + if (!lazyLoadableImages[src]) { path = phantomas.getDOMPath(images[i]); diff --git a/lib/tools/phantomas/phantomasWrapper.js b/lib/tools/phantomas/phantomasWrapper.js index 520013e..910d3d5 100644 --- a/lib/tools/phantomas/phantomasWrapper.js +++ b/lib/tools/phantomas/phantomasWrapper.js @@ -37,19 +37,21 @@ var PhantomasWrapper = function() { // Mandatory 'reporter': 'json:pretty', 'analyze-css': true, + 'ignore-ssl-errors': true, 'skip-modules': [ - 'domHiddenContent', // overriden + 'ajaxRequests', // overridden + 'domHiddenContent', // overridden 'domMutations', // not compatible with webkit - 'domQueries', // overriden + 'domQueries', // overridden 'events', // overridden 'filmStrip', // not needed 'har', // not needed for the moment 'javaScriptBottlenecks', // needs to be launched after custom module scopeYLT 'jQuery', // overridden 'jserrors', // overridden - 'lazyLoadableImages', //overriden + 'lazyLoadableImages', //overridden 'pageSource', // not needed - 'windowPerformance' // overriden + 'windowPerformance' // overridden ].join(','), 'include-dirs': [ path.join(__dirname, 'custom_modules/core'), diff --git a/lib/tools/redownload/contentTypeChecker.js b/lib/tools/redownload/contentTypeChecker.js new file mode 100644 index 0000000..d5fa7ce --- /dev/null +++ b/lib/tools/redownload/contentTypeChecker.js @@ -0,0 +1,188 @@ +var debug = require('debug')('ylt:contentTypeChecker'); +var Q = require('q'); +var isJpg = require('is-jpg'); +var isPng = require('is-png'); +var isSvg = require('is-svg'); +var isGif = require('is-gif'); +var isWoff = require('is-woff'); +var isWoff2 = require('is-woff2'); +var isOtf = require('is-otf'); +var isTtf = require('is-ttf'); +var isEot = require('is-eot'); + +var ContentTypeChecker = function() { + + function checkContentType(entry) { + var deferred = Q.defer(); + + debug('Entering contentTypeChecker'); + + // Ignore very small files as they are generally tracking pixels + if (entry.weightCheck && entry.weightCheck.body && entry.weightCheck.bodySize > 100) { + var foundType; + + try { + foundType = findContentType(entry.weightCheck.body); + + if (!entry.contentType || entry.contentType === '') { + if (foundType === null) { + debug('ContentType is empty for file %s', entry.url); + } else { + debug('ContentType is empty for file %s. It should be %s.', entry.url, foundType.mimes[0]); + entry.oldContentType = null; + rewriteContentType(entry, foundType); + } + } else { + if (foundType !== null && foundType.mimes.indexOf(entry.contentType) === -1) { + debug('ContentType %s is wrong for %s. It should be %s.', entry.contentType, entry.url, foundType.mimes[0]); + entry.oldContentType = entry.contentType; + rewriteContentType(entry, foundType); + } + } + + } catch(err) { + debug('Error while analyzing the contentType of %s', entry.url); + debug(err); + } + } + + deferred.resolve(entry); + + return deferred.promise; + } + + function findContentType(body) { + var buffer = new Buffer(body, 'binary'); + + if (isJpg(buffer)) { + return contentTypes.jpeg; + } + + if (isPng(buffer)) { + return contentTypes.png; + } + + // https://github.com/sindresorhus/is-svg/issues/7 + if (/ 1024; + var looksMinified = linesRatio > 2 * 1024; debug('Lines ratio is %d bytes per line', Math.round(linesRatio)); debug(looksMinified ? 'It looks already minified' : 'It doesn\'t look minified'); diff --git a/lib/tools/weightChecker/gzipCompressor.js b/lib/tools/redownload/gzipCompressor.js similarity index 100% rename from lib/tools/weightChecker/gzipCompressor.js rename to lib/tools/redownload/gzipCompressor.js diff --git a/lib/tools/weightChecker/imageOptimizer.js b/lib/tools/redownload/imageOptimizer.js similarity index 82% rename from lib/tools/weightChecker/imageOptimizer.js rename to lib/tools/redownload/imageOptimizer.js index 1625200..b9c3b51 100644 --- a/lib/tools/weightChecker/imageOptimizer.js +++ b/lib/tools/redownload/imageOptimizer.js @@ -1,8 +1,10 @@ -var debug = require('debug')('ylt:imageOptimizer'); - -var Q = require('q'); -var Imagemin = require('imagemin'); -var jpegoptim = require('imagemin-jpegoptim'); +var debug = require('debug')('ylt:imageOptimizer'); +var Q = require('q'); +var imagemin = require('imagemin'); +var imageminJpegtran = require('imagemin-jpegtran'); +var imageminJpegoptim = require('imagemin-jpegoptim'); +var imageminOptipng = require('imagemin-optipng'); +var imageminSvgo = require('imagemin-svgo'); var ImageOptimizer = function() { @@ -34,7 +36,7 @@ var ImageOptimizer = function() { return entry; } - var newFileSize = newFile.contents.length; + var newFileSize = newFile.length; debug('JPEG lossless compression complete for %s', entry.url); @@ -55,11 +57,13 @@ var ImageOptimizer = function() { return entry; } - var newFileSize = newFile.contents.length; + var newFileSize = newFile.length; debug('JPEG lossy compression complete for %s', entry.url); - if (gainIsEnough(fileSize, newFileSize)) { + if (entry.weightCheck.lossless && entry.weightCheck.lossless < newFileSize) { + debug('Lossy compression is not as good as lossless compression. Skipping the lossy.'); + } else if (gainIsEnough(fileSize, newFileSize)) { if (entry.weightCheck.isOptimized !== false || newFileSize < entry.weightCheck.lossless) { entry.weightCheck.optimized = newFileSize; @@ -90,7 +94,7 @@ var ImageOptimizer = function() { return entry; } - var newFileSize = newFile.contents.length; + var newFileSize = newFile.length; debug('PNG lossless compression complete for %s', entry.url); @@ -124,12 +128,12 @@ var ImageOptimizer = function() { return entry; } - var newFileSize = newFile.contents.length; + var newFileSize = newFile.length; debug('SVG lossless compression complete for %s', entry.url); if (gainIsEnough(fileSize, newFileSize)) { - entry.weightCheck.bodyAfterOptimization = newFile.contents.toString(); + entry.weightCheck.bodyAfterOptimization = newFile.toString(); entry.weightCheck.lossless = entry.weightCheck.optimized = newFileSize; entry.weightCheck.isOptimized = false; debug('Filesize is %d bytes smaller (-%d%)', fileSize - newFileSize, Math.round((fileSize - newFileSize) * 100 / fileSize)); @@ -194,35 +198,29 @@ var ImageOptimizer = function() { var engine; if (type === 'jpeg' && !lossy) { - engine = Imagemin.jpegtran(); + engine = imageminJpegtran({progressive: true}); } else if (type === 'jpeg' && lossy) { - engine = jpegoptim({max: MAX_JPEG_QUALITY}); + engine = imageminJpegoptim({progressive: true, max: MAX_JPEG_QUALITY}); } else if (type === 'png' && !lossy) { - engine = Imagemin.optipng({optimizationLevel: OPTIPNG_COMPRESSION_LEVEL}); + engine = imageminOptipng({optimizationLevel: OPTIPNG_COMPRESSION_LEVEL}); } else if (type === 'svg' && !lossy) { - engine = Imagemin.svgo({ plugins: [ { removeUselessDefs: false } ] }); + engine = imageminSvgo({ plugins: [ { removeUselessDefs: false } ] }); } else { deferred.reject('No optimization engine found for imagemin'); } - try { - new Imagemin() - .src(imageBody) - .use(engine) - .run(function (err, files) { - if (err) { - deferred.reject(err); - } else { - deferred.resolve(files[0]); - var endTime = Date.now(); - debug('Optimization for %s took %d ms', type, endTime - startTime); - } - }); + imagemin.buffer(imageBody, {use: engine}) - } catch(err) { - deferred.reject(err); - } + .then(function(file) { + var endTime = Date.now(); + debug('Optimization for %s took %d ms', type, endTime - startTime); + deferred.resolve(file); + }) + + .catch(function(err) { + deferred.reject(file); + }); return deferred.promise; } diff --git a/lib/tools/weightChecker/weightChecker.js b/lib/tools/redownload/redownload.js similarity index 82% rename from lib/tools/weightChecker/weightChecker.js rename to lib/tools/redownload/redownload.js index 747ece5..97eff8a 100644 --- a/lib/tools/weightChecker/weightChecker.js +++ b/lib/tools/redownload/redownload.js @@ -4,20 +4,23 @@ * */ +/*jshint -W069 */ -var debug = require('debug')('ylt:weightChecker'); -var Q = require('q'); -var http = require('http'); -var zlib = require('zlib'); -var async = require('async'); -var request = require('request'); +var debug = require('debug')('ylt:redownload'); +var Q = require('q'); +var http = require('http'); +var zlib = require('zlib'); +var async = require('async'); +var request = require('request'); +var md5 = require('md5'); -var imageOptimizer = require('./imageOptimizer'); -var fileMinifier = require('./fileMinifier'); -var gzipCompressor = require('./gzipCompressor'); +var imageOptimizer = require('./imageOptimizer'); +var fileMinifier = require('./fileMinifier'); +var gzipCompressor = require('./gzipCompressor'); +var contentTypeChecker = require('./contentTypeChecker'); -var WeightChecker = function() { +var Redownload = function() { var MAX_PARALLEL_DOWNLOADS = 10; var REQUEST_TIMEOUT = 15000; // 15 seconds @@ -47,6 +50,8 @@ var WeightChecker = function() { redownloadEntry(entry, httpAuth) + .then(contentTypeChecker.checkContentType) + .then(imageOptimizer.optimizeImage) .then(fileMinifier.minifyFile) @@ -83,16 +88,32 @@ var WeightChecker = function() { offenders.totalRequests = listRequestsByType(results); metrics.totalRequests = offenders.totalRequests.total; - // Remove unwanted requests (redirections, about:blank) + + // Remove unwanted responses (redownload failed, about:blank) results = results.filter(function(result) { - return (result !== null && result.weightCheck && result.weightCheck.bodySize > 0); + return (result !== null && result.weightCheck && result.url.indexOf('about:blank') !== 0); }); + // Wrong contentType + offenders.incorrectContentTypes = listIncorrectContentTypes(results); + metrics.incorrectContentTypes = offenders.incorrectContentTypes.length; + // Total weight offenders.totalWeight = listRequestWeight(results); metrics.totalWeight = offenders.totalWeight.totalWeight; + // Empty files + offenders.emptyRequests = listEmptyRequests(results); + metrics.emptyRequests = offenders.emptyRequests.length; + + + // Now remove unwanted responses (redirections and empty files) + results = results.filter(function(result) { + return ((result.status < 300 || result.status >= 400) && result.weightCheck.bodySize > 0); + }); + + // Image compression offenders.imageOptimization = listImageNotOptimized(results); metrics.imageOptimization = offenders.imageOptimization.totalGain; @@ -109,7 +130,12 @@ var WeightChecker = function() { offenders.smallRequests = listSmallRequests(results); metrics.smallRequests = offenders.smallRequests.total; - data.toolsResults.weightChecker = { + // Detect identical files + offenders.identicalFiles = listIdenticalFiles(results); + metrics.identicalFiles = offenders.identicalFiles.avoidableRequests; + + + data.toolsResults.redownload = { metrics: metrics, offenders: offenders }; @@ -121,6 +147,35 @@ var WeightChecker = function() { return deferred.promise; } + function listIncorrectContentTypes(requests) { + var results = []; + + requests.forEach(function(req) { + if (req.oldContentType || req.oldContentType === null) { + results.push({ + url: req.url, + current: req.oldContentType, + correct: req.contentType + }); + } + }); + + return results; + } + + function listEmptyRequests(requests) { + var results = []; + + requests.forEach(function(req) { + var weight = req.weightCheck.bodySize || req.contentLength; + + if (weight === 0 && req.method === 'GET' && req.status === 200) { + results.push(req.url); + } + }); + + return results; + } function listRequestWeight(requests) { var results = { @@ -320,6 +375,45 @@ var WeightChecker = function() { return results; } + function listIdenticalFiles(requests) { + var hashes = {}; + var list = []; + var avoidableRequestsCount = 0; + + requests.forEach(function(req) { + var requestHash = md5(req.weightCheck.body); + + // Try to exclude tracking pixels + if (req.weightCheck.bodySize < 80 && req.type === 'image') { + return; + } + + if (!hashes[requestHash]) { + hashes[requestHash] = { + weight: req.weightCheck.bodySize, + urls: [] + }; + } + + if (hashes[requestHash].urls.indexOf(req.url) === -1) { + hashes[requestHash].urls.push(req.url); + } + }); + + for (var hash in hashes) { + if (hashes[hash].urls.length > 1) { + list.push(hashes[hash]); + avoidableRequestsCount += hashes[hash].urls.length - 1; + } + } + + return { + avoidableRequests: avoidableRequestsCount, + count: list.length, + list: list + }; + } + function redownloadEntry(entry, httpAuth) { var deferred = Q.defer(); @@ -367,6 +461,7 @@ var WeightChecker = function() { // Always add a gzip header before sending, in case the server listens to it var reqHeaders = entry.requestHeaders; reqHeaders['Accept-Encoding'] = 'gzip, deflate'; + reqHeaders['Connection'] = 'keep-alive'; var requestOptions = { method: entry.method, @@ -533,4 +628,4 @@ var WeightChecker = function() { }; }; -module.exports = new WeightChecker(); \ No newline at end of file +module.exports = new Redownload(); \ No newline at end of file diff --git a/package.json b/package.json index 29302be..ad99ee2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "yellowlabtools", - "version": "1.10.1", + "version": "1.11.0", "description": "Online tool to audit a webpage for performance and front-end quality issues", "license": "GPL-2.0", "author": { @@ -16,55 +16,68 @@ "yellowlabtools": "./bin/cli.js" }, "engines": { - "node": ">= 0.12.0" + "node": ">= 4.0" }, "main": "./lib/index.js", "dependencies": { - "angular": "1.5.2", - "angular-animate": "1.5.2", - "angular-chart.js": "0.9.0", + "angular": "1.5.7", + "angular-animate": "1.5.7", + "angular-chart.js": "0.10.2", "angular-local-storage": "0.2.7", - "angular-resource": "1.5.2", - "angular-route": "1.5.2", - "angular-sanitize": "1.5.2", + "angular-resource": "1.5.7", + "angular-route": "1.5.7", + "angular-sanitize": "1.5.7", "async": "1.5.2", - "body-parser": "1.15.0", - "chart.js": "1.0.2", - "clean-css": "3.4.10", + "body-parser": "1.15.2", + "chart.js": "1.1.1", + "clean-css": "3.4.18", "color-diff": "1.0.0", - "compression": "1.6.1", + "compression": "1.6.2", "cors": "2.7.1", "css-mq-parser": "0.0.3", "debug": "2.2.0", "easyxml": "2.0.1", - "express": "4.13.4", - "imagemin": "4.0.0", - "imagemin-jpegoptim": "4.1.0", + "express": "4.14.0", + "imagemin": "5.2.2", + "imagemin-jpegoptim": "5.0.0", + "imagemin-jpegtran": "5.0.2", + "imagemin-optipng": "5.1.0", + "imagemin-svgo": "5.1.0", + "is-eot": "1.0.0", + "is-gif": "1.0.0", "is-http2": "1.0.4", + "is-jpg": "1.0.0", + "is-otf": "0.1.2", + "is-png": "1.0.0", + "is-svg": "2.0.1", + "is-ttf": "0.2.2", + "is-woff": "1.0.3", + "is-woff2": "1.0.0", "lwip": "0.0.9", + "md5": "2.1.0", "meow": "3.7.0", - "minimize": "1.8.1", + "minimize": "2.0.0", "parse-color": "1.0.0", - "phantomas": "1.15.1", - "ps-node": "0.0.5", + "phantomas": "1.16.0", + "ps-node": "0.1.2", "q": "1.4.1", - "request": "2.69.0", - "rimraf": "2.5.2", + "request": "2.72.0", + "rimraf": "2.5.3", "temporary": "0.0.8", "try-thread-sleep": "1.0.0", - "uglify-js": "2.6.2" + "uglify-js": "2.7.0" }, "devDependencies": { "chai": "~3.5.0", "grunt": "~0.4.5", "grunt-blanket": "~0.0.10", "grunt-contrib-clean": "~1.0.0", - "grunt-contrib-concat": "~1.0.0", + "grunt-contrib-concat": "~1.0.1", "grunt-contrib-copy": "~1.0.0", "grunt-contrib-cssmin": "~1.0.1", - "grunt-contrib-htmlmin": "~1.1.0", + "grunt-contrib-htmlmin": "~1.4.0", "grunt-contrib-jshint": "~1.0.0", - "grunt-contrib-less": "~1.2.0", + "grunt-contrib-less": "~1.3.0", "grunt-contrib-uglify": "~1.0.1", "grunt-env": "~0.4.4", "grunt-express": "~1.4.1", @@ -72,12 +85,12 @@ "grunt-inline-angular-templates": "~0.1.5", "grunt-line-remover": "~0.0.2", "grunt-mocha-test": "~0.12.7", - "grunt-replace": "~0.11.0", + "grunt-replace": "~1.0.1", "grunt-usemin": "~3.1.1", - "grunt-webfont": "~1.2.0", + "grunt-webfont": "~1.4.0", "matchdep": "~1.0.1", - "mocha": "~2.4.5", - "sinon": "~1.17.3", + "mocha": "~2.5.3", + "sinon": "~1.17.4", "sinon-chai": "~2.8.0" }, "scripts": { diff --git a/test/core/contentTypeCheckerTest.js b/test/core/contentTypeCheckerTest.js new file mode 100644 index 0000000..ac67cc4 --- /dev/null +++ b/test/core/contentTypeCheckerTest.js @@ -0,0 +1,20 @@ +var should = require('chai').should(); +var contentTypeChecker = require('../../lib/tools/redownload/contentTypeChecker'); +var fs = require('fs'); +var path = require('path'); + +describe('contentTypeChecker', function() { + + var jpgImageContent = fs.readFileSync(path.resolve(__dirname, '../www/jpeg-image.jpg')); + var pngImageContent = fs.readFileSync(path.resolve(__dirname, '../www/png-image.png')); + var svgImageContent = fs.readFileSync(path.resolve(__dirname, '../www/svg-image.svg')); + var cssFileContent = fs.readFileSync(path.resolve(__dirname, '../www/unminified-stylesheet.css')); + + it('detect the right content type', function() { + contentTypeChecker.findContentType(jpgImageContent).mimes.should.deep.equal(['image/jpeg']); + contentTypeChecker.findContentType(pngImageContent).mimes.should.deep.equal(['image/png']); + contentTypeChecker.findContentType(svgImageContent).mimes.should.deep.equal(['image/svg+xml']); + should.equal(contentTypeChecker.findContentType(cssFileContent), null); + }); + +}); diff --git a/test/core/customPoliciesTest.js b/test/core/customPoliciesTest.js index 2f517cd..a506781 100644 --- a/test/core/customPoliciesTest.js +++ b/test/core/customPoliciesTest.js @@ -184,18 +184,20 @@ describe('customPolicies', function() { var versions = { '1.2.9': 0, '1.4.4': 0, - '1.5.0': 10, - '1.6.3': 20, - '1.7.0': 30, - '1.8.3a': 40, - '1.9.2': 50, - '1.10.1': 70, - '2.0.0-rc1': 70, - '1.11.1': 90, - '2.1.1-beta1': 90, - '1.12.1': 100, - '2.3.1': 100, - '3.1.0': 100 + '1.5.0': 0, + '1.6.3': 10, + '1.7.0': 20, + '1.8.3a': 30, + '1.9.2': 40, + '1.10.1': 50, + '2.0.0-rc1': 50, + '1.11.1': 70, + '2.1.1-beta1': 70, + '1.12.1': 90, + '2.2.1': 90, + '3.0.1': 100, + '3.1.0': 100, + '3.2.1': 100 }; for (var version in versions) { diff --git a/test/core/fileMinifierTest.js b/test/core/fileMinifierTest.js index 90ce452..348810b 100644 --- a/test/core/fileMinifierTest.js +++ b/test/core/fileMinifierTest.js @@ -1,5 +1,5 @@ var should = require('chai').should(); -var fileMinifier = require('../../lib/tools/weightChecker/fileMinifier'); +var fileMinifier = require('../../lib/tools/redownload/fileMinifier'); var fs = require('fs'); var path = require('path'); diff --git a/test/core/gzipCompressorTest.js b/test/core/gzipCompressorTest.js index 66a66d6..c436e02 100644 --- a/test/core/gzipCompressorTest.js +++ b/test/core/gzipCompressorTest.js @@ -1,6 +1,6 @@ var should = require('chai').should(); -var gzipCompressor = require('../../lib/tools/weightChecker/gzipCompressor'); -var fileMinifier = require('../../lib/tools/weightChecker/fileMinifier'); +var gzipCompressor = require('../../lib/tools/redownload/gzipCompressor'); +var fileMinifier = require('../../lib/tools/redownload/fileMinifier'); var fs = require('fs'); var path = require('path'); diff --git a/test/core/imageOptimizerTest.js b/test/core/imageOptimizerTest.js index f1bc613..e858962 100644 --- a/test/core/imageOptimizerTest.js +++ b/test/core/imageOptimizerTest.js @@ -1,5 +1,5 @@ var should = require('chai').should(); -var imageOptimizer = require('../../lib/tools/weightChecker/imageOptimizer'); +var imageOptimizer = require('../../lib/tools/redownload/imageOptimizer'); var fs = require('fs'); var path = require('path'); @@ -11,7 +11,7 @@ describe('imageOptimizer', function() { var fileSize = fileContent.length; imageOptimizer.compressJpegLosslessly(fileContent).then(function(newFile) { - var newFileSize = newFile.contents.length; + var newFileSize = newFile.length; newFileSize.should.be.below(fileSize); done(); }).fail(function(err) { @@ -25,7 +25,7 @@ describe('imageOptimizer', function() { var fileSize = fileContent.length; imageOptimizer.compressJpegLossly(fileContent).then(function(newFile) { - var newFileSize = newFile.contents.length; + var newFileSize = newFile.length; newFileSize.should.be.below(fileSize); done(); }).fail(function(err) { @@ -82,7 +82,7 @@ describe('imageOptimizer', function() { var fileSize = fileContent.length; imageOptimizer.compressPngLosslessly(fileContent).then(function(newFile) { - var newFileSize = newFile.contents.length; + var newFileSize = newFile.length; newFileSize.should.be.below(fileSize); done(); }).fail(function(err) { @@ -96,7 +96,7 @@ describe('imageOptimizer', function() { var fileSize = fileContent.length; imageOptimizer.compressPngLosslessly(fileContent).then(function(newFile) { - var newFileSize = newFile.contents.length; + var newFileSize = newFile.length; newFileSize.should.equal(fileSize); done(); }).fail(function(err) { @@ -110,7 +110,7 @@ describe('imageOptimizer', function() { var fileSize = fileContent.length; imageOptimizer.compressPngLosslessly(fileContent).then(function(newFile) { - var newFileSize = newFile.contents.length; + var newFileSize = newFile.length; newFileSize.should.equal(fileSize); done(); }).fail(function(err) { @@ -166,7 +166,7 @@ describe('imageOptimizer', function() { var fileSize = fileContent.length; imageOptimizer.compressSvgLosslessly(fileContent).then(function(newFile) { - var newFileSize = newFile.contents.length; + var newFileSize = newFile.length; newFileSize.should.be.below(fileSize); done(); }).fail(function(err) { diff --git a/test/core/offendersHelpersTest.js b/test/core/offendersHelpersTest.js index d2cc6fa..e054840 100644 --- a/test/core/offendersHelpersTest.js +++ b/test/core/offendersHelpersTest.js @@ -200,6 +200,47 @@ describe('offendersHelpers', function() { ]); }); + it('should transform a backtrace with the new PhantomJS 2.x syntax into an array', function() { + var result = offendersHelpers.backtraceToArray('each@http://m.australia.fr/js/min/vendors.js?20160706185900:4:5365 / f@http://m.australia.fr/js/min/vendors.js?20160706185900:17:82 / http://m.australia.fr/js/min/vendors.js?20160706185900:17:855 / handle@http://m.australia.fr/js/min/vendors.js?20160706185900:5:10871 / report@phantomjs://platform/phantomas.js:535:20 / phantomjs://platform/phantomas.js:524:15'); + + result.should.deep.equal([ + { + functionName: 'each', + file: 'http://m.australia.fr/js/min/vendors.js?20160706185900', + line: 4, + column: 5365 + }, + { + functionName: 'f', + file: 'http://m.australia.fr/js/min/vendors.js?20160706185900', + line: 17, + column: 82 + }, + { + file: 'http://m.australia.fr/js/min/vendors.js?20160706185900', + line: 17, + column: 855 + }, + { + functionName: 'handle', + file: 'http://m.australia.fr/js/min/vendors.js?20160706185900', + line: 5, + column: 10871 + }, + { + functionName: 'report', + file: 'phantomjs://platform/phantomas.js', + line: 535, + column: 20 + }, + { + file: 'phantomjs://platform/phantomas.js', + line: 524, + column: 15 + } + ]); + }); + it('should return null if it fails', function() { var result = offendersHelpers.backtraceToArray('http://pouet.com/js/jquery.footer-transverse-min-v1.0.20.js:1 /http://pouet.com/js/main.js:1'); diff --git a/test/core/weightCheckerTest.js b/test/core/redownloadTest.js similarity index 70% rename from test/core/weightCheckerTest.js rename to test/core/redownloadTest.js index 2e93bea..8a12ab3 100644 --- a/test/core/weightCheckerTest.js +++ b/test/core/redownloadTest.js @@ -1,9 +1,9 @@ var should = require('chai').should(); -var weightChecker = require('../../lib/tools/weightChecker/weightChecker'); +var redownload = require('../../lib/tools/redownload/redownload'); var fs = require('fs'); var path = require('path'); -describe('weightChecker', function() { +describe('redownload', function() { it('should download a list of files', function(done) { this.timeout(10000); @@ -121,49 +121,49 @@ describe('weightChecker', function() { } }; - weightChecker.recheckAllFiles(data) + redownload.recheckAllFiles(data) .then(function(data) { - data.toolsResults.should.have.a.property('weightChecker'); - data.toolsResults.weightChecker.should.have.a.property('metrics'); - data.toolsResults.weightChecker.should.have.a.property('offenders'); + data.toolsResults.should.have.a.property('redownload'); + data.toolsResults.redownload.should.have.a.property('metrics'); + data.toolsResults.redownload.should.have.a.property('offenders'); - data.toolsResults.weightChecker.offenders.should.have.a.property('totalWeight'); - data.toolsResults.weightChecker.offenders.totalWeight.totalWeight.should.be.above(0); - data.toolsResults.weightChecker.offenders.totalWeight.byType.html.requests.length.should.equal(1); - data.toolsResults.weightChecker.offenders.totalWeight.byType.js.requests.length.should.equal(2); - data.toolsResults.weightChecker.offenders.totalWeight.byType.css.requests.length.should.equal(1); - data.toolsResults.weightChecker.offenders.totalWeight.byType.image.requests.length.should.equal(2); - data.toolsResults.weightChecker.offenders.totalWeight.byType.other.requests.length.should.equal(1); + data.toolsResults.redownload.offenders.should.have.a.property('totalWeight'); + data.toolsResults.redownload.offenders.totalWeight.totalWeight.should.be.above(0); + data.toolsResults.redownload.offenders.totalWeight.byType.html.requests.length.should.equal(1); + data.toolsResults.redownload.offenders.totalWeight.byType.js.requests.length.should.equal(2); + data.toolsResults.redownload.offenders.totalWeight.byType.css.requests.length.should.equal(1); + data.toolsResults.redownload.offenders.totalWeight.byType.image.requests.length.should.equal(2); + data.toolsResults.redownload.offenders.totalWeight.byType.other.requests.length.should.equal(1); - data.toolsResults.weightChecker.offenders.should.have.a.property('imageOptimization'); - data.toolsResults.weightChecker.offenders.imageOptimization.totalGain.should.be.above(0); - data.toolsResults.weightChecker.offenders.imageOptimization.images.length.should.equal(2); + data.toolsResults.redownload.offenders.should.have.a.property('imageOptimization'); + data.toolsResults.redownload.offenders.imageOptimization.totalGain.should.be.above(0); + data.toolsResults.redownload.offenders.imageOptimization.images.length.should.equal(2); - data.toolsResults.weightChecker.offenders.should.have.a.property('gzipCompression'); - data.toolsResults.weightChecker.offenders.gzipCompression.totalGain.should.be.above(0); - data.toolsResults.weightChecker.offenders.gzipCompression.files.length.should.equal(5); + data.toolsResults.redownload.offenders.should.have.a.property('gzipCompression'); + data.toolsResults.redownload.offenders.gzipCompression.totalGain.should.be.above(0); + data.toolsResults.redownload.offenders.gzipCompression.files.length.should.equal(5); - data.toolsResults.weightChecker.offenders.should.have.a.property('fileMinification'); - data.toolsResults.weightChecker.offenders.fileMinification.totalGain.should.be.above(0); - data.toolsResults.weightChecker.offenders.fileMinification.files.length.should.equal(2); + data.toolsResults.redownload.offenders.should.have.a.property('fileMinification'); + data.toolsResults.redownload.offenders.fileMinification.totalGain.should.be.above(0); + data.toolsResults.redownload.offenders.fileMinification.files.length.should.equal(2); - data.toolsResults.weightChecker.metrics.should.have.a.property('totalRequests').that.equals(7); - data.toolsResults.weightChecker.offenders.should.have.a.property('totalRequests'); - data.toolsResults.weightChecker.offenders.totalRequests.byType.html.length.should.equal(1); - data.toolsResults.weightChecker.offenders.totalRequests.byType.js.length.should.equal(2); - data.toolsResults.weightChecker.offenders.totalRequests.byType.css.length.should.equal(1); - data.toolsResults.weightChecker.offenders.totalRequests.byType.image.length.should.equal(2); - data.toolsResults.weightChecker.offenders.totalRequests.byType.json.length.should.equal(0); - data.toolsResults.weightChecker.offenders.totalRequests.byType.webfont.length.should.equal(0); - data.toolsResults.weightChecker.offenders.totalRequests.byType.video.length.should.equal(0); - data.toolsResults.weightChecker.offenders.totalRequests.byType.other.length.should.equal(1); + data.toolsResults.redownload.metrics.should.have.a.property('totalRequests').that.equals(7); + data.toolsResults.redownload.offenders.should.have.a.property('totalRequests'); + data.toolsResults.redownload.offenders.totalRequests.byType.html.length.should.equal(1); + data.toolsResults.redownload.offenders.totalRequests.byType.js.length.should.equal(2); + data.toolsResults.redownload.offenders.totalRequests.byType.css.length.should.equal(1); + data.toolsResults.redownload.offenders.totalRequests.byType.image.length.should.equal(2); + data.toolsResults.redownload.offenders.totalRequests.byType.json.length.should.equal(0); + data.toolsResults.redownload.offenders.totalRequests.byType.webfont.length.should.equal(0); + data.toolsResults.redownload.offenders.totalRequests.byType.video.length.should.equal(0); + data.toolsResults.redownload.offenders.totalRequests.byType.other.length.should.equal(1); - data.toolsResults.weightChecker.metrics.should.have.a.property('smallRequests').that.equals(0); - data.toolsResults.weightChecker.offenders.should.have.a.property('smallRequests'); - data.toolsResults.weightChecker.offenders.smallRequests.byType.js.length.should.equal(0); - data.toolsResults.weightChecker.offenders.smallRequests.byType.css.length.should.equal(0); - data.toolsResults.weightChecker.offenders.smallRequests.byType.image.length.should.equal(0); + data.toolsResults.redownload.metrics.should.have.a.property('smallRequests').that.equals(0); + data.toolsResults.redownload.offenders.should.have.a.property('smallRequests'); + data.toolsResults.redownload.offenders.smallRequests.byType.js.length.should.equal(0); + data.toolsResults.redownload.offenders.smallRequests.byType.css.length.should.equal(0); + data.toolsResults.redownload.offenders.smallRequests.byType.image.length.should.equal(0); done(); }) @@ -187,7 +187,7 @@ describe('weightChecker', function() { type: 'js' }; - weightChecker.redownloadEntry(entry) + redownload.redownloadEntry(entry) .then(function(newEntry) { @@ -221,7 +221,7 @@ describe('weightChecker', function() { contentType: 'image/png' }; - weightChecker.redownloadEntry(entry) + redownload.redownloadEntry(entry) .then(function(newEntry) { @@ -258,7 +258,7 @@ describe('weightChecker', function() { contentLength: 999 }; - weightChecker.redownloadEntry(entry) + redownload.redownloadEntry(entry) .then(function(newEntry) { newEntry.weightCheck.should.have.a.property('message').that.equals('error while downloading: 404'); @@ -285,7 +285,7 @@ describe('weightChecker', function() { contentLength: 999 }; - weightChecker.redownloadEntry(entry) + redownload.redownloadEntry(entry) .then(function(newEntry) { newEntry.should.not.have.a.property('weightCheck'); @@ -298,7 +298,7 @@ describe('weightChecker', function() { }); it('should listRequestWeight', function() { - var totalWeightObj = weightChecker.listRequestWeight([{ + var totalWeightObj = redownload.listRequestWeight([{ method: 'GET', url: 'http://localhost:8388/jquery1.8.3.js', requestHeaders: { @@ -328,7 +328,7 @@ describe('weightChecker', function() { }); it('should listRequestWeight even if download failed', function() { - var totalWeightObj = weightChecker.listRequestWeight([{ + var totalWeightObj = redownload.listRequestWeight([{ method: 'GET', url: 'http://localhost:8388/jquery1.8.3.js', requestHeaders: { diff --git a/test/www/jquery-page.html b/test/www/jquery-page.html index f2e3f16..ff03ef8 100644 --- a/test/www/jquery-page.html +++ b/test/www/jquery-page.html @@ -190,6 +190,11 @@ $li.parentsUntil('body', 'div'); $li.siblings(); $li.siblings('li'); + + $.ajax({ + url: 'xml.xml', + async: false + }); \ No newline at end of file