diff --git a/Clipper/joplin-webclipper/content_scripts/index.js b/Clipper/joplin-webclipper/content_scripts/index.js index e0d61b7fc..ef38cd056 100644 --- a/Clipper/joplin-webclipper/content_scripts/index.js +++ b/Clipper/joplin-webclipper/content_scripts/index.js @@ -61,14 +61,19 @@ const output = {}; 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; - output[src] = { + + if (!output[src]) output[src] = []; + + output[src].push({ width: img.width, height: img.height, naturalWidth: img.naturalWidth, naturalHeight: img.naturalHeight, - }; + }); } return output; } @@ -98,17 +103,18 @@ // Cleans up element by removing all its invisible children (which we don't want to render as Markdown) // And hard-code the image dimensions so that the information can be used by the clipper server to // display them at the right sizes in the notes. - function cleanUpElement(element, imageSizes) { + function cleanUpElement(element, imageSizes, imageIndexes) { const childNodes = element.childNodes; + const hiddenNodes = []; - for (let i = childNodes.length - 1; i >= 0; i--) { + for (let i = 0; i < childNodes.length; i++) { const node = childNodes[i]; const nodeName = node.nodeName.toLowerCase(); const isHidden = node && node.classList && node.classList.contains('joplin-clipper-hidden'); if (isHidden) { - element.removeChild(node); + hiddenNodes.push(node); } else { // If the data-joplin-clipper-value has been set earlier, create a new DIV element @@ -123,16 +129,23 @@ if (nodeName === 'img') { const src = absoluteUrl(imageSrc(node)); node.setAttribute('src', src); - const imageSize = imageSizes[src]; + if (!(src in imageIndexes)) imageIndexes[src] = 0; + const imageSize = imageSizes[src][imageIndexes[src]]; + imageIndexes[src]++; if (imageSize) { node.width = imageSize.width; node.height = imageSize.height; } } - cleanUpElement(node, imageSizes); + cleanUpElement(node, imageSizes, imageIndexes); } } + + for (const hiddenNode of hiddenNodes) { + if (!hiddenNode.parentNode) continue; + hiddenNode.parentNode.remove(hiddenNode); + } } // When we clone the document before cleaning it, we lose some of the information that might have been set via CSS or @@ -292,7 +305,8 @@ // directly on the document, so we make a copy of it first. const cleanDocument = document.body.cloneNode(true); const imageSizes = getImageSizes(document, true); - cleanUpElement(cleanDocument, imageSizes); + const imageIndexes = {}; + cleanUpElement(cleanDocument, imageSizes, imageIndexes); const stylesheets = command.preProcessFor === 'html' ? getStyleSheets(document) : null; return clippedContentResponse(pageTitle(), cleanDocument.innerHTML, imageSizes, getAnchorNames(document), stylesheets); @@ -305,7 +319,8 @@ const container = document.createElement('div'); container.appendChild(range.cloneContents()); const imageSizes = getImageSizes(document, true); - cleanUpElement(container, imageSizes); + const imageIndexes = {}; + cleanUpElement(container, imageSizes, imageIndexes); return clippedContentResponse(pageTitle(), container.innerHTML, getImageSizes(document), getAnchorNames(document)); } else if (command.name === 'screenshot') { diff --git a/ReactNativeClient/lib/renderers/HtmlToHtml.js b/ReactNativeClient/lib/renderers/HtmlToHtml.js index d9e309334..14d0f1392 100644 --- a/ReactNativeClient/lib/renderers/HtmlToHtml.js +++ b/ReactNativeClient/lib/renderers/HtmlToHtml.js @@ -30,7 +30,7 @@ class HtmlToHtml { } // We need this extra style so that the images don't overflow - const extraStyle = '' + const extraStyle = ''; //' return { html: extraStyle + htmlUtils.headAndBodyHtml(dom.window.document), diff --git a/ReactNativeClient/lib/services/rest/Api.js b/ReactNativeClient/lib/services/rest/Api.js index b9f0d3be6..b167b0dd4 100644 --- a/ReactNativeClient/lib/services/rest/Api.js +++ b/ReactNativeClient/lib/services/rest/Api.js @@ -15,6 +15,7 @@ const md5 = require('md5'); const { shim } = require('lib/shim'); const HtmlToMd = require('lib/HtmlToMd'); const urlUtils = require('lib/urlUtils.js'); +const ArrayUtils = require('lib/ArrayUtils.js'); const { netUtils } = require('lib/net-utils'); const { fileExtension, safeFileExtension, safeFilename, filename } = require('lib/path-utils'); const ApiResponse = require('lib/services/rest/ApiResponse'); @@ -371,7 +372,7 @@ class Api { let note = await this.requestNoteToNote_(requestNote); - const imageUrls = markupLanguageUtils.extractImageUrls(note.markup_language, note.body); + const imageUrls = ArrayUtils.unique(markupLanguageUtils.extractImageUrls(note.markup_language, note.body)); this.logger().info('Request (' + requestId + '): Downloading images: ' + imageUrls.length); @@ -618,6 +619,8 @@ class Api { } replaceImageUrlsByResources_(markupLanguage, md, urls, imageSizes) { + const imageSizesIndexes = {}; + if (markupLanguage === Note.MARKUP_LANGUAGE_HTML) { return htmlUtils.replaceImageUrls(md, imageUrl => { const urlInfo = urls[imageUrl]; @@ -628,7 +631,9 @@ class Api { let output = md.replace(/(!\[.*?\]\()([^\s\)]+)(.*?\))/g, (match, before, imageUrl, after) => { const urlInfo = urls[imageUrl]; if (!urlInfo || !urlInfo.resource) return before + imageUrl + after; - const imageSize = imageSizes[urlInfo.originalUrl]; + if (!(urlInfo.originalUrl in imageSizesIndexes)) imageSizesIndexes[urlInfo.originalUrl] = 0; + const imageSize = imageSizes[urlInfo.originalUrl][imageSizesIndexes[urlInfo.originalUrl]]; + imageSizesIndexes[urlInfo.originalUrl]++; const resourceUrl = Resource.internalUrl(urlInfo.resource); if (imageSize && (imageSize.naturalWidth !== imageSize.width || imageSize.naturalHeight !== imageSize.height)) {