Bläddra i källkod

Merge pull request #69 from gmetais/develop

v1.4.0
Gaël Métais 10 år sedan
förälder
incheckning
1057836788
43 ändrade filer med 357 tillägg och 163 borttagningar
  1. 13 16
      Gruntfile.js
  2. 3 0
      README.md
  3. 4 0
      bin/cli.js
  4. 6 5
      bower.json
  5. 8 0
      front/src/css/dashboard.css
  6. 1 2
      front/src/css/icons.css
  7. 41 1
      front/src/css/index.css
  8. 1 2
      front/src/css/main.css
  9. BIN
      front/src/fonts/icons.woff
  10. 0 0
      front/src/fonts/svg-icons/arrow-left3.svg
  11. 0 0
      front/src/fonts/svg-icons/bars.svg
  12. 0 0
      front/src/fonts/svg-icons/lab.svg
  13. 0 0
      front/src/fonts/svg-icons/list.svg
  14. 0 0
      front/src/fonts/svg-icons/loop.svg
  15. 0 0
      front/src/fonts/svg-icons/mobile.svg
  16. 0 0
      front/src/fonts/svg-icons/question.svg
  17. 0 0
      front/src/fonts/svg-icons/screen.svg
  18. 0 0
      front/src/fonts/svg-icons/tablet.svg
  19. 0 0
      front/src/fonts/svg-icons/warning.svg
  20. 3 1
      front/src/js/app.js
  21. 1 6
      front/src/js/controllers/dashboardCtrl.js
  22. 7 2
      front/src/js/controllers/indexCtrl.js
  23. 1 1
      front/src/js/controllers/ruleCtrl.js
  24. 1 2
      front/src/js/controllers/screenshotCtrl.js
  25. 1 1
      front/src/js/controllers/timelineCtrl.js
  26. 2 2
      front/src/js/directives/offendersDirectives.js
  27. 7 4
      front/src/js/services/apiService.js
  28. 23 0
      front/src/js/services/settingsService.js
  29. 10 0
      front/src/less/dashboard.less
  30. 4 54
      front/src/less/icons.less
  31. 47 1
      front/src/less/index.less
  32. 67 29
      front/src/less/main.less
  33. 3 0
      front/src/main.html
  34. 2 2
      front/src/views/dashboard.html
  35. 10 1
      front/src/views/index.html
  36. 3 3
      front/src/views/resultSubHeader.html
  37. 1 1
      front/src/views/screenshot.html
  38. 2 2
      lib/metadata/policies.js
  39. 4 2
      lib/server/controllers/apiController.js
  40. 50 0
      lib/tools/phantomas/custom_modules/modules/domHiddenYLT/domHiddenYLT.js
  41. 11 7
      lib/tools/phantomas/phantomasWrapper.js
  42. 15 15
      package.json
  43. 5 1
      test/api/apiTest.js

+ 13 - 16
Gruntfile.js

@@ -11,17 +11,18 @@ module.exports = function(grunt) {
         pkg: grunt.file.readJSON('package.json'),
         settings: grunt.file.readJSON('./server_config/settings.json'),
         
-        font: {
+        webfont: {
             icons: {
-                src: ['front/src/fonts/svg-icons/*.svg'],
-                destCss: 'front/src/less/icons.less',
-                destFonts: 'front/src/fonts/icons.woff',
-
-                // Optional: Custom routing of font filepaths for CSS
-                cssRouter: function (fontpath) {
-                    var pathArray = fontpath.split('/');
-                    var fileName = pathArray[pathArray.length - 1];
-                    return '/fonts/' + fileName;
+                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'
                 }
             }
         },
@@ -102,7 +103,6 @@ module.exports = function(grunt) {
             },
             build: {
                 files: [
-                    {src: ['./front/src/fonts/icons.woff'], dest: './front/build/fonts/icons.woff'},
                     {src: ['./front/src/img/favicon.png'], dest: './front/build/img/favicon.png'},
                     {src: ['./front/src/img/logo-large.png'], dest: './front/build/img/logo-large.png'},
                 ]
@@ -211,10 +211,7 @@ module.exports = function(grunt) {
             html: './front/build/main.html',
             css: './front/build/css/*.css',
             options: {
-                assetsDirs: ['front/build'],
-                patterns: {
-                    css: [[/(\/fonts\/icons\.woff)/gm, 'Replacing reference to icons.woff']]
-                }
+                assetsDirs: ['front/build']
             }
         },
         htmlmin: {
@@ -294,7 +291,7 @@ module.exports = function(grunt) {
 
 
     grunt.registerTask('icons', [
-        'font:icons',
+        'webfont:icons',
         'less',
         'clean:tmp'
     ]);

+ 3 - 0
README.md

@@ -32,6 +32,9 @@ Can be used to build automation tools in NodeJS. The documentation is [here](htt
 #### The Public API:
 Hosted on our http://yellowlab.tools server, it is a RESTful API that allows you to launch distant tests. The documentation is [here](https://github.com/gmetais/YellowLabTools/wiki/Public-API).
 
+#### The Grunt task:
+If you have a Continuous Integration platform, may I suggest you to use [grunt-yellowlabtools](https://github.com/gmetais/grunt-yellowlabtools)? You set it up once, then the task fails if the front-end quality conditions you defined are not met.
+
 
 ## Install your own private instance
 

+ 4 - 0
bin/cli.js

@@ -12,6 +12,7 @@ var cli = meow({
         '  yellowlabtools <url> <options>',
         '',
         'Options:',
+        '  --device             Use "phone" or "tablet" to simulate a mobile device (by user-agent and viewport size).',
         '  --screenshot         Will take a screenshot and use this value as the output path. It needs to end with ".png".',
         '  --js-deep-analysis   When activated, the javascriptExecutionTree will contain sub-requests.',
         ''
@@ -50,6 +51,9 @@ if (cli.flags.jsDeepAnalysis === true || cli.flags.jsDeepAnalysis === 'true') {
     options.jsDeepAnalysis = true;
 }
 
+// Device simulation
+options.device = cli.flags.device || 'desktop';
+
 
 (function execute(url, options) {
     'use strict';

+ 6 - 5
bower.json

@@ -1,11 +1,12 @@
 {
   "name": "yellowlabtools",
   "dependencies": {
-    "angular": "~1.3.14",
-    "angular-route": "~1.3.14",
-    "angular-resource": "~1.3.14",
-    "angular-sanitize": "~1.3.14",
-    "angular-animate": "~1.3.14"
+    "angular": "~1.3.15",
+    "angular-route": "~1.3.15",
+    "angular-resource": "~1.3.15",
+    "angular-sanitize": "~1.3.15",
+    "angular-animate": "~1.3.15",
+    "angular-local-storage": "~0.1.5"
   },
   "resolutions": {
     "angular": "~1.3.8"

+ 8 - 0
front/src/css/dashboard.css

@@ -46,6 +46,14 @@
   content: "+";
   opacity: 0.85;
 }
+.summary .globalScore .screenshotWrapper.phone:hover:after {
+  top: 1.7em;
+  left: 0.64em;
+}
+.summary .globalScore .screenshotWrapper.tablet:hover:after {
+  top: 1.5em;
+  left: 0.9em;
+}
 .summary .notations {
   display: table;
   width: 80%;

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 2
front/src/css/icons.css


+ 41 - 1
front/src/css/index.css

@@ -1,8 +1,14 @@
 .promess {
-  padding: 0em 2em 3em;
+  padding: 0em 2em;
+  margin-bottom: 0.5em;
   font-weight: normal;
   font-size: 1.2em;
 }
+.price {
+  padding: 0em 2em 3em;
+  margin-top: 0em;
+  font-size: 0.9em;
+}
 .url {
   width: 50%;
 }
@@ -10,9 +16,43 @@
   background: #e74c3c;
   color: #fff;
 }
+.launchBtn:focus {
+  background: #ffa319;
+}
 .launchBtn.disabled {
   background: #deaca6;
 }
+.launchBtn.disabled:focus {
+  color: #ddd;
+}
+.device {
+  margin-top: 3em;
+}
+.device .item {
+  display: inline-block;
+  margin: 1em 0.75em;
+  width: 5.5em;
+  height: 5.5em;
+  color: #FFF;
+  border: 1px solid #FFF;
+  padding: 1px;
+  border-radius: 0.5em;
+  cursor: pointer;
+  text-decoration: none;
+  font-size: 0.8em;
+}
+.device .item.active {
+  color: #ffa319;
+  border: 2px solid #ffa319;
+  padding: 0;
+}
+.device .item:hover {
+  color: #ffa319;
+}
+.device .item > div {
+  margin: 0.2em 0 0.1em;
+  font-size: 3em;
+}
 .features {
   display: table;
   width: 50%;

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 2
front/src/css/main.css


BIN
front/src/fonts/icons.woff


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
front/src/fonts/svg-icons/arrow-left3.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
front/src/fonts/svg-icons/bars.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
front/src/fonts/svg-icons/lab.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
front/src/fonts/svg-icons/list.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
front/src/fonts/svg-icons/loop.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
front/src/fonts/svg-icons/mobile.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
front/src/fonts/svg-icons/question.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
front/src/fonts/svg-icons/screen.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
front/src/fonts/svg-icons/tablet.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
front/src/fonts/svg-icons/warning.svg


+ 3 - 1
front/src/js/app.js

@@ -12,8 +12,10 @@ var yltApp = angular.module('YellowLabTools', [
     'resultsFactory',
     'apiService',
     'menuService',
+    'settingsService',
     'gradeDirective',
-    'offendersDirectives'
+    'offendersDirectives',
+    'LocalStorageModule'
 ]);
 
 yltApp.run(['$rootScope', '$location', function($rootScope, $location) {

+ 1 - 6
front/src/js/controllers/dashboardCtrl.js

@@ -34,7 +34,7 @@ dashboardCtrl.controller('DashboardCtrl', ['$scope', '$rootScope', '$routeParams
     };
 
     $scope.testAgain = function() {
-        API.launchTest($scope.result.params.url);
+        API.relaunchTest($scope.result);
     };
 
     /// When comming from a social shared link, the user needs to click on "See full report" button to display the full dashboard.
@@ -59,10 +59,5 @@ dashboardCtrl.controller('DashboardCtrl', ['$scope', '$rootScope', '$routeParams
         window.open(url, 'sharer', 'top=' + winTop + ',left=' + winLeft + ',toolbar=0,status=0,width=' + winWidth + ',height=' + winHeight);
     }
 
-    // Returns the URL of the JSON result
-    $scope.getAPIUrl = function() {
-        return '/api/results/' + $scope.runId;
-    };
-
     loadResults();
 }]);

+ 7 - 2
front/src/js/controllers/indexCtrl.js

@@ -1,9 +1,14 @@
 var indexCtrl = angular.module('indexCtrl', []);
 
-indexCtrl.controller('IndexCtrl', ['$scope', '$location', 'API', function($scope, $location, API) {
+indexCtrl.controller('IndexCtrl', ['$scope', 'Settings', 'API', function($scope, Settings, API) {
+    
+    $scope.settings = Settings.getMergedSettings();
+    console.log($scope.settings);
+
     $scope.launchTest = function() {
         if ($scope.url) {
-            API.launchTest($scope.url);
+            Settings.saveSettings($scope.settings);
+            API.launchTest($scope.url, $scope.settings);
         }
     };
 }]);

+ 1 - 1
front/src/js/controllers/ruleCtrl.js

@@ -29,7 +29,7 @@ ruleCtrl.controller('RuleCtrl', ['$scope', '$rootScope', '$routeParams', '$locat
     };
 
     $scope.testAgain = function() {
-        API.launchTest($scope.result.params.url);
+        API.relaunchTest($scope.result);
     };
 
     loadResults();

+ 1 - 2
front/src/js/controllers/screenshotCtrl.js

@@ -10,7 +10,6 @@ screenshotCtrl.controller('ScreenshotCtrl', ['$scope', '$rootScope', '$routePara
             Results.get({runId: $routeParams.runId}, function(result) {
                 $rootScope.loadedResult = result;
                 $scope.result = result;
-                init();
             }, function(err) {
                 $scope.error = true;
             });
@@ -24,7 +23,7 @@ screenshotCtrl.controller('ScreenshotCtrl', ['$scope', '$rootScope', '$routePara
     };
 
     $scope.testAgain = function() {
-        API.launchTest($scope.result.params.url);
+        API.relaunchTest($scope.result);
     };
 
     loadResults();

+ 1 - 1
front/src/js/controllers/timelineCtrl.js

@@ -141,7 +141,7 @@ timelineCtrl.controller('TimelineCtrl', ['$scope', '$rootScope', '$routeParams',
     };
 
     $scope.testAgain = function() {
-        API.launchTest($scope.result.params.url);
+        API.relaunchTest($scope.result);
     };
 
     loadResults();

+ 2 - 2
front/src/js/directives/offendersDirectives.js

@@ -166,7 +166,7 @@
         
         // escape HTML in args
         for (var i = 0 ; i < 4 ; i ++) {
-            if (unescapedArgs[i]) {
+            if (unescapedArgs[i] !== undefined) {
                 args[i] = escapeHTML(unescapedArgs[i]);
             }
         }
@@ -254,7 +254,7 @@
                 return 'replace <b>' + args[0] + '</b> with ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
 
             case 'jQuery - text':
-                if (args[0]) {
+                if (args[0] !== undefined) {
                     return 'set text "<b>' + args[0] + '</b>" to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
                 } else {
                     return 'get text from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);

+ 7 - 4
front/src/js/services/apiService.js

@@ -4,12 +4,13 @@ apiService.factory('API', ['$location', 'Runs', 'Results', function($location, R
 
     return {
 
-        launchTest: function(url) {
+        launchTest: function(url, settings) {
             Runs.save({
                 url: url,
                 waitForResponse: false,
                 screenshot: true,
-                jsTimeline: true
+                jsTimeline: true,
+                device: settings.device
             }, function(data) {
                 $location.path('/queue/' + data.runId);
             }, function(response) {
@@ -19,9 +20,11 @@ apiService.factory('API', ['$location', 'Runs', 'Results', function($location, R
                     alert('An error occured...');
                 }
             });
-        }
-
+        },
 
+        relaunchTest: function(result) {
+            this.launchTest(result.params.url, result.params.options);
+        }
     };
 
 }]);

+ 23 - 0
front/src/js/services/settingsService.js

@@ -0,0 +1,23 @@
+var settingsService = angular.module('settingsService', []);
+
+settingsService.factory('Settings', ['localStorageService', function(localStorageService) {
+
+    return {
+
+        getMergedSettings: function() {
+            var defaultSettings = {
+                device: 'desktop'
+            };
+            
+            var savedValues = localStorageService.get('settings');
+
+            return angular.extend(defaultSettings, savedValues);
+        },
+
+        saveSettings: function(settings) {
+            localStorageService.set('settings', settings);
+        }
+
+    };
+
+}]);

+ 10 - 0
front/src/less/dashboard.less

@@ -51,6 +51,16 @@
             opacity: 0.85;
         }
     }
+
+    .screenshotWrapper.phone:hover:after {
+        top: 1.7em;
+        left: 0.64em;
+    }
+
+    .screenshotWrapper.tablet:hover:after {
+        top: 1.5em;
+        left: 0.9em;
+    }
 }
 
 .summary .notations {

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 4 - 54
front/src/less/icons.less


+ 47 - 1
front/src/less/index.less

@@ -1,9 +1,16 @@
 .promess {
-    padding: 0em 2em 3em;
+    padding: 0em 2em;
+    margin-bottom: 0.5em;
     font-weight: normal;
     font-size: 1.2em;
 }
 
+.price {
+    padding: 0em 2em 3em;
+    margin-top: 0em;
+    font-size: 0.9em;
+}
+
 .url {
     width: 50%;
 }
@@ -11,8 +18,47 @@
 .launchBtn {
     background: #e74c3c;
     color: #fff;
+    &:focus {
+        background: #ffa319;
+    }
     &.disabled {
         background: #deaca6;
+        &:focus {
+            color: #ddd;
+        }
+    }
+    
+}
+
+.device {
+    margin-top: 3em;
+    .item {
+        display: inline-block;
+        margin: 1em 0.75em;
+        width: 5.5em;
+        height: 5.5em;
+        color: #FFF;
+        border: 1px solid #FFF;
+        padding: 1px;
+        border-radius: 0.5em;
+        cursor: pointer;
+        text-decoration: none;
+        font-size: 0.8em;
+
+        &.active {
+            color: #ffa319;
+            border: 2px solid #ffa319;
+            padding: 0;
+        }
+
+        &:hover {
+            color: #ffa319;
+        }
+
+        > div {
+            margin: 0.2em 0 0.1em;
+            font-size: 3em;
+        }
     }
 }
 

+ 67 - 29
front/src/less/main.less

@@ -126,11 +126,23 @@ a.linkButton {
 }
 
 
-.screenshotWrapper.desktop {
+.screenshotWrapper {
     display: inline-block;
     position: relative;
-    border: 0.2em solid #AAA;
     background: #000;
+
+    > div {
+        overflow: scroll;
+        position: relative;
+    }
+
+    .screenshotImage {
+        width: 100%;
+    }
+}
+
+.screenshotWrapper.desktop {
+    border: 0.2em solid #AAA;
     padding: 0.5em;
     border-top-left-radius: 0.4em;
     border-top-right-radius: 0.4em;
@@ -160,12 +172,61 @@ a.linkButton {
     > div {
         width: 12em;
         height: 6.75em;
-        overflow: scroll;
-        position: relative;
     }
+}
 
-    .screenshotImage {
-        width: 100%;
+.screenshotWrapper.phone {
+    border: 0.07em solid #CCC;
+    padding: 1em 0.3em 1.5em;
+    border-radius: 0.6em;
+
+    &:before {
+        position: absolute;
+        width: 0.8em;
+        height: 0.8em;
+        bottom: 0.3em;
+        left: 3.3em;
+        border: 0.07em solid #CCC;
+        border-radius: 0.5em;
+        content: " ";
+    }
+
+    &:after {
+        position: absolute;
+        width: 1em;
+        height: 0.1em;
+        bottom: 13.9em;
+        left: 3.2em;
+        background: #555;
+        border-radius: 0.05em;
+        content: " ";
+    }
+
+    > div {
+        width: 6.75em;
+        height: 12em;
+    }
+}
+
+.screenshotWrapper.tablet {
+    border: 0.07em solid #CCC;
+    padding: 0.8em 0.5em 0.9em;
+    border-radius: 0.6em;
+
+    &:before {
+        position: absolute;
+        width: 0.5em;
+        height: 0.5em;
+        bottom: 0.15em;
+        left: 4.35em;
+        border: 0.07em solid #CCC;
+        border-radius: 0.4em;
+        content: " ";
+    }
+
+    > div {
+        width: 8.25em;
+        height: 11em;
     }
 }
 
@@ -186,27 +247,4 @@ a.linkButton {
     .version {
         font-size: 0.7em;
     }
-}
-
-/* Icons */
-.icon-lab {
-    .icon(@lab);
-}
-.icon-question {
-    .icon(@question);
-}
-.icon-warning {
-    .icon(@warning);
-}
-.icon-back {
-    .icon(@arrow-left3);
-}
-.icon-summary {
-    .icon(@list);
-}
-.icon-spaghetti {
-    .icon(@bars);
-}
-.icon-loop {
-    .icon(@loop);
 }

+ 3 - 0
front/src/main.html

@@ -24,6 +24,7 @@
     <script src="/bower_components/angular-resource/angular-resource.min.js"></script>
     <script src="/bower_components/angular-sanitize/angular-sanitize.min.js"></script>
     <script src="/bower_components/angular-animate/angular-animate.min.js"></script>
+    <script src="/bower_components/angular-local-storage/dist/angular-local-storage.min.js"></script>
     <script src="/js/app.js"></script>
     <script src="/js/controllers/indexCtrl.js"></script>
     <script src="/js/controllers/dashboardCtrl.js"></script>
@@ -35,6 +36,8 @@
     <script src="/js/models/runsFactory.js"></script>
     <script src="/js/services/apiService.js"></script>
     <script src="/js/services/menuService.js"></script>
+    <script src="/js/services/settingsService.js"></script>
+
     <script src="/js/directives/gradeDirective.js"></script>
     <script src="/js/directives/offendersDirectives.js"></script>
     <!-- endbuild -->

+ 2 - 2
front/src/views/dashboard.html

@@ -11,7 +11,7 @@
         </div>
         <div>
             <a href="/result/{{result.runId}}/screenshot">
-                <div class="screenshotWrapper desktop">
+                <div class="screenshotWrapper" ng-class="result.params.options.device">
                     <div>
                         <img class="screenshotImage" ng-src="{{result.screenshotUrl}}"/>
                     </div>
@@ -47,7 +47,7 @@
     </div>
 
     <div ng-if="!error && !fromSocialShare" class="apiTip">
-        <b>Did you know? Yellow Lab Tools now has an API</b>! <a href="{{getAPIUrl()}}" target="_blank">Here</a> is the JSON output for this run. Checkout <a href="https://github.com/gmetais/YellowLabTools/wiki/Public-API" target="_blank">the API doc</a>.
+        <b>Did you know? Yellow Lab Tools has a brand new <a href="https://github.com/gmetais/grunt-yellowlabtools" target="_blank">Grunt plugin</a>!
     </div>
 
     <div class="tweet" ng-if="!error && !fromSocialShare">

+ 10 - 1
front/src/views/index.html

@@ -1,8 +1,17 @@
-<h2 class="promess">Free online test to help speeding up <b>heavy</b> web pages</h2>
+<h2 class="promess">Online test to help speeding up <b>heavy</b> web pages</h2>
+<p class="price">Free and open source!</p>
 
 <form ng-submit="launchTest()" >
     <input type="text" name="url" ng-model="url" placeholder="http://www.mysite.com" class="url" />
     <input type="submit" value="Launch test" class="launchBtn" ng-class="{disabled: !url}" />
+    <div class="settings">
+        <div class="device">
+            <div>Choose the simulated device:</div>
+            <div class="item" ng-class="{active: settings.device == 'desktop'}" ng-click="settings.device = 'desktop'"><div class="icon-screen"></div>Desktop</div>
+            <div class="item" ng-class="{active: settings.device == 'tablet'}" ng-click="settings.device = 'tablet'"><div class="icon-tablet"></div>Tablet</div>
+            <div class="item" ng-class="{active: settings.device == 'phone'}" ng-click="settings.device = 'phone'"><div class="icon-mobile"></div>Phone</div>
+        </div>
+    </div>
 </form>
 
 

+ 3 - 3
front/src/views/resultSubHeader.html

@@ -1,8 +1,8 @@
 <div>Tested url: &nbsp; <a href="{{result.params.url}}" target="_blank" class="testedUrl">{{result.params.url}}</a></div>
 
 <div class="resultsMenu">
-    <a class="menuItem back" href="/"><div class="icon-back"></div><span>New test<span></a>
+    <a class="menuItem back" href="/"><div class="icon-arrow-left3"></div><span>New test<span></a>
     <a class="menuItem restart" href="" ng-click="testAgain()"><div class="icon-loop"></div><span>Test again<span></a>
-    <div class="menuItem" ng-class="{active: Menu.getCurrentPage() == 'dashboard'}" ng-click="Menu.changePage('dashboard')"><div class="icon-summary"></div><span>Dashboard</span></div>
-    <div class="menuItem" ng-class="{active: Menu.getCurrentPage() == 'timeline'}" ng-click="Menu.changePage('timeline')"><div class="icon-spaghetti"></div><span>JS Timeline</span></div>
+    <div class="menuItem" ng-class="{active: Menu.getCurrentPage() == 'dashboard'}" ng-click="Menu.changePage('dashboard')"><div class="icon-list"></div><span>Dashboard</span></div>
+    <div class="menuItem" ng-class="{active: Menu.getCurrentPage() == 'timeline'}" ng-click="Menu.changePage('timeline')"><div class="icon-bars"></div><span>JS Timeline</span></div>
 </div>

+ 1 - 1
front/src/views/screenshot.html

@@ -2,7 +2,7 @@
 <div class="screenshot board">
     <h2>Screenshot</h2>
 
-    <div class="screenshotWrapper desktop">
+    <div class="screenshotWrapper" ng-class="result.params.options.device">
         <div>
             <img class="screenshotImage" ng-src="{{result.screenshotUrl}}"/>
         </div>

+ 2 - 2
lib/metadata/policies.js

@@ -226,7 +226,7 @@ var policies = {
             };
         }
     },
-    "evalCalls": {
+    /*"evalCalls": {
         "tool": "phantomas",
         "label": "eval calls",
         "message": "<p>The 'eval' function is slow and is a bad coding practice. Try to get rid of it.</p>",
@@ -234,7 +234,7 @@ var policies = {
         "isBadThreshold": 10,
         "isAbnormalThreshold": 20,
         "hasOffenders": false
-    },
+    },*/
     "documentWriteCalls": {
         "tool": "phantomas",
         "label": "document.write calls",

+ 4 - 2
lib/server/controllers/apiController.js

@@ -33,7 +33,8 @@ var ApiController = function(app) {
                 waitForResponse: req.body.waitForResponse !== false && req.body.waitForResponse !== 'false' && req.body.waitForResponse !== 0,
                 partialResult: req.body.partialResult || null,
                 screenshot: req.body.screenshot || false,
-                jsTimeline: req.body.jsTimeline || false
+                jsTimeline: req.body.jsTimeline || false,
+                device: req.body.device || 'desktop'
             }
         };
 
@@ -66,7 +67,8 @@ var ApiController = function(app) {
 
             var runOptions = {
                 screenshot: run.params.screenshot ? screenshot.getTmpFilePath() : false,
-                jsDeepAnalysis: run.params.jsTimeline
+                jsDeepAnalysis: run.params.jsTimeline,
+                device: run.params.device
             };
 
             return ylt(run.params.url, runOptions);

+ 50 - 0
lib/tools/phantomas/custom_modules/modules/domHiddenYLT/domHiddenYLT.js

@@ -0,0 +1,50 @@
+/**
+ * Analyzes DOM hidden content
+ */
+/* global document: true, Node: true, window: true */
+
+exports.version = '0.1.a';
+
+exports.module = function(phantomas) {
+    'use strict';
+
+    // total length of HTML of hidden elements (i.e. display: none)
+    phantomas.setMetric('hiddenContentSize'); // @desc the size of content of hidden elements on the page (with CSS display: none) @offenders
+
+    // HTML size
+    phantomas.on('report', function() {
+        phantomas.evaluate(function() {
+            (function(phantomas) {
+                phantomas.spyEnabled(false, 'checking the hiddenContentSize');
+
+                var runner = new phantomas.nodeRunner();
+
+                runner.walk(document.body, function(node, depth) {
+                    switch (node.nodeType) {
+                        case Node.ELEMENT_NODE:
+                            // @see https://developer.mozilla.org/en/DOM%3awindow.getComputedStyle
+                            var styles = window.getComputedStyle(node);
+
+                            if (styles && styles.getPropertyValue('display') === 'none') {
+                                if (typeof node.innerHTML === 'string') {
+                                    var size = node.innerHTML.length;
+                                    phantomas.incrMetric('hiddenContentSize', size);
+
+                                    // log hidden containers bigger than 1 kB
+                                    if (size > 1024) {
+                                        phantomas.addOffender('hiddenContentSize', phantomas.getDOMPath(node) + ' (' + size + ' characters)');
+                                    }
+                                }
+
+                                // don't run for child nodes as they're hidden as well
+                                return false;
+                            }
+                            break;
+                    }
+                });
+
+                phantomas.spyEnabled(true);
+            }(window.__phantomas));
+        });
+    });
+};

+ 11 - 7
lib/tools/phantomas/phantomasWrapper.js

@@ -11,11 +11,6 @@ var PhantomasWrapper = function() {
 
     /**
      * This is the phantomas launcher. It merges user chosen options into the default options
-     * Available options :
-     *
-     * - timeout : in seconds (default 60)
-     * - jsDeepAnalysis : should we inspect subrequests in the javascript execution tree?
-     *
      */
     this.execute = function(data) {
 
@@ -27,7 +22,9 @@ var PhantomasWrapper = function() {
             // Cusomizable options
             'timeout': task.options.timeout || 60,
             'js-deep-analysis': task.options.jsDeepAnalysis || false,
-            'user-agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36',
+            'user-agent': (task.options.device === 'desktop') ? 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36' : null,
+            'tablet': (task.options.device === 'tablet'),
+            'phone': (task.options.device === 'phone'),
             'screenshot': task.options.screenshot || false,
 
             // Mandatory
@@ -35,6 +32,7 @@ var PhantomasWrapper = function() {
             'analyze-css': true,
             'skip-modules': [
                 'blockDomains', // not needed
+                'domHiddenContent', // overriden
                 'domMutations', // not compatible with webkit
                 'domQueries', // overriden
                 'eventListeners', // overridden
@@ -63,7 +61,13 @@ var PhantomasWrapper = function() {
                 value = '"' + value + '"';
             }
 
-            optionsString += ' ' + '--' + opt + '=' + value;
+            if (value === true) {
+                optionsString += ' ' + '--' + opt;
+            } else if (value === false || value === null) {
+                // Nothing
+            } else {
+                optionsString += ' ' + '--' + opt + '=' + value;
+            }
 
         }
         debug('node node_modules/phantomas/bin/phantomas.js --url=' + task.url + optionsString + ' --verbose');

+ 15 - 15
package.json

@@ -1,6 +1,6 @@
 {
   "name": "yellowlabtools",
-  "version": "1.3.2",
+  "version": "1.4.0",
   "description": "Online tool to audit a webpage for performance and front-end quality issues",
   "repository": {
     "type": "git",
@@ -12,21 +12,21 @@
   "main": "./lib/index.js",
   "dependencies": {
     "async": "~0.9.0",
-    "body-parser": "~1.12.1",
+    "body-parser": "~1.12.3",
     "compression": "~1.4.3",
     "cors": "^2.5.3",
     "debug": "~2.1.3",
-    "express": "~4.12.2",
+    "express": "~4.12.3",
     "lwip": "0.0.6",
-    "meow": "^3.0.0",
+    "meow": "^3.1.0",
     "phantomas": "1.10.0",
-    "ps-node": "0.0.3",
-    "q": "~1.1.2",
+    "ps-node": "0.0.4",
+    "q": "~1.2.1",
     "rimraf": "~2.3.2",
     "temporary": "0.0.8"
   },
   "devDependencies": {
-    "chai": "^2.1.2",
+    "chai": "^2.3.0",
     "grunt": "^0.4.5",
     "grunt-blanket": "^0.0.8",
     "grunt-contrib-clean": "^0.6.0",
@@ -34,22 +34,22 @@
     "grunt-contrib-copy": "^0.8.0",
     "grunt-contrib-cssmin": "^0.12.2",
     "grunt-contrib-htmlmin": "^0.4.0",
-    "grunt-contrib-jshint": "^0.11.0",
-    "grunt-contrib-less": "^1.0.0",
-    "grunt-contrib-uglify": "^0.8.0",
+    "grunt-contrib-jshint": "^0.11.2",
+    "grunt-contrib-less": "^1.0.1",
+    "grunt-contrib-uglify": "^0.9.1",
     "grunt-env": "^0.4.4",
     "grunt-express": "^1.4.1",
-    "grunt-filerev": "^2.1.2",
-    "grunt-fontsmith": "^0.9.1",
+    "grunt-filerev": "^2.3.1",
     "grunt-inline-angular-templates": "^0.1.5",
     "grunt-line-remover": "^0.0.2",
     "grunt-mocha-test": "^0.12.7",
     "grunt-replace": "^0.8.0",
     "grunt-usemin": "^3.0.0",
+    "grunt-webfont": "^0.5.2",
     "matchdep": "^0.3.0",
-    "mocha": "^2.2.1",
-    "request": "^2.53.0",
-    "sinon": "^1.14.0",
+    "mocha": "^2.2.4",
+    "request": "^2.55.0",
+    "sinon": "^1.14.1",
     "sinon-chai": "^2.7.0"
   },
   "scripts": {

+ 5 - 1
test/api/apiTest.js

@@ -96,7 +96,8 @@ describe('api', function() {
             body: {
                 url: wwwUrl + '/simple-page.html',
                 waitForResponse: true,
-                screenshot: true
+                screenshot: true,
+                device: 'tablet'
             },
             json: true,
             headers: {
@@ -164,6 +165,9 @@ describe('api', function() {
                 body.should.have.a.property('javascriptExecutionTree').that.is.an('object');
                 body.javascriptExecutionTree.should.deep.equal({});
 
+                // Check if the device is set to tablet
+                body.params.options.should.have.a.property('device').that.equals('tablet');
+
                 // Check if the screenshot temporary file was correctly removed
                 body.params.options.should.not.have.a.property('screenshot');
                 // Check if the screenshot buffer was correctly removed

Vissa filer visades inte eftersom för många filer har ändrats