How to close all, or only some, tabs in Safari using AppleScript? - scripting

I have made a very simple AppleScript to close tabs in Safari. The problem is, it works, but not completely. Only a couple of tabs are closed. Here's the code:
tell application "Safari"
repeat with aWindow in windows
repeat with aTab in tabs of aWindow
if [some condition is encountered] then
aTab close
end if
end repeat
end repeat
end tell
I've also tried this script:
tell application "Safari"
repeat with i from 0 to the number of items in windows
set aWindow to item i of windows
repeat with j from 0 to the number of tabs in aWindow
set aTab to item j of tabs of aWindow
if [some condition is encountered] then
aTab close
end if
end repeat
end repeat
end tell
... but it does not work either (same behavior).
I tried that on my system (MacBook Pro jan 2008), as well as on a Mac Pro G5 under Tiger and the script fails on both, albeit with a much less descriptive error on Tiger.
Running the script a few times closes a few tab each time until none is left, but always fails with the same error after closing a few tabs. Under Leopard I get an out of bounds error. Since I am using fast enumeration (not using "repeat from 0 to number of items in windows") I don't see how I can get an out of bounds error with this...
My goal is to use the Cocoa Scripting Bridge to close tabs in Safari from my Objective-C Cocoa application but the Scripting Bridge fails in the same manner. The non-deletable tabs show as NULL in the Xcode debugger, while the other tabs are valid objects from which I can get values back (such as their title). In fact I tried with the Scripting Bridge first then told myself why not try this directly in AppleScript and I was surprised to see the same results.
I must have a glaring omission or something in there... (seems like a bug in Safari AppleScript support to me... :S) I've used repeat loops and Obj-C 2.0 fast enumeration to iterate through collections before with zero problems, so I really don't see what's wrong here.
Anyone can help?
Thanks in advance!

I have a script that closes all of the tabs but does not need a repeat loop.
set closeTab to "Stack Overflow" as string
tell application "Safari"
close (every tab of window 1 whose name is not equal to closeTab)
end tell
See if that works for you.
Note: change "Stack Overflow" to whatever the title name is of
the tab you want to stay open.

this works for me nice and simple
tell application "Safari"
close every window
end tell
ok you have to go from the count to 1 otherwise the count will be off when you close the window
tell application "Safari"
repeat with i from (count of windows) to 1 by -1
repeat with j from (count of tabs of window i) to 1 by -1
set thistab to tab j of window i
set foo to name of thistab
if foo is not equal to "bar" then close thistab
end repeat
end repeat
end tell

Both provided answers are fine, but I think it is better to combine both. This way you will be closing all tabs of all chrome windows, and it is less verbose than the first answer:
set closeTab to "Post Attendee" as string
tell application "Google Chrome"
repeat with i from (count of windows) to 1 by -1
close (every tab of window i whose name is not equal to closeTab)
end repeat
end tell

Related

Why AppleScript doesn't enter Repeat loop

My overall goal is to close Avast window during start up. Performing this repeat until loop does not trigger the code within the loop.
tell application "System Events"
set ids to bundle identifier of every application process
repeat until ids contains "com.avast.AAFM"
wait(1)
beep
set ids to bundle identifier of every application process
end repeat
tell application "System Events" to tell process "Avast" to tell the front window to if (exists) then tell attribute "AXCloseButton" to click its value
end tell
Using a clumsy brute force approach to check if com.avast.AAFM is one of the apps open before quitting. If not then wait a second and check again. I'm not understanding why the repeat loop isn't run. When Avast is running the script does close Avast.
Is wait a valid AppleScript command?
In this case I'd prefer a simple repeat loop and exit repeat
repeat
tell application "System Events" to set ids to bundle identifier of every application process
if ids contains "com.avast.AAFM" then exit repeat
delay 1
beep
end repeat
tell application "System Events" to tell process "Avast" to tell the front window to if (exists) then tell attribute "AXCloseButton" to click its value
You can close (on startup) any application windows simpler:
set appID to "com.avast.AAFM"
repeat until window 1 of application id appID exists
delay 1
end repeat
tell application id appID to close windows

Find Message Number of Popup Dialog Text

I don't find the answer on the internet.
When I open some orders in a transaction, I have sometimes a popup. I want to execute some code if this popup text is 'Consider the subsequent documents'. If it is another text, I will execute some other code.
Problem is, we use SAP in several languages (EN, DE, FR), so I can't just say "if text = 'Consider the subsequent documents'. I know each message has its own ID in SAP. I know how to find this ID manually (see screenshot below), but I can't access it with findById(), even using the Record and Playback tool, because it uses the Performance Assistant window, which is not a children of the SAP session (it's a separate application, see second screenshot).
How could I do?
If session.ActiveWindow.Name = "wnd[1]" Then 'There is a popup
'Find message number (haven't find better way)
session.findById("wnd[1]/tbar[0]/btn[1]").press 'Press "Info"
'Line missing here to click the button in Performance Assistant Window. Record & Playback tool does not catch it.
If session.findById("wnd[2]/usr/txtHELP_INFO-MESSAGENR").Text = "081" Then
'Some code
End If
'close popups
session.findById("wnd[2]").sendVKey 0
session.findById("wnd[1]").sendVKey 0
End If
I found out that it is possible to display the Help in a Modal window (=Popup) rather than in a separate window Performance Assistant. Then it is possible with findById() functions to get the message number Popup raises > F1 > F9 > findByID(...).Text
To set the Help in a Modal window, the user must first (manually or programmatically) check a setting:

Scripting Safari to open 10 URLs and then rotate through through then about every 15 seconds

I need to be able to open roughly 10 URLs in Safari and then rotate through them, pausing on each for about 15 seconds. I'd also like the Safari window to be maximized.
I tried this with a simple Javascript, but it causes the windows to refresh every time and that's distracting. So, I think AppleScript with Safari would be a "cleaner" approach
Here's what I've started with:
tell application "Safari"
activate
tell window 1
make new tab with properties {URL:"http://news.yahoo.com"}
make new tab with properties {URL:"http://news.google.com"}
make new tab with properties {URL:"http://www.macintouch.com"}
set current tab to tab 1
set numTabs to number of tabs
end tell
activate
tell window 1
repeat with j from 1 to (count of tabs of window 1) by 1
set current tab to j
activate
delay 5
end repeat
end tell
end tell
This AppleScript code works for me using the latest version of macOS Mojave.
Be sure to fill in the remaining 7 URL's. I think this following code achieves what you are looking for.
tell application "Safari"
activate
if not (exists of window 1) then make new document
tell window 1
set startingTabForRotating to make new tab with properties {URL:"http://news.yahoo.com"} -- URL 1
make new tab with properties {URL:"http://news.google.com"} -- URL 2
--make new tab with properties {URL:"something.com"}
--make new tab with properties {URL:"something.com"}
--make new tab with properties {URL:"something.com"}
--make new tab with properties {URL:"something.com"}
--make new tab with properties {URL:"something.com"}
--make new tab with properties {URL:"something.com"}
--make new tab with properties {URL:"something.com"}
set endingTabForRotating to make new tab with properties {URL:"http://www.macintouch.com"} -- URL 10
set current tab to startingTabForRotating
delay 1
tell application "System Events"
if exists of UI element 3 of window 1 of application process "Safari" then
click UI element 3 of window 1 of application process "Safari" -- Maximizes Safari
end if
end tell
repeat until current tab is endingTabForRotating
delay 15
set current tab to tab ((index of current tab) + 1)
end repeat
end tell
end tell
I've taken a slightly different approach to #wch1zpink. In the script below, you can edit and add to the property URLs, which will define what tabs to open whilst keeping them collected at the top of the script and separate from the main body of the code. You can supply nonsense URLs if you wish, as these are dealt with gracefully by the script.
I've also elected to maximise the window rather than go fullscreen, but if you prefer fullscreen, then this is easily done and I've included a snippet at the end to demonstrate a clean way to achieve this.
property URLs : {"https://google.com", ¬
"https://dropbox.com", ¬
"https://imdb.com", ¬
"https://bbc.co.uk", ¬
"foobar blah blah", ¬
"https://disney.com"}
property errorPage : "safari-resource:/ErrorPage.html"
property time : 15 -- time (seconds) between tab switches
tell application "System Events" to set screenSize to ¬
the size of scroll area 1 of process "Finder"
tell application "Safari"
make new document with properties {URL:the first item in the URLs}
tell the front window
set W to it
repeat with URL in the rest of the URLs
make new tab with properties {URL:URL}
end repeat
set the bounds to {0, 0} & the screenSize
activate
tell (a reference to the current tab) to repeat
delay my time
if not (W exists) then return
set N to the number of tabs in W
try
set contents to tab (index mod N + 1) in W
end try
if its URL = errorPage then close
end repeat
end tell
end tell
The other property you can set is time, which defines how long (in seconds) to pause on each tab, presently set to 15 seconds. The tabs rotate in turn, but do so on a continuous loop until you close the window. Any tabs with nonsense URLs are immediately closed just before coming into focus, so the transition to the next sensible tab is actually seamless and you won't notice the closures.
You can still open new tabs as you please (within the 15-second window), and you can switch to whichever tab you desire manually. The progression will continue from whichever tab you select acting as the new starting point.
Fullscreen
Currently, the script sets the size of the window to the maximum area available on your screen which, in modern versions of macOS, should take into account the menu bar and dock without obscuring them.
If you prefer a fullscreen mode, then you would replace this line:
set the bounds to {0, 0} & the screenSize
with this:
tell application "System Events" to set value of attribute ¬
"AXFullScreen" of front window of process "Safari" to true
Strictly speaking, you ought to try and sit this line outside of the tell application "Safari" block, by splitting the Safari block into two; coming out of the first block to enact the fullscreen mode; then re-entering the second block. But Safari won't die if you don't do this, so a simple cut-and-paste that exchanges one line for the other will suffice, as long as you appreciate that this would be considered lazy and poor form.

Get NSTextFieldCell value using AppleScript

I am trying to automate a few tasks where I need to get the text displayed on a dialog box (the 6 digit code).
Accessibility Inspector revealed the following hierarchy:
I am a beginner in the field of AppleScript and have read a few examples where something similar could be done like this:
set myText to textField's stringValue() as text
But I'm not sure if this could work in my case, as the Accessibility Inspector does not show any variable names for NSTextFieldCell which contains the 6 digit code.
How can I extract the 6 digit code in the NSTextFieldCell and possibly return this value so that a shell script can use this code?
I have something like this right now -
tell application "FollowUpUI"
activate
# get the 6 digit code
end tell
Update
After some help, i have tried to traverse to the text field
tell application "System Events"
repeat with theProcess in processes
#initialize
tell theProcess
set processName to name
set allWindows to windows
end tell
#check if process exists
if processName is "FollowUpUI" then
activate
say "FollowUpUI found"
set windowsCount to count of the allWindows
#only one window should exist
if windowsCount is 1 then
say "1 window was found"
tell window 1
tell group 1
tell text field 1
set code to value
end tell
end tell
end tell
end if
end if
end repeat
end tell
but i've got stuck because of an error -
System Events got an error: cant get window 1. Invalid index.
I am not sure if this is a syntax error. Any pointers would be helpful. Thanks
The error occurs because you have to reference window 1 of process "FollowUpUI"
Your code is too complicated, you just need to check if the process exists
tell application "System Events"
if exists process "FollowUpUI" then
tell process "FollowUpUI"
tell window 1
tell static text 1 of group 1
set code to its value
end tell
end tell
end tell
end if
end tell
If the code is a part of a workflow you have to wait until the window is open
tell application "System Events"
repeat until exists window 1 of process "FollowUpUI"
delay 0.2
end repeat
tell window 1 of process "FollowUpUI"
tell static text 1 of group 1
set code to its value
end tell
end tell
end tell
I added its before value to make sure that it refers to the current reference (the text field)
As you don't send key or mouse events to the window you don't need to activate anything.
Not an answer but I can't paste code in a comment. Here's a trick to get the UI Element. Hit Command-Shift-4 to get a cursor with coordinates. Aim at the UI Element and remember the coordinates. Click to get the arrow cursor back. Use the coordinates in this script.
activate application "FollowUpUI"
tell application "System Events"
tell application process "FollowUpUI"
click at {290, 150}
end tell
end tell
Script Editor must be allowed to control the computer. You can set this in the System prefs, Security & Privacy, Privacy.

AppleScript weirdness

I'm using this AppleScript to cycle through the tabs in Safari for a "dashboard" at work:
tell application "Safari"
repeat
repeat with i from (count of tabs of window 1) to 1 by -1
set thisTab to tab i of window 1
set current tab of window 1 to thisTab
delay 10
end repeat
end repeat
end tell
The problem I am having is that when I put Safari into full screen, I get this error:
error "The variable thisTab is not defined." number -2753 from "thisTab"
Why is this happening and how do I fix it?
Thanks in advance!
It's a bug.
Solution:
tell application "Safari"
repeat
repeat with i from (count of tabs of window 1) to 1 by -1
tell window 1 to set current tab to tab i
delay 10
end repeat
end repeat
end tell