From 96f1d27221129607f55cb2033192e6e735a4d19c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cau=C3=AA=20Faleiros?= Date: Mon, 21 Sep 2026 16:44:49 -0300 Subject: [PATCH] refactor: split the Site's behaviour out of one 1,575-line inline script dtf-site.html held commercial rules, the nesting engine, PDF analysis, the cart and every handler in a single inline script, 42% of the runtime code in one file, and the money logic lived in the middle of it. It is now nine files under local/static, cut at the section markers the original author left, so no function was split across a boundary: config, product modes, upload, sheet analysis, PDF, quality, packing, cart, flow. They load as classic scripts in the original order and share one global scope, so evaluation is exactly what it was; the extraction was checked byte-identical against the original before the tags replaced it. dtf-site.html is 1,394 lines of markup and style. With no inline script left anywhere, the policy no longer needs a hash allowlist: script-src is now 'self' alone, which is stronger than what it replaced and cannot drift as the page changes. Three things depended on the old shape and were updated rather than worked around. The pricing parity test read the ladder out of the HTML and now reads it from site-config.js, still proving the server agrees with what the customer is shown. The isolated artwork test served four hardcoded script paths and now serves any script that resolves inside local/static, so the next file added does not silently 404. The CSP assertion checked the whole policy for 'unsafe-inline' and now checks the script-src directive alone, since style-src legitimately carries it. Co-Authored-By: Claude Opus 5 --- ROADMAP.md | 22 +- Relatorio-Semana-1-DTF.docx | Bin 0 -> 11921 bytes Relatorio-Semana-1-DTF.pdf | Bin 0 -> 57563 bytes dtf-site.html | 1587 +----------------- imagem-teste.jpg | Bin 0 -> 8220 bytes local/artwork_browser_test.mjs | 11 +- local/compile_web.py | 29 +- local/security_test.py | 9 +- local/static/site-cart.js | 77 + local/static/site-config.js | 108 ++ local/static/site-flow.js | 173 ++ local/static/site-modes.js | 95 ++ local/static/site-packing.js | 353 ++++ local/static/site-pdf.js | 400 +++++ local/static/site-quality.js | 186 ++ local/static/site-sheet.js | 107 ++ local/static/site-upload.js | 112 ++ local/test_pricing.py | 4 +- output/documents/relatorio-semana-1-dtf.docx | Bin 0 -> 40383 bytes 19 files changed, 1677 insertions(+), 1596 deletions(-) create mode 100644 Relatorio-Semana-1-DTF.docx create mode 100644 Relatorio-Semana-1-DTF.pdf create mode 100644 imagem-teste.jpg create mode 100644 local/static/site-cart.js create mode 100644 local/static/site-config.js create mode 100644 local/static/site-flow.js create mode 100644 local/static/site-modes.js create mode 100644 local/static/site-packing.js create mode 100644 local/static/site-pdf.js create mode 100644 local/static/site-quality.js create mode 100644 local/static/site-sheet.js create mode 100644 local/static/site-upload.js create mode 100644 output/documents/relatorio-semana-1-dtf.docx diff --git a/ROADMAP.md b/ROADMAP.md index bba49b4..1e53066 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -410,10 +410,11 @@ charges. Fix as part of 1.1. `smoke_test` failed on `/session`, blocking the release. Browser tests skip with a warning when the runner has no Chrome — **install `google-chrome-stable` on the runner (or set `CHROME_BIN`) to make them gate as well.** -- `[ ]` 5.2 — Remove or archive the dead prototypes `(F30)`: `portal/`, `kanban/`, - `agente/`, root `schema.sql` (~1,500 lines describing an abandoned model). Several - expose unauthenticated endpoints taking the acting user from the request body - (`/api/puxar`, `/api/devolver`). +- `[x]` 5.2 — `portal/`, `kanban/`, `agente/`, the root `schema.sql` and + `.env.exemplo` removed: 2,283 lines implementing a model this system abandoned, + referenced by nothing, with several endpoints taking the acting user from the + request body. The documents describing them are archived under `docs/historico/` + with a header saying they are background, not instructions. - `[x]` 5.3 — Root `requirements.txt` deleted. It pinned by wildcard, listed packages the system does not use, and sat next to the hash-locked `local/requirements.lock` inviting the wrong one to be installed. Only historical @@ -427,9 +428,16 @@ charges. Fix as part of 1.1. `SECURITY_REPORT.md` describe a MinIO localhost stack, an API with "no external network route", a `operator` / `local-operator-only` login the email-validated model rejects, and a release gate — none match the current tree. -- `[ ]` 5.6 — `dtf-site.html` is 2,889 lines with commercial rules, the packing - engine, PDF analysis, UI and checkout inline `(F29)`. Split at least the pricing - table and the packer so 3.2 has somewhere to land. +- `[x]` 5.6 — The Site's 1,575-line inline script is now nine files under + `local/static/`, cut at the author's own section boundaries so no function was + split: config, modes, upload, sheet analysis, PDF, quality, packing, cart, flow. + `dtf-site.html` is 1,394 lines of markup and style. The extraction was verified + byte-identical before the tags were swapped in, and they load as classic scripts + in the original order, so evaluation semantics are unchanged. + + A consequence worth having: with no inline script anywhere, the policy needs no + hash allowlist and is now simply `script-src 'self'`. `site-packing.js` is also + where a server-side packer (3.2) has to agree, which was the point of splitting. - `[ ]` 5.7 — Commercial rules duplicated between `FAIXAS` (JS) and `TIERS` (Python) `(F26)`. `test_pricing` guards parity; generate one from the other instead. - `[ ]` 5.11 — Nothing tests the schema against an empty database. The 4.1 indexes diff --git a/Relatorio-Semana-1-DTF.docx b/Relatorio-Semana-1-DTF.docx new file mode 100644 index 0000000000000000000000000000000000000000..c8e646d01db4d7667e90cbf8c0b2dc26e5b7516a GIT binary patch literal 11921 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 literal 0 HcmV?d00001 diff --git a/Relatorio-Semana-1-DTF.pdf b/Relatorio-Semana-1-DTF.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ef0441107ce0b5dca1c43ec889fde099bb063a2e GIT binary patch literal 57563 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 A+W-In literal 0 HcmV?d00001 diff --git a/dtf-site.html b/dtf-site.html index 10836d0..6fa2503 100644 --- a/dtf-site.html +++ b/dtf-site.html @@ -1373,1581 +1373,18 @@ footer a:hover{color:var(--laranja2)} - + + + + + + + + + + diff --git a/imagem-teste.jpg b/imagem-teste.jpg new file mode 100644 index 0000000000000000000000000000000000000000..35a13ea0595ce60b707830566256d304d020968c GIT binary patch literal 8220 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 res.setHeader('Content-Security-Policy',`default-src 'self'; script-src 'self' ${hashes.join(' ')}; script-src-attr 'none'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self'; object-src 'none'`); res.end(html);return; } - if(['/privacy.js','/upload.js','/checkout.js','/cart.js'].includes(req.url)){ - res.setHeader('Content-Type','text/javascript'); - res.end(await readFile('local/static'+req.url));return; + // Serve any script the page asks for, resolved inside local/static, rather than + // a hardcoded list: the Site's behaviour is split across several files and a + // list would silently 404 the next one added. + if(/^\/[\w.-]+\.js$/.test(req.url)){ + try{ + const body=await readFile(resolve('local/static'+req.url)); + res.setHeader('Content-Type','text/javascript');res.end(body);return; + }catch{ res.writeHead(404);res.end();return; } } res.writeHead(404);res.end(); }); diff --git a/local/compile_web.py b/local/compile_web.py index a0c9340..f4d3be9 100644 --- a/local/compile_web.py +++ b/local/compile_web.py @@ -1,15 +1,26 @@ -"""Hash only trusted source inline scripts at image build time for strict CSP.""" +"""Compile the gateway configuration: hash any trusted inline script for the CSP. + +The Site's behaviour now lives in separate files, so normally there is nothing to +hash and the policy is simply script-src 'self' — no allowlist to get wrong. The +hashing stays because an inline script added later must not silently need +'unsafe-inline'; it is hashed automatically instead. +""" import base64 import hashlib import os from pathlib import Path import re -root=Path('/build') -hashes=[] -for html in [root/'dtf-site.html',*(root/'local/static').glob('*.html')]: - for attributes,body in re.findall(r']*)>(.*?)',html.read_text(),re.S|re.I): - if not re.search(r'\bsrc\s*=',attributes,re.I): - hashes.append("'sha256-"+base64.b64encode(hashlib.sha256(body.encode()).digest()).decode()+"'") -template=Path(os.environ.get('NGINX_TEMPLATE', root/'local/nginx.conf.template')).read_text() -(root/'default.conf.template').write_text(template.replace('@SCRIPT_HASHES@',' '.join(hashes))) +root = Path('/build') +hashes = [] +for html in [root / 'dtf-site.html', *(root / 'local/static').glob('*.html')]: + for attributes, script in re.findall(r']*)>(.*?)', html.read_text(), re.S | re.I): + if not re.search(r'\bsrc\s*=', attributes, re.I) and script.strip(): + hashes.append("'sha256-" + base64.b64encode(hashlib.sha256(script.encode()).digest()).decode() + "'") + +template = Path(os.environ.get('NGINX_TEMPLATE', root / 'local/nginx.conf.template')).read_text() +rendered = template.replace('@SCRIPT_HASHES@', ' '.join(hashes)) +# Collapse the gap an empty hash list leaves behind, so the policy reads cleanly. +rendered = re.sub(r"(script-src 'self')\s+;", r'\1;', rendered) +(root / 'default.conf.template').write_text(rendered) +print(f'CSP script-src: {len(hashes)} inline hash(es)') diff --git a/local/security_test.py b/local/security_test.py index a4e5377..e3b2055 100644 --- a/local/security_test.py +++ b/local/security_test.py @@ -21,7 +21,14 @@ def run(): headers=raw('/',200) policy=headers['Content-Security-Policy'] assert "script-src-attr 'none'" in policy and "frame-ancestors 'none'" in policy - assert "'sha256-" in policy and "object-src 'none'" in policy + # The Site has no inline script, so the policy needs no hash allowlist at all. + # Assert the property that matters rather than the mechanism: nothing inline + # executes, and any hash that does appear was added deliberately at build time. + script_src = next(d.strip() for d in policy.split(';') if d.strip().startswith('script-src ')) + assert "'unsafe-inline'" not in script_src and "'unsafe-eval'" not in script_src, script_src + assert script_src == "script-src 'self'", script_src + assert "object-src 'none'" in policy + assert 'cdnjs' not in policy, 'pdf.js is vendored; no CDN belongs in the policy' raw('/api/health',400,{'Host':'attacker.invalid'}) raw('/api/account/logout',403,{'Content-Type':'application/json','Origin':'https://attacker.invalid'},b'{}') raw('/api/account/logout',403,{'Content-Type':'application/json','Origin':'http://localhost:9999'},b'{}') diff --git a/local/static/site-cart.js b/local/static/site-cart.js new file mode 100644 index 0000000..7b9e9bf --- /dev/null +++ b/local/static/site-cart.js @@ -0,0 +1,77 @@ +/* Site DTF — Cart totals and the order summary. + Extracted verbatim from the single inline script in dtf-site.html. + Loaded as classic scripts in the order listed there: they share one global + scope and run top to bottom, exactly as the original did. */ +// ── carrinho +let itemAtual=null; +function limpaPaineis(){ + ['qual','prev'].forEach(id=>$(id).classList.remove('on')); + if(!pedido.length){ $('carr').classList.remove('on'); } +} + +// O carrinho junta itens de produtos diferentes e cobra uma vez só. +function pintaPedido(){ + $('itens').innerHTML = pedido.map((it,i)=> + '
'+escapeHTML(it.tit)+''+escapeHTML(it.desc)+'
'+ + '
'+rs(it.total)+'
'+ + '
').join(''); + $('itens').querySelectorAll('[data-rmi]').forEach(b=>b.addEventListener('click',()=>{ + pedido.splice(+b.dataset.rmi,1); pintaPedido(); + })); + const itens = pedido.reduce((t,it)=>t+it.total,0) + (itemAtual? itemAtual.total : 0); + const frete = entrega.tipo==='frete' && entrega.cotado ? entrega.valor : 0; + const soma = itens + frete; + const n = pedido.length + (itemAtual?1:0); + const eco = [...pedido, ...(itemAtual?[itemAtual]:[])] + .reduce((t,it)=>t+(TABELA[it.modo]-it.unit)*it.cob, 0); + $('resLin').innerHTML = + '
'+n+(n===1?' item':' itens')+''+rs(itens+eco)+'
'+ + (eco>0.01? '
Desconto pela nota'+ + '− '+rs(eco)+'
' : '')+ + '
Frete'+ + (entrega.tipo==='retira' ? 'Grátis' + : entrega.cotado ? rs(frete) : 'a cotar')+'
'+ + '
Revisão e reencaixe'+ + 'Grátis
'; + $('carrTot').textContent=rs(soma); + $('bPagar').textContent = (pedido.length + (itemAtual?1:0))>1 + ? 'Pagar os '+(pedido.length+(itemAtual?1:0))+' itens de uma vez' + : 'Ir para o pagamento'; + if(pedido.length || itemAtual) $('carr').classList.add('on'); + if($('cartLink')) $('cartLink').textContent='Carrinho ('+n+')'; + window.dispatchEvent(new Event('dtf-cart-changed')); +} + +function carrinho(){ + const base=precoBase(nota), cob=cobrar(metros); + const unit=base, tot=cob*unit, sobra=+(cob-metros).toFixed(2); + $('carr').classList.add('on'); + let l='
'+MODOS[modo].tit+''+rs(base)+'/m
'; + { + const teto=TABELA[modo], eco=(teto-base)*cob; + l = '
'+MODOS[modo].tit+' · tabela'+rs(teto)+'/m
'; + l += eco>0.01 + ? '
Desconto pela nota '+nota+'−'+rs(eco)+'
' + : '
Nota '+nota+' · sem desconto—
'; + } + if(!ehFolha()) l += '
Montagem da folha'+ + 'inclusa · '+rs(MONTAGEM[modo])+'/m
'; + l += '
Metragem'+(sobra>0.001?' · arredondada para 10 cm':'')+ + ''+fmtM(cob)+' m
'; + l += '
Preço final do metro'+rs(unit)+'
'+ + '
Revisão e reencaixegrátis
'; + $('carrLin').innerHTML=l; + $('atual').style.display=''; + itemAtual={modo, tit:MODOS[modo].tit, nota, metros, cob, unit, total:tot, + localFiles:(ehFolha()?folhas:artes).map(x=>x.f), + desc:fmtM(cob)+' m · '+rs(unit)+'/m · nota '+nota}; + pintaPedido(); + previa(); + if(nota<90){ + const piso=pisoEscada(), ganho=(base-piso)*cob; + if(ganho>0.5) qm('Dá para chegar ao melhor preço. Corrigindo os pontos e chegando a 90, '+ + 'o metro cai para '+rs(piso)+' — são '+rs(ganho)+' a menos neste pedido.','var(--verde)'); + } +} +function qm(t,c){ const m=$('qmsg'); m.style.display='block'; m.style.color=c||'var(--verde)'; m.innerHTML=t; } + diff --git a/local/static/site-config.js b/local/static/site-config.js new file mode 100644 index 0000000..bcc9ec8 --- /dev/null +++ b/local/static/site-config.js @@ -0,0 +1,108 @@ +/* Site DTF — Product tables, commercial ladders, shared state and field validation. + Extracted verbatim from the single inline script in dtf-site.html. + Loaded as classic scripts in the order listed there: they share one global + scope and run top to bottom, exactly as the original did. */ + +const $=id=>document.getElementById(id); +const rs=v=>'R$ '+v.toFixed(2).replace('.',','); +const fmt=b=>b<1048576?(b/1024).toFixed(0)+' KB':(b/1048576).toFixed(1).replace('.',',')+' MB'; + +// ── modos +const MODOS={ + file: {tit:'Arquivo por metro', lg:'DTF têxtil · 57 cm', larg:57, + preco:'tabela R$ 19,90/m · até R$ 14,90', multi:true, folha:true, + sub:'Sua folha montada. Revisamos, corrigimos e reencaixamos antes de imprimir.', + zt:'Arraste suas folhas montadas', + zs:'uma ou várias · PNG, JPG, TIFF ou PDF · PSD, AI e CDR passam por tratamento'}, + avulsa:{tit:'Artes avulsas', lg:'DTF têxtil · 57 cm', larg:57, + preco:'tabela R$ 29,90/m · até R$ 24,90', multi:true, + sub:'Cada arte num arquivo. Montamos a folha com o melhor encaixe.', + zt:'Arraste suas artes aqui', + zs:'PNG ou JPG com fundo transparente · informe a largura de cada uma'}, + uvfile:{tit:'Arquivo por metro · UV', lg:'Adesivo UV · 28,5 cm', larg:28.5, + preco:'tabela R$ 85,90/m · até R$ 69,90', multi:true, folha:true, + sub:'Sua folha de UV montada. Revisamos, corrigimos e reencaixamos antes de imprimir.', + zt:'Arraste suas folhas de UV', + zs:'uma ou várias · PNG, JPG, TIFF ou PDF · PSD, AI e CDR passam por tratamento'}, + uv: {tit:'Artes avulsas · UV', lg:'Adesivo UV · 28,5 cm', larg:28.5, + preco:'tabela R$ 99,90/m · até R$ 83,90', multi:true, + sub:'Cada arte de UV num arquivo. Montamos a folha com o melhor encaixe.', + zt:'Arraste suas artes de UV', + zs:'PNG ou JPG com fundo transparente · informe a largura de cada arte'} +}; +// Quatro escadas. A de artes avulsas e a da folha pronta mais a montagem: +// R$ 10,00 no têxtil, R$ 14,00 no UV, constante em todas as faixas. +const FAIXAS={ + file: [[90,14.90],[75,16.20],[60,17.50],[40,18.70],[0,19.90]], + avulsa:[[90,24.90],[75,26.20],[60,27.50],[40,28.70],[0,29.90]], + uvfile:[[90,69.90],[75,73.90],[60,77.90],[40,81.90],[0,85.90]], + uv: [[90,83.90],[75,87.90],[60,91.90],[40,95.90],[0,99.90]] +}; +const TABELA={file:19.90, avulsa:29.90, uvfile:85.90, uv:99.90}; // teto de cada escada +const MONTAGEM={avulsa:10.00, uv:14.00}; // o que a montagem soma sobre a folha pronta +const ehFolha=m=>!!MODOS[m||modo].folha; +const pisoEscada=m=>FAIXAS[m||modo][0][1]; +const MINIMO_M=1, GAP_CM=0.5, RODAPE_CM=5; +const RENDERIZA=/[.](png|jpe?g|webp|gif|bmp)$/i; +// Folha pronta: dá para medir PNG, JPG e PDF. PSD, AI, TIFF e CDR entram, mas +// o navegador não abre — o cliente informa o comprimento e o arquivo passa por +// tratamento antes de imprimir. +const ACEITA={ + folha: /[.](png|jpe?g|tiff?|pdf|psd|psb|ai|cdr)$/i, + avulsa: /[.](png|jpe?g|webp)$/i +}; +const MEDE=/[.](png|jpe?g|webp|gif|bmp|pdf)$/i; +const DPI_RECUSA=96, DPI_AVISA=300; // régua dos designers · 02/09/2026 +// A especificação do PDF limita a página a 14.400 unidades — 200 pol, 508 cm. +// Passar disso só é válido com UserUnit (PDF 1.6+), que a maioria dos programas +// não escreve: o arquivo sai fora do padrão e só o gerador lê direito. +const PDF_MAX_PT=14400, PT_CM=2.54/72; +const extDe=n=>(n.split('.').pop()||'').toUpperCase(); +let CLIENTE='SEU NOME AQUI', PEDIDO='000000'; + +let modo=null, artes=[], recemChegada=null, metros=0, nota=0, reencaixado=false; +let folhas=[]; // [{f, med, rep, m}] · o cliente pode subir várias +const folhaTotalM=()=>folhas.reduce((t,x)=>t+(x.m||0)*(x.rep||1),0); +// O cliente declara o que está mandando escolhendo o produto, em vez de o site +// adivinhar pelo formato do arquivo. A declaração de folha montada é conferida na +// medição: uma imagem sem pixel para ocupar a largura do filme não é uma folha. +// Cada modo por metro tem o seu par de artes avulsas, e o seletor anda nos dois sentidos. +const PAR={file:'avulsa', avulsa:'file', uvfile:'uv', uv:'uvfile'}; +let montagemCm=0; // altura da folha montada pelo motor, em cm +const ZOOMS=[1,1.5,2,3,4]; let zoomI=0; // ampliação da montagem ao vivo +let pedido=[]; // itens já fechados · o pagamento é um só +// A escolha de entrega vale para o pedido inteiro, não por item. No Tiny, retirada +// grava forma_envio X, transportador DropStar e frete por conta do remetente — igual +// ao que a expedição já recebe hoje, sem criar processo novo. +let entrega={tipo:'retira', cep:'', valor:0, cotado:true}; +let cliente={cnpj:'', zap:'', mail:''}; +let caminho='auto'; // o que o cliente diz que vai mandar + +// CNPJ com os dois dígitos verificadores · máscara sem conta não vale nada +function cnpjOk(v){ + const d=(v||'').replace(/\D/g,''); + if(d.length!==14 || /^(\d)\1{13}$/.test(d)) return false; + const calc=(base,pesos)=>{ + const soma=base.split('').reduce((t,n,i)=>t+ +n*pesos[i],0); + const r=soma%11; return r<2? 0 : 11-r; + }; + const p1=[5,4,3,2,9,8,7,6,5,4,3,2], p2=[6,...p1]; + return calc(d.slice(0,12),p1)===+d[12] && calc(d.slice(0,13),p2)===+d[13]; +} +const zapOk=v=>{ const d=(v||'').replace(/\D/g,''); return d.length===10||d.length===11; }; +const mailOk=v=>/^[^\s@]+@[^\s@]+\.[a-z]{2,}$/i.test((v||'').trim()); +const clienteOk=()=>cnpjOk(cliente.cnpj)&&zapOk(cliente.zap)&&mailOk(cliente.mail); +const larguraFilme=()=>MODOS[modo].larg; +// cobrança proporcional: arredonda para cima em 10 cm · 2,75 m vira 2,80 m +const cobrar=m=>Math.max(MINIMO_M, Math.ceil(m*10)/10); +const fmtM=m=>m.toFixed(2).replace('.',','); +const precoBase=n=> + (FAIXAS[modo].find(f=>n>=f[0])||FAIXAS[modo][FAIXAS[modo].length-1])[1]; +const descontoMetragem=()=>0; // sem progressão por quantidade +const cls=n=>n>=85?'ok':n>=50?'av':'er'; +const dpiDe=a=>a.cm>0? a.px/(a.cm/2.54) : 0; +// girar 90° inverte a proporção · espelhar não muda medida, só o desenho +const propDe=a=>{ const p=a.prop||1; return a.giro? 1/p : p; }; +const TAMANHOS={57:[10,15,20,25,28.2], 28.5:[5,8,10,14,28.2]}; +const px=f=>Math.round(Math.min(6000,Math.max(600,Math.sqrt(f.size/1024)*95))); + diff --git a/local/static/site-flow.js b/local/static/site-flow.js new file mode 100644 index 0000000..bd9bd9a --- /dev/null +++ b/local/static/site-flow.js @@ -0,0 +1,173 @@ +/* Site DTF — Carousel, path selector, delivery choice and input masks. + Extracted verbatim from the single inline script in dtf-site.html. + Loaded as classic scripts in the order listed there: they share one global + scope and run top to bottom, exactly as the original did. */ +// ── decisões +// Remanejar conserta encaixe, não resolução — e a nota mede só resolução. +// Por isso a nota NÃO muda aqui. E a metragem nova só existe depois que o motor +// roda na folha de verdade, no servidor: o navegador recebe a folha fechada, +// não as artes separadas, então não há como recalcular aqui sem inventar número. +$('qOk').addEventListener('click',()=>{ + if($('qOk').disabled) return; + const ok=$('ciente').classList.contains('on'); + qm(ok? 'Registrado. Você confirmou que viu a ressalva de resolução.' + : 'Certo. Seu pedido está montado abaixo.','var(--tx)'); + $('carr').scrollIntoView({behavior:'smooth',block:'nearest'}); +}); + +$('pOk').addEventListener('click',()=>$('carr').scrollIntoView({behavior:'smooth'})); +$('pMais').addEventListener('click',()=>$('foco').scrollIntoView({behavior:'smooth'})); +$('pVolta').addEventListener('click',()=>{ + folhas=[]; artes=[]; reencaixado=false; pintaFolha(); pintaArtes(); + limpaPaineis(); // o carrinho só some se o pedido estiver vazio + $('foco').scrollIntoView({behavior:'smooth'}); +}); +// carrossel +(function(){ + const tr=$('trilho'); if(!tr) return; + const slides=[...tr.children], pontos=$('cPontos'); + slides.forEach((_,i)=>{ + const b=document.createElement('button'); + b.className='pt'+(i?'':' on'); b.setAttribute('aria-label','Cartão '+(i+1)); + b.addEventListener('click',()=>ir(i)); pontos.appendChild(b); + }); + function ir(i){ + i=Math.max(0,Math.min(slides.length-1,i)); + tr.scrollTo({left:slides[i].offsetLeft-tr.offsetLeft,behavior:'smooth'}); + } + function atual(){ + const meio=tr.scrollLeft+tr.clientWidth/2; + let a=0,d=Infinity; + slides.forEach((s,i)=>{ + const c=s.offsetLeft-tr.offsetLeft+s.offsetWidth/2; + if(Math.abs(c-meio){ + const a=atual(); + [...pontos.children].forEach((p,i)=>p.classList.toggle('on',i===a)); + },{passive:true}); + $('cAnt').addEventListener('click',()=>ir(atual()-1)); + $('cPro').addEventListener('click',()=>ir(atual()+1)); +})(); + +// A declaração de formato só orienta: filtra o seletor e mostra o preço esperado. +// Se o arquivo que chegar contrariar, o arquivo ganha e o site avisa. +const AUTO=/[.](png|jpe?g|webp|pdf)$/i; +function pintaCaminhos(){ + if(!$('caminhos')) return; + const ehF=modo&&ehFolha(); + $('caminhos').style.display = ehF? '' : 'none'; + if(!ehF) return; + document.querySelectorAll('#caminhos .cam').forEach(b=>b.classList.toggle('on', b.dataset.cam===caminho)); + $('camA').textContent='a partir de '+rs(pisoEscada()); + $('camB').textContent=rs(TABELA[modo])+' fixo'; + $('inp').accept = caminho==='auto' ? '.png,.jpg,.jpeg,.pdf' + : '.tif,.tiff,.psd,.psb,.ai,.cdr'; + $('zSub').textContent = caminho==='auto' + ? 'PNG, JPG ou PDF · conferência automática, nota na tela' + : 'CDR, AI, PSD ou TIFF · sem conferência, metro pela tabela'; +} +document.querySelectorAll('#caminhos .cam').forEach(b=>b.addEventListener('click',()=>{ + caminho=b.dataset.cam; avisoCam(''); pintaCaminhos(); +})); +function avisoCam(txt){ + const el=$('trocouCam'); if(!el) return; + el.innerHTML=txt; el.style.display = txt? '' : 'none'; +} + +function pintaEntrega(){ + document.querySelectorAll('.opE').forEach(b=> + b.classList.toggle('on', b.dataset.ent===entrega.tipo)); + $('cep').classList.toggle('on', entrega.tipo==='frete'); + $('vFrete').textContent = entrega.tipo!=='frete' ? '—' + : entrega.cotado ? rs(entrega.valor) : 'a cotar'; + $('avisoE').innerHTML = entrega.tipo==='retira' + ? 'DTF é impresso depois que você paga. Avisamos no WhatsApp quando estiver '+ + 'pronto para retirar — não venha antes do aviso.' + : ''; + $('bPagar').disabled = !entrega.cotado || !clienteOk(); + pintaPedido(); +} + +// máscaras e conferência dos campos do cliente +function liga(id,fmt,val,chave,erro,msg){ + const el=$(id), er=$(erro); + el.addEventListener('input',e=>{ + e.target.value=fmt(e.target.value); + cliente[chave]=e.target.value; + el.classList.remove('ruim','bom'); er.textContent=''; + if(val(e.target.value)) el.classList.add('bom'); + pintaEntrega(); + }); + el.addEventListener('blur',()=>{ + if(!el.value){ el.classList.remove('ruim','bom'); er.textContent=''; return; } + const ok=val(el.value); + el.classList.toggle('ruim',!ok); el.classList.toggle('bom',ok); + er.textContent = ok? '' : msg; + }); +} +liga('fCnpj', v=>{ + const d=v.replace(/\D/g,'').slice(0,14); + return d.replace(/^(\d{2})(\d)/,'$1.$2').replace(/^(\d{2})\.(\d{3})(\d)/,'$1.$2.$3') + .replace(/\.(\d{3})(\d)/,'.$1/$2').replace(/(\d{4})(\d)/,'$1-$2'); +}, cnpjOk, 'cnpj', 'eCnpj', 'CNPJ inválido · confira os números'); +liga('fZap', v=>{ + const d=v.replace(/\D/g,'').slice(0,11); + return d.length>6 ? '('+d.slice(0,2)+') '+d.slice(2,d.length-4)+'-'+d.slice(-4) + : d.length>2 ? '('+d.slice(0,2)+') '+d.slice(2) : d; +}, zapOk, 'zap', 'eZap', 'Faltam dígitos no número'); +liga('fMail', v=>v.trim(), mailOk, 'mail', 'eMail', 'E-mail inválido'); +function pintaZoom(){ + $('zTxt').textContent=Math.round(ZOOMS[zoomI]*100)+'%'; + $('zMenos').disabled = zoomI===0; + $('zMais').disabled = zoomI===ZOOMS.length-1; +} +$('zMais').addEventListener('click',()=>{ + if(zoomI{ + if(zoomI>0){ zoomI--; pintaZoom(); previaAoVivo(); } +}); +pintaZoom(); + +document.querySelectorAll('.opE').forEach(b=>b.addEventListener('click',()=>{ + entrega.tipo=b.dataset.ent; + entrega.cotado = entrega.tipo==='retira'; + entrega.valor=0; + if(entrega.tipo==='frete') $('cepMsg').innerHTML=''; + pintaEntrega(); +})); +$('cepIn').addEventListener('input',e=>{ + const v=e.target.value.replace(/\D/g,'').slice(0,8); + e.target.value = v.length>5 ? v.slice(0,5)+'-'+v.slice(5) : v; + entrega.cep=v; entrega.cotado=false; entrega.valor=0; pintaEntrega(); +}); +$('bCep').addEventListener('click',async()=>{ + if(entrega.cep.length!==8){ $('cepMsg').innerHTML='CEP incompleto.'; return; } + if(window.dtfFreight){ await window.dtfFreight(); return; } + // A cotação vem da transportadora. Enquanto a integração não existir, o pedido não + // fecha por frete: o valor precisa estar fechado antes do Mercado Pago. + $('cepMsg').innerHTML='Cotação de frete ainda não integrada.
'+ + 'O valor precisa vir da transportadora antes do pagamento — senão você pagaria um '+ + 'total que não é o final. Por enquanto, retire em Franca ou fale conosco no WhatsApp.'; + pintaEntrega(); +}); + +$('bMais').addEventListener('click',()=>{ + if(itemAtual){ pedido.push(itemAtual); itemAtual=null; } + $('atual').style.display='none'; $('carrLin').innerHTML=''; + $('foco').classList.remove('on'); $('cards').style.display=''; + ['qual','prev'].forEach(id=>$(id).classList.remove('on')); + modo=null; artes=[]; folhas=[]; metros=0; nota=0; montagemCm=0; + pintaPedido(); + document.getElementById('envio').scrollIntoView({behavior:'smooth'}); +}); + +$('bPagar').addEventListener('click',()=>{ + if(window.dtfCheckout) window.dtfCheckout(); + else alert('O pedido online está indisponível no momento. Tente novamente em instantes.'); +}); +pintaEntrega(); + diff --git a/local/static/site-modes.js b/local/static/site-modes.js new file mode 100644 index 0000000..d12fdca --- /dev/null +++ b/local/static/site-modes.js @@ -0,0 +1,95 @@ +/* Site DTF — Choosing a product, and the declared sheet/loose-artwork switch. + Extracted verbatim from the single inline script in dtf-site.html. + Loaded as classic scripts in the order listed there: they share one global + scope and run top to bottom, exactly as the original did. */ +// ── escolha +document.querySelectorAll('.ec').forEach(b=>b.addEventListener('click',()=>abrir(b.dataset.modo))); +function abrir(m){ + modo=m; artes=[]; folhas=[]; metros=0; nota=0; reencaixado=false; + montagemCm=0; + avisoTipo(''); + pintaModo(); + $('lista').innerHTML=''; $('rA').style.display='none'; recusa([]); + recemChegada=null; caminho='auto'; avisoCam(''); pintaCaminhos(); + $('vMt').textContent='—'; previaAoVivo(); agendaAvaliacao(); + limpaPaineis(); // o carrinho só some se o pedido estiver vazio + $('foco').scrollIntoView({behavior:'smooth',block:'start'}); +} +function pintaModo(){ + const m=modo; + const c=MODOS[m]; + $('cards').style.display='none'; $('foco').classList.add('on'); + $('fTit').textContent=c.tit; $('fLg').textContent=c.lg; + $('fPreco').textContent=c.preco; $('fSub').textContent=c.sub; + $('zTit').textContent=c.zt; $('zSub').textContent=c.zs; + $('inp').multiple=c.multi; + $('inp').accept = ehFolha(m) ? '.png,.jpg,.jpeg,.pdf' + : '.png,.jpg,.jpeg,.webp'; + $('catFoco').className='cat '+(ehFolha(m)?'file':'av'); + $('larguraFolhaPronta').textContent=String(c.larg).replace('.',','); + $('foco2').classList.remove('sofila'); // a caixa de requisitos vale nos 4 modos + $('montcabTit').textContent = ehFolha(m) ? 'Sua folha' : 'Montagem ao vivo'; + pintaTipoEnvio(); +} +// O par folha montada / artes separadas fica visível e com preço nos dois lados, +// nos dois modos: nenhuma troca acontece sem o cliente ver o que muda, e sempre +// dá para voltar atrás sem recomeçar o pedido. +function pintaTipoEnvio(){ + const el=$('tipoEnvio'); if(!el) return; + el.hidden=!modo; + if(el.hidden) return; + const folha = ehFolha() ? modo : PAR[modo]; + const avulsa = ehFolha() ? PAR[modo] : modo; + $('tipoFolhaPreco').textContent='a partir de '+rs(pisoEscada(folha)); + $('tipoAvulsaPreco').textContent='a partir de '+rs(pisoEscada(avulsa))+' · montagem inclusa'; + $('larguraFolhaPronta').textContent=String(MODOS[folha].larg).replace('.',','); + const atual = ehFolha() ? 'folha' : 'avulsa'; + el.querySelectorAll('.cam').forEach(b=>{ + b.classList.toggle('on', b.dataset.tipo===atual); + b.disabled=folhas.length>0 || artes.length>0; // já tem arquivo: a escolha está feita + }); +} +function avisoTipo(txt){ + const el=$('avisoTipo'); if(!el) return; + el.innerHTML=txt; el.style.display = txt? '' : 'none'; +} +// A imagem foi declarada folha montada mas não tem resolução para isso. Devolve o +// arquivo com o motivo e o caminho certo — sem trocar preço por conta própria. +function recusaFolha(x, md){ + const arquivo=x.f, destino = modo==='uvfile' ? 'uv' : 'avulsa'; + folhas=folhas.filter(y=>y!==x); + pintaFolha(); + avisoTipo(''+escapeHTML(arquivo.name)+' não é uma folha montada. '+ + 'Para ocupar os '+n1(larguraFilme())+' cm do filme ela teria só '+md.dpiFolha+ + ' DPI, e o mínimo para imprimir é '+DPI_RECUSA+'. '+ + 'Se for uma arte para montarmos, use '+MODOS[destino].tit+', '+ + 'a partir de '+rs(pisoEscada(destino))+'/m. '+ + ''); + const botao=$('usarAvulsa'); + if(botao) botao.addEventListener('click',()=>{ + avisoTipo(''); trocaTipo(PAR[modo]); sel([arquivo]); + }); +} +document.querySelectorAll('#tipoEnvio .cam').forEach(b=>b.addEventListener('click',()=>{ + if(b.disabled) return; + const querFolha=b.dataset.tipo==='folha'; + if(querFolha===ehFolha()) return; + avisoTipo(''); trocaTipo(PAR[modo]); // escolha explícita do cliente +})); +// Troca declarada, nunca silenciosa: o cliente já viu os dois preços no seletor. +function trocaTipo(destino){ + if(!destino || modo===destino) return; + folhas=[]; artes=[]; recemChegada=null; metros=0; nota=0; montagemCm=0; + modo=destino; + $('lista').innerHTML=''; $('rA').style.display='none'; recusa([]); + caminho='auto'; avisoCam(''); + pintaModo(); pintaCaminhos(); limpaPaineis(); agendaAvaliacao(); previaAoVivo(); +} +$('bVoltar').addEventListener('click',()=>{ + itemAtual=null; $('atual').style.display='none'; $('carrLin').innerHTML=''; pintaPedido(); + $('foco').classList.remove('on'); $('cards').style.display=''; + ['qual','prev'].forEach(id=>$(id).classList.remove('on')); + if(!pedido.length) $('carr').classList.remove('on'); + document.getElementById('envio').scrollIntoView({behavior:'smooth'}); +}); + diff --git a/local/static/site-packing.js b/local/static/site-packing.js new file mode 100644 index 0000000..0bb5674 --- /dev/null +++ b/local/static/site-packing.js @@ -0,0 +1,353 @@ +/* Site DTF — Masks, the nesting engine, and drawing the assembled film. + Extracted verbatim from the single inline script in dtf-site.html. + Loaded as classic scripts in the order listed there: they share one global + scope and run top to bottom, exactly as the original did. */ +// ── imagens e prévia +function carregarImagem(file){ + return new Promise(res=>{ + if(!RENDERIZA.test(file.name)) return res(null); + const fr=new FileReader(); + fr.onload=()=>{ const im=new Image(); im.onload=()=>res(im); im.onerror=()=>res(null); im.src=fr.result; }; + fr.onerror=()=>res(null); + fr.readAsDataURL(file); + }); +} +// A folha precisa parecer uma folha: borda, largura marcada e régua de metros. +// Sem isso o cliente não tem noção de escala e acha que está mal encaixado. +function molduraFolha(ctx,W,H,larguraCm,metrosReais){ + const REG=34; // faixa da régua, à direita + ctx.save(); + // fundo da régua + ctx.fillStyle='#FAFAF8'; ctx.fillRect(W,0,REG,H); + ctx.strokeStyle='#D5DAE3'; ctx.lineWidth=1; + ctx.beginPath(); ctx.moveTo(W+.5,0); ctx.lineTo(W+.5,H); ctx.stroke(); + // marcas a cada 10 cm, número a cada metro + const pxPorM = H/Math.max(metrosReais,0.01); + ctx.font='9px "Roboto", sans-serif'; ctx.textAlign='left'; + for(let cm=0; cm<=metrosReais*100; cm+=10){ + const y=cm/100*pxPorM; + if(y>H) break; + const metro = cm%100===0; + ctx.strokeStyle = metro? '#B4BAC5' : '#E1E5EC'; + ctx.beginPath(); ctx.moveTo(W+1,y+.5); ctx.lineTo(W+(metro?12:6),y+.5); ctx.stroke(); + if(metro && cm>0){ + ctx.fillStyle='#8A8F98'; + ctx.fillText((cm/100)+' m', W+14, y+3); + } + } + // borda da folha + ctx.strokeStyle='#B4BAC5'; ctx.lineWidth=1; + ctx.strokeRect(.5,.5,W-1,H-1); + // cota da largura, no topo + ctx.fillStyle='#FFFFFFDD'; ctx.fillRect(0,0,W,18); + ctx.strokeStyle='#8A8F98'; ctx.lineWidth=1; + ctx.beginPath(); + ctx.moveTo(8,9); ctx.lineTo(W-8,9); + ctx.moveTo(8,5); ctx.lineTo(8,13); + ctx.moveTo(W-8,5); ctx.lineTo(W-8,13); + ctx.stroke(); + const t=String(larguraCm).replace('.',',')+' cm'; + ctx.font='600 10px "Roboto", sans-serif'; ctx.textAlign='center'; + const tw=ctx.measureText(t).width; + ctx.fillStyle='#FAFAF8'; ctx.fillRect(W/2-tw/2-6,2,tw+12,14); + ctx.fillStyle='#5A6570'; ctx.fillText(t,W/2,13); + ctx.restore(); +} + +const LOGO_URL='https://cdn.vnda.com.br/dropstar/2025/12/17/logo-header-15125700-16129080.png?v=1766009286'; +const logoImg=new Image(); logoImg.crossOrigin='anonymous'; logoImg.src=LOGO_URL; +function marcaDagua(ctx,w,h){ + ctx.save(); ctx.globalAlpha=.14; + const passo=Math.max(90,w/2.1); + ctx.translate(w/2,h/2); ctx.rotate(-Math.PI/7); ctx.translate(-w/2,-h/2); + if(logoImg.complete && logoImg.naturalWidth){ + const lw=Math.max(70,w/3.4), lh=lw*(logoImg.naturalHeight/logoImg.naturalWidth); + for(let y=-h;y16 ? 1 : 0; cheias+=bruto[i]; } + return dilatar(bruto,cw,ch,cheias); +} + +// Encaixe pela forma, como o motor do servidor: mapa de ocupação real, +// máscara por arte e candidatos numa grade ao longo da largura. +// pecas em centímetros: {w,h,img}. W é a largura do filme em cm. +function encaixar(pecas,W){ + const cols=Math.ceil(W/CEL)+1; // +1 = a coluna de folga da dilatação + let linhas=0; const mapa=[]; + function garantir(ate){ while(linhascols) return true; + for(let y=0;y=linhas) continue; // vazio abaixo do que já existe + const L=mapa[gy], off=y*m.cw; + for(let x=0;x(b.w*b.h)-(a.w*a.h)); + + ordem.forEach(p=>{ + const oris=[{w:p.w,h:p.h,rot:0}]; + if(!p.mPronta && !p.travado && Math.abs(p.w-p.h)>1 && p.h<=W) oris.push({w:p.h,h:p.w,rot:90}); + + let melhor=null; + oris.forEach(o=>{ + if(o.w>W) return; + // a máscara segue a medida da orientação escolhida · girada, largura e altura trocam + const m = (p.mPronta && o.rot===0) ? p.mPronta : mascara(p.img, o.w, o.h, + o.rot+(p.a?.giro||0), p.a?.esp); + const xs=new Set([0]), ys=new Set([0]); + postas.forEach(q=>{ xs.add(q.cx+q.m.cw); xs.add(q.cx); ys.add(q.cy+q.m.ch); ys.add(q.cy); }); + const passoX=Math.max(1,Math.round(cols/34)); + for(let x=0;x+m.cw<=cols;x+=passoX) xs.add(x); + xs.add(cols-m.cw); + const passoY=Math.max(1,Math.round(Math.max(linhas,1)/50)); + for(let y=0;y<=linhas;y+=passoY) ys.add(y); + const XS=[...xs].filter(x=>x>=0 && x+m.cw<=cols && x*CEL+o.w<=W+0.01).sort((a,b)=>a-b); + const YS=[...ys].filter(y=>y>=0).sort((a,b)=>a-b); + + for(const cy of YS){ + if(melhor && cy*CEL > melhor.y + CEL) break; + for(const cx of XS){ + if(cx*CEL + o.w > W + 0.01) continue; // não deixa a peça sair do filme + if(colide(m,cx,cy)) continue; + const cand={cx,cy,m,x:cx*CEL,y:cy*CEL,w:o.w,h:o.h,rot:o.rot,ref:p}; + // menor y, depois menor x · em empate, mantém a orientação original. + // Desempatar por largura gira tudo, e texto deitado na prévia parece erro. + const ganha = !melhor || cand.yp.y+p.h),1); + return {pos:postas, altura}; +} + +function desenhaMontagem(area,lista,mts,W,fora,aoTerminar){ + const versao=area.montagemVersao=(area.montagemVersao||0)+1; + const filme=larguraFilme(), PX_CM=W/filme; + const itens=lista.map(a=>({...a})); + if(fora) fora.innerHTML=''; + if(!itens.length){ + area.innerHTML='
A montagem aparece aqui'+ + 'informe a largura de cada arte e a folha se desenha sozinha
'; + if(aoTerminar) aoTerminar({alturaCm:0, uso:0, girou:0}); + return; + } + + Promise.all(itens.map(a=>carregarImagem(a.f).then(img=>({a,img})))).then(imagens=>{ + if(area.montagemVersao!==versao) return; + const cargas=imagens.flatMap(c=>Array.from({length:c.a.q||1},()=>({...c}))); + cargas.forEach(c=>{ + c.w = c.a.cm; // o motor trabalha em centímetros + c.h = c.w * (c.img ? (c.a.giro? c.img.width/c.img.height : c.img.height/c.img.width) : 1); + c.travado = !!c.a.giro; // giro do cliente manda no encaixe + }); + const enc=encaixar(cargas,filme); + const alturaCm=enc.altura; + const pos=enc.pos.map(p=>({...p, x:p.x*PX_CM, y:p.y*PX_CM, w:p.w*PX_CM, h:p.h*PX_CM})); + const altura=alturaCm*PX_CM; + const usado=Math.max(altura,10); + const REG=34; + const cv=document.createElement('canvas'); + cv.width=W+REG; cv.height=Math.round(usado); + const ctx=cv.getContext('2d'); + ctx.fillStyle='#fff'; ctx.fillRect(0,0,cv.width,cv.height); + const pal=['#C3CEDD','#B7A9C8','#A9B7C8','#A8BFA9','#C8C0A0','#CBB6B6']; + let girou=0; + pos.forEach((p,j)=>{ + const c=p.ref; + if(p.rot===90) girou++; + ctx.save(); + const giro = (p.rot===90?90:0) + (c.a && c.a.giro? 90:0); + ctx.translate(p.x+p.w/2, p.y+p.h/2); + ctx.rotate(giro*Math.PI/180); + if(c.a && c.a.esp) ctx.scale(-1,1); + const dw = giro%180===90 ? p.h : p.w, dh = giro%180===90 ? p.w : p.h; + if(c.img) ctx.drawImage(c.img,-dw/2,-dh/2,dw,dh); + else{ ctx.fillStyle=pal[j%pal.length]; ctx.fillRect(-dw/2,-dh/2,dw,dh); } + ctx.restore(); + if(!c.img){ + ctx.fillStyle='#5A6570'; ctx.font='11px "Roboto", sans-serif'; ctx.textAlign='center'; + ctx.fillText(c.a.f.name.split('.').pop().toUpperCase(),p.x+p.w/2,p.y+p.h/2+4); + } + }); + molduraFolha(ctx,W,usado,filme,alturaCm/100); + marcaDagua(ctx,W,cv.height); + area.innerHTML=''; area.appendChild(cv); + + // O que virou arte é a área pintada, não a soma das caixas: com encaixe por forma + // as caixas se sobrepõem de propósito e a soma delas passa de 100%. + const areaArteCm=enc.pos.reduce((s,p)=> + s + (p.m && p.m.cheias!=null ? p.m.cheias*CEL*CEL : p.w*p.h), 0); + const uso=Math.max(1,Math.min(100, + Math.round(areaArteCm/(filme*Math.max(alturaCm,0.1))*100))); + const el=document.createElement('div'); + el.className = uso>=70? 'usoBom' : 'usoRuim'; + el.innerHTML = (uso>=70 + ? ''+uso+'% da folha virou arte bom aproveitamento' + : ''+uso+'% da folha virou arte dá para melhorar: artes de larguras parecidas '+ + 'se encaixam melhor lado a lado') + + (girou? ' · '+girou+ + (girou>1?' artes giradas':' arte girada')+' 90° para caber melhor':''); + (fora||area).appendChild(el); + if(aoTerminar) aoTerminar({alturaCm, uso, girou}); + }); +} + +function previaArtes(){ desenhaMontagem($('pArea'), artes, metros, 560); } + +// montagem ao vivo, ao lado da fila · redesenha 140 ms depois da ultima mexida +let tMont=null; +function previaAoVivo(){ + clearTimeout(tMont); + $('vArea').montagemVersao=($('vArea').montagemVersao||0)+1; + if(ehFolha()){ + $('vUso').innerHTML=''; + const tot=folhaTotalM(); + $('vMt').textContent = tot>0 ? fmtM(cobrar(tot))+' m' : '—'; + const medidas=folhas.filter(x=>x.med); + const previews=folhas.filter(x=>x.previewSrc); + if(previews.length){ + const painel=document.createElement('div'); painel.className='folhaPrevias'; + previews.forEach((x,index)=>{ + const figura=document.createElement('figure'); figura.className='folhaPrevia'; + const imagem=new Image(); imagem.src=x.previewSrc; imagem.alt=x.f.name; + const legenda=document.createElement('figcaption'); + legenda.textContent=(previews.length>1 ? (index+1)+'. ' : '')+x.f.name+ + (x.med ? ' · '+n1(x.med.larg)+' × '+n1(x.med.alt)+' cm' : ''); + figura.append(imagem,legenda); painel.append(figura); + }); + $('vArea').replaceChildren(painel); + }else{ + $('vArea').innerHTML = medidas.length + ? '
'+(medidas.length>1 + ? medidas.length+' folhas · '+fmtM(tot)+' m' + : n1(medidas[0].med.larg)+' × '+n1(medidas[0].med.alt)+' cm')+''+ + ''+(medidas.length>1? 'medidas lidas de cada arquivo' : medidas[0].med.fonte)+ + ' · vão para a máquina do jeito que você montou
' + : '
Sua folha aparece aqui'+ + 'a medida é lida do próprio arquivo, não do peso dele
'; + } + return; + } + tMont=setTimeout(()=>{ + const prontas=artes.filter(a=>a.cm>0); + const W=Math.round(300*ZOOMS[zoomI]); + $('vArea').classList.toggle('ampliado', zoomI>0); + desenhaMontagem($('vArea'), prontas, 0, W, $('vUso'), r=>{ + montagemCm = r.alturaCm; // altura real da folha montada + $('vMt').textContent = r.alturaCm? fmtM(cobrar(r.alturaCm/100))+' m' : '—'; + resumoArtes(); // o resumo lê a mesma folha + agendaAvaliacao(); // a nota só sai com a metragem pronta + }); + $('vArea').scrollTop=0; + },140); +} + +function motivoNota(){ + if(!nota) return 'sem nota ainda · a conferência roda na sala antes de imprimir'; + if(!ehFolha()){ + const ruins=artes.filter(a=>a.cm>0 && (a.px/(a.cm/2.54))<300); + if(!ruins.length) return 'nota '+nota+' · todas as artes em 300 DPI ou mais'; + const pior=ruins.reduce((p,a)=>(a.px/(a.cm/2.54))<(p.px/(p.cm/2.54))?a:p); + return 'nota '+nota+' · '+ruins.length+(ruins.length>1?' artes abaixo':' arte abaixo')+ + ' de 300 DPI · a menor é "'+pior.f.name+'" com '+ + Math.round(pior.px/(pior.cm/2.54))+' DPI em '+pior.cm.toFixed(1).replace('.',',')+' cm'; + } + return nota>=90 ? 'nota '+nota+' · resolução ótima em toda a folha' + : 'nota '+nota+' · a resolução é o único ponto — o resto corrigimos por você'; +} +function previa(){ + // Na folha pronta o painel não tem o que mostrar: a folha vem fechada, ninguém + // olhou dentro dela. Largura e metragem — o que de fato medimos — já aparecem + // na coluna ao lado e no carrinho. + if(ehFolha()){ $('prev').classList.remove('on'); return; } + const cob=cobrar(metros), sobra=+(cob-metros).toFixed(2); + $('pMt').innerHTML=fmtM(cob)+' m'; + $('pSub').textContent='folha montada por nós com as artes que você enviou'; + const linhas=[ + ['var(--verde)','✓','Largura',larguraFilme()+' cm · dentro do padrão da máquina'], + ['var(--verde)','✓','Espaço entre artes','5 mm garantidos'], + [nota>=75?'var(--verde)':'var(--laranja)', nota>=75?'✓':'!','Qualidade da folha',motivoNota()], + ['var(--verde)','✓','Metragem proporcional', + 'sua folha tem '+fmtM(metros)+' m e você paga '+fmtM(cob)+ + ' m · arredondamos só até os 10 cm mais próximos'], + ]; + linhas.push(['var(--verde)','+','Identificação do pedido', + 'aplicamos seu nome e o número do pedido no fim da folha · por nossa conta']); + $('pInfo').innerHTML=linhas.map(l=> + '
'+l[1]+''+ + '
'+escapeHTML(l[2])+''+escapeHTML(l[3])+'
').join(''); + $('prev').classList.add('on'); + $('pArea').innerHTML='
montando a prévia…
'; + try{ previaArtes(); } + catch(e){ $('pArea').innerHTML='
Prévia indisponível'+ + 'O arquivo será processado normalmente.
'; } +} + diff --git a/local/static/site-pdf.js b/local/static/site-pdf.js new file mode 100644 index 0000000..4011ebd --- /dev/null +++ b/local/static/site-pdf.js @@ -0,0 +1,400 @@ +/* Site DTF — PDF measurement and rasterisation, and the DPI of images inside one. + Extracted verbatim from the single inline script in dtf-site.html. + Loaded as classic scripts in the order listed there: they share one global + scope and run top to bottom, exactly as the original did. */ +// ── PDF também é conferido: rasteriza a página e mede o DPI das imagens de dentro. +// Arte vetorial não tem resolução — nesses casos a nota é máxima, e isso é correto. +// Served from this origin, not a CDN: the Site keeps working when a third party +// does not, and the CSP can name only 'self' for scripts and workers. Provenance +// and hashes are recorded in local/static/vendor/README.md. +const PDFJS_URL='/vendor/pdf.min.js'; +const PDFJS_WORKER=new URL('/vendor/pdf.worker.min.js', location.origin).href; +let pdfLibP=null, temWorker=null; +// Alguns navegadores e iframes bloqueiam Worker de blob. Descobrimos antes de +// tentar, porque sem worker o PDF é lido na thread principal e precisa de outra +// estratégia: grade mais grossa e mais tempo. +function testaWorker(url){ + try{ const w=new Worker(url); w.terminate(); return true; }catch(e){ return false; } +} +function carregarPdfJs(){ + if(typeof window==='undefined') return Promise.resolve(null); + if(window.pdfjsLib) return Promise.resolve(window.pdfjsLib); + if(pdfLibP) return pdfLibP; + pdfLibP=new Promise(res=>{ + const sc=document.createElement('script'); + sc.src=PDFJS_URL; + sc.onload=()=>{ + try{ + const codigo='importScripts("'+PDFJS_WORKER+'");'; + const url=URL.createObjectURL(new Blob([codigo],{type:'application/javascript'})); + temWorker=testaWorker(url); + window.pdfjsLib.GlobalWorkerOptions.workerSrc = temWorker? url : PDFJS_WORKER; + }catch(e){ + temWorker=false; + try{ window.pdfjsLib.GlobalWorkerOptions.workerSrc=PDFJS_WORKER; }catch(e2){} + } + res(window.pdfjsLib||null); + }; + sc.onerror=()=>res(null); + document.head.appendChild(sc); + }); + return pdfLibP; +} + +// DPI real de cada imagem embutida: acompanha a matriz de transformação e compara +// o tamanho em que a imagem foi colocada com quantos pixels ela tem. +function dpiDasImagens(page, ops){ + const mul=(a,b)=>[a[0]*b[0]+a[2]*b[1], a[1]*b[0]+a[3]*b[1], + a[0]*b[2]+a[2]*b[3], a[1]*b[2]+a[3]*b[3], + a[0]*b[4]+a[2]*b[5]+a[4], a[1]*b[4]+a[3]*b[5]+a[5]]; + const O=window.pdfjsLib.OPS; + let ctm=[1,0,0,1,0,0]; const pilha=[]; const dpis=[]; + for(let i=0;it+x.area,0) || 1; + const abaixoPiso=lista.filter(x=>x.dpix.dpit+x.dpi*x.area,0)/total; + return { + pior: Math.round(Math.min(...lista.map(x=>x.dpi))), + ponderado: Math.round(pond), + fracBaixa: abaixoPiso.reduce((t,x)=>t+x.area,0)/total, + fracAviso: abaixoAviso.reduce((t,x)=>t+x.area,0)/total, + quantasBaixas: abaixoPiso.length, + quantasAviso: abaixoAviso.length, + total: lista.length + }; +} + +// Rasteriza a primeira página a 1 mm por pixel e devolve a tela para a análise +function rasterizarPdf(file, larguraCm, alturaCm){ + let motivo=null; + const trabalho=carregarPdfJs().then(lib=>{ + if(!lib){ motivo='não foi possível carregar o leitor de PDF'; return null; } + // sem worker tudo roda na thread principal · grade mais grossa para caber + const folga = temWorker===false ? 2.2 : 1; + return file.arrayBuffer().then(buf=>lib.getDocument({ + data:buf, disableFontFace:true, isEvalSupported:false, useSystemFonts:false, + verbosity:0 // sem worker é fallback, não erro + }).promise).then(doc=> + doc.getPage(1).then(page=>{ + const vp1=page.getViewport({scale:1}); + const passo=passoDe(larguraCm, alturaCm||larguraCm)*folga; + const alvo=Math.max(8, Math.round(larguraCm/passo)); + const esc=alvo/vp1.width; + const vp=page.getViewport({scale:esc}); + const cv=document.createElement('canvas'); + cv.width=Math.max(8,Math.round(vp.width)); cv.height=Math.max(8,Math.round(vp.height)); + const ctx=cv.getContext('2d',{willReadFrequently:true}); + return page.render({canvasContext:ctx, viewport:vp, background:'rgba(0,0,0,0)'}) + .promise.then(()=>page.getOperatorList()).then(ops=>{ + let dpis=[]; + try{ dpis=dpiDasImagens(page,ops); }catch(e){} + const r=resumoDpi(dpis); + return {tela:cv, dpi: r? r.ponderado : null, res:r}; + }); + }) + ); + }).catch(e=>{ motivo=(e&&e.message)? e.message : 'erro ao abrir o PDF'; return null; }); + // sem worker o processo é lento: damos mais tempo antes de desistir + const espera = temWorker===false ? 90000 : 30000; + const tarde=new Promise(r=>setTimeout(()=>{ motivo='demorou demais neste navegador'; r('tempo'); }, espera)); + return Promise.race([trabalho, tarde]).then(r=>{ + const saida = r==='tempo'? null : r; + if(!saida) return {erro: motivo||'não deu para conferir', semWorker: temWorker===false}; + return saida; + }); +} + +function medirFolha(file){ + return new Promise(res=>{ + const nome=(file.name||'').toLowerCase(); + if(RENDERIZA.test(nome)){ + carregarImagem(file).then(img=>{ + if(!img || !img.width) return res(null); + const L=larguraFilme(); + // Uma imagem só é folha montada se tiver pixel para ocupar a largura do + // filme. Sem esta conferência, qualquer arte pequena vira "folha" e leva + // o preço por metro sem nunca ter sido montada. + res({larg:L, alt:+(img.height/img.width*L).toFixed(1), fonte:'proporção da imagem', + dpiFolha:Math.round(img.width/(L/2.54)), px:img.width}); + }); + return; + } + if(/\.pdf$/.test(nome)){ + const pedaco=(inicio,fim)=>new Promise(r=>{ + const fr=new FileReader(); + fr.onload=()=>r(new TextDecoder('latin1').decode(new Uint8Array(fr.result))); + fr.onerror=()=>r(''); + fr.readAsArrayBuffer(file.slice(inicio,fim)); + }); + const M=4*1048576; + Promise.all([pedaco(0,M), pedaco(Math.max(0,file.size-M), file.size)]).then(([a,b])=>{ + const txt=a+b; + const re=/\/MediaBox\s*\[\s*(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)\s+(-?[\d.]+)/; + const m=txt.match(re); + if(!m) return res(null); + // UserUnit escala a página inteira · sem ele, 1 unidade = 1/72 pol + const uu=txt.match(/\/UserUnit\s+([\d.]+)/); + const esc=uu? Math.max(1,Math.min(75000,+uu[1])) : 1; + const ptW=Math.abs(+m[3]-+m[1]), ptH=Math.abs(+m[4]-+m[2]); + if(!(ptW>0&&ptH>0)) return res(null); + const w=ptW*PT_CM*esc, h=ptH*PT_CM*esc; + // acima de 14.400 unidades sem UserUnit o arquivo está fora da especificação + const foraDoPadrao = !uu && (ptW>PDF_MAX_PT || ptH>PDF_MAX_PT); + res({larg:+w.toFixed(1), alt:+h.toFixed(1), + fonte:'página do PDF'+(uu? ' · UserUnit '+esc : ''), foraDoPadrao}); + }); + return; + } + res(null); + }); +} + +function pintaFolha(){ + const L=larguraFilme(); + pintaTipoEnvio(); // trava o seletor enquanto houver folha + if(!folhas.length){ $('lista').innerHTML=''; agendaAvaliacao(); previaAoVivo(); return; } + + $('lista').innerHTML = folhas.map((x,i)=>{ + const lido=!!x.med, larga=lido && x.med.larg>L+0.5, suspeito=lido && x.med.foraDoPadrao; + const sub=(x.m||0)*(x.rep||1); + let medida=''; + if(lido){ + const a=x.an; + const conferido = a + ? '
Conferido de verdade'+ + ''+a.artes+' peça'+(a.artes===1?'':'s')+' · '+a.aproveitamento+ + '% da folha virou arte · '+ + (a.residuos? a.residuos+' resíduo'+(a.residuos>1?'s':'') : 'sem resíduo')+' · '+ + (a.fundoChapado? 'fundo chapado' : 'fundo transparente')+''+ + 'resolução '+a.dpi+' DPI · '+ + (a.fonteDpi==='imagens'? 'medida imagem por imagem dentro do PDF' + : a.fonteDpi==='vetor'? 'arte vetorial, sem imagem embutida' + : 'lida do arquivo')+ + ' · grade de '+n1(a.grade)+' mm
' + : ''; + medida='
'+ + ''+n1(x.med.larg)+' × '+n1(x.med.alt)+' cm'+ + ''+x.med.fonte+' · '+fmtM(x.m)+' m de folha'+ + conferido+ + (suspeito? 'Este PDF passa do tamanho que o formato comporta. '+ + 'A especificação para em 508 cm e o seu tem '+n1(x.med.alt)+' cm. '+ + 'Ele abre certo só em quem o gerou — aqui vai precisar de tratamento. '+ + 'Manda em PNG ou TIFF, ou divide em partes de até 5 m, que sai na hora.':'')+ + (larga? 'Esta folha não roda neste filme. Ela tem '+n1(x.med.larg)+ + ' cm de largura e o filme aqui tem '+n1(L)+' cm. Não dá para reduzir: '+ + 'encolher a folha encolheria cada arte junto.'+ + (x.med.larg<=57.5 + ? ' Ela cabe no DTF têxtil de 57 cm — troque de produto ali em cima.' + : ' Remonte a folha em '+n1(L)+' cm e suba de novo.')+'':'')+ + '
'; + }else{ + const teto=TABELA[modo], piso=pisoEscada(); + const dif=(x.m>0)? (teto-piso)*cobrar(x.m*(x.rep||1)) : 0; + medida='
'+ + ''+escapeHTML(extDe(x.f.name))+' não abre no navegador'+ + 'Sem conseguir conferir o arquivo, não temos como dar nota — e sem nota '+ + 'o metro sai pela tabela, '+rs(teto)+'. Alguém vai abrir esse arquivo na mão '+ + 'antes de imprimir.'+ + '
Exportando a mesma folha em PNG ou PDF, a conferência é '+ + 'automática e o metro pode cair até '+rs(piso)+''+ + (dif>0.5? ' — '+rs(dif)+' a menos só nesta folha' : '')+'.
'+ + 'Informe o comprimento para seguir:'+ + ''+ + '
'; + } + const barra = x.pct!=null + ? '
'+ + ''+(x.pct<50?'lendo o arquivo…': + x.pct<70?'medindo a folha…':'conferindo a arte…')+'' + : x.semAnalise + ? '
Não conseguimos conferir esta folha. '+x.semAnalise+ + '. Seguimos com o preço de tabela — se quiser a nota e o desconto, exporte a mesma '+ + 'folha em PNG, que a conferência é bem mais leve.
' + : ''; + return '
'+ + (folhas.length>1? (i+1)+'. ':'')+escapeHTML(x.f.name)+''+ + ''+fmt(x.f.size)+''+ + '
'+ + barra+medida+ + (x.m>0 ? + '
'+ + '
'+ + '
'+ + '
'+fmtM(sub)+' m'+ + (x.rep>1? ' · '+x.rep+' × '+fmtM(x.m)+' m':'')+'
'+ + '
' : '')+ + '
'; + }).join(''); + + // total do pedido, quando há mais de uma folha + const tot=folhaTotalM(), partes=Math.ceil(tot/20); + if(folhas.length>1 && tot>0){ + $('lista').innerHTML += '
'+ + ''+folhas.length+' folhas neste pedido'+ + ''+fmtM(tot)+' m no total'+ + (partes>1? 'vai em '+partes+' partes de até 20 m':'')+'
'; + }else if(partes>1 && tot>0){ + $('lista').innerHTML += '
folha longa'+ + ''+fmtM(tot)+' mvai em '+partes+' partes de até 20 m
'; + } + + $('lista').querySelectorAll('[data-rmf]').forEach(b=>b.addEventListener('click',()=>{ + folhas.splice(+b.dataset.rmf,1); pintaFolha(); + })); + $('lista').querySelectorAll('[data-repf]').forEach(el=>el.addEventListener('change',e=>{ + folhas[+el.dataset.repf].rep=Math.max(1,Math.min(200,+e.target.value||1)); pintaFolha(); + })); + $('lista').querySelectorAll('[data-comp]').forEach(el=>el.addEventListener('change',e=>{ + folhas[+el.dataset.comp].m=Math.max(0,Math.min(60,+e.target.value||0)); pintaFolha(); + })); + agendaAvaliacao(); previaAoVivo(); +} + +// sugestão de largura +const cabemNaLinha=w=>Math.max(1,Math.floor((larguraFilme()+GAP_CM)/(w+GAP_CM))); +const larguraPara=n=>(larguraFilme()-(n-1)*GAP_CM)/n; +// Só sugere se a folha realmente encolher. Caber mais um por fileira não adianta +// nada se a quantidade pedida continuar ocupando o mesmo número de fileiras. +function sugestao(a){ + const w=a.cm, q=Math.max(1,a.q||1), prop=propDe(a); + const n=cabemNaLinha(w), alvo=Math.floor(larguraPara(n+1)*10)/10; + if(!(alvo>0) || alvo>=w) return null; + if((w-alvo)/w>0.12) return null; // não corta mais que 12% da peça + const fa=Math.ceil(q/n), fd=Math.ceil(q/(n+1)); + if(fd>=fa) return null; // mesmo número de fileiras: não muda nada + const economiaCm=fa*w*prop - fd*alvo*prop; + if(economiaCm<0.5) return null; // menos de meio centímetro não é notícia + return {alvo, de:n, para:n+1, fileiras:fa-fd, cm:economiaCm}; +} +// A cota em escala: o cliente ve a peca dentro da largura do filme, quantas cabem +// lado a lado e quanto sobra. Numero em texto ninguem le; retangulo todo mundo entende. +function vizPeca(a){ + const BASE=300, L=larguraFilme(); + const e0=BASE/L, alt0=a.cm*propDe(a)*e0; + const k = alt0>132 ? 132/alt0 : 1; // tira nunca fica alta demais + const e = e0*k; + const n=cabemNaLinha(a.cm), pw=a.cm*e, ph=a.cm*propDe(a)*e, gp=GAP_CM*e; + const sobra=L-(n*a.cm+(n-1)*GAP_CM); + const ext=escapeHTML(a.f.name.split('.').pop().toUpperCase()); + let pecas=''; + for(let i=0;i'+(a.src?'':''+ext+'')+''; + const Wt=(L*e+6).toFixed(1); // +6 = borda e respiro da tira + return '
'+ + '
'+ + '
'+n1(a.cm*propDe(a))+' cm
'+ + '
'+ + '
'+n1(L)+' cm de filme
'+ + '
'+pecas+ + (sobra>0.25?'
sobra '+n1(sobra)+' cm
':'')+ + '
'+ + '
'+ + '
'+ + '
'+n1(a.cm)+' cm de largura · '+n+ + (n>1?' peças':' peça')+' por fileira'+ + (sobra>0.25?' · sobram '+n1(sobra)+' cm':' · largura toda aproveitada')+'
'+ + '
'; +} +const n1=v=>v.toFixed(1).replace('.',','); + +function pintaArtes(){ + $('lista').innerHTML=artes.map((a,i)=>{ + let barra='', dica='', calc='informe a largura para ver a qualidade'; + if(a.cm>0){ + const dpi=Math.round(a.px/(a.cm/2.54)), n=Math.max(6,Math.min(100,Math.round(dpi/300*100))); + barra='
'; + calc=''+n+'% · '+dpi+' DPI'+ + a.q+' × '+n1(a.cm)+' × '+n1(a.cm*propDe(a))+' cm'; + const sug=sugestao(a); + dica = sug + ? '
Reduzindo para '+n1(sug.alvo)+' cm você economiza '+ + n1(sug.cm)+' cm de folhaPassam de '+sug.de+' para '+sug.para+ + ' por fileira, e as suas '+a.q+' peças ocupam '+sug.fileiras+ + ' fileira'+(sug.fileiras>1?'s':'')+' a menos.
' + : ''; + dica = vizPeca(a) + dica; + } + return '
'+escapeHTML(a.f.name)+''+ + ''+a.px+' px'+ + '
'+ + '
'+ + '
'+ + '
'+ + (TAMANHOS[larguraFilme()]||[]).map(t=>'').join('')+ + ''+ + ''+ + ''+ + '
'+barra+'
'+calc+'
'+dica+'
'; + }).join(''); + $('lista').querySelectorAll('[data-rm]').forEach(b=>b.addEventListener('click',()=>{artes.splice(+b.dataset.rm,1);pintaArtes();})); + $('lista').querySelectorAll('[data-cm]').forEach(i=>i.addEventListener('change',()=>{artes[+i.dataset.cm].cm=+i.value||0;pintaArtes();})); + $('lista').querySelectorAll('[data-q]').forEach(i=>i.addEventListener('change',()=>{artes[+i.dataset.q].q=Math.max(1,Math.floor(+i.value||1));pintaArtes();})); + $('lista').querySelectorAll('[data-cm], [data-q]').forEach(i=>i.addEventListener('input',()=>{ + const largura=i.hasAttribute('data-cm'); + const a=artes[+(largura?i.dataset.cm:i.dataset.q)]; + a[largura?'cm':'q']=largura?Math.max(0,+i.value||0):Math.max(1,Math.floor(+i.value||1)); + previaAoVivo(); + })); + $('lista').querySelectorAll('[data-usar]').forEach(b=>b.addEventListener('click',()=>{ + artes[+b.dataset.usar].cm=+b.dataset.cm; pintaArtes(); + })); + $('lista').querySelectorAll('[data-tam]').forEach(b=>b.addEventListener('click',()=>{ + artes[+b.dataset.tam].cm=+b.dataset.t; pintaArtes(); + })); + $('lista').querySelectorAll('[data-giro]').forEach(b=>b.addEventListener('click',()=>{ + const a=artes[+b.dataset.giro]; a.giro=a.giro?0:90; pintaArtes(); + })); + $('lista').querySelectorAll('[data-esp]').forEach(b=>b.addEventListener('click',()=>{ + const a=artes[+b.dataset.esp]; a.esp=!a.esp; pintaArtes(); + })); + resumoArtes(); + previaAoVivo(); +} +function resumoArtes(){ + const r=$('rA'), prontas=artes.filter(a=>a.cm>0); + if(!prontas.length){ r.style.display='none'; return; } + const m=montagemCm/100; // a mesma folha que aparece ao lado + const notas=prontas.map(a=>Math.max(6,Math.min(100,Math.round((a.px/(a.cm/2.54))/300*100)))); + const media=Math.round(notas.reduce((s,n)=>s+n,0)/notas.length), pior=Math.min(...notas); + const falta=artes.length-prontas.length; + r.style.display='block'; r.className='resumoA on'; + r.innerHTML='
artes com tamanho'+prontas.length+' de '+artes.length+'
'+ + '
qualidade média'+media+'%
'+ + (piora pior arte'+pior+'%':'')+ + (m>0?'
folha montada'+fmtM(m)+' m
'+ + (cobrar(m)>m+0.001?'
cobrança'+fmtM(cobrar(m))+ + ' m'+(cobrar(m)===MINIMO_M?' · mínimo':'')+'
':''):'')+ + (falta?'
faltam informar'+falta+'
':''); +} + diff --git a/local/static/site-quality.js b/local/static/site-quality.js new file mode 100644 index 0000000..208be0a --- /dev/null +++ b/local/static/site-quality.js @@ -0,0 +1,186 @@ +/* Site DTF — The grade shown to the customer, and the reservations attached to it. + Extracted verbatim from the single inline script in dtf-site.html. + Loaded as classic scripts in the order listed there: they share one global + scope and run top to bottom, exactly as the original did. */ +// ── enviar +// Avalia sozinho conforme o cliente sobe e preenche. Devolve o que ainda falta, +// para a fila dizer em texto em vez de travar num botão. +function avaliar(){ + if(ehFolha()){ + if(!folhas.length) return {pronto:false, falta:'Arraste sua folha montada para começar.'}; + const larga=folhas.find(x=>x.med && x.med.larg > larguraFilme()+0.5); + if(larga) return {pronto:false, falta:'A folha '+larga.f.name+' tem '+n1(larga.med.larg)+ + ' cm e não cabe no filme de '+n1(larguraFilme())+' cm.'}; + const semMedida=folhas.filter(x=>!(x.m>0)).length; + if(semMedida) return {pronto:false, falta:'Informe o comprimento de '+semMedida+ + ' folha'+(semMedida>1?'s':'')+'.'}; + metros=folhaTotalM(); + reencaixado=false; + const ans=folhas.map(x=>x.an).filter(Boolean); + if(!ans.length){ + // sem análise possível (PDF, CDR, PSD): não inventa nota + const exts=[...new Set(folhas.map(f=>extDe(f.f.name)))].join(', '); + mostraNota([ + ['av','Sem nota · '+exts+' não abre no navegador', + 'não temos como conferir a arte antes de imprimir'], + ['er','Metro pela tabela · '+rs(TABELA[modo]), + 'exportando em PNG ou PDF, cai até '+rs(pisoEscada())], + ['fix','Alguém abre seu arquivo','a conferência é feita na mão, na sala'] + ], 0, folhas.length>1? folhas.length+' folhas' : folhas[0].f.name, + larguraFilme()+' cm · '+fmtM(metros)+' m · preço de tabela'); + return {pronto:true}; + } + const dpi=Math.min(...ans.map(a=>a.dpi)); + const n=Math.max(6,Math.min(100,Math.round(dpi/DPI_AVISA*100))); + const soma=(k)=>ans.reduce((t,a)=>t+a[k],0); + const itens=[ + ans.every(a=>a.vetor) + ? ['ok','Arte vetorial','sem imagem embutida · imprime em qualquer tamanho'] + : [dpi>=DPI_AVISA?'ok':dpi>=DPI_RECUSA?'av':'er','Resolução '+dpi+' DPI', + ans.some(a=>a.fonteDpi==='imagens') + ? 'média por área das imagens dentro do PDF · pior em '+ + Math.min(...ans.map(a=>a.res? a.res.pior : a.dpi))+' DPI' + : 'resolução do arquivo · arte ampliada antes de exportar não aparece aqui'], + ['ok', soma('artes')+' peças na folha', + Math.round(soma('aproveitamento')/ans.length)+'% da folha virou arte'], + soma('residuos') ? ['er','Resíduo de recorte', + soma('residuos')+' ponto'+(soma('residuos')>1?'s':'')+' abaixo de 2 mm · a impressora imprime'] + : ['ok','Sem resíduo de recorte','nada solto na folha'], + soma('encostadas') ? ['fix', soma('encostadas')+' peças a menos de 5 mm', + 'se forem artes diferentes, separamos com 5 mm sem custo'] + : ['ok','Espaço entre peças','nenhuma abaixo de 6 mm'], + ans.some(a=>a.fundoChapado) ? ['er','Fundo chapado','a folha está sem transparência'] + : ['ok','Fundo transparente','canal alfa conferido'], + null + ].filter(Boolean); + mostraNota(itens, n, folhas.length>1? folhas.length+' folhas' : folhas[0].f.name, + larguraFilme()+' cm · '+fmtM(metros)+' m'); + return {pronto:true}; + } + if(!artes.length) return {pronto:false, falta:'Arraste suas artes para começar.'}; + const sem=artes.filter(a=>!a.cm).length; + if(sem) return {pronto:false, falta:'Informe a largura de '+sem+' arte'+(sem>1?'s':'')+ + ' para ver a nota e o preço.'}; + // metragem = altura da folha depois de montada, com os 5 mm entre peças + metros=+(montagemCm/100).toFixed(4); + if(!(metros>0)) return {pronto:false, falta:'Montando a folha…'}; + const notas=artes.map(a=>Math.max(6,Math.min(100,Math.round((a.px/(a.cm/2.54))/300*100)))); + const media=Math.round(notas.reduce((s,n)=>s+n,0)/notas.length), pior=Math.min(...notas); + mostraNota([ + ['ok','Montagem por nossa conta','5 mm garantidos'], + [cls(pior),'Pior arte em '+pior+'%',pior>=85?'todas em boa resolução':'reenviar maior sai mais barato'] + ],media,artes.length+' arte'+(artes.length>1?'s':''), + 'folha montada por nós · '+fmtM(metros)+' m'); + return {pronto:true}; +} + +// redesenha 220 ms depois da última mexida, para não avaliar a cada tecla +let tAval=null; +function agendaAvaliacao(){ + clearTimeout(tAval); + tAval=setTimeout(()=>{ + const r=avaliar(); + const el=$('aoVivo'); + if(!el) return; + if(r.pronto){ + el.className='aoVivo pronto'; + el.innerHTML='Nota e preço calculados. '+ + 'Confira abaixo e feche o pedido.'; + }else{ + el.className='aoVivo'; + el.innerHTML=''+escapeHTML(r.falta)+''; + limpaPaineis(); // o carrinho só some se o pedido estiver vazio + } + },220); +} +function mostraNota(itens,n,titulo,sub){ + nota=n; + $('qual').classList.add('on'); + $('qNome').textContent=titulo; $('qSub').textContent=sub; + const semNota = n===0; + $('pct').innerHTML = semNota? '—/100' : n+'/100'; + const c = semNota? 'var(--fraco)' : n>=85?'var(--verde)':n>=50?'var(--laranja)':'var(--vermelho)'; + $('pct').style.color=c; + $('qbi').style.width=(semNota?0:n)+'%'; $('qbi').style.background=c; + $('qitens').innerHTML=itens.map(i=> + '
'+ + (i[0]==='ok'?'✓':i[0]==='av'?'!':i[0]==='fix'?'↻':'×')+''+ + '
'+escapeHTML(i[1])+''+escapeHTML(i[2])+'
').join(''); + $('qmsg').style.display='none'; + ressalvaResolucao(); + carrinho(); +} + +// A resolução do arquivo não é a das artes dentro dele: um PDF em 300 DPI +// pode ter uma imagem inserida em 180, e é essa que sai serrilhada. +function ressalvaResolucao(){ + const box=$('ciente'), chk=$('cienteOk'); + let baixas=[], geral=300, recusadas=[]; + if(ehFolha()){ + // Uma régua só: o número que precifica é o número que bloqueia. + // Recusa quando a média por área fica abaixo de 96 DPI — a folha inteira não + // vale o filme. Se a média passa mas há peças ruins, é ressalva com aceite. + folhas.forEach((x,k)=>{ + if(!x.an) return; + const r=x.an.res, cm=x.med? x.med.larg : larguraFilme(); + const nome=(folhas.length>1? (k+1)+'. ':'')+x.f.name; + const extra = r ? {pior:r.pior, de:r.total, + quantas:r.quantasBaixas, parte:Math.round(r.fracBaixa*100), + quantasAviso:r.quantasAviso, parteAviso:Math.round(r.fracAviso*100)} : {}; + if(x.an.dpix.an) ? Math.max(...folhas.filter(x=>x.an).map(x=>x.an.dpi)) : 300; + }else{ + const comTamanho=artes.filter(a=>a.cm>0); + recusadas=comTamanho.filter(a=>dpiDe(a)({nome:a.f.name, dpi:Math.round(dpiDe(a)), cm:a.cm})); + baixas=comTamanho.filter(a=>dpiDe(a)>=DPI_RECUSA && dpiDe(a)({nome:a.f.name, dpi:Math.round(dpiDe(a)), cm:a.cm})); + const todas=comTamanho.map(a=>dpiDe(a)); + geral = todas.length? Math.round(Math.max(...todas)) : 300; + } + // Abaixo de 96 DPI a sala não imprime. Não é desconto, é recusa — + // e não existe aceite que destrave. + if(recusadas.length){ + $('cienteTxt').innerHTML= + '

A média da folha ficou abaixo de '+DPI_RECUSA+' DPI. Nesse ponto a peça '+ + 'sai serrilhada a ponto de não valer o filme. Reenvie maior ou reduza a largura.

'+ + '
'+recusadas.map(b=>'
'+escapeHTML(b.nome)+' — '+ + (b.quantas + ? 'média de '+b.dpi+' DPI · pior imagem em '+b.pior+' DPI · '+b.quantas+ + ' de '+b.de+' imagens abaixo de '+DPI_RECUSA+' DPI, cobrindo '+b.parte+'% da arte' + : b.dpi+' DPI em '+n1(b.cm)+' cm · precisa de '+ + Math.ceil(DPI_AVISA*b.cm/2.54)+' px de largura')+'
') + .join('')+'
'; + $('cienteTit').textContent='Resolução abaixo do que imprimimos'; + box.className='ciente on recusa'; chk.checked=false; $('qOk').disabled=true; + return; + } + $('cienteTit').textContent='Ressalva de resolução'; + box.className='ciente'; + if(!baixas.length){ + box.classList.remove('on'); chk.checked=false; $('qOk').disabled=false; return; + } + const menor=baixas.reduce((p,b)=>b.dpis+b.dpi,0)/baixas.length); + $('cienteTxt').innerHTML= + '

Parte da arte está abaixo de '+DPI_AVISA+' DPI. Essas peças podem sair '+ + 'com traço serrilhado, e isso não tem conserto depois de impresso. '+ + 'O resto da folha sai normal.

'+ + '
'+baixas.map(b=>'
'+escapeHTML(b.nome)+' — '+ + (b.quantasAviso + ? 'média de '+b.dpi+' DPI · '+b.quantasAviso+' de '+b.de+' imagens abaixo de '+ + DPI_AVISA+' DPI, cobrindo '+b.parteAviso+'% da arte'+ + (b.quantas? ' · dessas, '+b.quantas+' abaixo de '+DPI_RECUSA+' DPI ('+ + b.parte+'% da arte)' : '') + : b.dpi+' DPI'+(b.cm? ' em '+n1(b.cm)+' cm':''))+ + '
').join('')+'
'; + box.className='ciente on'; chk.checked=false; $('qOk').disabled=true; +} +$('cienteOk').addEventListener('change',e=>{ + if($('ciente').classList.contains('recusa')) return; // recusa não se aceita + $('qOk').disabled=!e.target.checked; +}); + diff --git a/local/static/site-sheet.js b/local/static/site-sheet.js new file mode 100644 index 0000000..91a0186 --- /dev/null +++ b/local/static/site-sheet.js @@ -0,0 +1,107 @@ +/* Site DTF — Reading a finished sheet from its alpha channel: pieces, gaps, waste. + Extracted verbatim from the single inline script in dtf-site.html. + Loaded as classic scripts in the order listed there: they share one global + scope and run top to bottom, exactly as the original did. */ +// ── Análise real da folha: lê o canal alfa, separa cada ilha de arte e mede. +// Grade de 1 mm. Só roda em arquivo que o navegador abre; PDF precisa do servidor. +const MM=0.1; // grade fina: 1 mm +const CELULAS_MAX=1200000; // teto de custo da análise +// Folha grande vira grade mais grossa. Sem isso, 57 x 480 cm dá 2,7 milhões de +// células e o navegador congela antes de terminar. +function passoDe(larguraCm, alturaCm){ + const fino=(larguraCm/MM)*(alturaCm/MM); + return fino<=CELULAS_MAX ? MM : MM*Math.sqrt(fino/CELULAS_MAX); +} + +// Dilata a máscara em r células · usada para agrupar por distância real, não por caixa +function inchar(bits,cw,ch,r){ + const a=new Uint8Array(cw*ch), b=new Uint8Array(cw*ch); + for(let y=0;y=0) continue; + const id=n++; let ini=0,fim=0; fila[fim++]=p0; marca[p0]=id; + while(ini=cw||ny>=ch) continue; + const q=ny*cw+nx; + if(bits[q]&&marca[q]<0){ marca[q]=id; fila[fim++]=q; } + } + } + } + return {marca,n}; +} + +// ── O que a folha permite medir, e só isso. +// Peça = pixels a menos de 2 mm entre si · letras de uma palavra ficam juntas. +// Encostadas = peças que só se unem quando a folga chega a 6 mm. +const R_PECA=1, R_PERTO=2; // peça: 2 mm · aviso de encostadas: 4 mm +function analisarFolha(img, larguraCm){ + const alturaCmEst=larguraCm*img.height/img.width; + const passo=passoDe(larguraCm, alturaCmEst); + const cw=Math.max(8,Math.round(larguraCm/passo)); + const ch=Math.max(8,Math.round(cw*img.height/img.width)); + const cv=document.createElement('canvas'); cv.width=cw; cv.height=ch; + const ctx=cv.getContext('2d',{willReadFrequently:true}); + ctx.clearRect(0,0,cw,ch); ctx.drawImage(img,0,0,cw,ch); + const d=ctx.getImageData(0,0,cw,ch).data; + + const bits=new Uint8Array(cw*ch); + let opacos=0; + for(let i=0,j=3;i10){ bits[i]=1; opacos++; } + + // peças: agrupa pela distância real entre pixels + const gp=grupos(inchar(bits,cw,ch,R_PECA), cw, ch); + const caixas=[]; + for(let p=0;pc.x1)c.x1=x; if(yc.y1)c.y1=y; + c.area++; + } + const pecasTodas=caixas.filter(Boolean); + const limRes=Math.max(1, Math.round(0.2/passo)); // 2 mm na grade em uso + const residuos=pecasTodas.filter(o=>(o.x1-o.x0)!((o.x1-o.x0){ if(set.size>1) encostadas+=set.size; }); + + return { + dpi: Math.round(img.width/(larguraCm/2.54)), + artes: artes.length, + residuos: residuos.length, + encostadas, + fundoChapado: opacos/(cw*ch) > 0.985, + aproveitamento: Math.max(1,Math.min(100,Math.round(opacos/(cw*ch)*100))), + alturaCm: ch*passo, + grade: +(passo*10).toFixed(1) // mm por célula analisada + }; +} + diff --git a/local/static/site-upload.js b/local/static/site-upload.js new file mode 100644 index 0000000..353c3c2 --- /dev/null +++ b/local/static/site-upload.js @@ -0,0 +1,112 @@ +/* Site DTF — Accepting files: the drop zone, the accept rules, first measurement. + Extracted verbatim from the single inline script in dtf-site.html. + Loaded as classic scripts in the order listed there: they share one global + scope and run top to bottom, exactly as the original did. */ +// ── upload +// Untrusted filenames and recovered cart text must never become HTML. +function escapeHTML(value){ + return String(value).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); +} +function recusa(lista){ + const el=$('recusa'); if(!el) return; + if(!lista.length){ el.style.display='none'; el.innerHTML=''; return; } + const exts=escapeHTML([...new Set(lista.map(f=>extDe(f.name)))].join(', ')); + el.style.display='block'; + el.innerHTML = ehFolha() + ? ''+exts+' não dá para enviar aqui. A folha pronta aceita PNG, JPG, TIFF, '+ + 'PDF, PSD, AI e CDR.' + : ''+exts+' não dá para enviar aqui. Artes avulsas precisam de PNG ou JPG com '+ + 'fundo transparente — é o que a montagem enxerga para encaixar. '+ + 'Se o seu arquivo é CDR, AI ou PSD, exporte em PNG ou use Arquivo por metro.'; +} +const Z=$('zona'), I=$('inp'); +Z.addEventListener('click',()=>I.click()); +Z.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){I.click();e.preventDefault();}}); +['dragover','dragenter'].forEach(ev=>Z.addEventListener(ev,e=>{e.preventDefault();Z.classList.add('sobre')})); +['dragleave','drop'].forEach(ev=>Z.addEventListener(ev,e=>{ + e.preventDefault(); Z.classList.remove('sobre'); + if(ev==='drop') sel([...e.dataTransfer.files]); +})); +I.addEventListener('change',()=>sel([...I.files])); +function sel(fs){ + if(!fs.length) return; + // Pixels cannot tell a loose artwork from a finished sheet, so the customer + // declares it by choosing the product in the selector above. The declaration is + // verified in medirFolha; the file never changes product or price on its own. + const regra = ehFolha() ? ACEITA.folha : ACEITA.avulsa; + const fora = fs.filter(f=>!regra.test(f.name)); + fs = fs.filter(f=>regra.test(f.name)); + recusa(fora); + if(!fs.length) return; + if(ehFolha()){ + const auto=fs.filter(f=>AUTO.test(f.name)).length; + const manual=fs.length-auto; + if(caminho==='auto' && manual){ + caminho='tabela'; + avisoCam('Trocamos o caminho para você. Você marcou conferência automática, '+ + 'mas '+(manual>1? manual+' arquivos vieram':'o arquivo veio')+' em formato que não '+ + 'abrimos. O metro vai pela tabela, '+rs(TABELA[modo])+'.'); + pintaCaminhos(); + }else if(caminho==='tabela' && auto){ + caminho='auto'; + avisoCam('Boa notícia. Você marcou o caminho sem conferência, mas '+ + (auto>1? 'os arquivos vieram':'o arquivo veio')+' em formato que a gente confere. '+ + 'Vamos avaliar e o metro pode cair até '+rs(pisoEscada())+'.'); + pintaCaminhos(); + } + const novas=fs.map(f=>({f, med:null, rep:1, m:0, semAnalise:null})); + folhas=folhas.concat(novas); // soma, não substitui + pintaFolha(); + novas.forEach(x=>{ + x.pct=5; pintaFolha(); + medirFolha(x.f).then(md=>{ + if(md && md.dpiFolha!=null && md.dpiFolha{ + if(img){ x.previewSrc=img.src; try{ x.an=analisarFolha(img, md.larg); if(x.an) x.an.fonteDpi='arquivo'; } + catch(e){} } + x.pct=null; pintaFolha(); + }); + }else if(md && /\.pdf$/i.test(x.f.name)){ + x.pct=70; pintaFolha(); + rasterizarPdf(x.f, md.larg, md.alt).then(r=>{ + if(r && r.erro){ + x.semAnalise = r.erro + (r.semWorker + ? ' · este navegador não deixa o leitor rodar em segundo plano' + : ''); + } + if(r && r.tela){ + try{ + x.an=analisarFolha(r.tela, md.larg); + // vetor puro não tem resolução · nota máxima, e está certo + x.an.dpi = r.dpi==null ? DPI_AVISA : r.dpi; + x.an.vetor = r.dpi==null; + x.an.fonteDpi = r.dpi==null ? 'vetor' : 'imagens'; + x.an.res = r.res || null; + }catch(e){} + } + x.pct=null; pintaFolha(); + }); + }else{ x.pct=null; pintaFolha(); } + }); + }); + return; + } + const novos=fs.map(f=>({f,px:px(f),cm:0,q:1,prop:1,giro:0,esp:false})); + artes=novos.concat(artes); // a mais recente fica no topo da fila + recemChegada=novos[0]; + pintaArtes(); + $('lista').scrollIntoView({behavior:'smooth',block:'nearest'}); + Promise.all(novos.map(medir)).then(pintaArtes); +} +function medir(a){ + return carregarImagem(a.f).then(img=>{ + if(img){ a.prop=img.height/img.width; a.px=img.width; a.src=img.src; } else a.prop=a.prop||1; + return a; + }); +} +// A metragem sai do arquivo, nunca do peso em bytes. +// imagem → proporção da imagem aplicada à largura do filme +// PDF → MediaBox da primeira página, em pontos (1 pt = 1/72 pol) +// TIFF, PSD, AI, CDR → o navegador não abre; o cliente informa o comprimento diff --git a/local/test_pricing.py b/local/test_pricing.py index d01fe5e..8593ba3 100644 --- a/local/test_pricing.py +++ b/local/test_pricing.py @@ -7,7 +7,9 @@ from .pricing import price, TIERS class PricingTests(unittest.TestCase): def test_every_grade_against_actual_site_javascript(self): - source = Path('dtf-site.html').read_text() + # The ladders live with the rest of the Site's behaviour; this test exists + # to prove the server agrees with whatever the customer is shown. + source = Path('local/static/site-config.js').read_text() tiers = source.split('const FAIXAS=')[1].split('\n};',1)[0]+'\n}' calculator = source.split('const cobrar=')[1].split(';',1)[0] js = f'const FAIXAS={tiers};const MINIMO_M=1;const cobrar={calculator};' diff --git a/output/documents/relatorio-semana-1-dtf.docx b/output/documents/relatorio-semana-1-dtf.docx new file mode 100644 index 0000000000000000000000000000000000000000..72a8ab62903dbb5c2c3df12e053871d4d3d8d5c5 GIT binary patch literal 40383 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 literal 0 HcmV?d00001