1/* stimulusFetch: 'lazy' */ 2import { Controller } from '@hotwired/stimulus'; 3 4// noinspection JSUnusedGlobalSymbols loaded by stimulus 5/** 6 * @extends {Controller<HTMLElement>} 7 * @property {string} uploadUrlValue 8 * @property {string} currentPathValue 9 * @property {string} nameValue 10 * @property {Object} transValue 11 */ 12export default class extends Controller { 13 14 static values = { 15 uploadUrl: String, 16 currentPath: String, 17 name: String, 18 trans: Object 19 } 20 21 /** 22 * @param {string} key 23 * @returns {string} 24 */ 25 #trans (key) { 26 return this.transValue[key] || key; 27 } 28 29 async createFolder() { 30 /** @type {HTMLElement} */ 31 const element = this.element; 32 const name = window.prompt(this.currentPathValue + '\n\n' + this.#trans("Enter folder name"), ''); 33 if (!name) return; 34 const formData = new FormData(); 35 formData.append('folder', name); 36 formData.append('path', this.currentPathValue); 37 const response = await fetch(this.uploadUrlValue, { 38 method: 'POST', 39 body: formData 40 }); 41 if (response.ok) { 42 element.closest('turbo-frame').reload(); 43 } else { 44 console.error('Create folder failed'); 45 } 46 } 47 48 setSelectedFile(evt) { 49 this.selectedFile = evt.target.value; 50 } 51 52 selectFile() { 53 if (this.selectedFile) { 54 /** @type {HTMLElement} */ 55 const element = this.element; 56 element.dispatchEvent(new CustomEvent('frame-stack:close', {composed: true, bubbles: true})); 57 window.dispatchEvent(new CustomEvent('file-selected', {detail: {path: this.selectedFile, name: this.nameValue}})); 58 } 59 } 60 61 connect() { 62 /** @type {HTMLElement} */ 63 const element = this.element; 64 this.bound = { 65 "new-folder": () => this.createFolder(), 66 "file-select:ok": () => this.selectFile() 67 }; 68 for (let n in this.bound) { 69 window.addEventListener(n, this.bound[n]); 70 } 71 element.addEventListener('dragover', this.onDragOver.bind(this)); 72 element.addEventListener('drop', this.onDrop.bind(this)); 73 } 74 75 disconnect() { 76 for (let n in this.bound) { 77 window.removeEventListener(n, this.bound[n]); 78 } 79 } 80 81 onDragOver(event) { 82 event.preventDefault(); 83 event.stopPropagation(); 84 /** @type {HTMLElement} */ 85 const element = this.element; 86 element.classList.add('drag-over'); 87 } 88 89 onDrop(event) { 90 event.preventDefault(); 91 event.stopPropagation(); 92 /** @type {HTMLElement} */ 93 const element = this.element; 94 element.classList.remove('drag-over'); 95 96 const files = event.dataTransfer.files; 97 if (files.length > 0) { 98 this.uploadFiles(files).then(() => {}); 99 } 100 } 101 102 async uploadFiles(files) { 103 /** @type {HTMLElement} */ 104 const element = this.element; 105 const formData = new FormData(); 106 formData.append('path', this.currentPathValue); 107 108 for (let i = 0; i < files.length; i++) { 109 formData.append(`file_${i}`, files[i]); 110 } 111 112 try { 113 const response = await fetch(this.uploadUrlValue, { 114 method: 'POST', 115 body: formData 116 }); 117 118 if (response.ok) { 119 // Refresh the page or the Turbo Frame to show new files 120 element.closest('turbo-frame').reload(); 121 } else { 122 console.error('Upload failed'); 123 } 124 } catch (error) { 125 console.error('Error uploading files:', error); 126 } 127 } 128}
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.