(event)
| 277 | } |
| 278 | |
| 279 | export async function folderDropHandler(event) { |
| 280 | event.preventDefault(); |
| 281 | event.currentTarget.classList.remove('drop-hover'); |
| 282 | |
| 283 | const dropFolder = |
| 284 | event.currentTarget.getAttribute('data-folder') || |
| 285 | event.currentTarget.getAttribute('data-dest-folder') || |
| 286 | 'root'; |
| 287 | |
| 288 | let dragData = null; |
| 289 | try { |
| 290 | const raw = event.dataTransfer.getData('application/json') || '{}'; |
| 291 | dragData = JSON.parse(raw); |
| 292 | } catch (e) { |
| 293 | dragData = null; |
| 294 | } |
| 295 | |
| 296 | if (!dragData) { |
| 297 | showToast(t('invalid_drag_data')); |
| 298 | return; |
| 299 | } |
| 300 | |
| 301 | // --------------------------- |
| 302 | // 1) FOLDER → FOLDER MOVE |
| 303 | // --------------------------- |
| 304 | if (dragData.dragType === 'folder' && dragData.folder) { |
| 305 | const source = String(dragData.folder); |
| 306 | const destination = dropFolder || 'root'; |
| 307 | const sourceId = String(dragData.sourceId || dragData.sourceSourceId || '').trim(); |
| 308 | const destSourceId = getPaneSourceIdForElement(event.currentTarget) || getActiveSourceId(); |
| 309 | const crossSource = sourceId && destSourceId && sourceId !== destSourceId; |
| 310 | |
| 311 | // quick no-op: same parent as before |
| 312 | const oldParent = parentFolderOf(source); |
| 313 | if (!crossSource && destination === oldParent) { |
| 314 | showToast(t('source_dest_same')); |
| 315 | return; |
| 316 | } |
| 317 | |
| 318 | // optional: mirror PHP self/descendant guard so we fail fast |
| 319 | const norm = s => { |
| 320 | if (!s) return ''; |
| 321 | return s.replace(/^[/\\]+|[/\\]+$/g, ''); |
| 322 | }; |
| 323 | const srcNorm = norm(source); |
| 324 | const dstNorm = destination === 'root' ? '' : norm(destination); |
| 325 | |
| 326 | if ( |
| 327 | !crossSource && |
| 328 | dstNorm !== '' && |
| 329 | ( |
| 330 | dstNorm.toLowerCase() === srcNorm.toLowerCase() || |
| 331 | (dstNorm + '/').toLowerCase().startsWith((srcNorm + '/').toLowerCase()) |
| 332 | ) |
| 333 | ) { |
| 334 | showToast(t('dest_cannot_be_descendant')); |
| 335 | return; |
| 336 | } |
no test coverage detected