openmedialibrary/static/reader/pdf.js

469 lines
17 KiB
JavaScript
Raw Normal View History

var id = document.location.pathname.split('/')[1];
2019-01-24 11:52:44 +00:00
var annotations = [];
var currentPage = 1, rendered = false
2024-06-10 13:05:17 +00:00
var highlightInactive = true
var selectedAnnotation
const SELECTION = 0
const HIGHLIGHT = 1
var div = document.createElement("div")
div.innerHTML = `
<button id="cropFile" class="toolbarButton cropFile hiddenLargeView" title="Highlight" tabindex="30" data-l10n-id="crop_file">
<span data-l10n-id="crop_file_label">Highlight</span>
</button>
`
var cropFile = div.querySelector("#cropFile")
document.querySelector('#toolbarViewerRight').insertBefore(cropFile, document.querySelector('#toolbarViewerRight').firstChild)
// secondary menu
div.innerHTML = `
<button id="secondaryCropFile" class="secondaryToolbarButton visibleMediumView cropFile" title="Highlight" tabindex="50" data-l10n-id="crop">
<span data-l10n-id="crop_label">Highlight</span>
</button>
`
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)
})
2019-01-23 15:14:59 +00:00
Ox.load({
'UI': {
loadCSS: false
}
2019-01-23 15:14:59 +00:00
}, function() {
Ox.$parent.bindMessage(function(data, event) {
if (event == 'selectAnnotation') {
var annotation = annotations.filter(function(a) { return a.id == data.id })[0]
2019-01-25 11:08:45 +00:00
var delay = 0
2019-01-23 15:14:59 +00:00
if (
annotation &&
annotation.page &&
PDFViewerApplication.pdfViewer.currentPageNumber != annotation.page
) {
PDFViewerApplication.pdfViewer.currentPageNumber = annotation.page;
2019-01-25 11:08:45 +00:00
delay = 250
2019-01-23 15:14:59 +00:00
}
annotation && setTimeout(function() {
2019-01-25 11:08:45 +00:00
var el = document.querySelector('.a' + annotation.id);
if (el && !isInView(el)) {
2019-01-25 11:08:45 +00:00
document.querySelector('#viewerContainer').scrollTop = el.offsetTop + el.parentElement.offsetTop - 64;
}
}, delay)
2024-06-10 13:05:17 +00:00
var oldSelection = selectedAnnotation
selectedAnnotation = data.id
2019-01-23 15:14:59 +00:00
selectAnnotation(data.id)
2024-06-10 13:05:17 +00:00
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()
2019-01-24 11:52:44 +00:00
} else if (event == 'addAnnotations') {
if (data.replace) {
document.querySelectorAll('.oml-annotation').forEach(function(a) {
a.remove()
})
annotations = []
}
2019-01-24 11:52:44 +00:00
data.annotations.forEach(function(annotation) {
annotations.push(annotation)
2024-06-10 13:05:17 +00:00
if (annotation.type == HIGHLIGHT) {
renderHighlights(annotation.page)
} else {
renderAnnotation(annotation)
}
2019-01-24 11:52:44 +00:00
})
2019-01-31 06:22:19 +00:00
} else if (event == 'removeAnnotation') {
removeAnnotation(data.id)
} else {
console.log('got', event, 'data', data)
2019-01-23 15:14:59 +00:00
}
})
})
window.addEventListener('keydown', function(event) {
if (event.key == 'Delete') {
var selected = document.querySelector('.oml-annotation.selected')
if (selected) {
removeAnnotation(selected.dataset.id)
}
2019-01-31 18:42:52 +00:00
} else if (event.key == 'n' || event.keyCode == 13) {
2019-02-09 18:10:14 +00:00
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() {
2019-01-23 15:14:59 +00:00
var pageNumber = PDFViewerApplication.pdfViewer.currentPageNumber;
var pageIndex = pageNumber - 1;
var page = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
var pageRect = page.canvas.getClientRects()[0];
2019-01-23 15:14:59 +00:00
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));
});
2019-01-23 15:14:59 +00:00
var text = selection.toString();
2019-02-09 18:10:14 +00:00
var position = [pageNumber].concat(Ox.sort(selected.map(function(c) { return [c[1], c[0]]}))[0]);
2019-01-23 15:14:59 +00:00
return {
2024-06-10 13:05:17 +00:00
type: SELECTION,
2019-01-23 15:14:59 +00:00
page: pageNumber,
pageLabel: PDFViewerApplication.pdfViewer.currentPageLabel,
2019-02-09 11:43:00 +00:00
position: position,
2019-01-23 15:14:59 +00:00
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) {
2019-01-23 15:14:59 +00:00
var pageIndex = annotation.page - 1;
var page = PDFViewerApplication.pdfViewer.getPageView(pageIndex);
2019-01-24 11:52:44 +00:00
if (!page || !page.canvas) {
setTimeout(function() {
renderAnnotation(annotation)
2019-01-24 11:52:44 +00:00
}, 50)
return
}
2019-01-22 14:06:48 +00:00
var pageElement = page.canvas.parentElement.parentElement;
var viewport = page.viewport;
2024-06-10 13:05:17 +00:00
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);
});
2024-06-10 13:05:17 +00:00
} else {
// console.log("annotation without position", annotation)
}
}
function addAnnotation(annotation) {
annotations.push(annotation)
2019-01-23 15:14:59 +00:00
Ox.$parent.postMessage('addAnnotation', annotation)
}
function selectAnnotation(id) {
2019-01-23 15:14:59 +00:00
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'
})
2024-06-10 13:05:17 +00:00
annotations.forEach(a => {
if (a.id == id && a.type == HIGHLIGHT) {
renderHighlights(a.page)
}
})
}
2019-01-31 18:42:52 +00:00
function deselectAnnotation(id) {
document.querySelectorAll('.oml-annotation.a' + id).forEach(function(g) {
g.classList.remove('selected')
g.style.backgroundColor = 'yellow'
})
}
2019-01-31 18:42:52 +00:00
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)
2019-01-31 18:42:52 +00:00
if (!Ox.contains(ids, id)) {
ids.push(id)
Ox.$parent.postMessage('selectAnnotation', {id: null})
2019-01-31 18:42:52 +00:00
}
})
}
function removeAnnotation(id) {
document.querySelectorAll('.oml-annotation.a' + id).forEach(function(a) {
a.remove()
})
annotations = annotations.filter(function(annotation) {
return annotation.id != id
})
2019-01-24 12:57:26 +00:00
Ox.$parent.postMessage('removeAnnotation', {id: id})
}
2019-01-23 15:14:59 +00:00
function loadAnnotations(page) {
document.querySelectorAll('.oml-annotation.page' + page).forEach(function(e) {
e.remove()
})
annotations.filter(function(a) {
2024-06-10 13:05:17 +00:00
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;
}
2024-06-10 13:05:17 +00:00
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();
})
}
})
}