concise way to write commands to repeat a keystroke, or a number of KS

Hey, I’ve got a script GUI script for the safari save menu with tab & 0.1 delay repeated 22 times in total (not sure if the delay’s necessary in GUI scripts do tell). It would be better to write it more concisely how could I write, (press tab)*7times or (press tab, delay 0.1)*7times in one line?

THIS IS THE CODE I’M TRYING TO MAKE AS SHORT AS POSSIBLE:

Do I need the delays? How can I repeat tab or tab, delay more concisely.

   tell application "Safari"
       activate
       delay 0.1
       tell application "System Events"
           tell process "Safari"
               --this will only save stuff in one place
               click menu item "Save As." of menu 1 of menu bar item "File" of menu bar 1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke (ASCII character 31) --> down arrow
               delay 0.1
               keystroke "pa"
               delay 0.1
               keystroke return
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke pOSIXPathOfTargetFile
               key down return
               key up return
               delay 0.2
               key down return
               key up return
           end tell
           tell process "Safari"
               --this will only save stuff in one place
               click menu item "Save As." of menu 1 of menu bar item "File" of menu bar 1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke (ASCII character 31) --> down arrow
               delay 0.1
               keystroke "we"
               delay 0.1
               keystroke return
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke tab
               delay 0.1
               keystroke pOSIXPathOfTargetFile
               key down return
               key up return
               delay 0.2
               key down return
               key up return
           end tell
       end tell
   end tell

My first answer would be impossible because it makes your code unreadable. But of course it is not impossible. First of all you need to overwrite the delay command in AppleScript so it returns a value. Then you can do multiple commands on one line to set them in an list.

{delay 1, display dialog "hello"}

on delay __seconds
	continue delay __seconds
	return null
end delay

I see you’ve changed your post.

this:

keystroke tab
delay 0.1
keystroke tab
delay 0.1
keystroke tab
delay 0.1
keystroke tab
delay 0.1
keystroke tab
delay 0.1
keystroke tab
delay 0.1
keystroke tab
delay 0.1

could be written down as

repeat 7 times
keystroke tab
delay 0.1
end repeat 

great, thanks! do you need the delays for maneuvering the GUI with tab, enter, arrow keys etc? It works without them but I’m not sure if it’s as robust.

Hey Zeus Christie…

The following is working reasonably well in Tiger 10.4.11. Maybe you can actually get it to work in your environment.



tell application "Safari"
	activate
	
	set doc_name to name of window 1
	set doc_type to URL of document 1
	
	if doc_name ends with ".com" then
		set corrected_doc_name to characters 1 through -5 of doc_name as string
	else
		set corrected_doc_name to doc_name
	end if
	
	if doc_type ends with ".webarchive" then
		set web_archive to "True"
		set doc_source to source of document 1
	else
		set web_archive to "False"
	end if
	
end tell

tell application "Safari" to activate

--

if web_archive is "True" then
	
	tell me to activate
	display dialog "Save Web Archive as..." default answer corrected_doc_name buttons {"Cancel", "PDF", "Source"} default button 3 with title "Safari - Save Document"
	
	copy the result as list to {text_returned, button_returned}
	
	set preferred_doc_name to text_returned
	
	tell application "Safari" to activate
	
	--
	
	if button_returned is "Source" then
		
		set output_file to (path to desktop as text) & preferred_doc_name & ".html"
		
		try
			open for access file output_file with write permission
			write doc_source & return to file output_file starting at eof as «class utf8»
			close access file output_file
		on error
			close access file output_file
		end try
		
	end if
	
	tell application "Safari" to activate
	
	--
	
	if button_returned is "PDF" then
		
		tell application "Safari" to activate
		
		tell application "System Events"
			
			tell process "Safari" -- save as PDF
				
				
				try
					
					-- repeat until sheet 1 of window 1 exists
					keystroke "p" using command down -- print dialog appears
					-- end repeat
					
				end try
				
				set PDF_button to UI element 6 of UI element 4 of sheet 1 of window 1
				
				set {ButtonLeft, ButtonTop} to position of PDF_button
				set {ButtonWidth, ButtonHeight} to size of PDF_button
				set clickX to ButtonLeft + (ButtonWidth div 2)
				set clickY to ButtonTop + (ButtonHeight div 2)
				
				click at {clickX, clickY}
				
				delay 1
				
				keystroke (ASCII character 31)
				keystroke (ASCII character 3)
				
				delay 1.4 -- save dialog appears
				
				keystroke "d" using command down
				
				delay 0.2
				
				if doc_name ends with ".com" or preferred_doc_name is not corrected_doc_name then
					
					keystroke preferred_doc_name -- else leave page name as is...
					
					delay 0.2
					
				end if
				
				delay 1
				
				keystroke (ASCII character 3)
				
			end tell
			
		end tell
		
		tell application "Safari" to activate
		
	end if
	
end if

tell application "Safari" to activate

----------

if web_archive is not "True" then
	
	if page_loaded(30) then
		
		tell me to activate
		display dialog "Save as..." default answer corrected_doc_name buttons {"PDF", "Source", "Archive"} default button 3 with title "Safari - Save Document"
		
		copy the result as list to {text_returned, button_returned}
		
		set preferred_doc_name to text_returned
		
		tell application "Safari" to activate
		
	end if
	
	tell application "Safari" to activate
	
	--
	
	if button_returned is "PDF" then
		
		tell application "Safari" to activate
		
		tell application "System Events"
			
			tell process "Safari" -- save as PDF
				
				
				try
					
					-- repeat until sheet 1 of window 1 exists
					keystroke "p" using command down -- print dialog appears
					-- end repeat
					
				end try
				
				set PDF_button to UI element 6 of UI element 4 of sheet 1 of window 1
				
				set {ButtonLeft, ButtonTop} to position of PDF_button
				set {ButtonWidth, ButtonHeight} to size of PDF_button
				set clickX to ButtonLeft + (ButtonWidth div 2)
				set clickY to ButtonTop + (ButtonHeight div 2)
				
				click at {clickX, clickY}
				
				delay 1
				
				keystroke (ASCII character 31)
				keystroke (ASCII character 3)
				
				delay 1.4 -- save dialog appears
				
				keystroke "d" using command down
				
				delay 0.2
				
				if doc_name ends with ".com" or preferred_doc_name is not corrected_doc_name then
					
					keystroke preferred_doc_name -- else leave page name as is...
					
					delay 0.2
					
				end if
				
				delay 1
				
				keystroke (ASCII character 3)
				
			end tell
			
		end tell
		
		tell application "Safari" to activate
		
	end if
	
	tell application "Safari" to activate
	
	--
	
	if button_returned is "Source" then
		
		tell application "Safari" to activate
		
		tell application "System Events"
			
			tell process "Safari" -- save as Source
				
				try
					
					-- repeat until sheet 1 of window 1 exists
					keystroke "s" using command down -- save dialog appears
					-- end repeat
					
				end try
				
				set save_as to get value of pop up button 1 of group 1 of sheet 1 of window 1
				
				if save_as is "Web Archive" then
					
					click pop up button 1 of group 1 of sheet 1 of window 1
					
					delay 0.2
					
					keystroke (ASCII character 30)
					keystroke (ASCII character 3)
					
					delay 0.4
					
				end if
				
				delay 0.2
				
				keystroke "d" using command down
				
				delay 0.2
				
				if doc_name ends with ".com" or preferred_doc_name is not corrected_doc_name then
					
					keystroke preferred_doc_name -- else leave page name as is...
					
					delay 0.2
					
				end if
				
				delay 1
				
				click button 1 of sheet 1 of window 1 -- worth a try...
				
				-- keystroke (ASCII character 3) -- if keystroked doc name is long, often won't dismiss dialog
				
			end tell
			
		end tell
		
		tell application "Safari" to activate
		
	end if
	
	tell application "Safari" to activate
	
	--
	
	if button_returned is "Archive" then
		
		tell application "Safari" to activate
		
		tell application "System Events"
			
			tell process "Safari" -- save as Archive
				
				try
					
					-- repeat until sheet 1 of window 1 exists
					keystroke "s" using command down -- save dialog appears
					-- end repeat
					
				end try
				
				set save_as to get value of pop up button 1 of group 1 of sheet 1 of window 1
				
				if save_as is "Page Source" then
					
					click pop up button 1 of group 1 of sheet 1 of window 1
					
					delay 0.2
					
					keystroke (ASCII character 31)
					keystroke (ASCII character 3)
					
					delay 0.4
					
				end if
				
				delay 0.2
				
				keystroke "d" using command down
				
				delay 0.2
				
				if doc_name ends with ".com" or preferred_doc_name is not corrected_doc_name then
					
					keystroke preferred_doc_name -- else leave page name as is...
					
					delay 0.2
					
				end if
				
				delay 1
				
				click button 1 of sheet 1 of window 1 -- worth a try...
				
				-- keystroke (ASCII character 3) -- if keystroked doc name is long, often won't dismiss dialog
				
			end tell
			
		end tell
		
		tell application "Safari" to activate
		
	end if
	
	--
	
	tell application "Safari" to activate
	
end if

tell application "Safari" to activate

-----

on page_loaded(timeout_value) -- in seconds
	--delay 2
	repeat with i from 1 to timeout_value
		tell application "Safari"
			if name of current tab of window 1 is not "Loading" then exit repeat
		end tell
		--delay 1
	end repeat
	if i is timeout_value then return false
	tell application "Safari"
		repeat until (do JavaScript "document.readyState" in document 1) is "complete"
			delay 0.5
		end repeat
	end tell
	return true
end page_loaded


(The page load routine borrows heavily from one by StefanK…)

Peter B.


  1. Hmm. Safari’s AppleScript ‘save’ command appears to be pretty ineffective ” with version 5.1 in Snow Leopard anyway. :confused:

  2. The effect of all that tabbing in Ole’s script depends on a setting at the bottom of the “Keyboard Shortcuts” tab in the user’s “Keyboard” preferences. If the setting’s “Text boxes and lists only”, as on my machine, the effect is utter rubbish. If it’s “All controls” ” and a suitable delay is inserted to ensure that the “Save As.” dialog has opened before the tabbing begins ” the focus will eventually end up on the pop-up button which allows the choice between saving as “Page Source” or as “Web Archive”. Presumably that’s the situation on Ole’s machine.

The number of tabs can be reduced from 7 to 4 by back-tabbing:

tell application "System Events"
	repeat 4 times
		keystroke tab using {shift down}
	end repeat
end tell

However, things go faster if the button and its menu are addressed directly:

tell application "System Events"
	tell application process "Safari"
		set frontmost to true
		keystroke "s" using {command down}
		tell sheet 1 of window 1
			repeat until (it exists)
				delay 0.2
			end repeat
			tell pop up button 1 of group 1
				click
				click menu item "Web Archive" of menu 1 -- Assuming an English language display.
			end tell
		end tell
	end tell
end tell

This has the additional advantage of being independent of the user’s keyboard preferences, although like Ole’s script, it’s language dependent and (probably) system-version dependent.

  1. That still leaves the problem of saving the document in the right place. If I type a POSIX path into the “Export As:” field, the file appears wherever the dialog’s pointing at that moment with the entire POSIX path as its name. The POSIX path has to go into the additional sheet which appears in response to the Shift-Command-“g” keystroke.
set pOSIXPathOfTargetFile to POSIX path of (path to desktop) & "Test" -- Just for testing,

tell application "System Events"
	tell application process "Safari"
		set frontmost to true
		keystroke "s" using {command down}
		tell sheet 1 of window 1
			-- Wait for the "Save As." dialog sheet.
			repeat until (it exists)
				delay 0.2
			end repeat
			-- Set the "Page Source/Web Archive" pop-up.
			tell pop up button 1 of group 1
				click
				click menu item "Web Archive" of menu 1 -- Assuming an English language display.
			end tell
			
			keystroke "g" using {shift down, command down} -- or of course: keystroke "G" using {command down}
			tell sheet 1 -- sheet 1 of sheet 1 of group window 1
				-- Wait for the "Go to folder:" sheet.
				repeat until (it exists)
					delay 0.2
				end repeat
				-- Set the value of the text field.
				set value of text field 1 to pOSIXPathOfTargetFile
				-- Dismiss the sheet and wait for it to go.
				keystroke return
				repeat while (it exists)
					delay 0.2
				end repeat
			end tell
			-- The folder from the path has been selected (assuming it exists) and the file name has been entered into the "Save As." sheet's "Export As:" text field with the appropriate extension.
			-- Hit return to dismiss the dialog and perform the save. 
			keystroke return
		end tell
	end tell
end tell

You could write something similar for saving the page source, but this is faster and less obtrusive:

set pOSIXPathOfTargetFile to POSIX path of (path to desktop) & "Test.html"

tell application "Safari" to set sourceText to source of document 1

set fRef to (open for access pOSIXPathOfTargetFile with write permission)
try
	set eof fRef to 0
	write sourceText as string to fRef
end try
close access fRef

Never coerce a record to list and assume that the items will be in any particular order! (Unless it’s a one-property record, of course. ;)) Use:

display dialog "Save Web Archive as..." default answer "" buttons {"Cancel", "PDF", "Source"} default button 3 with title "Safari - Save Document"
set {text_returned, button_returned} to {text returned, button returned} of result

Or:

display dialog "Save Web Archive as..." default answer "" buttons {"Cancel", "PDF", "Source"} default button 3 with title "Safari - Save Document"
set {text returned:text_returned, button returned:button_returned} to result

Hmm…

Once again, it was ‘borrowed’ code from a (now) very old script, as I recall.

I will use one of your examples in future.

Thanks, Nigel.

The script itself is also something of a kludge, but hey… it works here.

Peter B.