shutterbug 0.2.5 → 0.4.3
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 +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) {
|