(()=>{ 'use strict'; if(window.CateriumBranding)return; const NAME_KEY='sunPdfBrandNameV1',LOGO_KEY='sunPdfBrandLogoV1'; const PIXEL='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQIHWP4z8DwHwAFgAI/ScLbtAAAAABJRU5ErkJggg=='; const esc=value=>String(value??'').replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); const read=key=>{try{return localStorage.getItem(key)||''}catch(_){return ''}}; const object=key=>{try{const v=JSON.parse(read(key)||'{}');return v&&typeof v==='object'&&!Array.isArray(v)?v:{}}catch(_){return {}}}; const safeLogo=src=>/^data:image\/(?:png|jpeg|webp);base64,[a-z0-9+/=\s]+$/i.test(String(src||''))&&String(src).length<=2000000?String(src):''; const clean=value=>String(value||'').trim().slice(0,160); function identity(){ const profile=object('sunCompanyProfileV1'); const storedName=read(NAME_KEY); // An explicitly cleared logo must not reappear from an older profile. let hasLogo=false;try{hasLogo=localStorage.getItem(LOGO_KEY)!==null}catch(_){} return { name:clean(storedName||profile.shortName||profile.name)||'Моя компания', logo:safeLogo(hasLogo?read(LOGO_KEY):profile.logo), city:clean(profile.city), contacts:[profile.phone,profile.email,profile.website].map(clean).filter(Boolean).join(' · ') }; } function changed(){window.dispatchEvent(new CustomEvent('caterium:document-brand-changed'));} function saveName(value){localStorage.setItem(NAME_KEY,clean(value));changed();} function clearLogo(){localStorage.setItem(LOGO_KEY,'');changed();} // Remove empty PNG margins, keeping a small transparent breathing space. // Used both on upload and on older logos already saved in offer snapshots. function prepareLogoImage(image){ const width=image.naturalWidth||image.width,height=image.naturalHeight||image.height; const scale=Math.min(1,960/Math.max(width,height)); const source=document.createElement('canvas');source.width=Math.max(1,Math.round(width*scale));source.height=Math.max(1,Math.round(height*scale)); const ctx=source.getContext('2d',{willReadFrequently:true});ctx.drawImage(image,0,0,source.width,source.height); const pixels=ctx.getImageData(0,0,source.width,source.height).data; let left=source.width,top=source.height,right=-1,bottom=-1; for(let y=0;y8){left=Math.min(left,x);top=Math.min(top,y);right=Math.max(right,x);bottom=Math.max(bottom,y)} if(rightJSON.stringify([window.SunCloudV2?.getSession?.()?.user?.id,window.SunCloudV2?.getWorkspace?.()?.id]); const startedIn=scope(); if(!file||!/^image\/(?:png|jpeg|webp)$/i.test(file.type))throw new Error('Выберите PNG, JPEG или WebP.'); if(file.size>15*1024*1024)throw new Error('Логотип должен быть меньше 15 МБ.'); const src=await new Promise((resolve,reject)=>{const reader=new FileReader();reader.onload=()=>resolve(String(reader.result));reader.onerror=()=>reject(new Error('Не удалось прочитать файл.'));reader.readAsDataURL(file)}); const logo=await new Promise((resolve,reject)=>{const img=new Image();img.onload=()=>resolve(img);img.onerror=()=>reject(new Error('Не удалось открыть изображение.'));img.src=src}); const canvas=prepareLogoImage(logo); const data=canvas.toDataURL('image/png'); if(!safeLogo(data))throw new Error('Изображение слишком сложное. Выберите логотип меньшего размера.'); if(startedIn!==scope())throw new Error('Аккаунт или компания изменились. Загрузите логотип ещё раз.'); localStorage.setItem(LOGO_KEY,data);changed();return data; } const documentName=()=>identity().name; const documentLogo=()=>identity().logo; const documentImage=()=>documentLogo()||PIXEL; const documentContacts=()=>identity().contacts; const documentCity=()=>identity().city; function snapshot(value){const brand=identity();return {...value,brandName:brand.name,logo:brand.logo,brandContacts:brand.contacts,brandCity:brand.city};} let sidebar={name:'Caterium',logo:'caterium-mark-light.svg',special:false}; let request=0,checkedUser='',pendingUser=''; const sidebarScope=()=>JSON.stringify([window.SunCloudV2?.getSession?.()?.user?.id||'',window.SunCloudV2?.getWorkspace?.()?.id||'']); function sidebarHTML(){return `${esc(sidebar.name)}${esc(sidebar.name)}`;} function renderSidebar(){ const node=document.querySelector('body > header .brand');if(!node)return; const html=sidebarHTML();if(node.innerHTML!==html)node.innerHTML=html; node.dataset.cateriumBrand=sidebar.special?'solnce':'caterium'; } function resetSidebar(){request++;checkedUser='';pendingUser='';sidebar={name:'Caterium',logo:'caterium-mark-light.svg',special:false};renderSidebar();} async function refreshSidebar(){ const cloud=window.SunCloudV2,session=cloud?.getSession?.(),uid=session?.user?.id||''; if(!uid){resetSidebar();return;} const ws=cloud.getWorkspace?.()?.id||'',scope=sidebarScope(); if(checkedUser===scope||pendingUser===scope){renderSidebar();return;} resetSidebar();pendingUser=scope;const current=request; try{ // New RPC verifies active membership and the selected company. An older // database keeps its owner-only branding until the additive migration runs. let result=ws?await cloud.getClient()?.rpc('caterium_workspace_sidebar_brand',{p_workspace:ws}):null; if(!ws||['PGRST202','42883'].includes(result?.error?.code))result=await cloud.getClient()?.rpc('caterium_my_sidebar_brand'); if(current!==request||sidebarScope()!==scope)return; if(result?.error||!result?.data)return; if(result.data.variant==='solnce')sidebar={name:'Солнце Кейтеринг',logo:'sun-logo.png',special:true}; checkedUser=scope;renderSidebar(); }catch(_){/* Until identity is confirmed, show the shared product brand. */} finally{if(current===request)pendingUser='';} } function boot(){ const style=document.createElement('style');style.id='caterium-company-branding-style'; style.textContent='#sunPdfBrandCard .sun-document-brand-setting{display:grid;grid-template-columns:minmax(180px,300px) minmax(0,1fr);gap:24px;align-items:center}#sunPdfBrandPreview{height:140px;display:flex;align-items:center;justify-content:center;padding:16px;border-radius:12px;background:repeating-conic-gradient(#edf0eb 0% 25%,#fff 0% 50%) 0/20px 20px;border:1px solid #dde2d9;color:#475349;font-size:13px}#sunPdfBrandPreview img{width:auto!important;height:auto!important;max-width:100%!important;max-height:108px!important;object-fit:contain;border:0!important;border-radius:0!important}@media(max-width:640px){#sunPdfBrandCard .sun-document-brand-setting{grid-template-columns:1fr}}body > header .brand .caterium-product-mark{background:#fff7e6;border-radius:14px;padding:6px;filter:none!important}';document.head.appendChild(style); renderSidebar();refreshSidebar(); } window.CateriumBranding=Object.freeze({identity,documentName,documentLogo,documentImage,documentContacts,documentCity, nameHTML:()=>esc(documentName()),logoHTML:()=>esc(documentImage()),contactsHTML:()=>esc(documentContacts()),cityHTML:()=>esc(documentCity()), escapeHTML:esc,snapshot,saveName,saveLogo,clearLogo,prepareLogoImage,sidebarHTML,renderSidebar,refreshSidebar,resetSidebar}); for(const event of ['sun:cloud-state-applied','sun:cloud-permissions-changed','suncloudsync'])window.addEventListener(event,()=>{refreshSidebar();changed()}); window.addEventListener('sun:cloud-tenant-changing',resetSidebar); window.addEventListener('storage',event=>{if([NAME_KEY,LOGO_KEY,'sunCompanyProfileV1'].includes(event.key))changed();if(/^sb-.*-auth-token$/.test(event.key||''))resetSidebar()}); if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',boot,{once:true});else boot(); })();