Skip to content

Commit

Permalink
#25 add a web based qr code reader
Browse files Browse the repository at this point in the history
  • Loading branch information
syjer committed Feb 10, 2015
1 parent bf3d97d commit 24ad87e
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 13 deletions.
4 changes: 3 additions & 1 deletion src/main/java/alfio/config/MvcConfiguration.java
Expand Up @@ -121,8 +121,10 @@ public void postHandle(HttpServletRequest request, HttpServletResponse response,
response.addHeader("Content-Security-Policy", "default-src 'none'; "//block all by default
+ " script-src 'self' https://ajax.googleapis.com/ https://js.stripe.com/ https://api.stripe.com/ https://ssl.google-analytics.com/;"//
+ " style-src 'self';"//
+ " img-src 'self' https:;"//
+ " img-src 'self' https: data:;"//
+ " child-src 'self';"//webworker
+ " font-src 'self';"//
+ " media-src 'self';"//for loading camera api
+ " connect-src 'self' https://api.stripe.com;"); //<- currently stripe.js use jsonp but if they switch to xmlhttprequest+cors we will be ready
}
};
Expand Down
1 change: 1 addition & 0 deletions src/main/webapp/WEB-INF/templates/admin/index.ms
Expand Up @@ -22,6 +22,7 @@
<script src="{{request.contextPath}}/resources/bower_components/ng-file-upload/angular-file-upload.min.js"></script>
<script src="{{request.contextPath}}/resources/bower_components/Chart.js/Chart.js"></script>
<script src="{{request.contextPath}}/resources/bower_components/angular-chart.js/angular-chart.js"></script>
<script src="{{request.contextPath}}/resources/js/jsqrcode/jsqrcode.min.js"></script>
<script src="{{request.contextPath}}/resources/js/admin/ng-app/admin-application.js"></script>
<script src="{{request.contextPath}}/resources/js/admin/directive/admin-directive.js"></script>
<script src="{{request.contextPath}}/resources/js/admin/filter/util.js"></script>
Expand Down
Expand Up @@ -7,19 +7,33 @@ <h1>Check-In: {{event.shortName}}</h1>
<h2>Scan the ticket</h2>
</div>
<div>
<form data-ng-submit="checkIn(ticket)">
<div class="form-group">
<textarea class="form-control" rows="3" data-ng-model="ticket.code"></textarea>
<form data-ng-submit="scanning.visible = false; checkIn(ticket)" data-ng-init="scanning.visible = false;">
<div data-ng-hide="scanning.visible || ticket.code != null">
<p><button class="btn btn-primary" type="button" data-ng-click="scanning.visible = true;selectSource(selectedSource);">Scan</button></p>
</div>
<div class="form-group" data-ng-if="checkInResult">
{{checkInResult.status}} : {{checkInResult.message}}
<div class="form-group" data-ng-show="scanning.visible">
<p>Select video source: <select data-ng-options="video.label for video in videos" data-ng-change="selectSource(selectedSource)" data-ng-model="selectedSource"></select></p>
<p><button class="btn btn-primary" type="button" data-ng-click="stopScanning()">Stop scanning</button></p>
<video muted autoplay data-ng-show="selectedSource != null" id="checkInVideoElement"></video>
<canvas id="checkInImageCanvas" class="ng-hide"></canvas>

</div>
<div class="form-group" data-ng-if="checkInResult.status === 'MUST_PAY'">
<p>{{confirmPaymentResult.message}}</p>
<button type="button" class="btn btn-primary" data-ng-click="confirmPayment(ticket)">Confirm payment</button>
<div data-ng-show="ticket.code != null">
<p>Scanned value:</p>
<textarea class="form-control" rows="3" data-ng-model="ticket.code"></textarea>

<div class="form-group" data-ng-if="checkInResult">
{{checkInResult.status}} : {{checkInResult.message}}
</div>
<div data-ng-if="checkInResult.status === 'MUST_PAY'">
<p>{{confirmPaymentResult.message}}</p>
<button type="button" class="btn btn-primary" data-ng-click="confirmPayment(ticket)">Confirm payment</button>
</div>

<button type="submit" class="btn btn-primary">Submit</button>
<button type="button" class="btn btn-primary" data-ng-click="ticket.code = null; scanning.visible = true">Redo scan</button>
<button type="reset" class="btn btn-default" data-ng-click="stopScanning();resetForm(ticket)">Stop scanning</button>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
<button type="reset" class="btn btn-default" data-ng-click="resetForm(ticket)">Reset</button>
</form>
</div>

Expand Down
98 changes: 96 additions & 2 deletions src/main/webapp/resources/js/admin/ng-app/admin-application.js
Expand Up @@ -112,7 +112,8 @@

];
});

navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
var validationResultHandler = function(form, deferred) {
return function(validationResult) {
if(validationResult.errorCount > 0) {
Expand Down Expand Up @@ -590,7 +591,100 @@
};
});

admin.controller('EventCheckInController', function($scope, $stateParams, EventService, CheckInService) {
admin.controller('EventCheckInController', function($scope, $stateParams, $timeout, $log, EventService, CheckInService) {


navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;

$scope.ticket = {};
$scope.videos = [];
$scope.stream = null;

var timeoutPromise = null;


function stopScanning() {
if (!!$scope.stream) {
$scope.stream.stop();
}
$scope.scanning.visible = false;
$timeout.cancel(timeoutPromise);
}


$scope.stopScanning = stopScanning;

$scope.$on('$destroy', stopScanning);

function captureFrame() {
if($scope.scanning.visible) {
$log.debug('try to capture frame');
try {
var videoElement = document.getElementById('checkInVideoElement');
var canvas = document.getElementById("checkInImageCanvas");
canvas.height = videoElement.videoHeight;
canvas.width = videoElement.videoWidth;

canvas.getContext("2d").drawImage(videoElement, 0, 0);
qrcode.callback = function(result) {
if(result === 'error decoding QR Code') {
$log.debug('error decoding qr code');
} else {
$scope.$apply(function() {
$scope.scanning.visible = false;
$scope.ticket.code = result;
});
}
};
qrcode.decode(canvas.toDataURL());
} catch(e) {
$log.debug('error', e)
}
}

timeoutPromise = $timeout(function() {
captureFrame();
}, 500);
}


$scope.selectSource = function(source) {
if(source == undefined) {
return;
}

var videoElement = document.getElementById('checkInVideoElement');
if (!!$scope.stream) {
videoElement.src = null;
$scope.stream.stop();
}

var constraint = {video: {optional: [{sourceId: source.source.id}]}};

navigator.getUserMedia(constraint, function(stream) {
$scope.stream = stream; // make stream available to console
videoElement.src = window.URL.createObjectURL(stream);
videoElement.play();
$timeout.cancel(timeoutPromise);
captureFrame();
}, function() {
alert('error while loading camera');
$timeout.cancel(timeoutPromise);
});
};

MediaStreamTrack.getSources(function(sources) {
var videos = [];
angular.forEach(sources, function(v,i) {
if(v.kind === 'video') {
videos.push({ source: v, label: (v.label || 'camera ' + i)});
}
});
$scope.$apply(function() {
$scope.videos = videos;
});
});

EventService.getEvent($stateParams.eventName).success(function(result) {
$scope.event = result.event;
CheckInService.findAllTickets(result.event.id).success(function(tickets) {
Expand Down
5 changes: 5 additions & 0 deletions src/main/webapp/resources/js/jsqrcode/jsqrcode.min.js

Large diffs are not rendered by default.

0 comments on commit 24ad87e

Please sign in to comment.