(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, }); } })();