468 lines
17 KiB
JavaScript
468 lines
17 KiB
JavaScript
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 = `
|
|
<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)
|
|
})
|
|
|
|
|
|
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();
|
|
})
|
|
|
|
}
|
|
})
|
|
|
|
}
|