# Enviar imagens

# Envie suas imagens

Escolha as fotos do seu pedido e envie por aqui. Depois você pode conferir, ordenar e apagar. Não precisa de cadastro.

⏳

Verificando o seu link…

🔑

## Digite o código

Informe o código de 4 dígitos que enviamos junto com o link.

Continuar

🔒

## Link indisponível

Este link expirou ou não é mais válido. Peça um novo link para o nosso atendimento pelo WhatsApp.

**Toque para escolher as imagens**ou arraste os arquivos até aqui 

⚠️ Depois de tocar em Enviar, não feche esta página nem troque de aplicativo até aparecer “Imagens recebidas”. Se sair antes, o envio é interrompido.

### Para enviar

### Suas imagens

Selecionar

A ordem abaixo é a ordem das fotos no pedido (foto-01, foto-02…). Toque no número de uma foto ou use as setas para mover. Toque na foto para ampliar.

Selecionar repetidas

EnviarApagarCancelar

ApagarCancelar

×

(function(){ var API='https://n8n.srv1091606.hstgr.cloud/webhook/enquadra-upload'; var CHUNK=3276800, PARALLEL=2; var $=function(id){return document.getElementById(id)}; var token=(new URLSearchParams(location.search).get('t')||'').trim(); var pin='', expires='', sending=false, selMode=false, selected={}, localUrl={}, gallery=\[\], queue=\[\]; var wlock=null, saveTimer=null, busy=0, chain=Promise.resolve(), retries=0, lastR=0, okTotal=0; var POP=\[0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4\]; function el(t,c,x){var e=document.createElement(t); if(c)e.className=c; if(x!=null)e.textContent=x; return e} function label(i,total){return 'foto-'+String(i+1).padStart(Math.max(2,String(total).length),'0')} function wait(ms){return new Promise(function(r){setTimeout(r,ms)})} function fmt(b){return 1048576>b?Math.max(1,Math.round(b/1024))+' KB':(b/1048576).toFixed(1)+' MB'} function api(b){return fetch(API,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(Object.assign({token:token,pin:pin},b))}).then(function(r){return r.json()})} function wl(){ try{ if(navigator.wakeLock){ navigator.wakeLock.request('screen').then(function(l){wlock=l}).catch(function(){}); } }catch(e){} } function unwl(){ try{ if(wlock){ wlock.release(); wlock=null; } }catch(e){} } var WARN\_IDLE='⚠️ Depois de tocar em Enviar, não feche esta página nem troque de aplicativo até aparecer “Imagens recebidas”. Se sair antes, o envio é interrompido.'; var WARN\_ON='⚠️ Enviando… NÃO feche esta página nem troque de aplicativo. Aguarde aparecer “Imagens recebidas”.'; function setWarn(on){ var w=$('enq-warn'); w.className=on?'warn on':'warn'; w.textContent=on?WARN\_ON:WARN\_IDLE; } function setStatus(t,err){ var s=$('enq-status'); s.textContent=t||''; s.className=err?'gstatus err':'gstatus'; } /\* ---------- repetidas ---------- \*/ function ham(a,b){ if(!a||!b||a.length!==16||b.length!==16) return 99; var d=0; for(var i=0;16>i;i++){ d+=POP\[parseInt(a.charAt(i),16)^parseInt(b.charAt(i),16)\]; } return d; } function rel(a,b){ if(a.sha&&b.sha&&a.sha===b.sha) return 'igual'; return ''; } async function sha256(file){ try{ if(file.size>134217728||!(window.crypto&&crypto.subtle)) return ''; var buf=await file.arrayBuffer(); var h=await crypto.subtle.digest('SHA-256',buf); return Array.prototype.map.call(new Uint8Array(h),function(x){return ('0'+x.toString(16)).slice(-2)}).join(''); }catch(e){ return ''; } } async function phash(file){ try{ if(!/^image\\//.test(file.type)||file.size>62914560) return ''; var bmp=await createImageBitmap(file); var c1=document.createElement('canvas'); c1.width=72; c1.height=64; var x1=c1.getContext('2d'); x1.imageSmoothingQuality='high'; x1.drawImage(bmp,0,0,72,64); if(bmp.close) bmp.close(); var c2=document.createElement('canvas'); c2.width=9; c2.height=8; var x2=c2.getContext('2d',{willReadFrequently:true}); x2.imageSmoothingQuality='high'; x2.drawImage(c1,0,0,9,8); var d=x2.getImageData(0,0,9,8).data, bits=''; for(var r=0;8>r;r++){ for(var q=0;8>q;q++){ var i1=(r\*9+q)\*4, i2=(r\*9+q+1)\*4; var g1=d\[i1\]\*0.299+d\[i1+1\]\*0.587+d\[i1+2\]\*0.114, g2=d\[i2\]\*0.299+d\[i2+1\]\*0.587+d\[i2+2\]\*0.114; bits+=(g1>g2)?'1':'0'; } } var hex=''; for(var k=0;64>k;k+=4){ hex+=parseInt(bits.substr(k,4),2).toString(16); } return hex; }catch(e){ return ''; } } function markQueueDups(){ queue.forEach(function(f,i){ f.dup=null; if(f.state==='done') return; for(var j=0;gallery.length>j;j++){ var r=rel(f,gallery\[j\]); if(r){ f.dup={kind:r,ref:label(j,gallery.length)}; return; } } for(var k=0;i>k;k++){ var r2=rel(f,queue\[k\]); if(r2){ f.dup={kind:r2,ref:queue\[k\].file.name}; return; } } }); } /\* ---------- barra fixa ---------- \*/ function updateBar(){ var main=!$('enq-main').hidden; var pend=queue.filter(function(f){return f.state!=='done'}).length; var nsel=Object.keys(selected).length; var showDel=main&&selMode, showSend=main&&!selMode&&pend>0; $('enq-del').hidden=!showDel; $('enq-cancel').hidden=!showDel; $('enq-send').hidden=!showSend; $('enq-del').disabled=(nsel===0); $('enq-del').textContent=nsel?('Apagar '+nsel+(nsel===1?' foto':' fotos')):'Escolha as fotos'; $('enq-send').disabled=sending; $('enq-send').textContent=sending?'Enviando… não feche a página':('Enviar '+pend+(pend===1?' arquivo':' arquivos')); var any=showDel||showSend; $('enq-bar').hidden=!any; $('enq-up').className=any?'enq-up hasbar':'enq-up'; } function show(id){ \['enq-loading','enq-pin','enq-invalid','enq-main'\].forEach(function(x){$(x).hidden=(x!==id)}); updateBar(); } /\* ---------- fila de envio ---------- \*/ function renderQueue(){ var ul=$('enq-list'); ul.innerHTML=''; var shown=queue.filter(function(f){return true}); $('enq-qwrap').hidden=(shown.length===0); shown.forEach(function(f){ var li=el('li',(f.state||'')+(f.dup&&f.state!=='done'?' isdup':'')); var th=el('div','th'); if(f.url){ var im=el('img'); im.alt=''; im.src=f.url; im.onerror=function(){ im.remove(); th.textContent=(f.file.name.split('.').pop()||'arq').slice(0,4); }; th.appendChild(im); } else th.textContent=(f.file.name.split('.').pop()||'arq').slice(0,4); var mid=el('div'); mid.appendChild(el('div','nm',f.file.name)); mid.appendChild(el('div','mt',f.state==='done'?'Enviado':f.state==='fail'?(f.err||'Falhou. Toque em Enviar para tentar de novo'):fmt(f.file.size))); if(f.dup&&f.state!=='done') mid.appendChild(el('div','db',(f.dup.kind==='igual'?'Repetida de ':'Parecida com ')+f.dup.ref)); var bar=el('div','bar'); var bi=el('i'); bi.style.width=(f.pct||0)+'%'; bar.appendChild(bi); mid.appendChild(bar); li.appendChild(th); li.appendChild(mid); var rm=el('button','rm','×'); rm.title='Remover'; rm.disabled=sending; rm.style.visibility=(sending||f.state==='done')?'hidden':'visible'; rm.onclick=function(){ queue=queue.filter(function(x){return x!==f}); markQueueDups(); renderQueue(); }; li.appendChild(rm); ul.appendChild(li); }); updateBar(); } function renderProgress(){ var n=Date.now(); if(n-lastR>150){ lastR=n; renderQueue(); } } function add(list){ Array.prototype.forEach.call(list,function(file){ if(!(file.size>0)) return; var f={file:file,pct:0,state:'',sha:'',phash:'',url:'',dup:null}; if(/^image\\//.test(file.type)){ try{ f.url=URL.createObjectURL(file); }catch(e){} } f.hashP=Promise.all(\[sha256(file),phash(file)\]).then(function(r){ f.sha=r\[0\]; f.phash=r\[1\]; markQueueDups(); renderQueue(); }); queue.push(f); }); $('enq-msg').hidden=true; markQueueDups(); renderQueue(); } /\* ---------- galeria ---------- \*/ function toggle(id){ if(selected\[id\]) delete selected\[id\]; else selected\[id\]=1; renderGallery(); } function openLb(g){ var u=localUrl\[g.item\_id\]||g.large||g.thumb; if(!u) return; $('enq-lbi').src=u; $('enq-lb').hidden=false; } function move(from,to){ if(from===to||0>to||to>=gallery.length) return; var it=gallery.splice(from,1)\[0\]; gallery.splice(to,0,it); renderGallery(); scheduleSave(); } function renderGallery(){ var n=gallery.length; $('enq-gwrap').hidden=(n===0); $('enq-drop').className=n?'drop small':'drop'; $('enq-drop-t').textContent=n?'+ Adicionar mais imagens':'Toque para escolher as imagens'; if(!n){ selMode=false; selected={}; updateBar(); return; } $('enq-gt').textContent='Suas imagens ('+n+')'; $('enq-selbtn').textContent=selMode?'Concluir':'Selecionar'; $('enq-gnote').hidden=selMode; var dups={}, dn=0; gallery.forEach(function(g,i){ for(var j=0;i>j;j++){ var r=rel(g,gallery\[j\]); if(r){ dups\[i\]={kind:r,ref:label(j,n)}; dn++; return; } } }); $('enq-dupbar').hidden=(dn===0); $('enq-duptxt').textContent=dn+(dn===1?' foto parece repetida':' fotos parecem repetidas')+' (marcadas em laranja). Confira se quer manter.'; var grid=$('enq-grid'); grid.innerHTML=''; gallery.forEach(function(g,i){ var d=dups\[i\]; var c=el('div','gc'+(d?' dup':'')+(selected\[g.id\]?' sel':'')+(selMode?' selmode':'')); var im=el('img'); im.alt=g.name; var u=localUrl\[g.item\_id\]||g.thumb; if(u) im.src=u; else im.className='nothumb'; im.onerror=function(){ im.className='nothumb'; }; im.onclick=function(){ if(selMode) toggle(g.id); else openLb(g); }; c.appendChild(im); if(!u) c.appendChild(el('div','ph','Gerando prévia…')); if(selMode){ var ck=el('button','gx ck'+(selected\[g.id\]?' on':''),selected\[g.id\]?'✓':''); ck.setAttribute('aria-label','Selecionar'); ck.onclick=function(){ toggle(g.id); }; c.appendChild(ck); } else { var sl=el('select','gn'); sl.setAttribute('aria-label','Posição da foto'); for(var k=0;n>k;k++){ var o=el('option',null,label(k,n)); o.value=String(k); sl.appendChild(o); } sl.value=String(i); sl.onchange=function(){ move(i,parseInt(sl.value,10)); }; c.appendChild(sl); var x=el('button','gx','×'); x.setAttribute('aria-label','Apagar'); x.onclick=function(){ askDel(\[g.id\]); }; c.appendChild(x); } if(d) c.appendChild(el('div','gd',(d.kind==='igual'?'Repetida de ':'Parecida com ')+d.ref)); if(!selMode){ var m=el('div','gm'); var b1=el('button',null,'‹'); b1.setAttribute('aria-label','Mover para antes'); b1.disabled=(i===0); b1.onclick=function(){ move(i,i-1); }; var b2=el('button',null,'›'); b2.setAttribute('aria-label','Mover para depois'); b2.disabled=(i===n-1); b2.onclick=function(){ move(i,i+1); }; m.appendChild(b1); m.appendChild(b2); c.appendChild(m); } grid.appendChild(c); }); updateBar(); } async function refresh(){ var r=await api({action:'files'}); if(!(r&&r.ok)) return false; gallery=r.files||\[\]; var keep={}; gallery.forEach(function(g){ if(selected\[g.id\]) keep\[g.id\]=1; }); selected=keep; markQueueDups(); renderGallery(); renderQueue(); var miss=gallery.some(function(g){ return !g.thumb&&!localUrl\[g.item\_id\]; }); if(miss&&6>retries){ retries++; setTimeout(function(){ if(!busy&&!saveTimer) refresh(); },4000); } else if(!miss){ retries=0; } return true; } /\* ---------- salvar ordem / apagar ---------- \*/ function enqueue(fn){ busy++; chain=chain.then(fn).catch(function(){ setStatus('Não foi possível salvar. Verifique a internet e tente de novo.',true); }).then(function(){ busy--; }); return chain; } function order(){ return gallery.map(function(g){return g.id}); } async function doSave(){ var r=await api({action:'save',order:order()}); if(!(r&&r.ok)) throw new Error('save'); if(!saveTimer&&busy===1) await refresh(); setStatus('Ordem salva ✓'); } function scheduleSave(){ setStatus('Salvando a ordem…'); if(saveTimer) clearTimeout(saveTimer); saveTimer=setTimeout(function(){ saveTimer=null; enqueue(doSave); },900); } function askDel(ids){ $('enq-mt').textContent=(ids.length===1?'Apagar esta foto?':'Apagar '+ids.length+' fotos?')+' Depois de apagar, você precisará enviar de novo se mudar de ideia.'; $('enq-modal').hidden=false; $('enq-my').onclick=function(){ $('enq-modal').hidden=true; doDel(ids); }; $('enq-mn').onclick=function(){ $('enq-modal').hidden=true; }; } function doDel(ids){ var set={}; ids.forEach(function(x){ set\[x\]=1; }); gallery=gallery.filter(function(g){ return !set\[g.id\]; }); selected={}; selMode=false; renderGallery(); markQueueDups(); renderQueue(); setStatus('Apagando…'); enqueue(async function(){ var r=await api({action:'delete',ids:ids}); if(!(r&&r.ok)) throw new Error('del'); var r2=await api({action:'save',order:order()}); if(!(r2&&r2.ok)) throw new Error('save'); if(!saveTimer&&busy===1) await refresh(); setStatus(ids.length===1?'Foto apagada ✓':'Fotos apagadas ✓'); }); } /\* ---------- envio ---------- \*/ function putChunk(url,blob,start,total,onprog){ return new Promise(function(res,rej){ var x=new XMLHttpRequest(); x.open('PUT',url); x.setRequestHeader('Content-Range','bytes '+start+'-'+(start+blob.size-1)+'/'+total); x.upload.onprogress=function(e){ if(e.lengthComputable) onprog(e.loaded) }; x.onload=function(){ (x.status>=200&&300>x.status)?res({status:x.status,text:x.responseText}):rej({status:x.status}) }; x.onerror=function(){rej({status:0})}; x.ontimeout=x.onerror; x.timeout=180000; x.send(blob); }); } async function register(f,id){ for(var t=0;3>t;t++){ try{ var r=await api({action:'register',items:\[{id:id,size:f.file.size,sha:f.sha||'',phash:f.phash||''}\]}); if(r&&r.ok) return true; }catch(e){} await wait(800\*(t+1)); } return false; } async function sendOne(f){ var total=f.file.size, attempt=0; try{ if(f.hashP) await f.hashP; }catch(e){} while(true){ try{ var s=await api({action:'start',filename:f.file.name}); if(!s.ok||!s.uploadUrl){ if(s.error==='invalid\_or\_expired'||s.error==='locked'||s.error==='wrong\_pin') throw {fatal:true}; throw {status:0}; } var pos=0, last=null; while(total>pos){ var end=Math.min(pos+CHUNK,total), blob=f.file.slice(pos,end), tries=0; while(true){ try{ last=await putChunk(s.uploadUrl,blob,pos,total,function(l){ f.pct=Math.round((pos+l)/total\*100); renderProgress(); }); break; } catch(e){ if(++tries>=3) throw e; await wait(1200\*tries); } } pos=end; f.pct=Math.round(pos/total\*100); renderProgress(); } var it=null; try{ it=JSON.parse((last&&last.text)||'null'); }catch(e){} f.state='done'; f.pct=100; if(it&&it.id){ if(f.url) localUrl\[it.id\]=f.url; await register(f,it.id); } return true; }catch(e){ if(e&&e.fatal){ f.state='fail'; f.err='Este link expirou. Peça um novo ao atendimento.'; return false; } if(++attempt>=2){ f.state='fail'; f.err=null; return false; } await wait(1500); } } } async function run(){ if(sending) return; sending=true; setWarn(true); wl(); queue.forEach(function(f){ if(f.state==='fail'){ f.state=''; f.pct=0; } }); renderQueue(); var todo=queue.filter(function(f){return f.state!=='done'}); var okCount=0; async function worker(){ while(todo.length){ var f=todo.shift(); if(await sendOne(f)) okCount++; renderQueue(); } } var ws=\[\]; for(var i=0;PARALLEL>i;i++) ws.push(worker()); await Promise.all(ws); if(okCount){ setStatus('Organizando suas fotos…'); await enqueue(async function(){ var r=await api({action:'save',order:order()}); if(!(r&&r.ok)) throw new Error('save'); await refresh(); setStatus(''); }); } sending=false; setWarn(false); unwl(); queue=queue.filter(function(f){return f.state!=='done'}); markQueueDups(); renderQueue(); if(okCount){ okTotal+=okCount; var m=$('enq-msg'); m.textContent='✅ Imagens recebidas! Recebemos '+okTotal+(okTotal===1?' arquivo':' arquivos')+' certinho. Você pode conferir abaixo, mudar a ordem ou apagar alguma foto. Quando terminar, é só voltar para a conversa no WhatsApp.'; m.hidden=false; try{ m.scrollIntoView({behavior:'smooth',block:'start'}); }catch(e){} } } /\* ---------- eventos ---------- \*/ $('enq-file').onchange=function(e){ add(e.target.files); e.target.value=''; }; var drop=$('enq-drop'); \['dragenter','dragover'\].forEach(function(ev){ drop.addEventListener(ev,function(e){ e.preventDefault(); drop.classList.add('over'); }); }); \['dragleave','drop'\].forEach(function(ev){ drop.addEventListener(ev,function(e){ e.preventDefault(); drop.classList.remove('over'); }); }); drop.addEventListener('drop',function(e){ if(e.dataTransfer&&e.dataTransfer.files) add(e.dataTransfer.files); }); $('enq-send').onclick=run; $('enq-selbtn').onclick=function(){ selMode=!selMode; selected={}; renderGallery(); }; $('enq-cancel').onclick=function(){ selMode=false; selected={}; renderGallery(); }; $('enq-del').onclick=function(){ var ids=Object.keys(selected).map(function(x){return parseInt(x,10)}); if(ids.length) askDel(ids); }; $('enq-dupsel').onclick=function(){ var n=gallery.length; selected={}; selMode=true; gallery.forEach(function(g,i){ for(var j=0;i>j;j++){ if(rel(g,gallery\[j\])){ selected\[g.id\]=1; return; } } }); renderGallery(); }; $('enq-lb').onclick=function(){ $('enq-lb').hidden=true; $('enq-lbi').src=''; }; window.addEventListener('beforeunload',function(e){ if(sending||busy||saveTimer){ e.preventDefault(); e.returnValue=''; } }); /\* ---------- inicio ---------- \*/ function ready(exp){ var d=new Date(exp); $('enq-note').textContent='Link válido até '+d.toLocaleDateString('pt-BR')+'. Pode enviar arquivos de qualquer tamanho.'; show('enq-main'); renderQueue(); refresh().catch(function(){}); } function tryPin(){ var v=($('enq-pin-in').value||'').replace(/\\D/g,''); if(v.length!==4){ $('enq-pin-err').textContent='Digite os 4 dígitos.'; return; } var b=$('enq-pin-go'); b.disabled=true; $('enq-pin-err').textContent=''; api({action:'verify',pin:v}).then(function(r){ if(r.ok){ pin=v; ready(expires); } else if(r.error==='locked'){ $('enq-pin-err').textContent='Muitas tentativas. Peça um novo link ao atendimento.'; } else if(r.error==='wrong\_pin'){ $('enq-pin-err').textContent='Código incorreto. Tentativas restantes: '+r.left+'.'; } else { show('enq-invalid'); } }).catch(function(){ $('enq-pin-err').textContent='Não foi possível verificar. Tente de novo.'; }).then(function(){ b.disabled=false; }); } $('enq-pin-go').onclick=tryPin; $('enq-pin-in').addEventListener('keydown',function(e){ if(e.key==='Enter') tryPin(); }); if(!/^\[a-f0-9\]{64}$/.test(token)){ show('enq-invalid'); } else api({action:'info'}).then(function(r){ if(!r.ok){ show('enq-invalid'); return; } expires=r.expires\_at; if(r.needs\_pin){ show('enq-pin'); setTimeout(function(){ try{$('enq-pin-in').focus()}catch(e){} },50); } else { ready(expires); } }).catch(function(){ show('enq-invalid'); }); })(); ![](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)

---

> Source: [Enquadra.eu](https://enquadra.eu/pages/enviar-imagens)
