|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
function label(element, value) {
|
|
|
|
if (element.hasAttribute('value')) {
|
|
|
|
if (value !== undefined) {
|
|
|
|
element.setAttribute('value', value)
|
|
|
|
} else {
|
|
|
|
return element.getAttribute('value')
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (value !== undefined) {
|
|
|
|
element.textContent = value
|
|
|
|
} else {
|
|
|
|
return element.textContent
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function countdownDeleteButton(event) {
|
|
|
|
const btn = event.target
|
|
|
|
const countdown = btn.getAttribute('data-countdown')
|
|
|
|
|
|
|
|
if (countdown !== '1') {
|
|
|
|
event.preventDefault()
|
|
|
|
btn.classList.add('__on-countdown')
|
|
|
|
|
|
|
|
const hasCountdown = countdown !== null
|
|
|
|
const newCountdown = hasCountdown ? countdown - 1 : 3
|
|
|
|
|
|
|
|
if (!hasCountdown) {
|
|
|
|
btn.setAttribute('data-label', label(btn))
|
|
|
|
}
|
|
|
|
|
|
|
|
label(btn, newCountdown)
|
|
|
|
btn.setAttribute('data-countdown', newCountdown)
|
|
|
|
setTimeout(countdownDeleteButton, 1000, event)
|
|
|
|
} else {
|
|
|
|
label(btn, btn.getAttribute('data-label'))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
document.querySelectorAll('.delete-btn').forEach(function (btn) {
|
|
|
|
btn.addEventListener('click', countdownDeleteButton)
|
|
|
|
})
|
|
|
|
|
|
|
|
function dynamicTables() {
|
|
|
|
const pageSize = 40
|
|
|
|
|
|
|
|
const range = (x, y) =>
|
|
|
|
x > y ? [] : [x, ...range(x + 1, y)];
|
|
|
|
const visibleRows = page =>
|
|
|
|
range(page * pageSize, (page + 1) * pageSize - 1)
|
|
|
|
const tblRows = tbl =>
|
|
|
|
[...tbl.tBodies[0].rows]
|
|
|
|
const partial11 = (fn, arg1) =>
|
|
|
|
function (arg2) {
|
|
|
|
fn(arg1, arg2)
|
|
|
|
}
|
|
|
|
const partial21 = (fn, arg1, arg2) =>
|
|
|
|
function(arg3) {
|
|
|
|
fn(arg1, arg2, arg3)
|
|
|
|
}
|
|
|
|
|
|
|
|
function toggleVisibilities(tbl, ixsToShow) {
|
|
|
|
tblRows(tbl).forEach(function (row, ix) {
|
|
|
|
row.style.display = ixsToShow.includes(ix) ? 'table-row' : 'none';
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function setCurrPage(ctx, currPageIx) {
|
|
|
|
const rows = tblRows(ctx.tbl).length
|
|
|
|
const pages = Math.ceil(rows / pageSize)
|
|
|
|
|
|
|
|
ctx.rowsLbls.forEach(function (elem) {
|
|
|
|
elem.innerText = rows
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx.pageLbls.forEach(function (elem) {
|
|
|
|
elem.innerText = (currPageIx + 1) + ' / ' + pages
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx.tbl.setAttribute('data-page', currPageIx)
|
|
|
|
}
|
|
|
|
|
|
|
|
function changePage(ctx, ix) {
|
|
|
|
toggleVisibilities(ctx.tbl, visibleRows(ix))
|
|
|
|
setCurrPage(ctx, ix)
|
|
|
|
}
|
|
|
|
|
|
|
|
function enablePagination(ctx, on) {
|
|
|
|
[...ctx.nextBtns, ...ctx.prevBtns].forEach(function (btn) {
|
|
|
|
btn.disabled = !on
|
|
|
|
})
|
|
|
|
ctx.pageLbls.forEach(function (elem) {
|
|
|
|
elem.style.display = on ? 'inline' : 'none'
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function prevButton() {
|
|
|
|
const btn = document.createElement('button')
|
|
|
|
btn.classList.add('prev')
|
|
|
|
btn.classList.add('pg-btn')
|
|
|
|
btn.innerText = 'Prev'
|
|
|
|
return btn;
|
|
|
|
}
|
|
|
|
|
|
|
|
function nextButton() {
|
|
|
|
const btn = document.createElement('button')
|
|
|
|
btn.classList.add('next')
|
|
|
|
btn.classList.add('pg-btn')
|
|
|
|
btn.innerText = 'Next'
|
|
|
|
return btn
|
|
|
|
}
|
|
|
|
|
|
|
|
function currPage() {
|
|
|
|
const rowInfo = document.createElement('span')
|
|
|
|
rowInfo.classList.add('row-info')
|
|
|
|
const rows = document.createElement('span')
|
|
|
|
rows.classList.add('rows')
|
|
|
|
rows.innerText = '0'
|
|
|
|
const page = document.createElement('span')
|
|
|
|
page.classList.add('page')
|
|
|
|
page.innerText = '1'
|
|
|
|
|
|
|
|
rowInfo.insertAdjacentElement('afterbegin', rows)
|
|
|
|
rowInfo.insertAdjacentElement('afterbegin', page)
|
|
|
|
|
|
|
|
return rowInfo
|
|
|
|
}
|
|
|
|
|
|
|
|
function searchBox() {
|
|
|
|
const input = document.createElement('input')
|
|
|
|
input.classList.add('search')
|
|
|
|
input.type = 'search'
|
|
|
|
input.placeholder = 'search...'
|
|
|
|
return input
|
|
|
|
}
|
|
|
|
|
|
|
|
function onSearch(ctx, evt) {
|
|
|
|
const term = evt.target.value.toLocaleLowerCase()
|
|
|
|
if (term.length === 0) {
|
|
|
|
changePage(ctx, 0)
|
|
|
|
enablePagination(ctx, true)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
enablePagination(ctx, false)
|
|
|
|
|
|
|
|
const contents = tblRows(ctx.tbl).map(function (row) {
|
|
|
|
return Array.from(row.cells).reduce(function (carr, cell) {
|
|
|
|
return carr + cell.innerText.toLocaleLowerCase()
|
|
|
|
}, "")
|
|
|
|
})
|
|
|
|
|
|
|
|
const ixs = []
|
|
|
|
contents.forEach(function (rowContent, ix) {
|
|
|
|
if (rowContent.match(term)) {
|
|
|
|
ixs.push(ix)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx.rowsLbls.forEach(function (elem) {
|
|
|
|
elem.innerText = ixs.length
|
|
|
|
})
|
|
|
|
|
|
|
|
toggleVisibilities(ctx.tbl, ixs)
|
|
|
|
}
|
|
|
|
|
|
|
|
function onPrevClick(ctx) {
|
|
|
|
const page = ctx.tbl.getAttribute('data-page') * 1
|
|
|
|
if (page > 0) {
|
|
|
|
changePage(ctx, page - 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function onNextClick(ctx) {
|
|
|
|
const page = ctx.tbl.getAttribute('data-page') * 1
|
|
|
|
const lastRowNum = (page + 1) * pageSize
|
|
|
|
if (lastRowNum < tblRows(ctx.tbl).length) {
|
|
|
|
changePage(ctx, page + 1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function buildContext(tbl) {
|
|
|
|
const currPage1 = currPage()
|
|
|
|
const currPage2 = currPage()
|
|
|
|
const ctx = {
|
|
|
|
tbl: tbl,
|
|
|
|
currPages: [currPage1, currPage2],
|
|
|
|
pageLbls: [currPage1.children[0], currPage2.children[0]],
|
|
|
|
rowsLbls: [currPage1.children[1], currPage2.children[1]],
|
|
|
|
searchBox: searchBox(),
|
|
|
|
prevBtns: [prevButton(), prevButton()],
|
|
|
|
nextBtns: [nextButton(), nextButton()]
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.searchBox.addEventListener('input', partial11(onSearch, ctx))
|
|
|
|
ctx.prevBtns.forEach(function (btn) {
|
|
|
|
btn.addEventListener('click', partial11(onPrevClick, ctx))
|
|
|
|
})
|
|
|
|
ctx.nextBtns.forEach(function (btn) {
|
|
|
|
btn.addEventListener('click', partial11(onNextClick, ctx))
|
|
|
|
})
|
|
|
|
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
function addToolbar(tbl) {
|
|
|
|
const ctx = buildContext(tbl)
|
|
|
|
|
|
|
|
const toolbarTop = document.createElement('section')
|
|
|
|
toolbarTop.classList.add('tbl-toolbar')
|
|
|
|
toolbarTop.insertAdjacentElement('beforeend', ctx.currPages[0])
|
|
|
|
toolbarTop.insertAdjacentElement('beforeend', ctx.searchBox)
|
|
|
|
toolbarTop.insertAdjacentElement('beforeend', ctx.prevBtns[0])
|
|
|
|
toolbarTop.insertAdjacentElement('beforeend', ctx.nextBtns[0])
|
|
|
|
|
|
|
|
const toolbarBottom = document.createElement('section')
|
|
|
|
toolbarBottom.classList.add('tbl-toolbar')
|
|
|
|
toolbarBottom.insertAdjacentElement('beforeend', ctx.currPages[1])
|
|
|
|
toolbarBottom.insertAdjacentElement('beforeend', ctx.prevBtns[1])
|
|
|
|
toolbarBottom.insertAdjacentElement('beforeend', ctx.nextBtns[1])
|
|
|
|
|
|
|
|
tbl.insertAdjacentElement('afterend', toolbarBottom)
|
|
|
|
tbl.insertAdjacentElement('beforebegin', toolbarTop)
|
|
|
|
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
function flipSortDir(dir) {
|
|
|
|
return (dir || 'asc') === 'asc' ? 'desc' : 'asc'
|
|
|
|
}
|
|
|
|
|
|
|
|
function clearSortAttributes(tbl) {
|
|
|
|
[...tbl.tHead.rows[0].cells]
|
|
|
|
.forEach(th => th.removeAttribute('data-sort'))
|
|
|
|
}
|
|
|
|
|
|
|
|
function onSort(ctx, ix, event) {
|
|
|
|
const tbl = ctx.tbl
|
|
|
|
console.debug(tbl, ix, event)
|
|
|
|
|
|
|
|
const sortDir = event.target.getAttribute('data-sort') || 'asc'
|
|
|
|
const sortMod = sortDir === 'asc' ? -1 : 1;
|
|
|
|
|
|
|
|
const rows = tblRows(tbl)
|
|
|
|
const newTbody = document.createElement('tbody')
|
|
|
|
|
|
|
|
rows.sort((row1, row2) => {
|
|
|
|
const val1 = row1.cells[ix].textContent
|
|
|
|
const val2 = row2.cells[ix].textContent
|
|
|
|
return sortMod * (val1 > val2 ? 1
|
|
|
|
: val2 > val1 ? -1
|
|
|
|
: 0);
|
|
|
|
})
|
|
|
|
|
|
|
|
rows.forEach(row => newTbody.appendChild(row))
|
|
|
|
tbl.replaceChild(newTbody, tbl.tBodies[0])
|
|
|
|
|
|
|
|
clearSortAttributes(tbl)
|
|
|
|
event.target.setAttribute('data-sort', flipSortDir(sortDir))
|
|
|
|
changePage(ctx, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
function enableSorting(ctx) {
|
|
|
|
const ths = ctx.tbl.querySelectorAll('thead th')
|
|
|
|
ths.forEach((th, ix) => {
|
|
|
|
th.addEventListener('click', partial21(onSort, ctx, ix))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
document.querySelectorAll('table').forEach(function (tbl) {
|
|
|
|
const ctx = addToolbar(tbl)
|
|
|
|
changePage(ctx, 0)
|
|
|
|
enableSorting(ctx)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
dynamicTables()
|
|
|
|
})
|