File: D:/HostingSpaces/SBogers95/rentman.io/resources/assets/js/kms/attributes/autocomplete.js
class AutoCompleteController {
/**
* @param autocompleteInputElement {HTMLInputElement}
*/
constructor(autocompleteInputElement)
{
if(autocompleteInputElement.tagName !== 'INPUT' || autocompleteInputElement.type !== 'text') {
console.error('The given parameter must be an instance of HTMLInputElement');
return;
}
this.autoCompleteSelectField = autocompleteInputElement; //The input element that must autocomplete what you type
let input = this.autoCompleteSelectField.parentElement.querySelector('input[type="hidden"]'); //The input element that wil store the real value for the autocompleteInput Attribute
if(!input) {
console.error('Expected an HTMLInputElement as a sibling of the supplied autoCompleteInput element (HTMLInputElement) to store the autocomplete\'s real value');
return;
}
this.input = input;
if(!'attributeKey' in this.autoCompleteSelectField.dataset) {
console.error('attributeKey dataset value not set');
return;
}
this.attributeKey = this.autoCompleteSelectField.dataset.attributeKey;
if(!'autosaveUrl' in this.autoCompleteSelectField.dataset) {
console.error('autosaveUrl dataset value not set');
return;
}
this.autosaveUrl = this.autoCompleteSelectField.dataset.autosaveUrl;
if(!'maxItemsToSelect' in this.autoCompleteSelectField.dataset) {
console.error('maxItemsToSelect dataset value not set');
return;
}
this.maxItemsToSelect = this.autoCompleteSelectField.dataset.maxItemsToSelect;
if(!'itemData' in this.autoCompleteSelectField.dataset) {
console.error('itemData dataset value not set');
return;
}
this.dataSet = JSON.parse(this.autoCompleteSelectField.dataset.itemdata);
if(!'values' in this.autoCompleteSelectField.dataset) {
console.error('values dataset value not set');
return;
}
this.valuesFromAttribute = this.autoCompleteSelectField.dataset.values.split(',');
let openElement = this.input.parentElement.querySelector('.open');
if(!openElement) {
console.error('Could not find autocomplete open element. It should be a sibling of the input')
}
this.openbutton = openElement;
let itemsWrapper = autocompleteInputElement.parentElement.parentElement.querySelector('div.items');
if(!itemsWrapper) {
console.error('Could not find a div HTMLElement with class "items" inside the autoCompletesInputElement\'s parent parent element')
}
this.itemsWrapper = itemsWrapper;
this.listenersEnabled = true;
this.constructedSuccessfully = true;
this.initialize();
};
initialize() {
if(this.constructedSuccessfully === false) {
console.error('AutoCompleteController was not constructed successfully. Could not initialize because of it');
return;
}
this.makeAutoCompleteInput();
this.addItems();
this.updateRealInput();
this.enableListeners(true);
this.enableAutoCompleteOpenButton();
}
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 autocomplete 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.
*/
makeAutoCompleteInput() {
let self = this;
let jqueryElement = $(this.autoCompleteSelectField);
return jqueryElement.autocomplete({
source: this.dataSet,
minLength: 0,
focus: function (event, ui) {
return false;
},
select: function (event, ui) {
$(this.autoCompleteSelectField).val("");
self.addItem(ui.item.id, ui.item.value);
self.updateRealInput();
self.autosaveIfNeeded();
self.enableListeners(true);
return false;
},
});
}
/**
* Make sure we can open the autocomplete field dropdown with the open button
*/
enableAutoCompleteOpenButton() {
let self = this;
this.openbutton.addEventListener('click', function(event) {
$(self.autoCompleteSelectField).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');
itemElement.innerHTML = value;
this.itemsWrapper.appendChild(itemElement);
let removeButton = document.createElement('span');
removeButton.classList.add('remove');
itemElement.dataset.id = id;
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) {
let self = this;
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.autoCompleteSelectField.classList.remove('hidden');
if(!enable) {
item.classList.add('readonly');
this.autoCompleteSelectField.classList.add('hidden');
this.openbutton.classList.add('hidden');
}
}
}
}