{"id":808,"date":"2025-09-08T17:00:08","date_gmt":"2025-09-08T15:00:08","guid":{"rendered":"https:\/\/24hr-fitness.eu\/?page_id=808"},"modified":"2025-09-08T18:34:31","modified_gmt":"2025-09-08T16:34:31","slug":"multi-sports","status":"publish","type":"page","link":"https:\/\/24hr-fitness.eu\/sk\/multi-sports\/","title":{"rendered":"<p>Multi-Sports<\/p>"},"content":{"rendered":"<div class=\"multisport-wrap\">\r\n    <div class=\"language-toggle\">\r\n      <button id=\"ms-langBtn\" type=\"button\">Slov&aacute;k<\/button>\r\n    <\/div>\r\n\r\n    <div class=\"container\">\r\n      <a href=\"\/sk\/\"><img decoding=\"async\" src=\"https:\/\/24hr-fitness.eu\/wp-content\/plugins\/multisport-gymsys\/assets\/images\/logo.png\" id=\"logo_url\" alt=\"My Logo\" class=\"logo\" style=\"width:80%;max-width:300px;\"><\/a>\r\n      <h1 id=\"ms-title\">Registration &amp; Entry Ticket Terminal<\/h1>\r\n\r\n      <div class=\"camera-container\" style=\"display: block;\">\r\n        <video id=\"ms-camera\" autoplay playsinline style=\"display: none;\"><\/video>\r\n        <canvas id=\"ms-canvas\" style=\"display: none;\"><\/canvas>\r\n        <div id=\"ms-camera-ui\">\r\n          <button id=\"ms-start-camera\" class=\"ms-button\">Start Camera<\/button>\r\n          <button id=\"ms-capture-photo\" class=\"ms-button\" style=\"display: none;\">Capture Photo<\/button>\r\n          <button id=\"ms-retake-photo\" class=\"ms-button\" style=\"display: none;\">Retake Photo<\/button>\r\n          <button id=\"ms-confirm-photo\" class=\"ms-button\" style=\"display: none;\">Confirm Photo<\/button>\r\n        <\/div>\r\n        <div class=\"camera-instructions\" id=\"ms-camera-instructions\">\r\n          Pros&iacute;m odfotografujte sa na overenie | Please take your photo for verification\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <div class=\"scanner-container\" style=\"display: none;\">\r\n        <div class=\"face-preview-container\">\r\n          <img id=\"ms-face-preview\" style=\"display: none; max-width: 200px; border-radius: 8px; margin: 10px auto;\" alt=\"Face Preview\">\r\n        <\/div>\r\n        <input type=\"password\" id=\"ms-qrInput\" placeholder=\"Scan Your Multisport QR Code Here To Register and\/Or Get Your Entry Ticket.\" autofocus>\r\n        <div class=\"scan-text\"><\/div>\r\n        <div id=\"ms-visit-info\" class=\"visit-card\"><\/div>\r\n        <div id=\"ms-qr-container\" style=\"text-align:center;margin-top:20px;\"><\/div>\r\n\r\n        <div id=\"ms-timeout-warning\" style=\"display:none;\">\r\n          <div id=\"ms-timeout-text\">Will cancel in 3:00<\/div>\r\n          <div id=\"ms-timeout-bar\"><div id=\"ms-timeout-progress\"><\/div><\/div>\r\n        <\/div>\r\n\r\n        <div id=\"ms-loading-overlay\"><div class=\"spinner\"><\/div><\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <style>\r\n    .multisport-wrap { justify-content:center; align-items:center; background:#1e1e1e; color:#fff; font-family:Arial,sans-serif; }\r\n    .multisport-wrap .scanner-container { text-align:center; width:80%; margin:auto; }\r\n    .multisport-wrap #ms-qrInput { width:80%; padding:15px; font-size:20px; border:2px solid #333; border-radius:8px; text-align:center; outline:none; background:#282828; color:#fff; transition:all .3s ease; }\r\n    .multisport-wrap #ms-qrInput:focus { border-color:#0caa41; box-shadow:0 0 10px #0caa41; }\r\n    .multisport-wrap .scan-text { margin-top:15px; margin-bottom:10px; font-size:20px; opacity:.8; }\r\n    .multisport-wrap .scanning { animation: ms-blink 1s infinite alternate; }\r\n    @keyframes ms-blink { from{opacity:1;} to{opacity:.5;} }\r\n    .multisport-wrap #ms-timeout-bar { width:80%; margin:10px auto; height:8px; background:#333; border-radius:4px; overflow:hidden; }\r\n    .multisport-wrap #ms-timeout-progress { width:100%; background:#0caa41; transition:width 1s linear; }\r\n    .multisport-wrap #ms-loading-overlay { display:none; position:fixed; inset:0; background:rgba(0,0,0,.5); z-index:9999; align-items:center; justify-content:center; }\r\n    .multisport-wrap #ms-loading-overlay .spinner { width:48px; height:48px; border:5px solid #fff; border-top-color:transparent; border-radius:50%; animation: ms-spin 1s linear infinite; }\r\n    @keyframes ms-spin { to { transform: rotate(360deg);} }\r\n\r\n    \/* Camera UI Styles *\/\r\n    .multisport-wrap .camera-container { width: 100%; max-width: 640px; margin: 0 auto 20px; text-align: center; }\r\n    .multisport-wrap #ms-camera { width: 100%; max-width: 640px; height: auto; margin: 0 auto; border-radius: 8px; }\r\n    .multisport-wrap #ms-canvas { width: 100%; max-width: 640px; height: auto; margin: 0 auto; border-radius: 8px; }\r\n    .multisport-wrap .face-preview-container { text-align: center; margin: 10px auto; }\r\n    .multisport-wrap #ms-face-preview { border: 2px solid #0caa41; box-shadow: 0 2px 8px rgba(12, 170, 65, 0.2); }\r\n    .multisport-wrap .ms-button { \r\n      background: #0caa41; color: #fff; border: none; padding: 10px 20px; \r\n      border-radius: 4px; margin: 10px 5px; cursor: pointer; font-size: 16px;\r\n      transition: background-color 0.3s ease;\r\n    }\r\n    .multisport-wrap .ms-button:hover { background: #098a35; }\r\n    .multisport-wrap .ms-button:disabled { background: #666; cursor: not-allowed; }\r\n    .multisport-wrap .camera-instructions {\r\n      margin: 15px 0; padding: 10px; background: rgba(12, 170, 65, 0.1);\r\n      border-radius: 4px; color: #0caa41; font-size: 16px;\r\n    }\r\n  <\/style><link rel=\"stylesheet\" href=\"https:\/\/24hr-fitness.eu\/wp-content\/plugins\/multisport-gymsys\/assets\/css\/custom-style.css\"><script>\r\n  (function(){\r\n    const MS_CONFIG = {\r\n      uploadEndpoint: \"https:\/\/24hr-fitness.eu\/sk\/wp-json\/multisport\/v1\/upload\",\r\n      verifyEndpoint: \"https:\/\/24hr-fitness.eu\/sk\/wp-json\/multisport\/v1\/verify\",\r\n      deleteEndpoint: \"https:\/\/24hr-fitness.eu\/sk\/wp-json\/multisport\/v1\/deleteVisits\",\r\n      registerUserEndpoint: \"https:\/\/24hr-fitness.eu\/sk\/wp-json\/multisport\/v1\/register-user\",\r\n      scanAgainEndpoint: \"https:\/\/24hr-fitness.eu\/sk\/wp-json\/multisport\/v1\/scan-again\",\r\n    };\r\n\r\n    let allowFocus = true;\r\n    const input       = document.getElementById(\"ms-qrInput\");\r\n    const scanText    = document.querySelector(\".multisport-wrap .scan-text\") || document.querySelector(\".scan-text\");\r\n    const visitInfo   = document.getElementById(\"ms-visit-info\");\r\n    const qrContainer = document.getElementById(\"ms-qr-container\");\r\n    const loading     = document.getElementById(\"ms-loading-overlay\");\r\n    const timeoutWrap = document.getElementById(\"ms-timeout-warning\");\r\n    const timeoutText = document.getElementById(\"ms-timeout-text\");\r\n    const timeoutProg = document.getElementById(\"ms-timeout-progress\");\r\n    const titleEl     = document.getElementById(\"ms-title\");\r\n    const langBtn     = document.getElementById(\"ms-langBtn\");\r\n\r\n    \/\/ Camera elements\r\n    const cameraContainer = document.querySelector(\".camera-container\");\r\n    const scannerContainer = document.querySelector(\".scanner-container\");\r\n    const video = document.getElementById(\"ms-camera\");\r\n    const canvas = document.getElementById(\"ms-canvas\");\r\n    const startButton = document.getElementById(\"ms-start-camera\");\r\n    const captureButton = document.getElementById(\"ms-capture-photo\");\r\n    const retakeButton = document.getElementById(\"ms-retake-photo\");\r\n    const confirmButton = document.getElementById(\"ms-confirm-photo\");\r\n    const cameraInstructions = document.getElementById(\"ms-camera-instructions\");\r\n\r\n    let stream = null;\r\n    let photoData = null;\r\n    \r\n    \/\/ Camera functions\r\n    async function startCamera() {\r\n      const logoImage = document.getElementById('logo_url');\r\n      if (logoImage) {\r\n        logoImage.style.display = 'none';\r\n      }\r\n      \/\/ Visual feedback that we're trying to start the camera\r\n      startButton.disabled = true;\r\n      cameraInstructions.style.color = '#0caa41';\r\n      cameraInstructions.textContent = currentLang === 'sk' ? \r\n        'Sp&uacute;??a sa kamera...' : \r\n        'Starting camera...';\r\n\r\n      \/\/ Feature detection\r\n      if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {\r\n        console.warn('getUserMedia not supported');\r\n        startButton.disabled = false;\r\n        cameraInstructions.style.color = '#ff4444';\r\n        cameraInstructions.textContent = currentLang === 'sk' ? \r\n          'Tento prehliada? nepodporuje pr&iacute;stup ku kamere. Sk&uacute;ste in&yacute; prehliada? (Chrome, Firefox, Edge).' :\r\n          'This browser does not support camera access. Try a different browser (Chrome, Firefox, Edge).';\r\n        return;\r\n      }\r\n\r\n      try {\r\n        \r\n        \/\/ Try preferred facingMode first, fallback to generic video:true if it fails\r\n        const constraints = { video: { facingMode: 'user' }, audio: false };\r\n        try {\r\n          stream = await navigator.mediaDevices.getUserMedia(constraints);\r\n        } catch (e) {\r\n          console.warn('facingMode failed, retrying with generic video constraint', e);\r\n          stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: false });\r\n        }\r\n\r\n        video.srcObject = stream;\r\n        try { \r\n          \r\n          await video.play();\r\n        } catch (e) {\r\n          throw new Error(currentLang === 'sk' ? \r\n            'Nepodarilo sa spusti? video stream' : \r\n            'Failed to start video stream');\r\n        }\r\n\r\n        video.style.display = 'block';\r\n        startButton.style.display = 'none';\r\n        captureButton.style.display = 'inline-block';\r\n        cameraInstructions.style.color = '#0caa41';\r\n        cameraInstructions.textContent = currentLang === 'sk' ?\r\n          'Umiestnite svoju tv&aacute;r do stredu obrazovky' :\r\n          'Position your face in the center of the frame';\r\n\r\n      } catch (err) {\r\n        console.error('Camera error:', err);\r\n        startButton.disabled = false;\r\n        cameraInstructions.style.color = '#ff4444';\r\n       \r\n        if (logoImage) {\r\n          logoImage.style.display = 'block';\r\n        }\r\n        \/\/ Specific error messages for common cases\r\n        let errorMsg = '';\r\n        if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {\r\n          errorMsg = currentLang === 'sk' ?\r\n            'Pr&iacute;stup ku kamere bol zamietnut&yacute;. Kliknite na ikonu kamery v prehliada?i a povo?te pr&iacute;stup.' :\r\n            'Camera access was denied. Click the camera icon in your browser and allow access.';\r\n        } else if (err.name === 'NotFoundError' || err.name === 'DevicesNotFoundError') {\r\n          errorMsg = currentLang === 'sk' ?\r\n            'Nena?la sa ?iadna kamera. Pripojte kameru a sk&uacute;ste to znova.' :\r\n            'No camera found. Please connect a camera and try again.';\r\n        } else if (err.name === 'NotReadableError' || err.name === 'TrackStartError') {\r\n          errorMsg = currentLang === 'sk' ?\r\n            'Kamera je pou?&iacute;van&aacute; inou aplik&aacute;ciou. Zatvorte ostatn&eacute; aplik&aacute;cie pou?&iacute;vaj&uacute;ce kameru.' :\r\n            'Camera is in use by another application. Close other apps using the camera.';\r\n        } else {\r\n          errorMsg = currentLang === 'sk' ?\r\n            `Nepodarilo sa spusti? kameru: ${err.message || 'Nezn&aacute;ma chyba'}` :\r\n            `Failed to start camera: ${err.message || 'Unknown error'}`;\r\n        }\r\n        \r\n        cameraInstructions.textContent = errorMsg;\r\n      }\r\n    }\r\n\r\n    function stopCamera() {\r\n      if (stream) {\r\n        stream.getTracks().forEach(track => track.stop());\r\n        video.srcObject = null;\r\n      }\r\n    }\r\n\r\n    function capturePhoto() {\r\n      canvas.width = video.videoWidth;\r\n      canvas.height = video.videoHeight;\r\n      canvas.getContext('2d').drawImage(video, 0, 0);\r\n      photoData = canvas.toDataURL('image\/jpeg');\r\n      video.style.display = \"none\";\r\n      canvas.style.display = \"block\";\r\n      captureButton.style.display = \"none\";\r\n      retakeButton.style.display = \"inline-block\";\r\n      confirmButton.style.display = \"inline-block\";\r\n      if (currentLang === 'sk') {\r\n        cameraInstructions.textContent = \"Potvr?te fotografiu alebo ju nasn&iacute;majte znova\";\r\n      } else {\r\n        cameraInstructions.textContent = \"Confirm photo or retake\";\r\n      }\r\n    }\r\n\r\n    function retakePhoto() {\r\n      canvas.style.display = \"none\";\r\n      video.style.display = \"block\";\r\n      retakeButton.style.display = \"none\";\r\n      confirmButton.style.display = \"none\";\r\n      captureButton.style.display = \"inline-block\";\r\n      photoData = null;\r\n      if (currentLang === 'sk') {\r\n        cameraInstructions.textContent = \"Umiestnite svoju tv&aacute;r do stredu obrazovky\";\r\n      } else {\r\n        cameraInstructions.textContent = \"Position your face in the center of the frame\";\r\n      }\r\n    }\r\n\r\n    function confirmPhoto() {\r\n      stopCamera();\r\n      const logoImage = document.getElementById('logo_url');\r\n      if (logoImage) {\r\n        logoImage.style.display = 'none';\r\n      }\r\n      cameraContainer.style.display = \"none\";\r\n      scannerContainer.style.display = \"block\";\r\n\r\n      \/\/ Show the captured photo in the preview\r\n      const facePreview = document.getElementById('ms-face-preview');\r\n      facePreview.src = photoData;\r\n      facePreview.style.display = \"block\";\r\n\r\n      \/\/ Display countdown in the top-right corner\r\n      const countdownDiv = document.createElement('div');\r\n      countdownDiv.id = 'countdown';\r\n      countdownDiv.style.position = 'absolute';\r\n      countdownDiv.style.top = '10px';\r\n      countdownDiv.style.right = '10px';\r\n      countdownDiv.style.fontSize = '3.5em';\r\n      countdownDiv.style.color = '#ff0000';\r\n      document.body.appendChild(countdownDiv);\r\n\r\n      let countdown = 180;\r\n      const interval = setInterval(() => {\r\n        countdownDiv.textContent = countdown;\r\n        countdown--;\r\n        if (countdown < 0) {\r\n          clearInterval(interval);\r\n          location.reload();\r\n        }\r\n      }, 1000);\r\n\r\n      if (currentLang === 'sk') {\r\n        cameraInstructions.textContent = \"Teraz naskenujte svoj QR k&oacute;d\";\r\n      } else {\r\n        cameraInstructions.textContent = \"Now scan your QR code\";\r\n      }\r\n    }\r\n\r\n    \/\/ Camera event listeners\r\n    if (startButton) startButton.addEventListener('click', startCamera);\r\n    if (captureButton) captureButton.addEventListener('click', capturePhoto);\r\n    if (retakeButton) retakeButton.addEventListener('click', retakePhoto);\r\n    if (confirmButton) confirmButton.addEventListener('click', confirmPhoto);\r\n\r\n    function keepFocused(){ if(!allowFocus) return; if(document.activeElement !== input){ input.focus(); } }\r\n    function showLoading(){ loading.style.display = 'flex'; }\r\n    function hideLoading(){ loading.style.display = 'none'; }\r\n\r\n    let countdownInterval = null, timeoutHandle = null;\r\n    const totalSeconds = 180;\r\n    let currentLang = 'en';\r\n    function startCountdownTimer(){\r\n      let secondsLeft = totalSeconds;\r\n      timeoutWrap.style.display = 'block';\r\n      clearInterval(countdownInterval); clearTimeout(timeoutHandle);\r\n      countdownInterval = setInterval(() => {\r\n        secondsLeft--;\r\n        const m = Math.floor(secondsLeft\/60);\r\n        const s = String(secondsLeft%60).padStart(2,'0');\r\n        timeoutText.textContent = (currentLang==='en') ? `Will cancel in ${m}:${s}` : `Zru?&iacute; sa o ${m}:${s}`;\r\n        timeoutProg.style.width = ((secondsLeft\/totalSeconds)*100)+'%';\r\n        if (secondsLeft<=0) clearInterval(countdownInterval);\r\n      }, 1000);\r\n      timeoutHandle = setTimeout(()=>{ qrContainer.style.display='none'; location.reload(); }, totalSeconds*1000);\r\n    }\r\n    function cancelCountdownTimer(){ clearInterval(countdownInterval); clearTimeout(timeoutHandle); timeoutWrap.style.display='none'; }\r\n    function isValidEmail(e){ return \/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$\/.test(e); }\r\n    function toggleLanguage(){\r\n      if (currentLang==='en') {\r\n        titleEl.textContent = 'Termin&aacute;l registr&aacute;cie a vstupeniek';\r\n        const sb=document.getElementById('submitTicketBtn'), cb=document.getElementById('cancelVisitBtn'), em=document.getElementById('email'), ph=document.getElementById('phone');\r\n        if (sb) sb.textContent='Odosla? l&iacute;stok';\r\n        if (cb) cb.textContent='Zru?i? n&aacute;v?tevu';\r\n        if (em) em.placeholder='Zadajte svoj email';\r\n        if (ph) ph.placeholder='Zadajte svoje telef&oacute;nne ?&iacute;slo';\r\n        input.placeholder = 'Naskenujte svoj Multisport QR k&oacute;d tu a zaregistrujte sa a\/alebo z&iacute;skajte vstupenku.';\r\n        startButton.textContent = 'Spusti? kameru';\r\n        captureButton.textContent = 'Nasn&iacute;ma? fotografiu';\r\n        retakeButton.textContent = 'Znova nasn&iacute;ma?';\r\n        confirmButton.textContent = 'Potvrdi? fotografiu';\r\n        if (video.style.display === 'block') {\r\n          cameraInstructions.textContent = 'Umiestnite svoju tv&aacute;r do stredu obrazovky';\r\n        } else if (canvas.style.display === 'block') {\r\n          cameraInstructions.textContent = 'Potvr?te fotografiu alebo ju nasn&iacute;majte znova';\r\n        } else {\r\n          cameraInstructions.textContent = 'Pros&iacute;m, nasn&iacute;majte svoju fotografiu pre overenie';\r\n        }\r\n        langBtn.textContent='English'; currentLang='sk';\r\n      } else {\r\n        titleEl.textContent = 'Registration & Entry Ticket Terminal';\r\n        const sb=document.getElementById('submitTicketBtn'), cb=document.getElementById('cancelVisitBtn'), em=document.getElementById('email'), ph=document.getElementById('phone');\r\n        if (sb) sb.textContent='Submit Ticket';\r\n        if (cb) cb.textContent='Cancel Visit';\r\n        if (em) em.placeholder='Enter your email';\r\n        if (ph) ph.placeholder='Enter your phone number';\r\n        input.placeholder = 'Scan Your Multisport QR Code Here To Register and\/Or Get Your Entry Ticket.';\r\n        startButton.textContent = 'Start Camera';\r\n        captureButton.textContent = 'Take Photo';\r\n        retakeButton.textContent = 'Retake Photo';\r\n        confirmButton.textContent = 'Confirm Photo';\r\n        if (video.style.display === 'block') {\r\n          cameraInstructions.textContent = 'Position your face in the center of the frame';\r\n        } else if (canvas.style.display === 'block') {\r\n          cameraInstructions.textContent = 'Confirm photo or retake';\r\n               } else {\r\n          cameraInstructions.textContent = 'Pros&iacute;m odfotografujte sa na overenie | Please take your photo for verification';\r\n        }\r\n        langBtn.textContent='Slov&aacute;k'; currentLang='en';\r\n      }\r\n    }\r\n    langBtn.addEventListener('click', toggleLanguage);\r\n\r\n    const errorMessages = {\r\n      PartnerFacilityServiceInvalid: {\r\n        en: 'Provided service UUID is wrong',\r\n        sk: 'Poskytnut&eacute; UUID slu?by je chybn&eacute;'\r\n      },\r\n      AccountIsBlocked: {\r\n        en: 'Partner account (partner UUID) is blocked',\r\n        sk: 'Partner nie je akt&iacute;vny'\r\n      },\r\n      FacilityIsNotActive: {\r\n        en: 'Facility inactive on provided date',\r\n        sk: '?portovisko nie je akt&iacute;vne.'\r\n      },\r\n      CheckIfCardBarCodeExists: {\r\n        en: 'Card does not exist on provided date',\r\n        sk: 'Karta neexistuje v syst&eacute;me MultiSport.'\r\n      },\r\n      CardBlockedForFraud: {\r\n        en: 'Due to technical problems, the card could not be loaded, please contact the MultiSport Benefit helpline.',\r\n        sk: 'Kv&ocirc;li technick&yacute;m probl&eacute;mom nebolo mo?n&eacute; na?&iacute;ta? kartu MultiSport, kontaktujte pros&iacute;m infolinku MultiSport.'\r\n      },\r\n      CheckClientStatus: {\r\n        en: 'Client (card holder\\'s employer) not active on provided date',\r\n        sk: 'Klient nie je akt&iacute;vny.'\r\n      },\r\n      ClientAccessToPartner: {\r\n        en: 'No access for specific client (card holder\\'s employer) to that facility',\r\n        sk: 'T&aacute;to karta nem&ocirc;?e by? pou?it&aacute; v tomto zariaden&iacute;.'\r\n      },\r\n      CheckCardStatus: { \r\n        en: 'Card inactive on provided date',\r\n        sk: 'Karta nie je akt&iacute;vna.'\r\n      },\r\n      CheckForVisitRegisterInSelectedDay: {\r\n        en: 'Limit of number of visits for speficic activity reached for provided date',\r\n        sk: 'Limit n&aacute;v?tev na tejto karte u? bol vy?erpan&yacute;.'\r\n      },\r\n      FacilityHourLimit: {\r\n        en: 'Visit attempt outside of allowed visit hours',\r\n        sk: 'Pokus o n&aacute;v?tevu mimo povolen&yacute;ch n&aacute;v?tevn&yacute;ch hod&iacute;n.'\r\n      },\r\n      FacilityVisitsLimit: {\r\n        en: 'Allowed number of visits within billing period for specific facility reached',\r\n        sk: 'Partner prekro?il povolen&yacute; limit n&aacute;v?tev za mesiac.'\r\n      },\r\n      CardTypeLimit: {\r\n        en: 'Card type (Employee, Accompany, Child, etc.) not allowed for specific activity or facility',\r\n        sk: 'Tento typ karty nem&ocirc;?e by? pou?it&yacute; na dan&uacute; aktivitu.'\r\n      },\r\n      ServiceIsNotActive: {\r\n        en: 'Service within provided facility is disabled for provided date',\r\n        sk: 'Slu?ba nie je akt&iacute;vna.'\r\n      },\r\n      ShortTimeLimit: {\r\n        en: 'Attempt to register the same visit within short amount of time. Do not try again, visit already registered (usually happens with network errors or hardware terminal malfunctions)',\r\n        sk: 'T&aacute;to n&aacute;v?teva u? bola zaregistrovan&aacute; v priebehu posledn&yacute;ch 10 sek&uacute;nd.'\r\n      },\r\n      UserPhotoNotVerified: {\r\n        en: 'User photo in mobile application is not verified. Verification can be done only via PartnerZone MultiSport.',\r\n        sk: 'Fotka u?&iacute;vate?a nebola zatia? overen&aacute;. Overenie fotografie je potrebn&eacute; realizova? v partnerskej z&oacute;ne MultiSport.'\r\n      },\r\n      VirtualAccessOnOtherDevice: {\r\n        en: 'User has active card on another mobile device',\r\n        sk: 'U?&iacute;vate? m&aacute; virtu&aacute;lnu kartu MultiSport aktivovan&uacute; na inom zariaden&iacute;.'\r\n      },\r\n      VirtualAccessDisabled: {\r\n        en: 'User moved virtual card back to physical plastic card. Please scan plastic card.',\r\n        sk: 'U?&iacute;vate? m&aacute; akt&iacute;vnu plastov&uacute; kartu MultiSport. Na?&iacute;tajte plastov&uacute; kartu MultiSport.'\r\n      },\r\n      RegistrationCodeExpired: {\r\n        en: 'Validity of QR code expired. Please try to scan current QR code from mobile application.',\r\n        sk: 'Platnos? QR k&oacute;du vypr?ala. Sk&uacute;ste na?&iacute;ta? aktu&aacute;lny QR k&oacute;d z aplik&aacute;cie MyMultiSport.'\r\n      },\r\n      PlasticCardAccessDisabled: {\r\n        en: 'User moved plastic card to virtual card in mobile device. Please scan virtual card.',\r\n        sk: 'U?&iacute;vate? m&aacute; akt&iacute;vnu virtu&aacute;lnu kartu MultiSport v mobilnom zariaden&iacute;. Na?&iacute;tajte QR k&oacute;d z aplik&aacute;cie MyMultiSport.'\r\n      },\r\n      ReadingCardDataFailed: {\r\n        en: 'Invalid QR code or Barcode',\r\n        sk: 'Neplatn&yacute; QR k&oacute;d alebo Barcode'\r\n      },\r\n      Exception: {\r\n        en: 'Unexpected exception',\r\n        sk: 'Neo?ak&aacute;van&aacute; chyba'\r\n      },\r\n      FacilityAcceptsOnlyVirtualCardVisits: {\r\n        en: 'This facility accepts only visits with virtual MultiSport card.',\r\n        sk: 'Toto zariadenie akceptuje iba vstupy s virtu&aacute;lnou kartou MultiSport.'\r\n      },\r\n      default: {\r\n        en: 'Error',\r\n        sk: 'Chyba'\r\n      }\r\n    };\r\n\r\n    function displayScanMessage(data) {\r\n      \/\/ Accept either a raw string (e.g. 'RegistrationCodeExpired') or an object with a `.message`.\r\n      \/\/ Normalize message (remove quotes, trim spaces, and take only code part)\r\n      const raw = (typeof data === 'string') ? data : (data?.message || '');\r\n      let key = (raw || '').trim();\r\n      key = key.split(':')[0].replace(\/['\"]+\/g, '').trim();\r\n\r\n      \/\/ 1) Exact match (fast path)\r\n      if (Object.prototype.hasOwnProperty.call(errorMessages, key)) {\r\n        return errorMessages[key];\r\n      }\r\n\r\n      \/\/ 2) Case-insensitive exact match\r\n      const lowerKey = key.toLowerCase();\r\n      const ciExact = Object.keys(errorMessages).find(k => k.toLowerCase() === lowerKey);\r\n      if (ciExact) return errorMessages[ciExact];\r\n\r\n      \/\/ 3) Partial match: find a known error key that is included in the message key\r\n      \/\/    This uses `includes` and is case-insensitive. Skip the 'default' key.\r\n      const partial = Object.keys(errorMessages).find(k => k !== 'default' && lowerKey.includes(k.toLowerCase()));\r\n      if (partial) return errorMessages[partial];\r\n\r\n      \/\/ 4) Fallback to default\r\n      return errorMessages.default;\r\n    }\r\n\r\n    let timer=null, currentAction = 'initial'; \/\/ Track current UI state\r\n    input.addEventListener(\"input\", function(e){\r\n      const qrCode=e.target.value;\r\n      if(!qrCode) return;\r\n      clearTimeout(timer);\r\n      timer=setTimeout(()=>{\r\n        showLoading();\r\n        \r\n        \/\/ Determine which endpoint to call based on current action\r\n        let endpoint = MS_CONFIG.uploadEndpoint;\r\n        let requestData = { qrData: qrCode, mode: 'live', faceData: photoData };\r\n        \r\n        if (currentAction === 'scan_again') {\r\n          endpoint = MS_CONFIG.scanAgainEndpoint;\r\n          requestData = { qrData: qrCode, mode: 'live' };\r\n        }\r\n        \r\n        fetch(endpoint, {\r\n          method:'POST', \r\n          headers: {'Content-Type':'application\/json'},\r\n          body: JSON.stringify(requestData)\r\n        })\r\n        .then(r=>r.json())\r\n        .then(data=>{\r\n          handleQRScanResponse(data);\r\n        })\r\n        .catch(()=>{ \r\n          hideLoading(); \r\n          scanText.textContent=\"Chyba! Sk&uacute;ste to znova.\\nError! Try again.\"; \r\n        });\r\n      }, 2000);\r\n    });\r\n\r\n    function handleQRScanResponse(data) {\r\n      if(data.status==200){\r\n        if(data.action === 'register_user') {\r\n          \/\/ New user needs to input information\r\n          handleNewUserRegistration(data);\r\n        } else if(data.action === 'scan_again') {\r\n          \/\/ Existing user, face verified, ask to scan again\r\n          handleScanAgain(data);\r\n        } else if(data.action === 'registration_complete') {\r\n          \/\/ Registration completed, reset to scan again\r\n          handleRegistrationComplete(data);\r\n        } else if(data.user && data.user.email) {\r\n          \/\/ Direct entry (existing member)\r\n          handleDirectEntry(data);\r\n        } else if(data.data && !data.user) {\r\n          \/\/ New visitor flow (legacy)\r\n          handleNewVisitor(data);\r\n        } else if(data.registered_user) {\r\n          \/\/ Entry granted for registered user\r\n          handleEntryGranted(data);\r\n        }\r\n      } else { \r\n        hideLoading(); \r\n        \r\n        \/\/ Handle face verification specific errors\r\n        if (data.error_type === 'face_mismatch') {\r\n          handleFaceVerificationError(data);\r\n        } else if (data.error_type === 'verification_system_error') {\r\n          handleVerificationSystemError(data);\r\n        } else {\r\n          \/\/ Handle other errors using existing logic\r\n          const err_message = displayScanMessage(data.message);\r\n          scanText.textContent = `${err_message.sk}\\n${err_message.en}`;\r\n        }\r\n      }\r\n    }\r\n\r\n    function handleFaceVerificationError(data) {\r\n      allowFocus = false;\r\n      startCountdownTimer();\r\n      \r\n      \/\/ Display detailed error message\r\n      scanText.style.color = '#ff4444';\r\n      scanText.textContent = data.message || 'Overenie tv&aacute;re zlyhalo | Face verification failed';\r\n      \r\n      \/\/ Show error details in visit info\r\n      let errorDetails = `\r\n        <div style=\"background: rgba(255,68,68,0.1); border: 2px solid #ff4444; border-radius: 8px; padding: 20px; margin: 20px 0;\">\r\n          <h3 style=\"color: #ff4444; margin-top: 0;\">? Overenie tv&aacute;re zlyhalo | Face Verification Failed\r\n          <p><strong>Detaily chyby | Error Details:\r\n          <ul style=\"text-align: left; margin: 10px 0;\">\r\n            <li>Zachyten&aacute; fotografia sa nezhoduje s registrovanou | The captured photo does not match your registered image`;\r\n      \r\n      if (data.distance !== null) {\r\n        errorDetails += `<li>Vzdialenos? podobnosti: ${data.distance.toFixed(3)} | Similarity distance: ${data.distance.toFixed(3)}`;\r\n      }\r\n      \r\n      if (data.verification_details) {\r\n        errorDetails += `<li>Registrovan&yacute; u?&iacute;vate?: ${data.verification_details.user_name || 'Nezn&aacute;my'} | Registered user: ${data.verification_details.user_name || 'Unknown'}`;\r\n        errorDetails += `<li>D&aacute;tum registr&aacute;cie: ${data.verification_details.registered_date || 'Nezn&aacute;my'} | Registration date: ${data.verification_details.registered_date || 'Unknown'}`;\r\n      }\r\n      \r\n      errorDetails += `\r\n          \r\n          <p style=\"margin-top: 15px;\"><strong>Sk&uacute;ste znova s: | <strong>Please try again with:\r\n          <ul style=\"text-align: left;\">\r\n            <li>Better lighting on your face | Lep?&iacute;m osvetlen&iacute;m tv&aacute;re\r\n            <li>Face clearly visible to camera | Tv&aacute;r jasne vidite?n&aacute; pre kameru\r\n            <li>Make sure you are the registered person | Uistite sa, ?e ste registrovan&aacute; osoba\r\n          \r\n          <button id=\"retryFaceVerification\" style=\"background: #0caa41; color: white; border: none; padding: 10px 20px; border-radius: 4px; margin-top: 15px; cursor: pointer;\">\r\n            Try Again | Sk&uacute;si? znova\r\n          \r\n          <button id=\"cancelFaceVerification\" style=\"background: #666; color: white; border: none; padding: 10px 20px; border-radius: 4px; margin-top: 15px; margin-left: 10px; cursor: pointer;\">\r\n            Cancel | Zru?i?\r\n          \r\n        \r\n      `;\r\n      \r\n      visitInfo.innerHTML = errorDetails;\r\n      visitInfo.style.display = 'block';\r\n      \r\n      \/\/ Add event listeners for buttons\r\n      document.getElementById('retryFaceVerification').addEventListener('click', () => {\r\n        resetToInitial();\r\n      });\r\n      \r\n      document.getElementById('cancelFaceVerification').addEventListener('click', () => {\r\n        resetToInitial();\r\n      });\r\n    }\r\n\r\n    function handleVerificationSystemError(data) {\r\n      allowFocus = false;\r\n      startCountdownTimer();\r\n      \r\n      \/\/ Display system error message\r\n      scanText.style.color = '#ff8800';\r\n      scanText.textContent = data.message || 'Face verification system error | Syst&eacute;mov&aacute; chyba overovania tv&aacute;re';\r\n      \r\n      \/\/ Show system error details\r\n      let errorDetails = `\r\n        <div style=\"background: rgba(255,136,0,0.1); border: 2px solid #ff8800; border-radius: 8px; padding: 20px; margin: 20px 0;\">\r\n          <h3 style=\"color: #ff8800; margin-top: 0;\">?? System Error | Syst&eacute;mov&aacute; chyba\r\n          <p><strong>The face verification service is currently unavailable.\r\n          <p><strong>Slu?ba overenia tv&aacute;re je moment&aacute;lne nedostupn&aacute;.`;\r\n      \r\n      if (data.technical_error) {\r\n        errorDetails += `\r\n          <details style=\"margin-top: 15px;\">\r\n            <summary style=\"cursor: pointer; color: #ff8800;\">Technical Details | Technick&eacute; detaily\r\n            <p style=\"font-family: monospace; background: #2a2a2a; padding: 10px; border-radius: 4px; margin-top: 10px; word-break: break-all;\">\r\n              ${data.technical_error}\r\n            \r\n          `;\r\n      }\r\n      \r\n      errorDetails += `\r\n          <button id=\"retrySystemError\" style=\"background: #ff8800; color: white; border: none; padding: 10px 20px; border-radius: 4px; margin-top: 15px; cursor: pointer;\">\r\n            Retry | Sk&uacute;si? znova\r\n          \r\n          <button id=\"cancelSystemError\" style=\"background: #666; color: white; border: none; padding: 10px 20px; border-radius: 4px; margin-top: 15px; margin-left: 10px; cursor: pointer;\">\r\n            Cancel | Zru?i?\r\n          \r\n        \r\n      `;\r\n      \r\n      visitInfo.innerHTML = errorDetails;\r\n      visitInfo.style.display = 'block';\r\n      \r\n      \/\/ Add event listeners for buttons\r\n      document.getElementById('retrySystemError').addEventListener('click', () => {\r\n        resetToInitial();\r\n      });\r\n      \r\n      document.getElementById('cancelSystemError').addEventListener('click', () => {\r\n        resetToInitial();\r\n      });\r\n    }\r\n\r\n    function handleNewUserRegistration(data) {\r\n      hideLoading(); \r\n      allowFocus=false; \r\n      startCountdownTimer(); \r\n      scanText.textContent='';\r\n      currentAction = 'register_user';\r\n      \r\n      const content = `\r\n        <p><strong>New User Registration | Registr&aacute;cia nov&eacute;ho pou?&iacute;vate?a\r\n        <p>Pros&iacute;m vypl?te va?e &uacute;daje | Please provide your information:\r\n        <label for=\"firstName\">First Name | Meno\r\n        <input type=\"text\" id=\"firstName\" placeholder=\"Enter first name | Zadajte meno\" required>\r\n        <label for=\"lastName\">Last Name | Priezvisko\r\n        <input type=\"text\" id=\"lastName\" placeholder=\"Enter last name | Zadajte priezvisko\" required>\r\n        <label for=\"email\">Email\r\n        <input type=\"email\" id=\"email\" placeholder=\"Enter your email | Zadajte email\" required>\r\n        <label for=\"phone\">Phone | Telef&oacute;n\r\n        <input type=\"tel\" id=\"phone\" placeholder=\"Enter phone number | Zadajte telef&oacute;n\">\r\n        <label for=\"company\">Company | Spolo?nos?\r\n        <input type=\"text\" id=\"company\" placeholder=\"Enter company name | Zadajte n&aacute;zov spolo?nosti\">\r\n        <button id=\"completeRegistrationBtn\">Complete Registration | Dokon?i? registr&aacute;ciu\r\n        <button id=\"cancelRegistrationBtn\">Cancel | Zru?i?`;\r\n        \r\n      visitInfo.innerHTML=content; \r\n      visitInfo.style.display='block';\r\n\r\n      \/\/ Auto-fill if we have data from MS API\r\n      if(data.data) {\r\n        if(data.data.firstName) document.getElementById('firstName').value = data.data.firstName;\r\n        if(data.data.lastName) document.getElementById('lastName').value = data.data.lastName;\r\n        if(data.data.facilityName) document.getElementById('company').value = data.data.facilityName;\r\n      }\r\n\r\n      \/\/ Focus on the first name input field\r\n      setTimeout(() => {\r\n        const firstNameInput = document.getElementById('firstName');\r\n        if (firstNameInput) {\r\n          firstNameInput.focus();\r\n        }\r\n      }, 100);\r\n\r\n      document.getElementById('completeRegistrationBtn').addEventListener('click', ()=>{\r\n        const firstName = document.getElementById('firstName').value.trim();\r\n        const lastName = document.getElementById('lastName').value.trim();\r\n        const email = document.getElementById('email').value.trim();\r\n        const phone = document.getElementById('phone').value.trim();\r\n        const company = document.getElementById('company').value.trim();\r\n        \r\n        if(!firstName || !lastName || !email) { \r\n          scanText.textContent=\"Pros&iacute;m vypl?te povinn&eacute; polia: Meno, Priezvisko, Email\\nPlease fill in required fields: First Name, Last Name, Email\"; \r\n          return; \r\n        }\r\n        \r\n        if(!isValidEmail(email)){ \r\n          scanText.textContent=\"Neplatn&yacute; e-mail. Zadajte svoj e-mail znova.\\nInvalid Email. Please input your email again.\"; \r\n          return; \r\n        }\r\n        \r\n        showLoading();\r\n        fetch(MS_CONFIG.registerUserEndpoint, {\r\n          method:'POST', \r\n          headers: {'Content-Type':'application\/json'},\r\n          body: JSON.stringify({\r\n            fixed_qr_code: data.fixed_qr_code,\r\n            face_image_url: data.face_image_url,\r\n            first_name: firstName,\r\n            last_name: lastName,\r\n            email: email,\r\n            phone: phone,\r\n            company_name: company\r\n          })\r\n        })\r\n        .then(r=>r.json())\r\n        .then(regData=>{\r\n          handleQRScanResponse(regData);\r\n        })\r\n        .catch(()=>{ \r\n          hideLoading();\r\n          scanText.textContent=\"Chyba! Sk&uacute;ste to znova.\\nError! Try again.\"; \r\n        });\r\n      });\r\n\r\n      document.getElementById('cancelRegistrationBtn').addEventListener('click', ()=>{\r\n        resetToInitial();\r\n      });\r\n    }\r\n\r\n    function handleScanAgain(data) {\r\n      hideLoading();\r\n      allowFocus=true; \r\n      input.focus(); \r\n      input.value=\"\"; \r\n      cancelCountdownTimer();\r\n      currentAction = 'scan_again';\r\n      \r\n      scanText.textContent = data.message + '\\n\\nWelcome back, ' + \r\n        (data.user_data.first_name + ' ' + data.user_data.last_name).trim() + \r\n        (data.user_data.company_name ? ' (' + data.user_data.company_name + ')' : '');\r\n      visitInfo.style.display='none';\r\n    }\r\n\r\n    function handleRegistrationComplete(data) {\r\n      hideLoading();\r\n      allowFocus=true; \r\n      input.focus(); \r\n      input.value=\"\"; \r\n      cancelCountdownTimer();\r\n      currentAction = 'scan_again';\r\n      \r\n      scanText.textContent = data.message;\r\n      visitInfo.style.display='none';\r\n    }\r\n\r\n    function handleDirectEntry(data) {\r\n      hideLoading(); \r\n      scanText.textContent=''; \r\n      visitInfo.style.display='none';\r\n      allowFocus=true; \r\n      input.focus(); \r\n      input.value=\"\"; \r\n      cancelCountdownTimer();\r\n      currentAction = 'initial';\r\n      \r\n      const today=new Date().toISOString().split('T')[0];\r\n      const htmlContent = `\r\n        <!DOCTYPE html><html><head><title>No: ${data.user.lastid}\r\n        <style>@media print{body{width:6.5in!important}@page{size:6.5in 11in!important;margin:.5in}label:after{content:none!important}}\r\n        <body onload=\"window.print()\" onclick=\"window.print()\">\r\n        <div style=\"text-align:center;padding:0;margin:0;page-break-before:always\">\r\n          <div><img decoding=\"async\" src=\"${data.user.img_bg_path}\" style=\"margin:0;padding:0;height:130px\">\r\n          <p style=\"color:#0ba0bb;font-size:35px;margin:15px\">${data.user.subject}\r\n          <p style=\"color:#0ba0bb;font-size:25spx;margin:15px\">Card No: ${data.user.ticket_number}\r\n          <p style=\"color:#0ba0bb;font-size:30px;margin:10px\">No. ${data.user.lastid}\r\n          <div style=\"height:40px\">\r\n          <p style=\"font-size:40px;color:#0ba0bb;margin:0\">${data.user.date_time}\r\n          <p style=\"font-size:30px;color:#0ba0bb;margin:0\">${data.user.gender_group}\r\n          <img decoding=\"async\" src=\"https:\/\/api.qrserver.com\/v1\/create-qr-code\/?size=400x400&data=${data.user.ticket_number}\">\r\n          <p style=\"font-size:18px;margin:0\">${today}<br>\r\n        `;\r\n      const w=window.open('','_blank'); \r\n      if(w&&w.document){ \r\n        w.document.write(htmlContent); \r\n        w.document.close(); \r\n      }\r\n    }\r\n\r\n    function handleNewVisitor(data) {\r\n      hideLoading(); \r\n      allowFocus=false; \r\n      startCountdownTimer(); \r\n      scanText.textContent='';\r\n      currentAction = 'new_visitor';\r\n      \r\n      const content = `\r\n        <p><strong>Hi ${data.data.firstName} ${data.data.lastName}, Welcome to our Gym!\r\n        <p>${data.data.facilityName} ${data.data.serviceName}\r\n        <label for=\"email\">Email\r\n        <input type=\"email\" id=\"email\" placeholder=\"Enter your email\" required>\r\n        <label for=\"phone\">Phone\r\n        <input type=\"tel\" id=\"phone\" placeholder=\"Enter your phone number\" required>\r\n        <button id=\"submitTicketBtn\">Submit Ticket\r\n        <button id=\"cancelVisitBtn\">Cancel Visit`;\r\n      visitInfo.innerHTML=content; \r\n      visitInfo.style.display='block';\r\n\r\n      document.getElementById('submitTicketBtn').addEventListener('click', ()=>{\r\n        const email=document.getElementById('email').value;\r\n        const phone=document.getElementById('phone').value;\r\n        if(!email || !phone){ \r\n          scanText.textContent=\"Zadajte, pros&iacute;m, svoj e-mail a telef&oacute;nne ?&iacute;slo.\\nPlease input your email and phone number.\"; \r\n          return; \r\n        }\r\n        if(!isValidEmail(email)){ \r\n          scanText.textContent=\"Neplatn&yacute; e-mail. Zadajte svoj e-mail znova.\\nInvalid Email. Please input your email again.\"; \r\n          return; \r\n        }\r\n        const new_visit_data = Object.assign({}, data.data, { email, phone });\r\n        showLoading();\r\n        fetch(MS_CONFIG.verifyEndpoint, {\r\n          method:'POST', \r\n          headers: {'Content-Type':'application\/json'},\r\n          body: JSON.stringify({ visit_data: new_visit_data, mode: 'live' })\r\n        })\r\n        .then(r=>r.json())\r\n        .then(data2=>{\r\n          if(data2.status==200){\r\n            scanText.textContent=''; \r\n            visitInfo.style.display='none';\r\n            allowFocus=true; \r\n            input.focus(); \r\n            input.value=\"\"; \r\n            cancelCountdownTimer();\r\n            currentAction = 'initial';\r\n            \r\n            const today=new Date().toISOString().split('T')[0];\r\n            const htmlContent = `\r\n              <!DOCTYPE html><html><head><title>No: ${data2.data.lastid}\r\n              <style>@media print{body{width:6.5in!important}@page{size:6.5in 11in!important;margin:.5in}label:after{content:none!important}}\r\n              <body onload=\"window.print()\" onclick=\"window.print()\">\r\n              <div style=\"text-align:center;padding:0;margin:0;page-break-before:always\">\r\n                <div><img decoding=\"async\" src=\"${data2.data.img_bg_path}\" style=\"margin:0;padding:0;height:130px\">\r\n                <p style=\"color:#0ba0bb;font-size:35px;margin:15px\">${data2.data.subject}\r\n                <p style=\"color:#0ba0bb;font-size:25spx;margin:15px\">Card No: ${data2.data.ticket_number}\r\n                <p style=\"color:#0ba0bb;font-size:30px;margin:10px\">No. ${data2.data.lastid}\r\n                <div style=\"height:40px\">\r\n                <p style=\"font-size:40px;color:#0ba0bb;margin:0\">${data2.data.date_time}\r\n                <p style=\"font-size:30px;color:#0ba0bb;margin:0\">${data2.data.gender_group}\r\n                <img decoding=\"async\" src=\"https:\/\/api.qrserver.com\/v1\/create-qr-code\/?size=400x400&data=${data2.data.ticket_number}\">\r\n                <p style=\"font-size:18px;margin:0\">${today}<br>\r\n              `;\r\n            const w=window.open('','_blank'); \r\n            if(w&&w.document){ \r\n              w.document.write(htmlContent); \r\n              w.document.close(); \r\n              setTimeout(()=>{ try{w.close()}catch(e){} },60000); \r\n            }\r\n          } else { \r\n            const err_message = displayScanMessage(data2.message);\r\n            scanText.textContent = `${err_message.sk}\\n${err_message.en}`;\r\n          }\r\n        })\r\n        .catch(()=>{ \r\n          scanText.textContent=\"Chyba! Sk&uacute;ste to znova.\\nError! Try again.\"; \r\n        })\r\n        .finally(()=>{ \r\n          hideLoading(); \r\n        });\r\n      });\r\n\r\n      document.getElementById('cancelVisitBtn').addEventListener('click', ()=>{\r\n        showLoading();\r\n        fetch(MS_CONFIG.deleteEndpoint, {\r\n          method:'POST', \r\n          headers: {'Content-Type':'application\/json'},\r\n          body: JSON.stringify({ visit_data: data.data, mode: 'live' })\r\n        })\r\n        .then(r=>r.json())\r\n        .then(d3=>{\r\n          if(d3.status==200){ \r\n            resetToInitial();\r\n          } else { \r\n            scanText.textContent = \"Error! Chyba!\" + (d3.message?.title || ''); \r\n          }\r\n        })\r\n        .catch(()=>{ \r\n          scanText.textContent=\"Chyba! Sk&uacute;ste to znova.\\nError! Try again.\"; \r\n        })\r\n        .finally(()=>{ \r\n          hideLoading(); \r\n          qrContainer.style.display='none'; \r\n        });\r\n      });\r\n    }\r\n\r\n    function handleEntryGranted(data) {\r\n      hideLoading(); \r\n      scanText.textContent=''; \r\n      visitInfo.style.display='none';\r\n      allowFocus=true; \r\n      input.focus(); \r\n      input.value=\"\"; \r\n      cancelCountdownTimer();\r\n      currentAction = 'initial';\r\n      \r\n      \/\/ Show success message with user info\r\n      scanText.textContent = data.message + '\\n\\nWelcome ' + \r\n        (data.registered_user.first_name + ' ' + data.registered_user.last_name).trim() + \r\n        (data.registered_user.company_name ? ' from ' + data.registered_user.company_name : '') + '!';\r\n      \r\n      if(data.user && data.user.lastid) {\r\n        const today=new Date().toISOString().split('T')[0];\r\n        const htmlContent = `\r\n          <!DOCTYPE html><html><head><title>No: ${data.user.lastid}\r\n          <style>@media print{body{width:6.5in!important}@page{size:6.5in 11in!important;margin:.5in}label:after{content:none!important}}\r\n          <body onload=\"window.print()\" onclick=\"window.print()\">\r\n          <div style=\"text-align:center;padding:0;margin:0;page-break-before:always\">\r\n            <div><img decoding=\"async\" src=\"${data.user.img_bg_path||''}\" style=\"margin:0;padding:0;height:130px\">\r\n            <p style=\"color:#0ba0bb;font-size:35px;margin:15px\">${data.user.subject||'Entry Ticket'}\r\n            <p style=\"color:#0ba0bb;font-size:25spx;margin:15px\">Card No: ${data.user.ticket_number||data.data.barcode}\r\n            <p style=\"color:#0ba0bb;font-size:30px;margin:10px\">No. ${data.user.lastid}\r\n            <div style=\"height:40px\">\r\n            <p style=\"font-size:40px;color:#0ba0bb;margin:0\">${data.user.date_time||new Date().toLocaleString()}\r\n            <p style=\"font-size:30px;color:#0ba0bb;margin:0\">${data.user.gender_group||''}\r\n            <img decoding=\"async\" src=\"https:\/\/api.qrserver.com\/v1\/create-qr-code\/?size=400x400&data=${data.user.ticket_number||data.data.barcode}\">\r\n            <p style=\"font-size:18px;margin:0\">${today}<br>\r\n          `;\r\n        const w=window.open('','_blank'); \r\n        if(w&&w.document){ \r\n          w.document.write(htmlContent); \r\n          w.document.close(); \r\n        }\r\n      }\r\n    }\r\n\r\n    function resetToInitial() {\r\n      hideLoading();\r\n      scanText.textContent=''; \r\n      scanText.style.color = ''; \/\/ Reset text color to default\r\n      visitInfo.style.display='none'; \r\n      allowFocus=true; \r\n      input.focus(); \r\n      input.value=\"\";\r\n      cancelCountdownTimer(); \r\n      currentAction = 'initial';\r\n      qrContainer.style.display='none';\r\n    }\r\n    \r\n    input.addEventListener(\"blur\", keepFocused);\r\n    setInterval(keepFocused, 1000);\r\n  })();\r\n  <\/script><\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"_links":{"self":[{"href":"https:\/\/24hr-fitness.eu\/sk\/wp-json\/wp\/v2\/pages\/808"}],"collection":[{"href":"https:\/\/24hr-fitness.eu\/sk\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/24hr-fitness.eu\/sk\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/24hr-fitness.eu\/sk\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/24hr-fitness.eu\/sk\/wp-json\/wp\/v2\/comments?post=808"}],"version-history":[{"count":7,"href":"https:\/\/24hr-fitness.eu\/sk\/wp-json\/wp\/v2\/pages\/808\/revisions"}],"predecessor-version":[{"id":816,"href":"https:\/\/24hr-fitness.eu\/sk\/wp-json\/wp\/v2\/pages\/808\/revisions\/816"}],"wp:attachment":[{"href":"https:\/\/24hr-fitness.eu\/sk\/wp-json\/wp\/v2\/media?parent=808"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}