compaa 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d711cdbc668d05ba379bd3c23f3b4c976f14d64e
4
- data.tar.gz: 539c603dc0d32fa92df5f4e7101ed6901d0993f4
3
+ metadata.gz: 9c8d165574630697b7fc7b420eef9892d182f145
4
+ data.tar.gz: 68192e078d30afd1805e444e72bff910e056a5f6
5
5
  SHA512:
6
- metadata.gz: d6cc2c9503a331f7ea53743c219947c013f6318e1067ad53813191b3331b91a47bb7cf621944bfa0426b7c772a40451325c65a0f429ceb27aea891b311ddb20d
7
- data.tar.gz: 489961c1c644e26993143c1dc33dedd4de3c1247875c8a74c377f09dd188775b64b2434108536eda6fd5dc1383608ea3c3a0c1d4d1bbb0e1ec4c3ef14bc1ba80
6
+ metadata.gz: 03094bac2f80369957d8e5c1a2e365e74c9c9c25461e5709bd3644b6ece33cc98900be3718e1908316e71d56f7f957104cb07eef2746d6008e9d2069d02d84a2
7
+ data.tar.gz: c5d07e05502f92836fc34a9449c2c708f4a911cae5ec3c17279d135ac92cef9ca5f87e81372ba913050492c2d75d691e2457f486f240418895ad9840706145f1
data/.gitignore CHANGED
@@ -6,3 +6,4 @@ Gemfile.lock
6
6
  coverage
7
7
  tags
8
8
  tmp
9
+ spec/javascripts/compiled
data/Rakefile CHANGED
@@ -8,6 +8,10 @@ load 'jasmine/tasks/jasmine.rake'
8
8
 
9
9
  Bundler::GemHelper.install_tasks
10
10
 
11
+ task :start_mock do
12
+ require_relative 'mock_app'
13
+ end
14
+
11
15
  namespace :spec do
12
16
  Rake::TestTask.new :units do |t|
13
17
  t.libs << 'spec'
@@ -23,11 +27,15 @@ namespace :spec do
23
27
  t.colors = true
24
28
  t.keep_on_error = true
25
29
  end
30
+
31
+ task :js => :start_mock
26
32
  end
27
33
 
28
- task :spec => ['spec:units', 'spec:js', 'spec:integration']
34
+ task 'jasmine:require' => :start_mock
35
+
36
+ task :spec => ['spec:units', 'jasmine:ci', 'spec:integration']
29
37
 
30
- task :default => [:spec]
38
+ task :default => :spec
31
39
 
32
40
  task :demo do
33
41
  require 'compaa'
data/bin/compaa CHANGED
@@ -2,5 +2,11 @@
2
2
 
3
3
  require 'compaa'
4
4
  require 'rack'
5
+ require 'launchy'
6
+
7
+ Thread.new do
8
+ sleep 0.25
9
+ Launchy.open('http://localhost:7788')
10
+ end
5
11
 
6
12
  Rack::Server.start(:app => Compaa::RackApp.new, :Port => 7788)
@@ -1,3 +1,4 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
1
2
  require 'compaa/version'
2
3
 
3
4
  Gem::Specification.new do |gem|
@@ -30,4 +31,6 @@ Gem::Specification.new do |gem|
30
31
 
31
32
  gem.add_dependency 'haml'
32
33
  gem.add_dependency 'rack'
34
+ gem.add_dependency 'launchy'
35
+ gem.add_dependency 'coffee-script'
33
36
  end
@@ -0,0 +1,59 @@
1
+ (function() {
2
+ var supportsBlending = window.navigator.userAgent.indexOf('Firefox/2') !== -1,
3
+ firstImage = new Image(),
4
+ secondImage = new Image(),
5
+ differenceCanvas = document.createElement('canvas');
6
+
7
+ function resize(subject, width, height) {
8
+ subject.width = width;
9
+ subject.height = height;
10
+ };
11
+
12
+ function makeItBlend(imageOne, imageTwo, callback) {
13
+ firstImage = imageOne;
14
+ secondImage = imageTwo;
15
+
16
+ setBlenderWidth();
17
+ drawCanvas();
18
+ callback(differenceCanvas);
19
+ };
20
+
21
+ function setBlenderWidth() {
22
+ resize(differenceCanvas, secondImage.width, secondImage.height);
23
+ };
24
+
25
+ function drawCanvas() {
26
+ supportsBlending ? blendForRealz() : blendFallback();
27
+ };
28
+
29
+ function blendForRealz() {
30
+ var context;
31
+
32
+ context = differenceCanvas.getContext('2d');
33
+ context.globalCompositeOperation = 'difference';
34
+ context.drawImage(secondImage, 0, 0);
35
+ context.drawImage(firstImage, 0, 0);
36
+ };
37
+
38
+ function blendFallback() {
39
+ var canvas, over, under;
40
+
41
+ canvas = document.createElement('canvas');
42
+ resize(canvas, firstImage.width, firstImage.height);
43
+
44
+ over = canvas.getContext('2d');
45
+ under = differenceCanvas.getContext('2d');
46
+
47
+ over.clearRect(0, 0, over.canvas.width, over.canvas.height);
48
+ under.clearRect(0, 0, under.canvas.width, under.canvas.height);
49
+
50
+ over.drawImage(secondImage, 0, 0);
51
+ under.drawImage(firstImage, 0, 0);
52
+
53
+ over.blendOnto(under, 'difference');
54
+ };
55
+
56
+ window.blender = {
57
+ makeItBlend: makeItBlend
58
+ };
59
+ })();
@@ -1,153 +1,151 @@
1
- var Compaa = {
2
- imageTypes: ['difference', 'animation', 'referenceImage', 'generatedImage'],
3
- compaaHost: '',
4
-
5
- init: function() {
6
- this.attachClickHandlers();
7
-
8
- $.getJSON(this.compaaHost + '/artifacts.json', function(json) {
9
- this.setArtifacts(json.artifacts);
10
- this.paintThePicture();
11
- }.bind(this));
12
- },
13
-
14
- attachClickHandlers: function() {
15
- this.attachButtonClickHandlers();
16
- this.attachAcceptClickHandler();
17
- this.attachRejectClickHandler();
18
- },
19
-
20
- setArtifacts: function(artifacts) {
21
- this.artifacts = artifacts;
22
- },
23
-
24
- paintThePicture: function() {
25
- this.drawDifferenceCanvas();
26
- this.setDifferenceImage();
27
- this.setGeneratedImage();
28
- this.setReferenceImage();
29
- this.show('generatedImage');
30
- },
31
-
32
- show: function(mode) {
33
- _.each(this.imageTypes, function(type) {
34
- $('#' + type).hide();
1
+ (function() {
2
+ var imageTypes = ['difference', 'animation', 'referenceImage', 'generatedImage'],
3
+ compaaHost = '',
4
+ artifacts = [],
5
+ generatedImage = new Image(),
6
+ referenceImage = new Image();
7
+
8
+
9
+ function init() {
10
+ attachClickHandlers();
11
+
12
+ storeArtifacts(function() {
13
+ paintThePicture();
14
+ show('difference');
15
+ });
16
+ };
17
+
18
+ function storeArtifacts(callback) {
19
+ var xhr;
20
+
21
+ xhr = new XMLHttpRequest();
22
+ xhr.open('GET', compaaHost + '/artifacts.json', true);
23
+ xhr.onload = function() {
24
+ artifacts = JSON.parse(xhr.responseText).artifacts;
25
+ callback();
26
+ };
27
+ xhr.send();
28
+ };
29
+
30
+ function attachClickHandlers() {
31
+ attachButtonClickHandlers();
32
+ attachAcceptClickHandler();
33
+ attachRejectClickHandler();
34
+ };
35
+
36
+ function replaceCanvasWith(newCanvas) {
37
+ newCanvas.id = 'difference';
38
+ document.body.replaceChild(newCanvas, document.getElementById('difference'));
39
+ };
40
+
41
+ function paintThePicture() {
42
+ generatedImage.onload = function() {
43
+ referenceImage.onload = function() {
44
+ window.blender.makeItBlend(generatedImage, referenceImage, function(blendedImage) {
45
+ replaceCanvasWith(blendedImage);
46
+ setDifferenceImage();
47
+ setGeneratedImage();
48
+ setReferenceImage();
49
+ });
50
+ };
51
+
52
+ referenceImage.onerror = function() {
53
+ setGeneratedImage();
54
+ setReferenceImage();
55
+ disableOtherButtons();
56
+ show('generatedImage');
57
+ };
58
+
59
+ referenceImage.src = referenceImagePath();
60
+ };
61
+
62
+ generatedImage.src = generatedImagePath();
63
+ };
64
+
65
+ function disableOtherButtons() {
66
+ imageTypes.map(function(element) {
67
+ return document.getElementById(element + 'Button');
68
+ }).forEach(function(element) {
69
+ element.className = element.className + ' disabled';
70
+ });
71
+ };
72
+
73
+ function show(mode) {
74
+ imageTypes.forEach(function(type) {
75
+ document.getElementById(type).style.display = 'none';
35
76
  });
36
77
 
37
- $('#' + mode).show();
38
- },
39
-
40
- attachButtonClickHandlers: function() {
41
- _.each(this.imageTypes, function(element) {
42
- $('#' + element + 'Button').click(function(evt) {
43
- evt.preventDefault();
44
- this.show(element);
45
- }.bind(this));
46
- }.bind(this));
47
- },
48
-
49
- supportsBlending: function() {
50
- return window.navigator.userAgent.indexOf('Firefox/2') !== -1;
51
- },
52
-
53
- drawDifferenceCanvas: function() {
54
- var generatedImage = new Image(),
55
- referenceImage = new Image(),
56
- context = document.getElementById('difference').getContext('2d');
57
-
58
- $([referenceImage, generatedImage]).error(function() {
59
- $('#difference').attr('width', 0).attr('height', 0);
78
+ document.getElementById(mode).style.display = 'inline';
79
+ };
80
+
81
+ function attachButtonClickHandlers() {
82
+ imageTypes.forEach(function(element) {
83
+ document.getElementById(element + 'Button').onclick = function() {
84
+ show(element);
85
+ };
60
86
  });
87
+ };
88
+
89
+ function acceptImage() {
90
+ var url, xhr;
91
+
92
+ url = compaaHost + '/screenshots?filepath=' + generatedImagePath();
93
+ xhr = new XMLHttpRequest();
94
+ xhr.open('POST', url, true);
95
+ xhr.onload = moveToNextArtifact;
96
+ xhr.send();
97
+ };
98
+
99
+ function moveToNextArtifact() {
100
+ artifacts.shift();
101
+ if (currentArtifact()) {
102
+ paintThePicture();
103
+ } else {
104
+ endGame();
105
+ }
106
+ };
61
107
 
62
- $(referenceImage).load(function() {
63
- $(generatedImage).load(function() {
64
- $('#difference')
65
- .attr('width', referenceImage.width)
66
- .attr('height', referenceImage.height);
108
+ function attachAcceptClickHandler() {
109
+ document.getElementById('accept').onclick = acceptImage;
110
+ };
67
111
 
68
- if (this.supportsBlending()) {
69
- context.globalCompositeOperation = 'difference';
70
- } else {
71
- context.globalAlpha = 0.5;
72
- }
112
+ function attachRejectClickHandler() {
113
+ document.getElementById('reject').onclick = moveToNextArtifact;
114
+ };
73
115
 
74
- context.drawImage(referenceImage, 0, 0);
75
- context.drawImage(generatedImage, 0, 0);
76
- }.bind(this));
116
+ function setGeneratedImage() {
117
+ document.getElementById('generatedImage').src = generatedImagePath();
118
+ };
77
119
 
78
- generatedImage.src = this.generatedImagePath();
79
- }.bind(this));
120
+ function setReferenceImage() {
121
+ document.getElementById('referenceImage').src = referenceImagePath();
122
+ };
80
123
 
81
- referenceImage.src = this.referenceImagePath();
82
- },
124
+ function setDifferenceImage() {
125
+ document.getElementById('animation').src = differenceGifPath();
126
+ };
83
127
 
84
- acceptImage: function() {
85
- var url = this.compaaHost + '/screenshots?filepath=' + this.generatedImagePath();
128
+ function differenceGifPath() {
129
+ return referenceImagePath().replace('reference_screenshots', 'differences_in_screenshots_this_run') + '_difference.gif';
130
+ };
86
131
 
87
- $.post(url, function() {
88
- this.moveToNextArtifact();
89
- }.bind(this));
90
- },
132
+ function referenceImagePath() {
133
+ return currentArtifact();
134
+ };
91
135
 
92
- rejectImage: function() {
93
- this.moveToNextArtifact();
94
- },
136
+ function generatedImagePath() {
137
+ return referenceImagePath().replace('reference_screenshots', 'screenshots_generated_this_run');
138
+ };
95
139
 
96
- moveToNextArtifact: function() {
97
- this.artifacts.shift();
140
+ function currentArtifact() {
141
+ return artifacts[0];
142
+ };
98
143
 
99
- if (this.currentArtifact()) {
100
- this.paintThePicture();
101
- } else {
102
- this.endGame();
103
- }
104
- },
105
-
106
- attachAcceptClickHandler: function() {
107
- $('#accept').click(function(e) {
108
- e.preventDefault();
109
- this.acceptImage();
110
- }.bind(this));
111
- },
112
-
113
- attachRejectClickHandler: function() {
114
- $('#reject').click(function(e) {
115
- e.preventDefault();
116
- this.rejectImage();
117
- }.bind(this));
118
- },
119
-
120
- setGeneratedImage: function() {
121
- document.getElementById('generatedImage').src = this.generatedImagePath();
122
- },
123
-
124
- setReferenceImage: function() {
125
- document.getElementById('referenceImage').src = this.referenceImagePath();
126
- },
127
-
128
- setDifferenceImage: function() {
129
- document.getElementById('animation').src = this.differenceGifPath();
130
- },
131
-
132
- differenceGifPath: function() {
133
- return this.referenceImagePath()
134
- .replace('reference_screenshots', 'differences_in_screenshots_this_run') + '_difference.gif';
135
- },
136
-
137
- referenceImagePath: function() {
138
- return this.currentArtifact();
139
- },
140
-
141
- generatedImagePath: function() {
142
- return this.referenceImagePath()
143
- .replace('reference_screenshots', 'screenshots_generated_this_run');
144
- },
145
-
146
- currentArtifact: function() {
147
- return this.artifacts[0];
148
- },
149
-
150
- endGame: function() {
151
- $('body').replaceWith('<h1>Done!</h1>');
152
- }
153
- };
144
+ function endGame() {
145
+ document.body.innerHTML = '<h1>Done!</h1>';
146
+ };
147
+
148
+ window.Compaa = {
149
+ init: init
150
+ };
151
+ })();
@@ -0,0 +1,51 @@
1
+ (function() {
2
+ function inGroupsOf(groupSize, collection, callback) {
3
+ for (var i = 0; i < collection.length; i += groupSize) {
4
+ callback(collection.slice(i, i + groupSize));
5
+ }
6
+ };
7
+
8
+ function range(n) {
9
+ var result = []
10
+ for (var i = 0; i < n; i++) result.push(i);
11
+ return result;
12
+ };
13
+
14
+ CanvasRenderingContext2D.prototype.blendOnto = function(destinationContext, blendMode) {
15
+ var width = Math.min(this.canvas.width, destinationContext.canvas.width),
16
+ height = Math.min(this.canvas.height, destinationContext.canvas.height);
17
+
18
+ var source = this.getImageData(0, 0, width, height),
19
+ destination = destinationContext.getImageData(0, 0, width, height);
20
+
21
+ var sourceData = source.data,
22
+ destinationData = destination.data;
23
+
24
+ inGroupsOf(4, range(destinationData.length), function(pixels) {
25
+ var redPixel = pixels[0],
26
+ greenPixel = pixels[1],
27
+ bluePixel = pixels[2],
28
+ alphaPixel = pixels[3];
29
+
30
+
31
+ var sourceAlpha = sourceData[alphaPixel] / 255;
32
+ var destinationAlpha = destinationData[alphaPixel] / 255;
33
+ var destinationAlpha2 = sourceAlpha + destinationAlpha - sourceAlpha * destinationAlpha;
34
+
35
+ var sourceRedAlpha = sourceData[redPixel] / 255 * sourceAlpha;
36
+ var destinationRedAlpha = destinationData[redPixel] / 255 * destinationAlpha;
37
+ var sourceGreenAlpha = sourceData[greenPixel] / 255 * sourceAlpha;
38
+ var destinationGreenAlpha = destinationData[greenPixel] / 255 * destinationAlpha;
39
+ var sourceBlueAlpha = sourceData[bluePixel] / 255 * sourceAlpha;
40
+ var destinationBlueAlpha = destinationData[bluePixel] / 255 * destinationAlpha;
41
+ var demultiply = 255 / destinationAlpha2;
42
+
43
+ destinationData[alphaPixel] = destinationAlpha2 * 255;
44
+ destinationData[redPixel] = (sourceRedAlpha + destinationRedAlpha - 2 * Math.min(sourceRedAlpha * destinationAlpha, destinationRedAlpha * sourceAlpha)) * demultiply;
45
+ destinationData[greenPixel] = (sourceGreenAlpha + destinationGreenAlpha - 2 * Math.min(sourceGreenAlpha * destinationAlpha, destinationGreenAlpha * sourceAlpha)) * demultiply;
46
+ destinationData[bluePixel] = (sourceBlueAlpha + destinationBlueAlpha - 2 * Math.min(sourceBlueAlpha * destinationAlpha, destinationBlueAlpha * sourceAlpha)) * demultiply;
47
+ });
48
+
49
+ destinationContext.putImageData(destination, 0, 0);
50
+ };
51
+ })();