| <!doctype html> |
| <html> |
| <head> |
| <link rel="stylesheet" href="chrome://resources/css/text_defaults.css"> |
| <style> |
| body { |
| display: flex; |
| flex-direction: column; |
| margin: 0; |
| } |
| |
| #header, |
| #footer { |
| display: flex; |
| flex: none; |
| } |
| |
| #header { |
| align-items: flex-start; |
| padding-top: 15pt; |
| } |
| |
| #footer { |
| align-items: flex-end; |
| padding-bottom: 15pt; |
| } |
| |
| #content { |
| flex: auto; |
| } |
| |
| .left { |
| flex: none; |
| padding-left: 24pt; /* csschecker-disable-line left-right */ |
| padding-right: 6pt; /* csschecker-disable-line left-right */ |
| } |
| |
| .center { |
| flex: auto; |
| padding-left: 24pt; /* csschecker-disable-line left-right */ |
| padding-right: 24pt; /* csschecker-disable-line left-right */ |
| text-align: center; |
| } |
| |
| .right { |
| flex: none; |
| /* historically does not account for RTL */ |
| padding-left: 6pt; /* csschecker-disable-line left-right */ |
| padding-right: 24pt; /* csschecker-disable-line left-right */ |
| } |
| |
| .grow { |
| flex: auto; |
| } |
| |
| .text { |
| font-size: 8pt; |
| overflow: hidden; |
| text-overflow: ellipsis; |
| white-space: nowrap; |
| } |
| </style> |
| <script> |
| |
| function getComputedStyleAsFloat(style, value) { |
| return parseFloat(style.getPropertyValue(value).slice(0, -2)); |
| } |
| |
| function elementIntersects(element, topPos, bottomPos, leftPos, rightPos) { |
| const rect = element.getBoundingClientRect(); |
| const style = window.getComputedStyle(element); |
| |
| // Only consider the size of |element|, so remove the padding from |rect|. |
| // The padding is used for positioning. |
| rect.top += getComputedStyleAsFloat(style, 'padding-top'); |
| rect.bottom -= getComputedStyleAsFloat(style, 'padding-bottom'); |
| rect.left += getComputedStyleAsFloat(style, 'padding-left'); |
| rect.right -= getComputedStyleAsFloat(style, 'padding-right'); |
| return leftPos < rect.right && rightPos > rect.left && topPos < rect.bottom && |
| bottomPos > rect.top; |
| } |
| |
| function setupHeaderFooterTemplate(options) { |
| const body = document.querySelector('body'); |
| const header = document.querySelector('#header'); |
| const footer = document.querySelector('#footer'); |
| |
| body.style.width = `${options.width}px`; |
| body.style.height = `${options.height}px`; |
| header.style.height = `${options.topMargin}px`; |
| footer.style.height = `${options.bottomMargin}px`; |
| |
| const topMargin = options.topMargin; |
| const bottomMargin = options.height - options.bottomMargin; |
| const leftMargin = options.leftMargin; |
| const rightMargin = options.width - options.rightMargin; |
| |
| header.innerHTML = options['headerTemplate'] || ` |
| <div class='date text left'></div> |
| <div class='title text center'></div>`; |
| footer.innerHTML = options['footerTemplate'] || ` |
| <div class='url text left grow'></div> |
| <div class='text right'> |
| <span class='pageNumber'></span>/<span class='totalPages'></span> |
| </div>`; |
| |
| const date = new Date(options.date); |
| const formatter = |
| new Intl.DateTimeFormat( |
| navigator.languages[0].split('@')[0], |
| { dateStyle: 'short', timeStyle: 'short' }); |
| options.date = formatter.format(date); |
| for (const cssClass of ['date', 'title', 'url', 'pageNumber', 'totalPages']) { |
| for (const element of document.querySelectorAll(`.${cssClass}`)) { |
| element.textContent = options[cssClass]; |
| } |
| } |
| for (const element of document.querySelectorAll(`.text`)) { |
| if (options.isRtl && |
| !element.classList.contains('url') && |
| !element.classList.contains('title')) { |
| element.dir = 'rtl'; |
| } |
| if (elementIntersects(element, topMargin, bottomMargin, leftMargin, |
| rightMargin)) { |
| element.style.visibility = 'hidden'; |
| } |
| } |
| } |
| |
| </script> |
| </head> |
| <body> |
| <div id="header"></div> |
| <div id="content"></div> |
| <div id="footer"></div> |
| </body> |
| </html> |