2019-07-19 20:35:53 +02:00
|
|
|
function absoluteUrl(url) {
|
|
|
|
if (!url) {
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
|
|
|
const protocol = url.toLowerCase().split(':')[0];
|
|
|
|
if (['http', 'https', 'file'].indexOf(protocol) >= 0) {
|
|
|
|
return url;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (url.indexOf('//') === 0) {
|
|
|
|
return location.protocol + url;
|
|
|
|
} else if (url[0] === '/') {
|
|
|
|
return location.protocol + '//' + location.host + url;
|
|
|
|
} else {
|
|
|
|
return getBaseUrl() + '/' + url;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function pageTitle() {
|
|
|
|
const titleElements = document.getElementsByTagName("title");
|
|
|
|
|
|
|
|
return titleElements.length ? titleElements[0].text.trim() : document.title.trim();
|
|
|
|
}
|
|
|
|
|
|
|
|
function getReadableDocument() {
|
2022-02-19 19:35:39 +01:00
|
|
|
// Readability directly change the passed document, so clone to preserve the original web page.
|
2019-07-19 20:35:53 +02:00
|
|
|
const documentCopy = document.cloneNode(true);
|
2022-02-19 19:35:39 +01:00
|
|
|
const readability = new Readability(documentCopy, {
|
|
|
|
serializer: el => el // so that .content is returned as DOM element instead of HTML
|
|
|
|
});
|
|
|
|
|
2019-07-19 20:35:53 +02:00
|
|
|
const article = readability.parse();
|
|
|
|
|
|
|
|
if (!article) {
|
|
|
|
throw new Error('Could not parse HTML document with Readability');
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
title: article.title,
|
2022-02-19 19:35:39 +01:00
|
|
|
body: article.content,
|
2019-07-19 20:35:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getRectangleArea() {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const overlay = document.createElement('div');
|
|
|
|
overlay.style.opacity = '0.6';
|
|
|
|
overlay.style.background = 'black';
|
|
|
|
overlay.style.width = '100%';
|
|
|
|
overlay.style.height = '100%';
|
|
|
|
overlay.style.zIndex = 99999999;
|
|
|
|
overlay.style.top = 0;
|
|
|
|
overlay.style.left = 0;
|
|
|
|
overlay.style.position = 'fixed';
|
|
|
|
|
|
|
|
document.body.appendChild(overlay);
|
|
|
|
|
|
|
|
const messageComp = document.createElement('div');
|
|
|
|
|
|
|
|
const messageCompWidth = 300;
|
2022-02-19 19:37:23 +01:00
|
|
|
messageComp.setAttribute("tabindex", "0"); // so that it can be focused
|
2019-07-19 20:35:53 +02:00
|
|
|
messageComp.style.position = 'fixed';
|
|
|
|
messageComp.style.opacity = '0.95';
|
|
|
|
messageComp.style.fontSize = '14px';
|
|
|
|
messageComp.style.width = messageCompWidth + 'px';
|
|
|
|
messageComp.style.maxWidth = messageCompWidth + 'px';
|
|
|
|
messageComp.style.border = '1px solid black';
|
|
|
|
messageComp.style.background = 'white';
|
|
|
|
messageComp.style.color = 'black';
|
|
|
|
messageComp.style.top = '10px';
|
|
|
|
messageComp.style.textAlign = 'center';
|
|
|
|
messageComp.style.padding = '10px';
|
|
|
|
messageComp.style.left = Math.round(document.body.clientWidth / 2 - messageCompWidth / 2) + 'px';
|
|
|
|
messageComp.style.zIndex = overlay.style.zIndex + 1;
|
|
|
|
|
|
|
|
messageComp.textContent = 'Drag and release to capture a screenshot';
|
|
|
|
|
|
|
|
document.body.appendChild(messageComp);
|
|
|
|
|
|
|
|
const selection = document.createElement('div');
|
|
|
|
selection.style.opacity = '0.5';
|
|
|
|
selection.style.border = '1px solid red';
|
|
|
|
selection.style.background = 'white';
|
|
|
|
selection.style.border = '2px solid black';
|
|
|
|
selection.style.zIndex = overlay.style.zIndex - 1;
|
|
|
|
selection.style.top = 0;
|
|
|
|
selection.style.left = 0;
|
|
|
|
selection.style.position = 'fixed';
|
|
|
|
|
|
|
|
document.body.appendChild(selection);
|
|
|
|
|
2022-02-19 19:37:23 +01:00
|
|
|
messageComp.focus(); // we listen on keypresses on this element to cancel on escape
|
|
|
|
|
2019-07-19 20:35:53 +02:00
|
|
|
let isDragging = false;
|
|
|
|
let draggingStartPos = null;
|
|
|
|
let selectionArea = {};
|
|
|
|
|
|
|
|
function updateSelection() {
|
|
|
|
selection.style.left = selectionArea.x + 'px';
|
|
|
|
selection.style.top = selectionArea.y + 'px';
|
|
|
|
selection.style.width = selectionArea.width + 'px';
|
|
|
|
selection.style.height = selectionArea.height + 'px';
|
|
|
|
}
|
|
|
|
|
|
|
|
function setSelectionSizeFromMouse(event) {
|
|
|
|
if (event.clientX < draggingStartPos.x) {
|
|
|
|
selectionArea.x = event.clientX;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (event.clientY < draggingStartPos.y) {
|
|
|
|
selectionArea.y = event.clientY;
|
|
|
|
}
|
|
|
|
|
|
|
|
selectionArea.width = Math.max(1, Math.abs(event.clientX - draggingStartPos.x));
|
|
|
|
selectionArea.height = Math.max(1, Math.abs(event.clientY - draggingStartPos.y));
|
|
|
|
updateSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
function selection_mouseDown(event) {
|
|
|
|
selectionArea = {x: event.clientX, y: event.clientY, width: 0, height: 0};
|
|
|
|
draggingStartPos = {x: event.clientX, y: event.clientY};
|
|
|
|
isDragging = true;
|
|
|
|
updateSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
function selection_mouseMove(event) {
|
|
|
|
if (!isDragging) return;
|
|
|
|
setSelectionSizeFromMouse(event);
|
|
|
|
}
|
|
|
|
|
2022-02-19 19:37:23 +01:00
|
|
|
function removeOverlay() {
|
2019-07-19 20:35:53 +02:00
|
|
|
isDragging = false;
|
|
|
|
|
|
|
|
overlay.removeEventListener('mousedown', selection_mouseDown);
|
|
|
|
overlay.removeEventListener('mousemove', selection_mouseMove);
|
|
|
|
overlay.removeEventListener('mouseup', selection_mouseUp);
|
|
|
|
|
|
|
|
document.body.removeChild(overlay);
|
|
|
|
document.body.removeChild(selection);
|
|
|
|
document.body.removeChild(messageComp);
|
2022-02-19 19:37:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function selection_mouseUp(event) {
|
|
|
|
setSelectionSizeFromMouse(event);
|
|
|
|
|
|
|
|
removeOverlay();
|
2019-07-19 20:35:53 +02:00
|
|
|
|
|
|
|
console.info('selectionArea:', selectionArea);
|
|
|
|
|
|
|
|
if (!selectionArea || !selectionArea.width || !selectionArea.height) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Need to wait a bit before taking the screenshot to make sure
|
|
|
|
// the overlays have been removed and don't appear in the
|
|
|
|
// screenshot. 10ms is not enough.
|
|
|
|
setTimeout(() => resolve(selectionArea), 100);
|
|
|
|
}
|
|
|
|
|
2022-02-19 19:37:23 +01:00
|
|
|
function cancel(event) {
|
|
|
|
if (event.key === "Escape") {
|
|
|
|
removeOverlay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-19 20:35:53 +02:00
|
|
|
overlay.addEventListener('mousedown', selection_mouseDown);
|
|
|
|
overlay.addEventListener('mousemove', selection_mouseMove);
|
|
|
|
overlay.addEventListener('mouseup', selection_mouseUp);
|
2022-02-19 19:37:23 +01:00
|
|
|
overlay.addEventListener('mouseup', selection_mouseUp);
|
|
|
|
messageComp.addEventListener('keydown', cancel);
|
2019-07-19 20:35:53 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function makeLinksAbsolute(container) {
|
|
|
|
for (const link of container.getElementsByTagName('a')) {
|
|
|
|
if (link.href) {
|
|
|
|
link.href = absoluteUrl(link.href);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getImages(container) {
|
|
|
|
const images = [];
|
|
|
|
|
|
|
|
for (const img of container.getElementsByTagName('img')) {
|
|
|
|
if (!img.src) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-04-02 22:49:27 +02:00
|
|
|
const existingImage = images.find(image => image.src === img.src);
|
2019-07-19 20:35:53 +02:00
|
|
|
|
2020-04-02 22:49:27 +02:00
|
|
|
if (existingImage) {
|
|
|
|
img.src = existingImage.imageId;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const imageId = randomString(20);
|
|
|
|
|
|
|
|
images.push({
|
|
|
|
imageId: imageId,
|
|
|
|
src: img.src
|
|
|
|
});
|
2019-07-19 20:35:53 +02:00
|
|
|
|
2020-04-02 22:49:27 +02:00
|
|
|
img.src = imageId;
|
|
|
|
}
|
2019-07-19 20:35:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return images;
|
|
|
|
}
|
|
|
|
|
2021-02-22 15:30:12 +06:00
|
|
|
function createLink(clickAction, text, color = "lightskyblue") {
|
2021-02-22 14:35:55 +06:00
|
|
|
const link = document.createElement('a');
|
|
|
|
link.href = "javascript:";
|
|
|
|
link.style.color = color;
|
|
|
|
link.appendChild(document.createTextNode(text));
|
|
|
|
link.addEventListener("click", () => {
|
2021-02-22 15:30:12 +06:00
|
|
|
browser.runtime.sendMessage(null, clickAction)
|
2021-02-22 14:35:55 +06:00
|
|
|
});
|
2021-02-22 15:30:12 +06:00
|
|
|
|
2021-02-22 14:35:55 +06:00
|
|
|
return link
|
|
|
|
}
|
|
|
|
|
2019-07-19 20:35:53 +02:00
|
|
|
async function prepareMessageResponse(message) {
|
|
|
|
console.info('Message: ' + message.name);
|
|
|
|
|
|
|
|
if (message.name === "toast") {
|
|
|
|
let messageText;
|
|
|
|
|
|
|
|
if (message.noteId) {
|
2021-02-22 14:35:55 +06:00
|
|
|
messageText = document.createElement('p');
|
|
|
|
messageText.setAttribute("style", "padding: 0; margin: 0")
|
2019-07-19 20:35:53 +02:00
|
|
|
messageText.appendChild(document.createTextNode(message.message + " "));
|
2021-02-22 15:30:12 +06:00
|
|
|
messageText.appendChild(createLink(
|
|
|
|
{name: 'openNoteInTrilium', noteId: message.noteId},
|
2021-02-22 14:35:55 +06:00
|
|
|
"Open in Trilium."
|
2021-02-22 15:30:12 +06:00
|
|
|
));
|
2021-02-22 14:35:55 +06:00
|
|
|
|
2021-02-22 15:30:12 +06:00
|
|
|
// only after saving tabs
|
2021-02-22 14:35:55 +06:00
|
|
|
if (message.tabIds) {
|
|
|
|
messageText.appendChild(document.createElement("br"));
|
2021-02-22 15:30:12 +06:00
|
|
|
messageText.appendChild(createLink(
|
2021-02-22 14:35:55 +06:00
|
|
|
{name: 'closeTabs', tabIds: message.tabIds},
|
|
|
|
"Close saved tabs.",
|
|
|
|
"tomato"
|
2021-02-22 15:30:12 +06:00
|
|
|
));
|
2021-02-22 14:35:55 +06:00
|
|
|
}
|
2019-07-19 20:35:53 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
messageText = message.message;
|
|
|
|
}
|
|
|
|
|
|
|
|
await requireLib('/lib/toast.js');
|
|
|
|
|
|
|
|
showToast(messageText, {
|
|
|
|
settings: {
|
|
|
|
duration: 5000
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else if (message.name === "trilium-save-selection") {
|
|
|
|
const container = document.createElement('div');
|
|
|
|
|
|
|
|
const selection = window.getSelection();
|
|
|
|
|
|
|
|
for (let i = 0; i < selection.rangeCount; i++) {
|
|
|
|
const range = selection.getRangeAt(i);
|
|
|
|
|
|
|
|
container.appendChild(range.cloneContents());
|
|
|
|
}
|
|
|
|
|
|
|
|
makeLinksAbsolute(container);
|
|
|
|
|
|
|
|
const images = getImages(container);
|
|
|
|
|
|
|
|
return {
|
|
|
|
title: pageTitle(),
|
|
|
|
content: container.innerHTML,
|
|
|
|
images: images,
|
|
|
|
pageUrl: getPageLocationOrigin() + location.pathname + location.search
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
2022-02-19 19:37:23 +01:00
|
|
|
else if (message.name === 'trilium-get-rectangle-for-screenshot') {
|
2019-07-19 20:35:53 +02:00
|
|
|
return getRectangleArea();
|
|
|
|
}
|
|
|
|
else if (message.name === "trilium-save-page") {
|
|
|
|
await requireLib("/lib/JSDOMParser.js");
|
|
|
|
await requireLib("/lib/Readability.js");
|
|
|
|
await requireLib("/lib/Readability-readerable.js");
|
|
|
|
|
|
|
|
const {title, body} = getReadableDocument();
|
|
|
|
|
|
|
|
makeLinksAbsolute(body);
|
|
|
|
|
|
|
|
const images = getImages(body);
|
|
|
|
|
|
|
|
return {
|
|
|
|
title: title,
|
|
|
|
content: body.innerHTML,
|
|
|
|
images: images,
|
2019-07-19 21:05:22 +02:00
|
|
|
pageUrl: getPageLocationOrigin() + location.pathname + location.search,
|
|
|
|
clipType: 'page'
|
2019-07-19 20:35:53 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw new Error('Unknown command: ' + JSON.stringify(message));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
browser.runtime.onMessage.addListener(prepareMessageResponse);
|
|
|
|
|
|
|
|
const loadedLibs = [];
|
|
|
|
|
|
|
|
async function requireLib(libPath) {
|
|
|
|
if (!loadedLibs.includes(libPath)) {
|
|
|
|
loadedLibs.push(libPath);
|
|
|
|
|
|
|
|
await browser.runtime.sendMessage({name: 'load-script', file: libPath});
|
|
|
|
}
|
2021-03-31 22:23:43 +02:00
|
|
|
}
|