diff --git a/demos/choose-camera.html b/demos/choose-camera.html
new file mode 100644
index 0000000..01c00fd
--- /dev/null
+++ b/demos/choose-camera.html
@@ -0,0 +1,76 @@
+
+
+
+
+
+ WebcamJS Test Page
+
+
+
+ Your captured image will appear here...
+
+ WebcamJS Test Page
+ Demonstrates Adobe Flash capture & display
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/flash/Webcam.as b/flash/Webcam.as
index e9b7421..7a1ea9f 100644
--- a/flash/Webcam.as
+++ b/flash/Webcam.as
@@ -40,6 +40,7 @@
private var image_format:String;
private var fps:int;
private var flip_horiz:Boolean;
+ private var camera_idx:int;
public function Webcam() {
// class constructor
@@ -68,45 +69,25 @@
// Hack to auto-select iSight camera on Mac (JPEGCam Issue #5, submitted by manuel.gonzalez.noriega)
// From: http://www.squidder.com/2009/03/09/trick-auto-select-mac-isight-in-flash/
- var cameraIdx:int = -1;
+ camera_idx = -1;
for (var idx = 0, len = Camera.names.length; idx < len; idx++) {
if (Camera.names[idx] == "USB Video Class Video") {
- cameraIdx = idx;
+ camera_idx = idx;
idx = len;
}
}
- if (cameraIdx > -1) camera = Camera.getCamera( String(cameraIdx) );
+ if (camera_idx > -1) camera = Camera.getCamera( String(camera_idx) );
else camera = Camera.getCamera();
if (camera != null) {
- camera.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
- camera.addEventListener(StatusEvent.STATUS, handleCameraStatus, false, 0, true);
- video = new Video( Math.max(video_width, dest_width), Math.max(video_height, dest_height) );
- video.attachCamera(camera);
- addChild(video);
-
- if ((video_width < dest_width) && (video_height < dest_height)) {
- video.scaleX = video_width / dest_width;
- video.scaleY = video_height / dest_height;
- }
-
- if (flip_horiz) {
- video.scaleX *= -1;
- video.x = video.width + video.x;
- }
-
- camera.setQuality(0, 100);
- camera.setKeyFrameInterval(10);
- camera.setMode( Math.max(video_width, dest_width), Math.max(video_height, dest_height), fps );
-
- // only detect motion once, to determine when camera is "live"
- camera.setMotionLevel( 1 );
-
+ ExternalInterface.addCallback('_getCameras', getCameras);
+ ExternalInterface.addCallback('_setCamera', setCamera);
+ ExternalInterface.addCallback('_initCamera', initCamera);
ExternalInterface.addCallback('_snap', snap);
ExternalInterface.addCallback('_configure', configure);
ExternalInterface.addCallback('_releaseCamera', releaseCamera);
- ExternalInterface.call('Webcam.flashNotify', 'flashLoadComplete', true);
+ ExternalInterface.call('Webcam.flashNotify', 'flashInitComplete', true);
}
else {
trace("You need a camera.");
@@ -141,6 +122,56 @@
}
}
+ public function getCameras() {
+ return Camera.names;
+ }
+
+ public function setCamera(name: String) {
+ for (var idx = 0, len = Camera.names.length; idx < len; idx++) {
+ if (Camera.names[idx] == name) {
+ camera_idx = idx;
+ return;
+ }
+ }
+ ExternalInterface.call('Webcam.flashNotify', "error", "camera: " + name + " is not found");
+ trace("camera: " + name + " is not found");
+ }
+
+ public function initCamera() {
+ releaseCamera();
+ if (camera_idx > -1) camera = Camera.getCamera(String(camera_idx));
+ else camera = Camera.getCamera();
+
+ if (camera != null) {
+ camera.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
+ camera.addEventListener(StatusEvent.STATUS, handleCameraStatus, false, 0, true);
+ video = new Video(Math.max(video_width, dest_width), Math.max(video_height, dest_height));
+ video.attachCamera(camera);
+ addChild(video);
+
+ if ((video_width < dest_width) && (video_height < dest_height)) {
+ video.scaleX = video_width / dest_width;
+ video.scaleY = video_height / dest_height;
+ }
+
+ if (flip_horiz) {
+ video.scaleX *= -1;
+ video.x = video.width + video.x;
+ }
+
+ camera.setQuality(0, 100);
+ camera.setKeyFrameInterval(10);
+ camera.setMode(Math.max(video_width, dest_width), Math.max(video_height, dest_height), fps);
+
+ // only detect motion once, to determine when camera is "live"
+ camera.setMotionLevel(1);
+ ExternalInterface.call('Webcam.flashNotify', 'flashLoadComplete', true);
+ } else {
+ trace("You need a camera.");
+ ExternalInterface.call('Webcam.flashNotify', "error", "No camera was detected.");
+ }
+ }
+
public function snap() {
// take snapshot from camera, and upload if URL was provided
trace("in snap(), drawing to bitmap");
@@ -185,13 +216,13 @@
}
public function releaseCamera() {
-
trace("in releaseCamera(), turn off camera");
- video.attachCamera(null);
- video.clear();
- camera = null;
- removeChild(video);
-
+ if (video != null){
+ video.attachCamera(null);
+ video.clear();
+ camera = null;
+ removeChild(video);
+ }
}
}
}
diff --git a/flash/Webcam.fla b/flash/Webcam.fla
index 6f17dd1..d666554 100644
Binary files a/flash/Webcam.fla and b/flash/Webcam.fla differ
diff --git a/webcam.js b/webcam.js
index dd945d5..afe6889 100644
--- a/webcam.js
+++ b/webcam.js
@@ -38,6 +38,7 @@ var Webcam = {
// globals
protocol: location.protocol.match(/https/i) ? 'https' : 'http',
+ inited: false, // true when webcam privilege is generated
loaded: false, // true when webcam movie finishes loading
live: false, // true when webcam is initialized and ready to snap
userMedia: true, // true when getUserMedia is supported natively
@@ -222,7 +223,7 @@ var Webcam = {
img.src = origObjURL;
},
- attach: function(elem) {
+ attach: function(elem, notInit) {
// create webcam preview and attach to DOM element
// pass in actual DOM reference, ID, or CSS selector
if (typeof(elem) == 'string') {
@@ -291,48 +292,51 @@ var Webcam = {
// add video element to dom
elem.appendChild( video );
this.video = video;
-
- // ask user for access to their camera
- var self = this;
- this.mediaDevices.getUserMedia({
- "audio": false,
- "video": this.params.constraints || {
- mandatory: {
- minWidth: this.params.dest_width,
- minHeight: this.params.dest_height
+ this.inited = true;
+ this.dispatch("init");
+ if(!notInit) {
+ // ask user for access to their camera
+ var self = this;
+ this.mediaDevices.getUserMedia({
+ "audio": false,
+ "video": this.params.constraints || {
+ mandatory: {
+ minWidth: this.params.dest_width,
+ minHeight: this.params.dest_height
+ }
}
- }
- })
- .then( function(stream) {
- // got access, attach stream to video
- video.onloadedmetadata = function(e) {
- self.stream = stream;
- self.loaded = true;
- self.live = true;
- self.dispatch('load');
- self.dispatch('live');
- self.flip();
- };
- // as window.URL.createObjectURL() is deprecated, adding a check so that it works in Safari.
- // older browsers may not have srcObject
- if ("srcObject" in video) {
- video.srcObject = stream;
- }
- else {
- // using URL.createObjectURL() as fallback for old browsers
- video.src = window.URL.createObjectURL(stream);
- }
- })
- .catch( function(err) {
- // JH 2016-07-31 Instead of dispatching error, now falling back to Flash if userMedia fails (thx @john2014)
- // JH 2016-08-07 But only if flash is actually installed -- if not, dispatch error here and now.
- if (self.params.enable_flash && self.detectFlash()) {
- setTimeout( function() { self.params.force_flash = 1; self.attach(elem); }, 1 );
- }
- else {
- self.dispatch('error', err);
- }
- });
+ })
+ .then( function(stream) {
+ // got access, attach stream to video
+ video.onloadedmetadata = function(e) {
+ self.stream = stream;
+ self.loaded = true;
+ self.live = true;
+ self.dispatch('load');
+ self.dispatch('live');
+ self.flip();
+ };
+ // as window.URL.createObjectURL() is deprecated, adding a check so that it works in Safari.
+ // older browsers may not have srcObject
+ if ("srcObject" in video) {
+ video.srcObject = stream;
+ }
+ else {
+ // using URL.createObjectURL() as fallback for old browsers
+ video.src = window.URL.createObjectURL(stream);
+ }
+ })
+ .catch( function(err) {
+ // JH 2016-07-31 Instead of dispatching error, now falling back to Flash if userMedia fails (thx @john2014)
+ // JH 2016-08-07 But only if flash is actually installed -- if not, dispatch error here and now.
+ if (self.params.enable_flash && self.detectFlash()) {
+ setTimeout( function() { self.params.force_flash = 1; self.attach(elem); }, 1 );
+ }
+ else {
+ self.dispatch('error', err);
+ }
+ });
+ }
}
else if (this.iOS) {
// prepare HTML elements
@@ -443,6 +447,12 @@ var Webcam = {
var div = document.createElement('div');
div.innerHTML = this.getSWFHTML();
elem.appendChild( div );
+ var _this = this;
+ if(!notInit){
+ this.on("init",function() {
+ _this.getMovie()._initCamera();
+ });
+ }
}
else {
this.dispatch('error', new WebcamError( this.params.noInterfaceFoundText ));
@@ -466,6 +476,138 @@ var Webcam = {
elem.style.height = '' + this.params.height + 'px';
}
},
+
+ getCameras: function (success) {
+ if (this.userMedia) {
+ this.mediaDevices.enumerateDevices().then(function(devices) {
+ var data = [];
+ for(var i = 0;i < devices.length;i++) {
+ if(devices[i].kind === "videoinput") {
+ data.push({
+ id: devices[i].deviceId,
+ groupId: devices[i].groupId,
+ label: devices[i].label
+ });
+ }
+ }
+ success(data);
+ })
+ } else if (this.iOS) {
+ } else if (this.params.enable_flash && this.detectFlash()) {
+ var devices = this.getMovie()._getCameras();
+ var data = [];
+ for(var i = 0;i < devices.length;i++) {
+ data.push({
+ id: devices[i],
+ groupId: "",
+ label: devices[i]
+ });
+ }
+ success(data);
+ }
+ },
+
+ setAndInitCamera: function (name) {
+ if (this.userMedia) {
+ // ask user for access to their camera
+ var self = this;
+ var video = this.video;
+ this.mediaDevices.getUserMedia({
+ "audio": false,
+ "video": {
+ deviceId: {
+ exact: name,
+ }
+ }
+ })
+ .then( function(stream) {
+ // got access, attach stream to video
+ video.onloadedmetadata = function(e) {
+ self.stream = stream;
+ self.loaded = true;
+ self.live = true;
+ self.dispatch('load');
+ self.dispatch('live');
+ self.flip();
+ };
+ // as window.URL.createObjectURL() is deprecated, adding a check so that it works in Safari.
+ // older browsers may not have srcObject
+ if ("srcObject" in video) {
+ video.srcObject = stream;
+ }
+ else {
+ // using URL.createObjectURL() as fallback for old browsers
+ video.src = window.URL.createObjectURL(stream);
+ }
+ })
+ .catch( function(err) {
+ // JH 2016-07-31 Instead of dispatching error, now falling back to Flash if userMedia fails (thx @john2014)
+ // JH 2016-08-07 But only if flash is actually installed -- if not, dispatch error here and now.
+ if (self.params.enable_flash && self.detectFlash()) {
+ setTimeout( function() { self.params.force_flash = 1; self.attach(elem); }, 1 );
+ }
+ else {
+ self.dispatch('error', err);
+ }
+ });
+ } else if (this.iOS) {
+ } else if (this.params.enable_flash && this.detectFlash()) {
+ var movie = this.getMovie();
+ movie._setCamera(name);
+ movie._initCamera();
+ }
+ },
+
+ initCamera: function () {
+ if (this.userMedia) {
+ // ask user for access to their camera
+ var self = this;
+ var video = this.video;
+ this.mediaDevices.getUserMedia({
+ "audio": false,
+ "video": this.params.constraints || {
+ mandatory: {
+ minWidth: this.params.dest_width,
+ minHeight: this.params.dest_height
+ }
+ }
+ })
+ .then( function(stream) {
+ // got access, attach stream to video
+ video.onloadedmetadata = function(e) {
+ self.stream = stream;
+ self.loaded = true;
+ self.live = true;
+ self.dispatch('load');
+ self.dispatch('live');
+ self.flip();
+ };
+ // as window.URL.createObjectURL() is deprecated, adding a check so that it works in Safari.
+ // older browsers may not have srcObject
+ if ("srcObject" in video) {
+ video.srcObject = stream;
+ }
+ else {
+ // using URL.createObjectURL() as fallback for old browsers
+ video.src = window.URL.createObjectURL(stream);
+ }
+ })
+ .catch( function(err) {
+ // JH 2016-07-31 Instead of dispatching error, now falling back to Flash if userMedia fails (thx @john2014)
+ // JH 2016-08-07 But only if flash is actually installed -- if not, dispatch error here and now.
+ if (self.params.enable_flash && self.detectFlash()) {
+ setTimeout( function() { self.params.force_flash = 1; self.attach(elem); }, 1 );
+ }
+ else {
+ self.dispatch('error', err);
+ }
+ });
+ } else if (this.iOS) {
+ } else if (this.params.enable_flash && this.detectFlash()) {
+ var movie = this.getMovie();
+ movie._initCamera();
+ }
+ },
reset: function() {
// shutdown camera, reset to potentially attach again
@@ -670,7 +812,7 @@ var Webcam = {
getMovie: function() {
// get reference to movie object/embed in DOM
- if (!this.loaded) return this.dispatch('error', new FlashError("Flash Movie is not loaded yet"));
+ if (!this.inited) return this.dispatch('error', new FlashError("Flash Movie is not loaded yet"));
var movie = document.getElementById('webcam_movie_obj');
if (!movie || !movie._snap) movie = document.getElementById('webcam_movie_embed');
if (!movie) this.dispatch('error', new FlashError("Cannot locate Flash movie in DOM"));
@@ -933,6 +1075,10 @@ var Webcam = {
flashNotify: function(type, msg) {
// receive notification from flash about event
switch (type) {
+ case "flashInitComplete":
+ this.inited = true;
+ this.dispatch("init");
+ break;
case 'flashLoadComplete':
// movie loaded successfully
this.loaded = true;
diff --git a/webcam.min.js b/webcam.min.js
index 03de2f7..9587063 100644
--- a/webcam.min.js
+++ b/webcam.min.js
@@ -1,2 +1 @@
-// WebcamJS v1.0.25 - http://github.com/jhuckaby/webcamjs - MIT Licensed
-(function(e){var t;function a(){var e=Error.apply(this,arguments);e.name=this.name="FlashError";this.stack=e.stack;this.message=e.message}function i(){var e=Error.apply(this,arguments);e.name=this.name="WebcamError";this.stack=e.stack;this.message=e.message}var s=function(){};s.prototype=Error.prototype;a.prototype=new s;i.prototype=new s;var Webcam={version:"1.0.26",protocol:location.protocol.match(/https/i)?"https":"http",loaded:false,live:false,userMedia:true,iOS:/iPad|iPhone|iPod/.test(navigator.userAgent)&&!e.MSStream,params:{width:0,height:0,dest_width:0,dest_height:0,image_format:"jpeg",jpeg_quality:90,enable_flash:true,force_flash:false,flip_horiz:false,fps:30,upload_name:"webcam",constraints:null,swfURL:"",flashNotDetectedText:"ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).",noInterfaceFoundText:"No supported webcam interface found.",unfreeze_snap:true,iosPlaceholderText:"Click here to open camera.",user_callback:null,user_canvas:null},errors:{FlashError:a,WebcamError:i},hooks:{},init:function(){var t=this;this.mediaDevices=navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices:navigator.mozGetUserMedia||navigator.webkitGetUserMedia?{getUserMedia:function(e){return new Promise(function(t,a){(navigator.mozGetUserMedia||navigator.webkitGetUserMedia).call(navigator,e,t,a)})}}:null;e.URL=e.URL||e.webkitURL||e.mozURL||e.msURL;this.userMedia=this.userMedia&&!!this.mediaDevices&&!!e.URL;if(navigator.userAgent.match(/Firefox\D+(\d+)/)){if(parseInt(RegExp.$1,10)<21)this.userMedia=null}if(this.userMedia){e.addEventListener("beforeunload",function(e){t.reset()})}},exifOrientation:function(e){var t=new DataView(e);if(t.getUint8(0)!=255||t.getUint8(1)!=216){console.log("Not a valid JPEG file");return 0}var a=2;var i=null;while(a8){console.log("Invalid EXIF orientation value ("+p+")");return 0}return p}}}else{a+=2+t.getUint16(a+2)}}return 0},fixOrientation:function(e,t,a){var i=new Image;i.addEventListener("load",function(e){var s=document.createElement("canvas");var r=s.getContext("2d");if(t<5){s.width=i.width;s.height=i.height}else{s.width=i.height;s.height=i.width}switch(t){case 2:r.transform(-1,0,0,1,i.width,0);break;case 3:r.transform(-1,0,0,-1,i.width,i.height);break;case 4:r.transform(1,0,0,-1,0,i.height);break;case 5:r.transform(0,1,1,0,0,0);break;case 6:r.transform(0,1,-1,0,i.height,0);break;case 7:r.transform(0,-1,-1,0,i.height,i.width);break;case 8:r.transform(0,-1,1,0,0,i.width);break}r.drawImage(i,0,0);a.src=s.toDataURL()},false);i.src=e},attach:function(a){if(typeof a=="string"){a=document.getElementById(a)||document.querySelector(a)}if(!a){return this.dispatch("error",new i("Could not locate DOM element to attach to."))}this.container=a;a.innerHTML="";var s=document.createElement("div");a.appendChild(s);this.peg=s;if(!this.params.width)this.params.width=a.offsetWidth;if(!this.params.height)this.params.height=a.offsetHeight;if(!this.params.width||!this.params.height){return this.dispatch("error",new i("No width and/or height for webcam. Please call set() first, or attach to a visible element."))}if(!this.params.dest_width)this.params.dest_width=this.params.width;if(!this.params.dest_height)this.params.dest_height=this.params.height;this.userMedia=t===undefined?this.userMedia:t;if(this.params.force_flash){t=this.userMedia;this.userMedia=null}if(typeof this.params.fps!=="number")this.params.fps=30;var r=this.params.width/this.params.dest_width;var o=this.params.height/this.params.dest_height;if(this.userMedia){var n=document.createElement("video");n.setAttribute("autoplay","autoplay");n.setAttribute("playsinline","playsinline");n.style.width=""+this.params.dest_width+"px";n.style.height=""+this.params.dest_height+"px";if(r!=1||o!=1){a.style.overflow="hidden";n.style.webkitTransformOrigin="0px 0px";n.style.mozTransformOrigin="0px 0px";n.style.msTransformOrigin="0px 0px";n.style.oTransformOrigin="0px 0px";n.style.transformOrigin="0px 0px";n.style.webkitTransform="scaleX("+r+") scaleY("+o+")";n.style.mozTransform="scaleX("+r+") scaleY("+o+")";n.style.msTransform="scaleX("+r+") scaleY("+o+")";n.style.oTransform="scaleX("+r+") scaleY("+o+")";n.style.transform="scaleX("+r+") scaleY("+o+")"}a.appendChild(n);this.video=n;var l=this;this.mediaDevices.getUserMedia({audio:false,video:this.params.constraints||{mandatory:{minWidth:this.params.dest_width,minHeight:this.params.dest_height}}}).then(function(t){n.onloadedmetadata=function(e){l.stream=t;l.loaded=true;l.live=true;l.dispatch("load");l.dispatch("live");l.flip()};if("srcObject"in n){n.srcObject=t}else{n.src=e.URL.createObjectURL(t)}}).catch(function(e){if(l.params.enable_flash&&l.detectFlash()){setTimeout(function(){l.params.force_flash=1;l.attach(a)},1)}else{l.dispatch("error",e)}})}else if(this.iOS){var h=document.createElement("div");h.id=this.container.id+"-ios_div";h.className="webcamjs-ios-placeholder";h.style.width=""+this.params.width+"px";h.style.height=""+this.params.height+"px";h.style.textAlign="center";h.style.display="table-cell";h.style.verticalAlign="middle";h.style.backgroundRepeat="no-repeat";h.style.backgroundSize="contain";h.style.backgroundPosition="center";var c=document.createElement("span");c.className="webcamjs-ios-text";c.innerHTML=this.params.iosPlaceholderText;h.appendChild(c);var d=document.createElement("img");d.id=this.container.id+"-ios_img";d.style.width=""+this.params.dest_width+"px";d.style.height=""+this.params.dest_height+"px";d.style.display="none";h.appendChild(d);var f=document.createElement("input");f.id=this.container.id+"-ios_input";f.setAttribute("type","file");f.setAttribute("accept","image/*");f.setAttribute("capture","camera");var l=this;var m=this.params;f.addEventListener("change",function(e){if(e.target.files.length>0&&e.target.files[0].type.indexOf("image/")==0){var t=URL.createObjectURL(e.target.files[0]);var a=new Image;a.addEventListener("load",function(e){var t=document.createElement("canvas");t.width=m.dest_width;t.height=m.dest_height;var i=t.getContext("2d");ratio=Math.min(a.width/m.dest_width,a.height/m.dest_height);var s=m.dest_width*ratio;var r=m.dest_height*ratio;var o=(a.width-s)/2;var n=(a.height-r)/2;i.drawImage(a,o,n,s,r,0,0,m.dest_width,m.dest_height);var l=t.toDataURL();d.src=l;h.style.backgroundImage="url('"+l+"')"},false);var i=new FileReader;i.addEventListener("load",function(e){var i=l.exifOrientation(e.target.result);if(i>1){l.fixOrientation(t,i,a)}else{a.src=t}},false);var s=new XMLHttpRequest;s.open("GET",t,true);s.responseType="blob";s.onload=function(e){if(this.status==200||this.status===0){i.readAsArrayBuffer(this.response)}};s.send()}},false);f.style.display="none";a.appendChild(f);h.addEventListener("click",function(e){if(m.user_callback){l.snap(m.user_callback,m.user_canvas)}else{f.style.display="block";f.focus();f.click();f.style.display="none"}},false);a.appendChild(h);this.loaded=true;this.live=true}else if(this.params.enable_flash&&this.detectFlash()){e.Webcam=Webcam;var h=document.createElement("div");h.innerHTML=this.getSWFHTML();a.appendChild(h)}else{this.dispatch("error",new i(this.params.noInterfaceFoundText))}if(this.params.crop_width&&this.params.crop_height){var p=Math.floor(this.params.crop_width*r);var u=Math.floor(this.params.crop_height*o);a.style.width=""+p+"px";a.style.height=""+u+"px";a.style.overflow="hidden";a.scrollLeft=Math.floor(this.params.width/2-p/2);a.scrollTop=Math.floor(this.params.height/2-u/2)}else{a.style.width=""+this.params.width+"px";a.style.height=""+this.params.height+"px"}},reset:function(){if(this.preview_active)this.unfreeze();this.unflip();if(this.userMedia){if(this.stream){if(this.stream.getVideoTracks){var e=this.stream.getVideoTracks();if(e&&e[0]&&e[0].stop)e[0].stop()}else if(this.stream.stop){this.stream.stop()}}delete this.stream;delete this.video}if(this.userMedia!==true&&this.loaded&&!this.iOS){var t=this.getMovie();if(t&&t._releaseCamera)t._releaseCamera()}if(this.container){this.container.innerHTML="";delete this.container}this.loaded=false;this.live=false},set:function(){if(arguments.length==1){for(var e in arguments[0]){this.params[e]=arguments[0][e]}}else{this.params[arguments[0]]=arguments[1]}},on:function(e,t){e=e.replace(/^on/i,"").toLowerCase();if(!this.hooks[e])this.hooks[e]=[];this.hooks[e].push(t)},off:function(e,t){e=e.replace(/^on/i,"").toLowerCase();if(this.hooks[e]){if(t){var a=this.hooks[e].indexOf(t);if(a>-1)this.hooks[e].splice(a,1)}else{this.hooks[e]=[]}}},dispatch:function(){var t=arguments[0].replace(/^on/i,"").toLowerCase();var s=Array.prototype.slice.call(arguments,1);if(this.hooks[t]&&this.hooks[t].length){for(var r=0,o=this.hooks[t].length;rERROR: the Webcam.js Flash fallback does not work from local disk. Please run it from a web server.'}if(!this.detectFlash()){this.dispatch("error",new a("Adobe Flash Player not found. Please install from get.adobe.com/flashplayer and try again."));return''+this.params.flashNotDetectedText+"
"}if(!i){var s="";var r=document.getElementsByTagName("script");for(var o=0,n=r.length;o';return t},getMovie:function(){if(!this.loaded)return this.dispatch("error",new a("Flash Movie is not loaded yet"));var e=document.getElementById("webcam_movie_obj");if(!e||!e._snap)e=document.getElementById("webcam_movie_embed");if(!e)this.dispatch("error",new a("Cannot locate Flash movie in DOM"));return e},freeze:function(){var e=this;var t=this.params;if(this.preview_active)this.unfreeze();var a=this.params.width/this.params.dest_width;var i=this.params.height/this.params.dest_height;this.unflip();var s=t.crop_width||t.dest_width;var r=t.crop_height||t.dest_height;var o=document.createElement("canvas");o.width=s;o.height=r;var n=o.getContext("2d");this.preview_canvas=o;this.preview_context=n;if(a!=1||i!=1){o.style.webkitTransformOrigin="0px 0px";o.style.mozTransformOrigin="0px 0px";o.style.msTransformOrigin="0px 0px";o.style.oTransformOrigin="0px 0px";o.style.transformOrigin="0px 0px";o.style.webkitTransform="scaleX("+a+") scaleY("+i+")";o.style.mozTransform="scaleX("+a+") scaleY("+i+")";o.style.msTransform="scaleX("+a+") scaleY("+i+")";o.style.oTransform="scaleX("+a+") scaleY("+i+")";o.style.transform="scaleX("+a+") scaleY("+i+")"}this.snap(function(){o.style.position="relative";o.style.left=""+e.container.scrollLeft+"px";o.style.top=""+e.container.scrollTop+"px";e.container.insertBefore(o,e.peg);e.container.style.overflow="hidden";e.preview_active=true},o)},unfreeze:function(){if(this.preview_active){this.container.removeChild(this.preview_canvas);delete this.preview_context;delete this.preview_canvas;this.preview_active=false;this.flip()}},flip:function(){if(this.params.flip_horiz){var e=this.container.style;e.webkitTransform="scaleX(-1)";e.mozTransform="scaleX(-1)";e.msTransform="scaleX(-1)";e.oTransform="scaleX(-1)";e.transform="scaleX(-1)";e.filter="FlipH";e.msFilter="FlipH"}},unflip:function(){if(this.params.flip_horiz){var e=this.container.style;e.webkitTransform="scaleX(1)";e.mozTransform="scaleX(1)";e.msTransform="scaleX(1)";e.oTransform="scaleX(1)";e.transform="scaleX(1)";e.filter="";e.msFilter=""}},savePreview:function(e,t){var a=this.params;var i=this.preview_canvas;var s=this.preview_context;if(t){var r=t.getContext("2d");r.drawImage(i,0,0)}e(t?null:i.toDataURL("image/"+a.image_format,a.jpeg_quality/100),i,s);if(this.params.unfreeze_snap)this.unfreeze()},snap:function(e,t){if(!e)e=this.params.user_callback;if(!t)t=this.params.user_canvas;var a=this;var s=this.params;if(!this.loaded)return this.dispatch("error",new i("Webcam is not loaded yet"));if(!e)return this.dispatch("error",new i("Please provide a callback function or canvas to snap()"));if(this.preview_active){this.savePreview(e,t);return null}var r=document.createElement("canvas");r.width=this.params.dest_width;r.height=this.params.dest_height;var o=r.getContext("2d");if(this.params.flip_horiz){o.translate(s.dest_width,0);o.scale(-1,1)}var n=function(){if(this.src&&this.width&&this.height){o.drawImage(this,0,0,s.dest_width,s.dest_height)}if(s.crop_width&&s.crop_height){var a=document.createElement("canvas");a.width=s.crop_width;a.height=s.crop_height;var i=a.getContext("2d");i.drawImage(r,Math.floor(s.dest_width/2-s.crop_width/2),Math.floor(s.dest_height/2-s.crop_height/2),s.crop_width,s.crop_height,0,0,s.crop_width,s.crop_height);o=i;r=a}if(t){var n=t.getContext("2d");n.drawImage(r,0,0)}e(t?null:r.toDataURL("image/"+s.image_format,s.jpeg_quality/100),r,o)};if(this.userMedia){o.drawImage(this.video,0,0,this.params.dest_width,this.params.dest_height);n()}else if(this.iOS){var l=document.getElementById(this.container.id+"-ios_div");var h=document.getElementById(this.container.id+"-ios_img");var c=document.getElementById(this.container.id+"-ios_input");iFunc=function(e){n.call(h);h.removeEventListener("load",iFunc);l.style.backgroundImage="none";h.removeAttribute("src");c.value=null};if(!c.value){h.addEventListener("load",iFunc);c.style.display="block";c.focus();c.click();c.style.display="none"}else{iFunc(null)}}else{var d=this.getMovie()._snap();var h=new Image;h.onload=n;h.src="data:image/"+this.params.image_format+";base64,"+d}return null},configure:function(e){if(!e)e="camera";this.getMovie()._configure(e)},flashNotify:function(e,t){switch(e){case"flashLoadComplete":this.loaded=true;this.dispatch("load");break;case"cameraLive":this.live=true;this.dispatch("live");break;case"error":this.dispatch("error",new a(t));break;default:break}},b64ToUint6:function(e){return e>64&&e<91?e-65:e>96&&e<123?e-71:e>47&&e<58?e+4:e===43?62:e===47?63:0},base64DecToArr:function(e,t){var a=e.replace(/[^A-Za-z0-9\+\/]/g,""),i=a.length,s=t?Math.ceil((i*3+1>>2)/t)*t:i*3+1>>2,r=new Uint8Array(s);for(var o,n,l=0,h=0,c=0;c>>(16>>>o&24)&255}l=0}}return r},upload:function(e,t,a){var i=this.params.upload_name||"webcam";var s="";if(e.match(/^data\:image\/(\w+)/))s=RegExp.$1;else throw"Cannot locate image format in Data URI";var r=e.replace(/^data\:image\/\w+\;base64\,/,"");var o=new XMLHttpRequest;o.open("POST",t,true);if(o.upload&&o.upload.addEventListener){o.upload.addEventListener("progress",function(e){if(e.lengthComputable){var t=e.loaded/e.total;Webcam.dispatch("uploadProgress",t,e)}},false)}var n=this;o.onload=function(){if(a)a.apply(n,[o.status,o.responseText,o.statusText]);Webcam.dispatch("uploadComplete",o.status,o.responseText,o.statusText)};var l=new Blob([this.base64DecToArr(r)],{type:"image/"+s});var h=new FormData;h.append(i,l,i+"."+s.replace(/e/,""));o.send(h)}};Webcam.init();if(typeof define==="function"&&define.amd){define(function(){return Webcam})}else if(typeof module==="object"&&module.exports){module.exports=Webcam}else{e.Webcam=Webcam}})(window);
\ No newline at end of file
+(function(u){var v;function l(){var e=Error.apply(this,arguments);e.name=this.name="FlashError";this.stack=e.stack;this.message=e.message}function g(){var e=Error.apply(this,arguments);e.name=this.name="WebcamError";this.stack=e.stack;this.message=e.message}var e=function(){};e.prototype=Error.prototype;l.prototype=new e;g.prototype=new e;var w={version:"1.0.26",protocol:location.protocol.match(/https/i)?"https":"http",inited:false,loaded:false,live:false,userMedia:true,iOS:/iPad|iPhone|iPod/.test(navigator.userAgent)&&!u.MSStream,params:{width:0,height:0,dest_width:0,dest_height:0,image_format:"jpeg",jpeg_quality:90,enable_flash:true,force_flash:false,flip_horiz:false,fps:30,upload_name:"webcam",constraints:null,swfURL:"",flashNotDetectedText:"ERROR: No Adobe Flash Player detected. Webcam.js relies on Flash for browsers that do not support getUserMedia (like yours).",noInterfaceFoundText:"No supported webcam interface found.",unfreeze_snap:true,iosPlaceholderText:"Click here to open camera.",user_callback:null,user_canvas:null},errors:{FlashError:l,WebcamError:g},hooks:{},init:function(){var t=this;this.mediaDevices=navigator.mediaDevices&&navigator.mediaDevices.getUserMedia?navigator.mediaDevices:navigator.mozGetUserMedia||navigator.webkitGetUserMedia?{getUserMedia:function(a){return new Promise(function(e,t){(navigator.mozGetUserMedia||navigator.webkitGetUserMedia).call(navigator,a,e,t)})}}:null;u.URL=u.URL||u.webkitURL||u.mozURL||u.msURL;this.userMedia=this.userMedia&&!!this.mediaDevices&&!!u.URL;if(navigator.userAgent.match(/Firefox\D+(\d+)/)){if(parseInt(RegExp.$1,10)<21)this.userMedia=null}if(this.userMedia){u.addEventListener("beforeunload",function(e){t.reset()})}},exifOrientation:function(e){var t=new DataView(e);if(t.getUint8(0)!=255||t.getUint8(1)!=216){console.log("Not a valid JPEG file");return 0}var a=2;var i=null;while(a8){console.log("Invalid EXIF orientation value ("+p+")");return 0}return p}}}else{a+=2+t.getUint16(a+2)}}return 0},fixOrientation:function(e,i,s){var r=new Image;r.addEventListener("load",function(e){var t=document.createElement("canvas");var a=t.getContext("2d");if(i<5){t.width=r.width;t.height=r.height}else{t.width=r.height;t.height=r.width}switch(i){case 2:a.transform(-1,0,0,1,r.width,0);break;case 3:a.transform(-1,0,0,-1,r.width,r.height);break;case 4:a.transform(1,0,0,-1,0,r.height);break;case 5:a.transform(0,1,1,0,0,0);break;case 6:a.transform(0,1,-1,0,r.height,0);break;case 7:a.transform(0,-1,-1,0,r.height,r.width);break;case 8:a.transform(0,-1,1,0,0,r.width);break}a.drawImage(r,0,0);s.src=t.toDataURL()},false);r.src=e},attach:function(t,e){if(typeof t=="string"){t=document.getElementById(t)||document.querySelector(t)}if(!t){return this.dispatch("error",new g("Could not locate DOM element to attach to."))}this.container=t;t.innerHTML="";var a=document.createElement("div");t.appendChild(a);this.peg=a;if(!this.params.width)this.params.width=t.offsetWidth;if(!this.params.height)this.params.height=t.offsetHeight;if(!this.params.width||!this.params.height){return this.dispatch("error",new g("No width and/or height for webcam. Please call set() first, or attach to a visible element."))}if(!this.params.dest_width)this.params.dest_width=this.params.width;if(!this.params.dest_height)this.params.dest_height=this.params.height;this.userMedia=v===undefined?this.userMedia:v;if(this.params.force_flash){v=this.userMedia;this.userMedia=null}if(typeof this.params.fps!=="number")this.params.fps=30;var i=this.params.width/this.params.dest_width;var s=this.params.height/this.params.dest_height;if(this.userMedia){var r=document.createElement("video");r.setAttribute("autoplay","autoplay");r.setAttribute("playsinline","playsinline");r.style.width=""+this.params.dest_width+"px";r.style.height=""+this.params.dest_height+"px";if(i!=1||s!=1){t.style.overflow="hidden";r.style.webkitTransformOrigin="0px 0px";r.style.mozTransformOrigin="0px 0px";r.style.msTransformOrigin="0px 0px";r.style.oTransformOrigin="0px 0px";r.style.transformOrigin="0px 0px";r.style.webkitTransform="scaleX("+i+") scaleY("+s+")";r.style.mozTransform="scaleX("+i+") scaleY("+s+")";r.style.msTransform="scaleX("+i+") scaleY("+s+")";r.style.oTransform="scaleX("+i+") scaleY("+s+")";r.style.transform="scaleX("+i+") scaleY("+s+")"}t.appendChild(r);this.video=r;this.inited=true;this.dispatch("init");if(!e){var n=this;this.mediaDevices.getUserMedia({audio:false,video:this.params.constraints||{mandatory:{minWidth:this.params.dest_width,minHeight:this.params.dest_height}}}).then(function(t){r.onloadedmetadata=function(e){n.stream=t;n.loaded=true;n.live=true;n.dispatch("load");n.dispatch("live");n.flip()};if("srcObject"in r){r.srcObject=t}else{r.src=u.URL.createObjectURL(t)}}).catch(function(e){if(n.params.enable_flash&&n.detectFlash()){setTimeout(function(){n.params.force_flash=1;n.attach(t)},1)}else{n.dispatch("error",e)}})}}else if(this.iOS){var l=document.createElement("div");l.id=this.container.id+"-ios_div";l.className="webcamjs-ios-placeholder";l.style.width=""+this.params.width+"px";l.style.height=""+this.params.height+"px";l.style.textAlign="center";l.style.display="table-cell";l.style.verticalAlign="middle";l.style.backgroundRepeat="no-repeat";l.style.backgroundSize="contain";l.style.backgroundPosition="center";var o=document.createElement("span");o.className="webcamjs-ios-text";o.innerHTML=this.params.iosPlaceholderText;l.appendChild(o);var c=document.createElement("img");c.id=this.container.id+"-ios_img";c.style.width=""+this.params.dest_width+"px";c.style.height=""+this.params.dest_height+"px";c.style.display="none";l.appendChild(c);var h=document.createElement("input");h.id=this.container.id+"-ios_input";h.setAttribute("type","file");h.setAttribute("accept","image/*");h.setAttribute("capture","camera");var n=this;var d=this.params;h.addEventListener("change",function(e){if(e.target.files.length>0&&e.target.files[0].type.indexOf("image/")==0){var a=URL.createObjectURL(e.target.files[0]);var h=new Image;h.addEventListener("load",function(e){var t=document.createElement("canvas");t.width=d.dest_width;t.height=d.dest_height;var a=t.getContext("2d");ratio=Math.min(h.width/d.dest_width,h.height/d.dest_height);var i=d.dest_width*ratio;var s=d.dest_height*ratio;var r=(h.width-i)/2;var n=(h.height-s)/2;a.drawImage(h,r,n,i,s,0,0,d.dest_width,d.dest_height);var o=t.toDataURL();c.src=o;l.style.backgroundImage="url('"+o+"')"},false);var t=new FileReader;t.addEventListener("load",function(e){var t=n.exifOrientation(e.target.result);if(t>1){n.fixOrientation(a,t,h)}else{h.src=a}},false);var i=new XMLHttpRequest;i.open("GET",a,true);i.responseType="blob";i.onload=function(e){if(this.status==200||this.status===0){t.readAsArrayBuffer(this.response)}};i.send()}},false);h.style.display="none";t.appendChild(h);l.addEventListener("click",function(e){if(d.user_callback){n.snap(d.user_callback,d.user_canvas)}else{h.style.display="block";h.focus();h.click();h.style.display="none"}},false);t.appendChild(l);this.loaded=true;this.live=true}else if(this.params.enable_flash&&this.detectFlash()){u.Webcam=w;var l=document.createElement("div");l.innerHTML=this.getSWFHTML();t.appendChild(l);var f=this;if(!e){this.on("init",function(){f.getMovie()._initCamera()})}}else{this.dispatch("error",new g(this.params.noInterfaceFoundText))}if(this.params.crop_width&&this.params.crop_height){var m=Math.floor(this.params.crop_width*i);var p=Math.floor(this.params.crop_height*s);t.style.width=""+m+"px";t.style.height=""+p+"px";t.style.overflow="hidden";t.scrollLeft=Math.floor(this.params.width/2-m/2);t.scrollTop=Math.floor(this.params.height/2-p/2)}else{t.style.width=""+this.params.width+"px";t.style.height=""+this.params.height+"px"}},getCameras:function(i){if(this.userMedia){this.mediaDevices.enumerateDevices().then(function(e){var t=[];for(var a=0;a-1)this.hooks[e].splice(a,1)}else{this.hooks[e]=[]}}},dispatch:function(){var e=arguments[0].replace(/^on/i,"").toLowerCase();var t=Array.prototype.slice.call(arguments,1);if(this.hooks[e]&&this.hooks[e].length){for(var a=0,i=this.hooks[e].length;aERROR: the Webcam.js Flash fallback does not work from local disk. Please run it from a web server.'}if(!this.detectFlash()){this.dispatch("error",new l("Adobe Flash Player not found. Please install from get.adobe.com/flashplayer and try again."));return''+this.params.flashNotDetectedText+"
"}if(!t){var a="";var i=document.getElementsByTagName("script");for(var s=0,r=i.length;s';return e},getMovie:function(){if(!this.inited)return this.dispatch("error",new l("Flash Movie is not loaded yet"));var e=document.getElementById("webcam_movie_obj");if(!e||!e._snap)e=document.getElementById("webcam_movie_embed");if(!e)this.dispatch("error",new l("Cannot locate Flash movie in DOM"));return e},freeze:function(){var e=this;var t=this.params;if(this.preview_active)this.unfreeze();var a=this.params.width/this.params.dest_width;var i=this.params.height/this.params.dest_height;this.unflip();var s=t.crop_width||t.dest_width;var r=t.crop_height||t.dest_height;var n=document.createElement("canvas");n.width=s;n.height=r;var o=n.getContext("2d");this.preview_canvas=n;this.preview_context=o;if(a!=1||i!=1){n.style.webkitTransformOrigin="0px 0px";n.style.mozTransformOrigin="0px 0px";n.style.msTransformOrigin="0px 0px";n.style.oTransformOrigin="0px 0px";n.style.transformOrigin="0px 0px";n.style.webkitTransform="scaleX("+a+") scaleY("+i+")";n.style.mozTransform="scaleX("+a+") scaleY("+i+")";n.style.msTransform="scaleX("+a+") scaleY("+i+")";n.style.oTransform="scaleX("+a+") scaleY("+i+")";n.style.transform="scaleX("+a+") scaleY("+i+")"}this.snap(function(){n.style.position="relative";n.style.left=""+e.container.scrollLeft+"px";n.style.top=""+e.container.scrollTop+"px";e.container.insertBefore(n,e.peg);e.container.style.overflow="hidden";e.preview_active=true},n)},unfreeze:function(){if(this.preview_active){this.container.removeChild(this.preview_canvas);delete this.preview_context;delete this.preview_canvas;this.preview_active=false;this.flip()}},flip:function(){if(this.params.flip_horiz){var e=this.container.style;e.webkitTransform="scaleX(-1)";e.mozTransform="scaleX(-1)";e.msTransform="scaleX(-1)";e.oTransform="scaleX(-1)";e.transform="scaleX(-1)";e.filter="FlipH";e.msFilter="FlipH"}},unflip:function(){if(this.params.flip_horiz){var e=this.container.style;e.webkitTransform="scaleX(1)";e.mozTransform="scaleX(1)";e.msTransform="scaleX(1)";e.oTransform="scaleX(1)";e.transform="scaleX(1)";e.filter="";e.msFilter=""}},savePreview:function(e,t){var a=this.params;var i=this.preview_canvas;var s=this.preview_context;if(t){var r=t.getContext("2d");r.drawImage(i,0,0)}e(t?null:i.toDataURL("image/"+a.image_format,a.jpeg_quality/100),i,s);if(this.params.unfreeze_snap)this.unfreeze()},snap:function(i,s){if(!i)i=this.params.user_callback;if(!s)s=this.params.user_canvas;var e=this;var r=this.params;if(!this.loaded)return this.dispatch("error",new g("Webcam is not loaded yet"));if(!i)return this.dispatch("error",new g("Please provide a callback function or canvas to snap()"));if(this.preview_active){this.savePreview(i,s);return null}var n=document.createElement("canvas");n.width=this.params.dest_width;n.height=this.params.dest_height;var o=n.getContext("2d");if(this.params.flip_horiz){o.translate(r.dest_width,0);o.scale(-1,1)}var t=function(){if(this.src&&this.width&&this.height){o.drawImage(this,0,0,r.dest_width,r.dest_height)}if(r.crop_width&&r.crop_height){var e=document.createElement("canvas");e.width=r.crop_width;e.height=r.crop_height;var t=e.getContext("2d");t.drawImage(n,Math.floor(r.dest_width/2-r.crop_width/2),Math.floor(r.dest_height/2-r.crop_height/2),r.crop_width,r.crop_height,0,0,r.crop_width,r.crop_height);o=t;n=e}if(s){var a=s.getContext("2d");a.drawImage(n,0,0)}i(s?null:n.toDataURL("image/"+r.image_format,r.jpeg_quality/100),n,o)};if(this.userMedia){o.drawImage(this.video,0,0,this.params.dest_width,this.params.dest_height);t()}else if(this.iOS){var a=document.getElementById(this.container.id+"-ios_div");var h=document.getElementById(this.container.id+"-ios_img");var l=document.getElementById(this.container.id+"-ios_input");iFunc=function(e){t.call(h);h.removeEventListener("load",iFunc);a.style.backgroundImage="none";h.removeAttribute("src");l.value=null};if(!l.value){h.addEventListener("load",iFunc);l.style.display="block";l.focus();l.click();l.style.display="none"}else{iFunc(null)}}else{var c=this.getMovie()._snap();var h=new Image;h.onload=t;h.src="data:image/"+this.params.image_format+";base64,"+c}return null},configure:function(e){if(!e)e="camera";this.getMovie()._configure(e)},flashNotify:function(e,t){switch(e){case"flashInitComplete":this.inited=true;this.dispatch("init");break;case"flashLoadComplete":this.loaded=true;this.dispatch("load");break;case"cameraLive":this.live=true;this.dispatch("live");break;case"error":this.dispatch("error",new l(t));break;default:break}},b64ToUint6:function(e){return e>64&&e<91?e-65:e>96&&e<123?e-71:e>47&&e<58?e+4:e===43?62:e===47?63:0},base64DecToArr:function(e,t){var a=e.replace(/[^A-Za-z0-9\+\/]/g,""),i=a.length,s=t?Math.ceil((i*3+1>>2)/t)*t:i*3+1>>2,r=new Uint8Array(s);for(var n,o,h=0,l=0,c=0;c>>(16>>>n&24)&255}h=0}}return r},upload:function(e,t,a){var i=this.params.upload_name||"webcam";var s="";if(e.match(/^data\:image\/(\w+)/))s=RegExp.$1;else throw"Cannot locate image format in Data URI";var r=e.replace(/^data\:image\/\w+\;base64\,/,"");var n=new XMLHttpRequest;n.open("POST",t,true);if(n.upload&&n.upload.addEventListener){n.upload.addEventListener("progress",function(e){if(e.lengthComputable){var t=e.loaded/e.total;w.dispatch("uploadProgress",t,e)}},false)}var o=this;n.onload=function(){if(a)a.apply(o,[n.status,n.responseText,n.statusText]);w.dispatch("uploadComplete",n.status,n.responseText,n.statusText)};var h=new Blob([this.base64DecToArr(r)],{type:"image/"+s});var l=new FormData;l.append(i,h,i+"."+s.replace(/e/,""));n.send(l)}};w.init();if(typeof define==="function"&&define.amd){define(function(){return w})}else if(typeof module==="object"&&module.exports){module.exports=w}else{u.Webcam=w}})(window);
\ No newline at end of file
diff --git a/webcam.swf b/webcam.swf
index e1d88cd..e65f4cb 100755
Binary files a/webcam.swf and b/webcam.swf differ