var id = document.location.pathname.split('/')[1]; var annotations = []; var currentPage = 1, rendered = false var highlightInactive = true var selectedAnnotation const SELECTION = 0 const HIGHLIGHT = 1 var div = document.createElement("div") div.innerHTML = ` ` var cropFile = div.querySelector("#cropFile") document.querySelector('#toolbarViewerRight').insertBefore(cropFile, document.querySelector('#toolbarViewerRight').firstChild) // secondary menu div.innerHTML = ` ` var secondaryCropFile = div.querySelector("#secondaryCropFile") document.querySelector('#secondaryToolbarButtonContainer').insertBefore( secondaryCropFile, document.querySelector('#secondaryToolbarButtonContainer').firstChild ) function initOverlay() { document.querySelectorAll('#cropFile,.secondaryToolbarButton.cropFile').forEach(btn => { btn.addEventListener('click', event=> { if (highlightInactive) { event.target.style.background = 'red' highlightInactive = false document.querySelectorAll('.crop-overlay.inactive').forEach(element => { element.classList.remove('inactive') }) } else { event.target.style.background = '' highlightInactive = true document.querySelectorAll('.crop-overlay').forEach(element => { element.classList.add('inactive') }) } }) }) PDFViewerApplication.initializedPromise.then(function() { PDFViewerApplication.pdfViewer.eventBus.on("pagesinit", function(event) { /* document.querySelector('#viewerContainer').addEventListener('scroll', event => { if (window.parent && window.parent.postMessage) { if (first) { first = false } else { window.parent.postMessage({event: 'scrolled', top: event.target.scrollTop}) } } }) */ }) PDFViewerApplication.pdfViewer.eventBus.on("pagerender", function(event) { var page = event.pageNumber.toString() var div = event.source.div var overlay = document.createElement('div') overlay.classList.add('crop-overlay') overlay.id = 'overlay' + page if (highlightInactive) { overlay.classList.add('inactive') } div.appendChild(overlay) renderHighlightSelectionOverlay(overlay, id, page, event.source) var highlights = document.createElement('div') highlights.classList.add('highlights') highlights.id = 'highlights' + page var canvas = document.createElement('canvas') highlights.appendChild(canvas) div.appendChild(highlights) renderHighlights(page) }) PDFViewerApplication.eventBus.on('pagerendered', function(event) { loadAnnotations(event.pageNumber) }) }) } document.addEventListener('DOMContentLoaded', function() { window.PDFViewerApplication ? initOverlay() : document.addEventListener("webviewerloaded", initOverlay) }) Ox.load({ 'UI': { loadCSS: false } }, function() { Ox.$parent.bindMessage(function(data, event) { if (event == 'selectAnnotation') { var annotation = annotations.filter(function(a) { return a.id == data.id })[0] var delay = 0 if ( annotation && annotation.page && PDFViewerApplication.pdfViewer.currentPageNumber != annotation.page ) { PDFViewerApplication.pdfViewer.currentPageNumber = annotation.page; delay = 250 } annotation && setTimeout(function() { var el = document.querySelector('.a' + annotation.id); if (el && !isInView(el)) { document.querySelector('#viewerContainer').scrollTop = el.offsetTop + el.parentElement.offsetTop - 64; } }, delay) var oldSelection = selectedAnnotation selectedAnnotation = data.id selectAnnotation(data.id) if (oldSelection) { var old = annotations.filter(function(a) { return a.id == oldSelection })[0] if (old && old.type == HIGHLIGHT) { renderHighlights(old.page) } } } else if (event == 'addAnnotation') { createAnnotation() } else if (event == 'addAnnotations') { if (data.replace) { document.querySelectorAll('.oml-annotation').forEach(function(a) { a.remove() }) annotations = [] } data.annotations.forEach(function(annotation) { annotations.push(annotation) if (annotation.type == HIGHLIGHT) { renderHighlights(annotation.page) } else { renderAnnotation(annotation) } }) } else if (event == 'removeAnnotation') { removeAnnotation(data.id) } else { console.log('got', event, 'data', data) } }) }) window.addEventListener('keydown', function(event) { if (event.key == 'Delete') { var selected = document.querySelector('.oml-annotation.selected') if (selected) { removeAnnotation(selected.dataset.id) } } else if (event.key == 'n' || event.keyCode == 13) { if (event.target.nodeName != 'INPUT') { var selected = document.querySelector('.oml-annotation.selected') if (!window.getSelection().isCollapsed) { createAnnotation() } else if (selected) { console.log('editNote?', selected.dataset.id) } event.stopPropagation() event.preventDefault() } } }) window.addEventListener('mouseup', function(event) { var selection = window.getSelection() if (selection.isCollapsed) { Ox.$parent.postMessage('selectText', false) } else { Ox.$parent.postMessage('selectText', true) } }) function getHighlight() { var pageNumber = PDFViewerApplication.pdfViewer.currentPageNumber; var pageIndex = pageNumber - 1; var page = PDFViewerApplication.pdfViewer.getPageView(pageIndex); var pageRect = page.canvas.getClientRects()[0]; var selection = window.getSelection() var selectionRects = selection.getRangeAt(0).getClientRects(); var viewport = page.viewport; var selected = Array.from(selectionRects).map(function (r) { return viewport.convertToPdfPoint(r.left - pageRect.x, r.top - pageRect.y).concat( viewport.convertToPdfPoint(r.right - pageRect.x, r.bottom - pageRect.y)); }); var text = selection.toString(); var position = [pageNumber].concat(Ox.sort(selected.map(function(c) { return [c[1], c[0]]}))[0]); return { type: SELECTION, page: pageNumber, pageLabel: PDFViewerApplication.pdfViewer.currentPageLabel, position: position, coords: selected, text: text, id: Ox.SHA1(pageNumber.toString() + JSON.stringify(selected)) }; } function createAnnotation() { var selected = document.querySelector('.oml-annotation.selected') if (selected) { deselectAllAnnotations() } var annot = getHighlight() renderAnnotation(annot) addAnnotation(annot) window.getSelection().removeAllRanges(); } function renderAnnotation(annotation) { var pageIndex = annotation.page - 1; var page = PDFViewerApplication.pdfViewer.getPageView(pageIndex); if (!page || !page.canvas) { setTimeout(function() { renderAnnotation(annotation) }, 50) return } var pageElement = page.canvas.parentElement.parentElement; var viewport = page.viewport; if (annotation.type == HIGHLIGHT) { renderHighlights(annotation.page) } else if (annotation.coords) { pageElement.querySelectorAll('.oml-annotation').forEach(el => el.remove()) annotation.coords.forEach(function (rect) { var bounds = viewport.convertToViewportRectangle(rect); var el = document.createElement('div'); el.classList.add('oml-annotation') el.classList.add('a' + annotation.id) el.classList.add('page' + annotation.page) el.dataset.id = annotation.id el.setAttribute('style', 'position: absolute; background-color: yellow;opacity:0.3;' + 'left:' + Math.min(bounds[0], bounds[2]) + 'px; top:' + Math.min(bounds[1], bounds[3]) + 'px;' + 'width:' + Math.abs(bounds[0] - bounds[2]) + 'px; height:' + Math.abs(bounds[1] - bounds[3]) + 'px;'); el.addEventListener('click', function() { if (!el.classList.contains('selected')) { selectAnnotation(annotation.id) Ox.$parent.postMessage('selectAnnotation', {id: annotation.id}) } }); pageElement.appendChild(el); }); } else { // console.log("annotation without position", annotation) } } function addAnnotation(annotation) { annotations.push(annotation) Ox.$parent.postMessage('addAnnotation', annotation) } function selectAnnotation(id) { document.querySelectorAll('.oml-annotation.selected').forEach(function(g) { g.classList.remove('selected') g.style.backgroundColor = 'yellow' }) document.querySelectorAll('.oml-annotation.a' + id).forEach(function(g) { g.classList.add('selected') g.style.backgroundColor = 'blue' }) annotations.forEach(a => { if (a.id == id && a.type == HIGHLIGHT) { renderHighlights(a.page) } }) } function deselectAnnotation(id) { document.querySelectorAll('.oml-annotation.a' + id).forEach(function(g) { g.classList.remove('selected') g.style.backgroundColor = 'yellow' }) } function deselectAllAnnotations() { var ids = [] document.querySelectorAll('.oml-annotation.selected').forEach(function(g) { g.classList.remove('selected') g.style.backgroundColor = 'yellow' var id = $(g).parents('.oml-annotation').data('id') //console.log('deselect', g, id) if (!Ox.contains(ids, id)) { ids.push(id) Ox.$parent.postMessage('selectAnnotation', {id: null}) } }) } function removeAnnotation(id) { document.querySelectorAll('.oml-annotation.a' + id).forEach(function(a) { a.remove() }) annotations = annotations.filter(function(annotation) { return annotation.id != id }) Ox.$parent.postMessage('removeAnnotation', {id: id}) } function loadAnnotations(page) { document.querySelectorAll('.oml-annotation.page' + page).forEach(function(e) { e.remove() }) annotations.filter(function(a) { return a.page == page && !a.type == HIGHLIGHT }).forEach(function(annot) { renderAnnotation(annot) }) } function isInView(element) { var docViewTop = $(window).scrollTop(); var docViewBottom = docViewTop + $(window).height(); var elementTop = $(element).offset().top; var elementBottom = elementTop + $(element).height(); return elementTop < docViewBottom && elementBottom > docViewTop; } function renderHighlightSelectionOverlay(root, documentId, page, source) { var canvas = document.createElement('canvas') root.appendChild(canvas) canvas.width = canvas.clientWidth canvas.height = canvas.clientHeight var ctx = canvas.getContext('2d'); var viewerContainer = document.querySelector('#viewerContainer') var bounds = root.getBoundingClientRect(); var base = 2048 var scale = Math.max(bounds.height, bounds.width) / base var last_mousex = last_mousey = 0; var mousex = mousey = 0; var mousedown = false; var p = { top: 0, left: 0, bottom: 0, right: 0 } var inside = false canvas.addEventListener('mousedown', function(e) { if (inside) { const coords = [ [p.left, p.top, p.right, p.bottom] ] addAnnotation({ type: HIGHLIGHT, id: Ox.SHA1(pageNumber.toString() + JSON.stringify(p)), text: "", page: parseInt(page), pageLabel: source.pageLabel, coords: coords, }) return } let bounds = root.getBoundingClientRect(); last_mousex = e.clientX - bounds.left; last_mousey = e.clientY - bounds.top; p.top = parseInt(last_mousey / scale) p.left = parseInt(last_mousex / scale) mousedown = true; }); document.addEventListener('mouseup', function(e) { if (mousedown) { mousedown = false; p.bottom = parseInt(mousey / scale) p.right = parseInt(mousex / scale) if (p.top > p.bottom) { var t = p.top p.top = p.bottom p.bottom = t } if (p.left > p.right) { var t = p.left p.left = p.right p.right = t } /* var url = `${baseUrl}/documents/${documentId}/2048p${page},${p.left},${p.top},${p.right},${p.bottom}.jpg` info.url = `${baseUrl}/document/${documentId}/${page}` info.page = page if (p.left != p.right && p.top != p.bottom) { var context = formatOutput(info, url) copyToClipboard(context) addToRecent({ document: documentId, page: parseInt(page), title: info.title, type: 'fragment', link: `${baseUrl}/documents/${documentId}/${page}`, src: url }) } */ } }); canvas.addEventListener('mousemove', function(e) { let bounds = root.getBoundingClientRect(); mousex = e.clientX - bounds.left; mousey = e.clientY - bounds.top; if(mousedown) { ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight) ctx.beginPath() var width = mousex - last_mousex var height = mousey - last_mousey ctx.rect(last_mousex, last_mousey, width, height) ctx.strokeStyle = 'black'; ctx.lineWidth = 2; ctx.stroke(); } else { let py = parseInt(mousey / scale) let px = parseInt(mousex / scale) if (py > p.top && py < p.bottom && px > p.left && px < p.right) { inside = true canvas.style.cursor = 'pointer' canvas.title = 'Click to add highlight' } else { inside = false canvas.style.cursor = '' canvas.title = '' } } }); } function renderHighlights(page) { var pageAnnotations = annotations.filter(annotation => { return annotation.type == HIGHLIGHT && (!page || (annotation.page == page)) }) pageAnnotations.forEach(annotation => { let page = annotation.page var canvas = document.querySelector(`#highlights${page} canvas`) if (canvas) { canvas.width = canvas.clientWidth canvas.height = canvas.clientHeight var ctx = canvas.getContext('2d'); var viewerContainer = document.querySelector('#viewerContainer') var bounds = canvas.parentElement.getBoundingClientRect(); var base = 2048 var scale = Math.max(bounds.height, bounds.width) / base ctx.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight) pageAnnotations.forEach(annotation => { ctx.beginPath() ctx.strokeStyle = annotation.id == selectedAnnotation ? 'blue' : 'yellow'; ctx.lineWidth = 2; annotation.coords.forEach(coord => { const width = coord[2] - coord[0], height = coord[3] - coord[1]; ctx.rect(coord[0] * scale, coord[1] * scale, width * scale, height * scale) }) ctx.stroke(); }) } }) }