Open Safari web page, scroll down & highlight text on said page.

Thank you again for your script. I tried your edited script and it still doesn’t work for me. It scrolls down, but doesn’t scroll to the correct area of the webpage, nor highlight any text. Weird.

I really don’t understand.

One more time, I double clicked the button [Open this Scriplet in your Editor] from message #3 (the one at top of the script, not the one at top of the events log).
The script opened in Script Debugger.
I clicked the button Execute and I got the sentence highlighted.

I made an other attempt with these two strings on entry and it behaved flawlessly.

set theURL to "https://fr.wikipedia.org/wiki/Le_Corbusier"
set theTEXT to "La modestie du commandeur influença probablement le choix définitif"

Same results after a copy / paste from Script Debugger to Apple’s Script Editor.

I made a last test with Shane STANLEY’s ASObjC Explorer
This time it’s only at the 3rd attempt that I got the sentence highlighted and it work only if I don’t activate the log events feature. I guess that it’s due to the use of javascript.

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) mardi 27 décembre 2016 19:55:28

I noticed you said you used Script Debugger to launch the script instead of Apple’s Script Editor. I used Script Debugger and it worked. For some reason, your script doesn’t work if it’s launched with Apple’s default Script Editor. My script works with either app for some reason. Bizarre.

I wrote also that it works with Apple’s Script Editor.
The only one with which I had problems is ASObjC Explorer but it’s not the kind of oddity which you describe. When the log events feature is active, the editor issue :
[format]Unable to compile running script (2)
fin de ligne, etc. prévu(s) mais identificateur trouvé(s). (-2741)[/format]

Which machine are you running ?
Maybe it’s a more recent than mine with a faster processor.
Mine is an iMac 21.5" mid 2011. Processor 2.8 GHz Intel Core 7

During my tests, the delay 0.05 after the instruction keystroke (character id 127) is critical. If I replace it by delay 0.02 the string isn’t highlighted.

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) mardi 27 décembre 2016 20:56:02

I just noticed a difference. My script highlights the text in blue without greying out the rest of the text - Your version highlights the text in blue, but keeps everything else greyed out. Not a big deal, but I prefer the way mine works where it’s not greyed out. Must have something to do with this part being different.

tell process "Safari" to keystroke "." using command down

MacBook Pro early 2011 w/i7 2.3 GHz and 16GB RAM.

Changing the delay to from 0.05 to 1 now makes it highlight text with Script Editor.

Your now works with the delay set to 1.0, but I find that my script is surprisingly a little bit faster overall.

Rats, one thing I find is that if I quit Safari, clear the History and Cache, both scripts tend to fail to select the text.

Replacing delay 0.05 by delay 1 is a bit exaggerated.
There is no need for such a delay to clear the field containing the string to search.

I am unable to guess what is giving the odd behavior described on your machine.

I cleared the cache and history and the script did its duty flawlessly.
On my machine, my version is fastest than yours but as you never responded when I asked for details about yours I am unable to try to find an explanation. I may just guess that yours was delivered in march 2015 or later as it was running 10.10 when you opened this thread.

According to Script Debugger, today the entire job was achieved in 4.11 seconds.
In Apple’s Script Editor “That took 2,983440041542 seconds.”

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) mercredi 28 décembre 2016 11:24:37

Is this any help? I’m not a JavaScript expert, I’m afraid.

set theURL to "https://en.wikipedia.org/wiki/The_Rolling_Stones"
set theTEXT to "Mick and I thought these songs were really puerile"

tell application "Safari"
	activate
	open location theURL
	delay 1
	repeat until (do JavaScript "document.readyState" in document 1) is "complete"
		delay 1
	end repeat
	-- This works from the current cursor position, so only really useful with a fresh document.
	-- theTEXT might need to be doctored, depending on the characters it contained.
	do JavaScript "window.find(\"" & theTEXT & "\")" in document 1 --> boolean indicating found or otherwise.
end tell

Hello Nigel
Your version is the fastest but after running it I am puzzled.
With it, the events log is :

tell application "Safari"
	activate
	open location "https://en.wikipedia.org/wiki/The_Rolling_Stones"
	do JavaScript "document.readyState" in document 1
		--> "complete"
	do JavaScript "window.find(\"Mick and I thought these songs were really puerile\")" in document 1
		--> true
end tell

With my code slightly modified to match your first instructions :

(*
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"
set startDate to current application's NSDate's |date|()
*)
set theURL to "https://en.wikipedia.org/wiki/The_Rolling_Stones"
set theTEXT to "Mick and I thought these songs were really puerile"
(*
set theURL to "https://fr.wikipedia.org/wiki/Le_Corbusier"
set theTEXT to "La modestie du commandeur influença probablement le choix définitif"
*)
tell application "Safari"
	activate
	--end tell # ACTIVE in original code
	
	open location theURL
	
	--tell application "Safari" # ACTIVE in original code
	delay 1
	repeat until (do JavaScript "document.readyState" in document 1) is "complete"
		delay 1
	end repeat
end tell

tell application "System Events" to tell process "Safari"
	set frontmost to true # ADDED
	delay 0.2 # REQUIRED when there is a single tell application "Safari", no need with the originals two tell instructions
	keystroke "f" using command down
	tell window 1
		-- class of UI elements --> {splitter group, button, button, button, group, button, toolbar}
		-- class of UI elements of splitter group 1 --> {splitter, tab}
		# The tab element is not "recognized"
		-- class of UI elements of UI element 2 of splitter group 1 --> {group, group}
		-- class of UI elements of group 2 of UI element 2 of splitter group 1 --> {button, group, text field, static text}
		set theTarget to text field 1 of group 2 of UI element 2 of splitter group 1
		-- value of theTarget
		(*
			repeat
				delay 1 / 1000
				if value of theTarget = "" then exit repeat
			end repeat
			*)
		set value of theTarget to theTEXT
	end tell
	keystroke "." using {command down}
	
end tell
(*
set timeDiff to startDate's timeIntervalSinceNow()
display dialog "That took " & (-timeDiff as real) & " seconds."
*)

The events log is :

tell application "Safari"
	activate
	open location "https://en.wikipedia.org/wiki/The_Rolling_Stones"
	do JavaScript "document.readyState" in document 1
		--> "interactive"
	do JavaScript "document.readyState" in document 1
		--> "complete"
end tell
tell application "System Events"
	set frontmost of process "Safari" to true
	keystroke "f" using command down
	get text field 1 of group 2 of UI element 2 of splitter group 1 of window 1 of process "Safari"
		--> text field 1 of group 2 of tab group 1 of splitter group 1 of window "The Rolling Stones - Wikipedia" of application process "Safari"
	set value of text field 1 of group 2 of tab group 1 of splitter group 1 of window "The Rolling Stones - Wikipedia" of application process "Safari" to "Mick and I thought these songs were really puerile"
	keystroke "." using {command down}
end tell

I really don’t understand why in repeated tests, your version execute only once the instruction
do JavaScript “document.readyState” in document 1
while mine executes it twice.

I scrap my head but find no explanation.

Worse, the code posted above behaves flawlessly from Apple’s Script Editor but fails from Script Debugger with the message :
[Format]
get text field 1 of group 2 of UI element 2 of splitter group 1 of window 1 of process “Safari”
index is out of range (errorAEIllegalindex:-1719)
[/Format]

Which is what I get from Apple’s Editor if I remove the “REQUIRED” delay 0.2

Don’t tell me that it’s a side effect of Xmas, I drunk absolutely no spirit for months :wink:

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) mercredi 28 décembre 2016 14:34:23

Hi Yvan.

I think it’s just due to transient conditions. It depends on how long it takes Safari to fetch and render the page, given conditions on the Internet and whatever the computer’s doing in the background at the time. The number of executions is different every time for me, with both scripts.

I find I have to put extra delays in your test script:

set theURL to "https://en.wikipedia.org/wiki/The_Rolling_Stones"
set theTEXT to "Mick and I thought these songs were really puerile"
(*
set theURL to "https://fr.wikipedia.org/wiki/Le_Corbusier"
set theTEXT to "La modestie du commandeur influença probablement le choix définitif"
*)
tell application "Safari"
	activate
	--end tell # ACTIVE in original code
	
	open location theURL
	
	--tell application "Safari" # ACTIVE in original code
	delay 1
	repeat until (do JavaScript "document.readyState" in document 1) is "complete"
		delay 1
	end repeat
end tell

tell application "System Events" to tell process "Safari"
	set frontmost to true # ADDED
	delay 0.2 # REQUIRED when there is a single tell application "Safari", no need with the originals two tell instructions
	keystroke "f" using command down
	tell window 1
		-- Wait until the "Find" text field appears.
		repeat until (text field 1 of group 2 of UI element 2 of splitter group 1 exists)
			delay 0.2
		end repeat
		set theTarget to text field 1 of group 2 of UI element 2 of splitter group 1
		set value of theTarget to theTEXT
	end tell
	delay 0.5 -- Wait until the found text is selected and comes into view.
	keystroke "." using {command down}
end tell

Far be it from me to doubt your words! :stuck_out_tongue:

Wow! Thank you Nigel and thank you again Yvan! Nigel, the JavaScript find is great. I’m testing it all out now.

Nigel, I tried your nice & fast script you graciously shared in post #15 above. I see in the comments in the script that it needs a fresh document. Does that mean it doesn’t work well if the page is slow to load? I’ve found that to be the case.

And, to both Yvan & Nigel it looks like the script in post #17 works pretty good overall. I’ve still had it glitch out on occasion when I clear the Safari cache & history and have slow page load, but it’s the most stable overall version I’ve tried yet.

MBP i7 early 2011, macOS 10.12.1

Hi.

I found when trying it out that the JavaScript ‘window.find’ ” in Safari at least ” is a kind of “find next” which doesn’t cycle round to the top of the page. If the search string’s already found and selected, using ‘window.find’ again tries to find the next occurrence of it. If there isn’t another occurrence before the bottom of the page, it doesn’t start again at the top like Mac OS’s ‘find’, but simply returns ‘false’. Similarly, if you click below the text before trying to ‘window.find’ it, it won’t be found (unless you add a parameter for searching backwards). My comment was more precautionary than relevant here, as the ‘open location’ command apparently reloads the page anyway, putting the focus at the top again.

One would hope that the ‘document.readyState’ repeat would take care of the page being slow to load, but I don’t know how reliable it is. So far, on my system, my script has successfully fetched your example page and selected your example text every time ” provided that Safari’s already open when the script’s run. I did have one failure-to-select yesterday when the script had to launch Safari itself.

Here is a script demonstrating that we may easily get rid of the first problem described by Nigel.
Some instructions force the cursor to move so that the search starts from the very beginning of the web page.
Some extraneous instructions move the cursor to an other string or to the very bottom of the web page.
Some others force Safari to quit when the count of pass is a multiple of 5.
I feel that this way every conditions are treated.

set pass to 0
repeat 50 times
	set pass to pass + 1
	log "pass = " & pass
	set theURL to "https://en.wikipedia.org/wiki/The_Rolling_Stones"
	set theTEXT to "Mick and I thought these songs were really puerile"
	(*
	set theURL to "https://en.wikipedia.org/wiki/Tadao_Ando"
	set theTEXT to "Tadao Ando (安藤 å¿ é›„ Andō Tadao?, born September 13"
	*)
	tell application "Safari"
		activate
		open location theURL
		delay 1
		repeat until (do JavaScript "document.readyState" in document 1) is "complete"
			delay 1
		end repeat
		tell application "System Events" to tell process "Safari"
			set frontmost to true
			tell window 1
				key code 115 # go to beginning so the search would scan from the beginning of the text
			end tell
		end tell
		delay 0.5 # required to let the cursor reach the beginning
		-- theTEXT might need to be doctored, depending on the characters it contained.
		do JavaScript "window.find(\"" & theTEXT & "\")" in document 1 --> boolean indicating found or otherwise.
		
		delay 0.5 # to see that the string is selected
		if (pass mod 3) ≠ 0 then
			# select a string near the end of the document
			set theTEXT to "Spitz, Marc (2011). Jagger: Rebel, Rock Star, Rambler, Rogue. Gotham Books. ISBN 978-1-59240-655-5."
			-- set theTEXT to "Works å®‰è—¤å¿ é›„ Tadao Ando"
			do JavaScript "window.find(\"" & theTEXT & "\")" in document 1
		else
			tell application "System Events" to tell process "Safari"
				set frontmost to true
				tell window 1
					key code 119 # go to very bottom of the webpage
				end tell
			end tell
		end if
		delay 0.5 # to see that the string is selected
	end tell
	
	if (pass mod 5) = 0 then
		# If pass is a multiple of 5, force Safari to quit
		tell application "Safari" to quit
		delay 0.2 # required to let Safari finish its quit process
	end if
end repeat

I’m just wondering what is the meaning of the “2nd problem” : – theTEXT might need to be doctored, depending on the characters it contained.

I assumed that it may be about “foreign” unicode text so I tested with :
set theURL to “https://en.wikipedia.org/wiki/Tadao_Ando
set theTEXT to “Tadao Ando (安藤 å¿ é›„ Andō Tadao?, born September 13”
and
set theTEXT to “Works å®‰è—¤å¿ é›„ Tadao Ando” as second string to search

I had nothing special to change to the datas.

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) jeudi 29 décembre 2016 14:10:51

Escaping any quotes and/or backslashes, basically.

Hello Nigel

I asked because when I tried to search the string defined by:
set theTEXT to “as "The World’s Greatest Rock and Roll Band".”
the script didn’t find it while it’s really available at the beginning of the page.
Same negative result with :
set theTEXT to "as " & quote & “The World’s Greatest Rock and Roll Band” & quote & “.”
or
set theTEXT to quoted form of "as " & quote & “The World’s Greatest Rock and Roll Band” & quote & “.”

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) jeudi 29 décembre 2016 16:13:36

Hi Yvan.

Yes. That’s the kind of thing to which I was referring. Since my script uses escaped double-quotes to quote the parameter in the string for the JavaScript command, any double-quotes in the parameter have to be double-escaped:

set theTEXT to "\\\"The World's Greatest Rock and Roll Band\\\""

tell application "Safari"
	-- Blah blah blah
	do JavaScript "window.find(\"" & theTEXT & "\")" in document 1
end tell

If I’d used single-quotes to quote the parameter, the parameter’s double-quotes would only need to be single-escaped and the single-quotes and apostrophes would need to be escaped:

set theTEXT to "\"The World\\'s Greatest Rock and Roll Band\""

tell application "Safari"
	-- Blah blah blah
	do JavaScript "window.find('" & theTEXT & "')" in document 1
end tell

Thanks Nigel.
I apologizes, it works but it’s too cumbersome for me.

I will stay with :

set theURL to "https://en.wikipedia.org/wiki/The_Rolling_Stones"
set theTEXT to "Mick and I thought these songs were really puerile"
set theTEXT to "as " & quote & "The World's Greatest Rock and Roll Band" & quote & "."
# or this easy to build alternate version
--set theTEXT to "as \"The World's Greatest Rock and Roll Band\"."
(*
set theURL to "https://fr.wikipedia.org/wiki/Le_Corbusier"
set theTEXT to "La modestie du commandeur influença probablement le choix définitif"
*)
tell application "Safari"
	activate
	--end tell # ACTIVE in original code
	
	open location theURL
	
	--tell application "Safari" # ACTIVE in original code
	delay 1
	repeat until (do JavaScript "document.readyState" in document 1) is "complete"
		delay 1
	end repeat
end tell

tell application "System Events" to tell process "Safari"
	set frontmost to true # ADDED
	delay 0.2 # REQUIRED when there is a single tell application "Safari", no need with the originals two tell instructions
	keystroke "f" using command down
	tell window 1
		-- Wait until the "Find" text field appears.
		repeat until (text field 1 of group 2 of UI element 2 of splitter group 1 exists)
			delay 0.2
		end repeat
		set theTarget to text field 1 of group 2 of UI element 2 of splitter group 1
		set value of theTarget to theTEXT
	end tell
	delay 0.5 -- Wait until the found text is selected and comes into view.
	keystroke "." using {command down}
end tell

I like it because the events log displays exactly what it inserts :

set value of text field 1 of group 2 of tab group 1 of splitter group 1 of window "The Rolling Stones - Wikipedia" of application process "Safari" to "as \"The World's Greatest Rock and Roll Band\"."

which exactly what the alternate version does.

It’s clear, deciphering Enigma wasn’t a task for me. :frowning:

Yvan KOENIG running Sierra 10.12.2 in French (VALLAURIS, France) jeudi 29 décembre 2016 17:45:41

Just thought I’d add that since I’ve completed some latest macOS Sierra upgrades with newer Safari versions, I’ve found that this part doesn’t work very well for waiting for the web page to load.


        tell application "Safari"
		activate
		delay 1
		repeat
			if (do JavaScript "document.readyState" in document 1) is "complete" then exit repeat
			delay 1 -- wait a second before checking again
		end repeat
		delay 1
	end tell

So, as of Sierra 10.12.3 I’ve been adding a dialog to many of my scripts that depend upon Safari loading a web page first. This is an example that works with Launchbar after I enter a search term.


on handle_string(theString)
	
	tell application "Safari"
		
                activate		
		open location "https://samplewebsite.com/forum/"
				
	end tell
	
	-- For some reason by keeping the following javascript it keeps dialog active so I can hit enter key
	
	tell application "Safari"
		activate
		delay 1
		repeat
			if (do JavaScript "document.readyState" in document 1) is "complete" then exit repeat
			delay 1 -- wait a second before checking again
		end repeat
		delay 1
	end tell
	
	-- Again, for some reason keeping the above javascript keeps dialog active below so I can hit enter key instead of having to click on the dialog with the mouse first to activate it	
	
        display dialog "Press Enter When Page Loads..." with title "Continue" buttons {"Continue"} default button 1
	if button returned of result is "Continue" then
	end if
	
	
	tell application "Safari"
		
		do JavaScript "document.getElementById('SearchQuery').value ='" & theString & "';" in document 1
		delay 1
		do JavaScript "document.getElementsByClassName('search Tooltip')[0].click()" in document 1
	end tell
	
		
end handle_string

This way when I launch a script from LaunchBar or somewhere else, I can simply hit the Enter key once the website loads. It’s been working great. I guess I’d prefer this to happen automatically, though.

Any input on this would be appreciated. I’m not sure of any other way to keep the AppleScript dialog active while Safari loads the page when I’m using LaunchBar.