blob: 9c33f84fa13e897a83356c5d0a0586d88a7af30d [file] [log] [blame]
zachr@google.com6f8e2c52013-08-07 15:43:04 +00001var MAX_SWAP_IMG_SIZE = 400;
2
zachr@google.com1bc995e2013-07-12 13:39:53 +00003angular.module('diff_viewer', []).
4config(['$routeProvider', function($routeProvider) {
5 // Show the list of differences by default
6 $routeProvider.
7 otherwise({ templateUrl: '/diff_list.html', controller: DiffListController});
8}]).
9directive('swapImg', function() {
10 // Custom directive for showing an image that gets swapped my mouseover.
11 return {
12 restrict: 'E', // The directive can be used as an element name
13 replace: true, // The directive replaces itself with the template
14 template: '<canvas ng-mouseenter="swap()" ng-mouseleave="swap()"></canvas>',
15 scope: { // The attributes below are bound to the scope
16 leftSrc: '@',
17 rightSrc: '@',
18 side: '@'
19 },
20 link: function(scope, elm, attrs, ctrl) {
21 var leftImage = new Image();
22 var rightImage = new Image();
23 var ctx = elm[0].getContext('2d');
24
25 scope.render = function() {
26 var image;
27 if (scope.side == "left") {
28 image = leftImage;
29 } else {
30 image = rightImage;
31 }
32
zachr@google.com6f8e2c52013-08-07 15:43:04 +000033 // Make it so the maximum size of an image is MAX_SWAP_IMG_SIZE, and the images are
34 // scaled down in halves.
zachr@google.com1bc995e2013-07-12 13:39:53 +000035 var divisor = 1;
zachr@google.com6f8e2c52013-08-07 15:43:04 +000036 while ((image.width / divisor) > MAX_SWAP_IMG_SIZE) {
zachr@google.com1bc995e2013-07-12 13:39:53 +000037 divisor *= 2;
38 }
39
40 // Set canvas to correct size and draw the image into it
41 elm[0].width = image.width / divisor;
42 elm[0].height = image.height / divisor;
43 ctx.drawImage(image, 0, 0, elm[0].width, elm[0].height);
44 };
45
46 // When the leftSrc attribute changes, load the image and then rerender
47 attrs.$observe('leftSrc', function(value) {
48 leftImage.src = value;
49 leftImage.onload = function() {
50 if (scope.side == "left") {
51 scope.render();
52 }
53 };
54 });
55
56 // When the rightSrc attribute changes, load the image and then rerender
57 attrs.$observe('rightSrc', function(value) {
58 rightImage.src = value;
59 rightImage.onload = function() {
60 if (scope.side == "right") {
61 scope.render();
62 }
63 };
64 });
65
66 // Swap which side to draw onto the canvas and then rerender
67 scope.swap = function() {
68 if (scope.side == "left") {
69 scope.side = "right";
70 } else {
71 scope.side = "left";
72 }
73 scope.render();
74 };
75 }
76 };
77});
78
zachr@google.com74c5ab12013-08-07 18:06:39 +000079function DiffListController($scope, $http, $location, $timeout, $parse) {
80 // Detect if we are running the web server version of the viewer. If so, we set a flag and
81 // enable some extra functionality of the website for rebaselining.
82 $scope.isDynamic = ($location.protocol() == "http" || $location.protocol() == "https");
83
zachr@google.com1bc995e2013-07-12 13:39:53 +000084 // Label each kind of differ for the sort buttons.
zachr@google.com9432c0c2013-07-09 21:08:28 +000085 $scope.differs = [
86 {
87 "title": "Different Pixels"
88 },
89 {
90 "title": "Perceptual Difference"
91 }
92 ];
zachr@google.com1bc995e2013-07-12 13:39:53 +000093
94 // Puts the records within AngularJS scope
zachr@google.com9432c0c2013-07-09 21:08:28 +000095 $scope.records = SkPDiffRecords.records;
zachr@google.com1bc995e2013-07-12 13:39:53 +000096
zachr@google.com74c5ab12013-08-07 18:06:39 +000097 // Keep track of the index of the last record to change so that shift clicking knows what range
98 // of records to apply the action to.
99 $scope.lastSelectedIndex = undefined;
100
zachr@google.com1bc995e2013-07-12 13:39:53 +0000101 // Indicates which diff metric is used for sorting
102 $scope.sortIndex = 1;
103
104 // Called by the sort buttons to adjust the metric used for sorting
105 $scope.setSortIndex = function(idx) {
106 $scope.sortIndex = idx;
zachr@google.com74c5ab12013-08-07 18:06:39 +0000107
108 // Because the index of things has most likely changed, the ranges of shift clicking no
109 // longer make sense from the user's point of view. We reset it to avoid confusion.
110 $scope.lastSelectedIndex = undefined;
zachr@google.com1bc995e2013-07-12 13:39:53 +0000111 };
112
113 // A predicate for pulling out the number used for sorting
114 $scope.sortingDiffer = function(record) {
115 return record.diffs[$scope.sortIndex].result;
116 };
zachr@google.com6f8e2c52013-08-07 15:43:04 +0000117
zachr@google.com74c5ab12013-08-07 18:06:39 +0000118 // Flash status indicator on the page, and then remove it so the style can potentially be
zachr@google.com6f8e2c52013-08-07 15:43:04 +0000119 // reapplied later.
zachr@google.com74c5ab12013-08-07 18:06:39 +0000120 $scope.flashStatus = function(success) {
zachr@google.com6f8e2c52013-08-07 15:43:04 +0000121 var flashStyle = success ? "success-flash" : "failure-flash";
122 var flashDurationMillis = success ? 500 : 800;
123
124 // Store the style in the record. The row will pick up the style this way instead of through
125 // index because index can change with sort order.
zachr@google.com74c5ab12013-08-07 18:06:39 +0000126 $scope.statusClass = flashStyle;
zachr@google.com6f8e2c52013-08-07 15:43:04 +0000127
128 // The animation cannot be repeated unless the class is removed the element.
129 $timeout(function() {
zachr@google.com74c5ab12013-08-07 18:06:39 +0000130 $scope.statusClass = "";
zachr@google.com6f8e2c52013-08-07 15:43:04 +0000131 }, flashDurationMillis);
zachr@google.com74c5ab12013-08-07 18:06:39 +0000132 };
zachr@google.com6f8e2c52013-08-07 15:43:04 +0000133
zachr@google.com74c5ab12013-08-07 18:06:39 +0000134 $scope.selectedRebaseline = function(index, event) {
135 // Retrieve the records in the same order they are displayed.
136 var recordsInOrder = $parse("records | orderBy:sortingDiffer")($scope);
137
138 // If the user is shift clicking, apply the last tick/untick to all elements in between this
139 // record, and the last one they ticked/unticked.
140 if (event.shiftKey && $scope.lastSelectedIndex !== undefined) {
141 var currentAction = recordsInOrder[index].isRebaselined;
142 var smallerIndex = Math.min($scope.lastSelectedIndex, index);
143 var largerIndex = Math.max($scope.lastSelectedIndex, index);
144 for (var recordIndex = smallerIndex; recordIndex <= largerIndex; recordIndex++) {
145 recordsInOrder[recordIndex].isRebaselined = currentAction;
146 }
147 $scope.lastSelectedIndex = index;
148 }
149 else
150 {
151 $scope.lastSelectedIndex = index;
152 }
153
154 };
155
156 $scope.commitRebaselines = function() {
157 // Gather up all records that have the rebaseline set.
158 var rebaselines = [];
159 for (var recordIndex = 0; recordIndex < $scope.records.length; recordIndex++) {
160 if ($scope.records[recordIndex].isRebaselined) {
161 rebaselines.push($scope.records[recordIndex].testPath);
162 }
163 }
164 $http.post("/commit_rebaselines", {
165 "rebaselines": rebaselines
zachr@google.com6f8e2c52013-08-07 15:43:04 +0000166 }).success(function(data) {
zachr@google.com74c5ab12013-08-07 18:06:39 +0000167 $scope.flashStatus(data.success);
zachr@google.com6f8e2c52013-08-07 15:43:04 +0000168 }).error(function() {
zachr@google.com74c5ab12013-08-07 18:06:39 +0000169 $scope.flashStatus(false);
zachr@google.com6f8e2c52013-08-07 15:43:04 +0000170 });
zachr@google.com6f8e2c52013-08-07 15:43:04 +0000171 };
zachr@google.com1bc995e2013-07-12 13:39:53 +0000172}