{"id":12401,"date":"2026-02-23T20:32:14","date_gmt":"2026-02-24T02:32:14","guid":{"rendered":"https:\/\/buzkme.com\/bazar\/?page_id=12401"},"modified":"2026-04-17T10:11:10","modified_gmt":"2026-04-17T16:11:10","slug":"buzkme","status":"publish","type":"page","link":"https:\/\/buzkme.com\/bazar\/buzkme\/","title":{"rendered":"ESCANEA EN TIENDA"},"content":{"rendered":"        <style>\n        .posinv-visor{padding:14px;}\n        .posinv-scan-card{max-width:720px;margin:0 auto;background:#fff;border:1px solid #e6e6e6;border-radius:18px;overflow:hidden;padding:14px;}\n        .posinv-scan-alert{margin:0 0 12px;padding:10px 12px;border-radius:12px;background:#fff4f4;border:1px solid #f3caca;color:#b42318;font-weight:600;}\n        .posinv-scan-title{margin:0 0 8px;font-size:22px;line-height:1.2;}\n        .posinv-scan-muted{opacity:.8;margin:0 0 12px;}\n        #posinvScanBox{width:100%;max-width:300px;margin:12px auto;border-radius:16px;overflow:hidden;display:none;background:#111;}\n        #posinvScanBox video{display:block;width:100%;height:auto;border-radius:16px;}\n        .posinv-scan-actions{display:flex;gap:10px;flex-wrap:wrap;justify-content:center;margin-top:10px;}\n        .posinv-scan-actions .button{border-radius:12px;padding:10px 14px;font-weight:600;}\n        .posinv-scan-input{display:flex;gap:10px;justify-content:center;flex-wrap:wrap;margin-top:12px;}\n        .posinv-scan-input input{width:min(420px,100%);padding:10px 12px;border:1px solid #dcdcdc;border-radius:12px;}\n        .posinv-scan-msg{margin-top:10px;text-align:center;font-weight:600;}\n        .posinv-visor-card{max-width:720px;margin:14px auto 0;background:#fff;border:1px solid #e6e6e6;border-radius:18px;overflow:hidden;}\n        .posinv-visor-img img{width:100%;height:auto;display:block;}\n        .posinv-visor-info{padding:14px;}\n        .posinv-visor-title{margin:0 0 8px;font-size:24px;line-height:1.2;}\n        .posinv-visor-price{font-size:22px;font-weight:700;margin-bottom:10px;}\n        .posinv-visor-short{font-size:15px;opacity:.9;}\n        <\/style>\n        <div class=\"posinv-visor\">\n          <div class=\"posinv-scan-card\">\n                        <h2 class=\"posinv-scan-title\">Escanear producto <span class=\"posinv-version\">v207<\/span><\/h2>\n            <p class=\"posinv-scan-muted\">Apunta la c\u00e1mara al c\u00f3digo (barcode o QR). Cuando el mismo c\u00f3digo se confirme 2 veces, se mostrar\u00e1 el producto sin apagar la c\u00e1mara.<\/p>\n\n            <div id=\"posinvScanBox\"><\/div>\n\n            <div class=\"posinv-scan-actions\">\n              <button type=\"button\" class=\"button button-primary\" id=\"posinvStartScan\">Activar c\u00e1mara<\/button>\n              <button type=\"button\" class=\"button\" id=\"posinvStopScan\" style=\"display:none;\">Detener<\/button>\n            <\/div>\n\n            <div class=\"posinv-scan-input\">\n              <input type=\"text\" id=\"posinvManualCode\" placeholder=\"O escribe el c\u00f3digo aqu\u00ed (ej. 750...)\" inputmode=\"numeric\" value=\"\" \/>\n              <button type=\"button\" class=\"button\" id=\"posinvGoManual\">Ver<\/button>\n            <\/div>\n\n            <div class=\"posinv-scan-msg\" id=\"posinvScanMsg\"><\/div>\n            <div class=\"posinv-visor-card\" id=\"posinvLiveResult\" style=\"display:none;\">\n              <div class=\"posinv-visor-img\"><img decoding=\"async\" id=\"posinvLiveImg\" src=\"\" alt=\"\"><\/div>\n              <div class=\"posinv-visor-info\">\n                <h2 class=\"posinv-visor-title\" id=\"posinvLiveName\"><\/h2>\n                <div class=\"posinv-visor-price\" id=\"posinvLivePrice\"><\/div>\n                <div class=\"posinv-visor-short\" id=\"posinvLiveShort\"><\/div>\n              <\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n\n        <script>\n        (function(){\n          var startBtn = document.getElementById('posinvStartScan');\n          var stopBtn  = document.getElementById('posinvStopScan');\n          var msgEl    = document.getElementById('posinvScanMsg');\n          var manualIn = document.getElementById('posinvManualCode');\n          var goBtn    = document.getElementById('posinvGoManual');\n          var box      = document.getElementById('posinvScanBox');\n          var ajaxUrl  = \"https:\\\/\\\/buzkme.com\\\/bazar\\\/wp-admin\\\/admin-ajax.php\";\n\n          var speechState = { voice:null, timer:0, redirectTimer:0, waitRedirect:false, audioCtx:null };\n          var state = { stream:null, video:null, detector:null, rafId:null, lastRaw:'', stableCount:0, startedAt:0, lastSeenAt:0, track:null, announcing:false, lastAcceptedRaw:'', lastAcceptedAt:0, scanPausedUntil:0, nextDetectAt:0, currentMsg:'', currentTone:'' };\n\n          function setMsg(t, tone){\n            if(!msgEl) return;\n            t = t || '';\n            tone = tone || 'soft';\n            if(state.currentMsg === t && state.currentTone === tone) return;\n            state.currentMsg = t;\n            state.currentTone = tone;\n            msgEl.textContent = t;\n            if(tone === 'ok'){ msgEl.style.color = 'green'; }\n            else if(tone === 'bad'){ msgEl.style.color = 'crimson'; }\n            else { msgEl.style.color = '#555'; }\n          }\n\n          function fullProductSpeech(name){\n            return String(name || '')\n              .replace(\/[\\r\\n\\t]+\/g, ' ')\n              .replace(\/\\s{2,}\/g, ' ')\n              .trim();\n          }\n\n          function playScanBeep(){\n            try{\n              var Ctx = window.AudioContext || window.webkitAudioContext;\n              if(!Ctx) return;\n              if(!speechState.audioCtx) speechState.audioCtx = new Ctx();\n              var ctx = speechState.audioCtx;\n              if(ctx.state === 'suspended' && ctx.resume){ ctx.resume().catch(function(){}); }\n              var osc = ctx.createOscillator();\n              var gain = ctx.createGain();\n              osc.type = 'sine';\n              osc.frequency.value = 1046;\n              gain.gain.setValueAtTime(0.0001, ctx.currentTime);\n              gain.gain.exponentialRampToValueAtTime(0.16, ctx.currentTime + 0.01);\n              gain.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + 0.14);\n              osc.connect(gain);\n              gain.connect(ctx.destination);\n              osc.start();\n              osc.stop(ctx.currentTime + 0.15);\n            }catch(_e){}\n          }\n\n          function openCode(code){\n            code = (code || '').trim();\n            if(!code) { setMsg('C\u00f3digo vac\u00edo.', 'bad'); return; }\n            var url = new URL(window.location.href);\n            url.searchParams.set('code', code);\n            window.location.href = url.toString();\n          }\n\n          function finishOpen(code){\n            window.clearTimeout(speechState.redirectTimer || 0);\n            speechState.redirectTimer = window.setTimeout(function(){ openCode(code); }, 60);\n          }\n\n          function speakFullProductName(name, code){\n            var phrase = fullProductSpeech(name);\n            if(!phrase || !('speechSynthesis' in window) || typeof window.SpeechSynthesisUtterance !== 'function'){\n              return;\n            }\n            try{ window.speechSynthesis.cancel(); }catch(_e){}\n            window.clearTimeout(speechState.timer || 0);\n            speechState.waitRedirect = true;\n            speechState.timer = window.setTimeout(function(){\n              try{\n                var u = new window.SpeechSynthesisUtterance(phrase);\n                u.lang = 'es-MX';\n                u.rate = 1.06;\n                u.pitch = 1;\n                if(speechState.voice) u.voice = speechState.voice;\n                var voices = window.speechSynthesis.getVoices ? window.speechSynthesis.getVoices() : [];\n                if(!speechState.voice && Array.isArray(voices) && voices.length){\n                  speechState.voice = voices.find(function(v){ return \/es[-_]?mx\/i.test(String(v.lang||'')); })\n                    || voices.find(function(v){ return \/^es\/i.test(String(v.lang||'')); })\n                    || null;\n                  if(speechState.voice) u.voice = speechState.voice;\n                }\n                u.onend = function(){};\n                u.onerror = function(){};\n                window.speechSynthesis.speak(u);\n                speechState.redirectTimer = 0;\n              }catch(_err){}\n            }, 70);\n          }\n\n          function fetchProductData(code){\n            code = String(code || '').trim();\n            if(!code) return Promise.resolve('');\n            var body = new URLSearchParams();\n            body.set('action', 'posinv_public_visor_lookup');\n            body.set('code', code);\n            return fetch(ajaxUrl, {\n              method: 'POST',\n              credentials: 'same-origin',\n              headers: { 'Content-Type': 'application\/x-www-form-urlencoded; charset=UTF-8' },\n              body: body.toString()\n            }).then(function(r){ return r.json().catch(function(){ return null; }); })\n              .then(function(json){\n                if(json && json.success && json.data){ return json.data; }\n                return null;\n              }).catch(function(){ return ''; });\n          }\n\n          function renderProduct(data){\n            if(!data) return;\n            var card = document.getElementById('posinvLiveResult');\n            var img = document.getElementById('posinvLiveImg');\n            var name = document.getElementById('posinvLiveName');\n            var price = document.getElementById('posinvLivePrice');\n            var short = document.getElementById('posinvLiveShort');\n            if(card) card.style.display = '';\n            if(img && data.image_url){ img.src = data.image_url; img.alt = data.name || ''; }\n            if(name) name.textContent = data.name || '';\n            if(price) price.innerHTML = data.price_html || '';\n            if(short) short.innerHTML = data.short_html || '';\n          }\n          function announceAndOpenCode(code){\n            code = (code || '').trim();\n            if(!code){ setMsg('C\u00f3digo vac\u00edo.', 'bad'); return; }\n            if(state.announcing) return;\n            state.announcing = true;\n            state.scanPausedUntil = Date.now() + 1800;\n            setMsg('Producto detectado. Buscando\u2026', 'ok');\n            fetchProductData(code).then(function(data){\n              state.announcing = false;\n              if(data && data.name){\n                playScanBeep();\n                renderProduct(data);\n                setMsg('Producto detectado', 'ok');\n                speakFullProductName(data.name, code);\n              }else{\n                setMsg('No encontrado: ' + code, 'bad');\n              }\n            }).catch(function(){\n              state.announcing = false;\n              setMsg('No se pudo buscar el producto.', 'bad');\n            });\n          }\n          if(goBtn){\n            goBtn.addEventListener('click', function(){\n              announceAndOpenCode(manualIn ? manualIn.value : '');\n            });\n          }\n          if(manualIn){\n            manualIn.addEventListener('keydown', function(e){\n              if(e.key === 'Enter'){ e.preventDefault(); announceAndOpenCode(manualIn.value); }\n            });\n          }\n\n          async function stopScan(keepMsg){\n            try{\n              if(state.rafId) cancelAnimationFrame(state.rafId);\n              state.rafId = null;\n              if(state.stream){ state.stream.getTracks().forEach(function(t){ t.stop(); }); }\n              state.stream = null;\n              if(state.video){ state.video.pause(); state.video.srcObject = null; }\n              state.video = null;\n              state.detector = null;\n              state.track = null;\n              state.lastRaw = '';\n              state.stableCount = 0;\n              state.lastSeenAt = 0;\n              state.scanPausedUntil = 0;\n              state.nextDetectAt = 0;\n              if(box){ box.innerHTML = ''; box.style.display = 'none'; }\n              if(!keepMsg){ setMsg('Escaneo detenido.', 'ok'); }\n            }catch(e){}\n            if(stopBtn) stopBtn.style.display = 'none';\n            if(startBtn) startBtn.style.display = 'inline-flex';\n          }\n\n          async function tick(){\n            if(!state.detector || !state.video) return;\n            var nowTick = Date.now();\n            if(state.scanPausedUntil && nowTick < state.scanPausedUntil){ state.rafId = requestAnimationFrame(tick); return; }\n            if(state.nextDetectAt && nowTick < state.nextDetectAt){ state.rafId = requestAnimationFrame(tick); return; }\n            state.nextDetectAt = nowTick + 180;\n            try{\n              var barcodes = await state.detector.detect(state.video);\n              if(barcodes && barcodes.length){\n                var raw = String(barcodes[0].rawValue || '').trim();\n                if(raw){\n                  state.lastSeenAt = Date.now();\n                  if((Date.now() - state.startedAt) < 700){\n                    setMsg('Apunta la c\u00e1mara al c\u00f3digo', 'soft');\n                  }else{\n                    if(raw === state.lastRaw){\n                      state.stableCount = Math.min(state.stableCount + 1, 2);\n                    }else{\n                      state.lastRaw = raw;\n                      state.stableCount = 1;\n                    }\n                    if(state.stableCount >= 2){ setMsg('Producto detectado', 'ok'); }\n                    if(state.stableCount >= 2){\n                      if(state.lastAcceptedRaw === raw && (Date.now() - (state.lastAcceptedAt || 0)) < 2500){\n                        setMsg('Producto detectado. Retira el c\u00f3digo para leer otro.', 'soft'); state.scanPausedUntil = Date.now() + 1200;\n                      }else{\n                        state.lastAcceptedRaw = raw;\n                        state.lastAcceptedAt = Date.now();\n                        announceAndOpenCode(raw);\n                      }\n                      state.stableCount = 0;\n                      state.lastRaw = '';\n                    }\n                  }\n                }\n              }else if(state.lastSeenAt && (Date.now() - state.lastSeenAt) > 2200){\n                state.lastRaw = '';\n                state.stableCount = 0;\n                setMsg('Apunta la c\u00e1mara al c\u00f3digo', 'soft');\n              }\n            }catch(e){}\n            state.rafId = requestAnimationFrame(tick);\n          }\n\n          async function startScan(){\n            if(!('BarcodeDetector' in window)){\n              setMsg('Tu navegador no soporta escaneo directo aqu\u00ed. Usa el campo para escribir el c\u00f3digo.', 'bad');\n              return;\n            }\n\n            try{\n              try{\n                state.detector = new BarcodeDetector({ formats: ['qr_code','ean_13','ean_8','code_128','code_39','upc_a','upc_e'] });\n              }catch(_e){\n                state.detector = new BarcodeDetector();\n              }\n\n              if(box){ box.innerHTML = ''; box.style.display = 'block'; }\n              state.video = document.createElement('video');\n              state.video.setAttribute('playsinline','');\n              state.video.autoplay = true;\n              state.video.muted = true;\n              if(box) box.appendChild(state.video);\n\n              state.stream = await navigator.mediaDevices.getUserMedia({\n                video: {\n                  facingMode: { ideal: 'environment' },\n                  width: { ideal: 1280 },\n                  height: { ideal: 720 },\n                  focusMode: { ideal: 'continuous' }\n                },\n                audio:false\n              });\n              state.video.srcObject = state.stream;\n              await state.video.play();\n\n              state.startedAt = Date.now();\n              state.lastRaw = '';\n              state.stableCount = 0;\n              state.lastSeenAt = 0;\n              state.scanPausedUntil = 0;\n              state.nextDetectAt = 0;\n              state.track = (state.stream && state.stream.getVideoTracks) ? (state.stream.getVideoTracks()[0] || null) : null;\n              if(state.track && state.track.applyConstraints){\n                try{\n                  var caps = (state.track.getCapabilities ? state.track.getCapabilities() : {}) || {};\n                  var advanced = [];\n                  if(caps.focusMode && caps.focusMode.indexOf && caps.focusMode.indexOf('continuous') !== -1){\n                    advanced.push({ focusMode: 'continuous' });\n                  }\n                  if(caps.zoom){\n                    var z = 1;\n                    if(typeof caps.zoom === 'object'){\n                      var minZ = Number(caps.zoom.min || 1);\n                      var maxZ = Number(caps.zoom.max || minZ || 1);\n                      z = Math.max(minZ, Math.min(maxZ, 2));\n                    }\n                    if(z > 1){ advanced.push({ zoom: z }); }\n                  }\n                  if(advanced.length){ state.track.applyConstraints({ advanced: advanced }).catch(function(){}); }\n                }catch(_capsErr){}\n              }\n\n              if(startBtn) startBtn.style.display = 'none';\n              if(stopBtn) stopBtn.style.display = 'inline-flex';\n\n              setMsg('Apunta la c\u00e1mara al c\u00f3digo', 'soft');\n              state.rafId = requestAnimationFrame(tick);\n            }catch(e){\n              setMsg('No se pudo abrir la c\u00e1mara. Revisa permisos del navegador.', 'bad');\n              await stopScan(true);\n            }\n          }\n\n          if(startBtn){ startBtn.addEventListener('click', function(){ startScan(); }); }\n          if(stopBtn){ stopBtn.addEventListener('click', function(){ stopScan(); }); }\n        })();\n        <\/script>\n        \n","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":""},"class_list":["post-12401","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/buzkme.com\/bazar\/wp-json\/wp\/v2\/pages\/12401","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/buzkme.com\/bazar\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/buzkme.com\/bazar\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/buzkme.com\/bazar\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/buzkme.com\/bazar\/wp-json\/wp\/v2\/comments?post=12401"}],"version-history":[{"count":3,"href":"https:\/\/buzkme.com\/bazar\/wp-json\/wp\/v2\/pages\/12401\/revisions"}],"predecessor-version":[{"id":20446,"href":"https:\/\/buzkme.com\/bazar\/wp-json\/wp\/v2\/pages\/12401\/revisions\/20446"}],"wp:attachment":[{"href":"https:\/\/buzkme.com\/bazar\/wp-json\/wp\/v2\/media?parent=12401"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}