/* Real multipart upload progress; registration remains authoritative on the server. */ (() => { 'use strict'; const number=new Intl.NumberFormat('fa-IR',{maximumFractionDigits:1}); function bytes(value){return value>=1048576?number.format(value/1048576)+' مگابایت':number.format(value/1024)+' کیلوبایت';} function duration(seconds){return seconds>=60?number.format(Math.ceil(seconds/60))+' دقیقه':number.format(Math.max(1,Math.ceil(seconds)))+' ثانیه';} function send(form){ const payload=new FormData(form),panel=document.querySelector('#receipt-upload'); const title=document.querySelector('#receipt-upload-title'),meter=document.querySelector('#receipt-upload-progress'); const percent=document.querySelector('#receipt-upload-percent'),amount=document.querySelector('#receipt-upload-bytes'),time=document.querySelector('#receipt-upload-time'); const button=document.querySelector('#submit-receipt'); const controls=[...form.querySelectorAll('input,select,textarea,button')].map(control=>[control,control.disabled]); controls.forEach(([control])=>{control.disabled=true;});form.classList.add('is-uploading');button.setAttribute('aria-busy','true'); panel.hidden=false;title.textContent='در حال ارسال فیش…';button.textContent='در حال ارسال…'; meter.value=0;percent.textContent='۰٪';amount.textContent='در انتظار شروع ارسال'; const started=performance.now();let loaded=0,total=0,lastProgress=started,processing=false; function updateTime(){ if(processing)return; const elapsed=(performance.now()-started)/1000; const estimate=loaded>0 && elapsed>=1 && total>loaded; time.textContent='زمان سپری‌شده: '+number.format(Math.floor(elapsed))+' ثانیه'+(performance.now()-lastProgress>5000?' · منتظر ادامه ارسال؛ سرعت اتصال تغییر کرده است.':estimate?' · حدود '+duration((total-loaded)*elapsed/loaded)+' دیگر برای ارسال':' · در حال محاسبه زمان باقی‌مانده…'); } function process(){ processing=true;meter.value=100;percent.textContent='۱۰۰٪'; title.textContent='ارسال کامل شد؛ در حال بررسی و ثبت فیش…';button.textContent='در حال ثبت…'; time.textContent='منتظر تأیید ثبت از سرور؛ زمان این مرحله به سرعت پردازش بستگی دارد.'; } updateTime();panel.scrollIntoView({block:'nearest'}); return new Promise((resolve,reject)=>{ const xhr=new XMLHttpRequest(),timer=setInterval(updateTime,1000);let settled=false; function finish(error,result){ if(settled)return;settled=true;clearInterval(timer); if(!result?.redirect_url){ controls.forEach(([control,disabled])=>{control.disabled=disabled;});form.classList.remove('is-uploading');button.removeAttribute('aria-busy'); title.textContent='ثبت فیش انجام نشد';time.textContent='اطلاعات و تصویر حفظ شده‌اند؛ پس از رفع مشکل دوباره ارسال کنید.'; } if(error)reject(error);else resolve(result); } xhr.upload.addEventListener('progress',event=>{ loaded=event.loaded;total=event.lengthComputable?event.total:0;lastProgress=performance.now(); amount.textContent='ارسال‌شده: '+bytes(loaded)+(total?' از '+bytes(total):''); if(total){const value=Math.min(100,Math.floor(loaded/total*100));meter.value=value;percent.textContent=number.format(value)+'٪';} else{meter.removeAttribute('value');percent.textContent='در حال ارسال';} updateTime(); }); xhr.upload.addEventListener('load',process); xhr.addEventListener('load',()=>{ const result=xhr.response; if(xhr.status>=200 && xhr.status<300 && result?.redirect_url){finish(null,result);return;} if(xhr.status===422 && result?.errors){finish(null,result);return;} const message=[401,403,410].includes(xhr.status)?'دسترسی یا نشست شما معتبر نیست؛ صفحه را دوباره باز کنید.':xhr.status===413?'حجم تصویر بیش از حد مجاز سرور است؛ تصویر کوچک‌تری انتخاب کنید.':'ثبت فیش تأیید نشد؛ دوباره تلاش کنید. در صورت تکرار، وضعیت فیش‌های خود را بررسی کنید.'; finish(new Error(message)); }); xhr.addEventListener('error',()=>finish(new Error('ارتباط با سرور قطع شد؛ اتصال اینترنت را بررسی و دوباره ارسال کنید.'))); xhr.addEventListener('abort',()=>finish(new Error('ارسال متوقف شد؛ دوباره تلاش کنید.'))); try{ xhr.open('POST',form.action || location.href);xhr.responseType='json'; xhr.setRequestHeader('Accept','application/json'); xhr.setRequestHeader('X-CSRFToken',payload.get('csrfmiddlewaretoken')); xhr.send(payload); }catch(error){finish(error);} }); } window.ReceiptUpload=Object.freeze({send}); })();