File: D:/HostingSpaces/netwerkbrabant/netwerkbrabant.nl/resources/assets/js/kms/attributes/multiselect.js
class MultiSelectController {
/**
* @param multiSelectElement {HTMLInputElement}
*/
constructor(multiSelectElement)
{
if(multiSelectElement.tagName !== 'INPUT' || multiSelectElement.type !== 'text') {
console.error('The given parameter must be an instance of HTMLInputElement');
return;
}
this.multiSelectSelectField = multiSelectElement; //The input element that must multiSelect what you type
let input = this.multiSelectSelectField.parentElement.querySelector('input[type="hidden"]'); //The input element that wil store the real value for the multiSelect Attribute
if(!input) {
console.error('Expected an HTMLInputElement as a sibling of the supplied multiSelect element (HTMLInputElement) to store the multiSelect\'s real value');
return;
}
this.input = input;
if(!'attributeKey' in this.multiSelectSelectField.dataset) {
console.error('attributeKey dataset value not set');
return;
}
this.attributeKey = this.multiSelectSelectField.dataset.attributeKey;
if(!'autosaveUrl' in this.multiSelectSelectField.dataset) {
console.error('autosaveUrl dataset value not set');
return;
}
this.autosaveUrl = this.multiSelectSelectField.dataset.autosaveUrl;
if(!'maxItemsToSelect' in this.multiSelectSelectField.dataset) {
console.error('maxItemsToSelect dataset value not set');
return;
}
this.maxItemsToSelect = this.multiSelectSelectField.dataset.maxItemsToSelect;
if(!'itemData' in this.multiSelectSelectField.dataset) {
console.error('itemData dataset value not set');
return;
}
this.dataSet = JSON.parse(this.multiSelectSelectField.dataset.itemdata);
if(!'values' in this.multiSelectSelectField.dataset) {
console.error('values dataset value not set');
return;
}
this.valuesFromAttribute = this.multiSelectSelectField.dataset.values.split(',');
let openElement = this.input.parentElement.querySelector('.open');
if(!openElement) {
console.error('Could not find multiSelect open element. It should be a sibling of the input')
}
this.openbutton = openElement;
let itemsWrapper = multiSelectElement.parentElement.parentElement.querySelector('div.items');
if(!itemsWrapper) {
console.error('Could not find a div HTMLElement with class "items" inside the multiSelectsInputElement\'s parent parent element')
}
this.itemsWrapper = itemsWrapper;
this.sortable = this.multiSelectSelectField.dataset.sortable;
if(this.sortable) {
let self = this;
this.dragElement = null;
// Sorting starts
itemsWrapper.addEventListener('dragstart', function (evt){
self.dragElement = evt.target; // Remembering an element that will be moved
// Limiting the movement type
evt.dataTransfer.effectAllowed = 'move';
evt.dataTransfer.setData('Text', self.dragElement.textContent);
// Subscribing to the events at dnd
self.itemsWrapper.addEventListener('dragover', self.onDragOver.bind(self), false);
self.itemsWrapper.addEventListener('dragend', self.onDragEnd.bind(self), false);
setTimeout(function () {
// If this action is performed without setTimeout, then
// the moved object will be of this class.
self.dragElement.classList.add('ghost');
}, 0)
}, false);
}
this.listenersEnabled = true;
this.constructedSuccessfully = true;
this.initialize();
};
initialize() {
if(this.constructedSuccessfully === false) {
console.error('MultiSelectController was not constructed successfully. Could not initialize because of it');
return;
}
this.makeMultiSelect();
this.addItems();
this.updateRealInput();
this.enableListeners(true);
this.enableMultiSelectOpenButton();
}
addItems() {
const dataSetLength = this.dataSet.length;
const valueFromAttributeLength = this.valuesFromAttribute.length;
for (let valuesIndex = 0; valuesIndex < valueFromAttributeLength; valuesIndex++) {
const idFromAttribute = parseInt(this.valuesFromAttribute[valuesIndex]);
for (let itemIndex = 0; itemIndex < dataSetLength; itemIndex++) {
const item = this.dataSet[itemIndex];
if (item.id === idFromAttribute) {
this.addItem(item.id, item.value);
break;
}
}
}
}
/**
* Makes the fake input field and multiSelect input field.
* Focus method is triggered when you hover over items in the menu
* select method is triggered when you click an item in the list.
*/
makeMultiSelect() {
let self = this;
let jqueryElement = $(this.multiSelectSelectField);
return jqueryElement.autocomplete({
source: this.dataSet,
minLength: 0,
focus: function (event, ui) {
return false;
},
select: function (event, ui) {
$(this.multiSelectSelectField).val("");
self.addItem(ui.item.id, ui.item.value);
self.updateRealInput();
self.autosaveIfNeeded();
self.enableListeners(true);
return false;
},
});
}
/**
* Make sure we can open the multiSelect field dropdown with the open button
*/
enableMultiSelectOpenButton() {
let self = this;
this.openbutton.addEventListener('click', function(event) {
$(self.multiSelectSelectField).autocomplete("search", "");
});
}
/**
* Adds a span tag containing the value given and the data-id property set to id given to
* the p tag with class items.
*/
addItem(id, value) {
let self = this;
if(this.itemsCount() > self.maxItemsToSelect) return;
let itemElement = document.createElement('p');
itemElement.classList.add('item');
if(this.sortable) {
itemElement.draggable = true;
value = '<span class="drag-icon"></span><span class="text">'+value+'</span>';
}
itemElement.innerHTML = value;
let removeButton = document.createElement('span');
removeButton.classList.add('remove');
itemElement.dataset.id = id;
itemElement.dataset.sort_order = this.itemsCount();
removeButton.addEventListener('click', (function (itemToRemove) {
return function() {
if(!self.listenersEnabled) return;
self.removeItem(itemToRemove);
}
})(id));
this.itemsWrapper.appendChild(itemElement);
itemElement.appendChild(removeButton);
}
itemsCount()
{
return this.itemsWrapper.childNodes.length;
};
/**
* Iterates over the child elements of the p element with class items and removes the element that has
* data-id set to the specified id. Then calls updateRealInput to update the hidden input
*/
removeItem(id) {
let items = this.itemsWrapper.children;
const itemsLength = this.itemsWrapper.children.length;
for (let index = 0; index < itemsLength; index++) {
const item = items[index];
if (parseInt(item.dataset.id) === id) {
this.itemsWrapper.removeChild(item);
break;
}
}
this.updateRealInput();
this.autosaveIfNeeded();
};
/**
* Iterates over the child elements of the p element with class items and concatenates their data-id
* values into a string. After that it sets the hidden input with the id string
*/
updateRealInput() {
let ids = [];
let items = this.itemsWrapper.children;
const itemsLength = items.length;
for (let i = 0; i < itemsLength; i++) {
const item = items[i];
ids[ids.length] = item.dataset.id;
}
let idString = ids.join(',');
this.input.setAttribute('value', idString);
let changeEvent = document.createEvent("Event");
changeEvent.initEvent("change", false, true);
// args: string type, boolean bubbles, boolean cancelable
this.input.dispatchEvent(changeEvent)
};
autosaveIfNeeded() {
if (!this.autosaveUrl) return;
let itemIds = this.input.getAttribute('value');
let self = this;
$.ajax({
type: "POST",
url: this.autosaveUrl,
data: {
'itemIds': itemIds,
},
success: function (response, textStatus, xhr) {
if (xhr.status === 200) {
console.log('autosave successfull to url: ' + self.autosaveUrl)
} else {
console.error('autosave failure to url (no code 200): ' + self.autosaveUrl + '');
console.error(response)
}
},
error: function (message) {
console.error('autosave failure to url: ' + self.autosaveUrl + '');
console.error(message)
},
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
dataType: 'json'
});
}
enableListeners(enable) {
this.listenersEnabled = enable;
let length = this.itemsWrapper.children.length;
//Add / remove listeners to items
for (let i = 0; i < length; i++) {
let item = this.itemsWrapper.children[i];
let id = item.dataset.id;
item.classList.remove('readonly');
this.multiSelectSelectField.classList.remove('hidden');
if(!enable) {
item.classList.add('readonly');
this.multiSelectSelectField.classList.add('hidden');
this.openbutton.classList.add('hidden');
}
}
}
onDragOver(evt) {
evt.preventDefault();
evt.dataTransfer.dropEffect = 'move';
let target = evt.target;
if (target && target !== this.dragElement && target.nodeName === 'P') {
// Sorting
this.itemsWrapper.insertBefore(this.dragElement, target.nextSibling || target);
}
}
onDragEnd(evt){
evt.preventDefault();
this.dragElement.classList.remove('ghost');
this.itemsWrapper.removeEventListener('dragover', this.onDragOver, false);
this.itemsWrapper.removeEventListener('dragend', this.onDragEnd, false);
console.log("dragEnd");
this.updateRealInput();
}
}