瀏覽代碼

Remove JS Timeline and Scroll Bottlenecks

Gaël Métais 6 年之前
父節點
當前提交
5e82eea9f1

+ 0 - 338
front/src/css/timeline.css

@@ -1,338 +0,0 @@
-/* Timeline colors, related to Window Performances */
-.execution {
-  text-align: center;
-}
-.selectScript {
-  padding-bottom: 2em;
-  font-size: 0.9em;
-}
-.selectScript select {
-  max-width: 30em;
-}
-.selectScript.empty {
-  font-size: 0.8em;
-}
-.selectScript.empty select {
-  width: 10em;
-}
-.timeline {
-  margin: 2em 0 5em;
-}
-.timeline .chart {
-  position: relative;
-  width: 100%;
-  border-bottom: 1px solid #000;
-}
-.timeline .startTime,
-.timeline .endTime {
-  position: absolute;
-  bottom: 0.5em;
-  font-size: 0.8em;
-}
-.timeline .startTime {
-  left: 0em;
-}
-.timeline .endTime {
-  right: 0em;
-}
-.timeline .chartPoints {
-  display: table;
-  height: 100px;
-  width: 99%;
-  margin: 0 auto;
-}
-.timeline .interval {
-  display: table-cell;
-  position: relative;
-  height: 100px;
-  width: 0.5%;
-}
-.timeline .interval .color {
-  position: absolute;
-  bottom: 0;
-  width: 100%;
-}
-.timeline .interval .color.clickable {
-  cursor: pointer;
-}
-.timeline div.interval:hover {
-  background: #9C4274;
-}
-.timeline .interval:hover .color {
-  background: #F04DA7;
-}
-.timeline .domComplete.interval {
-  background: #EDE3FF;
-}
-.timeline .domComplete .color {
-  background: #C2A3FF;
-}
-.timeline .domContentLoadedEnd.interval {
-  background: #D8F0F0;
-}
-.timeline .domContentLoadedEnd .color {
-  background: #7ECCCC;
-}
-.timeline .domContentLoaded.interval {
-  background: #E0FFD1;
-}
-.timeline .domContentLoaded .color {
-  background: #A7E846;
-}
-.timeline .domInteractive.interval {
-  background: #FFFCCC;
-}
-.timeline .domInteractive .color {
-  background: #FFE433;
-}
-.timeline .domCreation.interval {
-  background: #FFE0CC;
-}
-.timeline .domCreation .color {
-  background: #FF6600;
-}
-.timeline .tooltip.detailsOverlay {
-  position: absolute;
-  display: none;
-  width: auto;
-  padding: 0.5em 1em;
-  top: -1.5em;
-  right: 1em;
-}
-.timeline .interval:hover .tooltip {
-  display: block;
-}
-.timeline .legend {
-  display: table;
-  width: 100%;
-  margin-top: 1em;
-}
-.timeline .legend > div {
-  display: table-row;
-}
-.timeline .legend > div > div {
-  position: relative;
-  display: table-cell;
-  margin-top: 1em;
-}
-.timeline .titles {
-  font-weight: bold;
-}
-.timeline .titles > div {
-  padding: 0 1em 0 2em;
-}
-.timeline .tips {
-  font-size: 0.7em;
-}
-.timeline .tips > div {
-  padding: 1em 1em 0 0;
-}
-.timeline .legend .color {
-  display: block;
-  position: absolute;
-  left: 0;
-  height: 1.5em;
-  width: 1.5em;
-  border-radius: 0.2em;
-}
-.filters {
-  margin: 1em auto;
-  padding: 0.5em;
-  min-width: 30em;
-  width: 30%;
-  border: 1px dotted #aaa;
-  text-align: left;
-}
-.subFilters {
-  margin-left: 3em;
-  font-size: 0.9em;
-}
-.table {
-  display: table;
-  width: 100%;
-  border-spacing: 0.25em;
-}
-.table > div,
-.table > a {
-  display: table-row;
-}
-.table > .headers > div {
-  font-weight: bold;
-  padding: 0.5em 1em;
-}
-.table > div > div,
-.table > a > div {
-  padding: 0.1em 1em;
-  background: #f2f2f2;
-  display: table-cell;
-  text-align: left;
-}
-.table > div.jsError > .type,
-.table > div.jsError > .value {
-  color: #e74c3c;
-  font-weight: bold;
-}
-.table > div.windowPerformance > div,
-.table > div.windowPerformance > div.startTime {
-  background: #EBD8E2;
-}
-.table > div.showingDetails > div {
-  background: #f1c40f;
-}
-.table > div.highlight > div.startTime {
-  background-color: #C0F090;
-}
-.table > div.highlight-remove {
-  transition: 3s;
-}
-.table > div.highlight-remove > div.startTime {
-  transition: background-color 3s ease-in;
-}
-.table > div > .index {
-  color: #bbb;
-  word-break: normal;
-}
-.table > div > .type {
-  white-space: nowrap;
-}
-.table .children {
-  margin-top: 0.2em;
-  font-size: 0.8em;
-  line-height: 1.6em;
-}
-.table .child {
-  margin-left: 0.5em;
-}
-.table .child > .child {
-  margin-left: 1em;
-}
-.table .child:before {
-  content: "↳";
-}
-.table .child .childArgs {
-  display: none;
-}
-.table .child span {
-  position: relative;
-}
-.table .child span:hover {
-  background: #EBD8E2;
-}
-.table .child span:hover div {
-  display: inline-block;
-}
-.table .child span:hover .childArgs {
-  display: block;
-  position: absolute;
-  padding: 0 1em 0 2em;
-  left: 100%;
-  top: 0;
-  background: #EBD8E2;
-  line-height: 1.3em;
-  height: 1.3em;
-  z-index: 2;
-}
-.table .showingDetails .child span:hover {
-  background: inherit;
-}
-.table .showingDetails .child span:hover .childArgs {
-  display: none;
-}
-.table > div > .value {
-  width: 70%;
-  word-break: break-all;
-}
-.table > div > .details {
-  position: relative;
-}
-.table .details .icon-question {
-  color: #f1c40f;
-  cursor: pointer;
-}
-.table .icon-warning {
-  display: inline-block;
-  width: 0.8em;
-}
-.detailsOverlay {
-  display: none;
-  position: absolute;
-  right: 3em;
-  top: -3em;
-  width: 45em;
-  min-height: 1em;
-  padding: 0 1em 1em;
-  background: #fff;
-  border: 2px solid #f1c40f;
-  border-radius: 0.5em;
-  z-index: 2;
-}
-@media screen and (max-width: 1024px) {
-  .detailsOverlay {
-    width: 25em;
-  }
-}
-.showDetails .detailsOverlay {
-  display: block;
-}
-.detailsOverlay .closeBtn {
-  position: absolute;
-  top: 0.5em;
-  right: 0.5em;
-  color: #f1c40f;
-  cursor: pointer;
-}
-.detailsOverlay .advice {
-  color: #e74c3c;
-  font-weight: bold;
-}
-.detailsOverlay .trace {
-  word-break: break-all;
-}
-.table > div > .duration,
-.table > div > .startTime {
-  text-align: center;
-  white-space: nowrap;
-}
-.table > div > .startTime.domComplete {
-  background: #EDE3FF;
-}
-.table > div > .startTime.domContentLoadedEnd {
-  background: #D8F0F0;
-}
-.table > div > .startTime.domContentLoaded {
-  background: #E0FFD1;
-}
-.table > div > .startTime.domInteractive {
-  background: #FFFCCC;
-}
-.table > div > .startTime.domCreation {
-  background: #FFE0CC;
-}
-.execution .icon-warning {
-  color: #e74c3c;
-  cursor: pointer;
-}
-.queryWithoutResultsFilterOn > div {
-  display: none;
-}
-.queryWithoutResultsFilterOn > div.queryWithoutResults {
-  display: table-row;
-}
-.jQueryCallOnEmptyObjectFilterOn > div {
-  display: none;
-}
-.jQueryCallOnEmptyObjectFilterOn > div.jQueryCallOnEmptyObject {
-  display: table-row;
-}
-.eventNotDelegatedFilterOn > div {
-  display: none;
-}
-.eventNotDelegatedFilterOn > div.eventNotDelegated {
-  display: table-row;
-}
-.jsErrorFilterOn > div {
-  display: none;
-}
-.jsErrorFilterOn > div.jsError {
-  display: table-row;
-}

+ 0 - 5
front/src/js/app.js

@@ -7,7 +7,6 @@ var yltApp = angular.module('YellowLabTools', [
     'queueCtrl',
     'ruleCtrl',
     'screenshotCtrl',
-    'timelineCtrl',
     'runsFactory',
     'resultsFactory',
     'apiService',
@@ -64,10 +63,6 @@ yltApp.config(['$routeProvider', '$locationProvider',
                 templateUrl: 'views/dashboard.html',
                 controller: 'DashboardCtrl'
             }).
-            when('/result/:runId/timeline', {
-                templateUrl: 'views/timeline.html',
-                controller: 'TimelineCtrl'
-            }).
             when('/result/:runId/screenshot', {
                 templateUrl: 'views/screenshot.html',
                 controller: 'ScreenshotCtrl'

+ 0 - 190
front/src/js/controllers/timelineCtrl.js

@@ -1,190 +0,0 @@
-var timelineCtrl = angular.module('timelineCtrl', []);
-
-timelineCtrl.controller('TimelineCtrl', ['$scope', '$rootScope', '$routeParams', '$location', '$timeout', 'Menu', 'Results', 'API', function($scope, $rootScope, $routeParams, $location, $timeout, Menu, Results, API) {
-    $scope.runId = $routeParams.runId;
-    $scope.Menu = Menu.setCurrentPage('timeline', $scope.runId);
-
-    function loadResults() {
-        // Load result if needed
-        if (!$rootScope.loadedResult || $rootScope.loadedResult.runId !== $routeParams.runId) {
-            Results.get({runId: $routeParams.runId, exclude: 'toolsResults'}, function(result) {
-                $rootScope.loadedResult = result;
-                $scope.result = result;
-                render();
-            });
-        } else {
-            $scope.result = $rootScope.loadedResult;
-            render();
-        }
-    }
-
-    function render() {
-        initFilters();
-        initScriptFiltering();
-        initExecutionTree();
-        initTimeline();
-        $timeout(initProfiler, 100);
-    }
-
-    function initFilters() {
-        var hash = $location.hash();
-        var filter = null;
-        
-        if (hash.indexOf('filter=') === 0) {
-            filter = hash.substr(7);
-        }
-
-        $scope.warningsFilterOn = (filter !== null);
-        $scope.warningsFilters = {
-            queryWithoutResults: (filter === null || filter === 'queryWithoutResults'),
-            jQueryCallOnEmptyObject: (filter === null || filter === 'jQueryCallOnEmptyObject'),
-            eventNotDelegated: (filter === null || filter === 'eventNotDelegated'),
-            jsError: (filter === null || filter === 'jsError')
-        };
-    }
-
-    function initScriptFiltering() {
-        var offenders = $scope.result.rules.totalRequests.offendersObj.list.byType.js;
-        $scope.scripts = [];
-
-        offenders.forEach(function(filePath) {
-            var shortPath = filePath;
-
-            if (filePath.length > 100) {
-                shortPath = filePath.substr(0, 98) + '...';
-            }
-
-            var scriptObj = {
-                fullPath: filePath,
-                shortPath: shortPath
-            };
-
-            $scope.scripts.push(scriptObj);
-        });
-    }
-
-    function initExecutionTree() {
-        var originalExecutions = $scope.result.javascriptExecutionTree.children || [];
-        
-        // Detect the last event of all (before filtering) and read time
-        var lastEvent = originalExecutions[originalExecutions.length - 1];
-        $scope.endTime =  lastEvent.data.timestamp + (lastEvent.data.time || 0);
-
-        // Filter
-        $scope.executionTree = [];
-        originalExecutions.forEach(function(node) {
-            
-            // Filter by script (if enabled)
-            if ($scope.selectedScript) {
-                if (node.data.backtrace && node.data.backtrace.indexOf($scope.selectedScript.fullPath + ':') === -1) {
-                    return;
-                }
-                if (node.data.type === "jQuery loaded" || node.data.type === "jQuery version change") {
-                    return;
-                }
-            }
-
-            $scope.executionTree.push(node);
-        });
-    }
-
-    function initTimeline() {
-
-        // Split the timeline into 200 intervals
-        var numberOfIntervals = 199;
-        $scope.timelineIntervalDuration = $scope.endTime / numberOfIntervals;
-        
-        // Pre-fill array of as many elements as there are milleseconds
-        var millisecondsArray = Array.apply(null, new Array($scope.endTime + 1)).map(Number.prototype.valueOf,0);
-        
-        // Create the milliseconds array from the execution tree
-        $scope.executionTree.forEach(function(node) {
-            if (node.data.time !== undefined) {
-
-                // Ignore artefacts (durations > 100ms)
-                var time = Math.min(node.data.time, 100) || 1;
-
-                for (var i=node.data.timestamp, max=node.data.timestamp + time ; i<max ; i++) {
-                    millisecondsArray[i] |= 1;
-                }
-            }
-        });
-
-        // Pre-fill array of 200 elements
-        $scope.timeline = Array.apply(null, new Array(numberOfIntervals + 1)).map(Number.prototype.valueOf,0);
-
-        // Create the timeline from the milliseconds array
-        millisecondsArray.forEach(function(value, timestamp) {
-            if (value === 1) {
-                $scope.timeline[Math.floor(timestamp / $scope.timelineIntervalDuration)] += 1;
-            }
-        });
-        
-        // Get the maximum value of the array (needed for display)
-        $scope.timelineMax = Math.max.apply(Math, $scope.timeline);
-    }
-
-
-    function initProfiler() {
-        $scope.profilerData = $scope.executionTree;
-    }
-
-    $scope.changeScript = function() {
-        initExecutionTree();
-        initTimeline();
-        initProfiler();
-    };
-
-    $scope.findLineIndexByTimestamp = function(timestamp) {
-        var lineIndex = 0;
-
-        for (var i = 0; i < $scope.executionTree.length; i ++) {
-            var delta = $scope.executionTree[i].data.timestamp - timestamp;
-            
-            if (delta < $scope.timelineIntervalDuration) {
-                lineIndex = i;
-            }
-
-            if (delta > 0) {
-                break;
-            }
-        }
-
-        return lineIndex;
-    };
-
-
-    $scope.backToDashboard = function() {
-        $location.path('/result/' + $scope.runId);
-    };
-
-    $scope.testAgain = function() {
-        API.relaunchTest($scope.result);
-    };
-
-    loadResults();
-
-}]);
-
-timelineCtrl.directive('scrollOnClick', ['$animate', '$timeout', function($animate, $timeout) {
-    return {
-        restrict: 'A',
-        link: function (scope, element, attributes) {            
-            // When the user clicks on the timeline, find the right profiler line and scroll to it
-            element.on('click', function() {
-                var lineIndex = scope.findLineIndexByTimestamp(attributes.scrollOnClick);
-                var lineElement = angular.element(document.getElementById('line_' + lineIndex));
-                
-                // Animate the background color to "flash" the row
-                lineElement.addClass('highlight');
-                $timeout(function() {
-                    $animate.removeClass(lineElement, 'highlight');
-                    scope.$digest();
-                }, 50);
-
-
-                window.scrollTo(0, lineElement[0].offsetTop);
-            });
-        }
-    };
-}]);

+ 0 - 644
front/src/js/directives/offendersDirectives.js

@@ -93,511 +93,6 @@
         };
     });
 
-
-    function getJQueryContextButtonHTML(context, onASingleLine) {
-        if (context.length === 0) {
-            return '<span class="offenderButton">Empty jQuery object</span>';
-        }
-
-        if (context.length === 1) {
-            return getDomElementButtonHTML(context.elements[0], onASingleLine);
-        }
-
-        var html = context.length + ' elements (' + getDomElementButtonHTML(context.elements[0], onASingleLine) + ', ' + getDomElementButtonHTML(context.elements[1], onASingleLine);
-        if (context.length === 3) {
-            html += ', ' + getDomElementButtonHTML(context.elements[0], onASingleLine);
-        } else if (context.length > 3) {
-            html += ' and ' + (context.length - 2) + ' more...';
-        }
-        return html + ')';
-    }
-
-    function isJQuery(node) {
-        return node.data.type.indexOf('jQuery ') === 0;
-    }
-
-    function getNonJQueryHTML(node, onASingleLine) {
-        var type = node.data.type;
-
-        if (node.windowPerformance) {
-            switch (type) {
-                case 'documentScroll':
-                    return '(triggering the scroll event on <b>document</b>)';
-
-                case 'windowScroll':
-                    return '(triggering the scroll event on <b>window</b>)';
-
-                case 'window.onscroll':
-                    return '(calling the <b>window.onscroll</b> function)';
-
-                default:
-                    return '';
-            }
-        }
-
-        if (!node.data.callDetails) {
-            return '';
-        }
-
-        var args = node.data.callDetails.arguments;
-        var ctxt = node.data.callDetails.context;
-
-
-        switch (type) {
-            case 'getElementById':
-            case 'createElement':
-                return '<b>' + args[0] + '</b>';
-
-            case 'getElementsByClassName':
-            case 'getElementsByTagName':
-            case 'querySelector':
-            case 'querySelectorAll':
-                return '<b>' + args[0] + '</b> on ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine);
-
-            case 'appendChild':
-                return 'append ' + getDomElementButtonHTML(args[0], onASingleLine) + ' to ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine);
-
-            case 'insertBefore':
-                return 'insert ' + getDomElementButtonHTML(args[0], onASingleLine) + ' into ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine) + ' before ' + getDomElementButtonHTML(args[1], onASingleLine);
-
-            case 'addEventListener':
-                return 'bind <b>' + args[0] + '</b> to ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine);
-
-            case 'getComputedStyle':
-                return getDomElementButtonHTML(args[0], onASingleLine) + (args[1] || '');
-
-            case 'error':
-                return args[0];
-
-            case 'jQuery - onDOMReady':
-                return '(function)';
-
-            case 'documentScroll':
-                return 'The scroll event just triggered on document';
-
-            case 'windowScroll':
-                return 'The scroll event just triggered on window';
-
-            case 'window.onscroll':
-                return 'The window.onscroll function just got called';
-
-            default:
-                return '';
-        }
-    }
-
-    function getJQueryHTML(node, onASingleLine) {
-        var type = node.data.type;
-        var unescapedArgs = node.data.callDetails.arguments;
-        var args = [];
-        var ctxt = node.data.callDetails.context;
-        
-        // escape HTML in args
-        for (var i = 0 ; i < 4 ; i ++) {
-            if (unescapedArgs[i] !== undefined) {
-                args[i] = escapeHTML(unescapedArgs[i]);
-            }
-        }
-
-        if (type === 'jQuery loaded' || type === 'jQuery version change') {
-            return args[0];
-        }
-
-        switch (type) {
-            case 'jQuery - onDOMReady':
-            case 'jQuery - windowOnLoad':
-                return '(function)';
-
-            case 'jQuery - Sizzle call':
-                return '<b>' + args[0] + '</b> on ' + getDomElementButtonHTML(ctxt.elements[0], onASingleLine);
-
-            case 'jQuery - find':
-                if (ctxt && ctxt.length === 1 && ctxt.elements[0].type !== 'document') {
-                    return '<b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                } else {
-                    return '<b>' + args[0] + '</b>';
-                }
-                break;
-
-            case 'jQuery - html':
-                if (args[0] !== undefined) {
-                    return 'set content "<b>' + args[0] + '</b>" to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                } else {
-                    return 'get content from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - append':
-                return 'append ' + joinArgs(args) + ' to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - appendTo':
-                return 'append ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to <b>' + args[0] + '</b>';
-
-            case 'jQuery - prepend':
-                return 'prepend ' + joinArgs(args) + ' to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - prependTo':
-                return 'prepend ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to <b>' + args[0] + '</b>';
-
-            case 'jQuery - before':
-                return 'insert ' + joinArgs(args) + ' before ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - insertBefore':
-                return 'insert ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' before <b>' + args[0] + '</b>';
-
-            case 'jQuery - after':
-                return 'insert ' + joinArgs(args) + ' after ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - insertAfter':
-                return 'insert ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' after <b>' + args[0] + '</b>';
-
-            case 'jQuery - remove':
-            case 'jQuery - detach':
-                if (args[0]) {
-                    return getJQueryContextButtonHTML(ctxt, onASingleLine) + ' filtered by <b>' + args[0] + '</b>';
-                } else {
-                    return getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - empty':
-            case 'jQuery - clone':
-            case 'jQuery - unwrap':
-            case 'jQuery - show':
-            case 'jQuery - hide':
-            case 'jQuery - animate':
-            case 'jQuery - fadeIn':
-            case 'jQuery - fadeOut':
-            case 'jQuery - fadeTo':
-            case 'jQuery - fadeToggle':
-            case 'jQuery - slideDown':
-            case 'jQuery - slideUp':
-            case 'jQuery - slideToggle':
-                return getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - replaceWith':
-                return 'replace ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' with <b>' + args[0] + '</b>';
-
-            case 'jQuery - replaceAll':
-                return 'replace <b>' + args[0] + '</b> with ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - text':
-                if (args[0] !== undefined) {
-                    return 'set text "<b>' + args[0] + '</b>" to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                } else {
-                    return 'get text from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - wrap':
-            case 'jQuery - wrapAll':
-                return 'wrap ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' within <b>' + args[0] + '</b>';
-
-            case 'jQuery - wrapInner':
-                return 'wrap the content of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' within <b>' + args[0] + '</b>';
-
-            case 'jQuery - css':
-            case 'jQuery - attr':
-            case 'jQuery - prop':
-                if (isStringOfObject(args[0])) {
-                    return 'set <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                } else if (args[1]) {
-                    return 'set <b>' + args[0] + '</b> : <b>' + args[1] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                } else {
-                    return 'get <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - offset':
-            case 'jQuery - height':
-            case 'jQuery - innerHeight':
-            case 'jQuery - width':
-            case 'jQuery - innerWidth':
-            case 'jQuery - scrollLeft':
-            case 'jQuery - scrollTop':
-            case 'jQuery - position':
-                if (args[0]) {
-                    return 'set <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                } else {
-                    return 'get from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - outerHeight':
-            case 'jQuery - outerWidth':
-                if (args[0] && args[0] !== 'true') {
-                    return 'set <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                } else if (args[0] === 'true') {
-                    return 'get from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' (with include margins option)';
-                } else {
-                    return 'get from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - toggle':
-                if (args[0] === 'true') {
-                    return getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to visible';
-                } else if (args[0] === 'false') {
-                    return getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to hidden';
-                } else {
-                    return getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - on':
-            case 'jQuery - one':
-                if (isStringOfObject(args[0])) {
-                    return '<b>' + args[0].replace(/&quot;\(function\)&quot;/g, '(function)') + '</b>';
-                } else if (args[1] && isPureString(args[1])) {
-                    return 'bind <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by <b>' + args[1] + '</b>';
-                } else {
-                    return 'bind <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - off':
-                if (args[0]) {
-                    if (args[1]) {
-                        return 'unbind <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by <b>' + args[1] + '</b>';
-                    } else {
-                        return 'unbind <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                    }
-                } else {
-                    return 'unbind all events';
-                }
-                break;
-
-            case 'jQuery - live':
-            case 'jQuery - bind':
-                return 'bind <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - die':
-            case 'jQuery - unbind':
-                if (args[0]) {
-                    return 'unbind <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                } else {
-                    return 'unbind all events';
-                }
-                break;
-
-            case 'jQuery - delegate':
-                return 'bind <b>' + args[1] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by <b>' + args[0] + '</b>';
-
-            case 'jQuery - undelegate':
-                if (args[0]) {
-                    if (args[1]) {
-                        return 'unbind <b>' + args[1] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + '\'s children filtered by <b>' + args[0] + '</b>';
-                    } else {
-                        return 'unbind namespace <b>' + args[0] + '</b>';
-                    }
-                } else {
-                    return 'unbind all events';
-                }
-                break;
-
-            case 'jQuery - blur':
-            case 'jQuery - change':
-            case 'jQuery - click':
-            case 'jQuery - dblclick':
-            case 'jQuery - focus':
-            case 'jQuery - keydown':
-            case 'jQuery - keypress':
-            case 'jQuery - keyup':
-            case 'jQuery - mousedown':
-            case 'jQuery - mouseenter':
-            case 'jQuery - mouseleave':
-            case 'jQuery - mousemove':
-            case 'jQuery - mouseout':
-            case 'jQuery - mouseover':
-            case 'jQuery - mouseup':
-            case 'jQuery - resize':
-            case 'jQuery - scroll':
-            case 'jQuery - select':
-            case 'jQuery - submit':
-                if (args[0]) {
-                    return 'bind on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                } else {
-                    return 'triggered on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - error':
-            case 'jQuery - focusin':
-            case 'jQuery - focusout':
-            case 'jQuery - hover':
-            case 'jQuery - load':
-            case 'jQuery - unload':
-                return 'bind on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - removeAttr':
-            case 'jQuery - removeProp':
-                return 'remove <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - val':
-                if (args[0]) {
-                    return 'set value <b>' + args[0] + '</b> to ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                } else {
-                    return 'get value from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - hasClass':
-            case 'jQuery - addClass':
-            case 'jQuery - removeClass':
-                return '<b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - toggleClass':
-                if (args[0]) {
-                    if (args[1]) {
-                        return 'toggle <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' to <b>' + args[1] + '</b>';
-                    } else {
-                        return 'toggle <b>' + args[0] + '</b> on ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                    }
-                } else {
-                    return 'magic no-argument toggleClass';
-                }
-                break;
-
-            case 'jQuery - children':
-                if (args[0]) {
-                    return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' filtered by <b>' + args[0] + '</b>';
-                } else {
-                    return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - closest':
-                if (args[1]) {
-                    return 'closest <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' in context <b>' + args[1] + '</b>';
-                } else {
-                    return 'closest <b>' + args[0] + '</b> from ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - next':
-            case 'jQuery - nextAll':
-                if (args[0]) {
-                    return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching <b>' + args[0] + '</b>';
-                } else {
-                    return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - nextUntil':
-                if (args[0]) {
-                    if (args[1]) {
-                        return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b> and matching <b>' + args[1] + '</b>';
-                    } else {
-                        return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b>';
-                    }
-                } else {
-                    return 'after ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - offsetParent':
-                return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-
-            case 'jQuery - prev':
-            case 'jQuery - prevAll':
-                if (args[0]) {
-                    return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching <b>' + args[0] + '</b>';
-                } else {
-                    return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - prevUntil':
-                if (args[0]) {
-                    if (args[1]) {
-                        return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b> and matching <b>' + args[1] + '</b>';
-                    } else {
-                        return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b>';
-                    }
-                } else {
-                    return 'before ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - parent':
-            case 'jQuery - parents':
-                if (args[0]) {
-                    return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching <b>' + args[0] + '</b>';
-                } else {
-                    return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - parentsUntil':
-                if (args[0]) {
-                    if (args[1]) {
-                        return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b> and matching <b>' + args[1] + '</b>';
-                    } else {
-                        return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' until <b>' + args[0] + '</b>';
-                    }
-                } else {
-                    return 'of ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            case 'jQuery - siblings':
-                if (args[0]) {
-                    return 'near ' + getJQueryContextButtonHTML(ctxt, onASingleLine) + ' matching <b>' + args[0] + '</b>';
-                } else {
-                    return 'near ' + getJQueryContextButtonHTML(ctxt, onASingleLine);
-                }
-                break;
-
-            default:
-                return '';
-        }
-    }
-
-    function escapeHTML(html) {
-        var entityMap = {
-            "&": "&amp;",
-            "<": "&lt;",
-            ">": "&gt;",
-            '"': '&quot;',
-            "'": '&#39;',
-            "/": '&#x2F;'
-        };
-
-        return String(html).replace(/[&<>"'\/]/g, function (s) {
-            return entityMap[s];
-        });
-    }
-
-    function joinArgs(args) {
-        var html = '<b>' + args[0] + '</b>';
-        if (args[1]) {
-            html += ', <b>' + args[1] + '</b>';
-            if (args[2]) {
-                html += ', <b>' + args[2] + '</b>';
-                if (args[3]) {
-                    html += ', and more...';
-                }
-            }
-        }
-        return html;
-    }
-
-   function isStringOfObject(str) {
-        return typeof str === 'string' && str[0] === '{' && str[str.length - 1] === '}';
-    }
-
-    function isPureString(str) {
-        return typeof str === 'string' && str[0] !== '{' && str !== '(function)' && str !== '[Object]' && str !== '[Array]' && str !== 'true' && str !== 'false' && str !== 'undefined' && str !== 'unknown';
-    }
-
-    function getTimelineParamsHTML(node, onASingleLine) {
-        if (isJQuery(node)) {
-            return getJQueryHTML(node, onASingleLine);
-        } else {
-            return getNonJQueryHTML(node, onASingleLine);
-        }
-    }
-
     function getBacktraceHTML(backtrace) {
         var html = '';
         var parsedBacktrace = parseBacktrace(backtrace);
@@ -687,145 +182,6 @@
         return out;
     }
 
-    function getTimelineDetailsHTML(node) {
-        var html = '';
-
-        if (node.data.type != 'jQuery loaded' && node.data.type != 'jQuery version change' && !node.windowPerformance) {
-            if (node.warning || node.error) {
-                html += '<div class="icon-warning"></div>';
-            } else {
-                html += '<div class="icon-question"></div>';
-            }
-
-            html += '<div class="detailsOverlay">';
-            html += '<div class="closeBtn">✖</div>';
-
-            if (node.data.callDetails.context && node.data.callDetails.context.length === 0) {
-                html += '<h4>Called on 0 jQuery element</h4><p class="advice">Useless function call, as the jQuery object is empty.</p>';
-            } else if (node.eventNotDelegated) {
-                html += '<p class="advice">This binding should use Event Delegation instead of binding each element one by one.</p>';
-            }
-
-            if (node.data.resultsNumber === 0) {
-                html += '<p class="advice">The query returned 0 results. Could it be unused or dead code?</p>';
-            } else if (node.data.resultsNumber > 0) {
-                html += '<p>The query returned ' + node.data.resultsNumber + ' ' + (node.data.resultsNumber > 1 ? 'results' : 'result') + '.</p>';
-            }
-
-            if (node.data.backtrace) {
-                html += '<h4>Backtrace</h4>';
-                html += '<div class="table">';
-                html += getBacktraceHTML(node.data.backtrace);
-                html += '</div>';
-            }
-
-            html += '</div>';
-        }
-        
-        return html;
-    }
-
-    
-    offendersDirectives.directive('profilerLine', ['$filter', function($filter) {
-        
-        var numberWithCommas = $filter('number');
-
-        function getProfilerLineHTML(index, node) {
-            return  '<div class="index">' + (index + 1) + '</div>' +
-                    '<div class="type">' + node.data.type + (node.children ? '<div class="children">' + recursiveChildrenHTML(node) + '</div>' : '') + '</div>' +
-                    '<div class="value">' + getTimelineParamsHTML(node, false) + '</div>' +
-                    '<div class="details">' + getTimelineDetailsHTML(node) + '</div>' +
-                    '<div class="startTime ' + node.data.loadingStep + '">' + numberWithCommas(node.data.timestamp, 0) + ' ms</div>';
-        }
-
-        function recursiveChildrenHTML(node) {
-            var html = '';
-            
-            if (node.children) {
-                node.children.forEach(function(child) {
-                    html += '<div class="child"><span>' + child.data.type + '<div class="childArgs">' + getTimelineParamsHTML(child, true) + '</div></span>' + recursiveChildrenHTML(child) + '</div>';
-                });
-            }
-
-            return html;
-        }
-
-        function onDetailsClick(row) {
-            // Close if it's already open
-            if (row.classList.contains('showDetails')) {
-                closeDetails(row);
-                return;
-            }
-
-            // Close any other open details overlay
-            var openOnes = document.getElementsByClassName('showDetails');
-            if (openOnes.length > 0) {
-                openOnes[0].classList.remove('showDetails');
-            }
-
-            // Make it appear
-            row.classList.add('showDetails');
-
-            // Bind the close button
-            row.querySelector('.closeBtn').addEventListener('click', function() {
-                closeDetails(row);
-            });
-        }
-
-        function closeDetails(row) {
-            row.classList.remove('showDetails');
-
-            // Unbind the close button
-            row.querySelector('.closeBtn').removeEventListener('click', closeDetails);   
-        }
-
-        return {
-            restrict: 'E',
-            scope: {
-                index: '=',
-                node: '='
-            },
-            template: '<div></div>',
-            replace: true,
-            link: function(scope, element) {
-                
-                if (scope.node.error) {
-                    element.addClass('jsError');
-                } else if (scope.node.windowPerformance) {
-                    element.addClass('windowPerformance');
-                }
-
-                element.append(getProfilerLineHTML(scope.index, scope.node));
-                element[0].id = 'line_' + scope.index;
-
-                if (scope.node.warning) {
-                    element[0].classList.add('warning');
-
-                    if (scope.node.queryWithoutResults) {
-                        element[0].classList.add('queryWithoutResults');
-                    }
-
-                    if (scope.node.jQueryCallOnEmptyObject) {
-                        element[0].classList.add('jQueryCallOnEmptyObject');
-                    }
-
-                    if (scope.node.eventNotDelegated) {
-                        element[0].classList.add('eventNotDelegated');
-                    }
-                }
-
-
-                // Bind click on the details icon
-                var detailsIcon = element[0].querySelector('.details div');
-                if (detailsIcon) {
-                    detailsIcon.addEventListener('click', function() {
-                        onDetailsClick(this.parentNode.parentNode);
-                    });
-                }
-            }
-        };
-    }]);
-
     function shortenUrl(url, maxLength) {
         if (!maxLength) {
             maxLength = 110;

+ 0 - 3
front/src/js/services/menuService.js

@@ -22,9 +22,6 @@ menuService.factory('Menu', ['$location', function($location) {
                 case 'dashboard':
                     $location.path('/result/' + currentRunId);
                     break;
-                case 'timeline':
-                    $location.path('/result/' + currentRunId + '/timeline');
-                    break;
                 default:
                     console.err('Undefined Menu.changePage() destination');
             }

+ 0 - 387
front/src/less/timeline.less

@@ -1,387 +0,0 @@
-/* Timeline colors, related to Window Performances */
-@domCreationColor: #FF6600;
-@domCreationBg: #FFE0CC;
-@domContentLoadedColor: #A7E846;
-@domContentLoadedBg: #E0FFD1;
-@domContentLoadedEndColor: #7ECCCC;
-@domContentLoadedEndBg: #D8F0F0;
-@domCompleteColor: #C2A3FF;
-@domCompleteBg: #EDE3FF;
-@domInteractiveColor: #FFE433;
-@domInteractiveBg: #FFFCCC;
-
-.execution {
-    text-align: center;
-}
-
-.selectScript {
-    padding-bottom: 2em;
-    font-size: 0.9em;
-
-    select {
-        max-width: 30em;
-    }
-
-    &.empty {
-        font-size: 0.8em;
-
-        select {
-            width: 10em;
-        }
-    }
-}
-
-.timeline {
-    margin: 2em 0 5em;
-}
-.timeline .chart {
-    position: relative;
-    width: 100%;
-    border-bottom: 1px solid #000;
-}
-.timeline .startTime, .timeline .endTime {
-    position: absolute;
-    bottom: 0.5em;
-    font-size: 0.8em;
-}
-.timeline .startTime {
-    left: 0em;
-}
-.timeline .endTime {
-    right: 0em;
-}
-.timeline .chartPoints {
-    display: table;
-    height: 100px;
-    width: 99%;
-    margin: 0 auto;
-}
-.timeline .interval {
-    display: table-cell;
-    position: relative;
-    height: 100px;
-    width: 0.5%;
-}
-.timeline .interval .color {
-    position: absolute;
-    bottom: 0;
-    width: 100%;
-
-    &.clickable {
-        cursor: pointer;
-    }
-}
-.timeline div.interval:hover {
-    background: #9C4274;
-}
-.timeline .interval:hover .color {
-    background: #F04DA7;
-}
-.timeline .domComplete.interval {
-    background: @domCompleteBg;
-}
-.timeline .domComplete .color {
-    background: @domCompleteColor;
-}
-.timeline .domContentLoadedEnd.interval {
-    background: @domContentLoadedEndBg;
-}
-.timeline .domContentLoadedEnd .color {
-    background: @domContentLoadedEndColor;
-}
-.timeline .domContentLoaded.interval {
-    background: @domContentLoadedBg;
-}
-.timeline .domContentLoaded .color {
-    background: @domContentLoadedColor;
-}
-.timeline .domInteractive.interval {
-    background: @domInteractiveBg;
-}
-.timeline .domInteractive .color {
-    background: @domInteractiveColor;
-}
-.timeline .domCreation.interval {
-    background: @domCreationBg;
-}
-.timeline .domCreation .color {
-    background: @domCreationColor;
-}
-.timeline .tooltip.detailsOverlay {
-    position: absolute;
-    display: none;
-    width: auto;
-    padding: 0.5em 1em;
-    top: -1.5em;
-    right: 1em;
-}
-.timeline .interval:hover .tooltip {
-    display: block;
-}
-
-.timeline .legend {
-    display: table;
-    width: 100%;
-    margin-top: 1em;
-}
-.timeline .legend > div {
-    display: table-row;
-}
-.timeline .legend > div > div {
-    position: relative;
-    display: table-cell;
-    margin-top: 1em;
-}
-.timeline .titles {
-    font-weight: bold;
-}
-.timeline .titles > div {
-    padding: 0 1em 0 2em;
-}
-.timeline .tips {
-    font-size: 0.7em;
-}
-.timeline .tips > div {
-    padding: 1em 1em 0 0;
-}
-.timeline .legend .color {
-    display: block;
-    position: absolute;
-    left: 0;
-    height: 1.5em;
-    width: 1.5em;
-    border-radius: 0.2em;
-}
-
-.filters {
-    margin: 1em auto;
-    padding: 0.5em;
-    min-width: 30em;
-    width: 30%;
-    border: 1px dotted #aaa;
-    text-align: left;
-}
-
-.subFilters {
-    margin-left: 3em;
-    font-size: 0.9em;
-}
-
-.table {
-    display: table;
-    width: 100%;
-    border-spacing: 0.25em;
-}
-
-.table > div,
-.table > a {
-    display: table-row;
-}
-
-.table > .headers > div {
-    font-weight: bold;
-    padding: 0.5em 1em;
-}
-
-.table > div > div,
-.table > a > div {
-    padding: 0.1em 1em;
-    background: #f2f2f2;
-    display: table-cell;
-    text-align: left;
-}
-.table > div.jsError > .type, .table > div.jsError > .value {
-    color: #e74c3c;
-    font-weight: bold;
-}
-.table > div.windowPerformance > div, .table > div.windowPerformance > div.startTime {
-    background: #EBD8E2;
-}
-.table > div.showingDetails > div {
-    background: #f1c40f;
-}
-
-.table > div.highlight {
-    > div.startTime {
-        background-color: #C0F090;
-    }
-}
-.table > div.highlight-remove {
-    transition: 3s;
-
-    > div.startTime {
-        transition: background-color 3s ease-in;
-    }
-}
-
-.table > div > .index {
-    color: #bbb;
-    word-break: normal;
-}
-
-.table > div > .type {
-    white-space:nowrap;
-}
-
-.table .children {
-    margin-top: 0.2em;
-    font-size: 0.8em;
-    line-height: 1.6em;
-}
-
-.table .child {
-    margin-left: 0.5em;
-
-    > .child {
-        margin-left: 1em;
-    }
-
-    &:before {
-        content: "↳";
-    }
-
-    .childArgs {
-        display: none;
-    }
-
-    span {
-        position: relative;
-    }
-
-    span:hover {
-        background: #EBD8E2;
-
-        div {
-            display: inline-block;
-        }
-
-        .childArgs {
-            display: block;
-            position: absolute;
-            padding: 0 1em 0 2em;
-            left: 100%;
-            top: 0;
-            background: #EBD8E2;
-            line-height: 1.3em;
-            height: 1.3em;
-            z-index: 2;
-        }
-    }
-}
-
-.table .showingDetails .child span:hover {
-    background: inherit;
-
-    .childArgs {
-        display: none;
-    }
-}
-
-.table > div > .value {
-    width: 70%;
-    word-break: break-all;
-}
-
-.table > div > .details {
-    position: relative;
-}
-.table .details .icon-question {
-    color: #f1c40f;
-    cursor: pointer;
-}
-.table .icon-warning {
-    display: inline-block;
-    width: 0.8em;
-}
-
-.detailsOverlay {
-    display: none;
-    position: absolute;
-    right: 3em;
-    top: -3em;
-    width: 45em;
-    min-height: 1em;
-    padding: 0 1em 1em;
-    background: #fff;
-    border: 2px solid #f1c40f;
-    border-radius: 0.5em;
-    z-index: 2;
-}
-@media screen and (max-width: 1024px) {
-    .detailsOverlay {
-        width: 25em;
-    }
-}
-.showDetails .detailsOverlay {
-    display: block;
-}
-.detailsOverlay .closeBtn {
-    position: absolute;
-    top: 0.5em;
-    right: 0.5em;
-    color: #f1c40f;
-    cursor: pointer;
-}
-.detailsOverlay .advice {
-    color: #e74c3c;
-    font-weight: bold;
-}
-.detailsOverlay .trace {
-    word-break: break-all;
-}
-
-.table > div > .duration, .table > div > .startTime {
-    text-align: center;
-    white-space:nowrap;
-}
-.table > div > .startTime.domComplete {
-    background: @domCompleteBg;
-}
-.table > div > .startTime.domContentLoadedEnd {
-    background: @domContentLoadedEndBg;
-}
-.table > div > .startTime.domContentLoaded {
-    background: @domContentLoadedBg;
-}
-.table > div > .startTime.domInteractive {
-    background: @domInteractiveBg;
-}
-.table > div > .startTime.domCreation {
-    background: @domCreationBg;
-}
-.execution .icon-warning {
-    color: #e74c3c;
-    cursor: pointer;
-}
-.queryWithoutResultsFilterOn {
-    > div {
-        display: none;
-        &.queryWithoutResults {
-            display: table-row;
-        }
-    }
-}
-.jQueryCallOnEmptyObjectFilterOn {
-    > div {
-        display: none;
-        &.jQueryCallOnEmptyObject {
-            display: table-row;
-        }
-    }
-}
-.eventNotDelegatedFilterOn {
-    > div {
-        display: none;
-        &.eventNotDelegated {
-            display: table-row;
-        }
-    }
-}
-.jsErrorFilterOn {
-    > div {
-        display: none;
-        &.jsError {
-            display: table-row;
-        }
-    }
-}

+ 0 - 2
front/src/main.html

@@ -14,7 +14,6 @@
     <link rel="stylesheet" type="text/css" href="css/queue.css">
     <link rel="stylesheet" type="text/css" href="css/rule.css">
     <link rel="stylesheet" type="text/css" href="css/screenshot.css">
-    <link rel="stylesheet" type="text/css" href="css/timeline.css">
     <link rel="stylesheet" type="text/css" href="css/about.css">
     <!-- endbuild -->
 
@@ -51,7 +50,6 @@
     <script src="js/controllers/queueCtrl.js"></script>
     <script src="js/controllers/ruleCtrl.js"></script>
     <script src="js/controllers/screenshotCtrl.js"></script>
-    <script src="js/controllers/timelineCtrl.js"></script>
     <script src="js/models/resultsFactory.js"></script>
     <script src="js/models/runsFactory.js"></script>
     <script src="js/services/apiService.js"></script>

+ 0 - 1
front/src/views/resultSubHeader.html

@@ -4,5 +4,4 @@
     <a class="menuItem back" href="<%= baseUrl %>"><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-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>

+ 0 - 33
front/src/views/rule.html

@@ -229,19 +229,6 @@
                 <dom-tree tree="rule.offendersObj.tree"></dom-tree>
             </div>
 
-            <div ng-if="policyName === 'DOMaccessesOnScroll' && rule.offendersObj.children.length > 0">
-                <p>The table below shows the interactions between the JavaScript and the DOM on a scroll event.</p>
-                <div class="table" ng-class="{warningsFilterOn: warningsFilterOn}">
-                    <div class="headers">
-                        <div><!-- index --></div>
-                        <div>Type</div>
-                        <div>Params</div>
-                        <div><!-- details --></div>
-                    </div>
-                    <profiler-line ng-repeat="node in rule.offendersObj.children" data-index="$index" node="node"></profiler-line>
-                </div>
-            </div>
-
             <div ng-if="policyName === 'cssColors' && rule.offendersObj.count > 0">
                 <p>This is the colors palette, sized by total occurrences:</p>
                 <div class="colorPalette checker">
@@ -378,26 +365,6 @@
         </div>
     </div>
 
-    <div ng-if="policyName === 'DOMaccesses'">
-        <h3>{{rule.value}} offenders</h3>
-        Please open the <a href="result/{{runId}}/timeline">JS timeline</a>
-    </div>
-
-    <div ng-if="policyName === 'queriesWithoutResults'">
-        <h3>{{rule.value}} offenders</h3>
-        Please open the <a href="result/{{runId}}/timeline#filter=queryWithoutResults">JS timeline, filtered by "Queries without results"</a>
-    </div>
-
-    <div ng-if="policyName === 'jQueryCallsOnEmptyObject'">
-        <h3>{{rule.value}} offenders</h3>
-        Please open the <a href="result/{{runId}}/timeline#filter=jQueryCallOnEmptyObject">JS timeline, filtered by "jQuery calls on empty object"</a>
-    </div>
-
-    <div ng-if="policyName === 'jQueryNotDelegatedEvents'">
-        <h3>{{rule.value}} offenders</h3>
-        Please open the <a href="result/{{runId}}/timeline#filter=eventNotDelegated">JS timeline, filtered by "Events not delegated"</a>
-    </div>
-
     <div ng-if="policyName === 'cssBreakpoints'">
         <div ng-if="rule.value > 0" class="cssBreakpointsGraph">
             <h3>Breakpoints distribution graph</h3>

+ 0 - 100
front/src/views/timeline.html

@@ -1,100 +0,0 @@
-<div ng-include="'views/resultSubHeader.html'"></div>
-<div class="execution board">
-    <div class="selectScript" ng-class="{empty:!selectedScript}">
-        Filter timeline and profiler by script:
-        <select ng-model="selectedScript" ng-options="script.shortPath for script in scripts" ng-change="changeScript()">
-            <option value="">All (no filter)</option>
-        </select>
-    </div>
-
-    <h2>JavaScript Timeline</h2>
-    <p>This graph gives a quick view of when the JavaScript interactions with the DOM occur during the loading of the page.</p>
-
-    <div class="timeline">
-        <div class="chart">
-            <div class="chartPoints">
-                <div ng-repeat="duration in timeline track by $index"
-                     class="interval"
-                     ng-class="{
-                        domCreation: $index * timelineIntervalDuration < result.javascriptExecutionTree.data.domInteractive,
-                        domInteractive: $index * timelineIntervalDuration >= result.javascriptExecutionTree.data.domInteractive
-                            && $index * timelineIntervalDuration < result.javascriptExecutionTree.data.domContentLoaded,
-                        domContentLoaded: $index * timelineIntervalDuration >= result.javascriptExecutionTree.data.domContentLoaded
-                            && $index * timelineIntervalDuration < result.javascriptExecutionTree.data.domContentLoadedEnd,
-                        domContentLoadedEnd: $index * timelineIntervalDuration >= result.javascriptExecutionTree.data.domContentLoadedEnd
-                            && $index * timelineIntervalDuration < result.javascriptExecutionTree.data.domComplete,
-                        domComplete: $index * timelineIntervalDuration >= result.javascriptExecutionTree.data.domComplete
-                     }">
-                    <div style="height: {{100 * duration / timelineMax | number: 0}}px" class="color" ng-class="{clickable: duration > 0}" scroll-on-click="{{$index * timelineIntervalDuration}}"></div>
-                    <div class="tooltip detailsOverlay">
-                        <div>
-                            Timestamp: {{$index * timelineIntervalDuration | number: 0}} ms
-                        </div>
-                    </div>
-                </div>
-            </div>
-            <div class="startTime">0 ms</div>
-            <div class="endTime">{{endTime | number: 0}} ms</div>
-        </div>
-        <div class="legend">
-            <div class="titles">
-                <div class="domCreation"><div class="color"></div>DOM creation</div>
-                <div class="domInteractive"><div class="color"></div>DOM interactive</div>
-                <div class="domContentLoaded"><div class="color"></div>DOM content loaded event</div>
-                <div class="domContentLoadedEnd"><div class="color"></div>Page completion</div>
-                <div class="domComplete"><div class="color"></div>Page is complete</div>
-            </div>
-            <div class="tips">
-                <div>Executing JavaScript and DOM queries here is a <b>bad practice</b> and slows down the DOM construction.</div>
-                <div>Some frameworks do things here, but it's not reliable and should be avoided.</div>
-                <div>Also known as "document ready". This is where you should execute <b>top-priority</b> scripts, like binding action buttons or launch a video player.</div>
-                <div>Here you can execute <b>mid-priority</b> tasks. Loading a script with createElement('script') is one way to do so.</div>
-                <div>The page is considered loaded, it's time for low <b>priority things</b> : trackers, social plugins, easter egg...</div>
-            </div>
-        </div>
-    </div>
-
-    <h2>JavaScript Profiler</h2>
-    <p>
-        The table below shows the interactions between the JavaScript and the DOM. It is useful to understand what happens while the page loads.
-    </p>
-    <div class="filters">
-        <div>
-            <input type="checkbox" name="warningsFilter" ng-model="warningsFilterOn" id="warningsFilterOn" />
-            <label for="warningsFilterOn">Filter on warnings and errors</label>
-            <div class="subFilters" ng-if="warningsFilterOn">
-                <div>
-                    <input type="checkbox" name="filters" ng-model="warningsFilters.queryWithoutResults" id="queryWithoutResultsFilterOn"/>
-                    <label for="queryWithoutResultsFilterOn">Queries without results</label>
-                </div>
-                <div>
-                    <input type="checkbox" name="filters" ng-model="warningsFilters.jQueryCallOnEmptyObject" id="jQueryCallOnEmptyObjectFilterOn" />
-                    <label for="jQueryCallOnEmptyObjectFilterOn">jQuery calls on empty object</label>
-                </div>
-                <div>
-                    <input type="checkbox" name="filters" ng-model="warningsFilters.eventNotDelegated" id="eventNotDelegatedFilterOn" />
-                    <label for="eventNotDelegatedFilterOn">Events not delegated</label>
-                </div>
-                <div>
-                    <input type="checkbox" name="filters" ng-model="warningsFilters.jsError" id="jsErrorFilterOn" />
-                    <label for="jsErrorFilterOn">Errors</label>
-                </div>
-            </div>
-        </div>
-        {{queryWithoutResultsFilterOn}}
-    </div>
-    <div class="table" ng-class="{queryWithoutResultsFilterOn: warningsFilterOn && warningsFilters.queryWithoutResults, jQueryCallOnEmptyObjectFilterOn: warningsFilterOn && warningsFilters.jQueryCallOnEmptyObject, eventNotDelegatedFilterOn: warningsFilterOn && warningsFilters.eventNotDelegated, jsErrorFilterOn: warningsFilterOn && warningsFilters.jsError}">
-        <div class="headers">
-            <div><!-- index --></div>
-            <div>Type</div>
-            <div>Params</div>
-            <div><!-- details --></div>
-            <div>Timestamp</div>
-        </div>
-
-        <profiler-line ng-repeat="node in profilerData" data-index="$index" node="node"></profiler-line>
-
-    </div>
-
-    <div class="backToDashboard"><a href="#" ng-click="backToDashboard()">Back to dashboard</a></div>
-</div>

+ 0 - 48
lib/metadata/policies.js

@@ -66,24 +66,6 @@ var policies = {
             };
         }
     },
-    "DOMaccesses": {
-        "tool": "jsExecutionTransformer",
-        "label": "DOM access",
-        "message": "<p>This metric counts the number of calls to DOM related functions (both native DOM functions and jQuery functions) on page load.</p><p>The more your JavaScript code accesses the DOM, the slower the page will load.</p><p>Try, as much as possible, to have an HTML page fully generated by the server instead of making changes with JS.</p><p>Try to reduce the number of queries by refactoring your JavaScript code.</p><p>Binding too many events also has a cost. Try to use <a href=\"https://learn.jquery.com/events/event-delegation/\" target=\"_blank\">event delegation</a> as much as possible.</p>",
-        "isOkThreshold": 200,
-        "isBadThreshold": 2000,
-        "isAbnormalThreshold": 4000,
-        "hasOffenders": false
-    },
-    "queriesWithoutResults": {
-        "tool": "jsExecutionTransformer",
-        "label": "Queries without result",
-        "message": "<p>Number of queries that return no result. Both native and jQuery DOM requests are counted.</p><p>It suggests the query is not used on the page, probably because it is some dead code.</p><p>Or maybe the code is trying to find an HTML block that is not always here. Look at the JS Timeline to see if the scripts correctly figures out the HTML block is not here and immediatly stops interacting further with the DOM.</p>",
-        "isOkThreshold": 0,
-        "isBadThreshold": 150,
-        "isAbnormalThreshold": 250,
-        "hasOffenders": false
-    },
     "DOMqueriesAvoidable": {
         "tool": "phantomas",
         "label": "Duplicated DOM queries",
@@ -147,18 +129,6 @@ var policies = {
             };
         }
     },
-    "DOMaccessesOnScroll": {
-        "tool": "jsExecutionTransformer",
-        "label": "DOM access on scroll",
-        "message": "<p>This rule counts the number of DOM-accessing functions calls on a scroll event, such as queries, readings, writings, bindings and jQuery functions.</p><p>Two scroll events are triggered quickly, one after the other, and only the second one is analyzed so throttled functions are ignored.</p><p>One of the main reasons of a poor scrolling experience is when too much JS is executed on each scroll event. Note that some devices such as smartphones and MacBooks send more scroll events than others.</p><p>Reduce the number of DOM accesses inside scroll listeners. Put DOM queries outside them when possible. Use <a href=\"http://blogorama.nerdworks.in/javascriptfunctionthrottlingan/\" target=\"_blank\">throttling or debouncing</a>.</p>",
-        "isOkThreshold": 1,
-        "isBadThreshold": 20,
-        "isAbnormalThreshold": 35,
-        "hasOffenders": true,
-        "offendersTransformFn": function(offenders) {
-            return offenders;
-        }
-    },
     "jsErrors": {
         "tool": "phantomas",
         "label": "JavaScript errors",
@@ -348,24 +318,6 @@ var policies = {
         "isAbnormalThreshold": 2,
         "hasOffenders": true
     },
-    "jQueryCallsOnEmptyObject": {
-        "tool": "jsExecutionTransformer",
-        "label": "Calls on empty objects",
-        "message": "<p>This metric counts the number of jQuery functions called on an empty jQuery object. The call was useless.</p><p>This can be helpful to detect dead or unused code.</p>",
-        "isOkThreshold": 1,
-        "isBadThreshold": 100,
-        "isAbnormalThreshold": 180,
-        "hasOffenders": false
-    },
-    "jQueryNotDelegatedEvents": {
-        "tool": "jsExecutionTransformer",
-        "label": "Events not delegated",
-        "message": "<p>This is the number of events that are bound with the .bind() or the .on() function without using <a href=\"https://learn.jquery.com/events/event-delegation/\" target=\"_blank\">event delegation</a>.</p><p>This means jQuery binds each element contained in the object one by one. This is bad for performance.</p>",
-        "isOkThreshold": 1,
-        "isBadThreshold": 100,
-        "isAbnormalThreshold": 180,
-        "hasOffenders": false
-    },
     "cssParsingErrors": {
         "tool": "phantomas",
         "label": "CSS syntax error",

+ 0 - 17
lib/metadata/scoreProfileGeneric.json

@@ -32,21 +32,6 @@
                 "DOMidDuplicated": 1
             }
         },
-        "domManipulations": {
-            "label": "DOM manipulations",
-            "policies": {
-                "DOMaccesses": 3,
-                "queriesWithoutResults": 1,
-                "DOMqueriesAvoidable": 1
-            }
-        },
-        "scroll": {
-            "label": "Scroll bottlenecks",
-            "policies": {
-                "eventsScrollBound": 1,
-                "DOMaccessesOnScroll": 4
-            }
-        },
         "badJavascript": {
             "label": "Bad JavaScript",
             "policies": {
@@ -116,8 +101,6 @@
         "pageWeight": 3,
         "requests": 3,
         "domComplexity": 2,
-        "domManipulations": 2,
-        "scroll": 1,
         "badJavascript": 2,
         "jQuery": 1,
         "cssSyntaxError": 1,

+ 0 - 4
lib/runner.js

@@ -2,7 +2,6 @@ var Q                       = require('q');
 var debug                   = require('debug')('ylt:runner');
 
 var phantomasWrapper        = require('./tools/phantomas/phantomasWrapper');
-var jsExecutionTransformer  = require('./tools/jsExecutionTransformer');
 var colorDiff               = require('./tools/colorDiff');
 var mediaQueriesChecker     = require('./tools/mediaQueriesChecker');
 var isHttp2                 = require('./tools/isHttp2');
@@ -28,9 +27,6 @@ var Runner = function(params) {
     .then(function(phantomasResults) {
         data.toolsResults.phantomas = phantomasResults;
 
-        // Treat the JS Execution Tree from offenders
-        data = jsExecutionTransformer.transform(data);
-
         // Compare colors
         data = colorDiff.compareAllColors(data);
 

+ 1 - 1
lib/server/controllers/frontController.js

@@ -10,7 +10,7 @@ var FrontController = function(app) {
     var assetsPath = (app.get('env') === 'development') ? '../../../front/src' : '../../../front/build';
 
     // Routes templating    
-    var routes = ['/', '/about', '/result/:runId', '/result/:runId/timeline', '/result/:runId/screenshot', '/result/:runId/rule/:policy', '/queue/:runId'];
+    var routes = ['/', '/about', '/result/:runId', '/result/:runId/screenshot', '/result/:runId/rule/:policy', '/queue/:runId'];
 
     routes.forEach(function(route) {
         app.get(route, function(req, res) {

+ 0 - 210
lib/tools/jsExecutionTransformer.js

@@ -1,210 +0,0 @@
-var debug = require('debug')('ylt:jsExecutionTransformer');
-
-var offendersHelpers    = require('../offendersHelpers');
-var Collection          = require('./phantomas/custom_modules/util/collection');
-
-var jsExecutionTransformer = function() {
-
-    this.transform = function(data) {
-        var javascriptExecutionTree = {};
-        var jQueryFunctionsCollection = new Collection();
-        
-        var metrics = {
-            domInteractive: 0,
-            domContentLoaded: 0,
-            domContentLoadedEnd: 0,
-            domComplete: 0,
-
-            DOMaccesses: 0,
-            DOMaccessesOnScroll: 0,
-            queriesWithoutResults: 0
-        };
-
-        var offenders = {};
-
-        var hasjQuery = (data.toolsResults.phantomas.metrics.jQueryVersionsLoaded > 0);
-        if (hasjQuery) {
-            metrics.jQueryCalls = 0;
-            metrics.jQueryCallsOnEmptyObject = 0;
-            metrics.jQueryNotDelegatedEvents = 0;
-        }
-
-        try {
-
-            debug('Starting JS execution transformation');
-            javascriptExecutionTree = JSON.parse(data.toolsResults.phantomas.offenders.javascriptExecutionTree[0]);
-        
-            if (javascriptExecutionTree.children) {
-                javascriptExecutionTree.children.forEach(function(node, index) {
-                    
-                    var contextLength = (node.data.callDetails && node.data.callDetails.context) ? node.data.callDetails.context.length : null;
-
-                    if (isABindWithoutEventDelegation(node, contextLength)) {
-                        metrics.jQueryNotDelegatedEvents += contextLength;
-                        node.warning = true;
-                        node.eventNotDelegated = true;
-                    }
-
-                    if (node.data.resultsNumber === 0) {
-                        metrics.queriesWithoutResults ++;
-                        node.queryWithoutResults = true;
-                        node.warning = true;
-                    }
-
-                    if (contextLength === 0) {
-                        metrics.jQueryCallsOnEmptyObject ++;
-                        node.jQueryCallOnEmptyObject = true;
-                        node.warning = true;
-                    }
-
-                    if (node.data.type.indexOf('jQuery - ') === 0) {
-                        metrics.jQueryCalls ++;
-                        jQueryFunctionsCollection.push(node.data.type);
-                    }
-
-                    // Mark errors with an error flag
-                    if (node.data.type === 'error' || node.data.type === 'jQuery version change') {
-                        node.error = true;
-                    }
-
-                    // Mark a performance flag
-                    if (['domInteractive', 'domContentLoaded', 'domContentLoadedEnd', 'domComplete'].indexOf(node.data.type) >= 0) {
-                        node.windowPerformance = true;
-
-                        // Adjust the navigation timings (cause their not very well synchronised)
-                        switch(node.data.type) {
-                            case 'domInteractive':
-                                javascriptExecutionTree.data.domInteractive = node.data.timestamp;
-                                break;
-                            case 'domContentLoaded':
-                                javascriptExecutionTree.data.domContentLoaded = node.data.timestamp;
-                                break;
-                            case 'domContentLoadedEnd':
-                                javascriptExecutionTree.data.domContentLoadedEnd = node.data.timestamp;
-                                break;
-                            case 'domComplete':
-                                javascriptExecutionTree.data.domComplete = node.data.timestamp;
-                                break;
-                        }
-                    }
-                    // Fix rare bug when domComplete was never triggered
-                    if (index === javascriptExecutionTree.children.length - 1 && !javascriptExecutionTree.data.domComplete) {
-                        javascriptExecutionTree.data.domComplete = node.data.timestamp + 1000;
-                    }
-
-                    // Transform domPaths into objects
-                    changeListOfDomPaths(node);
-
-                    // Count the number of DOM accesses, by counting the tree leafs
-                    metrics.DOMaccesses += countTreeLeafs(node);
-                });
-
-            }
-            debug('JS execution transformation complete');
-
-
-            if (data.toolsResults.phantomas.offenders.scrollExecutionTree) {
-                debug('Starting scroll execution transformation');
-                offenders.DOMaccessesOnScroll = JSON.parse(data.toolsResults.phantomas.offenders.scrollExecutionTree[0]);
-                if (offenders.DOMaccessesOnScroll.children) {
-                    offenders.DOMaccessesOnScroll.children.forEach(function(node) {
-                        
-                        // Mark a event flag
-                        if (['documentScroll', 'windowScroll', 'window.onscroll'].indexOf(node.data.type) >= 0) {
-                            node.windowPerformance = true;
-                        }
-
-                        // Transform domPaths into objects
-                        changeListOfDomPaths(node);
-                        
-                        // Count the number of DOM accesses, by counting the tree leafs
-                        metrics.DOMaccessesOnScroll += countTreeLeafs(node);
-                    });
-                }
-                debug('Scroll execution transformation complete');
-            } else {
-                debug('Could not parse scrollExecutionTree');
-            }
-
-        } catch(err) {
-            throw err;
-        }
-
-        data.javascriptExecutionTree = javascriptExecutionTree;
-        
-        data.toolsResults.jsExecutionTransformer = {
-            metrics: metrics,
-            offenders: offenders
-        };
-
-        return data;
-    };
-
-    function treeRecursiveParser(node, fn) {
-        if (node.children) {
-            node.children.forEach(function(child) {
-                treeRecursiveParser(child, fn);
-            });
-        }
-        fn(node);
-    }
-
-    function changeListOfDomPaths(rootNode) {
-        treeRecursiveParser(rootNode, function(node) {
-            
-            if (node.data.callDetails && node.data.callDetails.context && node.data.callDetails.context.length > 0) {
-                node.data.callDetails.context.elements = node.data.callDetails.context.elements.map(offendersHelpers.domPathToDomElementObj, offendersHelpers);
-            }
-
-            if (node.data.type === 'appendChild' || node.data.type === 'insertBefore' || node.data.type === 'getComputedStyle') {
-                node.data.callDetails.arguments[0] = offendersHelpers.domPathToDomElementObj(node.data.callDetails.arguments[0]);
-            }
-
-            if (node.data.type === 'insertBefore') {
-                node.data.callDetails.arguments[1] = offendersHelpers.domPathToDomElementObj(node.data.callDetails.arguments[1]);
-            }
-        });
-    }
-
-    // Returns the number of leafs (nodes without children)
-    function countTreeLeafs(rootNode) {
-        var count = 0;
-
-        treeRecursiveParser(rootNode, function(node) {
-            if (!node.children &&
-                !node.error &&
-                !node.windowPerformance &&
-                node.data.type !== 'jQuery loaded') {
-                count ++;
-            }
-        });
-
-        return count;
-    }
-
-    function isPureString(str) {
-        return typeof str === 'string' && str[0] !== '{' && str !== '(function)' && str !== '[Object]' && str !== '[Array]' && str !== 'true' && str !== 'false' && str !== 'undefined' && str !== 'unknown' && str !== 'null';
-    }
-
-    function isABindWithoutEventDelegation(node, contextLength) {
-        // Count only on larger bindings
-        if (contextLength <= 3) {
-            return false;
-        }
-
-        if (node.data.type === 'jQuery - on' && node.data.callDetails.arguments[1] && !isPureString(node.data.callDetails.arguments[1])) {
-            return true;
-        }
-
-        if (node.data.type.indexOf('jQuery - ') === 0 && node.children && node.children.length === 1) {
-            var child = node.children[0];
-            if (child.data.type === 'jQuery - on' && child.data.callDetails.arguments[1] && !isPureString(child.data.callDetails.arguments[1])) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-};
-
-module.exports = new jsExecutionTransformer();

+ 0 - 387
lib/tools/phantomas/custom_modules/modules/domQYLT/domQYLT.js

@@ -1,387 +0,0 @@
-/**
- * Analyzes DOM queries done via native DOM methods
- */
-/* global Element: true, Document: true, Node: true, window: true */
-
-exports.version = '0.10.a';
-
-exports.module = function(phantomas) {
-    'use strict';
-
-    phantomas.setMetric('DOMqueries'); // @desc number of all DOM queries @offenders
-    phantomas.setMetric('DOMqueriesWithoutResults'); // @desc number of DOM queries that retutned nothing @offenders
-    phantomas.setMetric('DOMqueriesById'); // @desc number of document.getElementById calls
-    phantomas.setMetric('DOMqueriesByClassName'); // @desc number of document.getElementsByClassName calls
-    phantomas.setMetric('DOMqueriesByTagName'); // @desc number of document.getElementsByTagName calls
-    phantomas.setMetric('DOMqueriesByQuerySelectorAll'); // @desc number of document.querySelector(All) calls
-    phantomas.setMetric('DOMinserts'); // @desc number of DOM nodes inserts
-    phantomas.setMetric('DOMqueriesDuplicated'); // @desc number of DOM queries called more than once
-    phantomas.setMetric('DOMqueriesAvoidable'); // @desc number of repeated uses of a duplicated query
-
-    // fake native DOM functions
-    phantomas.on('init', function() {
-        phantomas.evaluate(function() {
-            (function(phantomas) {
-                function querySpy(type, query, fnName, context, hasNoResults) {
-                    phantomas.emit('domQuery', type, query, fnName, context, hasNoResults); // @desc DOM query has been made
-                }
-
-                phantomas.spy(Document.prototype, 'getElementById', function(id) {
-                    phantomas.incrMetric('DOMqueriesById');
-                    phantomas.addOffender('DOMqueriesById', '#%s (in %s)', id, '#document');
-
-                    phantomas.enterContext({
-                        type: 'getElementById',
-                        callDetails: {
-                            arguments: ['#' + id]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-
-                }, function(result, args) {
-                    var id = args ? '#' + args[0] : undefined;
-
-                    querySpy('id', id, 'getElementById', '#document', (result === null));
-
-                    var moreData = {
-                        resultsNumber : (result === null) ? 0 : 1
-                    };
-                    phantomas.leaveContext(moreData);
-                });
-
-                // selectors by class name
-                function selectorClassNameSpyBefore(className) {
-                    /*jshint validthis: true */
-
-                    var context = phantomas.getDOMPath(this);
-
-                    phantomas.incrMetric('DOMqueriesByClassName');
-                    phantomas.addOffender('DOMqueriesByClassName', '.%s (in %s)', className, context);
-
-                    phantomas.enterContext({
-                        type: 'getElementsByClassName',
-                        callDetails: {
-                            context: {
-                                length: 1,
-                                elements: [context]
-                            },
-                            arguments: ['.' + className]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-                }
-
-                function selectorClassNameAfter(result, args) {
-                    /*jshint validthis: true */
-
-                    var className = args ? '.' + args[0] : undefined;
-                    var context = phantomas.getDOMPath(this);
-
-                    querySpy('class', className, 'getElementsByClassName', context, (result.length === 0));
-                    
-                    var moreData = {
-                        resultsNumber : (result && result.length > 0) ? result.length : 0
-                    };
-                    phantomas.leaveContext(moreData);
-                }
-
-                phantomas.spy(Document.prototype, 'getElementsByClassName', selectorClassNameSpyBefore, selectorClassNameAfter);
-                phantomas.spy(Element.prototype, 'getElementsByClassName', selectorClassNameSpyBefore, selectorClassNameAfter);
-
-                // selectors by tag name
-                function selectorTagNameSpyBefore(tagName) {
-                    /*jshint validthis: true */
-
-                    var context = phantomas.getDOMPath(this);
-
-                    phantomas.incrMetric('DOMqueriesByTagName');
-                    phantomas.addOffender('DOMqueriesByTagName', '%s (in %s)', tagName, context);
-
-                    phantomas.enterContext({
-                        type: 'getElementsByTagName',
-                        callDetails: {
-                            context: {
-                                length: 1,
-                                elements: [context]
-                            },
-                            arguments: [tagName]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-                }
-
-                function selectorTagNameSpyAfter(result, args) {
-                    /*jshint validthis: true */
-                    
-                    var tagName = args ? args[0].toLowerCase() : undefined;
-                    var context = phantomas.getDOMPath(this);
-
-                    querySpy('tag name', tagName, 'getElementsByTagName', context, (result.length === 0));
-                    
-                    var moreData = {
-                        resultsNumber : (result && result.length > 0) ? result.length : 0
-                    };
-                    phantomas.leaveContext(moreData);
-                }
-
-                phantomas.spy(Document.prototype, 'getElementsByTagName', selectorTagNameSpyBefore, selectorTagNameSpyAfter);
-                phantomas.spy(Element.prototype, 'getElementsByTagName', selectorTagNameSpyBefore, selectorTagNameSpyAfter);
-
-
-                // selector queries
-                function selectorQuerySpy(selector, context) {
-                    phantomas.incrMetric('DOMqueriesByQuerySelectorAll');
-                    phantomas.addOffender('DOMqueriesByQuerySelectorAll', '%s (in %s)', selector, context);
-                }
-
-                function selectorQuerySpyBefore(selector) {
-                    /*jshint validthis: true */
-
-                    var context = phantomas.getDOMPath(this);
-                    selectorQuerySpy(selector, context);
-
-                    phantomas.enterContext({
-                        type: 'querySelector',
-                        callDetails: {
-                            context: {
-                                length: 1,
-                                elements: [context]
-                            },
-                            arguments: [selector]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-                }
-
-                function selectorQuerySpyAfter(result, args) {
-                    /*jshint validthis: true */
-
-                    var selector = args ? args[0] : undefined;
-                    var context = phantomas.getDOMPath(this);
-
-                    querySpy('selector', selector, 'querySelectorAll', context, (!result || result.length === 0));
-                    
-                    var moreData = {
-                        resultsNumber : result ? 1 : 0
-                    };
-                    phantomas.leaveContext(moreData);
-                }
-
-                function selectorAllQuerySpyBefore(selector) {
-                    /*jshint validthis: true */
-
-                    var context = phantomas.getDOMPath(this);
-                    selectorQuerySpy(selector, context);
-
-                    phantomas.enterContext({
-                        type: 'querySelectorAll',
-                        callDetails: {
-                            context: {
-                                length: 1,
-                                elements: [context]
-                            },
-                            arguments: [selector]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-                }
-
-                function selectorAllQuerySpryAfter(result, args) {
-                    /*jshint validthis: true */
-
-                    var selector = args ? args[0] : undefined;
-                    var context = phantomas.getDOMPath(this);
-
-                    querySpy('selector', selector, 'querySelectorAll', context, (!result || result.length === 0));
-
-                    var moreData = {
-                        resultsNumber : (result && result.length > 0) ? result.length : 0
-                    };
-                    phantomas.leaveContext(moreData);
-                }
-
-                phantomas.spy(Document.prototype, 'querySelector', selectorQuerySpyBefore, selectorQuerySpyAfter);
-                phantomas.spy(Document.prototype, 'querySelectorAll', selectorAllQuerySpyBefore, selectorAllQuerySpryAfter);
-                phantomas.spy(Element.prototype, 'querySelector', selectorQuerySpyBefore, selectorQuerySpyAfter);
-                phantomas.spy(Element.prototype, 'querySelectorAll', selectorAllQuerySpyBefore, selectorAllQuerySpryAfter);
-
-
-                // count DOM inserts
-                function appendChild(child, element, context, appended) {
-                    /*jshint validthis: true */
-
-                    // ignore appending to the node that's not yet added to DOM tree
-                    if (!element.parentNode) {
-                        return;
-                    }
-
-                    // don't count elements added to fragments as a DOM inserts (issue #350)
-                    // DocumentFragment > div[0]
-                    if (context.indexOf('DocumentFragment') === 0) {
-                        return;
-                    }
-
-                    phantomas.incrMetric('DOMinserts');
-                    phantomas.addOffender('DOMinserts', '"%s" appended to "%s"', appended, context);
-
-                    //phantomas.log('DOM insert: node "%s" appended to "%s"', appended, context);
-                }
-
-                function appendChildSpyBefore(child) {
-                    /*jshint validthis: true */
-
-                    var context = phantomas.getDOMPath(this);
-                    var appended = phantomas.getDOMPath(child);
-                    appendChild(child, this, context, appended);
-
-                    phantomas.enterContext({
-                        type: 'appendChild',
-                        callDetails: {
-                            context: {
-                                length: 1,
-                                elements: [context]
-                            },
-                            arguments: [appended]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-                }
-
-                function insertBeforeSpyBefore(child, refElement) {
-                    /*jshint validthis: true */
-                    
-                    var context = phantomas.getDOMPath(this);
-                    var appended = phantomas.getDOMPath(child);
-                    var referent = phantomas.getDOMPath(refElement);
-                    appendChild(child, this, context, appended);
-
-                    phantomas.enterContext({
-                        type: 'insertBefore',
-                        callDetails: {
-                            context: {
-                                length: 1,
-                                elements: [context]
-                            },
-                            arguments: [
-                                appended,
-                                referent
-                            ]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-                }
-
-                phantomas.spy(Node.prototype, 'appendChild', appendChildSpyBefore, function(result) {
-                    phantomas.leaveContext();
-                });
-                phantomas.spy(Node.prototype, 'insertBefore', insertBeforeSpyBefore, function(result) {
-                    phantomas.leaveContext();
-                });
-
-
-                phantomas.spy(Document.prototype, 'createElement', function(tagName) {
-                    
-                    phantomas.enterContext({
-                        type: 'createElement',
-                        callDetails: {
-                            arguments: [tagName]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-
-                }, function(result, args) {
-                    phantomas.leaveContext();
-                });
-
-
-                phantomas.spy(Document.prototype, 'createTextNode', function(text) {
-                    
-                    phantomas.enterContext({
-                        type: 'createTextNode',
-                        callDetails: {
-                            arguments: [text]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-
-                }, function(result, args) {
-                    phantomas.leaveContext();
-                });
-
-
-                phantomas.spy(Document.prototype, 'createDocumentFragment', function() {
-                    
-                    phantomas.enterContext({
-                        type: 'createDocumentFragment',
-                        callDetails: {
-                            arguments: []
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-
-                }, function(result, args) {
-                    phantomas.leaveContext();
-                });
-
-
-                phantomas.spy(window, 'getComputedStyle', function(element, pseudoElement) {
-                    var target = phantomas.getDOMPath(element);
-                    
-                    phantomas.enterContext({
-                        type: 'getComputedStyle',
-                        callDetails: {
-                            arguments: [target, pseudoElement]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-
-                }, function(result, args) {
-                    phantomas.leaveContext();
-                });
-
-            })(window.__phantomas);
-        });
-    });
-
-    // report DOM queries that return no results (issue #420)
-    phantomas.on('domQuery', function(type, query, fnName, context, hasNoResults) {
-        // ignore DOM queries within DOM fragments (used internally by jQuery)
-        if (context.indexOf('body') !== 0 && context.indexOf('#document') !== 0) {
-            return;
-        }
-
-        if (hasNoResults === true) {
-            phantomas.incrMetric('DOMqueriesWithoutResults');
-            phantomas.addOffender('DOMqueriesWithoutResults', '%s (in %s) using %s', query, context, fnName);
-        }
-    });
-
-    // count DOM queries by either ID, tag name, class name and selector query
-    // @see https://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-document-doctype
-    var Collection = require('../../util/collection'),
-        DOMqueries = new Collection();
-
-    phantomas.on('domQuery', function(type, query, fnName, context) {
-        phantomas.log('DOM query: by %s - "%s" (using %s) in %s', type, query, fnName, context);
-        phantomas.incrMetric('DOMqueries');
-
-        if (context && (
-                context.indexOf('html') === 0 ||
-                context.indexOf('body') === 0 ||
-                context.indexOf('head') === 0 ||
-                context.indexOf('#document') === 0
-            )) {
-            DOMqueries.push(type + ' "' + query + '" with ' + fnName + ' (in context ' + context + ')');
-        }
-    });
-
-    phantomas.on('report', function() {
-        DOMqueries.sort().forEach(function(query, cnt) {
-            if (cnt > 1) {
-                phantomas.incrMetric('DOMqueriesDuplicated');
-                phantomas.incrMetric('DOMqueriesAvoidable', cnt - 1);
-                phantomas.addOffender('DOMqueriesDuplicated', '%s: %d queries', query, cnt);
-            }
-        });
-    });
-};

+ 0 - 381
lib/tools/phantomas/custom_modules/modules/jQYLT/jQYLT.js

@@ -1,381 +0,0 @@
-/**
- * Analyzes jQuery activity
- *
- * @see http://code.jquery.com/jquery-1.10.2.js
- * @see http://code.jquery.com/jquery-2.0.3.js
- */
-/* global document: true, window: true */
-/* jshint -W030 */
-
-exports.version = '1.0.a';
-
-exports.module = function(phantomas) {
-    'use strict';
-
-    phantomas.setMetric('jQueryVersion', ''); // @desc version of jQuery framework (if loaded) [string]
-    phantomas.setMetric('jQueryVersionsLoaded'); // @desc number of loaded jQuery "instances" (even in the same version)
-    phantomas.setMetric('jQueryOnDOMReadyFunctions'); // @desc number of functions bound to onDOMReady event
-    phantomas.setMetric('jQueryWindowOnLoadFunctions'); // @desc number of functions bound to windowOnLoad event
-    phantomas.setMetric('jQuerySizzleCalls'); // @desc number of calls to Sizzle (including those that will be resolved using querySelectorAll)
-    phantomas.setMetric('jQueryEventTriggers'); // @desc number of jQuery event triggers
-
-    var jQueryFunctions = [
-        // DOM manipulations
-        'html',
-        'append',
-        'appendTo',
-        'prepend',
-        'prependTo',
-        'before',
-        'insertBefore',
-        'after',
-        'insertAfter',
-        'remove',
-        'detach',
-        'empty',
-        'clone',
-        'replaceWith',
-        'replaceAll',
-        'text',
-        'wrap',
-        'wrapAll',
-        'wrapInner',
-        'unwrap',
-
-        // Style manipulations
-        'css',
-        'offset',
-        'position',
-        'height',
-        'innerHeight',
-        'outerHeight',
-        'width',
-        'innerWidth',
-        'outerWidth',
-        'scrollLeft',
-        'scrollTop',
-
-        // Animations
-        'hide',
-        'show',
-        'toggle',
-        'animate',
-        'fadeIn',
-        'fadeOut',
-        'fadeTo',
-        'fadeToggle',
-        'slideDown',
-        'slideUp',
-        'slideToggle',
-
-        // Generic events
-        'on',
-        'off',
-        'live',
-        'die',
-        'delegate',
-        'undelegate',
-        'one',
-        'bind',
-        'unbind',
-
-        // More events
-        'blur',
-        'change',
-        'click',
-        'dblclick',
-        'error',
-        'focus',
-        'focusin',
-        'focusout',
-        'hover',
-        'keydown',
-        'keypress',
-        'keyup',
-        'load',
-        'mousedown',
-        'mouseenter',
-        'mouseleave',
-        'mousemove',
-        'mouseout',
-        'mouseover',
-        'mouseup',
-        'resize',
-        'scroll',
-        'select',
-        'submit',
-        'unload',
-
-        // Attributes
-        'attr',
-        'prop',
-        'removeAttr',
-        'removeProp',
-        'val',
-        'hasClass',
-        'addClass',
-        'removeClass',
-        'toggleClass',
-    ];
-
-    var jQueryTraversalFunctions = [
-        'children',
-        'closest',
-        'find',
-        'next',
-        'nextAll',
-        'nextUntil',
-        'offsetParent',
-        'parent',
-        'parents',
-        'parentsUntil',
-        'prev',
-        'prevAll',
-        'prevUntil',
-        'siblings'
-    ];
-
-    jQueryFunctions = jQueryFunctions.concat(jQueryTraversalFunctions);
-
-    // spy calls to jQuery functions
-    phantomas.on('init', function() {
-        phantomas.evaluate(function(jQueryFunctions, jQueryTraversalFunctions) {
-            (function(phantomas) {
-                var oldJQuery;
-
-                phantomas.spyGlobalVar('jQuery', function(jQuery) {
-                    var version;
-
-                    if (!jQuery || !jQuery.fn) {
-                        phantomas.log('jQuery: unable to detect version!');
-                        return;
-                    }
-
-                    // Tag the current version of jQuery to avoid multiple reports of jQuery being loaded
-                    // when it's actually only restored via $.noConflict(true) - see comments in #435
-                    if (jQuery.__phantomas === true) {
-                        phantomas.log('jQuery: this instance has already been seen by phantomas');
-                        return;
-                    }
-                    jQuery.__phantomas = true;
-
-                    // report the version of jQuery
-                    version = jQuery.fn.jquery;
-                    phantomas.emit('jQueryLoaded', version);
-
-                    phantomas.pushContext({
-                        type: (oldJQuery) ? 'jQuery version change' : 'jQuery loaded',
-                        callDetails: {
-                            arguments: ['version ' + version]
-                        },
-                        backtrace: phantomas.getBacktrace()
-                    });
-                    oldJQuery = version;
-
-                    // jQuery.ready.promise
-                    // works for jQuery 1.8.0+ (released Aug 09 2012)
-                    phantomas.spy(jQuery.ready, 'promise', function(func) {
-                        phantomas.incrMetric('jQueryOnDOMReadyFunctions');
-                        phantomas.addOffender('jQueryOnDOMReadyFunctions', phantomas.getCaller(3));
-
-                        phantomas.pushContext({
-                            type: 'jQuery - onDOMReady',
-                            callDetails: {
-                                arguments: [func]
-                            },
-                            backtrace: phantomas.getBacktrace()
-                        });
-
-                    }) || phantomas.log('jQuery: can not measure jQueryOnDOMReadyFunctions (jQuery used on the page is too old)!');
-
-
-                    // Sizzle calls - jQuery.find
-                    // works for jQuery 1.3+ (released Jan 13 2009)
-                    phantomas.spy(jQuery, 'find', function(selector, context) {
-                        phantomas.incrMetric('jQuerySizzleCalls');
-                        phantomas.addOffender('jQuerySizzleCalls', '%s (in %s)', selector, (phantomas.getDOMPath(context) || 'unknown'));
-                        
-                        phantomas.enterContext({
-                            type: 'jQuery - Sizzle call',
-                            callDetails: {
-                                context: {
-                                    length: 1,
-                                    elements: [phantomas.getDOMPath(context)]
-                                },
-                                arguments: [selector]
-                            },
-                            backtrace: phantomas.getBacktrace()
-                        });
-
-                    }, function(result) {
-                        var moreData = {
-                            resultsNumber : (result && result.length) ? result.length : 0
-                        };
-                        phantomas.leaveContext(moreData);
-                    }) || phantomas.log('jQuery: can not measure jQuerySizzleCalls (jQuery used on the page is too old)!');
-
-
-                    phantomas.spy(jQuery.fn, 'init', function(selector, context) {
-                        if (typeof selector === 'string' && /^#([\w\-]*)$/.exec(selector) !== null && !context) {
-
-                            phantomas.enterContext({
-                                type: 'jQuery - find',
-                                callDetails: {
-                                    arguments: [selector]
-                                },
-                                backtrace: phantomas.getBacktrace()
-                            });
-
-                        }
-
-                    }, function(result) {
-                        var data = phantomas.getContextData();
-
-                        if (data.type === 'jQuery - find' &&
-                                !data.callDetails.context &&
-                                data.callDetails.arguments.length === 1 &&
-                                /^#([\w\-]*)$/.exec(data.callDetails.arguments[0]) !== null) {
-
-                            var moreData = {
-                                resultsNumber : (result && result.length) ? result.length : 0
-                            };
-                            phantomas.leaveContext(moreData);
-                        }
-                    });
-
-
-                    if (!jQuery.event) {
-                        phantomas.spy(jQuery.event, 'trigger', function(ev, data, elem) {
-                            var path = phantomas.getDOMPath(elem),
-                                type = ev.type || ev;
-
-                            phantomas.log('Event: triggered "%s" on "%s"', type, path);
-
-                            phantomas.incrMetric('jQueryEventTriggers');
-                            phantomas.addOffender('jQueryEventTriggers', '"%s" on "%s"', type, path);
-                        });
-                    }
-
-                    // jQuery events bound to window' onLoad event (#451)
-                    phantomas.spy(jQuery.fn, 'on', function(eventName, func) {
-                        if ((eventName === 'load') && (this[0] === window)) {
-                            phantomas.incrMetric('jQueryWindowOnLoadFunctions');
-                            phantomas.addOffender('jQueryWindowOnLoadFunctions', phantomas.getCaller(2));
-                        }
-                    });
-
-                    // Add spys on many jQuery functions
-                    jQueryFunctions.forEach(function(functionName) {
-                        
-                        phantomas.spy(jQuery.fn, functionName, function(args) {
-
-                            // Clean args
-                            args = [].slice.call(arguments);
-                            args.forEach(function(arg, index) {
-                                
-                                if (arg instanceof Array) {
-                                    arg = '[Array]';
-                                }
-
-                                if (arg instanceof Object) {
-                                    
-                                    if (arg instanceof jQuery || (arg.jquery && arg.jquery.length > 0)) {
-                                        
-                                        arg = phantomas.getDOMPath(arg[0]) || 'unknown';
-
-                                    } else if (arg instanceof HTMLElement) {
-                                        
-                                        arg = phantomas.getDOMPath(arg) || 'unknown';
-
-                                    } else if (typeof arg === 'function') {
-
-                                        arg = '(function)';
-
-                                    } else {
-                                        
-                                        try {
-
-                                            for (var key in arg) {
-                                                if (typeof arg[key] === 'function') {
-                                                    arg[key] = '(function)';
-                                                }
-                                            }
-
-                                            arg = JSON.stringify(arg);
-                                        } catch(e) {
-                                            arg = '[Object]';
-                                        }
-                                        
-                                    }
-                                }
-
-                                if ((typeof arg === 'string' || arg instanceof String) && arg.length > 200) {
-                                    arg = arg.substring(0, 200) + '...';
-                                }
-
-                                if (typeof arg === 'function') {
-                                    arg = '(function)';
-                                }
-
-                                if (arg === true) {
-                                    arg = 'true';
-                                }
-
-                                if (arg === false) {
-                                    arg = 'false';
-                                }
-
-                                if (arg === null) {
-                                    arg = 'null';
-                                }
-
-                                if (typeof arg !== 'number' && typeof arg !== 'string' && !(arg instanceof String)) {
-                                    arg = 'undefined';
-                                }
-
-                                args[index] = arg;
-                            });
-
-                            var elements = [];
-                            for (var i = 0 ; i < this.length ; i++) {
-                                elements.push(phantomas.getDOMPath(this[i]));
-                            }
-
-                            phantomas.enterContext({
-                                type: 'jQuery - ' + functionName,
-                                callDetails: {
-                                    context: {
-                                        length: this.length,
-                                        elements: elements
-                                    },
-                                    arguments: args
-                                },
-                                backtrace: phantomas.getBacktrace()
-                            });
-
-                        }, function(result) {
-                            if (jQueryTraversalFunctions.indexOf(functionName) >= 0) {
-                                var moreData = {
-                                    resultsNumber : (result && result.length) ? result.length : 0
-                                };
-                                phantomas.leaveContext(moreData);
-                            } else {
-                                phantomas.leaveContext();
-                            }
-                        }) || phantomas.log('jQuery: can not track jQuery - ' + functionName + ' (this version of jQuery doesn\'t support it)');
-                    });
-                });
-            })(window.__phantomas);
-        }, jQueryFunctions, jQueryTraversalFunctions);
-    });
-
-
-    phantomas.on('jQueryLoaded', function(version) {
-        phantomas.log('jQuery: loaded v' + version);
-        phantomas.setMetric('jQueryVersion', version);
-
-        // report multiple jQuery "instances" (issue #435)
-        phantomas.incrMetric('jQueryVersionsLoaded');
-        phantomas.addOffender('jQueryVersionsLoaded', 'v%s', version);
-    });
-};

+ 0 - 69
lib/tools/phantomas/custom_modules/modules/javaScriptBottleYLT/javaScriptBottleYLT.js

@@ -1,69 +0,0 @@
-/**
- * Reports the use of functions known to be serious performance bottlenecks in JS
- *
- * @see http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/
- * @see http://www.quirksmode.org/blog/archives/2005/06/three_javascrip_1.html
- * @see http://www.stevesouders.com/blog/2012/04/10/dont-docwrite-scripts/
- *
- * Run phantomas with --spy-eval to count eval() calls (see issue #467)
- */
-/* global document: true, window: true */
-
-exports.version = '0.2';
-
-exports.module = function(phantomas) {
-    'use strict';
-    
-    phantomas.setMetric('documentWriteCalls'); //@desc number of calls to either document.write or document.writeln @offenders
-    phantomas.setMetric('evalCalls'); // @desc number of calls to eval (either direct or via setTimeout / setInterval) @offenders
-
-    // spy calls to eval only when requested (issue #467)
-    var spyEval = phantomas.getParam('spy-eval') === true;
-    if (!spyEval) {
-        phantomas.log('javaScriptBottlenecks: to spy calls to eval() run phantomas with --spy-eval option');
-    }
-
-    phantomas.on('init', function() {
-        phantomas.evaluate(function(spyEval) {
-            (function(phantomas) {
-                function report(msg, caller, backtrace, metric) {
-                    phantomas.log(msg + ': from ' + caller + '!');
-                    phantomas.log('Backtrace: ' + backtrace);
-
-                    phantomas.incrMetric(metric);
-                    phantomas.addOffender(metric, "%s from %s", msg, caller);
-                }
-
-                // spy calls to eval()
-                if (spyEval) {
-                    phantomas.spy(window, 'eval', function(code) {
-                        report('eval() called directly', phantomas.getCaller(), phantomas.getBacktrace(), 'evalCalls');
-                        phantomas.log('eval\'ed code: ' + (code || '').substring(0, 150) + '(...)');
-                    });
-                }
-
-                // spy calls to setTimeout / setInterval with string passed instead of a function
-                phantomas.spy(window, 'setTimeout', function(fn, interval) {
-                    if (typeof fn !== 'string') return;
-
-                    report('eval() called via setTimeout("' + fn + '")', phantomas.getCaller(), phantomas.getBacktrace(), 'evalCalls');
-                });
-
-                phantomas.spy(window, 'setInterval', function(fn, interval) {
-                    if (typeof fn !== 'string') return;
-
-                    report('eval() called via setInterval("' + fn + '")', phantomas.getCaller(), phantomas.getBacktrace(), 'evalCalls');
-                });
-
-                // spy document.write(ln)
-                phantomas.spy(document, 'write', function(arg) {
-                    report('document.write() used', phantomas.getCaller(), phantomas.getBacktrace(), 'documentWriteCalls');
-                });
-
-                phantomas.spy(document, 'writeln', function(arg) {
-                    report('document.writeln() used', phantomas.getCaller(), phantomas.getBacktrace(), 'documentWriteCalls');
-                });
-            })(window.__phantomas);
-        }, spyEval);
-    });
-};

+ 0 - 31
lib/tools/phantomas/custom_modules/modules/jsTreeYLT/jsTreeYLT.js

@@ -1,31 +0,0 @@
-/**
- * Saves the javascript interractions with the DOM
- *
- * Run phantomas with --js-execution-tree option to use this module
- */
-
-exports.version = '0.1';
-
-exports.module = function(phantomas) {
-    'use strict';
-
-    phantomas.setMetric('javascriptExecutionTree');
-
-    // save data
-    phantomas.on('report', function() {
-        phantomas.log('JS execution tree: Reading execution tree JSON');
-
-        phantomas.evaluate(function() {(function(phantomas) {
-            var fullTree = phantomas.readFullTree();
-
-            if (fullTree === null) {
-                phantomas.log('JS execution tree: error, the execution tree is not correctly closed');
-                return;
-            }
-
-            phantomas.setMetric('javascriptExecutionTree', true, true);
-            phantomas.addOffender('javascriptExecutionTree', JSON.stringify(fullTree));
-
-        })(window.__phantomas);});
-    });
-};

+ 0 - 69
lib/tools/phantomas/custom_modules/modules/scrollListener/scrollListener.js

@@ -1,69 +0,0 @@
-exports.version = '0.1';
-
-exports.module = function(phantomas) {
-    'use strict';
-
-    phantomas.setMetric('scrollExecutionTree');
-
-    phantomas.on('report', function() {
-
-        phantomas.evaluate(function() {
-            (function(phantomas) {
-
-                var evt = document.createEvent('CustomEvent');
-                evt.initCustomEvent('scroll', false, false, null);
-
-                function triggerScrollEvent() {
-                    phantomas.resetTree();
-
-                    try {
-
-                        // Chrome triggers them in this order:
-
-                        // 1. document
-                        phantomas.pushContext({
-                            type: 'documentScroll'
-                        });
-                        document.dispatchEvent(evt);
-
-                        // 2. window
-                        phantomas.pushContext({
-                            type: 'windowScroll'
-                        });
-                        window.dispatchEvent(evt);
-
-                        // No need to call window.onscroll(), it's called by the scroll event on window
-
-                    } catch(e) {
-                        phantomas.log('ScrollListener error: %s', e);
-                    }
-                }
-
-                var firstScrollTime = Date.now();
-                phantomas.log('ScrollListener: triggering a first scroll event...');
-                triggerScrollEvent();
-
-
-                // Ignore the first scroll event and only save the second one,
-                // because we want to detect un-throttled things, throttled ones are ok.
-                var secondScrollTime = Date.now();
-                phantomas.log('ScrollListener: triggering a second scroll event (%dms after the first)...', secondScrollTime - firstScrollTime);
-                triggerScrollEvent();
-
-
-                var fullTree = phantomas.readFullTree();
-                if (fullTree !== null) {
-                    phantomas.setMetric('scrollExecutionTree', true, true);
-                    phantomas.addOffender('scrollExecutionTree', JSON.stringify(fullTree));
-                    phantomas.log('ScrollListener: scrollExecutionTree correctly extracted');
-                } else {
-                    phantomas.log('Error: scrollExecutionTree could not be extracted');
-                }
-
-
-                phantomas.log('ScrollListener: end of scroll triggering');
-
-            })(window.__phantomas);
-        });
-    });
-};

+ 0 - 150
lib/tools/phantomas/custom_modules/modules/windowPerfYLT/windowPerfYLT.js

@@ -1,150 +0,0 @@
-/**
- * Measure when the page reaches certain states
- *
- * @see http://w3c-test.org/webperf/specs/NavigationTiming/#dom-performancetiming-domloading
- * @see https://developers.google.com/web/fundamentals/performance/critical-rendering-path/measure-crp
- */
-/* global document: true, window: true */
-
-exports.version = '1.0.a';
-
-exports.module = function(phantomas) {
-    'use strict';
-    
-    // times below are calculated relative to performance.timing.responseEnd (#117)
-    phantomas.setMetric('domInteractive');      // @desc time it took to parse the HTML and construct the DOM
-    phantomas.setMetric('domContentLoaded');    // @desc time it took to construct both DOM and CSSOM, no stylesheets that are blocking JavaScript execution (i.e. onDOMReady)
-    phantomas.setMetric('domContentLoadedEnd'); // @desc time it took to finish handling of onDOMReady event @unreliable
-    phantomas.setMetric('domComplete');         // @desc time it took to load all page resources, the loading spinner has stopped spinning
-
-    // backend vs frontend time
-    phantomas.setMetric('timeBackend');  // @desc time to the first byte compared to the total loading time [%]
-    phantomas.setMetric('timeFrontend'); // @desc time to window.load compared to the total loading time [%]
-
-    // measure dom... metrics from the moment HTML response was fully received
-    var responseEndTime = Date.now();
-
-    phantomas.on('responseEnd', function() {
-        responseEndTime = Date.now();
-        phantomas.log('Performance timing: responseEnd = %d', responseEndTime);
-    });
-
-    phantomas.on('init', function() {
-        phantomas.evaluate(function(responseEndTime) {
-            (function(phantomas) {
-                phantomas.spyEnabled(false, 'installing window.performance metrics');
-
-                phantomas.currentStep = 'domCreation';
-
-                // extend window.performance
-                // "init" event is sometimes fired twice, pass a value set by "responseEnd" event handler (fixes #192)
-                if (typeof window.performance === 'undefined') {
-                    window.performance = {
-                        timing: {
-                            responseEnd: responseEndTime
-                        }
-                    };
-
-                    phantomas.log('Performance timing: emulating window.performance');
-                }
-                else {
-                    phantomas.log('Performance timing: using native window.performance');
-                }
-
-                // onDOMReady
-                document.addEventListener("DOMContentLoaded", function() {
-                    
-                    setTimeout(function() {
-                        // use NavigationTiming if possible
-                        var time = window.performance.timing.domContentLoadedEventEnd ?
-                            (window.performance.timing.domContentLoadedEventEnd - window.performance.timing.responseEnd)
-                            :
-                            (Date.now() - responseEndTime);
-
-                        phantomas.currentStep = 'domContentLoadedEnd';
-                        phantomas.setMetric('domContentLoadedEnd', time, true);
-                        phantomas.log('Performance timing: document reached "DOMContentLoadedEnd" state after %d ms', time);
-
-                        phantomas.pushContext({
-                            type: 'domContentLoadedEnd'
-                        });
-                    }, 0);
-
-                    var time = Date.now() - responseEndTime;
-
-                    phantomas.currentStep = 'domContentLoaded';
-                    phantomas.setMetric('domContentLoaded', time, true);
-                    phantomas.log('Performance timing: document reached "DOMContentLoaded" state after %d ms', time);
-
-                    phantomas.pushContext({
-                        type: 'domContentLoaded'
-                    });
-                });
-
-                // emulate Navigation Timing
-                document.addEventListener('readystatechange', function() {
-                    var readyState = document.readyState,
-                        responseEndTime = window.performance.timing.responseEnd,
-                        time = Date.now() - responseEndTime,
-                        metricName;
-
-                    // @see http://www.w3.org/TR/html5/dom.html#documentreadystate
-                    switch(readyState) {
-                        // the browser has finished parsing all of the HTML and DOM construction is complete
-                        case 'interactive':
-                            metricName = 'domInteractive';
-                            break;
-
-                        // the processing is complete and all of the resources on the page have finished downloading
-                        case 'complete':
-                            metricName = 'domComplete';
-                            phantomas.log('Performance timing: %j', window.performance.timing);
-                            break;
-
-                        default:
-                            phantomas.log('Performance timing: unhandled "%s" state!', readyState);
-                            return;
-                    }
-
-                    phantomas.currentStep = metricName;
-                    phantomas.setMetric(metricName, time, true);
-                    phantomas.log('Performance timing: document reached "%s" state after %d ms', readyState, time);
-
-                    phantomas.pushContext({
-                        type: metricName
-                    });
-                });
-
-                phantomas.spyEnabled(true);
-            })(window.__phantomas);
-        }, responseEndTime);
-    });
-
-    /**
-     * Emit metrics with backend vs frontend time
-     *
-     * Performance Golden Rule:
-     * "80-90% of the end-user response time is spent on the frontend. Start there."
-     *
-     * @see http://www.stevesouders.com/blog/2012/02/10/the-performance-golden-rule/
-     */
-    phantomas.on('report', function() {
-        //  The “backend” time is the time it takes the server to get the first byte back to the client.
-        //  The “frontend” time is measured from the last byte of the response (responseEnd) until all resources are fetched (domComplete)
-        var backendTime = parseInt(phantomas.getMetric('timeToFirstByte'), 10),
-            frontendTime = parseInt(phantomas.getMetric('domComplete'), 10),
-            totalTime = backendTime + frontendTime,
-            backendTimePercentage;
-
-        if (totalTime === 0) {
-            return;
-        }
-
-        backendTimePercentage = Math.round(backendTime / totalTime * 100);
-
-        phantomas.setMetric('timeBackend', backendTimePercentage);
-        phantomas.setMetric('timeFrontend', 100 - backendTimePercentage);
-
-        phantomas.log('Performance timing: backend vs frontend time - %d% / %d%', backendTimePercentage, 100 - backendTimePercentage);
-    });
-};