I'm running into errors with this simple script. It's a validator to validate RSS feed with multiple validation sites using Safari. Everything works fine as long as the feed does not contain special characters or anything after the = sign.
The script should validate the feed that was copied to the clipboard.
For example, this feed works fine: http://thefirst.libsyn.com/rss
This feed gets truncated after ?id: https://www.npr.org/rss/podcast.php?id=510298
This is only happening on the Podbase validator site.
If I could get the script to click the Validate and Go buttons, that would be amazing, but this is pretty basic…just stuck as to why the feed is getting truncated.
set feed_url to the clipboard as string
set the podbaseurl to "http://podba.se/validate/?url=" & feed_url
set the feedvalidatorurl to "http://feedvalidator.org/check.cgi?url=" & feed_url
set the castfeedurl to "http://castfeedvalidator.com/?url=" & feed_url
tell application "Safari"
make new document
open location podbaseurl
open location feedvalidatorurl
open location castfeedurl
end tell
The problem is that https://podba.se/validate only makes it look like a single GET request is enough, whereas clicking the Go button interactively performs many individual GET requests behind the scenes whose results are pieced together on the current page (and the URL is then modified to include the submitted feed URL).
In other words: even solving the (odd) truncation problem wouldn't be enough.
Therefore, your best bet is indeed to simulate an interactive submission of a feed URL, which requires filling an input box with the feed URL and pressing the submission button.
Interactive submission must be simulated for the http://castfeedvalidator.com site as well, where pressing the submission button is sufficient, however.
(As you report, even though inspecting the request sent by the submission button shows that a variation of your URL - which only prepares the feed URL for submission - can be used to instantly submit it, doing so doesn't render the results correctly (missing styles)).
The following code implements both suggestions (the simulated-interaction approach was adapted from this answer of mine):
# Sample value
set the clipboard to "https://www.npr.org/rss/podcast.php?id=510298"
set feed_url to the clipboard as string
# Base URL only; the feed URL will be submitted by simulated interaction below.
set the podbaseurl to "http://podba.se/validate/"
set the feedvalidatorurl to "http://feedvalidator.org/check.cgi?url=" & feed_url
set the castfeedurl to "http://castfeedvalidator.com/?url=" & feed_url
tell application "Safari"
activate
set newDoc to make new document
set newWin to front window
# Simulate interactive submission of feed_url at podbaseurl.
set URL of newDoc to podbaseurl
my submitUrl(newDoc, podbaseurl, feed_url)
# The feedvalidateorurl can be opened normally.
open location feedvalidatorurl
# Simulate interactive submission of feed_url at castfeedurl.
set newTab to make new tab in newWin
set URL of newTab to castfeedurl
my submitUrl(newTab, castfeedurl, feed_url)
end tell
on submitUrl(doc, target_url, feed_url)
# Synthesize the JavaScript command.
set jsCode to "
(function () { // Use a closure to avoid polluting the global namespace.
function doIt(t) { // Helper function
if (doIt.done) return; // Already successfully called? Ignore.
try {
// Perform the desired action - may fail if invoked too early.
if (/^http:\\/\\/podba.se/.test('" & target_url & "')) {
document.querySelector('#url-input').value = '" & feed_url & "';
document.querySelector('#url-input').dispatchEvent(new Event('input'));
setTimeout(function() { document.querySelector('#go-button').click(); }, 0);
} else { // http://feedvalidator.org
document.querySelector('.btn-subscribe').click()
}
} catch(e) {
return; // Return without setting the success 'flag'.
}
doIt.done=true; // Set success 'flag' as a property on this function object.
};
// Attach a listener to the window's load event for invoking the helper function then.
window.addEventListener('load', doIt);
// If the document signals readiness -- which may still be too early, we can't know --
// also try to invoke the helper function *directly*.
if (document.readyState === 'complete') { doIt(); }
})();
"
# Execute the JavaScript command in the target page.
tell application "Safari"
tell doc to do JavaScript jsCode
end tell
end submitUrl
Related
I have an application that opens up some new tabs. I'm trying to cycle through these tabs, look at them, and then close them.
Dim tab_children, oDesc
Set oDesc = Description.Create
oDesc("micclass").value = "Browser"
Set tab_children = Desktop.ChildObjects(oDesc)
Dim title, handle, cTime
For i = 0 To tab_children.Count-1 Step 1
title = tab_children(i).GetROProperty("title")
handle = tab_children(i).GetROProperty("hwnd")
Window("hwnd:=" & handle).Restore
msgbox title & ": " & handle
Next
When we try to execute the .Restore, I receive an "object not visible" error. The tab that we're trying to restore is not the one that has focus, could that be the issue and if so how can we resolve it? I was under the impression that .Restore would bring that tab into focus based off of this thread, http://www.advancedqtp.com/old_forums/viewtopic.php?t=1970
The IDE I'm using is QTP, the Browser is IE.
A potential work around that I've been thinking about:
After the application opens up the new tabs, the last opened tab has focus. If we close that one, the 2nd to last has focus, all the way down to the original application's tab. Perhaps there's a way to utilize this information.
Restore has worked for me in the past, try using Activate-
Window("hwnd:=" & handle).Activate
Edited: Just tested the following and its working on my machine-
'Create Browser Descriptor
Set oBrowser=Description.Create
oBrowser("micclass").Value="Browser"
'Get the child objects
Set oBrowser=Desktop.ChildObjects(oBrowser)
totalcount = oBrowser.Count-1
For i=0 to totalcount
If Browser("micclass:=Browser", "index:="&i).Exist(0) Then
'get the hwnd everytime there's an iteration
ohwnd= Browser("micclass:=Browser", "index:=" & i).GetROProperty("hwnd")
'For debugging purposes
name = Browser("hwnd:="&ohwnd).GetROProperty("title")
msgbox name
Set oBrowser=Browser("hwnd:="&ohwnd)
'Page descriptor
Set oPage=Description.Create
oPage("micclass").Value="Page"
Set oPage=Browser("hwnd:="&ohwnd).ChildObjects(oPage)
For n=0 to oPage.Count-1
If oPage(n).Exist(0) Then
oBrowser.Close
Exit For
End If
Next
End If
Next
If you want to close only a particular page you can use the GETROPREPERTY("Title") in the If loop - If oPage(n).Exist(0)
Please tell me how do I click in point coordinates in application window?
I trying to UI automate my application on OSX 10.10 using JXA technology.
In documentation I found that it's possible using click at event. By I'am beginner of JXA and cant find how make a call.
Code snippet which I tried in Script Editor:
var app = Application('my_application_path')
app.window.click.at('{100,100}')
Thank you for help
You can interact with an application's user interface using the System Events application. Here is a script that clicks at certain coordinates in Safari:
// Activate Safari, so you will be able to click like a user
Application("Safari").activate()
// Access the Safari process of System Events
var SystemEvents = Application("System Events")
var Safari = SystemEvents.processes["Safari"]
// Call the click command, sending an array of coordinates [x, y]
Safari.click({ at: [300, 100] })
If you want to click a specific button (or other element of the user interface), it is more appropriate to click that specific element. For example:
// Click the third button of Safari's first window to minimize it
Safari.windows[0].buttons[2].click()
To learn what user interface elements can be interacted with and how, check out the Processes Suite in System Events' scripting dictionary. To open the dictionary, in Script Editor's menu bar, choose Window > Library, then select System Events in the Library window.
See https://github.com/dtinth/JXA-Cookbook/wiki/System-Events#clicking-menu-items
For example:
var fileMenu = proc.menuBars[0].menuBarItems.byName('File');
Below is an example of a portion of a script I wrote that automates creating mailboxes (aka folders) in Mail. I ended up using the UI file menus and click because using make() in the Mail DOM had issues for me. Hope it helps someone.
(() => {}
//this is part of a script that automates creating mailboxes (ie folders) in Apple Mail
//I used the file menu UI because when I tried the Mail library and make() method
//there was strange behavior when trying to interact with the new mailbox.
//However, when creating the new mailboxes thru the file menu, all seems to work fine
const Mail = Application('Mail');
const strId = Mail.accounts.byName('Exchange').id();
const exchange = Mail.accounts.byId(strId);
const activeClientFolder = exchange.mailboxes.byName('ActiveClient');
const SysEvents = Application('System Events');
const mail = SysEvents.processes.byName('Mail');
//next two lines insure Mail will be open and in front
mail.windows[0].actions.byName('AXRaise').perform();
mail.frontmost = true;
const mailboxMenu = mail.menuBars[0].menus.byName('Mailbox');
//below shows EXAMPLES of using .click(), keystroke(), and keyCode()
let newFolder = function (parentFolder, newFolderName, addTrailingDelay = true) {
//next line will select the parent mailbox (aka folder) where the new mailbox will be inserted
Mail.messageViewers[0].selectedMailboxes = parentFolder;
mailboxMenu.click();
delay(.2);
mailboxMenu.menuItems.byName('New Mailbox…').click();
delay(.2);
SysEvents.keystroke(newFolderName);
SysEvents.keyCode(36);
//delay is needed when creating multiple mailboxes with a loop
if (addTrailingDelay == true){
delay(1);
}
}
//now the payoff
const count = newActiveClients.length;
for(let i=0;i<count;i++){
/* Client Root Mailbox */
newFolder(activeClientFolder, newActiveClients[i], true);
/* Client Email Folders */
newFolder(activeClientFolder.mailboxes.byName(newActiveClients[i]), 'Client', true);
newFolder(activeClientFolder.mailboxes.byName(newActiveClients[i]).mailboxes.byName('Client'), 'Client_FYI_Sent');
newFolder(activeClientFolder.mailboxes.byName(newActiveClients[i]).mailboxes.byName('Client'), 'Client_FYI_Inbox');
newFolder(activeClientFolder.mailboxes.byName(newActiveClients[i]).mailboxes.byName('Client'), 'Client_FYI_Client_To');
newFolder(activeClientFolder.mailboxes.byName(newActiveClients[i]).mailboxes.byName('Client'), 'Client_From', false);
}
})()
Is it possible to directly change the font size of the text in an editable text box?
I have a tutorial stage where a message is displayed at the end when the user completes the tutorial.
I call this message endingText which is defined in my TutorialDefinition script:
string endingText = "You have completed the tutorial stage!";
In another script called TutorialEditor, I initialize it with the text that the user can input through the Editor. So, the endingText variable is there in the Definition script in case the user did not input anyhing...
void initValues()
{
endingText = stage.EndingText;
}
And finally, when the tutorial is done through the TutorialExecutor, I show the message, using:
endingText = GUILayout.TextArea( endingText );
The font and font size are set somewhere else through the current skin I think, but I wonder if it is possible to directly manipulate the above code snippet and change the font size?
I mean this line of code: endingText = GUILayout.TextArea( endingText ); that will eventually show the user the final message through void OnGUI() method...
(I understand I could if, instead of endingText, there was actual text within "" but what about now?)
How would I use Applescript to click on a web link in a google search. Can I identify them by name or number or anything?
Safari:
tell application "Safari"
open location "http://google.com/search?q=example"
do JavaScript "window.addEventListener('load', function() {
document.querySelectorAll('.r a')[0].click()
})" in document 1
end tell
Chrome:
tell application "Google Chrome"
open location "http://google.com/search?q=example"
tell active tab of window 1
execute javascript "window.addEventListener('load', function() {
document.querySelectorAll('.r a')[0].click()
})"
end tell
end tell
Edit: try something like this to match a YouTube result by title:
document.querySelector('.yt-uix-tile-link').click()
Edit 2: changed window.onload=function(){} to window.addEventListener('load',function(){}).
Building on #Lauri Ranta's great answer, here is convenience function clickOn(), which:
accepts a target document, a CSS selector string plus a zero-based index to select among what the selector string matches, and simulates a click on the element thus identified.
works irrespective of whether the target document is still being loaded or has already fully loaded - this turned out to be non-trivial. (Lauri's code relies on the window object's load event not to have fired yet).
works with both Safari and Google Chrome (if you don't have Chrome installed, you'll have to comment out a few lines)
Examples that use a delay to demonstrate that the click works even after the document has fully loaded:
# SAFARI
# Click on the *2nd* result returned from googling 'example' with Safari:
tell application "Safari"
open location "http://google.com/search?q=example"
delay 5 # sample delay - NOT needed
my clickOn(document 1, ".r a", 1)
end tell
# CHROME
tell application "Google Chrome"
open location "http://google.com/search?q=example"
delay 5 # sample delay - NOT needed
my clickOn(active tab of window 1, ".r a", 1)
end tell
clickOn source code:
on clickOn(doc, cssSelector, ndx)
# If no explicit index (into the matches returned by the CSS selector)
# is specified, default to 0.
if ndx is missing value or ndx = "" then set ndx to 0
# Synthesize the JavaScript command.
set jsCode to "
(function () { // Use a closure to avoid polluting the global namespace.
function doIt(t) { // Helper function
if (doIt.done) return; // Already successfully called? Ignore.
try {
// Perform the desired action - may fail if invoked too early.
document.querySelectorAll('" & cssSelector & "')[" & ndx & "].click();
} catch(e) {
return; // Return without setting the success 'flag'.
}
doIt.done=true; // Set success 'flag' as a property on this function object.
};
// Attach a listener to the window's load event for invoking the helper function then.
window.addEventListener('load', doIt);
// If the document signals readiness -- which may still be too early, we can't know --
// also try to invoke the helper function *directly*.
if (document.readyState === 'complete') { doIt(); }
})();
"
# Execute the JavaScript command in the target page.
if class of doc as text is "document" then # Safari: a «document» instance was passed
using terms from application "Safari"
tell doc to do JavaScript jsCode
end using terms from
else # Google Chrome: a «tab» instance was passed
# !! IF CHROME IS NOT INSTALLED, SIMPLY DEACTIVATE THIS `using terms from` BLOCK.
using terms from application "Google Chrome"
tell doc to execute javascript jsCode
end using terms from
end if
end clickOn
Since extensions can not access unsafeWindow, like Firefox can, to hook into DOM scripts am I looking for other ideas so I come to SO for help!
How about using some code to inject into DOM and sending the intercepted response to a background page, which then does some initial processing before calling a content script for final processing. When done, it answers to the background with a modified response, or the original (it depends), and the background page sends the response back to DOM which handles it to the DOM script response function.
There is just one problem with this, a background page cant communicate with the DOM.
I did a small test with injecting some code, where I output something to the console and an alert. The result wasnt good, as the alert fired but the console was empty - not even an error, which makes me wonder - what console received the output ?
function injectCode(fn){ // Executing an anonymous script
var script = document.createElement('script');
script.type = 'application/javascript';
script.textContent = '(' + fn + ')();';
document.documentElement.appendChild(script); // run the script
document.documentElement.removeChild(script); // clean up
}
var code = function(){
console.log('dom',window);
alert('code injected');
}
injectCode(code);
I also tried addEventListener, with DOMAttrModified DOMSubtreeModified DOMNodeInserted, on DOM elements that change when the DOM ajax response is fully parsed but all failed to fire.
Am I trying to do the impossible, by any means ?
Before continuing, make sure that you know the differences between the script contexts in an extension.
To inject a script from the background page, you have to execute a Content script, which on his turn injects the script as mentioned in your question / here.
Examples (using chrome.tabs.executeScript):
// null = current active tab
// Simple code, background:
chrome.tabs.executeScript(null, {
code: [
'var s = document.createElement("script");',
's.textContent = "console.log(window);";',
'(document.head||document.documentElement).appendChild(s);',
's.parentNode.removeChild(s);'
].join('\n')
});
I can imagine that this method is not doable for a big chuck of code. For a set of pre-defined scripts, you can then use two scripts: the code itself, and a helper script:
// config.js
var fn_code = function() {
console.log(window); ....
};
// helper.js
var s = document.createElement('script');
s.textContent = '(' + fn_code + ')();';
(document.head||document.documentElement).appendChild(s);
s.parentNode.removeChild(s);
// Background:
chrome.tabs.executeScript(null, {file: 'config.js'}, function() {
chrome.tabs.executeScript(null, {file: 'helper.js'});
});
Note: I did not directly link to "config.js", because that complicates the use when using manifest version 2, see "web_accessible_resources".
The previous method only shows how to execute code in one direction (background -> page). If there's a need to activate a background's function from the injected script, you have to define and listen to a custom event handler. See this answer + demo.
Because the code is injected, thus runs in the scope of the page, you have to check the console at the page.
When chrome.tabs.executeScript fails to execute the Content script (eg. because the extension does not have the permission to access a certain page), an error is logged at the console in the background page. This console can be accessed by following these steps.