2024-01-06 14:15:10 +02:00
|
|
|
// Do not import anything here -- the built version of this file is copied to packages/app-clipper's
|
|
|
|
// content_scripts folder.
|
|
|
|
|
2023-10-13 16:31:13 +02:00
|
|
|
function absoluteUrl(url: string) {
|
|
|
|
if (!url) return url;
|
|
|
|
const protocol = url.toLowerCase().split(':')[0];
|
|
|
|
if (['http', 'https', 'file', 'data'].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 `${baseUrl()}/${url}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function pageLocationOrigin() {
|
|
|
|
// location.origin normally returns the protocol + domain + port (eg. https://example.com:8080)
|
|
|
|
// but for file:// protocol this is browser dependant and in particular Firefox returns "null"
|
|
|
|
// in this case.
|
|
|
|
|
|
|
|
if (location.protocol === 'file:') {
|
|
|
|
return 'file://';
|
|
|
|
} else {
|
|
|
|
return location.origin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function baseUrl() {
|
|
|
|
let output = pageLocationOrigin() + location.pathname;
|
|
|
|
if (output[output.length - 1] !== '/') {
|
|
|
|
const output2 = output.split('/');
|
|
|
|
output2.pop();
|
|
|
|
output = output2.join('/');
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
function getJoplinClipperSvgClassName(svg: SVGSVGElement) {
|
|
|
|
for (const className of svg.classList) {
|
|
|
|
if (className.indexOf('joplin-clipper-svg-') === 0) return className;
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
type ImageObject = {
|
|
|
|
width: number;
|
|
|
|
height: number;
|
|
|
|
naturalWidth?: number;
|
|
|
|
naturalHeight?: number;
|
|
|
|
};
|
|
|
|
|
2023-10-17 22:10:18 +02:00
|
|
|
export function getImageSizes(element: Document, forceAbsoluteUrls = false) {
|
2023-10-13 16:31:13 +02:00
|
|
|
const output: Record<string, ImageObject[]> = {};
|
|
|
|
|
|
|
|
const images = element.getElementsByTagName('img');
|
|
|
|
for (let i = 0; i < images.length; i++) {
|
|
|
|
const img = images[i];
|
|
|
|
if (img.classList && img.classList.contains('joplin-clipper-hidden')) continue;
|
|
|
|
|
|
|
|
let src = imageSrc(img);
|
|
|
|
src = forceAbsoluteUrls ? absoluteUrl(src) : src;
|
|
|
|
|
|
|
|
if (!output[src]) output[src] = [];
|
|
|
|
|
|
|
|
output[src].push({
|
|
|
|
width: img.width,
|
|
|
|
height: img.height,
|
|
|
|
naturalWidth: img.naturalWidth,
|
|
|
|
naturalHeight: img.naturalHeight,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
const svgs = element.getElementsByTagName('svg');
|
|
|
|
for (let i = 0; i < svgs.length; i++) {
|
|
|
|
const svg = svgs[i];
|
|
|
|
if (svg.classList && svg.classList.contains('joplin-clipper-hidden')) continue;
|
|
|
|
|
|
|
|
const className = getJoplinClipperSvgClassName(svg);// 'joplin-clipper-svg-' + i;
|
|
|
|
|
|
|
|
if (!className) {
|
|
|
|
console.warn('SVG without a Joplin class:', svg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!svg.classList.contains(className)) {
|
|
|
|
svg.classList.add(className);
|
|
|
|
}
|
|
|
|
|
|
|
|
const rect = svg.getBoundingClientRect();
|
|
|
|
|
|
|
|
if (!output[className]) output[className] = [];
|
|
|
|
|
|
|
|
output[className].push({
|
|
|
|
width: rect.width,
|
|
|
|
height: rect.height,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
|
|
|
// In general we should use currentSrc because that's the image that's currently displayed,
|
|
|
|
// especially within <picture> tags or with srcset. In these cases there can be multiple
|
|
|
|
// sources and the best one is probably the one being displayed, thus currentSrc.
|
|
|
|
function imageSrc(image: HTMLImageElement) {
|
|
|
|
if (image.currentSrc) return image.currentSrc;
|
|
|
|
return image.src;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Given a document, return a <style> tag that contains all the styles
|
|
|
|
// required to render the page. Not currently used but could be as an
|
|
|
|
// option to clip pages as HTML.
|
|
|
|
// eslint-disable-next-line
|
|
|
|
export function getStyleSheets(doc: Document) {
|
|
|
|
const output = [];
|
|
|
|
for (let i = 0; i < doc.styleSheets.length; i++) {
|
|
|
|
const sheet = doc.styleSheets[i];
|
|
|
|
try {
|
|
|
|
for (const cssRule of sheet.cssRules) {
|
|
|
|
output.push({ type: 'text', value: cssRule.cssText });
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
// Calling sheet.cssRules will throw a CORS error on Chrome if the stylesheet is on a different domain.
|
|
|
|
// In that case, we skip it and add it to the list of stylesheet URLs. These URls will be downloaded
|
|
|
|
// by the desktop application, since it doesn't have CORS restrictions.
|
|
|
|
// eslint-disable-next-line
|
|
|
|
console.info('Could not retrieve stylesheet now:', sheet.href);
|
|
|
|
// eslint-disable-next-line
|
|
|
|
console.info('It will downloaded by the main application.');
|
|
|
|
// eslint-disable-next-line
|
|
|
|
console.info(error);
|
|
|
|
output.push({ type: 'url', value: sheet.href });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
}
|
2024-01-06 14:15:10 +02:00
|
|
|
|
|
|
|
// Required to run in Firefox with tabs.executeScript. See
|
|
|
|
// https://stackoverflow.com/a/44774834
|
|
|
|
undefined;
|