Image capture support for non WebRTC compliant browsers, via Flash.
This commit is contained in:
12
lms/static/js/vendor/jpegcam/webcam.min.js
vendored
12
lms/static/js/vendor/jpegcam/webcam.min.js
vendored
@@ -1,12 +0,0 @@
|
||||
/* JPEGCam v1.0.11 *//* Webcam library for capturing JPEG images and submitting to a server *//* Copyright (c) 2008 - 2009 Joseph Huckaby <jhuckaby@goldcartridge.com> *//* Licensed under the GNU Lesser Public License *//* http://www.gnu.org/licenses/lgpl.html *//* Usage:
|
||||
<script language="JavaScript">
|
||||
document.write( webcam.get_html(320, 240) );
|
||||
webcam.set_api_url( 'test.php' );
|
||||
webcam.set_hook( 'onComplete', 'my_callback_function' );
|
||||
function my_callback_function(response) {
|
||||
alert("Success! PHP returned: " + response);
|
||||
}
|
||||
</script>
|
||||
<a href="javascript:void(webcam.snap())">Take Snapshot</a>
|
||||
*/// Everything is under a 'webcam' Namespace
|
||||
window.webcam={version:"1.0.11",ie:!!navigator.userAgent.match(/MSIE/),protocol:location.protocol.match(/https/i)?"https":"http",callback:null,swf_url:"webcam.swf",shutter_url:"shutter.mp3",api_url:"",loaded:false,quality:90,shutter_sound:true,stealth:true,hooks:{onLoad:null,onAllow:null,onComplete:null,onError:null},set_hook:function(a,b){this.hooks[a]=b},fire_hook:function(a,b){if(this.hooks[a]){if(typeof this.hooks[a]==="function"){this.hooks[a](b)}else if(typeof this.hooks[a]==="array"){this.hooks[a][0][this.hooks[a][1]](b)}else if(window[this.hooks[a]]){window[this.hooks[a]](b)}return true}return false},set_api_url:function(a){this.api_url=a},set_swf_url:function(a){this.swf_url=a},get_html:function(a,b,c,d){if(!c){c=a}if(!d){d=b}var e="";var f="shutter_enabled="+(this.shutter_sound?1:0)+"&shutter_url="+encodeURIComponent(this.shutter_url)+"&width="+a+"&height="+b+"&server_width="+c+"&server_height="+d;if(this.ie){e+='<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="'+this.protocol+'://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0" width="'+a+'" height="'+b+'" id="webcam_movie" align="middle"><param name="allowScriptAccess" value="always" /><param name="allowFullScreen" value="false" /><param name="movie" value="'+this.swf_url+'" /><param name="loop" value="false" /><param name="menu" value="false" /><param name="quality" value="best" /><param name="bgcolor" value="#ffffff" /><param name="flashvars" value="'+f+'"/></object>'}else{e+='<embed id="webcam_movie" src="'+this.swf_url+'" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="'+a+'" height="'+b+'" name="webcam_movie" align="middle" wmode="opaque" allowScriptAccess="always" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="'+f+'" />'}this.loaded=false;return e},get_movie:function(){if(!this.loaded){return false}var a=document.getElementById("webcam_movie");if(!a){return false}return a},set_stealth:function(a){this.stealth=a},snap:function(a,b,c){if(b){this.set_hook("onComplete",b)}if(a){this.set_api_url(a)}if(typeof c!=="undefined"){this.set_stealth(c)}this.get_movie()._snap(this.api_url,this.quality,this.shutter_sound?1:0,this.stealth?1:0)},freeze:function(){this.get_movie()._snap("",this.quality,this.shutter_sound?1:0,0)},upload:function(a,b){if(b){this.set_hook("onComplete",b)}if(a){this.set_api_url(a)}this.get_movie()._upload(this.api_url)},reset:function(){this.get_movie()._reset()},configure:function(a){if(!a){a="camera"}this.get_movie()._configure(a)},set_quality:function(a){this.quality=a},set_shutter_sound:function(a,b){this.shutter_sound=a;this.shutter_url=b?b:"shutter.mp3"},flash_notify:function(a,b){switch(a){case"security":var c=b=="Camera.Unmuted";this.fire_hook("onAllow",c);break;case"flashLoadComplete":this.loaded=true;this.fire_hook("onLoad",b);break;case"error":if(!this.fire_hook("onError",b)){}break;case"success":this.fire_hook("onComplete",b.toString());break;default:break}}}
|
||||
BIN
lms/static/js/vendor/jpegcam/webcam.swf
vendored
BIN
lms/static/js/vendor/jpegcam/webcam.swf
vendored
Binary file not shown.
103
lms/static/js/verify_student/CameraCapture.as
Normal file
103
lms/static/js/verify_student/CameraCapture.as
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Simple Camera Capture application meant to be used where WebRTC is not supported
|
||||
* (e.g. Safari, Internet Explorer, Opera). All orchestration is assumed to happen
|
||||
* in JavaScript. The only function this application has is to capture a snapshot
|
||||
* and allow a 640x480 PNG of that snapshot to be made available to the JS as a
|
||||
* base64 encoded data URL.
|
||||
*
|
||||
* There are really only three methods:
|
||||
* snap() freezes the video and returns a PNG file as a data URL string. You can
|
||||
* assign this return value to an img's src attribute.
|
||||
* reset() restarts the the video.
|
||||
* imageDataUrl() returns the same thing as snap() --
|
||||
*/
|
||||
|
||||
package
|
||||
{
|
||||
import flash.display.BitmapData;
|
||||
import flash.display.PNGEncoderOptions;
|
||||
import flash.display.Sprite;
|
||||
import flash.events.Event;
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.geom.Rectangle;
|
||||
import flash.media.Camera;
|
||||
import flash.media.Video;
|
||||
import flash.utils.ByteArray;
|
||||
import mx.utils.Base64Encoder;
|
||||
|
||||
[SWF(width="640", height="480")]
|
||||
public class CameraCapture extends Sprite
|
||||
{
|
||||
// We pick these values because that's captured by the WebRTC spec
|
||||
private const VIDEO_WIDTH:int = 640;
|
||||
private const VIDEO_HEIGHT:int = 480;
|
||||
|
||||
private var camera:Camera;
|
||||
private var video:Video;
|
||||
private var b64EncodedImage:String = null;
|
||||
|
||||
public function CameraCapture()
|
||||
{
|
||||
addEventListener(Event.ADDED_TO_STAGE, init);
|
||||
}
|
||||
|
||||
protected function init(e:Event):void {
|
||||
camera = Camera.getCamera();
|
||||
camera.setMode(VIDEO_WIDTH, VIDEO_HEIGHT, 30);
|
||||
|
||||
video = new Video(VIDEO_WIDTH, VIDEO_HEIGHT);
|
||||
video.attachCamera(camera);
|
||||
|
||||
addChild(video);
|
||||
|
||||
ExternalInterface.addCallback("snap", snap);
|
||||
ExternalInterface.addCallback("reset", reset);
|
||||
ExternalInterface.addCallback("imageDataUrl", imageDataUrl);
|
||||
|
||||
// Notify the container that the SWF is ready to be called.
|
||||
ExternalInterface.call("setSWFIsReady");
|
||||
}
|
||||
|
||||
public function snap():String {
|
||||
// If we already have a b64 encoded image, just return that. The user
|
||||
// is calling snap() multiple times in a row without reset()
|
||||
if (b64EncodedImage) {
|
||||
return imageDataUrl();
|
||||
}
|
||||
|
||||
var bitmapData:BitmapData = new BitmapData(video.width, video.height);
|
||||
bitmapData.draw(video); // Draw a snapshot of the video onto our bitmapData
|
||||
video.attachCamera(null); // Stop capturing video
|
||||
|
||||
// Convert to PNG
|
||||
var pngBytes:ByteArray = new ByteArray();
|
||||
bitmapData.encode(
|
||||
new Rectangle(0, 0, video.width, video.height),
|
||||
new PNGEncoderOptions(),
|
||||
pngBytes
|
||||
);
|
||||
|
||||
// Convert to Base64 encoding of PNG
|
||||
var b64Encoder:Base64Encoder = new Base64Encoder();
|
||||
b64Encoder.encodeBytes(pngBytes);
|
||||
b64EncodedImage = b64Encoder.toString();
|
||||
|
||||
return imageDataUrl();
|
||||
}
|
||||
|
||||
public function reset():String {
|
||||
video.attachCamera(camera);
|
||||
b64EncodedImage = null;
|
||||
|
||||
return imageDataUrl();
|
||||
}
|
||||
|
||||
public function imageDataUrl():String {
|
||||
if (b64EncodedImage) {
|
||||
return "data:image/png;base64," + b64EncodedImage;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
lms/static/js/verify_student/CameraCapture.swf
Normal file
BIN
lms/static/js/verify_student/CameraCapture.swf
Normal file
Binary file not shown.
193
lms/static/js/verify_student/photocapture.js
Normal file
193
lms/static/js/verify_student/photocapture.js
Normal file
@@ -0,0 +1,193 @@
|
||||
var onVideoFail = function(e) {
|
||||
console.log('Failed to get camera access!', e);
|
||||
};
|
||||
|
||||
// Returns true if we are capable of video capture (regardless of whether the
|
||||
// user has given permission).
|
||||
function initVideoCapture() {
|
||||
window.URL = window.URL || window.webkitURL;
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
|
||||
navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||
return !(navigator.getUserMedia == undefined);
|
||||
}
|
||||
|
||||
var submitToPaymentProcessing = function() {
|
||||
var contribution_input = $("input[name='contribution']:checked")
|
||||
var contribution = 0;
|
||||
if(contribution_input.attr('id') == 'contribution-other')
|
||||
{
|
||||
contribution = $("input[name='contribution-other-amt']").val();
|
||||
}
|
||||
else
|
||||
{
|
||||
contribution = contribution_input.val();
|
||||
}
|
||||
var xhr = $.post(
|
||||
"create_order",
|
||||
{
|
||||
"course_id" : "${course_id}",
|
||||
"contribution": contribution
|
||||
},
|
||||
function(data) {
|
||||
for (prop in data) {
|
||||
$('<input>').attr({
|
||||
type: 'hidden',
|
||||
name: prop,
|
||||
value: data[prop]
|
||||
}).appendTo('#pay_form');
|
||||
}
|
||||
}
|
||||
)
|
||||
.done(function(data) {
|
||||
$("#pay_form").submit();
|
||||
})
|
||||
.fail(function(jqXhr,text_status, error_thrown) {
|
||||
alert(jqXhr.responseText);
|
||||
});
|
||||
}
|
||||
|
||||
function doResetButton(resetButton, captureButton, approveButton) {
|
||||
approveButton.removeClass('approved');
|
||||
nextButton.addClass('disabled');
|
||||
|
||||
captureButton.show();
|
||||
resetButton.hide();
|
||||
approveButton.hide();
|
||||
}
|
||||
|
||||
function doApproveButton(approveButton, nextButton) {
|
||||
approveButton.addClass('approved');
|
||||
nextButton.removeClass('disabled');
|
||||
}
|
||||
|
||||
function doSnapshotButton(captureButton, resetButton, approveButton) {
|
||||
captureButton.hide();
|
||||
resetButton.show();
|
||||
approveButton.show();
|
||||
}
|
||||
|
||||
function initSnapshotHandler(names, hasHtml5CameraSupport) {
|
||||
var name = names.pop();
|
||||
if (name == undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var video = $('#' + name + '_video');
|
||||
var canvas = $('#' + name + '_canvas');
|
||||
var image = $('#' + name + "_image");
|
||||
var captureButton = $("#" + name + "_capture_button");
|
||||
var resetButton = $("#" + name + "_reset_button");
|
||||
var approveButton = $("#" + name + "_approve_button");
|
||||
var nextButton = $("#" + name + "_next_button");
|
||||
var flashCapture = $("#" + name + "_flash");
|
||||
|
||||
var ctx = null;
|
||||
if (hasHtml5CameraSupport) {
|
||||
ctx = canvas[0].getContext('2d');
|
||||
}
|
||||
var localMediaStream = null;
|
||||
|
||||
function snapshot(event) {
|
||||
if (hasHtml5CameraSupport) {
|
||||
if (localMediaStream) {
|
||||
ctx.drawImage(video[0], 0, 0);
|
||||
image[0].src = canvas[0].toDataURL('image/png');
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
video[0].pause();
|
||||
}
|
||||
else {
|
||||
image[0].src = flashCapture[0].snap();
|
||||
}
|
||||
|
||||
doSnapshotButton(captureButton, resetButton, approveButton);
|
||||
return false;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
image[0].src = "";
|
||||
|
||||
if (hasHtml5CameraSupport) {
|
||||
video[0].play();
|
||||
}
|
||||
else {
|
||||
flashCapture[0].reset();
|
||||
}
|
||||
|
||||
doResetButton(resetButton, captureButton, approveButton);
|
||||
return false;
|
||||
}
|
||||
|
||||
function approve() {
|
||||
doApproveButton(approveButton, nextButton)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize state for this picture taker
|
||||
captureButton.show();
|
||||
resetButton.hide();
|
||||
approveButton.hide();
|
||||
nextButton.addClass('disabled');
|
||||
|
||||
// Connect event handlers...
|
||||
video.click(snapshot);
|
||||
captureButton.click(snapshot);
|
||||
resetButton.click(reset);
|
||||
approveButton.click(approve);
|
||||
|
||||
// If it's flash-based, we can just immediate initialize the next one.
|
||||
// If it's HTML5 based, we have to do it in the callback from getUserMedia
|
||||
// so that Firefox doesn't eat the second request.
|
||||
if (hasHtml5CameraSupport) {
|
||||
navigator.getUserMedia({video: true}, function(stream) {
|
||||
video[0].src = window.URL.createObjectURL(stream);
|
||||
localMediaStream = stream;
|
||||
|
||||
// We do this in a recursive call on success because Firefox seems to
|
||||
// simply eat the request if you stack up two on top of each other before
|
||||
// the user has a chance to approve the first one.
|
||||
initSnapshotHandler(names, hasHtml5CameraSupport);
|
||||
}, onVideoFail);
|
||||
}
|
||||
else {
|
||||
initSnapshotHandler(names, hasHtml5CameraSupport);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function objectTagForFlashCamera(name) {
|
||||
return '<object type="application/x-shockwave-flash" id="' +
|
||||
name + '" name="' + name + '" data=' +
|
||||
'"/static/js/verify_student/CameraCapture.swf"' +
|
||||
'width="500" height="375"><param name="quality" ' +
|
||||
'value="high"><param name="allowscriptaccess" ' +
|
||||
'value="sameDomain"></object>';
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(".carousel-nav").addClass('sr');
|
||||
$("#pay_button").click(submitToPaymentProcessing);
|
||||
// $("#confirm_pics_good").click(function() {
|
||||
// if (this.checked) {
|
||||
// $("#pay_button_frame").removeClass('disabled');
|
||||
// }
|
||||
// else {
|
||||
// $("#pay_button_frame").addClass('disabled');
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// $("#pay_button_frame").addClass('disabled');
|
||||
|
||||
var hasHtml5CameraSupport = initVideoCapture();
|
||||
|
||||
// If HTML5 WebRTC capture is not supported, we initialize jpegcam
|
||||
if (!hasHtml5CameraSupport) {
|
||||
$("#face_capture_div").html(objectTagForFlashCamera("face_flash"));
|
||||
$("#photo_id_capture_div").html(objectTagForFlashCamera("photo_id_flash"));
|
||||
}
|
||||
|
||||
initSnapshotHandler(["photo_id", "face"], hasHtml5CameraSupport);
|
||||
|
||||
});
|
||||
@@ -9,164 +9,7 @@
|
||||
<%block name="js_extra">
|
||||
<script src="${static.url('js/vendor/responsive-carousel/responsive-carousel.js')}"></script>
|
||||
<script src="${static.url('js/vendor/responsive-carousel/responsive-carousel.keybd.js')}"></script>
|
||||
<script src="${static.url('js/vendor/jpegcam/webcam.min.js')}"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var onVideoFail = function(e) {
|
||||
console.log('Failed to get camera access!', e);
|
||||
};
|
||||
|
||||
// Returns true if we are capable of video capture (regardless of whether the
|
||||
// user has given permission).
|
||||
function initVideoCapture() {
|
||||
window.URL = window.URL || window.webkitURL;
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia ||
|
||||
navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||
return !(navigator.getUserMedia == undefined);
|
||||
}
|
||||
|
||||
var submitToPaymentProcessing = function() {
|
||||
var contribution_input = $("input[name='contribution']:checked")
|
||||
var contribution = 0;
|
||||
if(contribution_input.attr('id') == 'contribution-other')
|
||||
{
|
||||
contribution = $("input[name='contribution-other-amt']").val();
|
||||
}
|
||||
else
|
||||
{
|
||||
contribution = contribution_input.val();
|
||||
}
|
||||
var xhr = $.post(
|
||||
"create_order",
|
||||
{
|
||||
"course_id" : "${course_id}",
|
||||
"contribution": contribution
|
||||
},
|
||||
function(data) {
|
||||
for (prop in data) {
|
||||
$('<input>').attr({
|
||||
type: 'hidden',
|
||||
name: prop,
|
||||
value: data[prop]
|
||||
}).appendTo('#pay_form');
|
||||
}
|
||||
}
|
||||
)
|
||||
.done(function(data) {
|
||||
$("#pay_form").submit();
|
||||
})
|
||||
.fail(function(jqXhr,text_status, error_thrown) {
|
||||
alert(jqXhr.responseText);
|
||||
});
|
||||
}
|
||||
|
||||
function initSnapshotHandler(names) {
|
||||
var name = names.pop();
|
||||
if (name == undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var video = $('#' + name + '_video');
|
||||
var canvas = $('#' + name + '_canvas');
|
||||
var image = $('#' + name + "_image");
|
||||
var captureButton = $("#" + name + "_capture_button");
|
||||
var resetButton = $("#" + name + "_reset_button");
|
||||
var approveButton = $("#" + name + "_approve_button");
|
||||
var nextButton = $("#" + name + "_next_button");
|
||||
|
||||
var ctx = canvas[0].getContext('2d');
|
||||
var localMediaStream = null;
|
||||
|
||||
function snapshot(event) {
|
||||
if (localMediaStream) {
|
||||
ctx.drawImage(video[0], 0, 0);
|
||||
image[0].src = canvas[0].toDataURL('image/png');
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
video[0].pause();
|
||||
|
||||
captureButton.hide();
|
||||
resetButton.show();
|
||||
approveButton.show();
|
||||
return false;
|
||||
}
|
||||
|
||||
function reset() {
|
||||
image[0].src = "";
|
||||
video[0].play();
|
||||
|
||||
approveButton.removeClass('approved');
|
||||
nextButton.addClass('disabled');
|
||||
|
||||
captureButton.show();
|
||||
resetButton.hide();
|
||||
approveButton.hide();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function approve() {
|
||||
approveButton.addClass('approved');
|
||||
nextButton.removeClass('disabled');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize state for this picture taker
|
||||
captureButton.show();
|
||||
resetButton.hide();
|
||||
approveButton.hide();
|
||||
nextButton.addClass('disabled');
|
||||
|
||||
// Connect event handlers...
|
||||
video.click(snapshot);
|
||||
captureButton.click(snapshot);
|
||||
resetButton.click(reset);
|
||||
approveButton.click(approve);
|
||||
|
||||
navigator.getUserMedia({video: true}, function(stream) {
|
||||
video[0].src = window.URL.createObjectURL(stream);
|
||||
localMediaStream = stream;
|
||||
|
||||
// We do this in a recursive call on success because Firefox seems to
|
||||
// simply eat the request if you stack up two on top of each other before
|
||||
// the user has a chance to approve the first one.
|
||||
initSnapshotHandler(names);
|
||||
}, onVideoFail);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$(".carousel-nav").addClass('sr');
|
||||
$("#pay_button").click(submitToPaymentProcessing);
|
||||
// $("#confirm_pics_good").click(function() {
|
||||
// if (this.checked) {
|
||||
// $("#pay_button_frame").removeClass('disabled');
|
||||
// }
|
||||
// else {
|
||||
// $("#pay_button_frame").addClass('disabled');
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// $("#pay_button_frame").addClass('disabled');
|
||||
|
||||
var hasHtml5CameraSupport = initVideoCapture();
|
||||
|
||||
// If HTML5 WebRTC capture is not supported, we initialize jpegcam
|
||||
if (!hasHtml5CameraSupport) {
|
||||
webcam.set_shutter_sound(false);
|
||||
webcam.set_quality(90); // JPEG quality
|
||||
webcam.set_swf_url("${static.url('js/vendor/jpegcam/webcam.swf')}");
|
||||
embedHtml = webcam.get_html(500, 375);
|
||||
$("#face_capture_div").html(embedHtml);
|
||||
}
|
||||
else {
|
||||
initSnapshotHandler(["photo_id", "face"]);
|
||||
}
|
||||
|
||||
});
|
||||
</script>
|
||||
<script src="${static.url('js/verify_student/photocapture.js')}"></script>
|
||||
</%block>
|
||||
|
||||
<%block name="content">
|
||||
@@ -324,7 +167,7 @@
|
||||
|
||||
<div class="wrapper-task">
|
||||
<div id="idcam" class="task cam">
|
||||
<div class="placeholder-cam">
|
||||
<div class="placeholder-cam" id="photo_id_capture_div">
|
||||
<video id="photo_id_video" width="500" height="375" autoplay></video><br/>
|
||||
<canvas id="photo_id_canvas" style="display:none;" width="640" height="480"></canvas>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user