shutterbug 0.2.5 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.travis.yml +3 -3
- data/README.md +6 -0
- data/bower.json +1 -1
- data/demo/The_Scream.jpg +0 -0
- data/demo/canvas_example.html +36 -0
- data/demo/index.html +1 -0
- data/lib/shutterbug.rb +1 -1
- data/lib/shutterbug/cache_manager.rb +2 -2
- data/lib/shutterbug/configuration.rb +4 -8
- data/lib/shutterbug/handlers.rb +5 -4
- data/lib/shutterbug/handlers/convert_handler.rb +38 -16
- data/lib/shutterbug/handlers/direct_upload_handler.rb +30 -0
- data/lib/shutterbug/handlers/file_handler.rb +30 -0
- data/lib/shutterbug/handlers/js_file_handler.rb +13 -10
- data/lib/shutterbug/handlers/shutterbug.js +123 -32
- data/lib/shutterbug/phantom_job.rb +11 -10
- data/lib/shutterbug/rackapp.rb +15 -18
- data/lib/shutterbug/rasterize.js +4 -4
- data/lib/shutterbug/storage.rb +1 -1
- data/lib/shutterbug/storage/file_storage.rb +21 -8
- data/lib/shutterbug/storage/s3_storage.rb +26 -31
- data/shutterbug.gemspec +0 -1
- data/spec/shared_examples_for_handlers.rb +13 -9
- data/spec/shared_examples_for_storage.rb +14 -10
- data/spec/shutterbug/configuration_spec.rb +1 -11
- data/spec/shutterbug/convert_handler_spec.rb +5 -4
- data/spec/shutterbug/direct_upload_handler_spec.rb +7 -0
- data/spec/shutterbug/file_handler_spec.rb +7 -0
- data/spec/shutterbug/file_storage_spec.rb +2 -1
- data/spec/shutterbug/js_file_handler_spec.rb +2 -1
- data/spec/shutterbug/rackapp_spec.rb +14 -28
- data/spec/shutterbug/s3_storage_spec.rb +1 -1
- metadata +10 -12
- data/lib/shutterbug/handlers/file_handlers.rb +0 -9
- data/lib/shutterbug/handlers/file_handlers/base.rb +0 -39
- data/lib/shutterbug/handlers/file_handlers/html_file.rb +0 -22
- data/lib/shutterbug/handlers/file_handlers/png_file.rb +0 -23
- data/spec/shared_examples_for_file_handlers.rb +0 -16
- data/spec/shutterbug/html_file_handler_spec.rb +0 -10
- data/spec/shutterbug/png_file_handler_spec.rb +0 -10
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MjY4NTQ1OTA1ZjFjZmFjZGUzNjVhMWZmNjc4ODM4N2VjZTVkMDFiYQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NzhmMTc5NGEzNzIzYjNjM2M1ZmIzM2U3NGJlMDhjYTgyYzRjNDZhNg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NmJkMjRiYjMyYjZhZjAyZmUyYTUxOTc3ZThiNjZlMWVkNWUwMjFmYTNlNTBi
|
10
|
+
MjcyM2EyZTE5MWZmNjgxMDdlM2UxZDRjOWRjYWI1NjFlMmUzNzExMWM4ZjRk
|
11
|
+
YmU5YWI5Y2YxY2MyYTkyOTE2YzdhMDAwZDBjYWE4M2RkZDliY2U=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZDFmZmM5ZjZiZjVjYjI4ODk0OTdlZmQxZmRiZmY5OTVlNjhmYTAzZjE4MzYz
|
14
|
+
ZWU1OTk4YjhkMmMwNjcxNmNhOWVhYzNjMzZkZGMyMWNhMTA2YjMzMjJiZDVh
|
15
|
+
ZWRlMzE0YzcyZmRjNTUyZGZlNzA5NTFlM2VhYzYwMzU2NmYxMmM=
|
data/.travis.yml
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
|
3
|
+
# - "1.9.2"
|
4
4
|
- "1.9.3"
|
5
5
|
- "2.0.0"
|
6
|
-
- jruby-19mode # JRuby in 1.9 mode
|
7
|
-
- rbx-19mode
|
6
|
+
# - jruby-19mode # JRuby in 1.9 mode
|
7
|
+
# - rbx-19mode
|
8
8
|
# uncomment this line if your project needs to run something other than `rake`:
|
9
9
|
# script: bundle exec rspec spec
|
data/README.md
CHANGED
@@ -179,6 +179,12 @@ And a Procfile which looks like this:
|
|
179
179
|
|
180
180
|
## Changes ##
|
181
181
|
|
182
|
+
* December 4, 2014 – v 0.4.3
|
183
|
+
* Added support of various image formats and quality settings.
|
184
|
+
|
185
|
+
* December 2, 2014 – v 0.3.0
|
186
|
+
* Improved canvas snapshot - data is uploaded directly to S3 from the browser (no PhantomJS rendering).
|
187
|
+
|
182
188
|
* November 12, 2014 – v 0.2.5
|
183
189
|
* Added setFailureCallback to shutterbug.js for gracefully handling ajax failures in some custom way.
|
184
190
|
* Updated CORS configuration in config.ru to allow [:options] requests.
|
data/bower.json
CHANGED
data/demo/The_Scream.jpg
ADDED
Binary file
|
@@ -0,0 +1,36 @@
|
|
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
|
+
</head>
|
9
|
+
|
10
|
+
<body>
|
11
|
+
<div>
|
12
|
+
<canvas id="src" width="350" height="350"></canvas>
|
13
|
+
</div>
|
14
|
+
<button class="shutterbug" data-dst="#dst">Snapshot</button>
|
15
|
+
<div id="dst"></div>
|
16
|
+
</body>
|
17
|
+
<script type="text/javascript">
|
18
|
+
$(document).ready(function(e) {
|
19
|
+
var img = new Image();
|
20
|
+
img.onload = function () {
|
21
|
+
var ctx = $("canvas")[0].getContext("2d");
|
22
|
+
ctx.fillStyle = "green";
|
23
|
+
ctx.fillRect(0, 0, 350, 350);
|
24
|
+
ctx.fillStyle = "orange";
|
25
|
+
ctx.fillRect(10, 10, 330, 330);
|
26
|
+
ctx.drawImage(img, 20, 20, 310, 310);
|
27
|
+
};
|
28
|
+
img.src = "The_Scream.jpg";
|
29
|
+
|
30
|
+
var bug = new Shutterbug("#src", "#dst", null, null, null, {format: "jpeg", quality: 0.75});
|
31
|
+
$("button.shutterbug").click(function(e) {
|
32
|
+
bug.getDomSnapshot();
|
33
|
+
});
|
34
|
+
});
|
35
|
+
</script>
|
36
|
+
</html>
|
data/demo/index.html
CHANGED
@@ -14,6 +14,7 @@
|
|
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
16
|
<li><a href="nested_iframe_example.html">Nested IFrames</a> example.</li>
|
17
|
+
<li><a href="canvas_example.html">Canvas</a> example.</li>
|
17
18
|
</ul>
|
18
19
|
</body>
|
19
20
|
</html>
|
data/lib/shutterbug.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
module Shutterbug
|
2
2
|
module CacheManager
|
3
|
-
autoload :NoCache,
|
4
|
-
autoload :CacheEntry,
|
3
|
+
autoload :NoCache, "shutterbug/cache_manager/no_cache"
|
4
|
+
autoload :CacheEntry, "shutterbug/cache_manager/cache_entry"
|
5
5
|
end
|
6
6
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'tmpdir'
|
2
|
+
|
2
3
|
module Shutterbug
|
3
4
|
class Configuration
|
4
|
-
|
5
5
|
attr_accessor :uri_prefix
|
6
6
|
attr_accessor :path_prefix
|
7
7
|
attr_accessor :resource_dir
|
@@ -27,19 +27,15 @@ module Shutterbug
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def fs_path_for(filename)
|
30
|
-
File.join(resource_dir,"phantom_#{filename}")
|
30
|
+
File.join(resource_dir, "phantom_#{filename}")
|
31
31
|
end
|
32
32
|
|
33
33
|
def url_prefix
|
34
34
|
"#{uri_prefix}#{path_prefix}"
|
35
35
|
end
|
36
36
|
|
37
|
-
def convert_path
|
38
|
-
"#{url_prefix}/make_snapshot"
|
39
|
-
end
|
40
|
-
|
41
37
|
def base_url(req)
|
42
|
-
req.POST()['base_url'] ||
|
38
|
+
req.POST()['base_url'] || req.referrer || "#{req.scheme}://#{req.host_with_port}"
|
43
39
|
end
|
44
40
|
|
45
41
|
def storage
|
@@ -50,4 +46,4 @@ module Shutterbug
|
|
50
46
|
return (self.s3_bin && self.s3_key && self.s3_secret)
|
51
47
|
end
|
52
48
|
end
|
53
|
-
end
|
49
|
+
end
|
data/lib/shutterbug/handlers.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
-
require "shutterbug/handlers/file_handlers"
|
2
1
|
module Shutterbug
|
3
2
|
module Handlers
|
4
|
-
autoload :ConvertHandler,
|
5
|
-
autoload :
|
3
|
+
autoload :ConvertHandler, "shutterbug/handlers/convert_handler"
|
4
|
+
autoload :DirectUploadHandler, "shutterbug/handlers/direct_upload_handler"
|
5
|
+
autoload :JsFileHandler, "shutterbug/handlers/js_file_handler"
|
6
|
+
autoload :FileHandler, "shutterbug/handlers/file_handler"
|
6
7
|
end
|
7
|
-
end
|
8
|
+
end
|
@@ -1,38 +1,60 @@
|
|
1
1
|
require 'stringio'
|
2
|
+
|
2
3
|
module Shutterbug
|
3
4
|
module Handlers
|
4
5
|
class ConvertHandler
|
5
6
|
|
6
|
-
def
|
7
|
-
|
7
|
+
def self.config
|
8
|
+
Configuration.instance
|
8
9
|
end
|
9
10
|
|
10
|
-
def regex
|
11
|
-
/#{
|
11
|
+
def self.regex
|
12
|
+
/#{self.config.path_prefix}\/make_snapshot/
|
12
13
|
end
|
13
14
|
|
14
15
|
def handle(helper, req, env)
|
15
16
|
response_text = convert(req).image_tag
|
16
|
-
helper.
|
17
|
+
helper.response(response_text, 'text/plain')
|
18
|
+
end
|
19
|
+
|
20
|
+
def convert_quality(val, format)
|
21
|
+
# Client sends quality between 0 and 1 (similar to .toDataURL() second argument).
|
22
|
+
# This conversion tries to ensure that the size of the final image is similar to
|
23
|
+
# .toDataURL() output with given quality settings.
|
24
|
+
val = val.to_f
|
25
|
+
case format
|
26
|
+
when "png"
|
27
|
+
val *= 10
|
28
|
+
when "jpeg"
|
29
|
+
val *= 100
|
30
|
+
else
|
31
|
+
val *= 100
|
32
|
+
end
|
33
|
+
# PhantomJS expects integer.
|
34
|
+
val.to_i
|
17
35
|
end
|
18
36
|
|
19
37
|
def convert(req)
|
20
|
-
html = req.POST()['content']
|
21
|
-
width = req.POST()['width']
|
22
|
-
height = req.POST()['height']
|
23
|
-
css = req.POST()['css']
|
24
|
-
|
25
|
-
|
38
|
+
html = req.POST()['content'] || ""
|
39
|
+
width = req.POST()['width'] || 1000
|
40
|
+
height = req.POST()['height'] || 700
|
41
|
+
css = req.POST()['css'] || ""
|
42
|
+
format = req.POST()['format'] || "png"
|
43
|
+
quality = req.POST()['quality'] || 1
|
44
|
+
quality = convert_quality(quality, format)
|
45
|
+
config = self.class.config
|
46
|
+
job = PhantomJob.new(config.base_url(req), html, css, width, height, format, quality)
|
47
|
+
unless (cache_entry = config.cache_manager.find(job.cache_key))
|
26
48
|
job.rasterize
|
27
49
|
html_entry = Shutterbug::CacheManager::CacheEntry.new(job.html_file)
|
28
|
-
|
29
|
-
html_entry.preview_url =
|
30
|
-
|
31
|
-
cache_entry =
|
50
|
+
image_entry = Shutterbug::CacheManager::CacheEntry.new(job.image_file)
|
51
|
+
html_entry.preview_url = image_entry.preview_url
|
52
|
+
config.cache_manager.add_entry(html_entry)
|
53
|
+
cache_entry = config.cache_manager.add_entry(image_entry)
|
32
54
|
end
|
33
55
|
# return the image tag
|
34
56
|
return cache_entry
|
35
57
|
end
|
36
58
|
end
|
37
59
|
end
|
38
|
-
end
|
60
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
# This handler can be used for fast, client-side upload of images (or canvas) directly to storage.
|
4
|
+
# When client is taking snapshot of image or canvas, we don't need to use PhantomJS.
|
5
|
+
module Shutterbug
|
6
|
+
module Handlers
|
7
|
+
class DirectUploadHandler
|
8
|
+
|
9
|
+
def self.regex
|
10
|
+
/#{Configuration.instance.path_prefix}\/img_upload_url/
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns put_url and get_url for a new file that should be uploaded by the client.
|
14
|
+
# Of course get_url will work after file is uploaded.
|
15
|
+
def handle(helper, req, env)
|
16
|
+
format = req.GET()['format'] || 'png'
|
17
|
+
|
18
|
+
object_name = "img-#{SecureRandom.uuid}.#{format}"
|
19
|
+
storage = Configuration.instance.storage
|
20
|
+
unless storage.respond_to? :put_url
|
21
|
+
return helper.response('direct upload not available', 'text/plain', 400)
|
22
|
+
end
|
23
|
+
helper.response({
|
24
|
+
put_url: storage.put_url(object_name),
|
25
|
+
get_url: storage.get_url(object_name),
|
26
|
+
}.to_json, 'application/json')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Shutterbug
|
2
|
+
module Handlers
|
3
|
+
class FileHandler
|
4
|
+
|
5
|
+
# relative url
|
6
|
+
def self.path_prefix
|
7
|
+
"#{Configuration.instance.path_prefix}/get_file"
|
8
|
+
end
|
9
|
+
|
10
|
+
# absolute url
|
11
|
+
def self.uri_prefix
|
12
|
+
"#{Configuration.instance.uri_prefix}#{self.path_prefix}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.regex
|
16
|
+
filename_matcher = "(([^\/|\.]+)\.?([^\/]+))?"
|
17
|
+
/#{self.path_prefix}\/#{filename_matcher}/
|
18
|
+
end
|
19
|
+
|
20
|
+
def handle(helper, req, env)
|
21
|
+
filename = self.class.regex.match(req.path)[1]
|
22
|
+
if File.extname(filename) == ''
|
23
|
+
filename += '.html'
|
24
|
+
end
|
25
|
+
file = Configuration.instance.storage.new(filename)
|
26
|
+
helper.response(file.get_content, file.mime_type)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -2,26 +2,29 @@ module Shutterbug
|
|
2
2
|
module Handlers
|
3
3
|
class JsFileHandler
|
4
4
|
|
5
|
+
def self.config
|
6
|
+
Configuration.instance
|
7
|
+
end
|
8
|
+
|
5
9
|
def self.js_path
|
6
|
-
"#{
|
10
|
+
"#{self.config.url_prefix}/shutterbug.js"
|
7
11
|
end
|
8
12
|
|
9
|
-
def regex
|
10
|
-
/#{
|
13
|
+
def self.regex
|
14
|
+
/#{self.config.path_prefix}\/shutterbug.js/
|
11
15
|
end
|
12
16
|
|
13
|
-
def
|
14
|
-
File.
|
17
|
+
def initialize
|
18
|
+
@javascript = File.read(js_file).gsub(/URL_PREFIX/, self.class.config.url_prefix)
|
15
19
|
end
|
16
20
|
|
17
|
-
def
|
18
|
-
|
19
|
-
@javascript = File.read(js_file).gsub(/CONVERT_PATH/,@config.convert_path)
|
21
|
+
def js_file
|
22
|
+
File.join(File.dirname(__FILE__), "shutterbug.js")
|
20
23
|
end
|
21
24
|
|
22
25
|
def handle(helper, req, env)
|
23
|
-
helper.
|
26
|
+
helper.response(@javascript, 'application/javascript')
|
24
27
|
end
|
25
28
|
end
|
26
29
|
end
|
27
|
-
end
|
30
|
+
end
|
@@ -1,9 +1,12 @@
|
|
1
1
|
/*global $ */
|
2
|
-
(function(){
|
3
|
-
var $ = window
|
2
|
+
(function() {
|
3
|
+
var $ = window.jQuery;
|
4
4
|
|
5
5
|
var MAX_TIMEOUT = 1500;
|
6
6
|
|
7
|
+
// IE9 doesn't implement this.
|
8
|
+
var BIN_DATA_SUPPORTED = typeof(window.Blob) === "function" && typeof(window.Uint8Array) === "function";
|
9
|
+
|
7
10
|
var getBaseUrl = function() {
|
8
11
|
var base = window.location.href;
|
9
12
|
return base;
|
@@ -50,7 +53,7 @@
|
|
50
53
|
// - element descentands are iframes - .find('iframe')
|
51
54
|
var $iframes = $element.find('iframe').addBack("iframe");
|
52
55
|
this._iframeContentRequests = [];
|
53
|
-
|
56
|
+
|
54
57
|
$iframes.each(function(i, iframeElem) {
|
55
58
|
var message = {
|
56
59
|
type: 'htmlFragRequest',
|
@@ -65,7 +68,7 @@
|
|
65
68
|
var requestDeffered = new $.Deferred();
|
66
69
|
self._iframeContentRequests[i] = requestDeffered;
|
67
70
|
setTimeout(function() {
|
68
|
-
// It handles a situation in which iframe doesn't support Shutterbug.
|
71
|
+
// It handles a situation in which iframe doesn't support Shutterbug.
|
69
72
|
// When we doesn't receive answer for some time, assume that we can't
|
70
73
|
// render this particular iframe (provide null as iframe description).
|
71
74
|
if (requestDeffered.state() !== "resolved") {
|
@@ -101,19 +104,22 @@
|
|
101
104
|
});
|
102
105
|
}
|
103
106
|
|
104
|
-
|
107
|
+
// .addBack('canvas') handles case when the element itself is a canvas.
|
108
|
+
var replacementImgs = $element.find('canvas').addBack('canvas').map(function(i, elem) {
|
109
|
+
// Use png here, as it supports transparency and canvas can be layered on top of other elements.
|
105
110
|
var dataUrl = elem.toDataURL('image/png');
|
106
|
-
var img = cloneDomItem($(elem),"<img>");
|
111
|
+
var img = cloneDomItem($(elem), "<img>");
|
107
112
|
img.attr('src', dataUrl);
|
108
113
|
return img;
|
109
114
|
});
|
110
115
|
|
111
|
-
element.
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
116
|
+
if (element.is('canvas')) {
|
117
|
+
element = replacementImgs[0];
|
118
|
+
} else {
|
119
|
+
element.find('canvas').each(function(i, elem) {
|
120
|
+
$(elem).replaceWith(replacementImgs[i]);
|
121
|
+
});
|
122
|
+
}
|
117
123
|
|
118
124
|
element.css({
|
119
125
|
'top':0,
|
@@ -150,34 +156,110 @@
|
|
150
156
|
var counter = $("<span>");
|
151
157
|
counter.html(time);
|
152
158
|
$(self.imgDst).html("creating snapshot: ").append(counter);
|
153
|
-
|
159
|
+
this.timer = setInterval(function(t) {
|
154
160
|
time = time + 1;
|
155
161
|
counter.html(time);
|
156
162
|
}, 1000);
|
163
|
+
var tagName = $(this.element).prop("tagName");
|
164
|
+
switch(tagName) {
|
165
|
+
case "CANVAS":
|
166
|
+
this.canvasSnapshot();
|
167
|
+
break;
|
168
|
+
default:
|
169
|
+
this.basicSnapshot();
|
170
|
+
break;
|
171
|
+
}
|
172
|
+
};
|
173
|
+
|
174
|
+
var canvasSnapshot = function() {
|
175
|
+
if (!BIN_DATA_SUPPORTED) {
|
176
|
+
return this.basicSnapshot();
|
177
|
+
}
|
178
|
+
var self = this;
|
179
|
+
$.ajax({
|
180
|
+
type: 'GET',
|
181
|
+
url: 'URL_PREFIX/img_upload_url?format=' + this.imageFormat
|
182
|
+
}).done(function(data) {
|
183
|
+
self.directUpload(data);
|
184
|
+
}).fail(function() {
|
185
|
+
// Use basic snapshot as a fallback.
|
186
|
+
// Direct upload is not supported on server side (e.g. due to used storage).
|
187
|
+
self.basicSnapshot();
|
188
|
+
});
|
189
|
+
};
|
190
|
+
|
191
|
+
function directUpload(options) {
|
192
|
+
var $canvas = $(this.element);
|
193
|
+
var dataURL = $canvas[0].toDataURL('image/' + this.imageFormat, this.imageQuality)
|
194
|
+
var blob = dataURLtoBlob(dataURL);
|
195
|
+
var self = this;
|
196
|
+
$.ajax({
|
197
|
+
type: 'PUT',
|
198
|
+
url: options.put_url,
|
199
|
+
data: blob,
|
200
|
+
processData: false,
|
201
|
+
contentType: false
|
202
|
+
}).done(function(data) {
|
203
|
+
self.success('<img src=' + options.get_url + '>');
|
204
|
+
}).fail(function(jqXHR, textStatus, errorThrown) {
|
205
|
+
self.fail(jqXHR, textStatus, errorThrown)
|
206
|
+
});
|
207
|
+
}
|
208
|
+
|
209
|
+
function dataURLtoBlob(dataURL) {
|
210
|
+
// Convert base64/URLEncoded data component to raw binary data held in a string.
|
211
|
+
if (dataURL.split(',')[0].indexOf('base64') === -1) {
|
212
|
+
throw new Error('expected base64 data');
|
213
|
+
}
|
214
|
+
var byteString = atob(dataURL.split(',')[1]);
|
215
|
+
// Separate out the mime component.
|
216
|
+
var mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0];
|
217
|
+
// Write the bytes of the string to a typed array.
|
218
|
+
var ia = new Uint8Array(byteString.length);
|
219
|
+
for (var i = 0; i < byteString.length; i++) {
|
220
|
+
ia[i] = byteString.charCodeAt(i);
|
221
|
+
}
|
222
|
+
return new Blob([ia], {type: mimeString});
|
223
|
+
}
|
224
|
+
|
225
|
+
var basicSnapshot = function() {
|
226
|
+
var self = this;
|
157
227
|
// Ask for HTML fragment and render it on server.
|
158
|
-
this.getHtmlFragment(function(
|
228
|
+
this.getHtmlFragment(function(html_data) {
|
229
|
+
html_data.format = self.imageFormat;
|
230
|
+
html_data.quality = self.imageQuality;
|
159
231
|
$.ajax({
|
160
|
-
url: "
|
232
|
+
url: "URL_PREFIX/make_snapshot",
|
161
233
|
type: "POST",
|
162
|
-
data:
|
234
|
+
data: html_data
|
163
235
|
}).success(function(msg) {
|
164
|
-
|
165
|
-
$(self.imgDst).html(msg);
|
166
|
-
}
|
167
|
-
if (self.callback) {
|
168
|
-
self.callback(msg);
|
169
|
-
}
|
170
|
-
clearInterval(timer);
|
236
|
+
self.success(msg)
|
171
237
|
}).fail(function(jqXHR, textStatus, errorThrown) {
|
172
|
-
|
173
|
-
if (self.failCallback) {
|
174
|
-
self.failCallback(jqXHR, textStatus, errorThrown);
|
175
|
-
}
|
176
|
-
clearInterval(timer);
|
238
|
+
self.fail(jqXHR, textStatus, errorThrown);
|
177
239
|
});
|
178
240
|
});
|
179
241
|
};
|
180
242
|
|
243
|
+
var success = function(imageTag) {
|
244
|
+
if (this.imgDst) {
|
245
|
+
$(this.imgDst).html(imageTag);
|
246
|
+
}
|
247
|
+
if (this.callback) {
|
248
|
+
this.callback(imageTag);
|
249
|
+
}
|
250
|
+
clearInterval(this.timer);
|
251
|
+
}
|
252
|
+
|
253
|
+
var fail = function(jqXHR, textStatus, errorThrown) {
|
254
|
+
if (this.imgDst) {
|
255
|
+
$(this.imgDst).html("snapshot failed");
|
256
|
+
}
|
257
|
+
if (this.failCallback) {
|
258
|
+
this.failCallback(jqXHR, textStatus, errorThrown);
|
259
|
+
}
|
260
|
+
clearInterval(this.timer);
|
261
|
+
}
|
262
|
+
|
181
263
|
var requestHtmlFrag = function() {
|
182
264
|
var destination = $(this.element)[0].contentWindow;
|
183
265
|
var message = {
|
@@ -188,7 +270,7 @@
|
|
188
270
|
};
|
189
271
|
|
190
272
|
var htmlSnap = function() {
|
191
|
-
this.getHtmlFragment(function callback(fragment){
|
273
|
+
this.getHtmlFragment(function callback(fragment) {
|
192
274
|
// FIXME btoa is not intended to encode text it is for for 8bit per char strings
|
193
275
|
// so if you send it a UTF8 string with a special char in it, it will fail
|
194
276
|
// this SO has a note about handling this:
|
@@ -219,7 +301,7 @@
|
|
219
301
|
};
|
220
302
|
|
221
303
|
// TODO: Construct using opts instead of positional arguments.
|
222
|
-
window.Shutterbug = function(selector, imgDst, callback, id, jQuery) {
|
304
|
+
window.Shutterbug = function(selector, imgDst, callback, id, jQuery, opt) {
|
223
305
|
if (typeof(jQuery) != "undefined" && jQuery != null) {
|
224
306
|
$ = jQuery;
|
225
307
|
}
|
@@ -229,12 +311,21 @@
|
|
229
311
|
$ = window.$;
|
230
312
|
}
|
231
313
|
|
314
|
+
opt = opt || {};
|
315
|
+
|
232
316
|
var shutterbugInstance = {
|
233
317
|
element: selector,
|
234
318
|
imgDst: imgDst,
|
235
319
|
callback: callback,
|
236
320
|
id: id,
|
321
|
+
imageFormat: opt.format || "png",
|
322
|
+
imageQuality: opt.quality || 1,
|
237
323
|
getDomSnapshot: getDomSnapshot,
|
324
|
+
basicSnapshot: basicSnapshot,
|
325
|
+
canvasSnapshot: canvasSnapshot,
|
326
|
+
directUpload: directUpload,
|
327
|
+
success: success,
|
328
|
+
fail: fail,
|
238
329
|
getHtmlFragment: getHtmlFragment,
|
239
330
|
requestHtmlFrag: requestHtmlFrag,
|
240
331
|
htmlSnap: htmlSnap,
|
@@ -260,8 +351,8 @@
|
|
260
351
|
|
261
352
|
var htmlFragRequestListen = function(message) {
|
262
353
|
var send_response = function(data) {
|
263
|
-
// Update timeout. When we receive a request from parent, we have to finish nested iframes
|
264
|
-
// rendering in that time. Otherwise parent rendering will timeout.
|
354
|
+
// Update timeout. When we receive a request from parent, we have to finish nested iframes
|
355
|
+
// rendering in that time. Otherwise parent rendering will timeout.
|
265
356
|
// Backward compatibility: Shutterbug v0.1.x don't send iframeReqTimeout.
|
266
357
|
shutterbugInstance.iframeReqTimeout = data.iframeReqTimeout != null ? data.iframeReqTimeout : MAX_TIMEOUT;
|
267
358
|
shutterbugInstance.getHtmlFragment(function(html) {
|