shutterbug 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -106,11 +106,15 @@ some useful things like finding and including all the css on the page, and 'seri
106
106
 
107
107
  ### Shutterbug JQuery custom events ###
108
108
 
109
- Shutterbug emits a jQuery custom event called 'shutterbug-saycheese' just prior to copying styles, elements, and canvas contents to the document fragment. This allows applications to do any preparation required before they are ready to be snapshotted.
109
+ Shutterbug emits a jQuery custom event called `shutterbug-saycheese` just prior to copying styles, elements, and canvas contents to the document fragment. This allows applications to do any preparation required before they are ready to be snapshotted.
110
110
 
111
- After all elements are copied, emits a 'shutterbug-asyouwere' event, so that any customized preperations can be torn down again.
111
+ In your application, you can register your event-handler like this:
112
112
 
113
- After all elements are copied, emits a 'shutterbug-asyouwere' event.
113
+ $(window).on('shutterbug-saycheese', function() {
114
+ api.renderCanvas();
115
+ });
116
+
117
+ After all elements are copied, emits a `shutterbug-asyouwere` event.
114
118
 
115
119
  ### Deploying on Heroku ###
116
120
 
data/bower.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "shutterbug",
3
+ "version": "0.1.3",
4
+ "homepage": "https://github.com/concord-consortium/shutterbug",
5
+ "authors": [
6
+ "Noah Paessel <npaessel@concord.org>"
7
+ ],
8
+ "description": "Javascript library for facilitating rendering DOM components in rasterized form.",
9
+ "main": "./lib/shutterbug/handlers/shutterbug.js",
10
+ "dependencies": {
11
+ "jquery": "1.11.0"
12
+ },
13
+ "keywords": [
14
+ "shutterbug"
15
+ ],
16
+ "license": "MIT",
17
+ "private": true,
18
+ "ignore": [
19
+ "**/.*",
20
+ "node_modules",
21
+ "bower_components",
22
+ "spec",
23
+ "demo",
24
+ "images"
25
+ ]
26
+ }
data/demo/iframe3.html ADDED
@@ -0,0 +1,29 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>demo</title>
5
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
6
+ <script type="text/javascript" src="/shutterbug/shutterbug.js"></script>
7
+ <link rel="stylesheet" type="text/css" href="main.css"></link>
8
+ <style type="text/css">
9
+ #src {
10
+ color: red;
11
+ width: 350px;
12
+ height: 250px;
13
+ }
14
+ </style>
15
+ </head>
16
+ <body>
17
+ <div id="src">
18
+ <p>(iframe that contains other iframes)</p>
19
+ <iframe src="iframe.html" width="140" height="140"></iframe>
20
+ <iframe src="iframe2.html" width="140" height="140"></iframe>
21
+ </div>
22
+ </div>
23
+ </body>
24
+ <script type="text/javascript">
25
+ $(document).ready(function(e) {
26
+ new Shutterbug('#src');
27
+ });
28
+ </script>
29
+ </html>
data/demo/iframe4.html ADDED
@@ -0,0 +1,29 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>demo</title>
5
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
6
+ <script type="text/javascript" src="/shutterbug/shutterbug.js"></script>
7
+ <link rel="stylesheet" type="text/css" href="main.css"></link>
8
+ <style type="text/css">
9
+ #src {
10
+ color: navy;
11
+ width: 350px;
12
+ height: 250px;
13
+ }
14
+ </style>
15
+ </head>
16
+ <body>
17
+ <div id="src">
18
+ <p>(iframe that contains other iframes, but not all of them support Shutterbug)</p>
19
+ <iframe src="iframe.html" width="140" height="140"></iframe>
20
+ <iframe src="iframe_no_shutterbug.html" width="140" height="140"></iframe>
21
+ </div>
22
+ </div>
23
+ </body>
24
+ <script type="text/javascript">
25
+ $(document).ready(function(e) {
26
+ new Shutterbug('#src');
27
+ });
28
+ </script>
29
+ </html>
@@ -0,0 +1,20 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>demo</title>
5
+ <link rel="stylesheet" type="text/css" href="main.css"></link>
6
+ <style type="text/css">
7
+ #src {
8
+ color: red;
9
+ width: 100px;
10
+ height: 100px;
11
+ }
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <div id="src">
16
+ (iframe without Shutterbug support)
17
+ </div>
18
+ </div>
19
+ </body>
20
+ </html>
data/demo/index.html CHANGED
@@ -13,6 +13,7 @@
13
13
  <li>Sourced vs. inline <a href="svg_example.html">SVG</a>.</li>
14
14
  <li>Very <a href="simple_example.html">simple</a> example.</li>
15
15
  <li><a href="iframe_example.html">IFrame</a> example.</li>
16
+ <li><a href="nested_iframe_example.html">Nested IFrames</a> example.</li>
16
17
  </ul>
17
18
  </body>
18
19
  </html>
@@ -0,0 +1,41 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>demo</title>
5
+ <script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
6
+ <script type="text/javascript" src="/shutterbug/shutterbug.js"></script>
7
+ <link rel="stylesheet" type="text/css" href="main.css">
8
+ <style type="text/css">
9
+ #dst, #dst2 {
10
+ padding: 0;
11
+ }
12
+ </style>
13
+ </head>
14
+
15
+ <body>
16
+ <div id="src1">
17
+ <iframe src="iframe3.html" width="400" height="300"></iframe>
18
+ </div>
19
+ <button class="shutterbug" data-dst="#dst">Snapshot</button>
20
+ <div id="dst"></div>
21
+
22
+ <div id="src2">
23
+ <iframe src="iframe4.html" width="400" height="300"></iframe>
24
+ </div>
25
+ <button class="shutterbug2" data-dst="#dst">Snapshot</button>
26
+ <div id="dst2"></div>
27
+
28
+ </body>
29
+ <script type="text/javascript">
30
+ $(document).ready(function(e) {
31
+ var bug = new Shutterbug("#src1", "#dst");
32
+ var bug2 = new Shutterbug("#src2", "#dst2", null, "plugh");
33
+ $("button.shutterbug").click(function(e) {
34
+ bug.getDomSnapshot();
35
+ });
36
+ $("button.shutterbug2").click(function(e) {
37
+ bug2.getDomSnapshot();
38
+ });
39
+ });
40
+ </script>
41
+ </html>
@@ -2,12 +2,14 @@
2
2
  (function(){
3
3
  var $ = window.$;
4
4
 
5
+ var MAX_TIMEOUT = 1500;
6
+
5
7
  var getBaseUrl = function() {
6
8
  var base = window.location.href;
7
9
  return base;
8
10
  };
9
11
 
10
- var cloneDomItem =function(elem, elemTag) {
12
+ var cloneDomItem = function(elem, elemTag) {
11
13
  var width = elem.width();
12
14
  var height = elem.height();
13
15
  var returnElm = $(elemTag);
@@ -20,89 +22,142 @@
20
22
  return returnElm;
21
23
  };
22
24
 
23
- var getHtmlFragment = function() {
24
- var $element = $(this.element);
25
-
26
- $element.trigger('shutterbug-saycheese');
27
-
28
- var css = $('<div>').append($('link[rel="stylesheet"]').clone()).append($('style').clone()).html();
29
- var width = $element.width();
30
- var height = $element.height();
31
- var element = null;
32
- var html_content;
33
-
34
- var replacementImgs = $element.find('canvas').map( function(count,elem) {
35
- var dataUrl = elem.toDataURL('image/png');
36
- var img = cloneDomItem($(elem),"<img>");
37
- img.attr('src', dataUrl);
38
- return img;
39
- });
25
+ var generateIFrameSrcData = function (iframeDesc) {
26
+ return "data:text/html," +
27
+ "<!DOCTYPE html>" +
28
+ "<html>" +
29
+ "<head>" +
30
+ "<base href='" + iframeDesc.base_url + "'>" +
31
+ "<meta content='text/html;charset=utf-8' http-equiv='Content-Type'>" +
32
+ "<title>content from " + iframeDesc.base_url + "</title>" +
33
+ iframeDesc.css +
34
+ "</head>" +
35
+ "<body>" +
36
+ iframeDesc.content +
37
+ "</body>" +
38
+ "</html>";
39
+ };
40
40
 
41
- element = $element.clone();
42
- element.find('canvas').each(function(i,elm) {
43
- var backgroundDiv = cloneDomItem($(elm),"<div>");
44
- // Add a backing (background) dom element for BG canvas property
45
- $(elm).replaceWith(replacementImgs[i]);
46
- backgroundDiv.insertBefore($(elm));
41
+ var getHtmlFragment = function(callback) {
42
+ var self = this;
43
+ var $element = $(this.element);
44
+ // .find('iframe').addBack("iframe") handles two cases:
45
+ // - element itself is an iframe - .addBack('iframe')
46
+ // - element descentands are iframes - .find('iframe')
47
+ var $iframes = $element.find('iframe').addBack("iframe");
48
+ this._iframeContentRequests = [];
49
+
50
+ $iframes.each(function(i, iframeElem) {
51
+ var message = {
52
+ type: 'htmlFragRequest',
53
+ id: self.id,
54
+ iframeReqId: i,
55
+ // We have to provide smaller timeout while sending message to nested iframes.
56
+ // Otherwise, when one of the nested iframes timeouts, then all will do the
57
+ // same and we won't render anything - even iframes that support Shutterbug.
58
+ iframeReqTimeout: self.iframeReqTimeout * 0.6
59
+ };
60
+ iframeElem.contentWindow.postMessage(JSON.stringify(message), "*");
61
+ var requestDeffered = new $.Deferred();
62
+ self._iframeContentRequests[i] = requestDeffered;
63
+ setTimeout(function() {
64
+ // It handles a situation in which iframe doesn't support Shutterbug.
65
+ // When we doesn't receive answer for some time, assume that we can't
66
+ // render this particular iframe (provide null as iframe description).
67
+ if (requestDeffered.state() !== "resolved") {
68
+ requestDeffered.resolve(null);
69
+ }
70
+ }, self.iframeReqTimeout);
47
71
  });
48
72
 
49
- element.css({
50
- 'top':0,
51
- 'left':0,
52
- 'margin':0,
53
- 'width':width,
54
- 'height':height
55
- });
73
+ $.when.apply($, this._iframeContentRequests).done(function() {
74
+ // This function is called when we receive responses from all nested iframes.
75
+ // Nested iframes descriptions will be provided as arguments.
76
+ $element.trigger('shutterbug-saycheese');
77
+
78
+ var css = $('<div>').append($('link[rel="stylesheet"]').clone()).append($('style').clone()).html();
79
+ var width = $element.width();
80
+ var height = $element.height();
81
+ var element = $element.clone();
82
+
83
+ if (arguments.length > 0) {
84
+ var nestedIFrames = arguments;
85
+ // This supports two cases:
86
+ // - element itself is an iframe - .addBack('iframe')
87
+ // - element descentands are iframes - .find('iframe')
88
+ element.find("iframe").addBack("iframe").each(function(i, iframeElem) {
89
+ // When iframe doesn't support Shutterbug, request will timeout and null will be received.
90
+ // In such case just ignore this iframe, we won't be able to render it.
91
+ if (nestedIFrames[i] == null) return;
92
+ $(iframeElem).attr("src", generateIFrameSrcData(nestedIFrames[i]));
93
+ });
94
+ }
56
95
 
57
- html_content = {
58
- content: $('<div>').append(element).html(),
59
- css: css,
60
- width: width,
61
- height: height,
62
- base_url: getBaseUrl()
63
- };
96
+ var replacementImgs = $element.find('canvas').map( function(count,elem) {
97
+ var dataUrl = elem.toDataURL('image/png');
98
+ var img = cloneDomItem($(elem),"<img>");
99
+ img.attr('src', dataUrl);
100
+ return img;
101
+ });
102
+
103
+ element.find('canvas').each(function(i,elm) {
104
+ var backgroundDiv = cloneDomItem($(elm),"<div>");
105
+ // Add a backing (background) dom element for BG canvas property
106
+ $(elm).replaceWith(replacementImgs[i]);
107
+ backgroundDiv.insertBefore($(elm));
108
+ });
109
+
110
+ element.css({
111
+ 'top':0,
112
+ 'left':0,
113
+ 'margin':0,
114
+ 'width':width,
115
+ 'height':height
116
+ });
117
+
118
+ var html_content = {
119
+ content: $('<div>').append(element).html(),
120
+ css: css,
121
+ width: width,
122
+ height: height,
123
+ base_url: getBaseUrl()
124
+ };
64
125
 
65
- $element.trigger('shutterbug-asyouwere');
126
+ $element.trigger('shutterbug-asyouwere');
66
127
 
67
- return html_content;
128
+ callback(html_content);
129
+ });
68
130
  };
69
131
 
70
- var getPng = function(html) {
71
- if(typeof html === 'undefined') {
72
- if($(this.element)[0] && $(this.element)[0].contentWindow) {
73
- this.requestHtmlFrag();
74
- return;
75
- }
76
- else {
77
- html = this.getHtmlFragment();
78
- }
79
- }
132
+ var getDomSnapshot = function() {
133
+ // Start timer.
80
134
  var self = this;
81
135
  var time = 0;
82
136
  var counter = $("<span>");
83
137
  counter.html(time);
84
-
85
138
  $(self.imgDst).html("creating snapshot: ").append(counter);
86
139
  var timer = setInterval(function(t) {
87
140
  time = time + 1;
88
141
  counter.html(time);
89
142
  }, 1000);
90
-
91
- $.ajax({
92
- url: "CONVERT_PATH",
93
- type: "POST",
94
- data: html
95
- }).success(function(msg) {
96
- if(self.imgDst) {
97
- $(self.imgDst).html(msg);
98
- }
99
- if (self.callback) {
100
- self.callback(msg);
101
- }
102
- clearInterval(timer);
103
- }).fail(function(e) {
104
- $(self.imgDst).html("snapshot failed");
105
- clearInterval(timer);
143
+ // Ask for HTML fragment and render it on server.
144
+ this.getHtmlFragment(function(html) {
145
+ $.ajax({
146
+ url: "CONVERT_PATH",
147
+ type: "POST",
148
+ data: html
149
+ }).success(function(msg) {
150
+ if(self.imgDst) {
151
+ $(self.imgDst).html(msg);
152
+ }
153
+ if (self.callback) {
154
+ self.callback(msg);
155
+ }
156
+ clearInterval(timer);
157
+ }).fail(function(e) {
158
+ $(self.imgDst).html("snapshot failed");
159
+ clearInterval(timer);
160
+ });
106
161
  });
107
162
  };
108
163
 
@@ -112,10 +167,10 @@
112
167
  type: 'htmlFragRequest',
113
168
  id: this.id
114
169
  };
115
- destination.postMessage(JSON.stringify(message),"*");
170
+ destination.postMessage(JSON.stringify(message), "*");
116
171
  };
117
172
 
118
- window.Shutterbug = function(selector,imgDst,callback,id,jQuery) {
173
+ window.Shutterbug = function(selector, imgDst, callback, id, jQuery) {
119
174
  if (typeof(jQuery) != "undefined" && jQuery != null) {
120
175
  $ = jQuery;
121
176
  }
@@ -130,10 +185,10 @@
130
185
  imgDst: imgDst,
131
186
  callback: callback,
132
187
  id: id,
133
- getDomSnapshot: getPng,
134
- getPng: getPng,
188
+ getDomSnapshot: getDomSnapshot,
135
189
  getHtmlFragment: getHtmlFragment,
136
- requestHtmlFrag: requestHtmlFrag
190
+ requestHtmlFrag: requestHtmlFrag,
191
+ iframeReqTimeout: MAX_TIMEOUT
137
192
  };
138
193
 
139
194
  var handleMessage = function(message, signature, func) {
@@ -152,22 +207,27 @@
152
207
 
153
208
  var htmlFragRequestListen = function(message) {
154
209
  var send_response = function(data) {
155
- var response = {
156
- type: 'htmlFragResponse',
157
- value: shutterbugInstance.getHtmlFragment(),
158
- id: data.id // return to sender only...
159
- };
160
- message.source.postMessage(JSON.stringify(response),"*");
210
+ // Update timeout. When we receive a request from parent,
211
+ // we have to finish nested iframes rendering in that time.
212
+ // Otherwise parent rendering will timeout.
213
+ shutterbugInstance.iframeReqTimeout = data.iframeReqTimeout;
214
+ shutterbugInstance.getHtmlFragment(function(html) {
215
+ var response = {
216
+ type: 'htmlFragResponse',
217
+ value: html,
218
+ iframeReqId: data.iframeReqId,
219
+ id: data.id // return to sender only...
220
+ };
221
+ message.source.postMessage(JSON.stringify(response), "*");
222
+ });
161
223
  };
162
224
  handleMessage(message, 'htmlFragRequest', send_response);
163
225
  };
164
226
 
165
227
  var htmlFragResponseListen = function(message) {
166
228
  var send_response = function(data) {
167
- var html = null;
168
- if(data.id === shutterbugInstance.id) {
169
- html = data.value;
170
- shutterbugInstance.getPng(data.value);
229
+ if (data.id === shutterbugInstance.id) {
230
+ shutterbugInstance._iframeContentRequests[data.iframeReqId].resolve(data.value);
171
231
  }
172
232
  };
173
233
  handleMessage(message, 'htmlFragResponse', send_response);
@@ -179,4 +239,4 @@
179
239
  });
180
240
  return shutterbugInstance;
181
241
  };
182
- })();
242
+ })();
data/lib/shutterbug.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Shutterbug
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  autoload :Rackapp, "shutterbug/rackapp"
4
4
  autoload :Configuration, "shutterbug/configuration"
5
5
  autoload :Storage, "shutterbug/storage"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shutterbug
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-11-13 00:00:00.000000000 Z
12
+ date: 2014-02-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -175,12 +175,17 @@ files:
175
175
  - LICENSE.md
176
176
  - README.md
177
177
  - Rakefile
178
+ - bower.json
178
179
  - config.ru
179
180
  - demo/iframe.html
180
181
  - demo/iframe2.html
182
+ - demo/iframe3.html
183
+ - demo/iframe4.html
181
184
  - demo/iframe_example.html
185
+ - demo/iframe_no_shutterbug.html
182
186
  - demo/index.html
183
187
  - demo/main.css
188
+ - demo/nested_iframe_example.html
184
189
  - demo/oil-and-water/heatbath.svg
185
190
  - demo/oil-and-water/ke-gradient.svg
186
191
  - demo/oil-and-water/oil-and-water.svg
@@ -234,7 +239,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
234
239
  version: '0'
235
240
  segments:
236
241
  - 0
237
- hash: 2889769158720153016
242
+ hash: -737520193773818108
238
243
  required_rubygems_version: !ruby/object:Gem::Requirement
239
244
  none: false
240
245
  requirements:
@@ -243,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
243
248
  version: '0'
244
249
  segments:
245
250
  - 0
246
- hash: 2889769158720153016
251
+ hash: -737520193773818108
247
252
  requirements: []
248
253
  rubyforge_project:
249
254
  rubygems_version: 1.8.25