PluginProbe ʕ •ᴥ•ʔ
Event Tickets with Ticket Scanner / 2.7.3
Event Tickets with Ticket Scanner v2.7.3
3.1.2 3.1.1 3.1.0 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.4 trunk 2.6.0 2.7.0 2.7.1 2.7.10 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.10 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.0 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 2.9.8 2.9.9 3.0.0 3.0.1 3.0.2 3.0.3
event-tickets-with-ticket-scanner / ticket_scanner.js
event-tickets-with-ticket-scanner Last commit date
3rd 1 year ago css 1 year ago img 1 year ago languages 1 year ago ticket 1 year ago vendors 1 year ago SASO_EVENTTICKETS.php 1 year ago backend.js 1 year ago changelog.txt 1 year ago db.php 1 year ago index.php 1 year ago init_file.php 1 year ago js_seatingplan.js 1 year ago order_details.js 1 year ago readme.txt 1 year ago saso-eventtickets-validator.js 1 year ago sasoEventtickets_AdminSettings.php 1 year ago sasoEventtickets_Authtoken.php 1 year ago sasoEventtickets_Base.php 1 year ago sasoEventtickets_Core.php 1 year ago sasoEventtickets_Frontend.php 1 year ago sasoEventtickets_Messenger.php 1 year ago sasoEventtickets_Options.php 1 year ago sasoEventtickets_PDF.php 1 year ago sasoEventtickets_Ticket.php 1 year ago sasoEventtickets_TicketBadge.php 1 year ago sasoEventtickets_TicketDesigner.php 1 year ago sasoEventtickets_TicketQR.php 1 year ago ticket_events.js 1 year ago ticket_scanner.js 1 year ago validator.js 1 year ago wc_backend.js 1 year ago wc_frontend.js 1 year ago woocommerce-hooks.php 1 year ago
ticket_scanner.js
1446 lines
1 jQuery(document).ready(()=>{
2 const { __, _x, _n, sprintf } = wp.i18n;
3 let system = {code:0 /* public ticket number */,
4 nonce:'', data:null /* retrieved data */, redeemed_successfully:false,
5 img_pfad:'',
6 last_scanned_ticket:{code:'', timestamp:0, auto_redeem:false, data:null},
7 last_nonce_check:0,
8 status:'ready' /* ready, retrieved, redeemed */
9 };
10 let myAjax;
11 if (typeof IS_PRETTY_PERMALINK_ACTIVATED === "undefined") {
12 IS_PRETTY_PERMALINK_ACTIVATED = false;
13 }
14
15 let rest_route = '/event-tickets-with-ticket-scanner/ticket/scanner/';
16 let pre_route = '../../../../..';
17 if (typeof Ajax_sasoEventtickets != "undefined" && Ajax_sasoEventtickets.wcTicketCompatibilityModeRestURL != '') {
18 pre_route = Ajax_sasoEventtickets.wcTicketCompatibilityModeRestURL.trim();
19 }
20
21 if (typeof Ajax_sasoEventtickets === "undefined") {
22 myAjax = {
23 url: pre_route + '/wp-json'+rest_route
24 };
25 system.nonce = NONCE;
26 } else {
27 myAjax = Ajax_sasoEventtickets;
28 system.nonce = myAjax.nonce;
29 if (Ajax_sasoEventtickets.wcTicketCompatibilityModeRestURL != "") {
30 myAjax.url = Ajax_sasoEventtickets.wcTicketCompatibilityModeRestURL.trim()+'/wp-json'+rest_route;
31 } else {
32 myAjax.url = myAjax._siteUrl+'/wp-json'+rest_route;
33 }
34 IS_PRETTY_PERMALINK_ACTIVATED = myAjax.IS_PRETTY_PERMALINK_ACTIVATED;
35 }
36 myAjax.rest_route = rest_route;
37 myAjax.non_pretty_permalink_url = pre_route+'/?rest_route='+myAjax.rest_route;
38
39 system.INPUTFIELD;
40 system.AUTHTOKENREMOVEBUTTON;
41 system.ADDITIONBUTTONS;
42 system.TIMEAREA;
43
44 function toBool(v) {
45 if (!v) return false;
46 if (v == "1") return true;
47 if (v == 1) return true;
48 if (v.toLowerCase() == "yes") return true;
49 return v == true;
50 }
51
52 var ticket_scanner_operating_option = {
53 redeem_auto: false,
54 distract_free: false,
55 distract_free_show_short_desc: false,
56 auth:"",
57 ticketScannerDontRememberCamChoice:toBool(myAjax.ticketScannerDontRememberCamChoice),
58 ticketScannerStartCamWithoutButtonClicked:false,
59 ticketScannerDontShowOptionControls:toBool(myAjax.ticketScannerDontShowOptionControls),
60 ticketScannerDontShowBtnPDF:toBool(myAjax.ticketScannerDontShowBtnPDF),
61 ticketScannerDontShowBtnBadge:toBool(myAjax.ticketScannerDontShowBtnBadge)
62 };
63
64 var loadingticket = false;
65 var div_ticket_info_area = null;
66 var div_order_info_area = null;
67
68 function addStyleCode(content, media) {
69 let c = document.createElement('style');
70 if (media) c.setAttribute("media", media);
71 c.innerHTML = content;
72 document.getElementsByTagName("head")[0].appendChild(c);
73 }
74
75 function onScanFailure(error) {
76 // handle scan failure, usually better to ignore and keep scanning.
77 // for example:
78 //console.warn(`Code scan error = ${error}`);
79 }
80 var html5QrcodeScanner = null;
81 var qrScanner = null;
82
83 function setStartCamWithoutButtonClicked(value) {
84 if (typeof value != "undefined") {
85 ticket_scanner_operating_option.ticketScannerStartCamWithoutButtonClicked = value;
86 } else {
87 ticket_scanner_operating_option.ticket_scanner_operating_option.ticketScannerStartCamWithoutButtonClicked = !ticket_scanner_operating_option.ticketScannerStartCamWithoutButtonClicked;
88 }
89 _storeValue("ticket_scanner_operating_option.ticketScannerStartCamWithoutButtonClicked", ticket_scanner_operating_option.ticketScannerStartCamWithoutButtonClicked ? 1 : 0);
90 }
91 function setRedeemImmediately(value) {
92 if (typeof value != "undefined") {
93 ticket_scanner_operating_option.redeem_auto = value;
94 } else {
95 ticket_scanner_operating_option.redeem_auto = !ticket_scanner_operating_option.redeem_auto;
96 }
97 _storeValue("ticket_scanner_operating_option.redeem_auto", ticket_scanner_operating_option.redeem_auto ? 1 : 0);
98 }
99 function setDistractFree(value) {
100 if (typeof value != "undefined") {
101 ticket_scanner_operating_option.distract_free = value;
102 } else {
103 ticket_scanner_operating_option.distract_free = !ticket_scanner_operating_option.distract_free;
104 }
105 _storeValue("ticket_scanner_operating_option.distract_free", ticket_scanner_operating_option.distract_free ? 1 : 0);
106 }
107 function setDistractFreeShowShortDesc(value) {
108 if (typeof value != "undefined") {
109 ticket_scanner_operating_option.distract_free_show_short_desc = value;
110 } else {
111 ticket_scanner_operating_option.distract_free_show_short_desc = !ticket_scanner_operating_option.distract_free_show_short_desc;
112 }
113 _storeValue("ticket_scanner_operating_option.distract_free_show_short_desc", ticket_scanner_operating_option.distract_free_show_short_desc ? 1 : 0);
114 }
115 function initAuthToken() {
116 let text = _loadValue("ticket_scanner_operating_option.auth");
117 if (system.PARA.auth) {
118 text = system.PARA.auth.trim();
119 }
120 if (text != "") {
121 try {
122 let json = JSON.parse(text);
123 setAuthToken(json, true);
124 } catch (e) {
125 alert(e);
126 }
127 }
128 }
129 function setAuthToken(token, doNotUpdateScanOption) {
130 // {"type":"auth","time":"2023-07-10 20:07:24","name":"saso","code":"AHR0CHM6LY92ZXJ3AWNRBHVUZY5KZS93B3JKCHJLC3M=_0C3C7AF3DCCD805F56EF02BEB9E39FFC","areacode":"ticketscanner","url":"https://verwicklung.de/wordpress/wp-content/plugins/event-tickets-with-ticket-scanner/ticket/"}
131 if (typeof token != "undefined" && typeof token.type != "undefined" && token.type == "auth") {
132 //ticket_scanner_operating_option.auth = token;
133 } else {
134 token = "";
135 }
136 ticket_scanner_operating_option.auth = token;
137 _storeValue("ticket_scanner_operating_option.auth", JSON.stringify(token));
138 if (!doNotUpdateScanOption) showScanOptions();
139 }
140 function onScanSuccess(decodedText, decodedResult) {
141 //if (decodedText) decodedText = decodedText.trim();
142 if (system.last_scanned_ticket.code == decodedText && system.last_scanned_ticket.timestamp + 10 > time()) {
143 return;
144 }
145 if (loadingticket) return;
146 loadingticket = true;
147 system.last_scanned_ticket = {code: decodedText, timestamp: time()};
148
149 if (qrScanner != null) {
150 //qrScanner.stop(); // faster if not executed
151 }
152
153 // store setting to cookies / or browser storage
154 if (!ticket_scanner_operating_option.ticketScannerDontRememberCamChoice && html5QrcodeScanner != null) {
155 _storeValue("ticketScannerCameraId", html5QrcodeScanner.persistedDataManager.data.lastUsedCameraId, 365);
156 }
157
158 updateTicketScannerInfoArea("<center>"+sprintf(/* translators: %s: ticket number */__("found %s", 'event-tickets-with-ticket-scanner'), decodedText)+'</center>');
159 // handle the scanned code as you like, for example:
160 //console.log(`Code matched = ${decodedText}`, decodedResult);
161 $("#reader_output").html(__("...loading...", 'event-tickets-with-ticket-scanner'));
162 //window.location.href = "?code="+encodeURIComponent(decodedText) + (ticket_scanner_operating_option.redeem_auto ? "&redeemauto=1" : "");
163
164 let token = null;
165 try {
166 token = JSON.parse(decodedText);
167 } catch(e) {}
168 if (token != null && typeof token == "object") {
169 if (token.type && token.type == "auth") {
170 setAuthToken(token);
171 clearAreas();
172 $("#reader_output").html('');
173 updateTicketScannerInfoArea('<h1 style="color:green !important;">'+__("Auth Token Set", 'event-tickets-with-ticket-scanner')+'</h3>');
174 window.setTimeout(()=>{
175 showScanNextTicketButton();
176 }, 350);
177 } else {
178 renderInfoBox(__("Scan error", 'event-tickets-with-ticket-scanner'), __("QR code content unknown. Can not extract data correctly. Please try a QR code of a ticket.", 'event-tickets-with-ticket-scanner'), showScanNextTicketButton);
179 }
180 } else {
181 /*
182 // not working with QRScanner? or the other scanner. Somehow content is not recognized correctly or not send. maybe a config value to be set. Because with text in it, the scanner is returning an empty string
183 // extract the public ticket number from the token. format is CRC32(TIMESTAMP)-ORDERID-TICKETNUMBER.
184 // the public ticket number can be part of text in the qr code, so we need to extract it.
185 if (decodedText.length > 12) {
186 debugger;
187 // format: NUMBER-NUMBER-TICKETNUMBER , TICKETNUMBER can be text and numbers
188 // example: 2523448324-671-ticket_2025052808_dc_XYJBSSAZGZBHENY
189 reg = /\b\d+-\d+-[A-Za-z0-9_]+\b/g;
190 console.log("decodedText: "+decodedText);
191 let matches = decodedText.match(reg);
192 if (matches && matches.length > 3) {
193 decodedText = matches[0]; // the ticket number is the third match
194 }
195 console.log("extracted ticket number from QR code: "+decodedText);
196 retrieveTicket(decodedText);
197 } else {
198 if (decodedText != "") {
199 renderInfoBox(__("Scan error", 'event-tickets-with-ticket-scanner'), "Cannot find the public ticket number in the QR code. Please try a QR code of a ticket.", showScanNextTicketButton);
200 }
201 }
202 */
203 if (decodedText != "") {
204 retrieveTicket(decodedText);
205 } else {
206 renderInfoBox(__("Scan error", 'event-tickets-with-ticket-scanner'), __("Cannot find the public ticket number in the QR code. Please try a QR code of a ticket.", 'event-tickets-with-ticket-scanner'), showScanNextTicketButton);
207 }
208 }
209
210 if (html5QrcodeScanner != null) {
211 window.setTimeout(()=>{
212 html5QrcodeScanner.clear().then((ignore) => {
213 // QR Code scanning is stopped.
214 // reload the page with the ticket info and redeem button
215 //console.log("stop success");
216 }).catch((err) => {
217 // Stop failed, handle it.
218 //console.log("stop failed");
219 });
220 }, 250);
221 }
222 }
223
224 function startScanner() {
225 if (!ticket_scanner_operating_option.redeem_auto) updateTicketScannerInfoArea("");
226 $("#reader_output").html("");
227 loadingticket = false;
228
229 if (system.PARA.useoldticketscanner) {
230 startScanner_html5QrcodeSCanner();
231 } else {
232 startScanner_QRScanner();
233 }
234 }
235 function startScanner_QRScanner() {
236 let deviceId = _loadValue("ticketScannerCameraId");
237 let v_id = 'saso_eventtickets_qr-video';
238 let camlist_id = 'saso_eventtickets_camList';
239 let start_cam = false;
240 if (document.getElementById(v_id) == null) {
241 $("#reader").html("");
242 start_cam = true;
243 $('#reader').append('<video id="'+v_id+'" style="width:100%" disablepictureinpicture playsinline></video>');
244 $('<select id="'+camlist_id+'" style="width: 100%;"></select>').appendTo($('#reader')).on("change", event=>{
245 _storeValue("ticketScannerCameraId", event.target.value, 365);
246 qrScanner.setCamera(event.target.value);//.then(updateFlashAvailability);
247 });
248 let btn = $('<button>').text("Stop Camera").appendTo($('#reader')).on("click", event=>{
249 qrScanner.stop();
250 qrScanner.destroy();
251 qrScanner = null;
252 btn.css("display", "none");
253 btn_start.css("display", "block");
254 });
255
256 // flashlight button
257 /*
258 https://github.com/nimiq/qr-scanner
259 Flashlight support
260 On supported browsers, you can check whether the currently used camera has a flash and turn it on or off. Note that hasFlash should be called after the scanner was successfully started to avoid the need to open a temporary camera stream just to query whether it has flash support, potentially asking the user for camera access.
261
262 qrScanner.hasFlash(); // check whether the browser and used camera support turning the flash on; async.
263 qrScanner.isFlashOn(); // check whether the flash is on
264 qrScanner.turnFlashOn(); // turn the flash on if supported; async
265 qrScanner.turnFlashOff(); // turn the flash off if supported; async
266 qrScanner.toggleFlash(); // toggle the flash if supported; async.
267 */
268
269 let btn_start = $('<button class="button-ticket-options button-primary" style="display:none;">').text(__("Start Camera", 'event-tickets-with-ticket-scanner')).appendTo($('#reader')).on("click", event=>{
270 btn_start.css("display", "none");
271 btn.css("display", "block");
272 startScanner();
273 });
274 }
275
276 if (qrScanner != null) {
277 qrScanner.stop();
278 qrScanner.destroy();
279 }
280 qrScanner = new QrScanner(
281 document.getElementById(v_id),
282 result => {
283 onScanSuccess(result.data, result);
284 },
285 { highlightScanRegion: true,
286 highlightCodeOutline: true,
287 willReadFrequently:true,
288 /* your options or returnDetailedScanResult: true if you're not specifying any other options */ }
289 );
290
291 if (deviceId != null && deviceId != "" && !ticket_scanner_operating_option.ticketScannerDontRememberCamChoice) {
292 qrScanner.setCamera(deviceId);
293 }
294
295 if (start_cam) {
296 qrScanner.start().then(() => {
297 //updateFlashAvailability();
298 // List cameras after the scanner started to avoid listCamera's stream and the scanner's stream being requested
299 // at the same time which can result in listCamera's unconstrained stream also being offered to the scanner.
300 // Note that we can also start the scanner after listCameras, we just have it this way around in the demo to
301 // start the scanner earlier.
302 QrScanner.listCameras(true).then(cameras => cameras.forEach(camera => {
303 const option = document.createElement('option');
304 option.value = camera.id;
305 option.text = camera.label;
306 if (camera.id == deviceId) {
307 option.selected = true;
308 }
309 $('#'+camlist_id).append(option);
310 }));
311 });
312 } else {
313 qrScanner.start();
314 }
315 }
316 function startScanner_html5QrcodeSCanner() {
317 if (html5QrcodeScanner == null) {
318 let options = { fps: 25, qrbox: {width: 250, height: 250} };
319 let deviceId = _loadValue("ticketScannerCameraId");
320 if (deviceId != null && deviceId != "" && !ticket_scanner_operating_option.ticketScannerDontRememberCamChoice) {
321 options.deviceId = {exact: deviceId}; // deviceId: { exact: cameraId}
322 }
323 html5QrcodeScanner = new Html5QrcodeScanner("reader",
324 options,
325 /* verbose= */ false);
326 }
327 //html5QrcodeScanner.render(onScanSuccess, onScanFailure);
328 html5QrcodeScanner.render(onScanSuccess);
329 window.qrs = html5QrcodeScanner;
330 }
331
332 function showScanNextTicketButton() {
333 let skip = ticket_scanner_operating_option.ticketScannerStartCamWithoutButtonClicked;
334 let div = $('<div>');
335 $('#reader').css("border", "none").html(div);
336 if (skip) {
337 startScanner();
338 } else {
339 let btngrp = $('<div>').css("text-align", 'center').appendTo(div);
340 $('<button class="button-ticket-options button-primary">').html(__("Scan next Ticket", 'event-tickets-with-ticket-scanner')).on("click", e=>{
341 clearAreas();
342 clearOrderInfos();
343 startScanner();
344 }).appendTo(btngrp);
345 if (system.status == "retrieved") {
346 let btn_redeem = $('<button class="button-ticket-options">').html(_x('Redeem Ticket', 'label', 'event-tickets-with-ticket-scanner')).css("background-color", 'gray').css('color', 'white').prop("disabled", true).on('click', e=>{
347 redeemTicket(system.code);
348 }).appendTo(btngrp);
349 if (canTicketBeRedeemed(system.last_scanned_ticket.data)) {
350 btn_redeem.prop("disabled", false).css('background-color','green');
351 }
352 }
353 if (qrScanner != null) {
354 $('<button class="button-ticket-options">').html(__("Stop camera", 'event-tickets-with-ticket-scanner')).on("click", e=>{
355 qrScanner.stop();
356 qrScanner.destroy();
357 qrScanner = null;
358 $(e.target).css("display", "none");
359 }).appendTo(btngrp);
360 }
361 }
362 }
363 function showScanOptions() {
364 let div = $('<div>');
365 if (!ticket_scanner_operating_option.ticketScannerDontShowOptionControls) {
366 let chkbox_redeem_imediately = $('<input type="checkbox">').on("click", e=>{
367 setRedeemImmediately();
368 }).appendTo(div);
369 if (ticket_scanner_operating_option.redeem_auto) chkbox_redeem_imediately.prop("checked", true);
370 div.append(' '+__("Scan and Redeem immediately", 'event-tickets-with-ticket-scanner'));
371 div.append("<br>");
372
373 let chkbox_distractfree = $('<input type="checkbox">').on("click", e=>{
374 setDistractFree();
375 if (ticket_scanner_operating_option.distract_free) {
376 $('#ticket_info').css("display", "none");
377 } else {
378 $('#ticket_info').css("display", "block");
379 }
380 }).appendTo(div);
381 if (ticket_scanner_operating_option.distract_free) chkbox_distractfree.prop("checked", true);
382 div.append(' '+__("Hide ticket information", 'event-tickets-with-ticket-scanner'));
383 div.append("<br>");
384
385 let chkbox_distractfree_showshortdesc = $('<input type="checkbox">').on("click", e=>{
386 setDistractFreeShowShortDesc();
387 if (system.status == "retrieved") {
388 displayTicketRetrievedInfo(system.last_scanned_ticket.data);
389 } else if (system.status == "redeemed") {
390 displayTicketRedeemedInfo(system.data);
391 }
392 }).appendTo(div);
393 if (ticket_scanner_operating_option.distract_free_show_short_desc) chkbox_distractfree_showshortdesc.prop("checked", true);
394 div.append(' '+__("Display short description if ticket information is hidden", 'event-tickets-with-ticket-scanner'));
395 div.append("<br>");
396
397
398 let chkbox_ticketScannerStartCamWithoutButtonClicked = $('<input type="checkbox">').on("click", e=>{
399 setStartCamWithoutButtonClicked(!ticket_scanner_operating_option.ticketScannerStartCamWithoutButtonClicked);
400 }).appendTo(div);
401 chkbox_ticketScannerStartCamWithoutButtonClicked.prop("checked", ticket_scanner_operating_option.ticketScannerStartCamWithoutButtonClicked);
402 div.append(' '+__("Start cam to scan next ticket immediately", 'event-tickets-with-ticket-scanner'));
403 div.append("<br>");
404
405 $('<input type="checkbox">').on("click", e=>{
406 window.location.href = "?code="+encodeURIComponent(system.code)
407 + (ticket_scanner_operating_option.redeem_auto ? "&redeemauto=1" : "")
408 + (system.PARA.useoldticketscanner ? "" : "&useoldticketscanner=1");
409 }).prop("checked", system.PARA.useoldticketscanner).appendTo(div);
410 div.append(' '+__("Use old ticket scanner library - compatibility mode", 'event-tickets-with-ticket-scanner'));
411 }
412
413 $('<div style="margin-top:40px;">').append(system.INPUTFIELD).appendTo(div);
414 if (typeof ticket_scanner_operating_option.auth == "object") div.append(system.AUTHTOKENREMOVEBUTTON);
415 div.append(system.ADDITIONBUTTONS);
416 system.TIMEAREA = $('<div>');
417 div.append(system.TIMEAREA);
418 $('#reader_options').html(div);
419 }
420
421 function addMetaTag(name, content) {
422 let head = document.getElementsByTagName("head")[0];
423 let metaTags = head.getElementsByTagName("meta");
424 let contains = false;
425 for (let i=0;i<metaTags.length;i++) {
426 let tag = metaTags[i];
427 if (tag.name == name) {
428 tag.content = content;
429 contains = true;
430 break;
431 }
432 }
433 if (!contains) {
434 let metaTag = document.createElement("meta");
435 metaTag.name = name;
436 metaTag.content = content;
437 head.appendChild(metaTag);
438 }
439 }
440
441 function _storeValue(name, wert, days) {
442 if (window.JAVAJSBridge && window.JAVAJSBridge.setItem) window.JAVAJSBridge.setItem(name, wert);
443 else setCookie(name, wert, days);
444 }
445 function _loadValue(name) {
446 if (window.JAVAJSBridge && window.JAVAJSBridge.getItem) return window.JAVAJSBridge.getItem(name);
447 return getCookie(name);
448 }
449 function setCookie(cname, cvalue, exdays) {
450 var d = new Date();
451 if (!exdays) exdays = 30;
452 d.setTime(d.getTime() + (exdays * 24 * 60 * 60 * 1000));
453 var expires = "expires="+d.toUTCString();
454 document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
455 }
456 function getCookie(cname) {
457 var name = cname + "=";
458 var ca = document.cookie.split(';');
459 for(var i = 0; i < ca.length; i++) {
460 var c = ca[i];
461 while (c.charAt(0) === ' ') {
462 c = c.substring(1);
463 }
464 if (c.indexOf(name) === 0) {
465 return c.substring(name.length, c.length);
466 }
467 }
468 return "";
469 }
470 function _getURLAndDateForAjax(action, myData, pcbf) {
471 let _data = {};
472 _data.action = action;
473 _data.t = new Date().getTime();
474 //if (system.nonce != '') _data._wpnonce = system.nonce;
475 if (system.nonce != '') _data.nonce = system.nonce;
476 pcbf && pcbf();
477 //if (myData) for(var key in myData) _data['data['+key+']'] = myData[key];
478 if (myData) for(var key in myData) _data[key] = myData[key];
479 if (ticket_scanner_operating_option && ticket_scanner_operating_option.auth && ticket_scanner_operating_option.auth.code && ticket_scanner_operating_option.auth.code != "") {
480 let key = "auth";
481 if (Ajax_sasoEventtickets && Ajax_sasoEventtickets._params && Ajax_sasoEventtickets._params.auth) key = Ajax_sasoEventtickets._params.auth;
482 _data[key] = ticket_scanner_operating_option.auth.code;
483 }
484 if (system.nonce != '') {
485 $.ajaxSetup({
486 beforeSend: function(xhr) {
487 xhr.setRequestHeader('X-WP-Nonce', system.nonce);
488 },
489 });
490 }
491
492 let url = myAjax.url;
493 if (IS_PRETTY_PERMALINK_ACTIVATED == false) {
494 url = myAjax.non_pretty_permalink_url;
495 }
496 url += action;
497 return {url:url, data:_data};
498 }
499 function _downloadFile(action, myData, filenameToStore, cbf, ecbf, pcbf) {
500 let call_data = _getURLAndDateForAjax(action, myData, pcbf);
501 let params = "";
502 for(let key in call_data.data) {
503 params += key+"="+encodeURIComponent(call_data.data[key])+"&";
504 }
505 let url = call_data.url+'?'+params;
506 //window.location.href = url;
507 ajax_downloadFile(url, filenameToStore, cbf);
508 }
509 function ajax_downloadFile(urlToSend, fileName, cbf) {
510 var req = new XMLHttpRequest();
511 req.open("GET", urlToSend, true);
512 req.responseType = "blob";
513 req.onload = function (event) {
514 var blob = req.response;
515 //var fileName = req.getResponseHeader("X-fileName") //if you have the fileName header available
516 var link=document.createElement('a');
517 link.href=window.URL.createObjectURL(blob);
518 link.download=fileName;
519 link.click();
520 cbf && cbf();
521 };
522
523 req.send();
524 }
525 function _makeGet(action, myData, cbf, ecbf, pcbf) {
526 let call_data = _getURLAndDateForAjax(action, myData, pcbf);
527 //console.log(call_data);
528 $.get( call_data.url, call_data.data, response=>{
529 if (typeof response == "string") {
530 response = JSON.parse(response);
531 }
532 if (response && response.data && response.data.nonce) {
533 system.last_nonce_check = new Date().getTime();
534 system.nonce = response.data.nonce;
535 }
536 if (!response.success) {
537 if (ecbf) ecbf(response);
538 else {
539 let msg = (typeof response.data !== "undefined" && response.data.status ? response.data.status : '') + " " + (response.data.message ? response.data.message : '');
540 renderFatalError(msg.trim());
541 }
542 } else {
543 cbf && cbf(response.data);
544 }
545 }, "json").always(jqXHR=>{
546 if(jqXHR.status == 401 || jqXHR.status == 403) {
547 renderFatalError(__("Access rights missing. Please login first.", 'event-tickets-with-ticket-scanner') + " "+(jqXHR.responseJSON && jqXHR.responseJSON.message ? jqXHR.responseJSON.message : '') );
548 }
549 if(jqXHR.status == 400) {
550 renderFatalError(jqXHR.responseJSON.message);
551 }
552 });
553 }
554 function _makePost(action, myData, cbf, ecbf, pcbf) {
555 let call_data = _getURLAndDateForAjax(action, myData, pcbf);
556 $.post( call_data.url, call_data.data, response=>{
557 if (typeof response == "string") {
558 response = JSON.parse(response);
559 }
560 if (response && response.data && response.data.nonce) {
561 system.last_nonce_check = new Date().getTime();
562 system.nonce = response.data.nonce;
563 }
564 if (!response.success) {
565 if (ecbf) ecbf(response);
566 else {
567 let msg = (response.data.status ? response.data.status : '') + " " + (response.data.message ? response.data.message : '');
568 renderFatalError(msg.trim());
569 }
570 } else {
571 cbf && cbf(response.data);
572 }
573 }, "json").always(jqXHR=>{
574 if(jqXHR.status == 401 || jqXHR.status == 403) {
575 renderFatalError(__("Access rights missing. Please login first.", 'event-tickets-with-ticket-scanner') + " " + (jqXHR.responseJSON && jqXHR.responseJSON.message ? jqXHR.responseJSON.message : '') );
576 }
577 if(jqXHR.status == 400) {
578 renderFatalError(jqXHR.responseJSON.message);
579 }
580 });
581 }
582 function _getSpinnerHTML() {
583 return '<span class="lds-dual-ring"></span>';
584 }
585 function makeDateFromString(timestring, timezone_id) {
586 let d = new Date(timestring);
587 return new Date(d.toLocaleString('en', {timeZone: timezone_id}));
588 }
589 function makeDate(timestamp, timezone_id) {
590 let d = new Date();
591 d.setTime(timestamp);
592 return new Date(d.toLocaleString('en', {timeZone: timezone_id}));
593 }
594 function time(timezone_id, timestamp) {
595 let d = new Date();
596 if (timestamp) {
597 d.setTime(timestamp);
598 }
599 if (timezone_id && timezone_id.indexOf("/") > 0) {
600 d = new Date(d.toLocaleString('en', {timeZone: timezone_id}));
601 }
602 return parseInt(d.getTime() / 1000);
603 }
604 function parseDate(str){
605 if (!str) return null;
606 return new Date(str.split(' ')[0].replace(/-/g,"/"));
607 }
608 function parseDateAndText(str, format) {
609 return Date2Text(parseDate(str).getTime(), format);
610 }
611 function DateTime2Text(millisek) {
612 return Date2Text(millisek, system.format_datetime ? system.format_datetime : "d.m.Y H:i");
613 }
614 function Date2Text(millisek, format, timezone_id) {
615 if (!millisek)
616 millisek = time(timezone_id) * 1000;
617 var d = new Date(millisek);
618 if (!format)
619 //format = system.format_date ? system.format_date : "%d.%m.%Y";
620 format = system.format_date ? system.format_date : "d.m.Y";
621 //format = "%d.%m.%Y %H:%i";
622 var tage = [
623 _x('Sun', 'cal', 'event-tickets-with-ticket-scanner'),
624 _x('Mon', 'cal', 'event-tickets-with-ticket-scanner'),
625 _x('Tue', 'cal', 'event-tickets-with-ticket-scanner'),
626 _x('Wed', 'cal', 'event-tickets-with-ticket-scanner'),
627 _x('Thu', 'cal', 'event-tickets-with-ticket-scanner'),
628 _x('Fri', 'cal', 'event-tickets-with-ticket-scanner'),
629 _x('Sat', 'cal', 'event-tickets-with-ticket-scanner')
630 ];
631 var monate = [
632 _x('Jan', 'cal', 'event-tickets-with-ticket-scanner'),
633 _x('Feb', 'cal', 'event-tickets-with-ticket-scanner'),
634 _x('Mar', 'cal', 'event-tickets-with-ticket-scanner'),
635 _x('Apr', 'cal', 'event-tickets-with-ticket-scanner'),
636 _x('May', 'cal', 'event-tickets-with-ticket-scanner'),
637 _x('Jun', 'cal', 'event-tickets-with-ticket-scanner'),
638 _x('Jul', 'cal', 'event-tickets-with-ticket-scanner'),
639 _x('Aug', 'cal', 'event-tickets-with-ticket-scanner'),
640 _x('Sep', 'cal', 'event-tickets-with-ticket-scanner'),
641 _x('Oct', 'cal', 'event-tickets-with-ticket-scanner'),
642 _x('Nov', 'cal', 'event-tickets-with-ticket-scanner'),
643 _x('Dec', 'cal', 'event-tickets-with-ticket-scanner')
644 ];
645 var formate = {'d':d.getDate()<10?'0'+d.getDate():d.getDate(),
646 'j':d.getDate(),'D':tage[d.getDay()],'w':d.getDate(),'m':d.getMonth()+1<10?'0'+(d.getMonth()+1):d.getMonth()+1,'M':monate[d.getMonth()],
647 'n':d.getMonth()+1,'Y':d.getFullYear(),'y':d.getYear()>100?d.getYear().toString().substring(d.getYear().toString().length-2):d.getYear(),
648 'H':d.getHours()<10?'0'+d.getHours():d.getHours(),'h':d.getHours()>12?d.getHours()-12:d.getHours(),
649 'i':d.getMinutes()<10?'0'+d.getMinutes():d.getMinutes(),'s':d.getSeconds()<10?'0'+d.getSeconds():d.getSeconds()
650 };
651 for (var akey in formate) {
652 //var rg = new RegExp('%'+akey, "g");
653 var rg = new RegExp(akey, "g");
654 format = format.replace(rg, formate[akey]);
655 }
656 return format;
657 }
658 function renderInfoBox(title, content, cbf) {
659 let _options = {
660 title: title,
661 modal: true,
662 minWidth: 400,
663 minHeight: 200,
664 buttons: [{text:_x('Ok', 'label', 'event-tickets-with-ticket-scanner'), click:function(){
665 $(this).dialog("close");
666 $(this).html("");
667 clearAreas();
668 $('#ticket_info').html(content);
669 $('#reader').html("");
670 if (cbf) cbf();
671 }}]
672 };
673 if (typeof content !== "string") content = JSON.stringify(content);
674 let dlg = $('<div/>').html(content);
675 dlg.dialog(_options);
676 return dlg;
677 }
678 function renderFatalError(content, cbf) {
679 return renderInfoBox('Error', content, cbf);
680 }
681 function basics_ermittelURLParameter() {
682 var parawerte = {};
683 var teile;
684 if (window.location.search !== "") {
685 teile = window.location.search.substring(1).split("&");
686 for (var a=0;a<teile.length;a++)
687 {
688 var pos = teile[a].indexOf("=");
689 if (pos < 0) {
690 parawerte[teile[a]] = true;
691 } else {
692 var key = teile[a].substring(0,pos);
693 parawerte[key] = decodeURIComponent(teile[a].substring(pos+1));
694 }
695 }
696 }
697 return parawerte;
698 }
699 function clearOrderInfos() {
700 $('#order_info').html("");
701 }
702 function clearAreas() {
703 $('#ticket_info_btns').html('');
704 $('#ticket_add_info').html('');
705 $('#ticket_info').html('');
706 updateTicketScannerInfoArea('');
707 $('#ticket_info_retrieved').html('');
708 }
709 function retrieveTicket(code, redeemed, cbf) {
710 clearAreas();
711 window.scrollBy(0,0);
712 let div = $('#ticket_info').html(_getSpinnerHTML());
713 div.css("display", "block");
714
715 // check if the code is URL
716 if (code.length > 12) {
717 if (code.substring(0,5).toLowerCase() == "https") {
718 if (code.substring(0,8).toLowerCase() == "https://" || (code.length > 14 && code.substring(0,14).toLowerCase() == "https%3A%2F%2F")) {
719 // extract code from URL
720 let url = code;
721 let pos = url.lastIndexOf("code=");
722 if (pos > 0) {
723 code = url.substring(pos + 5);
724 } else {
725 pos = url.toLowerCase().lastIndexOf("code%3d");
726 if (pos > 0) {
727 code = url.substring(pos + 7);
728 }
729 }
730 }
731 }
732 }
733 if (code == "") {
734 alert(__("no code found", 'event-tickets-with-ticket-scanner'));
735 showScanNextTicketButton();
736 return;
737 }
738
739 let redeem = ticket_scanner_operating_option.redeem_auto;
740 if (redeemed == true) {
741 redeem = false; // is already redeemed
742 }
743 system.last_scanned_ticket.auto_redeem = redeem;
744 _makeGet('retrieve_ticket', {'code':code, 'redeem':redeem ? 1 : 0}, data=>{
745 if (ticket_scanner_operating_option.distract_free) {
746 div.css("display", "none");
747 }
748 system.status = "retrieved";
749 system.data = data;
750 system.last_scanned_ticket.data = data;
751 system.code = code; // falls per code überschrieben wurde
752
753 if (typeof data.order_infos !== "undefined" && data.order_infos.is_order_ticket) {
754 system.format_datetime = data.option_displayDateTimeFormat;
755 system.format_date = data.option_displayDateFormat;
756 system.format_time = data.option_displayTimeFormat;
757 displayOrderTicketInfo(data);
758 showScanNextTicketButton();
759 } else {
760 system.format_datetime = data._ret.option_displayDateTimeFormat;
761 system.format_date = data._ret.option_displayDateFormat;
762 system.format_time = data._ret.option_displayTimeFormat;
763 displayTicketInfo(data);
764 displayTicketRetrievedInfo(data);
765 displayTicketAdditionalInfos(data);
766
767 $("#reader_output").html("");
768 if(!redeemed && ticket_scanner_operating_option.redeem_auto && typeof data._ret.redeem_operation !== "undefined") {
769 // display redeem operation
770 //redeemTicket(code);
771 displayRedeemedInfo(code, data._ret.redeem_operation);
772 } else {
773 showScanNextTicketButton();
774 }
775 }
776
777 cbf && cbf();
778 }, response=>{
779 clearAreas();
780 $("#reader_output").html('');
781 updateTicketScannerInfoArea('<h1 style="color:red !important;">'+response.data+'</h3>');
782 showScanNextTicketButton();
783 cbf && cbf();
784 });
785 }
786 function isTicketExpired(ticketRetObject) {
787 if (ticketRetObject.is_expired) return true;
788 return false;
789 }
790 function isRedeemTooEarly(data) {
791 if (data._ret._options.wcTicketDontAllowRedeemTicketBeforeStart) {
792 return data._ret._options.isRedeemOperationTooEarly;
793 }
794 return false;
795 }
796 function isRedeemTooLate(data) {
797 if (data._ret._options.wsticketDenyRedeemAfterstart) {
798 return data._ret._options.isRedeemOperationTooLate;
799 }
800 return false;
801 }
802 function isRedeemTooLateEndEvent(data) {
803 if (data._ret._options.wcTicketAllowRedeemTicketAfterEnd == false) {
804 return data._ret._options.isRedeemOperationTooLateEventEnded;
805 }
806 return false;
807 }
808
809 function canTicketBeRedeemedNow(data) {
810 if (isRedeemTooEarly(data)) return false;
811 if (isRedeemTooLateEndEvent(data)) return false;
812 if (isRedeemTooLate(data)) return false;
813 if (isTicketExpired(data._ret)) return false;
814 return true;
815 }
816 function displayTicketRetrievedInfo(data) {
817 let div = $('<div>').css("text-align", "center");
818 let metaObj = data.metaObj
819 let is_expired = isTicketExpired(data._ret);
820 if (!data._ret.is_paid) {
821 $('<h4 style="color:red !important;">').html(sprintf(/* translators: %s: order status */__('Ticket is NOT paid (%s).', 'label', 'event-tickets-with-ticket-scanner'),data._ret.order_status)).appendTo(div);
822 } else {
823 if (is_expired == false && metaObj['wc_ticket']['redeemed_date'] != "") {
824 let color = "red";
825 if (data._ret.max_redeem_amount > 1 && data.metaObj.wc_ticket.stats_redeemed.length < data._ret.max_redeem_amount) {
826 color = "green";
827 }
828 //if (system.last_scanned_ticket.auto_redeem == false) {
829 if (system.redeemed_successfully) {
830 $('<h4 style="color:'+color+' !important;">').html(data._ret.msg_redeemed).appendTo(div);
831 }
832 if (metaObj.wc_ticket.redeemed_date != '') {
833 div.append(data._ret.redeemed_date_label+' '+metaObj['wc_ticket']['redeemed_date']);
834 }
835 } else {
836 if (is_expired) {
837 div.append('<div style="color:red;">'+data._ret.msg_ticket_expired+'</div>');
838 div.append(data._ret.ticket_date_as_string);
839 } else {
840 if (data._ret.ticket_end_date == "" || data._ret.ticket_end_date_timestamp > time()) {
841 div.append('<div style="color:green;">'+data._ret.msg_ticket_valid+'</div>');
842 }
843 }
844 }
845
846 if (ticket_scanner_operating_option.distract_free) {
847 // display ticket title and subtitle
848 if (typeof data._ret.ticket_title != "undefined" && data._ret.ticket_title != "") {
849 div.append('<h4>'+data._ret.ticket_title+'</h4>');
850 }
851 if (typeof data._ret.ticket_subtitle != "undefined" && data._ret.ticket_subtitle != "") {
852 div.append('<h5>'+data._ret.ticket_subtitle+'</h5>');
853 }
854
855 // display short description and ticket_info
856 if (ticket_scanner_operating_option.distract_free_show_short_desc && typeof data._ret.short_desc != "undefined" && data._ret.short_desc != "") {
857 div.append('<div>'+data._ret.short_desc+'</div>');
858 }
859 if (typeof data._ret.ticket_info != "undefined" && data._ret.ticket_info != "") {
860 //div.append('<div>'+data._ret.ticket_info+'</div>');
861 }
862 console.log(data._ret);
863 }
864 if (is_expired == false) {
865 let _isRedeemTooLate = isRedeemTooLate(data);
866 let _isRedeemTooLateEndEvent = isRedeemTooLateEndEvent(data);
867 if (!canTicketBeRedeemedNow(data)) {
868 let error_msg = data._ret.msg_ticket_not_valid_yet;
869 if(_isRedeemTooLateEndEvent) {
870 error_msg = data._ret.msg_ticket_event_ended;
871 } else if(_isRedeemTooLate) {
872 error_msg = data._ret.msg_ticket_not_valid_anymore;
873 }
874 div.append('<div style="color:red;">'+error_msg+'</div>');
875 }
876 if (_isRedeemTooLate == false && _isRedeemTooLateEndEvent == false && data._ret._options.wcTicketDontAllowRedeemTicketBeforeStart) {
877 if (typeof data._ret.redeem_allowed_from != "undefined" && typeof data._ret.is_date_set != "undefined" && data._ret.is_date_set) {
878 //div.append("<div>Redeem allowed from: <b>"+data._ret.redeem_allowed_from+"</b></div>");
879 div.append('<div style="display: flex;flex-wrap: wrap;flex-direction: column;"><div>Redeem allowed from: </div><div style="font-weight:bold;">'+data._ret.redeem_allowed_from+'</div></div>');
880 }
881 }
882 }
883 }
884 $('#ticket_info_retrieved').html(div);
885 }
886 function displayTicketAdditionalInfos(data) {
887 let div = $('<div style="width:50%;display:inline-block;">');
888 if (data._ret.is_paid) {
889 $('<div>').html('<b>'+__('Ticket paid', 'event-tickets-with-ticket-scanner')+'</b>').css("color", "green").appendTo(div);
890 } else {
891 $('<div>').html(__('Ticket NOT paid', 'event-tickets-with-ticket-scanner')).css("color", "red").appendTo(div);
892 }
893 if (data.metaObj.wc_ticket.redeemed_date != "") {
894 $('<div>').html(__('Ticket is already redeemed', 'event-tickets-with-ticket-scanner')).appendTo(div);
895 } else {
896 $('<div>').html(__('Ticket not redeemed', 'event-tickets-with-ticket-scanner')).appendTo(div);
897 }
898 if (data._ret._options.displayConfirmedCounter) {
899 $('<div>').html(sprintf(/* translators: %s: confirmed check counter */__('Confirmed status validation check counter: <b>%s</b>', 'event-tickets-with-ticket-scanner'), data.metaObj.confirmedCount)).appendTo(div);
900 }
901 $('<div>').html(sprintf(/* translators: %s: max redeem amount */__('Max Redeem Amount for this ticket: <b>%s</b>', 'event-tickets-with-ticket-scanner'), data._ret.max_redeem_amount)).appendTo(div);
902 if(data._ret.max_redeem_amount > 1) {
903 $('<div>').html(sprintf(/* translators: 1: redeemd tickets 2: max redeem */__('Redeem usage: <b>%1$d</b> of <b>%2$d</b>', 'event-tickets-with-ticket-scanner'), data.metaObj.wc_ticket.stats_redeemed.length, data._ret.max_redeem_amount)).appendTo(div);
904 }
905
906 let div2 = $('<div style="width:50%;display:inline-block;">');
907 if (data._ret._options.wcTicketDontAllowRedeemTicketBeforeStart && typeof data._ret.is_date_set != "undefined" && data._ret.is_date_set) {
908 //if (data._ret._options.isRedeemOperationTooEarly) {
909 div2.append($('<div>').html(sprintf(/* translators: %s: date */__('Redeemable from %s', 'event-tickets-with-ticket-scanner'), data._ret.redeem_allowed_from)));
910 //}
911 }
912 if (typeof data._ret.redeem_allowed_until != "undefined" && typeof data._ret.is_date_set != "undefined" && data._ret.is_date_set) {
913 div2.append($('<div>').html(sprintf(/* translators: %s: date */__('Redeemable until %s', 'event-tickets-with-ticket-scanner'), data._ret.redeem_allowed_until)));
914 }
915
916 if (data.metaObj.woocommerce.creation_date != "") {
917 div2.append('<div>'+sprintf(/* translators: %s: date */__('Bought at %s', 'event-tickets-with-ticket-scanner'), DateTime2Text(new Date(data.metaObj.woocommerce.creation_date).getTime()))+'</div>');
918 }
919
920 let is_expired = isTicketExpired(data._ret);
921 if (typeof data.metaObj.expiration != "undefined") {
922 if (data.metaObj.expiration.date != "") {
923 div2.append('<div'+(is_expired ? ' style="font-weight:bold;"' : '')+'>'+sprintf(/* translators: %s: date */__('Expiration at %s', 'event-tickets-with-ticket-scanner'), DateTime2Text(new Date(data.metaObj.expiration.date).getTime()))+'</div>');
924 } else {
925 let date_expiration_ms = new Date(data.metaObj.woocommerce.creation_date).getTime();
926 date_expiration_ms += data.metaObj.expiration.days * 24 * 3600 * 1000;
927 let exp_text = data.metaObj.expiration.days > 0 ? sprintf(/* translators: 1: days 2: date */__('Expires after %1$d days (%2$s)', 'event-tickets-with-ticket-scanner'), data.metaObj.expiration.days, DateTime2Text( date_expiration_ms )) : '';
928 if (exp_text != "") {
929 div2.append('<div>'+exp_text+'</div>');
930 }
931 }
932 }
933
934 let div3 = $('<div>');
935 if (typeof data._ret.product !== "undefined") {
936 let product_name = data._ret.product.name + (data._ret.product.name_variant != "" ? " - "+data._ret.product.name_variant : "");
937 div3.css("margin-top", "10px").html(__('<b>Product information</b>', 'event-tickets-with-ticket-scanner'))
938 .append('<div>'+sprintf(__('#%s - %s', 'event-tickets-with-ticket-scanner'), data._ret.product.id, product_name)+'</div>');
939 if (data._ret.product.sku != "") {
940 div3.append('<div>'+sprintf(__('SKU: %s', 'event-tickets-with-ticket-scanner'), data._ret.product.sku)+'</div>');
941 }
942 }
943 let content = "";
944 if (ticket_scanner_operating_option.distract_free) {
945 content = '<div style="display:flex;text-align:center;flex-wrap: nowrap;flex-direction: row;justify-content: center;flex-basis: auto;">'+system.code+'</div>';
946 }
947 $('#ticket_add_info').html(content)
948 .append( $('<div style="padding-top:10px;width:100%;">').append(div).append(div2) )
949 .append(div3);
950 }
951 function displayRedeemedInfo(code, data) {
952 system.status = "redeemed";
953 system.redeemed_successfully = data.redeem_successfully;
954 displayTicketRedeemedInfo(data);
955 if(ticket_scanner_operating_option.redeem_auto) {
956 showScanNextTicketButton();
957 } else {
958 //retrieveTicket(code, true);
959 }
960 system.INPUTFIELD.focus();
961 system.INPUTFIELD.select();
962 }
963 function displayTicketRedeemedInfo(data) {
964 showScanNextTicketButton();
965 // zeige retrieved info an
966 let content = $('<div>').html('<div style="display:flex;text-align:center;flex-wrap: nowrap;flex-direction: row;justify-content: center;flex-basis: auto;">'+system.code+'</div>');
967 if (system.redeemed_successfully) {
968 content.append('<h3 style="color:green !important;text-align:center;">'+__('Redeemed', 'event-tickets-with-ticket-scanner')+'</h3>');
969 //content.append('<p style="text-align:center;color:green"><img src="'+system.img_pfad+'button_ok.png"><br><b>'+__('Successfully redeemed', 'event-tickets-with-ticket-scanner')+'</b></p>');
970 content.append('<p style="text-align:center;color:green"><img src="'+system.img_pfad+'button_ok.png"></p>');
971 } else {
972 content.append('<h3 style="color:red !important;text-align:center;">'+__('NOT REDEEMED - see reason below', 'event-tickets-with-ticket-scanner')+'</h3>');
973 //content.append('<p style="text-align:center;color:red;"><img src="'+system.img_pfad+'button_cancel.png"><br><b>'+__('Failed to redeem', 'event-tickets-with-ticket-scanner')+'</b></p>');
974 content.append('<p style="text-align:center;color:red;"><img src="'+system.img_pfad+'button_cancel.png"></p>');
975 }
976 if (typeof system.last_scanned_ticket.data != null && system.last_scanned_ticket.data._ret && system.last_scanned_ticket.data._ret.ticket_title && system.last_scanned_ticket.data._ret.ticket_title != "") {
977 content.append('<h4 style="text-align:center;">'+system.last_scanned_ticket.data._ret.ticket_title+'</h4>');
978 }
979 if (typeof system.last_scanned_ticket.data._ret.ticket_subtitle != "undefined" && system.last_scanned_ticket.data._ret && system.last_scanned_ticket.data._ret.ticket_subtitle && system.last_scanned_ticket.data._ret.ticket_subtitle != "") {
980 content.append('<h5 style="text-align:center;">'+system.last_scanned_ticket.data._ret.ticket_subtitle+'</h5>');
981 }
982 if (ticket_scanner_operating_option.distract_free_show_short_desc && typeof system.last_scanned_ticket.data._ret.short_desc != "undefined" && system.last_scanned_ticket.data._ret.short_desc != "") {
983 content.append('<div>'+system.last_scanned_ticket.data._ret.short_desc+'</div>');
984 }
985
986 if (typeof system.last_scanned_ticket.data != null && system.last_scanned_ticket.data.metaObj && system.last_scanned_ticket.data.metaObj.wc_ticket && system.last_scanned_ticket.data.metaObj.wc_ticket.redeemed_date && system.last_scanned_ticket.data.metaObj.wc_ticket.redeemed_date != "") {
987 content.append('<div style="text-align:center;">'+system.last_scanned_ticket.data._ret.redeemed_date_label+' '+system.last_scanned_ticket.data.metaObj.wc_ticket.redeemed_date+'</div>');
988 }
989 updateTicketScannerInfoArea(content);
990 }
991 function displayRedeemedOrderInfo(code, data) {
992 let content = $('<div>');
993 content.html('<center>'+code+'</center>');
994 if (data.errors.length > 0) {
995 content.append('<h3 style="color:red !important;text-align:center;">'+__('ERRORS - see reason below', 'event-tickets-with-ticket-scanner')+'</h3>');
996 } else if (data.not_redeemed.length) { // is not implemented yet
997 content.append('<h3 style="color:orange !important;text-align:center;">'+__('NOT REDEEMED - see reason below', 'event-tickets-with-ticket-scanner')+'</h3>');
998 } else {
999 content.append('<h3 style="color:green !important;text-align:center;">'+__('Order Redeemed', 'event-tickets-with-ticket-scanner')+'</h3>');
1000 }
1001 updateTicketScannerInfoArea(content);
1002
1003 system.INPUTFIELD.focus();
1004 system.INPUTFIELD.select();
1005 }
1006 function redeemTicket(code) {
1007 clearAreas();
1008 system.redeemed_successfully = false;
1009 $("#reader_output").html(__("start redeem ticket...loading..."));
1010 updateTicketScannerInfoArea(_getSpinnerHTML());
1011 _makeGet('redeem_ticket', {'code':code}, data=>{
1012 system.data = data;
1013 $("#reader_output").html('');
1014
1015 if (typeof data.is_order_ticket !== "undefined" && data.is_order_ticket) {
1016 // update li
1017 data.errors.forEach(item=>{
1018 let elems = $('#order_info').find('li[data-id="'+encodeURIComponent(item.code)+'"]');
1019 elems.css("padding", "5px");
1020 elems.css("margin-bottom", "5px");
1021 elems.css("background-color", "red");
1022 //elems.css("color", "white");
1023 elems.append("<br>"+item.error);
1024 });
1025 data.not_redeemed.forEach(item=>{
1026 let elems = $('#order_info').find('li[data-id="'+encodeURIComponent(item.code)+'"]');
1027 elems.css("padding", "5px");
1028 elems.css("margin-bottom", "5px");
1029 elems.css("background-color", "orange");
1030 elems.css("color", "black");
1031 elems.append("<br>Not redeemed");
1032 });
1033 data.redeemed.forEach(item=>{
1034 let info = item._ret.tickets_redeemed_show ? "<br>Redeemed: "+item._ret.tickets_redeemed:'';
1035 let elems = $('#order_info').find('li[data-id="'+encodeURIComponent(item.code)+'"]');
1036 elems.css("padding", "5px");
1037 elems.css("margin-bottom", "5px");
1038 elems.css("background-color", "green");
1039 //elems.css("color", "white");
1040 elems.append(info);
1041 });
1042 displayRedeemedOrderInfo(code, data);
1043 } else {
1044 displayRedeemedInfo(code, data);
1045 $('#ticket_info_btns').append(displayRedeemedTicketsInfo(data));
1046 }
1047
1048 }, response=>{
1049 clearAreas();
1050 $("#reader_output").html('');
1051 updateTicketScannerInfoArea('<h1 style="color:red !important;">'+response.data+'</h3>');
1052
1053 showScanNextTicketButton();
1054
1055 system.INPUTFIELD.focus();
1056 system.INPUTFIELD.select();
1057 });
1058 }
1059 function displayOrderTicketInfo(data) {
1060 let div = $('<div>').css('padding', '10px');
1061
1062 div.html('<h3 style="text-align:center;color:black !important;">'+_x("Order Ticket", 'label', 'event-tickets-with-ticket-scanner')+'</h3>');
1063 div.append($('<div style="text-align:center;">').html(data.order_infos.code));
1064 div.append("<b>"+_x("Includes", 'label', 'event-tickets-with-ticket-scanner')+": </b>"+sprintf(__('%s Products, %s Tickets', 'event-tickets-with-ticket-scanner'), data.order_infos.products.length, data.ticket_infos.length)+'<br>');
1065 div.append('<b>'+_x("Order ID", 'label', 'event-tickets-with-ticket-scanner')+': </b>#'+data.order_infos.id+'<br>');
1066 div.append('<b>'+_x("Created", 'label', 'event-tickets-with-ticket-scanner')+': </b>'+data.order_infos.date_created+'<br>');
1067 div.append('<b>'+_x("Created", 'label', 'event-tickets-with-ticket-scanner')+': </b>'+data.order_infos.date_completed+'<br>');
1068 div.append('<b>'+_x("Paid", 'label', 'event-tickets-with-ticket-scanner')+': </b>'+data.order_infos.date_paid+'<br>');
1069 div.append($('<button class="button-ticket-options button-primary">').html(_x("Redeem Complete Order", 'label', 'event-tickets-with-ticket-scanner')).on("click", e=>{
1070 redeemTicket(data.order_infos.code);
1071 }));
1072 let div_tickets = $('<div style="padding-top:15px;text-align:left;">');
1073 for (let pidx=0;pidx<data.order_infos.products.length;pidx++) {
1074 let product = data.order_infos.products[pidx];
1075 div_tickets.append("<b>"+product.product_name
1076 +(product.product_name_variant != "" ? " - "
1077 +product.product_name_variant : "")
1078 +'</b>');
1079 let ol = $('<ol style="padding-top:5px;">');
1080 for (let idx=0;idx<data.ticket_infos.length;idx++) {
1081 let item = data.ticket_infos[idx];
1082 if (item.product_id == product.product_id && item.product_parent_id == product.product_parent_id) {
1083 let li = $('<li data-id="'+encodeURIComponent(item.code_display)+'" style="padding-bottom:10px;">');
1084 let extra_content = item.code_display+'<br>';
1085 if (item.name_per_ticket != "" || item.value_per_ticket != "") {
1086 extra_content += item.name_per_ticket+" "+item.value_per_ticket;
1087 } else {
1088 extra_content += "No name or value on ticket set";
1089 }
1090 if (item.location) {
1091 extra_content += "<br>"+item.location;
1092 }
1093 if (item.ticket_date) {
1094 extra_content += "<br>"+item.ticket_date;
1095 }
1096 li.append(extra_content+'<br>')
1097 .append($('<button style="color:white;border-color:#008CBA;background-color:#008CBA;">').html("Retrieve ticket").on("click", e=>{
1098 retrieveTicket(item.code_public, true); // do not redeem automatically
1099 }))
1100 .append($('<button style="color:white;border-color:red;background-color:red;">').html("Redeem ticket").on("click", e=>{
1101 redeemTicket(item.code_public);
1102 }))
1103 .appendTo(ol);
1104 }
1105 }
1106 ol.appendTo(div_tickets);
1107 }
1108 div_tickets.appendTo(div);
1109
1110 div_order_info_area = $('#order_info').html(div);
1111 }
1112 function displayTicketInfo(data) {
1113 let codeObj = data;
1114 let metaObj = data.metaObj;
1115 let ret = data._ret;
1116 let div = $('<div>').css('padding', '10px');
1117 let border_color = 'green';
1118 if (isTicketExpired(data._ret)) {
1119 border_color = 'orange';
1120 }
1121 if (metaObj['wc_ticket']['redeemed_date'] != "") {
1122 border_color = 'red';
1123 }
1124 div.css("border", "1px solid "+border_color);
1125
1126 $('<h3 style="color:black !important;text-align:center;">').html(ret.ticket_heading).appendTo(div);
1127 $('<h4 style="color:black !important;margin-bottom:0;">').html(ret.ticket_title).appendTo(div);
1128 /* // ?? is the same like ret.ticiet_sub_title
1129 if (data._ret.product.name_variant != "") {
1130 $('<h5 style="color:black !important;margin-top:0;padding-top:0;">').html(data._ret.product.name_variant).appendTo(div);
1131 }
1132 */
1133 if (ret.ticket_sub_title != "") {
1134 $('<h5 style="color:black !important;margin-top:0;padding-top:0;">').html(ret.ticket_sub_title).appendTo(div);
1135 }
1136 $('<p>').html(ret.ticket_date_as_string).appendTo(div);
1137 if (ret.ticket_location != "") {
1138 $('<p>').html(ret.ticket_location_label+' '+ret.ticket_location).appendTo(div);
1139 }
1140 if (ret.short_desc != "") {
1141 div.append(ret.short_desc).append('<br>');
1142 }
1143 if (ret.ticket_info != "") {
1144 $('<p>').html(ret.ticket_info).appendTo(div);
1145 }
1146 if (ret.cst_label != "") {
1147 $('<p>').html('<b>'+ret.cst_label+'</b><br>'+ret.cst_billing_address+'<br>').appendTo(div);
1148 }
1149 if (ret.payment_label != "") {
1150 let date_order_paid = ret.payment_paid_at;
1151 let date_order_complete = null;
1152 if (ret.payment_completed_at !== "undefined") {
1153 date_order_complete = ret.payment_completed_at;
1154 }
1155 let p = $('<p>').appendTo(div);
1156 p.append('<b>'+ret.payment_label+'</b><br>');
1157 p.append("Order status: "+ret.order_status+"<br>");
1158 p.append(ret.payment_paid_at_label+' ');
1159 p.append('<b>'+date_order_paid+'</b><br>');
1160 if (date_order_complete != null) {
1161 p.append(ret.payment_completed_at_label+' ');
1162 p.append('<b>'+date_order_complete+'</b><br>');
1163 }
1164 p.append(ret.payment_method_label);
1165 if (ret.payment_method != "") {
1166 p.append(' '+ret.payment_method+' '+ret.payment_trx_id);
1167 }
1168 p.append('<br>');
1169 if (ret.coupon != "") {
1170 p.append(ret.coupon_label+' <b>'+ret.coupon+'</b><br>');
1171 }
1172 }
1173 if (metaObj.wc_ticket.name_per_ticket != "") {
1174 $('<p>').html(ret.name_per_ticket_label + " " +metaObj.wc_ticket.name_per_ticket).appendTo(div);
1175 }
1176 if (metaObj.wc_ticket.value_per_ticket != "") {
1177 $('<p>').html(ret.value_per_ticket_label + " " +metaObj.wc_ticket.value_per_ticket).appendTo(div);
1178 }
1179 if (ret.ticket_amount_label != "") {
1180 $('<p>').html(ret.ticket_amount_label).appendTo(div);
1181 }
1182 let p = $('<p>').html(ret.ticket_label+' <b>'+codeObj['code_display']+'</b><br>').appendTo(div);
1183 p.append(ret.paid_price_label+' <b>'+ret.paid_price_as_string+'</b>');
1184 if (ret.product_price != ret.paid_price) {
1185 p.append(' <b>('+ret.product_price_label+' '+ret.product_price_as_string+')</b>');
1186 }
1187 $('<p style="text-align:center;">').html(system.code).appendTo(div);
1188
1189 div_ticket_info_area = $('#ticket_info').html(div);
1190 displayTicketInfoButtons(data);
1191 }
1192 function canTicketBeRedeemed(data) {
1193 let allow_redeem = false;
1194 if (data._ret.allow_redeem_only_paid) {
1195 if (data._ret.is_paid) {
1196 allow_redeem = true;
1197 }
1198 } else {
1199 allow_redeem = true;
1200 }
1201 if (allow_redeem) {
1202 if (data.metaObj['wc_ticket']['redeemed_date'] != "") {
1203 allow_redeem = false;
1204 }
1205 if (data._ret.max_redeem_amount > 1 && data.metaObj.wc_ticket.stats_redeemed.length < data._ret.max_redeem_amount) {
1206 allow_redeem = true;
1207 }
1208 if (allow_redeem) {
1209 allow_redeem = canTicketBeRedeemedNow(data);
1210 }
1211 }
1212 return allow_redeem;
1213 }
1214 function displayTicketInfoButtons(data) {
1215 let div = $('<div>').css('text-align', 'center');
1216 if (!data._ret.is_paid) {
1217 $('<h4 style="color:red !important;">').html(sprintf(/* translators: %s: order status */__('Ticket is NOT paid (%s).', 'event-tickets-with-ticket-scanner'), data._ret.order_status)).appendTo(div);
1218 }
1219 $('<button class="button-ticket-options">').html(_x('Reload', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div).on('click', e=>{
1220 retrieveTicket(system.code, true);
1221 });
1222 let btn_redeem = $('<button class="button-ticket-options">').html(_x('Redeem Ticket', 'label', 'event-tickets-with-ticket-scanner')).css("background-color", 'gray').css('color', 'white').prop("disabled", true).appendTo(div).on('click', e=>{
1223 redeemTicket(system.code);
1224 });
1225 if (ticket_scanner_operating_option.ticketScannerDontShowBtnPDF == false) {
1226 $('<button class="button-ticket-options">').html(_x('PDF', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div).on('click', e=>{
1227 window.open(data.metaObj['wc_ticket']['_url']+'?pdf', '_blank');
1228 });
1229 }
1230 if (ticket_scanner_operating_option.ticketScannerDontShowBtnBadge == false) {
1231 $('<button class="button-ticket-options">').html(_x('Badge', 'label', 'event-tickets-with-ticket-scanner')).appendTo(div).on('click', e=>{
1232 _downloadFile('downloadPDFTicketBadge', {'code':data.code}, "eventticket_badge_"+data.code+".pdf");
1233 return false;
1234 });
1235 }
1236
1237 if (canTicketBeRedeemed(data)) {
1238 btn_redeem.prop("disabled", false).css('background-color','green');
1239 }
1240
1241 div.append(displayRedeemedTicketsInfo(data));
1242 div.append(displayTimezoneInformation(data));
1243 $('#ticket_info_btns').html(div);
1244 }
1245 function displayRedeemedTicketsInfo(data) {
1246 let div = $('<div>');
1247 let show = false;
1248 if (data._ret.tickets_redeemed_show) {
1249 show = true;
1250 $('<div style="color:black !important">').html(sprintf(/* translators: %d: amount redeemed tickets */__('%d tickets of this event (product) redeemed already by stats', 'event-tickets-with-ticket-scanner'), data._ret.tickets_redeemed)).appendTo(div);
1251 }
1252 if (data._ret.tickets_redeemed_show_c) {
1253 show = true;
1254 $('<div style="color:black !important">').html(sprintf(/* translators: %d: amount redeemed tickets */__('%d tickets of this event (product) redeemed already', 'event-tickets-with-ticket-scanner'), data._ret.tickets_redeemed_by_codes)).appendTo(div);
1255 }
1256 if (data._ret.tickets_redeemed_show_cn) {
1257 show = true;
1258 $('<div style="color:black !important">').html(sprintf(/* translators: %d: amount redeemed tickets */__('%d tickets of this event (product) not redeemed yet', 'event-tickets-with-ticket-scanner'), data._ret.tickets_redeemed_not_by_codes)).appendTo(div);
1259 }
1260 if (show) {
1261 div.css('text-align', 'center');
1262 div.css("padding-top", "10px");
1263 return div;
1264 }
1265 return '';
1266 }
1267 function updateTicketScannerInfoArea(content) {
1268 $('#ticket_scanner_info_area').html(content);
1269 if (toBool(myAjax.ticketScannerDisplayTimes)) {
1270 let data = system.data;
1271 if (data != null && typeof data != "undefined" && typeof data._ret != "undefined" && typeof data._ret._server != "undefined") {
1272 let div = $('<div style="padding-top:30px;">');
1273 div.append("Server: "+data._ret._server.time+" "+data._ret._server.timezone.timezone+" Offset: "+data._ret._server.timezone.timezone+"<br>");
1274 let date = new Date();
1275 div.append("Local: "+date+"<br>");
1276 system.TIMEAREA.html(div);
1277 }
1278 }
1279 }
1280 function displayTimezoneInformation(data) {
1281 let div = $('<div>').css('text-align', 'center');
1282 if (typeof system.PARA.displaytime !== "undefined") {
1283 div.css("padding", "10px;");
1284 //console.log(data);
1285 div.append("Ticket start timestamp: "+(data._ret.ticket_start_date_timestamp*1000)+"<br>");
1286 let d_t_s = new Date(data._ret.ticket_start_date_timestamp*1000);
1287 div.append("Ticket start timestamp date: "+d_t_s+"<br>");
1288
1289 div.append("Ticket end timestamp: "+(data._ret.ticket_end_date_timestamp*1000)+"<br>");
1290 let d_t_e = new Date(data._ret.ticket_end_date_timestamp*1000);
1291 div.append("Ticket end timestamp date: "+d_t_e+"<br>");
1292
1293 if (typeof data._ret._server !== "undefined") {
1294 try {
1295 div.append("Server timezone: "+data._ret._server.timezone.timezone+" Offset: "+data._ret._server.timezone.timezone+"<br>");
1296 div.append("Server time: "+data._ret._server.time+"<br>");
1297 div.append("UTC time: "+data._ret._server.UTC_time+"<br>");
1298 if (typeof data._ret.is_date_set != "undefined" && data._ret.is_date_set) {
1299 let date = new Date(data._ret.redeem_allowed_from);
1300 div.append('Redeem allowed from: '+date+'<br>');
1301 date = new Date(data._ret.redeem_allowed_until);
1302 div.append("Redeem allowed until: "+date+"<br>");
1303 }
1304 } catch(e) {
1305 //console.log(e);
1306 }
1307 }
1308
1309 let d_ts_n = new Date();
1310 div.append("Ticket scanner browser now-date: "+d_ts_n+"<br>");
1311 }
1312 return div;
1313 }
1314 function cleanPublicTicketNumber(code) {
1315 if (code) {
1316 return code.replace(/'/g, "-").trim();
1317 }
1318 return '';
1319 }
1320 function addInputField() {
1321 let div = $('<div>').css('text-align', 'center');
1322 $('<label for="barcode_scanner_input" class="form-label" style="color:#837878">').html(__('For QR code barcode scanner', 'event-tickets-with-ticket-scanner')).appendTo(div);
1323 $('<br>').appendTo(div);
1324 let inputField = $('<input style="width:70%;" name="barcode_scanner_input" placeholder="'+_x('Type in the ticket number and hit ENTER (optional to scanning)', 'attr', 'event-tickets-with-ticket-scanner')+'" type="text">')
1325 .appendTo(div)
1326 .on("change", ()=>{
1327 let code = cleanPublicTicketNumber(inputField.val());
1328 if (code != "") {
1329 clearOrderInfos();
1330 retrieveTicket(code, false, ()=>{
1331 inputField.focus();
1332 inputField.select();
1333 });
1334 }
1335 })
1336 .on("keypress", event=>{
1337 if (event.key === "Enter") {
1338 let code = cleanPublicTicketNumber(inputField.val());
1339 if (code != "") {
1340 event.preventDefault();
1341 clearOrderInfos();
1342 retrieveTicket(code, false, ()=>{
1343 inputField.focus();
1344 inputField.select();
1345 });
1346 }
1347 }
1348 });
1349 system.INPUTFIELD = div;
1350 }
1351 function addRemoveAuthTokenButton() {
1352 let div = $('<div style="padding-top:10px;">').css('text-align', 'center');
1353 $('<button style="background-color:red;color:white;">')
1354 .html("Remove Auth Token")
1355 .appendTo(div)
1356 .on("click", e=>{
1357 if (confirm("Do you want to delete the auth token?")) {
1358 setAuthToken();
1359 showScanOptions();
1360 }
1361 });
1362 system.AUTHTOKENREMOVEBUTTON = div;
1363 }
1364 function addClearCamDeviceButton() {
1365 let btn = $('<button>').html("Clear the stored cam device").on("click", event=>{
1366 _storeValue("ticketScannerCameraId", "", 1);
1367 window.location.reload(true);
1368 });
1369 if (!system.ADDITIONBUTTONS) system.ADDITIONBUTTONS = $('<div style="text-align:center;margin-top:20px;">');
1370 system.ADDITIONBUTTONS.append(btn);
1371 }
1372 function initStyle() {
1373 document.getElementsByClassName('ticket_content')[0].style.borderRadius="12px";
1374 let content = '';
1375 content += 'button.button-ticket-options {width:90%;margin-left:auto;margin-right:auto;margin-bottom:15px;display:block;border-radius:12px;padding:10px 15px;text-align:center;}';
1376 content += 'button.button-primary {background-color:#008CBA;color:white;border-color:#008CBA;}';
1377 content += '@media screen and (min-width: 720px) { button.button-ticket-options{width:50%;} }';
1378 addStyleCode(content);
1379 }
1380 function refreshNoncePeriodically() {
1381 // check if the last check of nonce is older than 4 minutes
1382 // do a ping to get the new nonce
1383 setInterval(()=>{
1384 let last_check = system.last_nonce_check;
1385 if (last_check == null || last_check == "") {
1386 last_check = 0;
1387 }
1388 let now = new Date().getTime();
1389 if (now - last_check > 240000) {
1390 _makeGet('ping', [], data=>{
1391 });
1392 }
1393 }, 60000);
1394 }
1395 function starten() {
1396 $ = jQuery;
1397 initStyle();
1398 addMetaTag("viewport", "width=device-width, initial-scale=1");
1399 $('#reader').html(_getSpinnerHTML());
1400 _makeGet('ping', [], data=>{
1401 system.data = data; // initialer daten empfang mit options
1402 system.img_pfad = data.img_pfad;
1403 system.PARA = basics_ermittelURLParameter();
1404
1405 if (toBool(myAjax.ticketScannerDontShowOptionControls)) {
1406 setRedeemImmediately(toBool(myAjax.ticketScannerScanAndRedeemImmediately));
1407 setDistractFree(toBool(myAjax.ticketScannerHideTicketInformation));
1408 setStartCamWithoutButtonClicked(toBool(myAjax.ticketScannerStartCamWithoutButtonClicked));
1409 } else {
1410 if (system.PARA.redeemauto || _loadValue("ticket_scanner_operating_option.redeem_auto") == "1" || setRedeemImmediately(toBool(myAjax.ticketScannerScanAndRedeemImmediately))) {
1411 setRedeemImmediately(true);
1412 }
1413 if (system.PARA.distractfree || _loadValue("ticket_scanner_operating_option.distract_free") == "1" || toBool(myAjax.ticketScannerHideTicketInformation)) {
1414 setDistractFree(true);
1415 }
1416 if (system.PARA.distractfreeshowshortdesc || _loadValue("ticket_scanner_operating_option.distract_free_show_short_desc") == "1" || toBool(myAjax.ticketScannerHideTicketInformationShowShortDesc)) {
1417 setDistractFreeShowShortDesc(true);
1418 }
1419 if (system.PARA.startcam || _loadValue("ticket_scanner_operating_option.ticketScannerStartCamWithoutButtonClicked") == "1" || toBool(myAjax.ticketScannerStartCamWithoutButtonClicked)) {
1420 setStartCamWithoutButtonClicked(true);
1421 }
1422 }
1423
1424 initAuthToken();
1425 addInputField();
1426 addClearCamDeviceButton();
1427 addRemoveAuthTokenButton();
1428 showScanOptions();
1429 refreshNoncePeriodically();
1430 if (system.PARA.code) {
1431 system.code = system.PARA.code;
1432 if (system.code != "") {
1433 system.code = cleanPublicTicketNumber(system.code);
1434 system.INPUTFIELD.val(system.code);
1435 retrieveTicket(system.code);
1436 }
1437 } else {
1438 startScanner();
1439 //showScanNextTicketButton();
1440 }
1441 });
1442 }
1443 var $;
1444 //window.onload = starten;
1445 starten();
1446 } );