fradrive/static/js/utils/asyncTable.js
2019-04-07 23:31:04 +02:00

306 lines
9.0 KiB
JavaScript

(function collonadeClosure() {
'use strict';
/**
*
* Async Table Utility
* makes table filters, sorting and pagination behave asynchronously via AJAX calls
*
* Attribute: [none]
* (will be set up automatically on tables)
*
* Example usage:
* (regular table)
*/
var ASYNC_TABLE_UTIL_NAME = 'asyncTable';
var ASYNC_TABLE_UTIL_SELECTOR = 'table';
var ASYNC_TABLE_LOCAL_STORAGE_KEY = 'ASYNC_TABLE';
var ASYNC_TABLE_INITIALIZED_CLASS = 'async-table--initialized';
var INPUT_DEBOUNCE = 600;
var HEADER_HEIGHT = 80;
var RESET_OPTIONS = [ 'scrollTo' ];
var TABLE_FILTER_FORM_CLASS = 'table-filter-form';
var ASYNC_TABLE_LOADING_CLASS = 'async-table--loading';
var ASYNC_TABLE_FILTER_LOADING_CLASS = 'async-table-filter--loading';
var asyncTableUtil = function(element) {
var asyncTableHeader;
var asyncTableId;
var lastHorizontalPosition;
var currentTableUrl;
var ths = [];
var pageLinks = [];
var pagesizeForm;
var scrollTable;
var utilInstances = [];
function init() {
if (!element) {
throw new Error('Async Table utility cannot be setup without an element!');
}
// param asyncTableDbHeader
if (element.dataset.asyncTableDbHeader !== undefined) {
asyncTableHeader = element.dataset.asyncTableDbHeader;
}
asyncTableId = element.id;
console.log('asyncTable.init()', { asyncTableId });
setup();
// mark initialized
element.classList.add(ASYNC_TABLE_INITIALIZED_CLASS);
return {
name: ASYNC_TABLE_UTIL_NAME,
element: element,
destroy: function() {},
};
}
function setup() {
scrollTable = element.closest('.scrolltable');
if (!scrollTable) {
return false;
}
// sortable element headers
ths = Array.from(element.querySelectorAll('th.sortable')).map(function(th) {
return { element: th };
});
// pagination links
var pagination = element.querySelector('#' + asyncTableId + '-pagination');
if (pagination) {
pageLinks = Array.from(pagination.querySelectorAll('.page-link')).map(function(link) {
return { element: link };
});
}
// pagesize form
pagesizeForm = element.querySelector('#' + asyncTableId + '-pagesize-form');
// // filter
// var filterForm = element.querySelector('.' + TABLE_FILTER_FORM_CLASS);
// if (filterForm) {
// options.updateTableFrom = updateTableFrom;
// utilInstances.push(window.utils.setup('asyncTableFilter', filterForm, options));
// }
processLocalStorage();
setupListeners();
element.classList.add(ASYNC_TABLE_INITIALIZED_CLASS);
}
function reset() {
removeListeners();
element.classList.remove(ASYNC_TABLE_INITIALIZED_CLASS);
}
function processLocalStorage() {
var scrollTo = getLocalStorageParameter('scrollTo');
if (scrollTo && scrollTable) {
window.scrollTo(scrollTo);
scrollTo = null;
}
var horizPos = getLocalStorageParameter('horizPos');
if (horizPos && scrollTable) {
scrollTable.scrollLeft = lastHorizontalPosition;
lastHorizontalPosition = null;
}
}
function setupListeners() {
ths.forEach(function(th) {
th.clickHandler = function(event) {
var boundClickHandler = clickHandler.bind(this);
var horizPos = (scrollTable || {}).scrollLeft;
lastHorizontalPosition = (scrollTable || {}).scrollLeft;
boundClickHandler(event, { horizPos });
};
th.element.addEventListener('click', th.clickHandler);
});
pageLinks.forEach(function(link) {
link.clickHandler = function(event) {
var boundClickHandler = clickHandler.bind(this);
var tableBoundingRect = scrollTable.getBoundingClientRect();
if (tableBoundingRect.top < HEADER_HEIGHT) {
var scrollTo = {
top: (scrollTable.offsetTop || 0) - HEADER_HEIGHT,
left: scrollTable.offsetLeft || 0,
behavior: 'smooth',
};
setLocalStorageParameter('scrollTo', JSON.stringify(scrollTo));
}
boundClickHandler(event, tableOptions);
}
link.element.addEventListener('click', link.clickHandler);
});
if (pagesizeForm) {
var pagesizeSelect = pagesizeForm.querySelector('[name=' + asyncTableId + '-pagesize]');
pagesizeSelect.addEventListener('change', changePagesizeHandler);
}
}
function removeListeners() {
ths.forEach(function(th) {
th.element.removeEventListener('click', th.clickHandler);
});
pageLinks.forEach(function(link) {
link.element.removeEventListener('click', link.clickHandler);
});
if (pagesizeForm) {
var pagesizeSelect = pagesizeForm.querySelector('[name=' + asyncTableId + '-pagesize]')
pagesizeSelect.removeEventListener('change', changePagesizeHandler);
}
}
function clickHandler(event, tableOptions) {
event.preventDefault();
var url = getClickDestination(this);
if (!url.match(/^http/)) {
url = new URL(window.location.origin + window.location.pathname + getClickDestination(this));
}
updateTableFrom(url, tableOptions);
}
function getClickDestination(el) {
if (!el.querySelector('a')) {
return '';
}
return el.querySelector('a').getAttribute('href');
}
function changePagesizeHandler(event) {
var pagesizeParamKey = asyncTableId + '-pagesize';
var pageParamKey = asyncTableId + '-page';
var url = new URL(currentTableUrl || window.location.href);
url.searchParams.set(pagesizeParamKey, event.target.value);
url.searchParams.set(pageParamKey, 0);
updateTableFrom(url);
}
// fetches new sorted element from url with params and replaces contents of current element
function updateTableFrom(url, tableOptions, callback) {
if (!HttpClient) {
throw new Error('HttpClient not found!');
}
element.classList.add(ASYNC_TABLE_LOADING_CLASS);
tableOptions = tableOptions || {};
var headers = {
'Accept': 'text/html',
[asyncTableHeader]: asyncTableId
};
HttpClient.get(url, headers).then(function(response) {
if (!response.ok) {
throw new Error('Looks like there was a problem fetching ' + url.href + '. Status Code: ' + response.status);
}
return response.text();
}).then(function(data) {
currentTableUrl = url.href;
reset();
updateWrapperContents(data, tableOptions);
if (callback && typeof callback === 'function') {
callback(element);
}
element.classList.remove(ASYNC_TABLE_LOADING_CLASS);
}).catch(function(err) {
console.error(err);
});
}
function updateWrapperContents(newHtml, tableOptions) {
tableOptions = tableOptions || {};
element.innerHTML = newHtml;
// merge global options and element specific options
// var resetOptions = {};
// Object.keys(options)
// .filter(function(key) {
// return !RESET_OPTIONS.includes(key);
// })
// .forEach(function(key) {
// resetOptions[key] = options[key];
// });
// var combinedOptions = {};
// combinedOptions = Object.keys(tableOptions)
// .filter(function(key) {
// return tableOptions.hasOwnProperty(key);
// })
// .map(function(key) {
// return { key, value: tableOptions[key] }
// })
// .reduce(function(cumulatedOpts, opt) {
// cumulatedOpts[opt.key] = opt.value;
// return cumulatedOpts;
// }, resetOptions);
if (UtilRegistry) {
UtilRegistry.setupAll();
}
}
return init();
};
function setLocalStorageParameter(key, value) {
var currentLSState = JSON.parse(window.localStorage.getItem(ASYNC_TABLE_LOCAL_STORAGE_KEY)) || {};
currentLSState[key] = value;
window.localStorage.setItem(ASYNC_TABLE_LOCAL_STORAGE_KEY, JSON.stringify(currentLSState));
}
function getLocalStorageParameter(key) {
var currentLSState = JSON.parse(window.localStorage.getItem(ASYNC_TABLE_LOCAL_STORAGE_KEY)) || {};
return currentLSState[key];
}
// debounce function, taken from Underscore.js
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
// register async table utility
if (UtilRegistry) {
UtilRegistry.register({
name: ASYNC_TABLE_UTIL_NAME,
selector: ASYNC_TABLE_UTIL_SELECTOR,
setup: asyncTableUtil,
});
}
})();