File: D:/HostingSpaces/SBogers10/carrot.komma.pro/resources/js/kms/attributes/fileDragAndDropHandler.js
class FileDragAndDropHandler
{
/**
*
*
* @param areaElement {HTMLElement}
*/
constructor(areaElement)
{
this.hooks = [];
this.selectButton = areaElement.querySelector('button.select');
if(!this.selectButton) {
console.log('FileDragAndDropHandler:constructor The document area must have a button in it with class select. But did not have one. Not responding to drag and drop');
return;
}
this.fileCatcherInput = areaElement.querySelector('input[type="file"]');
if(!this.fileCatcherInput) {
console.log('FileDragAndDropHandler:constructor The document area must have file input. But did not have one. Not responding to drag and drop');
return;
}
this.makeElementRespondToDragOverAndLeave(areaElement);
this.makeElementRespondToDrop(areaElement);
this.makeElementRespondToClick(areaElement);
this.makeFileInputPassFilesToHooks();
}
/**
* Adds an event listener to the file input and makes it pass its files to the document manager
*/
makeFileInputPassFilesToHooks()
{
let self = this;
this.fileCatcherInput.addEventListener('change', function(event) {
let nFiles = event.target.files.length;
for(let index = 0; index < nFiles; index++) {
let file = event.target.files[index];
// console.log(file);
self.createFileListForFileAndPassToHooks(file);
}
event.target.value = '';
});
}
/**
* Catches click on the element and passes them trough to the file input
*/
makeElementRespondToClick(element)
{
let self = this;
element.addEventListener('click', function(event) {
self.fileCatcherInput.click();
});
}
/**
* Hooks a hookable (an object that has a addFile method / function) to the drag and drop handler so that the drag and drop handler passes inputs to the
* hookable
* @param hookable {Class}
*/
hookTo(hookable)
{
if(typeof hookable.receiveFile !== 'function') {
console.error('The given hookable does not have a required receiveFile method / function. Not hooking the given "hookable".');
return;
}
this.hooks.push(hookable);
}
/**
* @param element HTMLElement that needs to be draggable or not
* @param respondOrNotBoolean
* @return {FileDragAndDropHandler}
*/
makeElementRespondToDrop(element, respondOrNotBoolean = true) {
if (respondOrNotBoolean) {
element.removeEventListener('drop', this.drop.bind(this));
element.addEventListener('drop', this.drop.bind(this));
} else {
element.removeEventListener('drop', this.drop.bind(this));
}
return this
}
/**
* Prepares the element so that it can receive items that are dropped onto it
*
* @param element HTMLElement that needs can be a drop target or not
* @param respondOrNotBoolean wheter or not it should be a drop target
*/
makeElementRespondToDragOverAndLeave(element, respondOrNotBoolean = true) {
if (respondOrNotBoolean) {
element.removeEventListener('dragover', this.dragOver.bind(this));
element.removeEventListener('dragleave', this.dragLeave.bind(this));
element.addEventListener('dragover', this.dragOver.bind(this));
element.addEventListener('dragleave', this.dragLeave.bind(this));
} else {
element.removeEventListener('dragover', this.dragOver.bind(this));
element.removeEventListener('dragleave', this.dragLeave.bind(this));
}
}
/**
* Occurs when a document (li) HTMLElement is being dragged over the target element.
* So the dragEvent target is not the element that you drag, but the place / HTMLElement where it may be dropped.
*
* @param dragEvent
*/
dragOver(dragEvent) {
dragEvent.preventDefault(); //Sets target HTMLElement to allow a drop
dragEvent.stopPropagation();
if (!dragEvent.target.id) return;
this.enableOrDisablePointerEventsOnChildrenOfElement(dragEvent.target, false);
dragEvent.target.classList.add('isDropTarget');
}
/**
* Occurs when a document (li) HTMLElement is NOT being dragged anymore over the target element.
*
* @param dragEvent
*/
dragLeave(dragEvent) {
if (!dragEvent.target.id) return;
dragEvent.stopPropagation();
this.enableOrDisablePointerEventsOnChildrenOfElement(dragEvent.target, true);
dragEvent.target.classList.remove('isDropTarget');
}
/**
* Occurs when a document is being dropped
*
* @param dragEvent
*/
drop(dragEvent) {
dragEvent.preventDefault(); //Prevent browser from activating links and buttons
let targetElement = dragEvent.target;
targetElement.classList.remove('isDropTarget');
this.enableOrDisablePointerEventsOnChildrenOfElement(targetElement, true);
// Prevent default behavior (Prevent file from being opened)
dragEvent.preventDefault();
if (dragEvent.dataTransfer.items) {
// Use DataTransferItemList interface to access the file(s)
for (let i = 0; i < dragEvent.dataTransfer.items.length; i++) {
// If dropped items aren't files, reject them
if (dragEvent.dataTransfer.items[i].kind === 'file') {
let file = dragEvent.dataTransfer.items[i].getAsFile();
this.createFileListForFileAndPassToHooks(file);
}
}
} else {
// Use DataTransfer interface to access the file(s)
for (let i = 0; i < dragEvent.dataTransfer.files.length; i++) {
let file = dragEvent.dataTransfer.files[i];
this.createFileListForFileAndPassToHooks(file);
}
}
}
createFileListForFileAndPassToHooks(file)
{
let length = this.hooks.length;
for(let index = 0; index < length; index++) {
this.hooks[index].receiveFile(file);
}
}
/**
* Enables / disables the listening to pointer (example: mouse) events on an element
* and all of it's children. This prevents for example the dragover event from beeing canceled
* when dragging over an input that resides in an element which listens to the dragover event
* because the input captures the mouse.
*/
enableOrDisablePointerEventsOnChildrenOfElement(element, enable)
{
let length = element.children.length;
for(let index = 1; index < length; index++)
{
if(enable === false) {
element.children[index].style.pointerEvents = 'none';
} else {
element.children[index].style.pointerEvents = null;
}
let childrenLength = element.children.children;
for(let childrenIndex = 1; childrenIndex < childrenLength; childrenIndex++)
{
this.enableOrDisablePointerEventsOnChildrenOfElement(element.children.children[index], enable);
}
}
}
}