feat: load order enhancements module

This commit is contained in:
pavlov346346-source 2026-09-11 13:43:11 +03:00
parent c41824ea6d
commit 5774573e45

View File

@ -3,16 +3,8 @@
const VERSION='17.7.3'; const VERSION='17.7.3';
const RELEASE='20260909-v17-7-3-clients-server-read'; const RELEASE='20260909-v17-7-3-clients-server-read';
const critical=img=>img.closest('header,.brand,#sunCloudAuthGate,.sun-auth-gate')||img.id==='sunLoginLogo'||img.classList.contains('sun-live-catalog-logo'); const critical=img=>img.closest('header,.brand,#sunCloudAuthGate,.sun-auth-gate')||img.id==='sunLoginLogo'||img.classList.contains('sun-live-catalog-logo');
const tune=img=>{ 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');};
if(!(img instanceof HTMLImageElement)||critical(img))return; const scan=root=>{if(root instanceof HTMLImageElement)tune(root);root?.querySelectorAll?.('img').forEach(tune);};
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 pendingImageRoots=new Set();let imageScanTimer=0; const pendingImageRoots=new Set();let imageScanTimer=0;
function queueImageScan(root){if(!root||root.nodeType!==1)return;for(const existing of [...pendingImageRoots]){if(existing===root||existing.contains?.(root))return;if(root.contains?.(existing))pendingImageRoots.delete(existing)}pendingImageRoots.add(root);if(imageScanTimer)return;imageScanTimer=setTimeout(()=>{imageScanTimer=0;const roots=[...pendingImageRoots];pendingImageRoots.clear();roots.forEach(scan)},80);} function queueImageScan(root){if(!root||root.nodeType!==1)return;for(const existing of [...pendingImageRoots]){if(existing===root||existing.contains?.(root))return;if(root.contains?.(existing))pendingImageRoots.delete(existing)}pendingImageRoots.add(root);if(imageScanTimer)return;imageScanTimer=setTimeout(()=>{imageScanTimer=0;const roots=[...pendingImageRoots];pendingImageRoots.clear();roots.forEach(scan)},80);}
@ -21,116 +13,32 @@
const toast=(text,type='info',ms=4500)=>{try{return window.SunEnterprise?.toast?.(text,type,ms)}catch(_){}}; 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 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)); const canvasBlob=(canvas,type,quality)=>new Promise((resolve,reject)=>canvas.toBlob(b=>b?resolve(b):reject(new Error('Не удалось сжать изображение.')),type,quality));
async function decodeImage(file){ 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;}}
if('createImageBitmap' in window){const bitmap=await createImageBitmap(file);return {source:bitmap,width:bitmap.width,height:bitmap.height,close:()=>bitmap.close?.()};} 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?.();}}
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 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){ 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);}
const raw=[...(input.files||[])];if(!raw.length)return; 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);
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}; window.SunAttachmentGuard={VERSION,MAX_FILE,TARGET,MAX_SIDE,compressImage,prepareAttachment};
let memoryData=null,memoryAt=0,memoryPromise=null,memoryTimer=0; let memoryData=null,memoryAt=0,memoryPromise=null,memoryTimer=0;
const MEMORY_CACHE_MS=15000,MEMORY_REFRESH_MS=30000,MEMORY_TIMEOUT_MS=8000; 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 esc=v=>window.SunSafe?.escapeHTML?window.SunSafe.escapeHTML(String(v??'')):String(v??'');
const developerVisible=()=>document.getElementById('sun-developer-console-v22')?.classList.contains('on'); const developerVisible=()=>document.getElementById('sun-developer-console-v22')?.classList.contains('on');
async function loadMemory({force=false}={}){ 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;}
const now=Date.now();if(memoryPromise)return memoryPromise;if(!force&&memoryData&&now-memoryAt<MEMORY_CACHE_MS)return memoryData; 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;}
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 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 startMemoryTimer(){if(memoryTimer)return;memoryTimer=setInterval(()=>{if(!document.hidden&&developerVisible())enhanceDeveloperMemory({force:true}).catch(()=>{})},MEMORY_REFRESH_MS)}
function loadDataLayer(){ const loadScript=(id,src,flag,async=true)=>{if(window[flag]||document.getElementById(id))return;const script=document.createElement('script');script.id=id;script.src=src;script.async=async;script.onerror=()=>console.error(`[Caterium] Не загрузился ${src}`);document.head.appendChild(script);};
if(window.CateriumDataV1773||document.getElementById('cateriumDataV1773Script'))return; function loadDataLayer(){loadScript('cateriumDataV1773Script',`core/data-layer-v1773.js?v=${RELEASE}`,'CateriumDataV1773',false)}
const script=document.createElement('script');script.id='cateriumDataV1773Script';script.src=`core/data-layer-v1773.js?v=${RELEASE}`;script.async=false;script.onerror=()=>console.error('[Caterium] Не загрузился data-layer-v1773.js');document.head.appendChild(script); function loadServerAutomation(){loadScript('cateriumServerAutomationV1770Script',`core/server-automation-v1770.js?v=${RELEASE}`,'CateriumServerAutomationV1770')}
} function loadHotfix(){loadScript('sunHotfixV1763Script',`core/hotfix-v1763.js?v=${RELEASE}`,'SunHotfixV1763')}
function loadServerAutomation(){ function loadOpsUX(){loadScript('sunOpsUXV1762Script',`core/ops-ux-v1762.js?v=${RELEASE}`,'SunOpsUXV1762')}
if(window.CateriumServerAutomationV1770||document.getElementById('cateriumServerAutomationV1770Script'))return; function loadUXFix(){loadScript('sunUXFixV1764Script',`core/ux-fixes-v1764.js?v=${RELEASE}`,'SunUXFixV1764')}
const script=document.createElement('script');script.id='cateriumServerAutomationV1770Script';script.src=`core/server-automation-v1770.js?v=${RELEASE}`;script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился server-automation-v1770.js');document.head.appendChild(script); function loadDeveloperUX(){loadScript('sunDeveloperUXV1768Script',`core/developer-console-v1768.js?v=${RELEASE}`,'SunDeveloperUXV1768')}
} function loadOfferWorkspace(){loadScript('sunOfferWorkspaceV1769Script',`core/offer-workspace-v1769.js?v=${RELEASE}`,'SunOfferWorkspaceV1769')}
function loadHotfix(){ function loadAuthSecurity(){loadScript('cateriumAuthSecurityV1774Script',`core/auth-security-v1774.js?v=${RELEASE}`,'CateriumAuthSecurityV1774',false)}
if(window.SunHotfixV1763||document.getElementById('sunHotfixV1763Script'))return; function loadOrderEnhancements(){loadScript('cateriumOrderEnhancementsV1775Script',`core/order-enhancements-v1775.js?v=${RELEASE}`,'__cateriumOrderEnhancementsV1775')}
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); const start=()=>{loadDataLayer();loadServerAutomation();loadHotfix();loadOpsUX();loadUXFix();loadDeveloperUX();loadOfferWorkspace();loadAuthSecurity();loadOrderEnhancements();scan(document);startMemoryTimer();const mo=new MutationObserver(records=>{records.forEach(r=>r.addedNodes.forEach(n=>{if(n.nodeType===1)queueImageScan(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}),loadDataLayer,loadServerAutomation,loadHotfix,loadOpsUX,loadUXFix,loadDeveloperUX,loadOfferWorkspace,loadAuthSecurity,loadOrderEnhancements,disconnect:()=>{mo.disconnect();if(memoryTimer){clearInterval(memoryTimer);memoryTimer=0}if(imageScanTimer){clearTimeout(imageScanTimer);imageScanTimer=0}pendingImageRoots.clear();}};};
}
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);
}
function loadDeveloperUX(){
if(window.SunDeveloperUXV1768||document.getElementById('sunDeveloperUXV1768Script'))return;
const script=document.createElement('script');script.id='sunDeveloperUXV1768Script';script.src=`core/developer-console-v1768.js?v=${RELEASE}`;script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль developer-console-v1768.js');document.head.appendChild(script);
}
function loadOfferWorkspace(){
if(window.SunOfferWorkspaceV1769||document.getElementById('sunOfferWorkspaceV1769Script'))return;
const script=document.createElement('script');script.id='sunOfferWorkspaceV1769Script';script.src=`core/offer-workspace-v1769.js?v=${RELEASE}`;script.async=true;script.onerror=()=>console.error('[Caterium] Не загрузился модуль offer-workspace-v1769.js');document.head.appendChild(script);
}
function loadAuthSecurity(){
if(window.CateriumAuthSecurityV1774||document.getElementById('cateriumAuthSecurityV1774Script'))return;
const script=document.createElement('script');script.id='cateriumAuthSecurityV1774Script';script.src=`core/auth-security-v1774.js?v=${RELEASE}`;script.async=false;script.onerror=()=>console.error('[Caterium] Не загрузился модуль auth-security-v1774.js');document.head.appendChild(script);
}
const start=()=>{
loadDataLayer();loadServerAutomation();loadHotfix();loadOpsUX();loadUXFix();loadDeveloperUX();loadOfferWorkspace();loadAuthSecurity();scan(document);startMemoryTimer();
const mo=new MutationObserver(records=>{records.forEach(r=>r.addedNodes.forEach(n=>{if(n.nodeType===1)queueImageScan(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}),loadDataLayer,loadServerAutomation,loadHotfix,loadOpsUX,loadUXFix,loadDeveloperUX,loadOfferWorkspace,loadAuthSecurity,disconnect:()=>{mo.disconnect();if(memoryTimer){clearInterval(memoryTimer);memoryTimer=0}if(imageScanTimer){clearTimeout(imageScanTimer);imageScanTimer=0}pendingImageRoots.clear();}};
};
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',start,{once:true});else start(); if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',start,{once:true});else start();
})(); })();