compaa 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ })();