1// Copyright (c) 2021-2025 GrayBits LLC 2 3import * as Notification from 'gray_matter/utility/notification' 4import axios from 'axios' 5 6export default class GenericUpload{ 7 constructor(e, url, options){ 8 this.element = e; 9 this.url = url; 10 this.element.interface = this; 11 this.setupButtons(); 12 if (!this.upload_button) return; 13 this.setupProgress(); 14 15 this.file_input = e.querySelector('input[type="file"]'); 16 this.file_input.addEventListener('change', (e) => this.startUploads()); 17 18 if (options){ 19 if (options.success) this.processSuccessfulUpload = options.success; 20 if (options.failure) this.processFailedUpload = options.failure; 21 if (options.preprocess) this.preprocessData = options.preprocess; 22 } 23 } 24 25 setupProgress(){ 26 this.progress_label = this.element.querySelector('.upload_progress_label'); 27 this.progress_label_initial_value = this.progress_label.innerHTML; 28 this.progress_value = this.element.querySelector('.upload_progress_value'); 29 this.progress_value.style.strokeDasharray = "157.08 157.08" 30 } 31 32 setupButtons(){ 33 this.upload_button = this.element.querySelector('button.upload'); 34 if (!this.upload_button) return; 35 36 this.upload_button.addEventListener('click', (e) => this.openFileBrowser(e)); 37 this.setupDrop(); 38 } 39 40 setupDrop(element){ 41 this.upload_button.addEventListener('dragenter', (e) => this.dragHighlight(e)); 42 this.upload_button.addEventListener('dragover', (e) => this.dragHighlight(e)); 43 this.upload_button.addEventListener('dragleave', (e) => this.dragLeave(e)); 44 this.upload_button.addEventListener('drop', (e) => this.drop(e)); 45 } 46 47 dragHighlight(e){ 48 e.preventDefault(); 49 e.stopPropagation(); 50 this.upload_button.classList.add('drop_active'); 51 } 52 53 dragLeave(e){ 54 e.preventDefault(); 55 e.stopPropagation(); 56 this.upload_button.classList.remove('drop_active'); 57 } 58 59 drop(e){ 60 e.preventDefault(); 61 e.stopPropagation(); 62 this.upload_button.classList.remove('drop_active'); 63 this.uploadFiles(e.dataTransfer.files); 64 } 65 66 67 openFileBrowser(){ 68 this.file_input.value = ''; 69 this.file_input.click(); 70 } 71 72 startUploads(){ 73 if (this.file_input.files.length == 0) return; 74 75 this.uploadFiles(this.file_input.files); 76 } 77 78 uploadFiles(files){ 79 this.showProgress(); 80 this.current_uploads = []; 81 this.current_loaded = []; 82 this.current_total = 0; 83 for (let i = 0; i < files.length; i++){ 84 this.current_uploads.push(files[i].size); 85 this.current_loaded.push(0); 86 this.current_total += files[i].size; 87 } 88 for (let i = 0; i < files.length; i++){ 89 this.startUpload(files[i], i); 90 } 91 } 92 93 async startUpload(file, index){ 94 let form_data = new FormData(); 95 form_data.append('file', file); 96 97 if (this.preprocessData){ 98 this.preprocessData(this, form_data); 99 } 100 101 try{ 102 const response = await axios.post(this.url, 103 form_data, { 104 onUploadProgress: (e) => { 105 let i = index; 106 this.current_loaded[i] = e.loaded; 107 let total_loaded = 0; 108 for (let i = 0; i < this.current_loaded.length; i++){ 109 total_loaded += this.current_loaded[i]; 110 } 111 this.setProgress(total_loaded / this.current_total); 112 } 113 } 114 ); 115 if (this.processSuccessfulUpload){ 116 this.processSuccessfulUpload(response); 117 } 118 } catch(error) { 119 if (this.processFailedUpload){ 120 this.processFailedUpload(error, file); 121 } else { 122 if (error.response){ 123 Notification.showError(error.response.data.error); 124 } else { 125 Notification.showError('Upload failed'); 126 } 127 } 128 } 129 this.hideProgress(); 130 } 131 132 showProgress(){ 133 this.progress_value.style.strokeDashoffset = '0'; 134 this.progress_label.innerHTML = ''; 135 this.element.classList.add('uploading'); 136 this.upload_button.disabled = true; 137 } 138 139 hideProgress(){ 140 this.element.classList.remove('uploading'); 141 this.progress_label.innerHTML = this.progress_label_initial_value; 142 this.upload_button.disabled = false;
143 } 144 145 setProgress(percentage){ 146 this.progress_label.innerHTML = (Math.min(100 * percentage, 100)).toFixed(0) + '%'; 147 this.progress_value.style.strokeDashoffset = 157.08 * (1 - percentage); 148 } 149};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.