https://amitai-brand-center.netlify.app/assets/brand-center-proposal-brand.js?v=20260928-languages-2
1// Visual identity only: product data, commercial copy and calculations stay intact. 2const PROPOSAL_BRANDS = { 3 amitai: {name:'AMITAI',logo:'/assets/proposals/image1.png',colorName:'Azul'}, 4 'integri-line': {name:'Integri-Line',logo:'/assets/proposals/integri-line-logo.png',colorName:'Morado'}, 5}; 6const proposalArtworkCache = new Map(); 7function fitProposalLogosForPdf(clonedDocument) { 8 // html2canvas does not implement object-fit. Size the clone's image itself 9 // to its intrinsic ratio, retaining the logo box and alignment in the editor. 10 clonedDocument.querySelectorAll('.paper [data-proposal-brand-logo]').forEach(image=>{ 11 const style=clonedDocument.defaultView.getComputedStyle(image); 12 const box={width:parseFloat(style.width),height:parseFloat(style.height),left:parseFloat(style.left),top:parseFloat(style.top)}; 13 if(!image.naturalWidth||!image.naturalHeight||!Object.values(box).every(Number.isFinite))throw Error('No se pudo preparar el logo para el PDF.'); 14 const scale=Math.min(box.width/image.naturalWidth,box.height/image.naturalHeight); 15 const width=image.naturalWidth*scale,height=image.naturalHeight*scale; 16 const alignLeft=clonedDocument.documentElement.dataset.proposalBrand==='integri-line'; 17 Object.assign(image.style,{width:width+'px',height:height+'px',left:(box.left+(alignLeft?0:(box.width-width)/2))+'px',top:(box.top+(box.height-height)/2)+'px'}); 18 }); 19} 20function proposalBrandArtwork(source) { 21 if (!proposalArtworkCache.has(source)) { 22 const pending = (async () => { 23 const image = new Image(); image.src = source; await image.decode(); 24 const canvas = document.createElement('canvas'); canvas.width=image.naturalWidth; canvas.height=image.naturalHeight; 25 const context=canvas.getContext('2d');
25 context.drawImage(image,0,0); 26 const pixels=context.getImageData(0,0,canvas.width,canvas.height),data=pixels.data; 27 // Theme the existing decorative artwork, retaining its silhouette, shading and alpha. 28 // Solution icons and logos are excluded so their identities remain unchanged. 29 for(let i=0;i<data.length;i+=4){ 30 const r=data[i],g=data[i+1],b=data[i+2],max=Math.max(r,g,b),min=Math.min(r,g,b),chroma=max-min; 31 if(!data[i+3]||chroma<25)continue; 32 const blue=b>r+20&&b>=g-10,yellow=r>b+30&&g>b+25; 33 if(!blue&&!yellow)continue; 34 const target=blue?[91,36,92]:[199,134,198]; 35 const saturation=chroma/255,white=min; 36 for(let c=0;c<3;c++)data[i+c]=Math.min(255,Math.round(white+target[c]*saturation)); 37 } 38 context.putImageData(pixels,0,0);return canvas.toDataURL('image/png'); 39 })(); 40 proposalArtworkCache.set(source,pending); 41 pending.catch(()=>proposalArtworkCache.delete(source)); 42 } 43 return proposalArtworkCache.get(source); 44} 45async function applyProposalBrand(brand='amitai') {
46 const identity=PROPOSAL_BRANDS[brand]||PROPOSAL_BRANDS.amitai; 47 document.documentElement.dataset.proposalBrand=brand; 48 document.querySelectorAll('[data-proposal-brand-logo]').forEach(image=>{image.src=identity.logo;image.alt=identity.name}); 49 document.querySelectorAll('[data-key="theme"] option[value="blue"]').forEach(option=>{option.textContent=window.proposalI18n?window.proposalI18n.text(identity.colorName):identity.colorName}); 50 await Promise.all([...document.querySelectorAll('[data-proposal-brand-artwork]')].map(async image=>{ 51 const source=image.dataset.proposalBrandArtwork; 52 if(brand==='amitai'){image.src=source;return} 53 const themed=await proposalBrandArtwork(source); 54 if(document.documentElement.dataset.proposalBrand===brand&&image.isConnected)image.src=themed; 55 })); 56}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.