diff --git a/anytask/issues/templates/file_uploader.html b/anytask/issues/templates/file_uploader.html index 70f068b0..ad1437d2 100644 --- a/anytask/issues/templates/file_uploader.html +++ b/anytask/issues/templates/file_uploader.html @@ -29,6 +29,103 @@ {% endblock %} }); + var $submitBtns = $('#fileupload button[type="submit"]'); + var $primarySubmit = $submitBtns.first(); + var originalSubmitHTML = $primarySubmit.html(); + var uploadStarted = false; + var maxFileSize = {{ max_file_size }}; + var uploadErrorMessages = { + 'Maximum number of files exceeded': '{% trans "prevysheno_kolvo_fajlov" %}', + 'Filetype not allowed': '{% trans "nedopustimyj_tip_fajla" %}', + 'File is too big': '{% trans "fajl_slishkom_bolshoj" %}', + 'File is too small': '{% trans "fajl_slishkom_mal" %}' + }; + + function formatBytes(b) { + if (b >= 1073741824) return (b / 1073741824).toFixed(1) + ' GB'; + if (b >= 1048576) return (b / 1048576).toFixed(1) + ' MB'; + if (b >= 1024) return (b / 1024).toFixed(1) + ' KB'; + return b + ' B'; + } + + function setProgress(pct) { + $('#upload-progress-bar') + .css('width', pct + '%') + .attr('aria-valuenow', pct) + .text(pct + '%'); + } + function setSubmitBusy(pct) { + var label = (typeof pct === 'number') ? (pct + '%') : '...'; + // Update only the text node, not the whole HTML — preserves the spinner + // element so its CSS animation doesn't restart on every progress tick. + var $label = $primarySubmit.find('.upload-busy-label'); + if ($label.length) { + $label.text(label); + } else { + $primarySubmit.html( + " " + + "" + label + "" + ); + } + } + function lockSend() { + $submitBtns.prop('disabled', true); + setSubmitBusy(); + setProgress(0); + $('#upload-progress-wrap').show(); + } + function unlockSend() { + uploadStarted = false; + $submitBtns.prop('disabled', false); + $primarySubmit.html(originalSubmitHTML); + setTimeout(function() { $('#upload-progress-wrap').fadeOut(300); }, 800); + } + + $('#fileupload') + .on('fileuploadadd', function() { + // Disable immediately on file selection (before processing/uploading) + $('#upload-error-msg').empty(); + $('#upload-error-alert').hide(); + lockSend(); + }) + .on('fileuploadsend', function() { + // HTTP request is about to start - upload is really happening + uploadStarted = true; + }) + .on('fileuploadstop', function() { + // All uploads finished (success or failure) + unlockSend(); + }) + .on('fileuploadprocessfail', function(e, data) { + // File failed client-side validation (too big, wrong type, etc.) + // No upload will start, so re-enable unless one was already going + var $msg = $('#upload-error-msg'); + $.each(data.files, function(i, file) { + if (!file.error) return; + var text = uploadErrorMessages[file.error] || file.error; + if (file.error === 'File is too big') { + text += ' (' + formatBytes(file.size) + + ', max ' + formatBytes(maxFileSize) + ')'; + } + $msg.append( + $('