Base64 Image not displaying as an image

Yes, that was a bit too terse, even for my standards. Here’s what I wanted to say.

Suppose you want to download the first image on a page that’s currently loaded in the topmost Safari window. The JavaScript code to get the image’s URL might be

const URL = document.getQuerySelector('img:nth-of-type(1)').src;

(no error-checking here, but you should add that for real-life code). This line is what I called constant in the above paragraph.

Now, you want to get this URL into your calling script:

// THIS DOES NOT WORK YET!
(() => {
	const browser = Application("Safari");
	const tab = browser.windows[0].currentTab();
        const browserCode = 
          `const URL = document.getQuerySelector('img:nth-of-type(1)').src;`;
	const result = browser.doJavaScript(browserCode, {in: tab});
})();

here, browserCode is set to a JavaScript template string, as indicated by the surrounding backticks. By using this construct, you avoid complicated quoting of single and double quotes, and you can also put JavaScript code inside the string if need be.

But this code does not work as one would want it to: When running the script, result will be undefined. That is because the return value of doJavaScript is the value of the last expression executed by the browser. That is an assignment to URL whose value is undefined.To have the browser return the URL itself, omit the assignment:

(() => {
	const browser = Application("Safari");
	const tab = browser.windows[0].currentTab();
        const browserCode = 
          `document.getQuerySelector('img:nth-of-type(1)').src;`;
	const result = browser.doJavaScript(browserCode, {in: tab});
})();

Now, the last statement executed is querySelector...src, and its value is the value of the img's src property.

Which is a string so that the whole thing works: doJavaScript can easily return strings and numbers to the calling code.

However, it can’t return more complicated objects like Array. Say you want to get the URLs of all images on a page with
[...document.getQuerySelectorAll('img')].map(e => e.src)
This will give you an Array of Strings. But in your calling code, you’ll get undefined if you run this line with doJavaScript. To overcome this, you can convert the result to a String:

const arrayOfStrings = [...document.getQuerySelectorAll('img')].map(e => e.src);
JSON.stringify(arrayOfStrings);

JSON.stringify converts its argument to a string, and since it’s the last statement in the code executed by the browser, this string will be returned to the calling script.

const browserCode = `const arrayOfStrings = [...document.getQuerySelectorAll('img')].map(e => e.src);
JSON.stringify(arrayOfStrings);`
const browser = Application("Safari");
const tab = browser.windows[0].currentTab();
const resultString= browser.doJavaScript(broserCode, {in: tab});
const resultArray = JSON.parse(resultString);

Here, JSON.parse converts the string back into an array of strings.

I hope it’s a bit clearer now.

Yes, a bit. Thank you for taking the time to explain all this.

I did have a look at rewriting the whole thing again in JXA but the full code does a lot of things apart from just getting the pictures from the website, so it became too difficult (for now).
I have now found an open access website that has a base64 image on it that I can use as an example of what my latest position is.

-- Use this website
-- https://www.w3docs.com/tools/image-base64

tell application "Safari"
	-- Get the picture
	set theitem to "flex p-6 h-80 mb-4 justify-center items-center rounded-md border border-gray-200 dark:border-gray-700"
	set myWindow to current tab of first window
	tell myWindow
		set theName to do JavaScript "document.getElementsByClassName('" & theitem & "')[0].outerHTML"
		set FrontTest to theName
	end tell
end tell

--Remove the text before "base64"
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to "base64,"
set FrontTest to every text item of FrontTest
set AppleScript's text item delimiters to astid

set Base64Text to text item 2 of FrontTest

set theBody to "Test Note with Image..."
set theStart to "<div><img style=\"max-width: 100%; max-height: 100%;\" src=\"data:image/png;base64,"
set theEnd to "<br></div>"

set theBody to theBody & theStart & Base64Text & theEnd

-- Send the result to Notes
tell application "Notes"
	--tell account "On My Mac" to tell folder "Notes"
	tell account "ICloud" to tell folder "Notes"
		make new note at end with properties {body:theBody}
	end tell
	save
end tell


set the clipboard to theBody

theBody

I am still not able to get the image to Notes directly. When I run the code, it produces a Notes document containing just a File, which needs to be double clicked to show the image. I can’t figure that out at all - it seems to work for others (see @KniazidisR post above).

So, as a temp workaround I set the clipboard to the HTML body (including the base64 string). I then paste the string into the W3Schools Tryit Editor to convert it into actual images. These images are then Copied and Pasted into the Notes file. Working - but long winded.

Is there a way to streamline that process please? For example, can I programmably convert the string back to JPG…

Absolutely brilliant - Many thanks.

I have put it in a loop so it collects all of the image data from the website and I have changed the

on makeDocumentFromClipboard()

To a simple

tell application "Notes"
		activate
		tell application "System Events"
			tell process "Notes" to keystroke "v" using {command down}
		end tell
	end tell

Images into Notes - Bosh!
:+1:

Thanks again for this. Although I am having trouble scaling this one up to deal with multiple images in one Notes file (up to about 20). :thinking: