Applescript Waiting for Safari Page Element to load before continue - safari

I try to have my Applescript to wait until a specific element on a web page is loaded, before clicking on it; but it is not working. The clicking does in fact work, but it doesn't wait until the ElementByName is loaded.
The complete code:
to clickName(theName, elementnum)
tell application "Safari"
tell front document to repeat until (do JavaScript ¬
"document.contains(document.getElementByName('" & theName & "'))")
delay 1
end repeat -- Finished loading
do JavaScript "document.getElementsByName('" & theName & "')[" & elementnum & "].click();" in document 1
end tell
end clickName
clickName("xxx", 0)
This part is not doing what I want:
tell front document to repeat until (do JavaScript ¬
"document.contains(document.getElementByName('" & theName & "'))")
delay 1
end repeat -- Finished loading
It just continues with the rest of the code, without checking if element 'xxx' is already loaded and therefor might miss the click event.
Please advice.

Related

How to click on this webpage button with AppleScript

I am having trouble clicking on the "search" button on a particular website. The website is a subscription service, so I am attaching a picture of the page pulled up in "inspect" mode as well as my code.
My code:
set myURL to "https://www.uptodate.com/contents/search"
tell application "Safari"
activate
make new document with properties {URL:myURL}
end tell
tell application "System Events"
repeat until exists (UI elements of groups of toolbar 1 of window 1 of application process "Safari" whose name = "Reload this page")
delay 0.3
end repeat
end tell
inputByID("tbSearch", "myVar")
clickClassName("newsearch-submit", 0)
###InputByID###
to inputByID(theId, theValue)
tell application "Safari"
do JavaScript "document.getElementById('" & theId & "').value ='" & theValue & "';" in document 1
end tell
end inputByID
###ClickByClass###
to clickClassName(theClassName, elementnum)
tell application "Safari"
do JavaScript "document.getElementsByClassName('" & theClassName & "')[" & elementnum & "].click();" in document 1
end tell
end clickClassName
As an alternative, you can use UI Scripting, where System Events preforms some keystrokes.
The following example AppleScript code works for me in macOS High Sierra1:
set myURL to "https://www.uptodate.com/contents/search"
tell application "Safari"
activate
make new document with properties {URL:myURL}
end tell
tell application "System Events"
repeat until (accessibility description of ¬
button 1 of UI element 1 of every group of toolbar 1 of window 1 of ¬
process "Safari" whose name = "Reload this page") contains "Reload this page"
delay 0.5
end repeat
end tell
tell application "Safari"
do JavaScript "document.getElementById('tbSearch').value ='PPE';" in document 1
end tell
tell application "System Events"
keystroke space
key code 51 -- # Press the delete key to remove the space.
keystroke return
end tell
1 NOTE: For macOS Mojave and later, change UI element 1 to UI element 2 in the repeat loop above.
Note: The example AppleScript code is just that and does not contain any error handling as may be appropriate. The onus is upon the user to add any error handling as may be appropriate, needed or wanted. Have a look at the try statement and error statement in the AppleScript Language Guide. See also, Working with Errors. Additionally, the use of the delay command may be necessary between events where appropriate, e.g. delay 0.5, with the value of the delay set appropriately.
I played around with this and of course not having a subscription cannot truly test it but perhaps you can see if this works for you - it seemed to be working:
tell application "Safari"
--activate --can work with or w/o Safari in foreground
set myURL to "https://www.uptodate.com/contents/search"
make new document with properties {URL:myURL}
delay 5 -- just waits for (hopefully) the page to load
set d to do JavaScript "document.forms[0][0].value = 'joint pain'" in document 1
do JavaScript "document.forms['searchForm'].submit();" in document 1
end tell

How to use AppleScript to search Safari's "smart" dropdown menu

I have a code that opens Safari to bing, inserts something to search, and searches it. However, I am trying to get the script to input the search THEN go down and search the autofill results rather than the actual search word. So far, I have been unable to do it. Here is a breakdown:
set theURL to "https://www.bing.com"
tell application "Safari" to make new document with properties {URL:theURL}
tell application "System Events"
repeat until exists (UI elements of groups of toolbar 1 of window 1 of application process "Safari" whose name = "Reload this page")
delay 0.5
end repeat
end tell
inputByID("sb_form_q", "coronavirus")
delay 0.5
clickID("sb_form_q") -- Here is where it breaks down. Trying to "click" the search bar after pasting so that System Events can 'arrow down'to the autofill results.
tell application "System Events"
key code 125
key code 125
end tell
delay (random number from 5 to 15)
clickID("sb_form_go")
delay 0.5
tell application "System Events"
delay 1.5
keystroke "w" using command down
end tell
to inputByID(theID, theValue)
tell application "Safari"
do JavaScript " document.getElementById('" & theID & "').value ='" & theValue & "';" in document 1
end tell
end inputByID
to clickID(theID)
tell application "Safari"
do JavaScript "document.getElementById('" & theID & "').click();" in document 1
end tell
end clickID
Ultimately, goal is to randomly search the autofill results based of the input. The code so far does not work and will just search the original input text.
I did some small edits in your code. Now this version of code, I test it more than 40 times. Not one lose. I write a lof of comments in the code. You can test it on your machine.
set theURL to "https://www.bing.com"
tell application "Safari"
activate
delay 0.3
-- 0th edit: I find "activate" is nessary for testing the code
make new document with properties {URL:theURL}
end tell
tell application "System Events"
tell process "Safari"
--1st edit: here "tell process "Safari"" is a must on my machine.
repeat until exists (button "Reload this page" of UI element 1 of group 2 of toolbar 1 of window 1)
delay 0.5
--2nd edit: the original line in your post "repeat until exists (UI elements of groups of toolbar 1 of window 1 of application process "Safari" whose name = "Reload this page")" doesn't work on my machine. If this doesn't work on your machine, you can change it back to your line.
end repeat
end tell
end tell
inputByID("sb_form_q", "coronavirus")
delay 0.3
--clickID("sb_form_q")
--delay 0.3
--3rd edit: I find this line is not nessary on my machine. Do you mean you use "arrow down" to show the autofill menu in your post? On my machine, after input "coronavirus", the dropdown autofill menu appears immediately and automatically. No need to use "arrow down" to show the dropdown menu.
tell application "System Events"
set randomNumber to random number from 1 to 8
repeat randomNumber times
key code 125
delay 0.3
end repeat
end tell
--delay (random number from 5 to 15)
clickID("sb_form_go")
--delay 0.5
--4th edit: actually i am not sure I understand what does the word "Randomly" mean in your post. I change the code here to select option in autofill randomly then execute the search.
tell application "System Events"
delay 2
keystroke "w" using command down
end tell
to inputByID(theID, theValue)
tell application "Safari"
do JavaScript " document.getElementById('" & theID & "').value ='" & theValue & "';" in document 1
end tell
end inputByID
to clickID(theID)
tell application "Safari"
do JavaScript "document.getElementById('" & theID & "').click();" in document 1
end tell
end clickID
My Safari version is "Version 10.1.2 (12603.3.8)" # MacOS 10.12.6. Let me know if it helps.

AppleScript Automation - Bulk uploading files in folder to a single-upload form

(First time with AppleScript...) I'm trying to bulk upload files from a local folder to a server via a single-upload form (legacy serverside software behind ddos wall, no control over it)
As I understand:
I can loop through each file in the filesystem.
With each file: Invoke "tell" Safari"
Invoke javascript to "click" a button by ID
file upload dialog, select the file to upload (?)
I'm having some trouble with syntax in implementing that...
(Also, if that's not the right/best approach, please provide a better one below!)
on run
tell application "Finder"
set mlist to (every file of folder "Macintosh HD:Users:username:filestouploadfolder") as alias list
repeat with this_file in mlist
tell application "Safari"
activate
do JavaScript "document.getElementById('selectToOpenFileDialog').click();" in document 1
choose file this_file
end tell
end repeat
end tell
return 0
end run
Hacked up a solution though it could probably be more elegant
on run
tell application "Finder"
set mfolder to "Macintosh HD:Users:yosun:png:"
set myFiles to name of every file of folder mfolder
end tell
repeat with aFile in myFiles
tell application "Safari"
activate
delay 1
do JavaScript "document.getElementById('addDeviceTargetUserView').click();" in document 1
delay 1
do JavaScript "document.getElementById('targetDimension').value=10;" in document 1
do JavaScript "document.getElementById('targetImgFile').click();" in document 1
end tell
tell application "System Events"
keystroke "G" using {command down, shift down}
delay 1
keystroke "~/png/" & aFile as string
delay 1
keystroke return
delay 1
keystroke return
delay 1
end tell
tell application "Safari"
activate
delay 1
do JavaScript "document.getElementById('AddDeviceTargetBtn').click();" in document 1
end tell
delay 10
end repeat
end run

File attachments are not attaching with applescript

I wrote a little applescript that attaches files in safari. Everything happens exactly how it would if I were attaching them manually, but for some unknown reason the attachments are not uploading even though they are being selected and submitted correctly.
I've spent a couple hours troubleshooting this and trying different variations with no success.
Here is the code that attaches them. I'm using cliclick in addition to applescript, which emulates mouse clicks.
set posix to "/Users/ea/Desktop/Guru/Deliverables" --set folder path
tell application "System Events"
keystroke "g" using {shift down, command down}
keystroke posix
delay 1
keystroke return
delay 2
keystroke "a" using {command down}
delay 5
do shell script "/usr/local/bin/cliclick m:381,339"
delay 3
do shell script "/usr/local/bin/cliclick m:818,590"
delay 2
do shell script "/usr/local/bin/cliclick tc:."
delay 2
end tell
files selected like they're supposed to
Upon clicking choose, nothing uploads.
Try This
tell application "Google Chrome" --Whatever your using
activate
set posix to "/Users/ea/Desktop/Guru/Deliverables/private" --are the files in here
tell application "System Events"
keystroke "g" using {shift down, command down}
keystroke posix
delay 1
keystroke return
delay 2
keystroke "a" using {command down}
delay 5
key code 36
end tell
end tell
Out of curiosity did you ever get it to work? and what do you mean by attaches files in safari? its select the files after you have the "file choosing window" open?
i know this is late, and someone seems to have already answered your question, but yeah you didn't respond to say if it worked or not, so heres something similar i created that might help:
to clickClassName(theClassName, elementnum)
tell application "Safari"
do JavaScript "document.getElementsByClassName('" & theClassName & "')[" & elementnum & "].click();" in document 1
end tell
end clickClassName
to getValueFromClass(theclass, num)
tell application "Safari"
tell document 1
set theFirstTableHTML to do JavaScript "\n document.getElementsByClassName('" & theclass & "')[" & num & "].value"
return theFirstTableHTML
end tell
end tell
end getValueFromClass
on run
choose file with prompt "Which folder would like average file size calculated?"
open {result}
end run
on open theimage
--tell application "Finder" to set xx to every file in item 1 of theimage
--display dialog "Hey! the file's alias is: " & (path of theimage as string)
--display dialog theimage
set filepath to POSIX path of theimage
tell application "Safari" to open location "https://upload.vstanced.com"
delay 2
clickClassName("btn btn-big white outline", 0)
tell application "System Events"
activate application "Safari"
delay 0.5
keystroke "g" using {shift down, command down} --open goto
set the clipboard to filepath
keystroke "v" using {command down}
delay 0.7
keystroke return -- enter goto text
delay 0.4
keystroke return --press enter on file
end tell
delay 1
clickClassName("btn btn-big green", 0)
set thedirectlink to ""
repeat 15 times
set thedirectlink to getValueFromClass("r2", 1)
delay 1
if thedirectlink is not equal to "" then
exit repeat
end if
end repeat
set the clipboard to thedirectlink
tell application "Safari" to close current tab of window 1
display notification "Upload complete" with title "VStanced Upload" subtitle thedirectlink sound name "blow"
end open
This is a droplet (in script editor save it with "file format" as "application"), when you drag an image onto it, it opens up an image hosting site, uses javascript to open the "file choosing window", and then pretty much does exactly what yours does.
After it selects and enters the file, it also selects "direct link" from a little "toggle button" thingy, copies the direct link to the clipboard, closes the tab, plays sound and shows a notification. if you end up trying it and it doesnt work, lengthen the delays. it does everything (i'm 99% sure). But if safari's not already open it will probably take too long to get to the page and that will mess everything up. Im also going to try to add parts that "wait" for the page to load.

Applescript: GUI Programming for recursively printing to PDF for Appleworks

The Goal: thousands of old Clarisworks and Appleworks documents need to become PDFs.
The Problem: Scripting Pages is not an option; it doesn't preserve formatting reliably. It must be Appleworks. This script works, when it works; but it skips files for reasons I cannot identify.
set appleworksFolder to choose folder
tell application "Finder"
set folderItems to (files of entire contents of appleworksFolder)
repeat with I from 1 to number of items in folderItems
set the_doc to item I of folderItems
set doc_name to name of the_doc as text
(* Some files are missing metatags and try to save as "Untitled Document",
this block ensures a file name is unique, later *)
tell application "Finder"
set the clipboard to doc_name & ".pdf"
end tell
(* Each file exists in a folder with a path.txt file that will later
be used to put the file back where it was originally stored prior
to this conversion process *)
if name of the_doc is not "path.txt" then
try
tell application "Finder"
(* Many files no longer have name extensions and appear as UNIX
executables if not repaired *)
try
set nmex to name extension of the_doc as text
on error
set nmex to "ok"
end try
if nmex is not "cwk" or "CWK" then
set the_doc_str to the_doc as text
set doc_path to POSIX path of the_doc_str
do shell script "mv '" & doc_path & "' " & "'" & doc_path & ".cwk'"
end if
delay 1
(* In case Appleworks hangs or otherwise bungs up, I force-quit
it at the end of the script; this ensures its closed before
it tries to open the next file *)
if (application process "Appleworks 6" of application "System Events" exists) then
do shell script "killall 'LaunchCFMApp'"
delay 1
end if
tell application "AppleWorks 6"
open the_doc
end tell
(* Some of the documents are huge, this delay gives the app time to load
since this is all GUI-scripted *)
delay 5
tell application process "Appleworks 6" of application "System Events"
(* This is where I think I am encountering problems; there are two
possible warnings that may or may not appear on opening the doc;
that Appleworks needs to append a version number to the file (if
its old) or that the file may be damaged and thus would need to be
skipped. I get system beeps sometimes in this part, but I don't know
why! *)
if (button "OK" of window 1 of application process "AppleWorks 6" of application "System Events" exists) then
delay 0.5
keystroke return
delay 0.5
end if
delay 2
if (button "OK" of window 1 of application process "AppleWorks 6" of application "System Events" exists) then
delay 0.5
keystroke return
delay 0.5
end if
delay 2
(* If the document loads, the Appleworks welcome pane won't be there;
this part of the script works flawlessly, when it happens. Sometimes
documents are outside of print margins, hence the press-ok-button
conditional *)
if not (window "Starting Points" of application process "AppleWorks 6" of application "System Events" exists) then
tell application process "Appleworks 6" of application "System Events"
keystroke "p" using command down
delay 1
click menu button "PDF" of window "Print" of application process "AppleWorks 6" of application "System Events"
delay 1
click menu item "Save as PDF…" of menu "PDF" of menu button "PDF" of window "Print" of application process "AppleWorks 6" of application "System Events"
delay 1
keystroke "v" using command down
click button "Save" of window "Save" of application process "AppleWorks 6" of application "System Events"
delay 8
keystroke "w" using command down
delay 0.5
if (button 1 of window 1 of application process "AppleWorks 6" of application "System Events" exists) then
delay 0.5
keystroke "d" using command down
delay 0.5
end if
delay 0.5
end tell
end if
do shell script "killall 'LaunchCFMApp'"
delay 3
end tell
end tell
end try
end if
end repeat
end tell
I want to this baby to just run through a couple of thousands files over a weekend and create PDFs, but each time I run it overnight I find a few hundred correctly processed documents, hundreds or thousands of skipped documents, and often a print dialogue for the script itself, which obviously comes from using Command+P outside of an Appleworks context. I'm an Applescript noob, to be sure, this has been driving me nuts for weeks!
This is not really a solution to your problem, as it is highly specific and tailored, but I have some possibly useful tips:
You don't need to close the "Starting Points" window. It will be dismissed when you tell AppleWorks to open a document.
Try to use as few simulated keystrokes as possible. For instance, instead of simulating cmd+P to open the Print dialog, it is better to simulate a click on the application's Print… command inside its File menu:
click menu item "Print…" of menu "File" of menu bar 1 of application process "AppleWorks 6"
and instead of performing a cmd+W to close the document, the proper way is to do
tell application "AppleWorks 6" to close front document saving no
If you do need to simulate keystrokes, ensure that they are received by the correct application, by activating it just before the keystroke command with a tell app "AppleWorks 6" to activate, just in case it wasn't the frontmost app at that point.
When checking for additional dialog windows, it's a bad idea to use if (button 1 of window 1 of (*...*) exists), because on my machine, for instance, checking this while the "Print" dialog was open caused the script to hang for minutes (possibly for good), and because it's always advisable to address a window by name rather than number (since window 1 is simply the frontmost window, and that can quickly become another). So, it's better to check if a window named such and such exists.
In order to process thousands of files where you expect errors to happen, you need to restructure your code and rethink the error handling. For instance, you could log error information to a text file in order to see which files have been skipped and why. To separate program logic from the error handling and details, you could use handlers, so your main loop could look like this:
set logfile to alias "Macintosh HD:Users:user:Desktop:errors.log"
open for access logfile with write permission
repeat with the_doc in folderItems
try
open_with_appleworks(the_doc)
print_to_pdf()
close_document_and_quit_appleworks()
on error error_message
write error_message & "\n" to logfile
close_document_and_quit_appleworks()
end try
end repeat
close access logfile
In a similar way, you can log success messages, too. In the single handlers, then, you are supposed to specify details about how exactly to do every step. They can also provide some error information to be logged:
to open_with_appleworks(the_doc)
tell app "AppleWorks 6"
try
(* do your stuff here *)
on error err_msg
(* re-signal the error to main loop! *)
error "Failed to open " & (the_doc as text) & err_msg
end try
end tell
end open_with_appleworks
Hard-coded delays slow down your script considerably: while only some of your documents might take a few seconds to open, your script will always wait 5 seconds – even if the document opens instantaneously. It's better to have a loop check whether the document has opened yet:
repeat while not (exists front document)
-- just wait
-- or, delay 0.1 -- if you really want to
end repeat
The same holds for nearly all your other delays, most of them can be avoided, as they will be much shorter or longer in practice. Instead of delaying half a second, hoping that the warning or dialog will have appeared by then, it's better to check whether it's there or not.
(one last tip and then I'll be quiet) Unfortunately, you seem to already have gone through the trouble of copying all the files to be processed into one folder, remembering in some "path.txt" where they came from... This can be avoided if the files are in sub-folders of some folder and can be found by a filter: You just create a "smsart folder" with that specific search (example), then say in your AppleScript set theFiles to choose file with multiple selections allowed, then select all files in that particular smart folder, and you're good. The script can then easily find out where a file f is by asking application "Finder" to get parent of f (that will return a Finder file reference, which can be easily converted to an alias and to a POSIX file address). Then, since you know the name and location of the PDF file generated by "printing" the document, you can move that PDF to the folder where its source document is.