Explorar o código

Enhancing index, view and dashboard ui

achrafbenyounes %!s(int64=10) %!d(string=hai) anos
pai
achega
c4a7b1f129

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

@@ -0,0 +1,479 @@
+/* Timeline colors, related to Window Performances */
+.resultsMenu {
+  margin-top: 2em;
+}
+.resultsMenu .menuItem {
+  display: inline-block;
+  margin: 1em;
+  width: 8em;
+  height: 7em;
+  color: #fff;
+  border: 3px solid #fff;
+  border-radius: 0.5em;
+  cursor: pointer;
+  text-decoration: none;
+}
+.resultsMenu .back {
+  color: #5e2846;
+  border-color: #5e2846;
+}
+.resultsMenu .menuItem div {
+  padding-top: 0.5em;
+  font-size: 3em;
+}
+.resultsMenu .active,
+.resultsMenu .menuItem.active:hover {
+  color: #ffa319;
+  border-color: #ffa319;
+}
+.resultsMenu .menuItem:hover {
+  color: #ffa319;
+}
+.resultsMenu span {
+  position: relative;
+  top: 0.5em;
+}
+.testedUrl {
+  color: inherit;
+}
+h4 {
+  margin-bottom: 0.5em;
+}
+.summary,
+.metrics,
+.execution {
+  margin-top: 2em;
+  padding: 1em;
+  background: #fff;
+  color: #000;
+  border-radius: 0.5em;
+  text-align: left;
+}
+.notations {
+  display: table;
+  width: 90%;
+  margin: 0 10%;
+  border-spacing: 1em;
+}
+.notations > div {
+  display: table-row;
+}
+.notations > div > div {
+  display: table-cell;
+  height: 2.5em;
+  vertical-align: middle;
+}
+.notations .notation {
+  font-weight: bold;
+  text-align: center;
+}
+.notations .criteria {
+  font-weight: normal;
+}
+.notations .A,
+.notations .B,
+.notations .C,
+.notations .D,
+.notations .E,
+.notations .F,
+.notations .NA {
+  width: 2.5em;
+  font-size: 2em;
+  text-align: center;
+  border-radius: 0.5em;
+  font-weight: bold;
+}
+.notations .grade .A,
+.notations .grade .B,
+.notations .grade .C,
+.notations .grade .D,
+.notations .grade .E,
+.notations .grade .F,
+.notations .grade .NA {
+  width: 1em;
+  height: 1em;
+  font-size: 1em;
+  color: transparent;
+  position: relative;
+  top: 0.15em;
+}
+.notations .A {
+  /* green */
+  background: #00DB61;
+}
+.notations .B {
+  /* green */
+  background: #CAD600;
+}
+.notations .C {
+  /* yellow */
+  background: #FFD119;
+}
+.notations .D {
+  /* orange */
+  background: #FFA319;
+}
+.notations .E {
+  /* red */
+  background: #FF6600;
+}
+.notations .F {
+  /* red */
+  background: #FF1919;
+}
+.notations .NA {
+  /* Non applicable */
+  background: #CCC;
+}
+.notations .icon-eye {
+  color: #9c4274;
+  cursor: pointer;
+}
+.notations .criteria .table {
+  width: 75%;
+}
+.notations .criteria .label {
+  width: 70%;
+}
+.notations .criteria .result {
+  width: 20%;
+  font-weight: bold;
+  white-space: nowrap;
+  text-align: center;
+}
+.notations .warning .label,
+.notations .warning .result {
+  color: #FF1919;
+}
+.notations .criteria .info {
+  width: 10%;
+  text-align: center;
+}
+.notations .criteria .icon-question {
+  color: #f1c40f;
+  cursor: pointer;
+}
+.timeline {
+  margin: 2em 0 5em;
+}
+.timeline .chart {
+  position: relative;
+  width: 100%;
+  border-bottom: 1px solid #000;
+}
+.timeline .startTime,
+.timeline .endTime {
+  position: absolute;
+  bottom: 0.5em;
+  font-size: 0.8em;
+}
+.timeline .startTime {
+  left: 0em;
+}
+.timeline .endTime {
+  right: 0em;
+}
+.timeline .chartPoints {
+  display: table;
+  height: 100px;
+  width: 99%;
+  margin: 0 auto;
+}
+.timeline .interval {
+  display: table-cell;
+  position: relative;
+  height: 100px;
+  width: 0.5%;
+}
+.timeline .interval .color {
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+}
+.timeline div.interval:hover {
+  background: #9C4274;
+}
+.timeline .interval:hover .color {
+  background: #F04DA7;
+}
+.timeline .domComplete.interval {
+  background: #ede3ff;
+}
+.timeline .domComplete .color {
+  background: #c2a3ff;
+}
+.timeline .domContentLoadedEnd.interval {
+  background: #d8f0f0;
+}
+.timeline .domContentLoadedEnd .color {
+  background: #7ecccc;
+}
+.timeline .domContentLoaded.interval {
+  background: #e0ffd1;
+}
+.timeline .domContentLoaded .color {
+  background: #a7e846;
+}
+.timeline .domInteractive.interval {
+  background: #fffccc;
+}
+.timeline .domInteractive .color {
+  background: #ffe433;
+}
+.timeline .domCreation.interval {
+  background: #ffe0cc;
+}
+.timeline .domCreation .color {
+  background: #ff6600;
+}
+.timeline .tooltip.detailsOverlay {
+  position: absolute;
+  display: none;
+  width: auto;
+  padding: 0.5em 1em;
+  top: -1.5em;
+  right: 1em;
+}
+.timeline .interval:hover .tooltip {
+  display: block;
+}
+.timeline .legend {
+  display: table;
+  width: 100%;
+  margin-top: 1em;
+}
+.timeline .legend > div {
+  display: table-row;
+}
+.timeline .legend > div > div {
+  position: relative;
+  display: table-cell;
+  margin-top: 1em;
+}
+.timeline .titles {
+  font-weight: bold;
+}
+.timeline .titles > div {
+  padding: 0 1em 0 2em;
+}
+.timeline .tips {
+  font-size: 0.7em;
+}
+.timeline .tips > div {
+  padding: 1em 1em 0 0;
+}
+.timeline .legend .color {
+  display: block;
+  position: absolute;
+  left: 0;
+  height: 1.5em;
+  width: 1.5em;
+  border-radius: 0.2em;
+}
+.metrics h4 {
+  padding-left: 2em;
+}
+.metrics .module {
+  padding-left: 4em;
+  padding-top: 0.5em;
+}
+.metrics .legend {
+  font-style: italic;
+  color: #aaa;
+}
+.metrics .offenders {
+  padding-left: 0em;
+  font-size: 0.8em;
+}
+.metrics .offenders div {
+  cursor: pointer;
+}
+.metrics .offenders ul {
+  margin-top: 0.5em;
+}
+.filters {
+  margin: 1em 0;
+  padding: 0.5em;
+  border: 1px dotted #aaa;
+}
+.slowRequestsLimit {
+  width: 3em;
+  font-size: 1em;
+  text-align: right;
+  border: 1px solid #aaa;
+}
+input.textFilter {
+  box-shadow: none;
+  font-size: 1em;
+  padding: 0 0.2em;
+  border: 1px solid #aaa;
+  border-radius: none;
+  width: 15em;
+}
+.table {
+  display: table;
+  width: 100%;
+  border-spacing: 0.25em;
+}
+.table > div {
+  display: table-row;
+}
+.table > .headers > div {
+  font-weight: bold;
+  padding: 0.5em 1em;
+}
+.table > div > div {
+  padding: 0.1em 1em;
+  background: #f2f2f2;
+  display: table-cell;
+  text-align: left;
+}
+.table > div.jsError > .type,
+.table > div.jsError > .value {
+  color: #e74c3c;
+  font-weight: bold;
+}
+.table > div.windowPerformance > div,
+.table > div.windowPerformance > div.startTime {
+  background: #EBD8E2;
+}
+.table > div.showingDetails > div {
+  background: #f1c40f;
+}
+.table > div > .index {
+  color: #bbb;
+}
+.table > div > .type {
+  white-space: nowrap;
+}
+.table > div > .value {
+  width: 70%;
+  word-break: break-all;
+}
+.table > div > .details {
+  position: relative;
+}
+.table .details .icon-question {
+  color: #f1c40f;
+  cursor: pointer;
+}
+.table .details .icon-warning {
+  cursor: pointer;
+}
+.detailsOverlay {
+  position: absolute;
+  right: 3em;
+  top: -3em;
+  width: 45em;
+  min-height: 1em;
+  padding: 0 1em 1em;
+  background: #fff;
+  border: 2px solid #f1c40f;
+  border-radius: 0.5em;
+  z-index: 1;
+}
+@media screen and (max-width: 1024px) {
+  .detailsOverlay {
+    width: 25em;
+  }
+}
+.detailsOverlay .closeBtn {
+  position: absolute;
+  top: 0.5em;
+  right: 0.5em;
+  color: #f1c40f;
+  cursor: pointer;
+}
+.detailsOverlay .advice {
+  color: #e74c3c;
+  font-weight: bold;
+}
+.detailsOverlay .trace {
+  word-break: break-all;
+}
+.table > div > .duration,
+.table > div > .startTime {
+  text-align: center;
+  white-space: nowrap;
+}
+.table > div > .startTime.domComplete {
+  background: #ede3ff;
+}
+.table > div > .startTime.domContentLoadedEnd {
+  background: #d8f0f0;
+}
+.table > div > .startTime.domContentLoaded {
+  background: #e0ffd1;
+}
+.table > div > .startTime.domInteractive {
+  background: #fffccc;
+}
+.table > div > .startTime.domCreation {
+  background: #ffe0cc;
+}
+.table .icon-warning {
+  color: #e74c3c;
+}
+/**** NgModal popin (have a look inside bower_components) ****/
+.ng-modal {
+  position: fixed;
+  z-index: 9999;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  text-align: left;
+}
+.ng-modal-overlay {
+  position: absolute;
+  z-index: 9999;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: #000;
+  opacity: 0.5;
+}
+.ng-modal-dialog {
+  z-index: 10000;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  width: 50%;
+  transform: translate(-50%, -50%);
+  -webkit-transform: translate(-50%, -50%);
+  background-color: #fff;
+  padding: 10px;
+  border: 3px solid #f1c40f;
+  border-radius: 0.5em;
+  color: #000;
+}
+.ng-modal-dialog-content {
+  overflow-x: hidden;
+  overflow-y: scroll;
+  word-wrap: break-word;
+  max-height: 20em;
+  font-weight: normal;
+  white-space: normal;
+}
+.ng-modal-close {
+  position: absolute;
+  top: 3px;
+  right: 5px;
+  cursor: pointer;
+  font-size: 120%;
+  padding: 5px;
+  display: inline-block;
+}
+.ng-modal-close-x {
+  font-weight: bold;
+  font-family: Arial, sans-serif;
+}
+.ng-modal-title {
+  font-weight: bold;
+  font-size: 1.5em;
+  display: block;
+  margin-bottom: 10px;
+  padding-bottom: 7px;
+  border-bottom: solid 1px #999;
+}

+ 6 - 0
front/src/css/icons.css

@@ -0,0 +1,6 @@
+@font-face {
+  font-family: "fontsmith-icons";
+  src: url("/front/fonts/icons.woff") format("woff");
+  font-weight: normal;
+  font-style: normal;
+}

+ 36 - 0
front/src/css/index.css

@@ -0,0 +1,36 @@
+.promess {
+  padding: 0em 2em 3em;
+  font-weight: normal;
+  font-size: 1.2em;
+}
+.url {
+  width: 50%;
+}
+.launchBtn {
+  background: #e74c3c;
+  color: #fff;
+}
+.launchBtn.disabled {
+  background: #deaca6;
+}
+input[type=submit],
+input.url {
+  padding: 0 0.5em;
+  margin: 0.5em;
+  font-size: 1.2em;
+  height: 2em;
+  border: 0 solid;
+  border-radius: 0.5em;
+  box-shadow: 0.1em 0.2em 0 0 #5e2846;
+  outline: none;
+}
+input[type=submit]:hover {
+  color: #ddd;
+}
+input[type=submit].clicked {
+  color: #ddd;
+  position: relative;
+  left: 0.1em;
+  top: 0.2em;
+  box-shadow: none;
+}

+ 139 - 0
front/src/css/main.css

@@ -0,0 +1,139 @@
+@font-face {
+  font-family: "fontsmith-icons";
+  src: url("/front/fonts/icons.woff") format("woff");
+  font-weight: normal;
+  font-style: normal;
+}
+html {
+  margin: 100px 50px;
+}
+body {
+  margin: 0 auto;
+  max-width: 1280px;
+  background: #9c4274;
+  color: #fff;
+  font-size: 16px;
+  text-align: center;
+}
+body,
+input[type=submit],
+input[type=text],
+input[type=url],
+input[type=number],
+button {
+  font-family: 'Century Gothic', helvetica, arial, sans-serif;
+}
+input[type=submit] {
+  cursor: pointer;
+}
+h1 span {
+  display: inline-block;
+  height: 1em;
+  width: 1em;
+  color: #ffa319;
+}
+.footer {
+  padding: 3em;
+  color: #4e1836;
+}
+.footer a {
+  color: inherit;
+}
+.footer .star {
+  font-weight: bold;
+}
+.footer .star span {
+  font-size: 1.2em;
+}
+/* Icons */
+.icon-lab {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-lab:before {
+  content: "\e003";
+}
+.icon-question {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-question:before {
+  content: "\e001";
+}
+.icon-warning {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-warning:before {
+  content: "\e000";
+}
+.icon-back {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-back:before {
+  content: "\e006";
+}
+.icon-summary {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-summary:before {
+  content: "\e002";
+}
+.icon-spaghetti {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-spaghetti:before {
+  content: "\e005";
+}
+.icon-eye {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-eye:before {
+  content: "\e004";
+}

+ 25 - 0
front/src/css/queue.css

@@ -0,0 +1,25 @@
+.status {
+  margin-top: 2em;
+  font-size: 2.5em;
+}
+@-webkit-keyframes rotating {
+  from {
+    -webkit-transform: rotate(0deg);
+  }
+  to {
+    -webkit-transform: rotate(360deg);
+  }
+}
+@keyframes rotating {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+.waiting .icon-lab {
+  -webkit-animation: rotating 3s linear 0s infinite;
+  -webkit-transform: translateZ(0);
+  animation: rotating 3s linear 0s infinite;
+}

BIN=BIN
front/src/fonts/icons.woff


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
front/src/fonts/svg-icons/arrow-left3.svg


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
front/src/fonts/svg-icons/bars.svg


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
front/src/fonts/svg-icons/eye.svg


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
front/src/fonts/svg-icons/lab.svg


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
front/src/fonts/svg-icons/list.svg


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
front/src/fonts/svg-icons/question.svg


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
front/src/fonts/svg-icons/warning.svg


+ 23 - 0
front/src/js/controllers/queueCtrl.js

@@ -0,0 +1,23 @@
+var queueCtrl = angular.module('queueCtrl', ['runsFactory']);
+
+queueCtrl.controller('QueueCtrl', ['$scope', '$routeParams', '$location', 'Runs', function($scope, $routeParams, $location, Runs) {
+    $scope.runId = $routeParams.runId;
+    
+    function getRunStatus () {
+        Runs.get({runId: $scope.runId}, function(data) {
+            $scope.status = data.status;
+            if (data.status.statusCode === 'running' || data.status.statusCode === 'awaiting') {
+                // Retrying in 2 seconds
+                setTimeout(getRunStatus, 2000);
+            } else if (data.status.statusCode === 'complete') {
+                $location.path('/result/' + $scope.runId);
+            } else {
+                // Handled by the view
+            }
+        });
+    }
+    
+    getRunStatus();
+}]);
+
+    

+ 34 - 0
front/src/js/directives/gradeDirective.js

@@ -0,0 +1,34 @@
+var gradeDirective = angular.module('gradeDirective', []);
+
+gradeDirective.directive('grade', function() {
+ 
+    return {
+        restrict: 'E',
+        scope: {
+            score: '=score'
+        },
+        template: '<div ng-class="getGrade(score)">{{getGrade(score)}}</div>',
+        replace: true,
+        controller : function($scope) {
+            $scope.getGrade = function(score) {
+                console.log(score);
+                if (score >= 85) {
+                    return 'A';
+                }
+                if (score >= 65) {
+                    return 'B';
+                }
+                if (score >= 45) {
+                    return 'C';
+                }
+                if (score >= 30) {
+                    return 'D';
+                }
+                if (score >= 15) {
+                    return 'E';
+                }
+                return 'F';
+            }
+        }
+    };
+});

+ 7 - 0
front/src/js/models/runsFactory.js

@@ -0,0 +1,7 @@
+var runsFactory = angular.module('runsFactory', ['ngResource']);
+
+runsFactory.factory('Runs', ['$resource', function($resource) {
+    return $resource('/api/runs/:runId', {
+    
+    });
+}]);

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

@@ -0,0 +1,507 @@
+
+/* Timeline colors, related to Window Performances */
+@domCreationColor: #FF6600;
+@domCreationBg: #FFE0CC;
+@domContentLoadedColor: #A7E846;
+@domContentLoadedBg: #E0FFD1;
+@domContentLoadedEndColor: #7ECCCC;
+@domContentLoadedEndBg: #D8F0F0;
+@domCompleteColor: #C2A3FF;
+@domCompleteBg: #EDE3FF;
+@domInteractiveColor: #FFE433;
+@domInteractiveBg: #FFFCCC;
+
+
+.resultsMenu {
+    margin-top: 2em;
+}
+
+.resultsMenu .menuItem {
+    display: inline-block;
+    margin: 1em;
+    width: 8em;
+    height: 7em;
+    color: #fff;
+    border: 3px solid #fff;
+    border-radius: 0.5em;
+    cursor: pointer;
+    text-decoration: none;
+}
+.resultsMenu .back {
+    color: #5e2846;
+    border-color: #5e2846;
+}
+.resultsMenu .menuItem div {
+    padding-top: 0.5em;
+    font-size: 3em;
+}
+.resultsMenu .active, .resultsMenu .menuItem.active:hover {
+    color: #ffa319;
+    border-color: #ffa319;
+}
+.resultsMenu .menuItem:hover {
+    color: #ffa319;
+}
+
+.resultsMenu span {
+    position: relative;
+    top: 0.5em;
+}
+
+.testedUrl {
+    color: inherit;
+}
+
+h4 {
+    margin-bottom: 0.5em;
+}
+
+.summary, .metrics, .execution {
+    margin-top: 2em;
+    padding: 1em;
+    background: #fff;
+    color: #000;
+    border-radius: 0.5em;
+    text-align: left;
+}
+
+.notations {
+    display: table;
+    width: 90%;
+    margin: 0 10%;
+    border-spacing: 1em;
+}
+.notations > div {
+    display: table-row;
+}
+.notations > div > div {
+    display: table-cell;
+    height: 2.5em;
+    vertical-align: middle;
+}
+.notations .notation {
+    font-weight: bold;
+    text-align: center;
+}
+.notations .criteria {
+    font-weight: normal;
+}
+.A, .B, .C, .D, .E, .F, .NA {
+    .notations & {
+        width: 2.5em;
+        font-size: 2em;
+        text-align: center;
+        border-radius: 0.5em;
+        font-weight: bold;
+    }
+    .notations .grade & {
+        width: 1em;
+        height: 1em;
+        font-size: 1em;
+        color: transparent;
+        position: relative;
+        top: 0.15em;
+    }
+}
+.notations .A {
+    /* green */
+    background: #00DB61;
+}
+.notations .B {
+    /* green */
+    background: #CAD600;
+}
+.notations .C {
+    /* yellow */
+    background: #FFD119;
+}
+.notations .D {
+    /* orange */
+    background: #FFA319;
+}
+.notations .E {
+    /* red */
+    background: #FF6600;
+}
+.notations .F {
+    /* red */
+    background: #FF1919;
+}
+.notations .NA {
+    /* Non applicable */
+    background: #CCC;
+}
+.notations .icon-eye {
+    color: #9c4274;
+    cursor: pointer;
+}
+
+.notations .criteria .table {
+    width: 75%;
+}
+.notations .criteria .label {
+    width: 70%;
+}
+.notations .criteria .result {
+    width: 20%;
+    font-weight: bold;
+    white-space: nowrap;
+    text-align: center;
+}
+.notations .warning .label, .notations .warning .result {
+    color: #FF1919;
+}
+.notations .criteria .info {
+    width: 10%;
+    text-align: center;
+}
+.notations .criteria .icon-question {
+    color: #f1c40f;
+    cursor: pointer;
+}
+
+
+.timeline {
+    margin: 2em 0 5em;
+}
+.timeline .chart {
+    position: relative;
+    width: 100%;
+    border-bottom: 1px solid #000;
+}
+.timeline .startTime, .timeline .endTime {
+    position: absolute;
+    bottom: 0.5em;
+    font-size: 0.8em;
+}
+.timeline .startTime {
+    left: 0em;
+}
+.timeline .endTime {
+    right: 0em;
+}
+.timeline .chartPoints {
+    display: table;
+    height: 100px;
+    width: 99%;
+    margin: 0 auto;
+}
+.timeline .interval {
+    display: table-cell;
+    position: relative;
+    height: 100px;
+    width: 0.5%;
+}
+.timeline .interval .color {
+    position: absolute;
+    bottom: 0;
+    width: 100%;
+}
+.timeline div.interval:hover {
+    background: #9C4274;
+}
+.timeline .interval:hover .color {
+    background: #F04DA7;
+}
+.timeline .domComplete.interval {
+    background: @domCompleteBg;
+}
+.timeline .domComplete .color {
+    background: @domCompleteColor;
+}
+.timeline .domContentLoadedEnd.interval {
+    background: @domContentLoadedEndBg;
+}
+.timeline .domContentLoadedEnd .color {
+    background: @domContentLoadedEndColor;
+}
+.timeline .domContentLoaded.interval {
+    background: @domContentLoadedBg;
+}
+.timeline .domContentLoaded .color {
+    background: @domContentLoadedColor;
+}
+.timeline .domInteractive.interval {
+    background: @domInteractiveBg;
+}
+.timeline .domInteractive .color {
+    background: @domInteractiveColor;
+}
+.timeline .domCreation.interval {
+    background: @domCreationBg;
+}
+.timeline .domCreation .color {
+    background: @domCreationColor;
+}
+.timeline .tooltip.detailsOverlay {
+    position: absolute;
+    display: none;
+    width: auto;
+    padding: 0.5em 1em;
+    top: -1.5em;
+    right: 1em;
+}
+.timeline .interval:hover .tooltip {
+    display: block;
+}
+
+.timeline .legend {
+    display: table;
+    width: 100%;
+    margin-top: 1em;
+}
+.timeline .legend > div {
+    display: table-row;
+}
+.timeline .legend > div > div {
+    position: relative;
+    display: table-cell;
+    margin-top: 1em;
+}
+.timeline .titles {
+    font-weight: bold;
+}
+.timeline .titles > div {
+    padding: 0 1em 0 2em;
+}
+.timeline .tips {
+    font-size: 0.7em;
+}
+.timeline .tips > div {
+    padding: 1em 1em 0 0;
+}
+.timeline .legend .color {
+    display: block;
+    position: absolute;
+    left: 0;
+    height: 1.5em;
+    width: 1.5em;
+    border-radius: 0.2em;
+}
+
+
+.metrics h4 {
+    padding-left: 2em;
+}
+
+.metrics .module {
+    padding-left: 4em;
+    padding-top: 0.5em;
+}
+
+.metrics .legend {
+    font-style: italic;
+    color: #aaa;
+}
+
+.metrics .offenders {
+    padding-left: 0em;
+    font-size: 0.8em;
+}
+
+.metrics .offenders div {
+    cursor: pointer;
+}
+
+.metrics .offenders ul {
+    margin-top: 0.5em;
+}
+
+.filters {
+    margin: 1em 0;
+    padding: 0.5em;
+    border: 1px dotted #aaa;
+}
+
+.slowRequestsLimit {
+    width: 3em;
+    font-size: 1em;
+    text-align: right;
+    border: 1px solid #aaa;
+}
+
+input.textFilter {
+    box-shadow: none;
+    font-size: 1em;
+    padding: 0 0.2em;
+    border: 1px solid #aaa;
+    border-radius: none;
+    width: 15em;
+}
+
+.table {
+    display: table;
+    width: 100%;
+    border-spacing: 0.25em;
+}
+
+.table > div {
+    display: table-row;
+}
+
+.table > .headers > div {
+    font-weight: bold;
+    padding: 0.5em 1em;
+}
+
+.table > div > div {
+    padding: 0.1em 1em;
+    background: #f2f2f2;
+    display: table-cell;
+    text-align: left;
+}
+.table > div.jsError > .type, .table > div.jsError > .value {
+    color: #e74c3c;
+    font-weight: bold;
+}
+.table > div.windowPerformance > div, .table > div.windowPerformance > div.startTime {
+    background: #EBD8E2;
+}
+.table > div.showingDetails > div {
+    background: #f1c40f;
+}
+
+.table > div > .index {
+    color: #bbb;
+}
+
+.table > div > .type {
+    white-space:nowrap;
+}
+
+.table > div > .value {
+    width: 70%;
+    word-break: break-all;
+}
+
+.table > div > .details {
+    position: relative;
+}
+.table .details .icon-question {
+    color: #f1c40f;
+    cursor: pointer;
+}
+.table .details .icon-warning {
+    cursor: pointer;
+}
+
+.detailsOverlay {
+    position: absolute;
+    right: 3em;
+    top: -3em;
+    width: 45em;
+    min-height: 1em;
+    padding: 0 1em 1em;
+    background: #fff;
+    border: 2px solid #f1c40f;
+    border-radius: 0.5em;
+    z-index: 1;
+}
+@media screen and (max-width: 1024px) {
+    .detailsOverlay {
+        width: 25em;
+    }
+}
+.detailsOverlay .closeBtn {
+    position: absolute;
+    top: 0.5em;
+    right: 0.5em;
+    color: #f1c40f;
+    cursor: pointer;
+}
+.detailsOverlay .advice {
+    color: #e74c3c;
+    font-weight: bold;
+}
+.detailsOverlay .trace {
+    word-break: break-all;
+}
+
+.table > div > .duration, .table > div > .startTime {
+    text-align: center;
+    white-space:nowrap;
+}
+.table > div > .startTime.domComplete {
+    background: @domCompleteBg;
+}
+.table > div > .startTime.domContentLoadedEnd {
+    background: @domContentLoadedEndBg;
+}
+.table > div > .startTime.domContentLoaded {
+    background: @domContentLoadedBg;
+}
+.table > div > .startTime.domInteractive {
+    background: @domInteractiveBg;
+}
+.table > div > .startTime.domCreation {
+    background: @domCreationBg;
+}
+
+.table .icon-warning {
+    color: #e74c3c;
+}
+
+
+/**** NgModal popin (have a look inside bower_components) ****/
+.ng-modal {
+    position: fixed;
+    z-index: 9999;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    text-align: left;
+}
+.ng-modal-overlay {
+    position: absolute;
+    z-index: 9999;
+    top: 0;
+    left: 0;
+    width: 100%;
+    height: 100%;
+    background-color: #000;
+    opacity: 0.5;
+}
+.ng-modal-dialog {
+    z-index: 10000;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    width: 50%;
+    transform: translate(-50%, -50%);
+    -webkit-transform: translate(-50%, -50%);
+    background-color: #fff;
+    padding: 10px;
+    border: 3px solid #f1c40f;
+    border-radius: 0.5em;
+    color: #000;
+}
+.ng-modal-dialog-content {
+    overflow-x: hidden;
+    overflow-y: scroll;
+    word-wrap: break-word;
+    max-height: 20em;
+    font-weight: normal;
+    white-space: normal;
+}
+.ng-modal-close {
+    position: absolute;
+    top: 3px;
+    right: 5px;
+    cursor: pointer;
+    font-size: 120%;
+    padding: 5px;
+    display: inline-block;
+}
+.ng-modal-close-x {
+    font-weight: bold;
+    font-family: Arial, sans-serif;
+}
+.ng-modal-title {
+    font-weight: bold;
+    font-size: 1.5em;
+    display: block;
+    margin-bottom: 10px;
+    padding-bottom: 7px;
+    border-bottom: solid 1px #999;
+}

+ 56 - 0
front/src/less/icons.less

@@ -0,0 +1,56 @@
+@arrow-left3-font-family: "fontsmith-icons";
+@arrow-left3-value: "\e006";
+@arrow-left3: '"fontsmith-icons"' '"\\e006"';
+@lab-font-family: "fontsmith-icons";
+@lab-value: "\e003";
+@lab: '"fontsmith-icons"' '"\\e003"';
+@warning-font-family: "fontsmith-icons";
+@warning-value: "\e000";
+@warning: '"fontsmith-icons"' '"\\e000"';
+@list-font-family: "fontsmith-icons";
+@list-value: "\e002";
+@list: '"fontsmith-icons"' '"\\e002"';
+@eye-font-family: "fontsmith-icons";
+@eye-value: "\e004";
+@eye: '"fontsmith-icons"' '"\\e004"';
+@bars-font-family: "fontsmith-icons";
+@bars-value: "\e005";
+@bars: '"fontsmith-icons"' '"\\e005"';
+@question-font-family: "fontsmith-icons";
+@question-value: "\e001";
+@question: '"fontsmith-icons"' '"\\e001"';
+
+.icon-font-family(@char) {
+  font-family: ~`@{char}[0]`;
+}
+
+.icon-font() {
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+
+.icon-content(@char) {
+  content: ~`@{char}[1]`;
+}
+
+.icon(@char) {
+  .icon-font-family(@char);
+  .icon-font();
+
+  &:before {
+    .icon-content(@char);
+  }
+}
+
+@font-face {
+  font-family: "fontsmith-icons";
+  src:url("/front/fonts/icons.woff") format("woff"),
+    ;
+  font-weight: normal;
+  font-style: normal;
+}

+ 38 - 0
front/src/less/index.less

@@ -0,0 +1,38 @@
+.promess {
+    padding: 0em 2em 3em;
+    font-weight: normal;
+    font-size: 1.2em;
+}
+
+.url {
+    width: 50%;
+}
+
+.launchBtn {
+    background: #e74c3c;
+    color: #fff;
+    &.disabled {
+        background: #deaca6;
+    }
+}
+
+input[type=submit], input.url {
+    padding: 0 0.5em;
+    margin: 0.5em;
+    font-size: 1.2em;
+    height: 2em;
+    border: 0 solid;
+    border-radius: 0.5em;
+    box-shadow: 0.1em 0.2em 0 0 #5e2846;
+    outline: none;
+}
+input[type=submit]:hover {
+    color: #ddd;
+}
+input[type=submit].clicked {
+    color: #ddd;
+    position: relative;
+    left: 0.1em;
+    top: 0.2em;
+    box-shadow: none;
+}

+ 66 - 0
front/src/less/main.less

@@ -0,0 +1,66 @@
+@import "icons.less";
+
+html {
+    margin: 100px 50px;
+}
+
+body {
+    margin: 0 auto;
+    max-width: 1280px;
+    background: #9c4274;
+    color: #fff;
+    font-size: 16px;
+    text-align: center;
+}
+
+body, input[type=submit], input[type=text], input[type=url], input[type=number], button {
+    font-family: 'Century Gothic', helvetica, arial, sans-serif;
+}
+
+input[type=submit] {
+    cursor: pointer;
+}
+
+h1 span {
+    display: inline-block;
+    height: 1em;
+    width: 1em;
+    color: #ffa319;
+}
+
+.footer {
+    padding: 3em;
+    color: #4e1836;
+    a {
+        color: inherit;
+    }
+    .star {
+        font-weight: bold;
+        span {
+            font-size: 1.2em;
+        }
+    }
+}
+
+/* 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-eye {
+    .icon(@eye);
+}

+ 19 - 0
front/src/less/queue.less

@@ -0,0 +1,19 @@
+.status {
+    margin-top: 2em;
+    font-size: 2.5em;
+}
+
+@-webkit-keyframes rotating {
+    from { -webkit-transform: rotate(0deg); }
+    to { -webkit-transform: rotate(360deg); }
+}
+@keyframes rotating {
+    from { transform: rotate(0deg); }
+    to { transform: rotate(360deg); }
+}
+
+.waiting .icon-lab {
+    -webkit-animation: rotating 3s linear 0s infinite;
+    -webkit-transform: translateZ(0);
+    animation: rotating 3s linear 0s infinite;
+}

+ 8 - 0
front/src/views/queue.html

@@ -0,0 +1,8 @@
+<div class="status" ng-if="status.statusCode == 'failed'">Test failed</div>
+<div class="status" ng-if="status.statusCode == 'awaiting'">
+    <ng-pluralize count="status.position" when="{
+                     'one': 'Waiting behind 1 other test',
+                     'other': 'Waiting behind {} other tests'}">
+    </ng-pluralize></div>
+<div class="status" ng-if="status.statusCode == 'running'">Test is running...</div>
+<div class="status" ng-if="status.statusCode == 'complete'">Test complete</div>

+ 672 - 0
src

@@ -0,0 +1,672 @@
+/* Timeline colors, related to Window Performances */
+.resultsMenu {
+  margin-top: 2em;
+}
+.resultsMenu .menuItem {
+  display: inline-block;
+  margin: 1em;
+  width: 8em;
+  height: 7em;
+  color: #fff;
+  border: 3px solid #fff;
+  border-radius: 0.5em;
+  cursor: pointer;
+  text-decoration: none;
+}
+.resultsMenu .back {
+  color: #5e2846;
+  border-color: #5e2846;
+}
+.resultsMenu .menuItem div {
+  padding-top: 0.5em;
+  font-size: 3em;
+}
+.resultsMenu .active,
+.resultsMenu .menuItem.active:hover {
+  color: #ffa319;
+  border-color: #ffa319;
+}
+.resultsMenu .menuItem:hover {
+  color: #ffa319;
+}
+.resultsMenu span {
+  position: relative;
+  top: 0.5em;
+}
+.testedUrl {
+  color: inherit;
+}
+h4 {
+  margin-bottom: 0.5em;
+}
+.summary,
+.metrics,
+.execution {
+  margin-top: 2em;
+  padding: 1em;
+  background: #fff;
+  color: #000;
+  border-radius: 0.5em;
+  text-align: left;
+}
+.notations {
+  display: table;
+  width: 90%;
+  margin: 0 10%;
+  border-spacing: 1em;
+}
+.notations > div {
+  display: table-row;
+}
+.notations > div > div {
+  display: table-cell;
+  height: 2.5em;
+  vertical-align: middle;
+}
+.notations .notation {
+  font-weight: bold;
+  text-align: center;
+}
+.notations .criteria {
+  font-weight: normal;
+}
+.notations .A,
+.notations .B,
+.notations .C,
+.notations .D,
+.notations .E,
+.notations .F,
+.notations .NA {
+  width: 2.5em;
+  font-size: 2em;
+  text-align: center;
+  border-radius: 0.5em;
+  font-weight: bold;
+}
+.notations .A {
+  /* green */
+  background: #00DB61;
+}
+.notations .B {
+  /* green */
+  background: #CAD63D;
+}
+.notations .C {
+  /* yellow */
+  background: #FFD119;
+}
+.notations .D {
+  /* orange */
+  background: #FFA319;
+}
+.notations .E {
+  /* red */
+  background: #FF6600;
+}
+.notations .F {
+  /* red */
+  background: #FF1919;
+}
+.notations .NA {
+  /* Non applicable */
+  background: #CCC;
+}
+.notations .icon-eye {
+  color: #9c4274;
+  cursor: pointer;
+}
+.notations .criteria .table {
+  width: 75%;
+}
+.notations .criteria .label {
+  width: 70%;
+}
+.notations .criteria .result {
+  width: 20%;
+  font-weight: bold;
+  white-space: nowrap;
+  text-align: center;
+}
+.notations .warning .label,
+.notations .warning .result {
+  color: #FF1919;
+}
+.notations .criteria .info {
+  width: 10%;
+  text-align: center;
+}
+.notations .criteria .icon-question {
+  color: #f1c40f;
+  cursor: pointer;
+}
+.timeline {
+  margin: 2em 0 5em;
+}
+.timeline .chart {
+  position: relative;
+  width: 100%;
+  border-bottom: 1px solid #000;
+}
+.timeline .startTime,
+.timeline .endTime {
+  position: absolute;
+  bottom: 0.5em;
+  font-size: 0.8em;
+}
+.timeline .startTime {
+  left: 0em;
+}
+.timeline .endTime {
+  right: 0em;
+}
+.timeline .chartPoints {
+  display: table;
+  height: 100px;
+  width: 99%;
+  margin: 0 auto;
+}
+.timeline .interval {
+  display: table-cell;
+  position: relative;
+  height: 100px;
+  width: 0.5%;
+}
+.timeline .interval .color {
+  position: absolute;
+  bottom: 0;
+  width: 100%;
+}
+.timeline div.interval:hover {
+  background: #9C4274;
+}
+.timeline .interval:hover .color {
+  background: #F04DA7;
+}
+.timeline .domComplete.interval {
+  background: #ede3ff;
+}
+.timeline .domComplete .color {
+  background: #c2a3ff;
+}
+.timeline .domContentLoadedEnd.interval {
+  background: #d8f0f0;
+}
+.timeline .domContentLoadedEnd .color {
+  background: #7ecccc;
+}
+.timeline .domContentLoaded.interval {
+  background: #e0ffd1;
+}
+.timeline .domContentLoaded .color {
+  background: #a7e846;
+}
+.timeline .domInteractive.interval {
+  background: #fffccc;
+}
+.timeline .domInteractive .color {
+  background: #ffe433;
+}
+.timeline .domCreation.interval {
+  background: #ffe0cc;
+}
+.timeline .domCreation .color {
+  background: #ff6600;
+}
+.timeline .tooltip.detailsOverlay {
+  position: absolute;
+  display: none;
+  width: auto;
+  padding: 0.5em 1em;
+  top: -1.5em;
+  right: 1em;
+}
+.timeline .interval:hover .tooltip {
+  display: block;
+}
+.timeline .legend {
+  display: table;
+  width: 100%;
+  margin-top: 1em;
+}
+.timeline .legend > div {
+  display: table-row;
+}
+.timeline .legend > div > div {
+  position: relative;
+  display: table-cell;
+  margin-top: 1em;
+}
+.timeline .titles {
+  font-weight: bold;
+}
+.timeline .titles > div {
+  padding: 0 1em 0 2em;
+}
+.timeline .tips {
+  font-size: 0.7em;
+}
+.timeline .tips > div {
+  padding: 1em 1em 0 0;
+}
+.timeline .legend .color {
+  display: block;
+  position: absolute;
+  left: 0;
+  height: 1.5em;
+  width: 1.5em;
+  border-radius: 0.2em;
+}
+.metrics h4 {
+  padding-left: 2em;
+}
+.metrics .module {
+  padding-left: 4em;
+  padding-top: 0.5em;
+}
+.metrics .legend {
+  font-style: italic;
+  color: #aaa;
+}
+.metrics .offenders {
+  padding-left: 0em;
+  font-size: 0.8em;
+}
+.metrics .offenders div {
+  cursor: pointer;
+}
+.metrics .offenders ul {
+  margin-top: 0.5em;
+}
+.filters {
+  margin: 1em 0;
+  padding: 0.5em;
+  border: 1px dotted #aaa;
+}
+.slowRequestsLimit {
+  width: 3em;
+  font-size: 1em;
+  text-align: right;
+  border: 1px solid #aaa;
+}
+input.textFilter {
+  box-shadow: none;
+  font-size: 1em;
+  padding: 0 0.2em;
+  border: 1px solid #aaa;
+  border-radius: none;
+  width: 15em;
+}
+.table {
+  display: table;
+  width: 100%;
+  border-spacing: 0.25em;
+}
+.table > div {
+  display: table-row;
+}
+.table > .headers > div {
+  font-weight: bold;
+  padding: 0.5em 1em;
+}
+.table > div > div {
+  padding: 0.1em 1em;
+  background: #f2f2f2;
+  display: table-cell;
+  text-align: left;
+}
+.table > div.jsError > .type,
+.table > div.jsError > .value {
+  color: #e74c3c;
+  font-weight: bold;
+}
+.table > div.windowPerformance > div,
+.table > div.windowPerformance > div.startTime {
+  background: #EBD8E2;
+}
+.table > div.showingDetails > div {
+  background: #f1c40f;
+}
+.table > div > .index {
+  color: #bbb;
+}
+.table > div > .type {
+  white-space: nowrap;
+}
+.table > div > .value {
+  width: 70%;
+  word-break: break-all;
+}
+.table > div > .details {
+  position: relative;
+}
+.table .details .icon-question {
+  color: #f1c40f;
+  cursor: pointer;
+}
+.table .details .icon-warning {
+  cursor: pointer;
+}
+.detailsOverlay {
+  position: absolute;
+  right: 3em;
+  top: -3em;
+  width: 45em;
+  min-height: 1em;
+  padding: 0 1em 1em;
+  background: #fff;
+  border: 2px solid #f1c40f;
+  border-radius: 0.5em;
+  z-index: 1;
+}
+@media screen and (max-width: 1024px) {
+  .detailsOverlay {
+    width: 25em;
+  }
+}
+.detailsOverlay .closeBtn {
+  position: absolute;
+  top: 0.5em;
+  right: 0.5em;
+  color: #f1c40f;
+  cursor: pointer;
+}
+.detailsOverlay .advice {
+  color: #e74c3c;
+  font-weight: bold;
+}
+.detailsOverlay .trace {
+  word-break: break-all;
+}
+.table > div > .duration,
+.table > div > .startTime {
+  text-align: center;
+  white-space: nowrap;
+}
+.table > div > .startTime.domComplete {
+  background: #ede3ff;
+}
+.table > div > .startTime.domContentLoadedEnd {
+  background: #d8f0f0;
+}
+.table > div > .startTime.domContentLoaded {
+  background: #e0ffd1;
+}
+.table > div > .startTime.domInteractive {
+  background: #fffccc;
+}
+.table > div > .startTime.domCreation {
+  background: #ffe0cc;
+}
+.table .icon-warning {
+  color: #e74c3c;
+}
+/**** NgModal popin (have a look inside bower_components) ****/
+.ng-modal {
+  position: fixed;
+  z-index: 9999;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  text-align: left;
+}
+.ng-modal-overlay {
+  position: absolute;
+  z-index: 9999;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: #000;
+  opacity: 0.5;
+}
+.ng-modal-dialog {
+  z-index: 10000;
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  width: 50%;
+  transform: translate(-50%, -50%);
+  -webkit-transform: translate(-50%, -50%);
+  background-color: #fff;
+  padding: 10px;
+  border: 3px solid #f1c40f;
+  border-radius: 0.5em;
+  color: #000;
+}
+.ng-modal-dialog-content {
+  overflow-x: hidden;
+  overflow-y: scroll;
+  word-wrap: break-word;
+  max-height: 20em;
+  font-weight: normal;
+  white-space: normal;
+}
+.ng-modal-close {
+  position: absolute;
+  top: 3px;
+  right: 5px;
+  cursor: pointer;
+  font-size: 120%;
+  padding: 5px;
+  display: inline-block;
+}
+.ng-modal-close-x {
+  font-weight: bold;
+  font-family: Arial, sans-serif;
+}
+.ng-modal-title {
+  font-weight: bold;
+  font-size: 1.5em;
+  display: block;
+  margin-bottom: 10px;
+  padding-bottom: 7px;
+  border-bottom: solid 1px #999;
+}
+
+@font-face {
+  font-family: "fontsmith-icons";
+  src: url("/public/fonts/icons.woff") format("woff");
+  font-weight: normal;
+  font-style: normal;
+}
+
+.promess {
+  padding: 0em 2em 3em;
+  font-weight: normal;
+  font-size: 1.2em;
+}
+.url {
+  width: 50%;
+}
+.launchBtn {
+  background: #e74c3c;
+  color: #fff;
+}
+input[type=submit],
+input.url {
+  padding: 0 0.5em;
+  margin: 0.5em;
+  font-size: 1.2em;
+  height: 2em;
+  border: 0 solid;
+  border-radius: 0.5em;
+  box-shadow: 0.1em 0.2em 0 0 #5e2846;
+  outline: none;
+}
+input[type=submit]:hover {
+  color: #ddd;
+}
+input[type=submit].clicked {
+  color: #ddd;
+  position: relative;
+  left: 0.1em;
+  top: 0.2em;
+  box-shadow: none;
+}
+
+#status {
+  margin-top: 2em;
+  font-size: 2.5em;
+}
+@-webkit-keyframes rotating {
+  from {
+    -webkit-transform: rotate(0deg);
+  }
+  to {
+    -webkit-transform: rotate(360deg);
+  }
+}
+@keyframes rotating {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+.waiting .icon-lab {
+  -webkit-animation: rotating 3s linear 0s infinite;
+  -webkit-transform: translateZ(0);
+  animation: rotating 3s linear 0s infinite;
+}
+
+@font-face {
+  font-family: "fontsmith-icons";
+  src: url("/public/fonts/icons.woff") format("woff");
+  font-weight: normal;
+  font-style: normal;
+}
+html {
+  margin: 100px 50px;
+}
+body {
+  margin: 0 auto;
+  max-width: 1280px;
+  background: #9c4274;
+  color: #fff;
+  font-size: 16px;
+  text-align: center;
+}
+body,
+input[type=submit],
+input[type=text],
+input[type=url],
+input[type=number],
+button {
+  font-family: 'Century Gothic', helvetica, arial, sans-serif;
+}
+input[type=submit] {
+  cursor: pointer;
+}
+h1 span {
+  display: inline-block;
+  height: 1em;
+  width: 1em;
+  color: #ffa319;
+}
+.footer {
+  padding: 3em;
+  color: #4e1836;
+}
+.footer a {
+  color: inherit;
+}
+.footer .star {
+  font-weight: bold;
+}
+.footer .star span {
+  font-size: 1.2em;
+}
+/* Icons */
+.icon-lab {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-lab:before {
+  content: "\e003";
+}
+.icon-question {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-question:before {
+  content: "\e001";
+}
+.icon-warning {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-warning:before {
+  content: "\e000";
+}
+.icon-back {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-back:before {
+  content: "\e006";
+}
+.icon-summary {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-summary:before {
+  content: "\e002";
+}
+.icon-spaghetti {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-spaghetti:before {
+  content: "\e005";
+}
+.icon-eye {
+  font-family: "fontsmith-icons";
+  speak: none;
+  font-style: normal;
+  font-weight: normal;
+  font-variant: normal;
+  text-transform: none;
+  line-height: 1;
+  -webkit-font-smoothing: antialiased;
+}
+.icon-eye:before {
+  content: "\e004";
+}

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio