Fix Developer Console memory refresh freeze, auto-complete and fully pay orders one minute after scheduled time, persist offer template per client proposal, and add styled Menu SVG icon. Includes PWA cache update and regression coverage.
115 lines
9.2 KiB
JavaScript
115 lines
9.2 KiB
JavaScript
(()=>{
|
||
'use strict';
|
||
const VERSION='17.6.4';
|
||
const RELEASE='20260907-v17-6-4-settings-orders-pdf-menu';
|
||
const critical=img=>img.closest('header,.brand,#sunCloudAuthGate,.sun-auth-gate')||img.id==='sunLoginLogo'||img.classList.contains('sun-live-catalog-logo');
|
||
const tune=img=>{
|
||
if(!(img instanceof HTMLImageElement)||critical(img))return;
|
||
if(!img.hasAttribute('loading'))img.loading='lazy';
|
||
if(!img.hasAttribute('decoding'))img.decoding='async';
|
||
if(!img.hasAttribute('fetchpriority'))img.setAttribute('fetchpriority','low');
|
||
};
|
||
const scan=root=>{
|
||
if(root instanceof HTMLImageElement)tune(root);
|
||
root?.querySelectorAll?.('img').forEach(tune);
|
||
};
|
||
|
||
const MAX_FILE=15*1024*1024,TARGET=2*1024*1024,MAX_SIDE=2048;
|
||
const guardedInputs=new WeakSet();
|
||
const toast=(text,type='info',ms=4500)=>{try{return window.SunEnterprise?.toast?.(text,type,ms)}catch(_){}};
|
||
const jpegName=name=>String(name||'photo').replace(/\.[^.]+$/,'')+'.jpg';
|
||
const canvasBlob=(canvas,type,quality)=>new Promise((resolve,reject)=>canvas.toBlob(b=>b?resolve(b):reject(new Error('Не удалось сжать изображение.')),type,quality));
|
||
async function decodeImage(file){
|
||
if('createImageBitmap' in window){const bitmap=await createImageBitmap(file);return {source:bitmap,width:bitmap.width,height:bitmap.height,close:()=>bitmap.close?.()};}
|
||
const url=URL.createObjectURL(file);try{const img=new Image();await new Promise((resolve,reject)=>{img.onload=resolve;img.onerror=reject;img.src=url});return {source:img,width:img.naturalWidth,height:img.naturalHeight,close:()=>URL.revokeObjectURL(url)};}catch(e){URL.revokeObjectURL(url);throw e;}
|
||
}
|
||
async function compressImage(file){
|
||
if(!(file instanceof File)||!String(file.type||'').startsWith('image/')||/gif|svg/i.test(file.type||''))return file;
|
||
let decoded;try{decoded=await decodeImage(file);}catch(_){return file;}
|
||
try{
|
||
const {width,height,source}=decoded;if(!width||!height)return file;
|
||
if(file.size<=TARGET&&Math.max(width,height)<=MAX_SIDE)return file;
|
||
let scale=Math.min(1,MAX_SIDE/Math.max(width,height)),quality=.84,best=null;
|
||
for(let pass=0;pass<5;pass++){
|
||
const w=Math.max(1,Math.round(width*scale)),h=Math.max(1,Math.round(height*scale));
|
||
const canvas=document.createElement('canvas');canvas.width=w;canvas.height=h;
|
||
const ctx=canvas.getContext('2d',{alpha:false});if(!ctx)return file;ctx.fillStyle='#fff';ctx.fillRect(0,0,w,h);ctx.drawImage(source,0,0,w,h);
|
||
const blob=await canvasBlob(canvas,'image/jpeg',quality);if(!best||blob.size<best.size)best=blob;if(blob.size<=TARGET)break;
|
||
quality=Math.max(.60,quality-.08);scale*=.86;
|
||
}
|
||
if(!best||best.size>=file.size)return file;
|
||
return new File([best],jpegName(file.name),{type:'image/jpeg',lastModified:file.lastModified||Date.now()});
|
||
}finally{decoded.close?.();}
|
||
}
|
||
async function prepareAttachment(file){const prepared=await compressImage(file);if(prepared.size>MAX_FILE)throw new Error(`Файл «${file.name}» больше 15 МБ даже после оптимизации.`);return {file:prepared,originalSize:file.size,savedBytes:Math.max(0,file.size-prepared.size)};}
|
||
async function guardChatFiles(input){
|
||
const raw=[...(input.files||[])];if(!raw.length)return;
|
||
const prepared=[];let saved=0;
|
||
for(const f of raw){try{const p=await prepareAttachment(f);prepared.push(p.file);saved+=p.savedBytes;}catch(e){toast(e.message||String(e),'warn',6000);}}
|
||
if(!prepared.length){input.value='';return;}
|
||
const dt=new DataTransfer();prepared.forEach(f=>dt.items.add(f));input.files=dt.files;
|
||
guardedInputs.add(input);input.dispatchEvent(new Event('change',{bubbles:true}));guardedInputs.delete(input);
|
||
if(saved>256*1024)toast(`Фото оптимизированы: сэкономлено ${(saved/1024/1024).toFixed(1)} МБ.`,'success',4200);
|
||
}
|
||
document.addEventListener('change',e=>{
|
||
const input=e.target;if(!(input instanceof HTMLInputElement)||input.id!=='sunChatFilesV29'||guardedInputs.has(input)||typeof DataTransfer==='undefined')return;
|
||
e.preventDefault();e.stopImmediatePropagation();guardChatFiles(input).catch(err=>{console.error('[Caterium photo compression]',err);guardedInputs.add(input);input.dispatchEvent(new Event('change',{bubbles:true}));guardedInputs.delete(input);});
|
||
},true);
|
||
window.SunAttachmentGuard={VERSION,MAX_FILE,TARGET,MAX_SIDE,compressImage,prepareAttachment};
|
||
|
||
let memoryData=null,memoryAt=0,memoryPromise=null,memoryTimer=0;
|
||
const MEMORY_CACHE_MS=15000,MEMORY_REFRESH_MS=30000,MEMORY_TIMEOUT_MS=8000;
|
||
const esc=v=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(v??'')):String(v??'');
|
||
const developerVisible=()=>document.getElementById('sun-developer-console-v22')?.classList.contains('on');
|
||
async function loadMemory({force=false}={}){
|
||
const now=Date.now();if(memoryPromise)return memoryPromise;if(!force&&memoryData&&now-memoryAt<MEMORY_CACHE_MS)return memoryData;
|
||
const c=window.SunCloudV2?.getClient?.();if(!c)return null;
|
||
memoryPromise=(async()=>{
|
||
let timer=0;
|
||
try{
|
||
const timeout=new Promise((_,reject)=>{timer=setTimeout(()=>reject(new Error('Developer memory timeout')),MEMORY_TIMEOUT_MS)});
|
||
const rpc=Promise.resolve(c.rpc('sun_dev_dashboard'));
|
||
const r=await Promise.race([rpc,timeout]);
|
||
if(r?.error)throw r.error;memoryData=r?.data||null;memoryAt=Date.now();return memoryData;
|
||
}catch(error){console.warn('[Caterium developer memory]',error?.message||error);return memoryData}
|
||
finally{clearTimeout(timer);memoryPromise=null}
|
||
})();
|
||
return memoryPromise;
|
||
}
|
||
async function enhanceDeveloperMemory({force=false}={}){
|
||
const view=document.getElementById('sun-developer-console-v22');if(!view?.classList.contains('on'))return null;
|
||
const body=document.getElementById('sunDevBody');if(!body)return null;
|
||
const version=document.getElementById('sunDevReleaseVersion');if(version)version.textContent=VERSION;
|
||
const d=await loadMemory({force});if(!d||!developerVisible())return d;
|
||
let box=document.getElementById('sunDevMemoryV1761');if(!box){box=document.createElement('div');box.id='sunDevMemoryV1761';box.className='sun-dev-grid';box.style.marginBottom='12px';body.prepend(box);}
|
||
const html=`<div class="sun-dev-kpi"><small>Память сервера</small><b>${esc(d.server_size||d.database_size||'—')}</b></div><div class="sun-dev-kpi"><small>База PostgreSQL</small><b>${esc(d.database_size||'—')}</b></div><div class="sun-dev-kpi"><small>Файлы Storage</small><b>${esc(d.storage_size||'—')}</b></div><div class="sun-dev-kpi"><small>Объектов Storage</small><b>${Number(d.storage_objects||0)}</b></div>`;
|
||
if(box.innerHTML!==html)box.innerHTML=html;
|
||
return d;
|
||
}
|
||
function scheduleMemoryRefresh(delay=80,force=false){setTimeout(()=>{if(developerVisible())enhanceDeveloperMemory({force}).catch(()=>{})},delay)}
|
||
function startMemoryTimer(){if(memoryTimer)return;memoryTimer=setInterval(()=>{if(!document.hidden&&developerVisible())enhanceDeveloperMemory({force:true}).catch(()=>{})},MEMORY_REFRESH_MS)}
|
||
|
||
function loadHotfix(){
|
||
if(window.SunHotfixV1763||document.getElementById('sunHotfixV1763Script'))return;
|
||
const script=document.createElement('script');script.id='sunHotfixV1763Script';script.src=`core/hotfix-v1763.js?v=${RELEASE}`;script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль hotfix-v1763.js');document.head.appendChild(script);
|
||
}
|
||
function loadOpsUX(){
|
||
if(window.SunOpsUXV1762||document.getElementById('sunOpsUXV1762Script'))return;
|
||
const script=document.createElement('script');script.id='sunOpsUXV1762Script';script.src=`core/ops-ux-v1762.js?v=${RELEASE}`;script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль ops-ux-v1762.js');document.head.appendChild(script);
|
||
}
|
||
function loadUXFix(){
|
||
if(window.SunUXFixV1764||document.getElementById('sunUXFixV1764Script'))return;
|
||
const script=document.createElement('script');script.id='sunUXFixV1764Script';script.src=`core/ux-fixes-v1764.js?v=${RELEASE}`;script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль ux-fixes-v1764.js');document.head.appendChild(script);
|
||
}
|
||
const start=()=>{
|
||
loadHotfix();loadOpsUX();loadUXFix();scan(document);startMemoryTimer();
|
||
const mo=new MutationObserver(records=>{records.forEach(r=>r.addedNodes.forEach(n=>{if(n.nodeType===1)scan(n)}));});
|
||
mo.observe(document.documentElement,{childList:true,subtree:true});
|
||
document.addEventListener('click',e=>{if(e.target.closest('#sunDeveloperNavV22,[data-dev-tab],#sunDevRefresh'))scheduleMemoryRefresh(100,true)},true);
|
||
window.addEventListener('sun:cloud-state-applied',()=>scheduleMemoryRefresh(180,true));
|
||
document.addEventListener('visibilitychange',()=>{if(!document.hidden&&developerVisible())scheduleMemoryRefresh(50,false)});
|
||
window.SunPerformance={VERSION,scanImages:()=>scan(document),refreshDeveloperMemory:(force=true)=>enhanceDeveloperMemory({force}),loadHotfix,loadOpsUX,loadUXFix,disconnect:()=>{mo.disconnect();if(memoryTimer){clearInterval(memoryTimer);memoryTimer=0;}}};
|
||
};
|
||
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',start,{once:true});else start();
|
||
})();
|