function sasoEventtickets(_myAjaxVar, doNotInit) { const { __, _x, _n, sprintf } = wp.i18n; let myAjax = _myAjaxVar; let self = this; let PREMIUM = null; var $ = jQuery; var PARAS = basics_ermittelURLParameter(); var DATA = { /*action: '',*/ nonce: myAjax.nonce, last_nonce_check: 0 }; var system = {is_debug:false, DYNJS:{}, DYNJS_CACHE:{}}; var FATAL_ERROR = false; var DIV = null; var LAYOUT = null; var DATA_LISTS = null; var DATA_AUTHTOKENS = null; var OPTIONS = { list:[], mapKeys:{}, versions:{mapKeys:{}}, meta_tags_keys:{list:[], mapKeys:{}}, infos:{}, tickets_for_testing:[], options_special:{}, dismissed_suggestions:[] }; var STATE = null; if (_myAjaxVar._doNotInit) doNotInit = true; function time() { return new Date().getTime(); } // Format activation timestamp ("YYYY-MM-DD HH:MM:SS" or with trailing "~" for estimate) // to a localized date. The "~" suffix marks values derived from filesystem mtime // rather than a real activation event — they're shown with "(estimate)" hint. function _formatActivationDate(s) { if (!s) return ''; var isEstimate = s.slice(-1) === '~'; if (isEstimate) s = s.slice(0, -1); var d = new Date(s.replace(' ', 'T') + 'Z'); if (isNaN(d.getTime())) return s + (isEstimate ? ' (estimate)' : ''); var dateStr = d.toLocaleDateString(); return dateStr + (isEstimate ? ' (estimate)' : ''); } function destroy_tags(t) { if (t != null) { t = t.replace("<", "").replace(">",""); } return t; } function _requestURL(action, myData) { let paras = '?action='+myAjax._action+'&a_sngmbh='+action+'&nonce='+ DATA.nonce+'&t='+time(); if (myData) { for(let key in myData) paras += '&data['+key+']='+encodeURIComponent(myData[key]); } for(let key in DATA) paras += '&'+key+'='+encodeURIComponent(DATA[key]); return myAjax.url + paras; } function _makePost(action, myData, cbf, ecbf, pcbf) { if (FATAL_ERROR) return; let _data = Object.assign({}, DATA); _data.action = myAjax._action; _data.a_sngmbh = action; _data.t = new Date().getTime(); _data.nonce = DATA.nonce; pcbf && pcbf(); for(var key in myData) _data['data['+key+']'] = myData[key]; // Pass through debug parameter if set in URL var urlParams = new URLSearchParams(window.location.search); if (urlParams.has('VollstartValidatorDebug')) { _data['VollstartValidatorDebug'] = urlParams.get('VollstartValidatorDebug') || '1'; } $.post( myAjax.url, _data, function( response ) { if (response && response.data && response.data.nonce) { DATA.last_nonce_check = new Date().getTime(); DATA.nonce = response.data.nonce; } if (!response.success) { if (ecbf) ecbf(response); else LAYOUT.renderFatalError(response.data); } else { cbf && cbf(response.data); } }).fail(function(jqXHR, textStatus) { if (ecbf) ecbf({success:false, data:textStatus}); else LAYOUT.renderFatalError(textStatus); }); } function _makeGet(action, myData, cbf, ecbf, pcbf) { if (FATAL_ERROR) return; let _data = Object.assign({}, DATA); _data.action = myAjax._action; _data.a_sngmbh = action; _data.t = new Date().getTime(); _data.nonce = DATA.nonce; pcbf && pcbf(); for(var key in myData) _data['data['+key+']'] = myData[key]; // Pass through debug parameter if set in URL var urlParams = new URLSearchParams(window.location.search); if (urlParams.has('VollstartValidatorDebug')) { _data['VollstartValidatorDebug'] = urlParams.get('VollstartValidatorDebug') || '1'; } $.get( myAjax.url, _data, function( response ) { if (response && response.data && response.data.nonce) { DATA.last_nonce_check = new Date().getTime(); DATA.nonce = response.data.nonce; } if (!response.success) { if (ecbf) ecbf(response); else LAYOUT.renderFatalError(response.data); } else { cbf && cbf(response.data); } }).fail(function(jqXHR, textStatus) { if (ecbf) ecbf({success:false, data:textStatus}); else LAYOUT.renderFatalError(textStatus); }); } function getOptionsFromServer(cbf, ecbf, pcbf) { _makeGet('getOptions', {}, options=>{ _setOptions(options); cbf && cbf(options); }, ecbf, pcbf); } function _downloadFile(action, myData, filenameToStore, cbf, ecbf, pcbf) { let _data = Object.assign({}, DATA); _data.action = myAjax._action; _data.a_sngmbh = action; _data.t = new Date().getTime(); _data.nonce = DATA.nonce; pcbf && pcbf(); for(var key in myData) _data['data['+key+']'] = myData[key]; let params = ""; for(var key in _data) params += key+"="+_data[key]+"&"; let url = myAjax.url+'?'+params; let window_name = myData.code ? myData.code : '_blank'; let new_window = window.open(url, window_name); //window.location.href = url; //ajax_downloadFile(url, filenameToStore, cbf); } function ajax_downloadFile(urlToSend, fileName, cbf) { var req = new XMLHttpRequest(); req.open("GET", urlToSend, true); req.responseType = "blob"; req.onload = function (event) { var blob = req.response; //var fileName = req.getResponseHeader("X-fileName") //if you have the fileName header available var link=document.createElement('a'); link.href=window.URL.createObjectURL(blob); link.download=fileName; link.click(); cbf && cbf(); }; req.send(); } function speakOutLoud(v, display) { if ('speechSynthesis' in window) { var t = typeof v === 'object' ? 'Value is an object.' : v; if (t.trim() == "") t = 'Value is empty'; var msg = new SpeechSynthesisUtterance(t); msg.lang = "en-US"; window.speechSynthesis.speak(msg); if (display) console.log("Speak:", v); } else { console.log(v); } } function _saveOptionValue(key, value, cbf, pcbf) { _makePost('changeOption', {'key':key, 'value':value}, ()=>{ cbf && cbf(); if (key == "wcTicketDesignerTemplateTest") { $("#wcTicketDesignerTemplateTest_button_PDF").prop("disabled", false).text(__('Preview Test Template Code as PDF', 'event-tickets-with-ticket-scanner')); } if (key == "serial") { // Hide license-related admin banners immediately — server has suppressed // them for 60s via transient, but JS fadeOut gives instant visual feedback // without waiting for page reload. let $serialValue = typeof value === 'string' ? value.trim() : ''; if ($serialValue !== '') { $('.saso-license-banner').fadeOut(400); } // Immediately recheck license after serial change let $statusEl = $('[data-key="serial"]').parent().find('.saso-license-inline-status'); if ($statusEl.length === 0) { $statusEl = $(''); $('[data-key="serial"]').after($statusEl); } $statusEl.html(''+__('Checking license...', 'event-tickets-with-ticket-scanner')+''); _makePost('recheckLicense', {}, function(result) { if (result) { let color = result.active ? 'green' : 'red'; let label = result.active ? __('Active', 'event-tickets-with-ticket-scanner') : __('Inactive', 'event-tickets-with-ticket-scanner'); if (result.subscription_type === 'lifetime') label += ' (Lifetime)'; $statusEl.html(''+label+''); } // ALWAYS reload after a serial save — the backend may have swapped // Starter → Premium during save, and the page state needs to refresh // to reflect the new plugin. Reloading regardless of active/inactive // ensures users never get stuck with a stale banner. if ($serialValue !== '') { setTimeout(function() { location.reload(); }, 2000); } }, function() { $statusEl.html(''+__('Check failed', 'event-tickets-with-ticket-scanner')+''); // Reload anyway — save succeeded, recheck just failed (network/server) if ($serialValue !== '') { setTimeout(function() { location.reload(); }, 3000); } }); if (_getOptions_Versions_getByKey('isOldPremiumDetected')) { __checkPremiumUpdateAfterSerial(value); } } }, null, ()=>{ pcbf && pcbf(); if (key == "wcTicketDesignerTemplateTest") { $("#wcTicketDesignerTemplateTest_button_PDF").prop("disabled", true).text(__('saving...', 'event-tickets-with-ticket-scanner')); } }); } function _setOptions(optionData) { OPTIONS.list = optionData.options; for (let a=0;aPlease go to the settings->permalinks and choose a permalink structure, that is not 'plain'.", 'event-tickets-with-ticket-scanner')); } OPTIONS.versions.mapKeys = optionData.versions; } system.is_debug = typeof optionData.versions.is_debug != "undefined" && optionData.versions.is_debug == 1 ? true : false; if (optionData.meta_tags_keys) { OPTIONS.meta_tags_keys.list = optionData.meta_tags_keys; OPTIONS.meta_tags_keys.mapKeys = {}; for (let a=0;a dismissNow) { // quiet — fall through } else if (serial == '') { if (STATE != "options") { let dlgContent = $('
'); dlgContent.append('

'+__('Thank you for using the Premium version!', 'event-tickets-with-ticket-scanner')+'

'); dlgContent.append('

'+__('Please enter your license key to activate updates and premium features.', 'event-tickets-with-ticket-scanner')+'

'); let serialInput = $(''); dlgContent.append(serialInput); let statusDiv = $('
').css({"margin-top":"10px","display":"none"}); dlgContent.append(statusDiv); dlgContent.dialog({ title: __('Premium License Key', 'event-tickets-with-ticket-scanner'), modal: true, width: 450, dialogClass: "no-close", open: function() { setTimeout(function(){ serialInput.focus(); }, 100); }, buttons: [ { text: __('Activate', 'event-tickets-with-ticket-scanner'), class: "button-primary", click: function() { let $dlg = $(this); let key = serialInput.val().trim(); if (key === '') { statusDiv.html(''+__('Please enter a license key.', 'event-tickets-with-ticket-scanner')+'').show(); return; } // Lock UI — big spinner, disable everything so user waits $dlg.parent().find('.ui-dialog-buttonpane button').prop('disabled', true).css({opacity: 0.5, cursor: 'not-allowed'}); $dlg.parent().find('.ui-dialog-buttonpane button.button-primary').text(__('Checking...', 'event-tickets-with-ticket-scanner')); serialInput.prop('disabled', true).css('opacity', 0.5); statusDiv.html( '
' + '' + ''+__('Validating license — please wait…', 'event-tickets-with-ticket-scanner')+'' + '
' ).show(); _makePost('changeOption', {key:'serial', value:key}, function() { statusDiv.html( '
' + '✓ '+__('License key saved. Reloading…', 'event-tickets-with-ticket-scanner') + '
' ); setTimeout(function(){ location.reload(); }, 1500); }, function(err) { $dlg.parent().find('.ui-dialog-buttonpane button').prop('disabled', false).css({opacity: 1, cursor: 'pointer'}); $dlg.parent().find('.ui-dialog-buttonpane button.button-primary').text(__('Activate', 'event-tickets-with-ticket-scanner')); serialInput.prop('disabled', false).css('opacity', 1); statusDiv.html(''+__('Error:', 'event-tickets-with-ticket-scanner')+' '+(err && err.data ? err.data : 'unknown')+'').show(); }); } }, { text: __('Later', 'event-tickets-with-ticket-scanner'), class: "button-secondary", click: function() { try { localStorage.setItem('saso_et_license_modal_dismissed_until', String(Date.now() + 24*60*60*1000)); } catch (e) { /* ignore */ } $(this).dialog("close"); } } ] }); } } if (serial != "" && typeof OPTIONS.infos.premium_expiration !== "undefined") { let expiration = OPTIONS.infos.premium_expiration; if (expiration.last_run != 0 && expiration.timestamp > 0) { let expirationDate = new Date(expiration.timestamp * 1000); let toCheck = new Date(); toCheck.setDate(toCheck.getDate() + 21); let today = new Date(); if (expirationDate <= today || toCheck >= expirationDate) { let msg = typeof expiration.message !== "undefined" && expiration.message != "" ? '
'+expiration.message : ''; // Only warn for subscriptions, NOT for lifetime/onetime licenses // Lifetime licenses continue working with Basic < 2.8.0 let subType = expiration.subscription_type || ''; let isLifetime = subType === 'lifetime' || subType === 'onetime' || !subType; if (!isLifetime) { // Monthly or Yearly subscription - Premium will STOP let isMonthly = subType.toLowerCase().includes('month'); let title, bodyText; if (isMonthly) { title = "Your monthly subscription payment is due soon!"; bodyText = "Your premium license will be disabled if the payment fails on "+expiration.expiration_date+ ' '+expiration.timezone+'.
Please ensure your payment method is up to date.
'+msg+'After payment failure, the plugin will revert to Basic features only.
You can manage your subscription in your account settings.'; } else { title = "Your premium license expires soon!"; bodyText = "Your premium license will be disabled on "+expiration.expiration_date+ ' '+expiration.timezone+'.
It will revert to Basic features only after expiration.
'+msg+'You can renew your premium license here.'; } let info_box = $('
').html(""+title+"
"+bodyText); $('body').find('div[data-id="plugin_info_area"]').html(info_box); } } } } } } function _getOptions_getByKey(key) { if (OPTIONS.mapKeys[key]) return OPTIONS.mapKeys[key]; return null; } function _getOptions_Meta_getByKey(key) { if (OPTIONS.meta_tags_keys.mapKeys[key]) return OPTIONS.meta_tags_keys.mapKeys[key]; return null; } function _getOptions_Versions_getByKey(key) { if (OPTIONS.versions.mapKeys[key]) return OPTIONS.versions.mapKeys[key]; return null; } function _getOptions_Infos_getByKey(key) { if (OPTIONS.infos[key]) return OPTIONS.infos[key]; return null; } function _getOptions_isActivatedByKey(key) { let po = _getOptions_getByKey(key); if (po == null) return false; return po.value == 1; } function _getOptions_Versions_isActivatedByKey(key) { let po = _getOptions_Versions_getByKey(key); if (po == null) return false; return po == 1; } function _getOptions_getLabelByKey(key) { let po = _getOptions_getByKey(key); if (po == null) return ""; return po.label; } function _getOptions_Meta_getLabelByKey(key) { let po = _getOptions_Meta_getByKey(key); if (po == null) return ""; return po.label; } function _getOptions_getValByKey(key) { let po = _getOptions_getByKey(key); if (po == null) return ""; return po.value == "" ? po['default'] : po.value; } function _getOptions_Versions_getValByKey(key) { let po = _getOptions_Versions_getByKey(key); if (po == null) return ""; return po; } function basics_ermittelURLParameter() { var parawerte = {}; var teile; if (window.location.search !== "") { teile = window.location.search.substring(1).split("&"); for (var a=0;a100?d.getYear().toString().substring(d.getYear().toString().length-2):d.getYear(), 'H':d.getHours()<10?'0'+d.getHours():d.getHours(),'h':d.getHours()>12?d.getHours()-12:d.getHours(), 'i':d.getMinutes()<10?'0'+d.getMinutes():d.getMinutes(),'s':d.getSeconds()<10?'0'+d.getSeconds():d.getSeconds() }; for (var akey in formate) { //var rg = new RegExp('%'+akey, "g"); var rg = new RegExp(akey, "g"); format = format.replace(rg, formate[akey]); } return format; } */ function DateFormatStringToDateTimeText(datestring, format, timezone_id) { if (!format) format = getDefaultDateTimeFormat(); let millisek = parseToMillis(datestring, timezone_id); return Date2Text(millisek, format, timezone_id); } function DateFormatStringToDateText(datestring, format, timezone_id) { let millisek = parseToMillis(datestring, timezone_id); return Date2Text(millisek, format, timezone_id); } function Date2Text(millisek, format, timezone_id) { // 1) Timezone bestimmen (Fallback: UTC) if (!timezone_id) { timezone_id = _getOptions_Versions_getByKey("date_WP_timezone") || "UTC"; } // 2) Timestamp normalisieren (PHP liefert oft Sekunden; JS braucht Millisekunden) if (typeof millisek === "string") millisek = Number(millisek); if (!millisek) { // Deine bestehende Logik – falls du hier einen Unix-TS in Sekunden bekommst, bitte ggf. *1000 ergänzen millisek = time(timezone_id); } if (String(Math.trunc(millisek)).length === 10) { millisek = millisek * 1000; } const date = new Date(Number(millisek)); // 3) Defaults für Format if (!format) { format = getDefaultDateFormat(); } // 4) Lokalisierte Kurzformen (nutzt deine _x-Übersetzungen) const tage = [ _x('Sun', 'cal', 'event-tickets-with-ticket-scanner'), _x('Mon', 'cal', 'event-tickets-with-ticket-scanner'), _x('Tue', 'cal', 'event-tickets-with-ticket-scanner'), _x('Wed', 'cal', 'event-tickets-with-ticket-scanner'), _x('Thu', 'cal', 'event-tickets-with-ticket-scanner'), _x('Fri', 'cal', 'event-tickets-with-ticket-scanner'), _x('Sat', 'cal', 'event-tickets-with-ticket-scanner') ]; const monate = [ _x('Jan', 'cal', 'event-tickets-with-ticket-scanner'), _x('Feb', 'cal', 'event-tickets-with-ticket-scanner'), _x('Mar', 'cal', 'event-tickets-with-ticket-scanner'), _x('Apr', 'cal', 'event-tickets-with-ticket-scanner'), _x('May', 'cal', 'event-tickets-with-ticket-scanner'), _x('Jun', 'cal', 'event-tickets-with-ticket-scanner'), _x('Jul', 'cal', 'event-tickets-with-ticket-scanner'), _x('Aug', 'cal', 'event-tickets-with-ticket-scanner'), _x('Sep', 'cal', 'event-tickets-with-ticket-scanner'), _x('Oct', 'cal', 'event-tickets-with-ticket-scanner'), _x('Nov', 'cal', 'event-tickets-with-ticket-scanner'), _x('Dec', 'cal', 'event-tickets-with-ticket-scanner') ]; // 5) Teile in gewünschter Timezone extrahieren const dtf = new Intl.DateTimeFormat("de-CH", { timeZone: timezone_id, year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", second: "2-digit", weekday: "short", hour12: false }); const parts = Object.fromEntries(dtf.formatToParts(date).map(p => [p.type, p.value])); const monthNum = Number(parts.month); // 1..12 (als "01".."12") const dayNum = Number(parts.day); // 1..31 const hourNum = Number(parts.hour); // 0..23 // Wochentag-Index (0=Sun..6=Sat) in der angegebenen Timezone const weekdayEn = new Intl.DateTimeFormat("en-US", { timeZone: timezone_id, weekday: "short" }).format(date); const weekdayIndex = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"].indexOf(weekdayEn); // 6) Token-Mapping (PHP-ähnlich) const formate = { 'd': parts.day, // 01..31 'j': String(dayNum), // 1..31 'D': tage[weekdayIndex], // So/Mo/... (aus _x oben, hier auf 'Sun'.. gemappt) 'w': String(weekdayIndex), // 0..6 (So=0) 'm': parts.month, // 01..12 'M': monate[monthNum - 1], // Jan..Dec (aus _x oben) 'n': String(monthNum), // 1..12 'Y': parts.year, // 2025 'y': parts.year.slice(-2), // 25 'H': parts.hour, // 00..23 'h': String(((hourNum % 12) || 12)).padStart(2,'0'), // 01..12 'i': parts.minute, // 00..59 's': parts.second // 00..59 }; // 7) Token ersetzen (ohne %; entspricht deiner aktuellen Logik) for (const akey in formate) { const rg = new RegExp(akey, "g"); format = format.replace(rg, formate[akey]); } return format; } // Hilfsfunktion: Offset-Minuten einer IANA-Zeitzone für einen UTC-Instant ermitteln. // Nutzt Intl.DateTimeFormat mit timeZoneName:'shortOffset' (z.B. "GMT+2"). function _getTzOffsetMinutes(utcDate, timezone_id) { const fmt = new Intl.DateTimeFormat('en-US', { timeZone: timezone_id, timeZoneName: 'shortOffset', year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }); const parts = fmt.formatToParts(utcDate); const z = parts.find(p => p.type === 'timeZoneName')?.value || 'GMT+0'; // Erwartete Form: "GMT+2" oder "GMT+02:00" const m = z.match(/GMT([+-])(\d{1,2})(?::?(\d{2}))?/i); if (!m) return 0; const sign = m[1] === '-' ? -1 : 1; const hours = parseInt(m[2], 10); const mins = m[3] ? parseInt(m[3], 10) : 0; return sign * (hours * 60 + mins); } // Wandelt verschiedenste Eingaben in einen UTC-Millis-Timestamp. // - Zahlen (Sekunden/Millis) -> normalisiert // - ISO-Strings mit Z/±hh:mm -> nativ geparst // - Naive Strings (z.B. "YYYY-MM-DD HH:mm:ss") -> als timezone_id-Wandzeit interpretiert function parseToMillis(input, timezone_id) { timezone_id = timezone_id || (typeof _getOptions_Versions_getByKey === 'function' ? _getOptions_Versions_getByKey("date_WP_timezone") : "UTC"); // 1) Direkt Number? if (typeof input === 'number') { // 10-stellige Sekunden -> *1000 if (String(Math.trunc(input)).length === 10) return input * 1000; return input; // bereits Millisekunden } // 2) String -> trim if (typeof input === 'string') { const s = input.trim(); // 2a) Reine Ziffern -> Sekunden/Millis if (/^\d+$/.test(s)) { const n = Number(s); return (s.length === 10) ? n * 1000 : n; } // 2b) ISO mit Z / Offset -> nativ (sicher) if (/T.*(Z|[+-]\d{2}:\d{2})$/.test(s)) { const d = new Date(s); if (!isNaN(d)) return d.getTime(); } // 2c) Naive Formate: "YYYY-MM-DD HH:mm:ss" | "YYYY/MM/DD HH:mm" | "YYYY-MM-DD" // Wir parsen Komponenten und interpretieren sie als Wandzeit in timezone_id. const m = s.match( /^(\d{4})[-\/](\d{2})[-\/](\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?$/ ); if (m) { const Y = parseInt(m[1], 10); const Mo = parseInt(m[2], 10); const D = parseInt(m[3], 10); const H = m[4] ? parseInt(m[4], 10) : 0; const I = m[5] ? parseInt(m[5], 10) : 0; const S = m[6] ? parseInt(m[6], 10) : 0; // Instant-Kandidat in UTC aus den "lokalen" Komponenten // Idee: Komponenten als UTC annehmen -> Offset der Ziel-Zeitzone abziehen. const utcGuess = new Date(Date.UTC(Y, Mo - 1, D, H, I, S)); // Offset der Ziel-Zone zum angegebenen Zeitpunkt holen (inkl. DST) const offMin = _getTzOffsetMinutes(utcGuess, timezone_id); // Echte UTC-Millis, wenn Y-M-D H:I:S die Wandzeit in timezone_id ist: return utcGuess.getTime() - offMin * 60 * 1000; } // 2d) Fallback: Versuch natives Date (Browser-lokal) – nicht ideal, aber besser als NaN const d = new Date(s.replace(' ', 'T')); if (!isNaN(d)) return d.getTime(); } // 3) Wenn alles fehlschlägt -> NaN (oder wirf Fehler je nach Policy) return NaN; } function _getMediaData(mediaid, cbf) { _makeGet('getMediaData', {'mediaid':mediaid}, (ret)=>{ cbf && cbf(ret); }); } function getDataLists(cbf) { if (DATA_LISTS !== null) cbf && cbf(); _makeGet('getLists', {}, data=>{ DATA_LISTS = data; __updateFirstStepsProgress(); cbf && cbf(DATA_LISTS); }); } function getCodeObjectMeta(codeObj) { if (codeObj.metaObj) return codeObj.metaObj; try { if (typeof codeObj.meta == "undefined" || codeObj.meta == "") { codeObj.metaObj = null; } else { codeObj.metaObj = JSON.parse(codeObj.meta); } } catch(e) { // new empty tickets have no meta //console.log("Error should not happen. Meta is broken. ", codeObj); codeObj.metaObj = null; } return codeObj.metaObj; } function updateCodeObject(codeObj, newCodeObj) { for(var prop in newCodeObj) { codeObj[prop] = newCodeObj[prop]; } codeObj.metaObj = null; } function closeDialog(dlg) { try { $(dlg).dialog("close"); } catch(e) {} $(dlg).html(''); try { $(dlg).dialog("destroy"); } catch(e) {} $(dlg).remove(); } function getUseFulVideosHTML() { return '

Useful videos

'; } function getAuthtokens(cbf) { if (DATA_AUTHTOKENS !== null) cbf && cbf(); _makeGet('getAuthtokens', {}, data=>{ DATA_AUTHTOKENS = data; cbf && cbf(DATA_AUTHTOKENS); }); } function _displayAuthTokensArea() { STATE = 'authtokens'; DIV.html(''); DIV.append(getBackButtonDiv()); DIV.append('

'+_x('Auth Token', 'label', 'event-tickets-with-ticket-scanner')+'

'); $('

').html(__('You can add auth tokens, that can be used to access your ticket scanner. Create an auth token and pass the QR code to the user or let the user scan it from your admin area. The used auth token will bypass any access restriction settings for the ticket scanner that are set in the options.', 'event-tickets-with-ticket-scanner')).appendTo(DIV); $('

').html(__('The user scan the QR code for the auth token with the ticket scanner. Just like a normal ticket. The system will store the auth token to the browser.', 'event-tickets-with-ticket-scanner')).appendTo(DIV); let loading = $('

').html(_getSpinnerHTML()).appendTo(DIV); let div2 = $('
').appendTo(DIV); let tplace = $('
'); getOptionsFromServer(reply=>{ let tabelle_authtokens_datatable; let btn_new = $('