jpeg_camera 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 64570f77a25c5034e335b4e59440007d56ceb361
4
+ data.tar.gz: 793cce21b60946b0f6b9e7d56d180ef900de62f1
5
+ SHA512:
6
+ metadata.gz: 6b0d9f6c6d15429d8c0dae0faf16d9850912f658cd26ffe6dfd54a4f927afc8821aaf0f33e6f628fee0851c7f93ef0f3e7da4ec33413efc7f371d5637bda69ff
7
+ data.tar.gz: 955210d1bd55c3697eb8801e2e2b131ba068e8acb7b732e380117c312ddc31006a8013ac0bad7cba1a3feaabd912d3d0be0e2a6c6bbe5d0bbbc7588f63db73a5
data/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Adam Wróbel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,146 @@
1
+ ## About
2
+
3
+ JpegCamera is a JavaScript library that allows you display a camera stream on
4
+ a web page and then capture, show and upload JPEG snapshots to the server. It
5
+ uses HTML5 in Chrome, Firefox and Opera and falls back to Flash in less capable
6
+ browsers. The video stream is placed without any UI in a container of your
7
+ choice and you control it through JavaScript API and your own UI elements.
8
+
9
+ The idea is based on a similar
10
+ [JpegCam](https://github.com/mattclements/jpegcam) library which was Flash only.
11
+ Beside working without Flash and offering a cleaner, more modern API, JpegCamera
12
+ has some nice, new features.
13
+
14
+ ## Features
15
+
16
+ - Works natively in Chrome, Firefox, Opera and with a Flash plugin in all other
17
+ browsers.
18
+ - Manage and upload multiple snapshots at once. You don't have to wait for the
19
+ first upload to finish before capturing the next image. This means you can
20
+ take a few shots in a short interval.
21
+ - Allows you to retry failed uploads.
22
+ - Easily read server response text and code after upload.
23
+ - Send CSRF tokens to secure your user's session from [Cross-site request
24
+ forgery](http://en.wikipedia.org/wiki/Cross-site_request_forgery#Prevention)
25
+
26
+ ## Dependencies
27
+
28
+ - [Canvas-to-Blob](https://github.com/blueimp/JavaScript-Canvas-to-Blob)
29
+ polyfill for the standard JavaScript `canvas.toBlob` method.
30
+ - [SWFObject](http://code.google.com/p/swfobject/) for embedding the
31
+ Flash-based fallback.
32
+
33
+ For convenience these scripts are packaged with JpegCamera.
34
+
35
+ ## Installation
36
+
37
+ You can load JpegCamera directly on any web page, but if you're writing Rails
38
+ 3.1 application consider using a gem. In either case you have an option
39
+ of loading full library that includes HTML5 implementation with Flash fallback
40
+ or HTML5 version only.
41
+
42
+ ### Standalone app
43
+
44
+ Copy all the files from `dist` into `jpeg_camera` directory under your server's
45
+ root.
46
+
47
+ Load JpegCamera and it's dependencies in the `HEAD` section of your page.
48
+
49
+ <script src="/jpeg_camera/swfobject.min.js" type="text/javascript"></script>
50
+ <script src="/jpeg_camera/canvas-to-blob.min.js" type="text/javascript"></script>
51
+ <script src="/jpeg_camera/jpeg_camera.min.js" type="text/javascript"></script>
52
+
53
+ SWFObject and Canvas-to-Blob are stored in separate files so that you don't have
54
+ to load them again if you already use them in your project. If you want to cut
55
+ down on HTTP requests then there is a concatenated version you can use.
56
+
57
+ <script src="/jpeg_camera/jpeg_camera_with_dependencies.min.js" type="text/javascript"></script>
58
+
59
+ If you want to use HTML5-only version you can load jpeg_camera_no_flash.min.js.
60
+ There is no "with dependencies" version of this file, so you have to remember
61
+ to also load Canvas-to-Blob. You don't need SWFObject for HTML5.
62
+
63
+ ### Ruby on Rails 3.1 applications
64
+
65
+ Require the gem in your Gemfile.
66
+
67
+ gem "jpeg_camera", "~> 1.0.0"
68
+
69
+ Add appropriate requires to your application.js. SWFObject and Canvas-to-Blob
70
+ are stored in separate files so that you don't have to load them again if you
71
+ already use them in your project. The assets pipeline will take care of
72
+ minifying and concatenating everything into one script.
73
+
74
+ //= require jpeg_camera/swfobject
75
+ //= require jpeg_camera/canvas-to-blob
76
+ //= require jpeg_camera/jpeg_camera
77
+
78
+ If you want to use HTML5-only version then use this instead:
79
+
80
+ //= require jpeg_camera/canvas-to-blob
81
+ //= require jpeg_camera/jpeg_camera_no_flash
82
+
83
+ Minified versions of all the scripts are also included in the gem if you don't
84
+ want to include JpegCamera in your application.js file and would rather use
85
+ `javascript_include_tag` on select pages of your application.
86
+
87
+ ## Usage
88
+
89
+ var camera = new JpegCamera("#camera");
90
+
91
+ var snapshot = camera.capture();
92
+
93
+ snapshot.show(); // Display the snapshot
94
+
95
+ snapshot.upload({api_url: "/upload_image"}).done(function(response) {
96
+ response_container.innerHTML = response;
97
+ this.discard(); // discard snapshot and show video stream again
98
+ }).fail(function() {
99
+ alert("Upload failed.");
100
+ });
101
+
102
+ A detailed documentation using in-code comments is maintained for
103
+ [JpegCamera](http://amw.github.io/jpeg_camera/doc/classes/JpegCamera.html) and
104
+ [Snapshot](http://amw.github.io/jpeg_camera/doc/classes/Snapshot.html)
105
+ classes.
106
+
107
+ ## Caveats
108
+
109
+ To use Flash fallback your camera container must be at least 215 pixels wide and
110
+ 138 pixels tall. This is the minimum to display privacy settings dialog.
111
+
112
+ With Flash in some browsers it's impossible to read response body for requests
113
+ that finish with status codes from outside the 2XX range (like 404 Not Found or
114
+ 422 Unprocessable Entity). If you're using version of JpegCamera with Flash
115
+ fallback your application should not rely on reading body of these responses.
116
+ The status code number is always available.
117
+
118
+ ## Contributing
119
+
120
+ The source code is available on [Github](https://github.com/amw/jpeg_camera).
121
+ Please send pull requests on topic branches.
122
+
123
+ To build dist files from source you need `npm` — Node Package Manager.
124
+
125
+ npm install # install required dependencies
126
+ npm install -g grunt-cli # install grunt command
127
+ grunt dist # build js & swf files
128
+ grunt js # only builds js files
129
+ grunt swf # only builds swf file
130
+ grunt doc # update documentation
131
+ grunt # build dist files and update documentation
132
+
133
+ To build swf file you need to have `mxmlc` available in your `$PATH`. It comes
134
+ in the [Flex SDK](http://www.adobe.com/devnet/flex/flex-sdk-download.html).
135
+
136
+ ## Acknowledgements
137
+
138
+ Thanks to Joseph Huckaby for creating and Matt Clements for maintaining
139
+ Flash-based [JpegCam library](http://code.google.com/p/jpegcam/) which I have
140
+ been using until HTML5 became a viable solution. If you're interested here's
141
+ [Matt's repo](https://github.com/mattclements/jpegcam) and here's
142
+ [mine](https://github.com/amw/jpegcam). Thanks to everyone else contributing to
143
+ that project.
144
+
145
+
146
+ Copyright [Adam Wróbel](http://adamwrobel.com), released under the MIT License.
@@ -0,0 +1,6 @@
1
+ require 'jpeg_camera/version'
2
+
3
+ module JpegCamera
4
+ class Engine < ::Rails::Engine
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ require "json"
2
+
3
+ module JpegCamera
4
+ package_file = File.expand_path '../../../package.json', __FILE__
5
+ package_info = JSON.parse File.read(package_file)
6
+ VERSION = package_info["version"]
7
+ HOMEPAGE = package_info["homepage"]
8
+ end
@@ -0,0 +1,95 @@
1
+ /*
2
+ * JavaScript Canvas to Blob 2.0.5
3
+ * https://github.com/blueimp/JavaScript-Canvas-to-Blob
4
+ *
5
+ * Copyright 2012, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Licensed under the MIT license:
9
+ * http://www.opensource.org/licenses/MIT
10
+ *
11
+ * Based on stackoverflow user Stoive's code snippet:
12
+ * http://stackoverflow.com/q/4998908
13
+ */
14
+
15
+ /*jslint nomen: true, regexp: true */
16
+ /*global window, atob, Blob, ArrayBuffer, Uint8Array, define */
17
+
18
+ (function (window) {
19
+ 'use strict';
20
+ var CanvasPrototype = window.HTMLCanvasElement &&
21
+ window.HTMLCanvasElement.prototype,
22
+ hasBlobConstructor = window.Blob && (function () {
23
+ try {
24
+ return Boolean(new Blob());
25
+ } catch (e) {
26
+ return false;
27
+ }
28
+ }()),
29
+ hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array &&
30
+ (function () {
31
+ try {
32
+ return new Blob([new Uint8Array(100)]).size === 100;
33
+ } catch (e) {
34
+ return false;
35
+ }
36
+ }()),
37
+ BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
38
+ window.MozBlobBuilder || window.MSBlobBuilder,
39
+ dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob &&
40
+ window.ArrayBuffer && window.Uint8Array && function (dataURI) {
41
+ var byteString,
42
+ arrayBuffer,
43
+ intArray,
44
+ i,
45
+ mimeString,
46
+ bb;
47
+ if (dataURI.split(',')[0].indexOf('base64') >= 0) {
48
+ // Convert base64 to raw binary data held in a string:
49
+ byteString = atob(dataURI.split(',')[1]);
50
+ } else {
51
+ // Convert base64/URLEncoded data component to raw binary data:
52
+ byteString = decodeURIComponent(dataURI.split(',')[1]);
53
+ }
54
+ // Write the bytes of the string to an ArrayBuffer:
55
+ arrayBuffer = new ArrayBuffer(byteString.length);
56
+ intArray = new Uint8Array(arrayBuffer);
57
+ for (i = 0; i < byteString.length; i += 1) {
58
+ intArray[i] = byteString.charCodeAt(i);
59
+ }
60
+ // Separate out the mime component:
61
+ mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
62
+ // Write the ArrayBuffer (or ArrayBufferView) to a blob:
63
+ if (hasBlobConstructor) {
64
+ return new Blob(
65
+ [hasArrayBufferViewSupport ? intArray : arrayBuffer],
66
+ {type: mimeString}
67
+ );
68
+ }
69
+ bb = new BlobBuilder();
70
+ bb.append(arrayBuffer);
71
+ return bb.getBlob(mimeString);
72
+ };
73
+ if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) {
74
+ if (CanvasPrototype.mozGetAsFile) {
75
+ CanvasPrototype.toBlob = function (callback, type, quality) {
76
+ if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) {
77
+ callback(dataURLtoBlob(this.toDataURL(type, quality)));
78
+ } else {
79
+ callback(this.mozGetAsFile('blob', type));
80
+ }
81
+ };
82
+ } else if (CanvasPrototype.toDataURL && dataURLtoBlob) {
83
+ CanvasPrototype.toBlob = function (callback, type, quality) {
84
+ callback(dataURLtoBlob(this.toDataURL(type, quality)));
85
+ };
86
+ }
87
+ }
88
+ if (typeof define === 'function' && define.amd) {
89
+ define(function () {
90
+ return dataURLtoBlob;
91
+ });
92
+ } else {
93
+ window.dataURLtoBlob = dataURLtoBlob;
94
+ }
95
+ }(this));
@@ -0,0 +1 @@
1
+ !function(t){"use strict";var e=t.HTMLCanvasElement&&t.HTMLCanvasElement.prototype,n=t.Blob&&function(){try{return Boolean(new Blob)}catch(t){return!1}}(),o=n&&t.Uint8Array&&function(){try{return 100===new Blob([new Uint8Array(100)]).size}catch(t){return!1}}(),r=t.BlobBuilder||t.WebKitBlobBuilder||t.MozBlobBuilder||t.MSBlobBuilder,i=(n||r)&&t.atob&&t.ArrayBuffer&&t.Uint8Array&&function(t){var e,i,a,l,u,B;for(e=t.split(",")[0].indexOf("base64")>=0?atob(t.split(",")[1]):decodeURIComponent(t.split(",")[1]),i=new ArrayBuffer(e.length),a=new Uint8Array(i),l=0;l<e.length;l+=1)a[l]=e.charCodeAt(l);return u=t.split(",")[0].split(":")[1].split(";")[0],n?new Blob([o?a:i],{type:u}):(B=new r,B.append(i),B.getBlob(u))};t.HTMLCanvasElement&&!e.toBlob&&(e.mozGetAsFile?e.toBlob=function(t,n,o){o&&e.toDataURL&&i?t(i(this.toDataURL(n,o))):t(this.mozGetAsFile("blob",n))}:e.toDataURL&&i&&(e.toBlob=function(t,e,n){t(i(this.toDataURL(e,n)))})),"function"==typeof define&&define.amd?define(function(){return i}):t.dataURLtoBlob=i}(this);
@@ -0,0 +1,736 @@
1
+ /*! JpegCamera 1.0.0 | 2013-07-29
2
+ (c) 2013 Adam Wrobel
3
+ http://amw.github.io/jpeg_camera */
4
+ (function() {
5
+ var JpegCamera, JpegCameraFlash, JpegCameraHtml5, Snapshot, supported_flash_version, _ref, _ref1,
6
+ __hasProp = {}.hasOwnProperty,
7
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
8
+
9
+ JpegCamera = (function() {
10
+ JpegCamera.DefaultOptions = {
11
+ shutter_url: "/jpeg_camera/shutter.mp3",
12
+ swf_url: "/jpeg_camera/jpeg_camera.swf",
13
+ on_debug: function(message) {
14
+ if (console && console.log) {
15
+ return console.log("JpegCamera: " + message);
16
+ }
17
+ },
18
+ quality: 0.9,
19
+ shutter: true,
20
+ mirror: false,
21
+ timeout: 0
22
+ };
23
+
24
+ function JpegCamera(container, options) {
25
+ if ("string" === typeof container) {
26
+ this.container = document.querySelector(container);
27
+ } else {
28
+ this.container = container;
29
+ }
30
+ if (!(this.container && this.container.offsetWidth)) {
31
+ throw "JpegCamera: invalid container";
32
+ }
33
+ this.container.innerHTML = "";
34
+ this.options = this._extend({}, this.constructor.DefaultOptions, options);
35
+ this._engine_init();
36
+ }
37
+
38
+ JpegCamera.prototype.ready = function(callback) {
39
+ this.options.on_ready = callback;
40
+ if (this.options.on_ready && this._is_ready) {
41
+ this.options.on_ready.call(this);
42
+ }
43
+ return this;
44
+ };
45
+
46
+ JpegCamera.prototype._is_ready = false;
47
+
48
+ JpegCamera.prototype.error = function(callback) {
49
+ this.options.on_error = callback;
50
+ if (this.options.on_error && this._error_occured) {
51
+ this.options.on_error.call(this, this._error_occured);
52
+ }
53
+ return this;
54
+ };
55
+
56
+ JpegCamera.prototype._error_occured = false;
57
+
58
+ JpegCamera.prototype.capture = function(options) {
59
+ var snapshot, _options;
60
+ if (options == null) {
61
+ options = {};
62
+ }
63
+ snapshot = new Snapshot(this, options);
64
+ this._snapshots[snapshot.id] = snapshot;
65
+ _options = snapshot._options();
66
+ if (_options.shutter) {
67
+ this._engine_play_shutter_sound();
68
+ }
69
+ this._engine_capture(snapshot, _options.mirror, _options.quality);
70
+ return snapshot;
71
+ };
72
+
73
+ JpegCamera.prototype._snapshots = {};
74
+
75
+ JpegCamera.prototype.show_stream = function() {
76
+ this._engine_show_stream();
77
+ this._displayed_snapshot = null;
78
+ return this;
79
+ };
80
+
81
+ JpegCamera.prototype.discard_all = function() {
82
+ var id, snapshot, _ref;
83
+ if (this._displayed_snapshot) {
84
+ this.show_stream();
85
+ }
86
+ _ref = this._snapshots;
87
+ for (id in _ref) {
88
+ snapshot = _ref[id];
89
+ this._engine_discard(snapshot);
90
+ snapshot._discarded = true;
91
+ }
92
+ this._snapshots = {};
93
+ return this;
94
+ };
95
+
96
+ JpegCamera.prototype._extend = function(object) {
97
+ var key, source, sources, value, _i, _len;
98
+ sources = Array.prototype.slice.call(arguments, 1);
99
+ for (_i = 0, _len = sources.length; _i < _len; _i++) {
100
+ source = sources[_i];
101
+ if (source) {
102
+ for (key in source) {
103
+ value = source[key];
104
+ object[key] = value;
105
+ }
106
+ }
107
+ }
108
+ return object;
109
+ };
110
+
111
+ JpegCamera.prototype._debug = function(message) {
112
+ if (this.options.on_debug) {
113
+ return this.options.on_debug.call(this, message);
114
+ }
115
+ };
116
+
117
+ JpegCamera.prototype._view_width = function() {
118
+ return parseInt(this.container.offsetWidth, 10);
119
+ };
120
+
121
+ JpegCamera.prototype._view_height = function() {
122
+ return parseInt(this.container.offsetHeight, 10);
123
+ };
124
+
125
+ JpegCamera.prototype._display = function(snapshot) {
126
+ this._engine_display(snapshot);
127
+ return this._displayed_snapshot = snapshot;
128
+ };
129
+
130
+ JpegCamera.prototype._displayed_snapshot = null;
131
+
132
+ JpegCamera.prototype._upload = function(snapshot, api_url, csrf_token, timeout) {
133
+ return this._engine_upload(snapshot, api_url, csrf_token, timeout);
134
+ };
135
+
136
+ JpegCamera.prototype._discard = function(snapshot) {
137
+ if (this._displayed_snapshot === snapshot) {
138
+ this.show_stream();
139
+ }
140
+ this._engine_discard(snapshot);
141
+ snapshot._discarded = true;
142
+ return delete this._snapshots[snapshot.id];
143
+ };
144
+
145
+ JpegCamera.prototype._prepared = function() {
146
+ this._is_ready = true;
147
+ if (this.options.on_ready) {
148
+ return this.options.on_ready.call(this);
149
+ }
150
+ };
151
+
152
+ JpegCamera.prototype._got_error = function(error) {
153
+ this._debug("Error - " + error);
154
+ this._error_occured = error;
155
+ if (this.options.on_error) {
156
+ return this.options.on_error.call(this, this._error_occured);
157
+ }
158
+ };
159
+
160
+ return JpegCamera;
161
+
162
+ })();
163
+
164
+ navigator.getUserMedia || (navigator.getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
165
+
166
+ if (navigator.getUserMedia) {
167
+ JpegCameraHtml5 = (function(_super) {
168
+ __extends(JpegCameraHtml5, _super);
169
+
170
+ function JpegCameraHtml5() {
171
+ _ref = JpegCameraHtml5.__super__.constructor.apply(this, arguments);
172
+ return _ref;
173
+ }
174
+
175
+ JpegCameraHtml5.prototype._engine_init = function() {
176
+ var get_user_media_options, horizontal_padding, that, vertical_padding;
177
+ this._debug("Using HTML5 engine");
178
+ this.internal_container = document.createElement("div");
179
+ this.internal_container.style.width = "100%";
180
+ this.internal_container.style.height = "100%";
181
+ this.internal_container.style.position = "relative";
182
+ this.container.appendChild(this.internal_container);
183
+ vertical_padding = Math.floor(this._view_height() * 0.2);
184
+ horizontal_padding = Math.floor(this._view_width() * 0.2);
185
+ this.message = document.createElement("div");
186
+ this.message["class"] = "message";
187
+ this.message.style.width = "100%";
188
+ this.message.style.height = "100%";
189
+ this._add_prefixed_style(this.message, "boxSizing", "border-box");
190
+ this.message.style.overflow = "hidden";
191
+ this.message.style.textAlign = "center";
192
+ this.message.style.paddingTop = "" + vertical_padding + "px";
193
+ this.message.style.paddingBottom = "" + vertical_padding + "px";
194
+ this.message.style.paddingLeft = "" + horizontal_padding + "px";
195
+ this.message.style.paddingRight = "" + horizontal_padding + "px";
196
+ this.message.style.position = "absolute";
197
+ this.message.style.zIndex = 3;
198
+ this.message.innerHTML = "Please allow camera access when prompted by the browser.";
199
+ this.internal_container.appendChild(this.message);
200
+ this.video_container = document.createElement("div");
201
+ this.video_container.style.width = "" + (this._view_width()) + "px";
202
+ this.video_container.style.height = "" + (this._view_height()) + "px";
203
+ this.video_container.style.overflow = "hidden";
204
+ this.video_container.style.position = "absolute";
205
+ this.video_container.style.zIndex = 1;
206
+ this.internal_container.appendChild(this.video_container);
207
+ this.video = document.createElement('video');
208
+ this.video.autoplay = true;
209
+ this._add_prefixed_style(this.video, "transform", "scalex(-1.0)");
210
+ window.AudioContext || (window.AudioContext = window.webkitAudioContext);
211
+ if (window.AudioContext) {
212
+ this._load_shutter_sound();
213
+ }
214
+ get_user_media_options = {
215
+ video: {
216
+ optional: [
217
+ {
218
+ minWidth: 1280
219
+ }, {
220
+ minWidth: 640
221
+ }, {
222
+ minWidth: 480
223
+ }, {
224
+ minWidth: 360
225
+ }
226
+ ]
227
+ }
228
+ };
229
+ that = this;
230
+ return navigator.getUserMedia(get_user_media_options, function(stream) {
231
+ that._remove_message();
232
+ if (window.URL) {
233
+ that.video.src = URL.createObjectURL(stream);
234
+ } else {
235
+ that.video.src = stream;
236
+ }
237
+ return that._wait_for_video_ready();
238
+ }, function(error) {
239
+ var code, key, value;
240
+ code = error.code;
241
+ for (key in error) {
242
+ value = error[key];
243
+ if (key === "code") {
244
+ continue;
245
+ }
246
+ that._got_error(key);
247
+ return;
248
+ }
249
+ return that._got_error("UNKNOWN ERROR");
250
+ });
251
+ };
252
+
253
+ JpegCameraHtml5.prototype._engine_play_shutter_sound = function() {
254
+ var source;
255
+ if (!this.shutter_buffer) {
256
+ return;
257
+ }
258
+ source = this.audio_context.createBufferSource();
259
+ source.buffer = this.shutter_buffer;
260
+ source.connect(this.audio_context.destination);
261
+ return source.start(0);
262
+ };
263
+
264
+ JpegCameraHtml5.prototype._engine_capture = function(snapshot, mirror, quality) {
265
+ var canvas, context, crop;
266
+ crop = this._get_capture_crop();
267
+ canvas = document.createElement("canvas");
268
+ canvas.width = crop.width;
269
+ canvas.height = crop.height;
270
+ context = canvas.getContext("2d");
271
+ context.drawImage(this.video, crop.x_offset, crop.y_offset, crop.width, crop.height, 0, 0, crop.width, crop.height);
272
+ snapshot._canvas = canvas;
273
+ snapshot._mirror = mirror;
274
+ return snapshot._quality = quality;
275
+ };
276
+
277
+ JpegCameraHtml5.prototype._engine_display = function(snapshot) {
278
+ if (this.displayed_canvas) {
279
+ this.internal_container.removeChild(this.displayed_canvas);
280
+ }
281
+ this.displayed_canvas = snapshot._canvas;
282
+ this.displayed_canvas.style.width = "" + (this._view_width()) + "px";
283
+ this.displayed_canvas.style.height = "" + (this._view_height()) + "px";
284
+ this.displayed_canvas.style.top = 0;
285
+ this.displayed_canvas.style.left = 0;
286
+ this.displayed_canvas.style.position = "absolute";
287
+ this.displayed_canvas.style.zIndex = 2;
288
+ this._add_prefixed_style(this.displayed_canvas, "transform", "scalex(-1.0)");
289
+ return this.internal_container.appendChild(this.displayed_canvas);
290
+ };
291
+
292
+ JpegCameraHtml5.prototype._engine_discard = function(snapshot) {
293
+ if (snapshot._xhr) {
294
+ snapshot._xhr.abort();
295
+ }
296
+ delete snapshot._xhr;
297
+ delete snapshot._canvas;
298
+ return delete snapshot._jpeg_blob;
299
+ };
300
+
301
+ JpegCameraHtml5.prototype._engine_show_stream = function() {
302
+ if (this.displayed_canvas) {
303
+ this.internal_container.removeChild(this.displayed_canvas);
304
+ this.displayed_canvas = null;
305
+ }
306
+ return this.video_container.style.display = "block";
307
+ };
308
+
309
+ JpegCameraHtml5.prototype._engine_upload = function(snapshot, api_url, csrf_token, timeout) {
310
+ var canvas, context, handler, that, xhr;
311
+ that = this;
312
+ if (snapshot._jpeg_blob) {
313
+ this._debug("Uploading the file");
314
+ handler = function(event) {
315
+ delete snapshot._xhr;
316
+ snapshot._status = event.target.status;
317
+ snapshot._response = event.target.responseText;
318
+ if (snapshot._status >= 200 && snapshot._status < 300) {
319
+ return snapshot._upload_done();
320
+ } else {
321
+ snapshot._error_message = event.target.statusText || "Unknown error";
322
+ return snapshot._upload_fail();
323
+ }
324
+ };
325
+ xhr = new XMLHttpRequest();
326
+ xhr.open('POST', api_url);
327
+ xhr.timeout = timeout;
328
+ if (csrf_token) {
329
+ xhr.setRequestHeader("X-CSRF-Token", csrf_token);
330
+ }
331
+ xhr.onload = handler;
332
+ xhr.onerror = handler;
333
+ xhr.onabort = handler;
334
+ xhr.send(snapshot._jpeg_blob);
335
+ return snapshot._xhr = xhr;
336
+ } else {
337
+ this._debug("Generating JPEG file");
338
+ if (snapshot._mirror) {
339
+ canvas = document.createElement("canvas");
340
+ canvas.width = snapshot._canvas.width;
341
+ canvas.height = snapshot._canvas.height;
342
+ context = canvas.getContext("2d");
343
+ context.setTransform(1, 0, 0, 1, 0, 0);
344
+ context.translate(canvas.width, 0);
345
+ context.scale(-1, 1);
346
+ context.drawImage(snapshot._canvas, 0, 0);
347
+ } else {
348
+ canvas = snapshot._canvas;
349
+ }
350
+ return canvas.toBlob(function(blob) {
351
+ snapshot._jpeg_blob = blob;
352
+ return that._engine_upload(snapshot, api_url, csrf_token, timeout);
353
+ }, "image/jpeg", this.quality);
354
+ }
355
+ };
356
+
357
+ JpegCameraHtml5.prototype._remove_message = function() {
358
+ return this.message.style.display = "none";
359
+ };
360
+
361
+ JpegCameraHtml5.prototype._load_shutter_sound = function() {
362
+ var request, that;
363
+ if (this.audio_context) {
364
+ return;
365
+ }
366
+ this.audio_context = new AudioContext();
367
+ request = new XMLHttpRequest();
368
+ request.open('GET', this.options.shutter_url, true);
369
+ request.responseType = 'arraybuffer';
370
+ that = this;
371
+ request.onload = function() {
372
+ return that.audio_context.decodeAudioData(request.response, function(buffer) {
373
+ return that.shutter_buffer = buffer;
374
+ });
375
+ };
376
+ return request.send();
377
+ };
378
+
379
+ JpegCameraHtml5.prototype._wait_for_video_ready = function() {
380
+ var crop, that, video_height, video_width;
381
+ video_width = parseInt(this.video.videoWidth);
382
+ video_height = parseInt(this.video.videoHeight);
383
+ if (video_width > 0 && video_height > 0) {
384
+ this.video_container.appendChild(this.video);
385
+ this.video_width = video_width;
386
+ this.video_height = video_height;
387
+ this._debug("Camera resolution " + this.video_width + "x" + this.video_height + "px");
388
+ crop = this._get_video_crop();
389
+ this.video.style.position = "relative";
390
+ this.video.style.width = "" + crop.width + "px";
391
+ this.video.style.height = "" + crop.height + "px";
392
+ this.video.style.left = "" + crop.x_offset + "px";
393
+ this.video.style.top = "" + crop.y_offset + "px";
394
+ return this._prepared();
395
+ } else if (this._status_checks_count > 100) {
396
+ return this._got_error("Camera failed to initialize in 10 seconds");
397
+ } else {
398
+ this._status_checks_count++;
399
+ that = this;
400
+ return setTimeout((function() {
401
+ return that._wait_for_video_ready();
402
+ }), 100);
403
+ }
404
+ };
405
+
406
+ JpegCameraHtml5.prototype._status_checks_count = 0;
407
+
408
+ JpegCameraHtml5.prototype._add_prefixed_style = function(element, style, value) {
409
+ var uppercase_style;
410
+ uppercase_style = style.charAt(0).toUpperCase() + style.slice(1);
411
+ element.style[style] = value;
412
+ element.style["Webkit" + uppercase_style] = value;
413
+ element.style["Moz" + uppercase_style] = value;
414
+ element.style["ms" + uppercase_style] = value;
415
+ return element.style["O" + uppercase_style] = value;
416
+ };
417
+
418
+ JpegCameraHtml5.prototype._get_video_crop = function() {
419
+ var scaled_video_height, scaled_video_width, video_ratio, video_scale, view_height, view_ratio, view_width;
420
+ view_width = this._view_width();
421
+ view_height = this._view_height();
422
+ video_ratio = this.video_width / this.video_height;
423
+ view_ratio = view_width / view_height;
424
+ if (video_ratio >= view_ratio) {
425
+ this._debug("Filling height");
426
+ video_scale = view_height / this.video_height;
427
+ scaled_video_width = Math.round(this.video_width * video_scale);
428
+ return {
429
+ width: scaled_video_width,
430
+ height: view_height,
431
+ x_offset: -Math.floor((scaled_video_width - view_width) / 2.0),
432
+ y_offset: 0
433
+ };
434
+ } else {
435
+ this._debug("Filling width");
436
+ video_scale = view_width / this.video_width;
437
+ scaled_video_height = Math.round(this.video_height * video_scale);
438
+ return {
439
+ width: view_width,
440
+ height: scaled_video_height,
441
+ x_offset: 0,
442
+ y_offset: -Math.floor((scaled_video_height - view_height) / 2.0)
443
+ };
444
+ }
445
+ };
446
+
447
+ JpegCameraHtml5.prototype._get_capture_crop = function() {
448
+ var snapshot_height, snapshot_width, video_ratio, view_height, view_ratio, view_width;
449
+ view_width = this._view_width();
450
+ view_height = this._view_height();
451
+ video_ratio = this.video_width / this.video_height;
452
+ view_ratio = view_width / view_height;
453
+ if (video_ratio >= view_ratio) {
454
+ snapshot_width = Math.round(this.video_height * view_ratio);
455
+ return {
456
+ width: snapshot_width,
457
+ height: this.video_height,
458
+ x_offset: Math.floor((this.video_width - snapshot_width) / 2.0),
459
+ y_offset: 0
460
+ };
461
+ } else {
462
+ snapshot_height = Math.round(this.video_width / view_ratio);
463
+ return {
464
+ width: this.video_width,
465
+ height: snapshot_height,
466
+ x_offset: 0,
467
+ y_offset: Math.floor((this.video_height - snapshot_height) / 2.0)
468
+ };
469
+ }
470
+ };
471
+
472
+ return JpegCameraHtml5;
473
+
474
+ })(JpegCamera);
475
+ ({
476
+ video_width: null,
477
+ video_height: null
478
+ });
479
+ window.JpegCamera = JpegCameraHtml5;
480
+ }
481
+
482
+ supported_flash_version = '9';
483
+
484
+ if (!window.JpegCamera && window.swfobject && swfobject.hasFlashPlayerVersion(supported_flash_version)) {
485
+ JpegCameraFlash = (function(_super) {
486
+ __extends(JpegCameraFlash, _super);
487
+
488
+ function JpegCameraFlash() {
489
+ _ref1 = JpegCameraFlash.__super__.constructor.apply(this, arguments);
490
+ return _ref1;
491
+ }
492
+
493
+ JpegCameraFlash._send_message = function(id, method) {
494
+ var args, instance;
495
+ instance = this._instances[parseInt(id)];
496
+ if (!instance) {
497
+ return;
498
+ }
499
+ args = Array.prototype.slice.call(arguments, 2);
500
+ return this.prototype[method].apply(instance, args);
501
+ };
502
+
503
+ JpegCameraFlash._instances = {};
504
+
505
+ JpegCameraFlash._next_id = 1;
506
+
507
+ JpegCameraFlash.prototype._engine_init = function() {
508
+ var attributes, callback, flash_object_id, flashvars, height, params, that, width;
509
+ this._debug("Using Flash engine");
510
+ this._id = this.constructor._next_id++;
511
+ this.constructor._instances[this._id] = this;
512
+ width = this._view_width();
513
+ height = this._view_height();
514
+ if (width < 215 || height < 138) {
515
+ this._got_error("camera is too small to display privacy dialog");
516
+ return;
517
+ }
518
+ flash_object_id = "flash_object_" + this._id;
519
+ params = {
520
+ loop: "false",
521
+ allowScriptAccess: "always",
522
+ allowFullScreen: "false",
523
+ quality: "best",
524
+ wmode: "opaque",
525
+ menu: "false"
526
+ };
527
+ attributes = {
528
+ id: flash_object_id,
529
+ align: "middle"
530
+ };
531
+ flashvars = {
532
+ id: this._id,
533
+ width: width,
534
+ height: height,
535
+ shutter_url: this.options.shutter_url
536
+ };
537
+ that = this;
538
+ callback = function(event) {
539
+ if (!event.success) {
540
+ return that._got_error("Flash loading failed.");
541
+ } else {
542
+ that._debug("Flash loaded");
543
+ return that._flash = document.getElementById(flash_object_id);
544
+ }
545
+ };
546
+ this.internal_container = document.createElement("div");
547
+ this.internal_container.id = "jpeg_camera_flash_" + this._id;
548
+ this.internal_container.style.width = "100%";
549
+ this.internal_container.style.height = "100%";
550
+ this.container.appendChild(this.internal_container);
551
+ return swfobject.embedSWF(this.options.swf_url, this.internal_container.id, width, height, '9', null, flashvars, params, attributes, callback);
552
+ };
553
+
554
+ JpegCameraFlash.prototype._engine_play_shutter_sound = function() {
555
+ return this._flash._play_shutter();
556
+ };
557
+
558
+ JpegCameraFlash.prototype._engine_capture = function(snapshot, mirror, quality) {
559
+ return this._flash._capture(snapshot.id, mirror, quality);
560
+ };
561
+
562
+ JpegCameraFlash.prototype._engine_display = function(snapshot) {
563
+ return this._flash._display(snapshot.id);
564
+ };
565
+
566
+ JpegCameraFlash.prototype._engine_discard = function(snapshot) {
567
+ return this._flash._discard(snapshot.id);
568
+ };
569
+
570
+ JpegCameraFlash.prototype._engine_show_stream = function() {
571
+ return this._flash._show_stream();
572
+ };
573
+
574
+ JpegCameraFlash.prototype._engine_upload = function(snapshot, api_url, csrf_token, timeout) {
575
+ return this._flash._upload(snapshot.id, api_url, csrf_token, timeout);
576
+ };
577
+
578
+ JpegCameraFlash.prototype._flash_prepared = function() {
579
+ return this._prepared();
580
+ };
581
+
582
+ JpegCameraFlash.prototype._flash_upload_complete = function(snapshot_id, status_code, error, response) {
583
+ var snapshot;
584
+ snapshot_id = parseInt(snapshot_id);
585
+ snapshot = this._snapshots[snapshot_id];
586
+ snapshot._status = parseInt(status_code);
587
+ snapshot._response = response;
588
+ if (snapshot._status >= 200 && snapshot._status < 300) {
589
+ return snapshot._upload_done();
590
+ } else {
591
+ snapshot._error_message = error;
592
+ return snapshot._upload_fail();
593
+ }
594
+ };
595
+
596
+ return JpegCameraFlash;
597
+
598
+ })(JpegCamera);
599
+ window.JpegCamera = JpegCameraFlash;
600
+ }
601
+
602
+ Snapshot = (function() {
603
+ Snapshot._next_snapshot_id = 1;
604
+
605
+ function Snapshot(camera, options) {
606
+ this.camera = camera;
607
+ this.options = options;
608
+ this.id = this.constructor._next_snapshot_id++;
609
+ }
610
+
611
+ Snapshot.prototype._discarded = false;
612
+
613
+ Snapshot.prototype.show = function() {
614
+ if (this._discarded) {
615
+ raise("discarded snapshot cannot be used");
616
+ }
617
+ this.camera._display(this);
618
+ return this;
619
+ };
620
+
621
+ Snapshot.prototype.hide = function() {
622
+ if (this.camera.displayed_snapshot() === this) {
623
+ this.camera.show_stream();
624
+ }
625
+ return this;
626
+ };
627
+
628
+ Snapshot.prototype.upload = function(options) {
629
+ var cache, csrf_token;
630
+ if (options == null) {
631
+ options = {};
632
+ }
633
+ if (this._discarded) {
634
+ raise("discarded snapshot cannot be used");
635
+ }
636
+ if (this._uploading) {
637
+ this._debug("Upload already in progress");
638
+ return;
639
+ }
640
+ this._uploading = true;
641
+ this._upload_options = options;
642
+ cache = this._options();
643
+ if (!cache.api_url) {
644
+ this.camera._debug("Snapshot#upload called without valid api_url");
645
+ throw "Snapshot#upload called without valid api_url";
646
+ }
647
+ if ("string" === typeof cache.csrf_token && cache.csrf_token.length > 0) {
648
+ csrf_token = cache.csrf_token;
649
+ } else {
650
+ csrf_token = null;
651
+ }
652
+ this._done = false;
653
+ this._response = null;
654
+ this._fail = false;
655
+ this._status = null;
656
+ this._error_message = null;
657
+ this.camera._upload(this, cache.api_url, csrf_token, cache.timeout);
658
+ return this;
659
+ };
660
+
661
+ Snapshot.prototype._upload_options = {};
662
+
663
+ Snapshot.prototype._uploading = false;
664
+
665
+ Snapshot.prototype.done = function(callback) {
666
+ var cache;
667
+ if (this._discarded) {
668
+ raise("discarded snapshot cannot be used");
669
+ }
670
+ this._upload_options.on_upload_done = callback;
671
+ cache = this._options();
672
+ if (cache.on_upload_done && this._done) {
673
+ cache.on_upload_done.call(this, this._response);
674
+ }
675
+ return this;
676
+ };
677
+
678
+ Snapshot.prototype._done = false;
679
+
680
+ Snapshot.prototype._response = null;
681
+
682
+ Snapshot.prototype.fail = function(callback) {
683
+ var cache;
684
+ if (this._discarded) {
685
+ raise("discarded snapshot cannot be used");
686
+ }
687
+ this._upload_options.on_upload_fail = callback;
688
+ cache = this._options();
689
+ if (cache.on_upload_fail && this._fail) {
690
+ cache.on_upload_fail.call(this, this._status, this._error_message, this._response);
691
+ }
692
+ return this;
693
+ };
694
+
695
+ Snapshot.prototype._fail = false;
696
+
697
+ Snapshot.prototype._status = null;
698
+
699
+ Snapshot.prototype._error_message = null;
700
+
701
+ Snapshot.prototype.discard = function() {
702
+ this.camera._discard(this);
703
+ return void 0;
704
+ };
705
+
706
+ Snapshot.prototype._options = function() {
707
+ return this.camera._extend({}, this.camera.options, this.options, this._upload_options);
708
+ };
709
+
710
+ Snapshot.prototype._upload_done = function() {
711
+ var cache;
712
+ this.camera._debug("Upload completed");
713
+ this._uploading = false;
714
+ this._done = true;
715
+ cache = this._options();
716
+ if (cache.on_upload_done) {
717
+ return cache.on_upload_done.call(this, this._response);
718
+ }
719
+ };
720
+
721
+ Snapshot.prototype._upload_fail = function() {
722
+ var cache;
723
+ this.camera._debug("Upload failed with status " + this._status);
724
+ this._uploading = false;
725
+ this._fail = true;
726
+ cache = this._options();
727
+ if (cache.on_upload_fail) {
728
+ return cache.on_upload_fail.call(this, this._status, this._error_message, this._response);
729
+ }
730
+ };
731
+
732
+ return Snapshot;
733
+
734
+ })();
735
+
736
+ }).call(this);