Created
February 25, 2023 17:00
-
-
Save dKvale/387bfa067261bb4355017ee599b5c97a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Production steps of ECMA-262, Edition 5, 15.4.4.18 | |
// Reference: http://es5.github.io/#x15.4.4.18 | |
if (!Array.prototype.forEach) { | |
Array.prototype.forEach = function(callback, thisArg) { | |
var T, k; | |
if (this === null) { | |
throw new TypeError(' this is null or not defined'); | |
} | |
// 1. Let O be the result of calling toObject() passing the | |
// |this| value as the argument. | |
var O = Object(this); | |
// 2. Let lenValue be the result of calling the Get() internal | |
// method of O with the argument "length". | |
// 3. Let len be toUint32(lenValue). | |
var len = O.length >>> 0; | |
// 4. If isCallable(callback) is false, throw a TypeError exception. | |
// See: http://es5.github.com/#x9.11 | |
if (typeof callback !== "function") { | |
throw new TypeError(callback + ' is not a function'); | |
} | |
// 5. If thisArg was supplied, let T be thisArg; else let | |
// T be undefined. | |
if (arguments.length > 1) { | |
T = thisArg; | |
} | |
// 6. Let k be 0 | |
k = 0; | |
// 7. Repeat, while k < len | |
while (k < len) { | |
var kValue; | |
// a. Let Pk be ToString(k). | |
// This is implicit for LHS operands of the in operator | |
// b. Let kPresent be the result of calling the HasProperty | |
// internal method of O with argument Pk. | |
// This step can be combined with c | |
// c. If kPresent is true, then | |
if (k in O) { | |
// i. Let kValue be the result of calling the Get internal | |
// method of O with argument Pk. | |
kValue = O[k]; | |
// ii. Call the Call internal method of callback with T as | |
// the this value and argument list containing kValue, k, and O. | |
callback.call(T, kValue, k, O); | |
} | |
// d. Increase k by 1. | |
k++; | |
} | |
// 8. return undefined | |
}; | |
} | |
// Production steps of ECMA-262, Edition 5, 15.4.4.19 | |
// Reference: http://es5.github.io/#x15.4.4.19 | |
if (!Array.prototype.map) { | |
Array.prototype.map = function(callback, thisArg) { | |
var T, A, k; | |
if (this == null) { | |
throw new TypeError(' this is null or not defined'); | |
} | |
// 1. Let O be the result of calling ToObject passing the |this| | |
// value as the argument. | |
var O = Object(this); | |
// 2. Let lenValue be the result of calling the Get internal | |
// method of O with the argument "length". | |
// 3. Let len be ToUint32(lenValue). | |
var len = O.length >>> 0; | |
// 4. If IsCallable(callback) is false, throw a TypeError exception. | |
// See: http://es5.github.com/#x9.11 | |
if (typeof callback !== 'function') { | |
throw new TypeError(callback + ' is not a function'); | |
} | |
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined. | |
if (arguments.length > 1) { | |
T = thisArg; | |
} | |
// 6. Let A be a new array created as if by the expression new Array(len) | |
// where Array is the standard built-in constructor with that name and | |
// len is the value of len. | |
A = new Array(len); | |
// 7. Let k be 0 | |
k = 0; | |
// 8. Repeat, while k < len | |
while (k < len) { | |
var kValue, mappedValue; | |
// a. Let Pk be ToString(k). | |
// This is implicit for LHS operands of the in operator | |
// b. Let kPresent be the result of calling the HasProperty internal | |
// method of O with argument Pk. | |
// This step can be combined with c | |
// c. If kPresent is true, then | |
if (k in O) { | |
// i. Let kValue be the result of calling the Get internal | |
// method of O with argument Pk. | |
kValue = O[k]; | |
// ii. Let mappedValue be the result of calling the Call internal | |
// method of callback with T as the this value and argument | |
// list containing kValue, k, and O. | |
mappedValue = callback.call(T, kValue, k, O); | |
// iii. Call the DefineOwnProperty internal method of A with arguments | |
// Pk, Property Descriptor | |
// { Value: mappedValue, | |
// Writable: true, | |
// Enumerable: true, | |
// Configurable: true }, | |
// and false. | |
// In browsers that support Object.defineProperty, use the following: | |
// Object.defineProperty(A, k, { | |
// value: mappedValue, | |
// writable: true, | |
// enumerable: true, | |
// configurable: true | |
// }); | |
// For best browser support, use the following: | |
A[k] = mappedValue; | |
} | |
// d. Increase k by 1. | |
k++; | |
} | |
// 9. return A | |
return A; | |
}; | |
} | |
var PagedTable = function (pagedTable) { | |
var me = this; | |
var source = function(pagedTable) { | |
var sourceElems = [].slice.call(pagedTable.children).filter(function(e) { | |
return e.hasAttribute("data-pagedtable-source"); | |
}); | |
if (sourceElems === null || sourceElems.length !== 1) { | |
throw("A single data-pagedtable-source was not found"); | |
} | |
return JSON.parse(sourceElems[0].innerHTML); | |
}(pagedTable); | |
var options = function(source) { | |
var options = typeof(source.options) !== "undefined" && | |
source.options !== null ? source.options : {}; | |
var columns = typeof(options.columns) !== "undefined" ? options.columns : {}; | |
var rows = typeof(options.rows) !== "undefined" ? options.rows : {}; | |
var positiveIntOrNull = function(value) { | |
return parseInt(value) >= 0 ? parseInt(value) : null; | |
}; | |
return { | |
pages: positiveIntOrNull(options.pages), | |
rows: { | |
min: positiveIntOrNull(rows.min), | |
max: positiveIntOrNull(rows.max), | |
total: positiveIntOrNull(rows.total) | |
}, | |
columns: { | |
min: positiveIntOrNull(columns.min), | |
max: positiveIntOrNull(columns.max), | |
total: positiveIntOrNull(columns.total) | |
} | |
}; | |
}(source); | |
var Measurer = function() { | |
// set some default initial values that will get adjusted in runtime | |
me.measures = { | |
padding: 12, | |
character: 8, | |
height: 15, | |
defaults: true | |
}; | |
me.calculate = function(measuresCell) { | |
if (!me.measures.defaults) | |
return; | |
var measuresCellStyle = window.getComputedStyle(measuresCell, null); | |
var newPadding = parsePadding(measuresCellStyle.paddingLeft) + | |
parsePadding(measuresCellStyle.paddingRight); | |
var sampleString = "ABCDEFGHIJ0123456789"; | |
var newCharacter = Math.ceil(measuresCell.clientWidth / sampleString.length); | |
if (newPadding <= 0 || newCharacter <= 0) | |
return; | |
me.measures.padding = newPadding; | |
me.measures.character = newCharacter; | |
me.measures.height = measuresCell.clientHeight; | |
me.measures.defaults = false; | |
}; | |
return me; | |
}; | |
var Page = function(data, options) { | |
var me = this; | |
var defaults = { | |
max: 7, | |
rows: 10 | |
}; | |
var totalPages = function() { | |
return Math.ceil(data.length / me.rows); | |
}; | |
me.number = 0; | |
me.max = options.pages !== null ? options.pages : defaults.max; | |
me.visible = me.max; | |
me.rows = options.rows.min !== null ? options.rows.min : defaults.rows; | |
me.total = totalPages(); | |
me.setRows = function(newRows) { | |
me.rows = newRows; | |
me.total = totalPages(); | |
}; | |
me.setPageNumber = function(newPageNumber) { | |
if (newPageNumber < 0) newPageNumber = 0; | |
if (newPageNumber >= me.total) newPageNumber = me.total - 1; | |
me.number = newPageNumber; | |
}; | |
me.setVisiblePages = function(visiblePages) { | |
me.visible = Math.min(me.max, visiblePages); | |
me.setPageNumber(me.number); | |
}; | |
me.getVisiblePageRange = function() { | |
var start = me.number - Math.max(Math.floor((me.visible - 1) / 2), 0); | |
var end = me.number + Math.floor(me.visible / 2) + 1; | |
var pageCount = me.total; | |
if (start < 0) { | |
var diffToStart = 0 - start; | |
start += diffToStart; | |
end += diffToStart; | |
} | |
if (end > pageCount) { | |
var diffToEnd = end - pageCount; | |
start -= diffToEnd; | |
end -= diffToEnd; | |
} | |
start = start < 0 ? 0 : start; | |
end = end >= pageCount ? pageCount : end; | |
var first = false; | |
var last = false; | |
if (start > 0 && me.visible > 1) { | |
start = start + 1; | |
first = true; | |
} | |
if (end < pageCount && me.visible > 2) { | |
end = end - 1; | |
last = true; | |
} | |
return { | |
first: first, | |
start: start, | |
end: end, | |
last: last | |
}; | |
}; | |
me.getRowStart = function() { | |
var rowStart = page.number * page.rows; | |
if (rowStart < 0) | |
rowStart = 0; | |
return rowStart; | |
}; | |
me.getRowEnd = function() { | |
var rowStart = me.getRowStart(); | |
return Math.min(rowStart + me.rows, data.length); | |
}; | |
me.getPaddingRows = function() { | |
var rowStart = me.getRowStart(); | |
var rowEnd = me.getRowEnd(); | |
return data.length > me.rows ? me.rows - (rowEnd - rowStart) : 0; | |
}; | |
}; | |
var Columns = function(data, columns, options) { | |
var me = this; | |
me.defaults = { | |
min: 5 | |
}; | |
me.number = 0; | |
me.visible = 0; | |
me.total = columns.length; | |
me.subset = []; | |
me.padding = 0; | |
me.min = options.columns.min !== null ? options.columns.min : me.defaults.min; | |
me.max = options.columns.max !== null ? options.columns.max : null; | |
me.widths = {}; | |
var widthsLookAhead = Math.max(100, options.rows.min); | |
var paddingColChars = 10; | |
me.emptyNames = function() { | |
columns.forEach(function(column) { | |
if (columns.label !== null && columns.label !== "") | |
return false; | |
}); | |
return true; | |
}; | |
var parsePadding = function(value) { | |
return parseInt(value) >= 0 ? parseInt(value) : 0; | |
}; | |
me.calculateWidths = function(measures) { | |
columns.forEach(function(column) { | |
var maxChars = Math.max( | |
column.label.toString().length, | |
column.type.toString().length | |
); | |
for (var idxRow = 0; idxRow < Math.min(widthsLookAhead, data.length); idxRow++) { | |
maxChars = Math.max(maxChars, data[idxRow][column.name.toString()].length); | |
} | |
me.widths[column.name] = { | |
// width in characters | |
chars: maxChars, | |
// width for the inner html columns | |
inner: maxChars * measures.character, | |
// width adding outer styles like padding | |
outer: maxChars * measures.character + measures.padding | |
}; | |
}); | |
}; | |
me.getWidth = function() { | |
var widthOuter = 0; | |
for (var idxCol = 0; idxCol < me.subset.length; idxCol++) { | |
var columnName = me.subset[idxCol].name; | |
widthOuter = widthOuter + me.widths[columnName].outer; | |
} | |
widthOuter = widthOuter + me.padding * paddingColChars * measurer.measures.character; | |
if (me.hasMoreLeftColumns()) { | |
widthOuter = widthOuter + columnNavigationWidthPX + measurer.measures.padding; | |
} | |
if (me.hasMoreRightColumns()) { | |
widthOuter = widthOuter + columnNavigationWidthPX + measurer.measures.padding; | |
} | |
return widthOuter; | |
}; | |
me.updateSlice = function() { | |
if (me.number + me.visible >= me.total) | |
me.number = me.total - me.visible; | |
if (me.number < 0) me.number = 0; | |
me.subset = columns.slice(me.number, Math.min(me.number + me.visible, me.total)); | |
me.subset = me.subset.map(function(column) { | |
Object.keys(column).forEach(function(colKey) { | |
column[colKey] = column[colKey] === null ? "" : column[colKey].toString(); | |
}); | |
column.width = null; | |
return column; | |
}); | |
}; | |
me.setVisibleColumns = function(columnNumber, newVisibleColumns, paddingCount) { | |
me.number = columnNumber; | |
me.visible = newVisibleColumns; | |
me.padding = paddingCount; | |
me.updateSlice(); | |
}; | |
me.incColumnNumber = function(increment) { | |
me.number = me.number + increment; | |
}; | |
me.setColumnNumber = function(newNumber) { | |
me.number = newNumber; | |
}; | |
me.setPaddingCount = function(newPadding) { | |
me.padding = newPadding; | |
}; | |
me.getPaddingCount = function() { | |
return me.padding; | |
}; | |
me.hasMoreLeftColumns = function() { | |
return me.number > 0; | |
}; | |
me.hasMoreRightColumns = function() { | |
return me.number + me.visible < me.total; | |
}; | |
me.updateSlice(0); | |
return me; | |
}; | |
var data = source.data; | |
var page = new Page(data, options); | |
var measurer = new Measurer(data, options); | |
var columns = new Columns(data, source.columns, options); | |
var table = null; | |
var tableDiv = null; | |
var header = null; | |
var footer = null; | |
var tbody = null; | |
// Caches pagedTable.clientWidth, specially for webkit | |
var cachedPagedTableClientWidth = null; | |
var onChangeCallbacks = []; | |
var clearSelection = function() { | |
if(document.selection && document.selection.empty) { | |
document.selection.empty(); | |
} else if(window.getSelection) { | |
var sel = window.getSelection(); | |
sel.removeAllRanges(); | |
} | |
}; | |
var columnNavigationWidthPX = 5; | |
var renderColumnNavigation = function(increment, backwards) { | |
var arrow = document.createElement("div"); | |
arrow.setAttribute("style", | |
"border-top: " + columnNavigationWidthPX + "px solid transparent;" + | |
"border-bottom: " + columnNavigationWidthPX + "px solid transparent;" + | |
"border-" + (backwards ? "right" : "left") + ": " + columnNavigationWidthPX + "px solid;"); | |
var header = document.createElement("th"); | |
header.appendChild(arrow); | |
header.setAttribute("style", | |
"cursor: pointer;" + | |
"vertical-align: middle;" + | |
"min-width: " + columnNavigationWidthPX + "px;" + | |
"width: " + columnNavigationWidthPX + "px;"); | |
header.onclick = function() { | |
columns.incColumnNumber(backwards ? -1 : increment); | |
me.animateColumns(backwards); | |
renderFooter(); | |
clearSelection(); | |
triggerOnChange(); | |
}; | |
return header; | |
}; | |
var maxColumnWidth = function(width) { | |
var padding = 80; | |
var columnMax = Math.max(cachedPagedTableClientWidth - padding, 0); | |
return parseInt(width) > 0 ? | |
Math.min(columnMax, parseInt(width)) + "px" : | |
columnMax + "px"; | |
}; | |
var clearHeader = function() { | |
var thead = pagedTable.querySelectorAll("thead")[0]; | |
thead.innerHTML = ""; | |
}; | |
var renderHeader = function(clear) { | |
cachedPagedTableClientWidth = pagedTable.clientWidth; | |
var fragment = document.createDocumentFragment(); | |
header = document.createElement("tr"); | |
fragment.appendChild(header); | |
if (columns.number > 0) | |
header.appendChild(renderColumnNavigation(-columns.visible, true)); | |
columns.subset = columns.subset.map(function(columnData) { | |
var column = document.createElement("th"); | |
column.setAttribute("align", columnData.align); | |
column.style.textAlign = columnData.align; | |
column.style.maxWidth = maxColumnWidth(null); | |
if (columnData.width) { | |
column.style.minWidth = | |
column.style.maxWidth = maxColumnWidth(columnData.width); | |
} | |
var columnName = document.createElement("div"); | |
columnName.setAttribute("class", "pagedtable-header-name"); | |
if (columnData.label === "") { | |
columnName.innerHTML = " "; | |
} | |
else { | |
columnName.appendChild(document.createTextNode(columnData.label)); | |
} | |
column.appendChild(columnName); | |
var columnType = document.createElement("div"); | |
columnType.setAttribute("class", "pagedtable-header-type"); | |
if (columnData.type === "") { | |
columnType.innerHTML = " "; | |
} | |
else { | |
columnType.appendChild(document.createTextNode("<" + columnData.type + ">")); | |
} | |
column.appendChild(columnType); | |
header.appendChild(column); | |
columnData.element = column; | |
return columnData; | |
}); | |
for (var idx = 0; idx < columns.getPaddingCount(); idx++) { | |
var paddingCol = document.createElement("th"); | |
paddingCol.setAttribute("class", "pagedtable-padding-col"); | |
header.appendChild(paddingCol); | |
} | |
if (columns.number + columns.visible < columns.total) | |
header.appendChild(renderColumnNavigation(columns.visible, false)); | |
if (typeof(clear) == "undefined" || clear) clearHeader(); | |
var thead = pagedTable.querySelectorAll("thead")[0]; | |
thead.appendChild(fragment); | |
}; | |
me.animateColumns = function(backwards) { | |
var thead = pagedTable.querySelectorAll("thead")[0]; | |
var headerOld = thead.querySelectorAll("tr")[0]; | |
var tbodyOld = table.querySelectorAll("tbody")[0]; | |
me.fitColumns(backwards); | |
renderHeader(false); | |
header.style.opacity = "0"; | |
header.style.transform = backwards ? "translateX(-30px)" : "translateX(30px)"; | |
header.style.transition = "transform 200ms linear, opacity 200ms"; | |
header.style.transitionDelay = "0"; | |
renderBody(false); | |
if (headerOld) { | |
headerOld.style.position = "absolute"; | |
headerOld.style.transform = "translateX(0px)"; | |
headerOld.style.opacity = "1"; | |
headerOld.style.transition = "transform 100ms linear, opacity 100ms"; | |
headerOld.setAttribute("class", "pagedtable-remove-head"); | |
if (headerOld.style.transitionEnd) { | |
headerOld.addEventListener("transitionend", function() { | |
var headerOldByClass = thead.querySelector(".pagedtable-remove-head"); | |
if (headerOldByClass) thead.removeChild(headerOldByClass); | |
}); | |
} | |
else { | |
thead.removeChild(headerOld); | |
} | |
} | |
if (tbodyOld) table.removeChild(tbodyOld); | |
tbody.style.opacity = "0"; | |
tbody.style.transition = "transform 200ms linear, opacity 200ms"; | |
tbody.style.transitionDelay = "0ms"; | |
// force relayout | |
window.getComputedStyle(header).opacity; | |
window.getComputedStyle(tbody).opacity; | |
if (headerOld) { | |
headerOld.style.transform = backwards ? "translateX(20px)" : "translateX(-30px)"; | |
headerOld.style.opacity = "0"; | |
} | |
header.style.transform = "translateX(0px)"; | |
header.style.opacity = "1"; | |
tbody.style.opacity = "1"; | |
} | |
me.onChange = function(callback) { | |
onChangeCallbacks.push(callback); | |
}; | |
var triggerOnChange = function() { | |
onChangeCallbacks.forEach(function(onChange) { | |
onChange(); | |
}); | |
}; | |
var clearBody = function() { | |
if (tbody) { | |
table.removeChild(tbody); | |
tbody = null; | |
} | |
}; | |
var renderBody = function(clear) { | |
cachedPagedTableClientWidth = pagedTable.clientWidth | |
var fragment = document.createDocumentFragment(); | |
var pageData = data.slice(page.getRowStart(), page.getRowEnd()); | |
pageData.forEach(function(dataRow, idxRow) { | |
var htmlRow = document.createElement("tr"); | |
htmlRow.setAttribute("class", (idxRow % 2 !==0) ? "even" : "odd"); | |
if (columns.hasMoreLeftColumns()) | |
htmlRow.appendChild(document.createElement("td")); | |
columns.subset.forEach(function(columnData) { | |
var cellName = columnData.name; | |
var dataCell = dataRow[cellName]; | |
var htmlCell = document.createElement("td"); | |
if (dataCell === "NA") htmlCell.setAttribute("class", "pagedtable-na-cell"); | |
if (dataCell === "__NA__") dataCell = "NA"; | |
var cellText = document.createTextNode(dataCell); | |
htmlCell.appendChild(cellText); | |
if (dataCell.length > 50) { | |
htmlCell.setAttribute("title", dataCell); | |
} | |
htmlCell.setAttribute("align", columnData.align); | |
htmlCell.style.textAlign = columnData.align; | |
htmlCell.style.maxWidth = maxColumnWidth(null); | |
if (columnData.width) { | |
htmlCell.style.minWidth = htmlCell.style.maxWidth = maxColumnWidth(columnData.width); | |
} | |
htmlRow.appendChild(htmlCell); | |
}); | |
for (var idx = 0; idx < columns.getPaddingCount(); idx++) { | |
var paddingCol = document.createElement("td"); | |
paddingCol.setAttribute("class", "pagedtable-padding-col"); | |
htmlRow.appendChild(paddingCol); | |
} | |
if (columns.hasMoreRightColumns()) | |
htmlRow.appendChild(document.createElement("td")); | |
fragment.appendChild(htmlRow); | |
}); | |
for (var idxPadding = 0; idxPadding < page.getPaddingRows(); idxPadding++) { | |
var paddingRow = document.createElement("tr"); | |
var paddingCellRow = document.createElement("td"); | |
paddingCellRow.innerHTML = " "; | |
paddingCellRow.setAttribute("colspan", "100%"); | |
paddingRow.appendChild(paddingCellRow); | |
fragment.appendChild(paddingRow); | |
} | |
if (typeof(clear) == "undefined" || clear) clearBody(); | |
tbody = document.createElement("tbody"); | |
tbody.appendChild(fragment); | |
table.appendChild(tbody); | |
}; | |
var getLabelInfo = function() { | |
var pageStart = page.getRowStart(); | |
var pageEnd = page.getRowEnd(); | |
var totalRows = data.length; | |
var totalRowsLabel = options.rows.total ? options.rows.total : totalRows; | |
var totalRowsLabelFormat = totalRowsLabel.toString().replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,'); | |
var infoText = (pageStart + 1) + "-" + pageEnd + " of " + totalRowsLabelFormat + " rows"; | |
if (totalRows < page.rows) { | |
infoText = totalRowsLabel + " row" + (totalRows != 1 ? "s" : ""); | |
} | |
if (columns.total > columns.visible) { | |
var totalColumnsLabel = options.columns.total ? options.columns.total : columns.total; | |
infoText = infoText + " | " + (columns.number + 1) + "-" + | |
(Math.min(columns.number + columns.visible, columns.total)) + | |
" of " + totalColumnsLabel + " columns"; | |
} | |
return infoText; | |
}; | |
var clearFooter = function() { | |
footer = pagedTable.querySelectorAll("div.pagedtable-footer")[0]; | |
footer.innerHTML = ""; | |
return footer; | |
}; | |
var createPageLink = function(idxPage) { | |
var pageLink = document.createElement("a"); | |
pageLinkClass = idxPage === page.number ? "pagedtable-index pagedtable-index-current" : "pagedtable-index"; | |
pageLink.setAttribute("class", pageLinkClass); | |
pageLink.setAttribute("data-page-index", idxPage); | |
pageLink.onclick = function() { | |
page.setPageNumber(parseInt(this.getAttribute("data-page-index"))); | |
renderBody(); | |
renderFooter(); | |
triggerOnChange(); | |
}; | |
pageLink.appendChild(document.createTextNode(idxPage + 1)); | |
return pageLink; | |
} | |
var renderFooter = function() { | |
footer = clearFooter(); | |
var next = document.createElement("a"); | |
next.appendChild(document.createTextNode("Next")); | |
next.onclick = function() { | |
page.setPageNumber(page.number + 1); | |
renderBody(); | |
renderFooter(); | |
triggerOnChange(); | |
}; | |
if (data.length > page.rows) footer.appendChild(next); | |
var pageNumbers = document.createElement("div"); | |
pageNumbers.setAttribute("class", "pagedtable-indexes"); | |
var pageRange = page.getVisiblePageRange(); | |
if (pageRange.first) { | |
var pageLink = createPageLink(0); | |
pageNumbers.appendChild(pageLink); | |
var pageSeparator = document.createElement("div"); | |
pageSeparator.setAttribute("class", "pagedtable-index-separator-left"); | |
pageSeparator.appendChild(document.createTextNode("...")) | |
pageNumbers.appendChild(pageSeparator); | |
} | |
for (var idxPage = pageRange.start; idxPage < pageRange.end; idxPage++) { | |
var pageLink = createPageLink(idxPage); | |
pageNumbers.appendChild(pageLink); | |
} | |
if (pageRange.last) { | |
var pageSeparator = document.createElement("div"); | |
pageSeparator.setAttribute("class", "pagedtable-index-separator-right"); | |
pageSeparator.appendChild(document.createTextNode("...")) | |
pageNumbers.appendChild(pageSeparator); | |
var pageLink = createPageLink(page.total - 1); | |
pageNumbers.appendChild(pageLink); | |
} | |
if (data.length > page.rows) footer.appendChild(pageNumbers); | |
var previous = document.createElement("a"); | |
previous.appendChild(document.createTextNode("Previous")); | |
previous.onclick = function() { | |
page.setPageNumber(page.number - 1); | |
renderBody(); | |
renderFooter(); | |
triggerOnChange(); | |
}; | |
if (data.length > page.rows) footer.appendChild(previous); | |
var infoLabel = document.createElement("div"); | |
infoLabel.setAttribute("class", "pagedtable-info"); | |
infoLabel.setAttribute("title", getLabelInfo()); | |
infoLabel.appendChild(document.createTextNode(getLabelInfo())); | |
footer.appendChild(infoLabel); | |
var enabledClass = "pagedtable-index-nav"; | |
var disabledClass = "pagedtable-index-nav pagedtable-index-nav-disabled"; | |
previous.setAttribute("class", page.number <= 0 ? disabledClass : enabledClass); | |
next.setAttribute("class", (page.number + 1) * page.rows >= data.length ? disabledClass : enabledClass); | |
}; | |
var measuresCell = null; | |
var renderMeasures = function() { | |
var measuresTable = document.createElement("table"); | |
measuresTable.style.visibility = "hidden"; | |
measuresTable.style.position = "absolute"; | |
measuresTable.style.whiteSpace = "nowrap"; | |
measuresTable.style.height = "auto"; | |
measuresTable.style.width = "auto"; | |
var measuresRow = document.createElement("tr"); | |
measuresTable.appendChild(measuresRow); | |
measuresCell = document.createElement("td"); | |
var sampleString = "ABCDEFGHIJ0123456789"; | |
measuresCell.appendChild(document.createTextNode(sampleString)); | |
measuresRow.appendChild(measuresCell); | |
tableDiv.appendChild(measuresTable); | |
} | |
me.init = function() { | |
tableDiv = document.createElement("div"); | |
pagedTable.appendChild(tableDiv); | |
var pagedTableClass = data.length > 0 ? | |
"pagedtable pagedtable-not-empty" : | |
"pagedtable pagedtable-empty"; | |
if (columns.total == 0 || (columns.emptyNames() && data.length == 0)) { | |
pagedTableClass = pagedTableClass + " pagedtable-empty-columns"; | |
} | |
tableDiv.setAttribute("class", pagedTableClass); | |
renderMeasures(); | |
measurer.calculate(measuresCell); | |
columns.calculateWidths(measurer.measures); | |
table = document.createElement("table"); | |
table.setAttribute("cellspacing", "0"); | |
table.setAttribute("class", "table table-condensed"); | |
tableDiv.appendChild(table); | |
table.appendChild(document.createElement("thead")); | |
var footerDiv = document.createElement("div"); | |
footerDiv.setAttribute("class", "pagedtable-footer"); | |
tableDiv.appendChild(footerDiv); | |
// if the host has not yet provided horizontal space, render hidden | |
if (tableDiv.clientWidth <= 0) { | |
tableDiv.style.opacity = "0"; | |
} | |
me.render(); | |
// retry seizing columns later if the host has not provided space | |
function retryFit() { | |
if (tableDiv.clientWidth <= 0) { | |
setTimeout(retryFit, 100); | |
} else { | |
me.render(); | |
triggerOnChange(); | |
} | |
} | |
if (tableDiv.clientWidth <= 0) { | |
retryFit(); | |
} | |
}; | |
var registerWidths = function() { | |
columns.subset = columns.subset.map(function(column) { | |
column.width = columns.widths[column.name].inner; | |
return column; | |
}); | |
}; | |
var parsePadding = function(value) { | |
return parseInt(value) >= 0 ? parseInt(value) : 0; | |
}; | |
me.fixedHeight = function() { | |
return options.rows.max != null; | |
} | |
me.fitRows = function() { | |
if (me.fixedHeight()) | |
return; | |
measurer.calculate(measuresCell); | |
var rows = options.rows.min !== null ? options.rows.min : 0; | |
var headerHeight = header !== null && header.offsetHeight > 0 ? header.offsetHeight : 0; | |
var footerHeight = footer !== null && footer.offsetHeight > 0 ? footer.offsetHeight : 0; | |
if (pagedTable.offsetHeight > 0) { | |
var availableHeight = pagedTable.offsetHeight - headerHeight - footerHeight; | |
rows = Math.floor((availableHeight) / measurer.measures.height); | |
} | |
rows = options.rows.min !== null ? Math.max(options.rows.min, rows) : rows; | |
page.setRows(rows); | |
} | |
// The goal of this function is to add as many columns as possible | |
// starting from left-to-right, when the right most limit is reached | |
// it tries to add columns from the left as well. | |
// | |
// When startBackwards is true columns are added from right-to-left | |
me.fitColumns = function(startBackwards) { | |
measurer.calculate(measuresCell); | |
columns.calculateWidths(measurer.measures); | |
if (tableDiv.clientWidth > 0) { | |
tableDiv.style.opacity = 1; | |
} | |
var visibleColumns = tableDiv.clientWidth <= 0 ? Math.max(columns.min, 1) : 1; | |
var columnNumber = columns.number; | |
var paddingCount = 0; | |
// track a list of added columns as we build the visible ones to allow us | |
// to remove columns when they don't fit anymore. | |
var columnHistory = []; | |
var lastTableHeight = 0; | |
var backwards = startBackwards; | |
var tableDivStyle = window.getComputedStyle(tableDiv, null); | |
var tableDivPadding = parsePadding(tableDivStyle.paddingLeft) + | |
parsePadding(tableDivStyle.paddingRight); | |
var addPaddingCol = false; | |
var currentWidth = 0; | |
while (true) { | |
columns.setVisibleColumns(columnNumber, visibleColumns, paddingCount); | |
currentWidth = columns.getWidth(); | |
if (tableDiv.clientWidth - tableDivPadding < currentWidth) { | |
break; | |
} | |
columnHistory.push({ | |
columnNumber: columnNumber, | |
visibleColumns: visibleColumns, | |
paddingCount: paddingCount | |
}); | |
if (columnHistory.length > 100) { | |
console.error("More than 100 tries to fit columns, aborting"); | |
break; | |
} | |
if (columns.max !== null && | |
columns.visible + columns.getPaddingCount() >= columns.max) { | |
break; | |
} | |
// if we run out of right-columns | |
if (!backwards && columnNumber + columns.visible >= columns.total) { | |
// if we started adding right-columns, try adding left-columns | |
if (!startBackwards && columnNumber > 0) { | |
backwards = true; | |
} | |
else if (columns.min === null || visibleColumns + columns.getPaddingCount() >= columns.min) { | |
break; | |
} | |
else { | |
paddingCount = paddingCount + 1; | |
} | |
} | |
// if we run out of left-columns | |
if (backwards && columnNumber == 0) { | |
// if we started adding left-columns, try adding right-columns | |
if (startBackwards && columnNumber + columns.visible < columns.total) { | |
backwards = false; | |
} | |
else if (columns.min === null || visibleColumns + columns.getPaddingCount() >= columns.min) { | |
break; | |
} | |
else { | |
paddingCount = paddingCount + 1; | |
} | |
} | |
// when moving backwards try fitting left columns first | |
if (backwards && columnNumber > 0) { | |
columnNumber = columnNumber - 1; | |
} | |
if (columnNumber + visibleColumns < columns.total) { | |
visibleColumns = visibleColumns + 1; | |
} | |
} | |
var lastRenderableColumn = { | |
columnNumber: columnNumber, | |
visibleColumns: visibleColumns, | |
paddingCount: paddingCount | |
}; | |
if (columnHistory.length > 0) { | |
lastRenderableColumn = columnHistory[columnHistory.length - 1]; | |
} | |
columns.setVisibleColumns( | |
lastRenderableColumn.columnNumber, | |
lastRenderableColumn.visibleColumns, | |
lastRenderableColumn.paddingCount); | |
if (pagedTable.offsetWidth > 0) { | |
page.setVisiblePages(Math.max(Math.ceil(1.0 * (pagedTable.offsetWidth - 250) / 40), 2)); | |
} | |
registerWidths(); | |
}; | |
me.fit = function(startBackwards) { | |
me.fitRows(); | |
me.fitColumns(startBackwards); | |
} | |
me.render = function() { | |
me.fitColumns(false); | |
// render header/footer to measure height accurately | |
renderHeader(); | |
renderFooter(); | |
me.fitRows(); | |
renderBody(); | |
// re-render footer to match new rows | |
renderFooter(); | |
} | |
var resizeLastWidth = -1; | |
var resizeLastHeight = -1; | |
var resizeNewWidth = -1; | |
var resizeNewHeight = -1; | |
var resizePending = false; | |
me.resize = function(newWidth, newHeight) { | |
function resizeDelayed() { | |
resizePending = false; | |
if ( | |
(resizeNewWidth !== resizeLastWidth) || | |
(!me.fixedHeight() && resizeNewHeight !== resizeLastHeight) | |
) { | |
resizeLastWidth = resizeNewWidth; | |
resizeLastHeight = resizeNewHeight; | |
setTimeout(resizeDelayed, 200); | |
resizePending = true; | |
} else { | |
me.render(); | |
triggerOnChange(); | |
resizeLastWidth = -1; | |
resizeLastHeight = -1; | |
} | |
} | |
resizeNewWidth = newWidth; | |
resizeNewHeight = newHeight; | |
if (!resizePending) resizeDelayed(); | |
}; | |
}; | |
var PagedTableDoc; | |
(function (PagedTableDoc) { | |
var allPagedTables = []; | |
PagedTableDoc.initAll = function() { | |
allPagedTables = []; | |
var pagedTables = [].slice.call(document.querySelectorAll('[data-pagedtable="false"],[data-pagedtable=""]')); | |
pagedTables.forEach(function(pagedTable, idx) { | |
pagedTable.setAttribute("data-pagedtable", "true"); | |
pagedTable.setAttribute("pagedtable-page", 0); | |
pagedTable.setAttribute("class", "pagedtable-wrapper"); | |
var pagedTableInstance = new PagedTable(pagedTable); | |
pagedTableInstance.init(); | |
allPagedTables.push(pagedTableInstance); | |
}); | |
}; | |
PagedTableDoc.resizeAll = function() { | |
allPagedTables.forEach(function(pagedTable) { | |
pagedTable.render(); | |
}); | |
}; | |
window.addEventListener("resize", PagedTableDoc.resizeAll); | |
return PagedTableDoc; | |
})(PagedTableDoc || (PagedTableDoc = {})); | |
window.onload = function() { | |
PagedTableDoc.initAll(); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment