How to tell if Safari has finished loading a web page

I have a macro that logs into a website and then proceeds to go thru multiple pages. It runs 2 reports and then has them downloaded this way. Right now, I put in some pauses to wait for the screen to paint and for activities to complete. But, sometimes, the pauses are ok and sometimes it takes longer. I would like to know if there is a command I can use that says “Wait until the next screen paints before proceeding” and “wait until the activity is completed”. The URL of these pages are dynamic. Is there a way?

1 Like

Under safari actions there is one called “wait on til webpage loads” or something like this. That should be useful in this scenario

\ Jimmy Hartington

1 Like

Thank you - Yes, I’m currently using this, but I’m still confused as to why I have to enter # of seconds - I assumed the function checks for the web page load already.

Also, I find that there are times when the macro gets ahead of itself - e.g., I have to enter several criteria on the report screen. I use the PAUSE for X seconds function between the fields already. Is there a way to tell Keyboard Maestro to WAIT until EACH COMMAND has been made before moving onto the next command other than the PAUSE function? All these PAUSE function entries seems kludgy - I’d like to know if there is a better way to do it.

1 Like

Hello Jo,

Because the javascript.readystate function is imperfect and can return true even when it’s not true. A default delay solves that issue most of the time.

Safari does not have a page-loaded property, so Peter is using javascript behind the scenes - and it is a bit of a kludge.

If you want to play with similar AppleScript code you can:

on SAFARI_PAGE_LOADED(timeout_value)
  delay 2
  repeat with i from 1 to the timeout_value
    tell application "Safari"
      if (do JavaScript "document.readyState" in document 1) is "complete" then
        return true
      else if i is the timeout_value then
        return false
      else
        delay 2
      end if
    end tell
  end repeat
  return false
end SAFARI_PAGE_LOADED

You can play with the timeout value and the delays and see what works for you.

Call the handler like this:

SAFARI_PAGE_LOADED(2)

The handler and the handler call should be in an Execute AppleScript Action.


Best Regards,
Chris

1 Like

Hello Jo,

You might be able to use Pause Until to get around some of that mess.

Do you need to manually enter data into these fields?

You’re aware that Keyboard Maestro can enter form data?

Since we cannot see your process it’s really hard to advise.


Best Regards,
Chris

1 Like

Hello Jo,

When Safari is loading a page that little circle with the arrow icon is an X.

You might be able to use the Wait for Safari to Finish Loading action in combination with a Pause Until Screen Image condition to get a better result.

--
Best Regards,
Chris

1 Like

This is one of my submacros that gives me trouble from time to time. I'm using "Set Safari Field 'document.forms' function. Is that what you mean by KM being able to enter form data? Thank you for your prompt responses thus far. I really appreciate it. If I can eliminate some of these pauses, that would be nice. - Animal Inventory.kmmacros (5.8 KB)

1 Like

Yes.

You should be able to eliminate some at least.

You could probably speed up the whole process by writing all the javascript yourself in one AppleScript Action, but without seeing the process run I cannot be certain.

Another option to consider is Smile a free AppleScript Automation Environment. It can open its own web windows and run javascript within them. (Not for the faint of heart - but very powerful.)


Best Regards,
Chris

1 Like

Thanks!!! Guess it’s time to bite the bullet and learn Javascript :slight_smile:

1 Like

Hey Jo,

Learning a little JavaScript gets your foot in the door.

Learning a lot will let you do magic.

(I have yet to learn a lot.)

Here’s an example AppleScript for filling a form and clicking a radio button:

tell application "Safari"
  tell front document
    do JavaScript "document.forms['form']['clientAcctNum'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['firstName'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['middleInitial'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['lastName'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['address'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['city'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['state'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['zipCode'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['phone'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['workPhone'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['custEmail'].value = '" & "<some-text>" & "'"
    do JavaScript "document.forms['form']['paymentAmount'].value = '" & "<some-text>" & "'"
    do JavaScript "document.getElementById('radioCreditCard').click()"
  end tell
end tell

I used Keyboard Maestro to discover the majority of the syntax, and then I Googled to refine that into something AppleScript would accept.

This particular form fills around 5 times faster with the AppleScript action than with the equivalent Safari Actions.


Best Regards,
Chris

2 Likes

Here’s how to open a web window in Smile.

tell application "Smile"
  activate
  set webWin to make new web window with properties {path name:"http://macscripter.net/viewtopic.php?pid=178569#p178569"}
end tell

The web window returns a pageloaded event like this:

script callback
  on pageloaded webWin
    save webWin in "~/Desktop/file.pdf"
    say "PDF exported."
  end pageloaded
end script

set _url to "http://macscripter.net/viewtopic.php?pid=178569#p178569"

make new web window with properties {path name:_url, script:callback, visible:true}

The generated pdf is currently blank, but I have a query in to the Smile User List to solve that little problem.

-ccs

1 Like

You could just use the Execute JavaScript in Safari action, which is pretty much functionally equivalent.

The Keyboard Maestro variables are available from within the Execute JavaScript in Safari action via document.kmvar.Variable_Name, eg

document.forms['form']['phone'].value = document.kmvar.Phone;
1 Like

That’s true, but it’s significantly slower for a multi-part form.

1 Like

It should not be slower to use the Execute JavaScript action, thats basically doing exactly what you show, except with a single do script instead of a bunch of them.

Using the individual Set Field actions certainly would be slower since that would be compiling AppleScript for each field.

1 Like

Eh? Hmm…

Okay. I misread your last post.

One multi-step Execute JavaScript Action instead of numerous Set-Safari-Field Actions.

Got it.

1 Like

Sometimes when you are looking to see if something has happened, it is easier to look if a condition before no longer exists and a condition after has not happened yet. If the web page changes in between, look for the conditions on either side, which may remain static. It’s a Foramen Problem. You are looking for an empty space in between. So look for the solid parts around the empty space.

I know this doesn’t name a specific function to use, it’s more a way to think about the problem.

I solved a knotty problem like this myself recently. I had three screens. Screen 1 had static elements. Screen 3 had static elements. Screen 2 varied considerably and required action to dismiss it, but I had to detect its presence and it varied an the amount of time it took to appear. So I had to figure out how long I was waiting… What I did was look to see when screen 1 disappeared and when I wasn’t seeing screen 3, and then I knew when screen 2 was on the window (because I’m working in an emulator, this all has to be done with matching images).

Dejah

2 Likes

Great post. This is a really good diagnostic technique, which I’ve used before, but it’s also one I wish I thought of more often. Thanks for the reminder.

Never heard this term before. Do you have a link as to its meaning and origin? I did a Google search and was presented with lots of links related to “a hole in the heart” so I think I see the correlation, but would like more information if it’s available. You’ve piqued my curiosity.

1 Like

Dan Thomas, I don’t really have a link since I kind of coined it myself. It’s the functional opposite of a Fence Post Problem. Instead of looking for the solid delimiter, you are looking for the empty space in between the solid delimiters.

Foramen is a medical term for a hole anywhere (thanks to the TV show Bones for the word). There may be a Formal Programming Community term for these sorts of problems, but since I’m mostly self-taught with relatively little formal training and words are my medium (international high tech journalist, 15 yrs) as much as code, I tend to coin my own terms when I need them. Sorry if I caused any confusion. I should have provided definition as maybe “foramen” was an unfamiliar word to many.

1 Like

No, that’s totally cool. I coin my own terms all the time. I love it! Thanks for sharing it.

1 Like