unminified-script.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. var timelineCtrl = angular.module('timelineCtrl', []);
  2. timelineCtrl.controller('TimelineCtrl', ['$scope', '$rootScope', '$routeParams', '$location', '$timeout', 'Menu', 'Results', 'API', function($scope, $rootScope, $routeParams, $location, $timeout, Menu, Results, API) {
  3. $scope.runId = $routeParams.runId;
  4. $scope.Menu = Menu.setCurrentPage('timeline', $scope.runId);
  5. function loadResults() {
  6. // Load result if needed
  7. if (!$rootScope.loadedResult || $rootScope.loadedResult.runId !== $routeParams.runId) {
  8. Results.get({runId: $routeParams.runId, exclude: 'toolsResults'}, function(result) {
  9. $rootScope.loadedResult = result;
  10. $scope.result = result;
  11. render();
  12. });
  13. } else {
  14. $scope.result = $rootScope.loadedResult;
  15. render();
  16. }
  17. }
  18. function render() {
  19. initFilters();
  20. initScriptFiltering();
  21. initExecutionTree();
  22. initTimeline();
  23. $timeout(initProfiler, 100);
  24. }
  25. function initFilters() {
  26. var hash = $location.hash();
  27. var filter = null;
  28. if (hash.indexOf('filter=') === 0) {
  29. filter = hash.substr(7);
  30. }
  31. $scope.warningsFilterOn = (filter !== null);
  32. $scope.warningsFilters = {
  33. queryWithoutResults: (filter === null || filter === 'queryWithoutResults'),
  34. jQueryCallOnEmptyObject: (filter === null || filter === 'jQueryCallOnEmptyObject'),
  35. eventNotDelegated: (filter === null || filter === 'eventNotDelegated'),
  36. jsError: (filter === null || filter === 'jsError')
  37. };
  38. }
  39. function initScriptFiltering() {
  40. var offenders = $scope.result.rules.jsCount.offendersObj.list;
  41. $scope.scripts = [];
  42. offenders.forEach(function(script) {
  43. var filePath = script.file;
  44. if (filePath.length > 100) {
  45. filePath = filePath.substr(0, 98) + '...';
  46. }
  47. var scriptObj = {
  48. fullPath: script.file,
  49. shortPath: filePath
  50. };
  51. $scope.scripts.push(scriptObj);
  52. });
  53. }
  54. function initExecutionTree() {
  55. var originalExecutions = $scope.result.javascriptExecutionTree.children || [];
  56. // Detect the last event of all (before filtering) and read time
  57. var lastEvent = originalExecutions[originalExecutions.length - 1];
  58. $scope.endTime = lastEvent.data.timestamp + (lastEvent.data.time || 0);
  59. // Filter
  60. $scope.executionTree = [];
  61. originalExecutions.forEach(function(node) {
  62. // Filter by script (if enabled)
  63. if ($scope.selectedScript) {
  64. if (node.data.backtrace && node.data.backtrace.indexOf($scope.selectedScript.fullPath + ':') === -1) {
  65. return;
  66. }
  67. if (node.data.type === "jQuery loaded" || node.data.type === "jQuery version change") {
  68. return;
  69. }
  70. }
  71. $scope.executionTree.push(node);
  72. });
  73. }
  74. function initTimeline() {
  75. // Split the timeline into 200 intervals
  76. var numberOfIntervals = 199;
  77. $scope.timelineIntervalDuration = $scope.endTime / numberOfIntervals;
  78. // Pre-fill array of as many elements as there are milleseconds
  79. var millisecondsArray = Array.apply(null, new Array($scope.endTime + 1)).map(Number.prototype.valueOf,0);
  80. // Create the milliseconds array from the execution tree
  81. $scope.executionTree.forEach(function(node) {
  82. if (node.data.time !== undefined) {
  83. // Ignore artefacts (durations > 100ms)
  84. var time = Math.min(node.data.time, 100) || 1;
  85. for (var i=node.data.timestamp, max=node.data.timestamp + time ; i<max ; i++) {
  86. millisecondsArray[i] |= 1;
  87. }
  88. }
  89. });
  90. // Pre-fill array of 200 elements
  91. $scope.timeline = Array.apply(null, new Array(numberOfIntervals + 1)).map(Number.prototype.valueOf,0);
  92. // Create the timeline from the milliseconds array
  93. millisecondsArray.forEach(function(value, timestamp) {
  94. if (value === 1) {
  95. $scope.timeline[Math.floor(timestamp / $scope.timelineIntervalDuration)] += 1;
  96. }
  97. });
  98. // Get the maximum value of the array (needed for display)
  99. $scope.timelineMax = Math.max.apply(Math, $scope.timeline);
  100. }
  101. function initProfiler() {
  102. $scope.profilerData = $scope.executionTree;
  103. }
  104. $scope.changeScript = function() {
  105. initExecutionTree();
  106. initTimeline();
  107. initProfiler();
  108. };
  109. $scope.findLineIndexByTimestamp = function(timestamp) {
  110. var lineIndex = 0;
  111. for (var i = 0; i < $scope.executionTree.length; i ++) {
  112. var delta = $scope.executionTree[i].data.timestamp - timestamp;
  113. if (delta < $scope.timelineIntervalDuration) {
  114. lineIndex = i;
  115. }
  116. if (delta > 0) {
  117. break;
  118. }
  119. }
  120. return lineIndex;
  121. };
  122. $scope.backToDashboard = function() {
  123. $location.path('/result/' + $scope.runId);
  124. };
  125. $scope.testAgain = function() {
  126. API.relaunchTest($scope.result);
  127. };
  128. loadResults();
  129. }]);
  130. timelineCtrl.directive('scrollOnClick', ['$animate', '$timeout', function($animate, $timeout) {
  131. return {
  132. restrict: 'A',
  133. link: function (scope, element, attributes) {
  134. // When the user clicks on the timeline, find the right profiler line and scroll to it
  135. element.on('click', function() {
  136. var lineIndex = scope.findLineIndexByTimestamp(attributes.scrollOnClick);
  137. var lineElement = angular.element(document.getElementById('line_' + lineIndex));
  138. // Animate the background color to "flash" the row
  139. lineElement.addClass('highlight');
  140. $timeout(function() {
  141. $animate.removeClass(lineElement, 'highlight');
  142. scope.$digest();
  143. }, 50);
  144. window.scrollTo(0, lineElement[0].offsetTop);
  145. });
  146. }
  147. };
  148. }]);