// DocBox 4 - Streamline's PDF Lightbox Viewer
// This script finds all links ending with .pdf and opens them in a lightbox viewer
var domainDocumentLinkData = [];
var onDocAccessDomain = false;
const currentDomain = window.location.hostname.replace('www.', '').toLowerCase();
// Helper function to clean HubSpot tracking parameters from URLs
function cleanHubSpotParams(urlString) {
try {
const url = new URL(urlString);
const hubspotParams = ['__hstc', '__hssc', '__hsfp', '_hsenc', '_hsmi', 'hsCtaTracking'];
hubspotParams.forEach(param => url.searchParams.delete(param));
return url.href;
} catch (e) {
// If URL parsing fails, return the original URL
return urlString;
}
}
window.setTimeout(function () {
if (currentDomain == 'docaccess.com' || currentDomain == ('localhost')) {
onDocAccessDomain = true;
initializeDocBox();
} else {
fetch(`https://docaccess.com/domains/${currentDomain}/domain.json`)
.then(response => response.json())
.then(data => {
domainDocumentLinkData = data;
initializeDocBox();
})
.catch(error => {
console.error("Error fetching domain data for DocAccess.", error);
});
}
}, 50);
function initializeDocBox() {
// Create the lightbox elements
function createLightbox() {
const lightbox = document.createElement('div');
lightbox.id = 'docbox-overlay';
lightbox.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
z-index: 9999999;
display: none;
justify-content: center;
align-items: center;
`;
const container = document.createElement('div');
container.id = 'docbox-container';
container.style.cssText = `
width: 90%;
height: 90%;
background-color: transparent;
position: relative;
border-radius: 5px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.5);
`;
const closeBtn = document.createElement('button');
closeBtn.id = 'docbox-close';
closeBtn.innerHTML = '×';
closeBtn.style.cssText = `
position: absolute;
top: 5px;
right: -40px;
font-size: 20px;
background: rgb(0, 0, 0);
border: none;
color: white;
cursor: pointer;
z-index: 10001;
border-radius: 5px;
box-shadow: rgba(0, 0, 0, 0.4) 5px 5px 20px;
width: 40px;
`;
closeBtn.addEventListener('click', closeLightbox);
const iframe = document.createElement('iframe');
iframe.id = 'docbox-iframe';
iframe.style.cssText = `
width: 100%;
height: 100%;
border: none;
border-radius: 5px;
`;
iframe.allow = "autoplay; encrypted-media";
iframe.allowFullscreen = true;
container.appendChild(closeBtn);
container.appendChild(iframe);
lightbox.appendChild(container);
document.body.appendChild(lightbox);
// Close lightbox when clicking outside the container
lightbox.addEventListener('click', function (e) {
if (e.target === lightbox) {
closeLightbox();
}
});
// Close lightbox with Escape key
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && lightbox.style.display === 'flex') {
closeLightbox();
}
});
}
// Open the lightbox with the given PDF URL
function openLightbox(pdfUrl, direct = false, domain = '') {
const lightbox = document.getElementById('docbox-overlay');
const iframe = document.getElementById('docbox-iframe');
let scriptRoot = 'https://docaccess.com';
if (currentDomain == ('localhost')) {
scriptRoot = '';
}
// Set the iframe source to the docviewer with the PDF URL as a parameter
let docviewerUrl = direct ? pdfUrl :
`${scriptRoot}/docviewer.html?url=${encodeURIComponent(pdfUrl)}`;
if (domain) {
docviewerUrl += `&domain=${domain}`;
}
iframe.src = docviewerUrl;
// Display the lightbox
lightbox.style.display = 'flex';
// Prevent scrolling on the body
document.body.style.overflow = 'hidden';
}
// Close the lightbox
function closeLightbox() {
const lightbox = document.getElementById('docbox-overlay');
const iframe = document.getElementById('docbox-iframe');
// Hide the lightbox
lightbox.style.display = 'none';
// Clear the iframe source
iframe.src = '';
// Re-enable scrolling on the body
document.body.style.overflow = '';
// Remove the tooltip
const tooltip = document.getElementById('transcript-view-tooltip');
if (tooltip) {
tooltip.parentNode.removeChild(tooltip);
}
}
// Find all PDF links and attach click handlers
// using common patterns found on institution and government websites
// including those that do not end in .pdf
function initPdfLinks(event, selector = `
a:not(.no-docbox)[href*=".pdf"],
a:not(.no-docbox)[href*="DocumentCenter/View/"],
a:not(.no-docbox)[href*="/ViewFile/"],
a:not(.no-docbox)[href*="/showpublisheddocument/"],
a.docbox-enable,
a.docbox-direct
`) {
// Create the lightbox if it doesn't exist
if (!document.getElementById('docbox-overlay')) {
createLightbox();
}
// Find all links ending with .pdf
const pdfLinks = document.querySelectorAll(selector);
// Attach click handlers to each PDF link
pdfLinks.forEach(link => {
// Prevent double initialization
if (link.classList.contains('docaccess-activated')) {
return;
}
// Clean HubSpot parameters from the link URL
const cleanedHref = cleanHubSpotParams(link.href);
const docHashPromise = generateSHA256(cleanedHref).then(hash => {
link.dataset.urlHash = hash;
if (link.classList.contains('docbox-direct') ||
link.classList.contains('docbox-enable') ||
domainDocumentLinkData.hasOwnProperty(hash) && domainDocumentLinkData[hash] == true
) {
link.dataset.urlStatus = 'enabled';
// OK to process this link, will continue
}
else if (domainDocumentLinkData.hasOwnProperty(hash) && domainDocumentLinkData[hash] == false) {
// don't process this link, it is defined as disabled
link.dataset.urlStatus = 'disabled';
return;
}
else if (!domainDocumentLinkData.hasOwnProperty(hash)) {
// send to discover and return;
link.dataset.urlStatus = 'new';
sendDomainLink(currentDomain, cleanedHref);
return;
}
// Remove any existing event listeners
const oldLink = link.cloneNode(true);
link.parentNode.replaceChild(oldLink, link);
link = oldLink;
link.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
// Prevent target="_blank" behavior
if (this.target === '_blank') {
this.target = '';
}
// Clean the URL again when opening
const cleanUrl = cleanHubSpotParams(this.href);
if (this.dataset.docaccessDomain) {
// Open the lightbox with the given PDF URL unless it has the docbox-direct class, in which case open it directly
openLightbox(cleanUrl, this.classList.contains('docbox-direct'), this.dataset.docaccessDomain);
} else {
// Open the lightbox with the given PDF URL unless it has the docbox-direct class, in which case open it directly
openLightbox(cleanUrl, this.classList.contains('docbox-direct'));
}
return false;
});
link.removeAttribute('target');
link.classList.add('docaccess-activated');
});
});
}
function sendDomainLink(domain, hrefRaw) {
// Initialize the Set if it doesn't exist
if (!sendDomainLink.sentLinks) {
sendDomainLink.sentLinks = new Set();
}
// Clean HubSpot parameters before processing
const cleanedHref = cleanHubSpotParams(hrefRaw);
const domainName = domain.replace(/^www\./, '').replace(/[^a-zA-Z0-9.-]/g, '').toLowerCase();
if (!domainName || !cleanedHref) return;
if (domainDocumentLinkData.length === 0) {
// No links in payload, don't send more
return;
}
let url;
try {
url = new URL(cleanedHref);
} catch (e) {
console.log('error parsing URL', cleanedHref, e);
return;
}
//Keeps the input reasonable, but might miss some links
//if (!url.href.includes('.pdf')) return;
//don't send docviewer links
if (url.href.includes('/docviewer')) return;
// Check if this link has already been sent (using cleaned URL)
if (sendDomainLink.sentLinks && sendDomainLink.sentLinks.has(cleanedHref)) {
return;
}
// Add this link to the sent links Set
sendDomainLink.sentLinks.add(cleanedHref);
fetch(`https://admin.docaccess.com/api/v1/domain_links`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
domain: domain,
url: cleanedHref
})
}).then(() => console.log('sent new domain link', domain, cleanedHref)).catch(error => console.log('error sending domain link', domain, cleanedHref));
}
// Initialize when the DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initPdfLinks);
} else {
initPdfLinks();
}
// Re-initialize when new content might be loaded
window.addEventListener('load', initPdfLinks);
// Expose the API for manual initialization
window.DocBox = {
init: initPdfLinks,
open: openLightbox,
close: closeLightbox
};
}
// Calculate SHA-256 hash of the link URL
async function generateSHA256(message) {
// 1. Convert the message string to a Uint8Array
const encoder = new TextEncoder();
const data = encoder.encode(message);
// 2. Compute the SHA-256 hash, which returns a Promise that resolves to an ArrayBuffer
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
// 3. Convert the ArrayBuffer to a byte array
const hashArray = Array.from(new Uint8Array(hashBuffer));
// 4. Convert bytes to hex string
const hashHex = hashArray
.map(byte => byte.toString(16).padStart(2, '0'))
.join('');
return hashHex;
}