Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save lunitrixx/cc700338d8957f190e0cfa64d00b9e43 to your computer and use it in GitHub Desktop.
Save lunitrixx/cc700338d8957f190e0cfa64d00b9e43 to your computer and use it in GitHub Desktop.

Revisions

  1. @theinvensi theinvensi renamed this gist Dec 15, 2021. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  2. @theinvensi theinvensi created this gist Dec 15, 2021.
    130 changes: 130 additions & 0 deletions index.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,130 @@
    class RepeatTableHeadersHandler extends Paged.Handler {
    constructor(chunker, polisher, caller) {
    super(chunker, polisher, caller)
    this.splitTablesRefs = []
    }

    afterPageLayout(pageElement, page, breakToken, chunker) {
    this.chunker = chunker
    this.splitTablesRefs = []

    if (breakToken) {
    const node = breakToken.node
    const tables = this.findAllAncestors(node, "table")
    if (node.tagName === "TABLE") tables.push(node)

    if (tables.length > 0) {
    this.splitTablesRefs = tables.map(t => t.dataset.ref)

    let thead = node.tagName === "THEAD" ? node : this.findFirstAncestor(node, "thead")
    if (thead) {
    let lastTheadNode = thead.hasChildNodes() ? thead.lastChild : thead
    breakToken.node = this.nodeAfter(lastTheadNode, chunker.source)
    }

    this.hideEmptyTables(pageElement, node)
    }
    }
    }

    hideEmptyTables(pageElement, breakTokenNode) {
    this.splitTablesRefs.forEach(ref => {
    let table = pageElement.querySelector("[data-ref='" + ref + "']")
    if (table) {
    let sourceBody = table.querySelector("tbody > tr")
    if (!sourceBody || this.refEquals(sourceBody.firstElementChild, breakTokenNode)) {
    table.style.visibility = "hidden"
    table.style.position = "absolute"
    let lineSpacer = table.nextSibling
    if (lineSpacer) {
    lineSpacer.style.visibility = "hidden"
    lineSpacer.style.position = "absolute"
    }
    }
    }
    })
    }

    refEquals(a, b) {
    return a && a.dataset && b && b.dataset && a.dataset.ref === b.dataset.ref
    }

    findFirstAncestor(element, selector) {
    while (element.parentNode && element.parentNode.nodeType === 1) {
    if (element.parentNode.matches(selector)) return element.parentNode
    element = element.parentNode
    }
    return null
    }

    findAllAncestors(element, selector) {
    const ancestors = []
    while (element.parentNode && element.parentNode.nodeType === 1) {
    if (element.parentNode.matches(selector)) ancestors.unshift(element.parentNode)
    element = element.parentNode
    }
    return ancestors
    }

    layout(rendered, layout) {
    this.splitTablesRefs.forEach(ref => {
    const renderedTable = rendered.querySelector("[data-ref='" + ref + "']")
    if (renderedTable) {
    if (!renderedTable.getAttribute("repeated-headers")) {
    const sourceTable = this.chunker.source.querySelector("[data-ref='" + ref + "']")
    this.repeatColgroup(sourceTable, renderedTable)
    this.repeatTHead(sourceTable, renderedTable)
    renderedTable.setAttribute("repeated-headers", true)
    }
    }
    })
    }

    repeatColgroup(sourceTable, renderedTable) {
    let colgroup = sourceTable.querySelectorAll("colgroup")
    let firstChild = renderedTable.firstChild
    colgroup.forEach((colgroup) => {
    let clonedColgroup = colgroup.cloneNode(true)
    renderedTable.insertBefore(clonedColgroup, firstChild)
    })
    }

    repeatTHead(sourceTable, renderedTable) {
    let thead = sourceTable.querySelector("thead")
    if (thead) {
    let clonedThead = thead.cloneNode(true)
    renderedTable.insertBefore(clonedThead, renderedTable.firstChild)
    }
    }

    nodeAfter(node, limiter) {
    if (limiter && node === limiter) return
    let significantNode = this.nextSignificantNode(node)
    if (significantNode) return significantNode
    if (node.parentNode) {
    while ((node = node.parentNode)) {
    if (limiter && node === limiter) return
    significantNode = this.nextSignificantNode(node)
    if (significantNode) return significantNode
    }
    }
    }

    nextSignificantNode(sib) {
    while ((sib = sib.nextSibling)) { if (!this.isIgnorable(sib)) return sib }
    return null
    }

    isIgnorable(node) {
    return (
    (node.nodeType === 8)
    || ((node.nodeType === 3) && this.isAllWhitespace(node))
    )
    }

    isAllWhitespace(node) {
    return !(/[^\t\n\r ]/.test(node.textContent))
    }
    }

    Paged.registerHandlers(RepeatTableHeadersHandler)