|
Creating Sherlock Channels, Part 2
Pages: 1, 2
Back to the parsing now. Open up the XPath Finder and use the URL I
gave earlier
http://www.macosxhints.com/search.php?query=sherlock&type=stories&mode=search.
You would look through it trying to find a path like earlier and
eventually get to the path
/img/table/tr/td/table/tr/td/table/tr/td/table/tr[2]/td/br/table
only to find that there are lots of trs, some of which
contain the information we're looking for (links to the hints and the
titles of the hints) and some of which don't. The solution to this is to
use the XPath
/img/table/tr/td/table/tr/td/table/tr/td/table/tr[2]/td/br/table//a
which selects all of the links within the path. So putting this together
we get:
let $searchGoods := for $searchItem in $htmlSearch/img/table/tr/td/table/tr/td/table/tr/td/table/tr[2]/td/br/table//a
return dictionary(
("description", $searchItem/b/text()/convert-html(.)),
("doubleClickURL", url-with-base($searchItem/@href, http-request-value($base, "ACTUAL_URL")))
)
You should be able to understand these. The only new thing here, other
than the // in the XPath, is the url-with-base
function, which takes a URL and a base URL and ensures that you get an
absolute URL. If the first URL is already absolute, it throws out the
second URL. If not, it gets the URL from $base and puts them
together. The only thing left to do here is to return the values:
return dictionary(
("Internet.SearchResultsTable.selectedRows", null()),
("Internet.SearchResultsTable.dataValue", $searchGoods),
("Internet.DetailHTMLView.HTMLData", ""),
("Internet.NetworkArrows.animating", false())
)
By now you should be able to understand all of that. The only other
thing to remember is that we have to stop the network busy spinner that
was started earlier. Open up your channel in Sherlock and try searching
for different keywords. Happily, we don't need to set up another parser to
extract the hints from the pages, Sherlock will use the one we've already
written.
Getting the Newest Hints button Working
One problem our users will have now is that they can't get to the
newest hints after they've done a search. This is where the Newest Hints
button comes into play, and I hope you now see the value of thinking
through the interface. We need to put some code at the
Internet.NewestHints.action path.
We could create the trigger and then paste the code from the
Internet.didInstall trigger, but that would break a
fundamental programming rule: "Never repeat two significant sized pieces
of code". So what we'll do is change the retrieving of the newest hints to
work nearly much the same way as searching does.
JavaScript code will handle the button action and
Internet.didInstall and then call some XQuery code to do the
parsing and displaying of the hints. Remember that any trigger that wants
to be called by other triggers instead of the UI must start with
DATA.
Change the path of the Internet.didInstall trigger to
DATA.action.newestHints . Then copy and paste the
Internet.SearchButton.action trigger and set the copy's path
to Internet.didInstall . On the last line of the new
Internet.didInstall trigger change DATA.action.performSearch
to DATA.action.newestHints. Copy and paste our new
Internet.didInstall trigger and change its path to
Internet.NewestHints.action. The last change we need to make
is to add these lines:
,
("Internet.NetworkArrows.animating", false())
to the DATA.action.newestHints trigger's
return statement. The reason we add the comma is because the
line that used to be the last one in the return statement is
now the second to last. The whole DATA.action.newestHints
trigger should now look like:
let $httpRequest := http-request("http://www.macosxhints.com/backend/geeklog.rdf")
let $rss := http-request-value($httpRequest, "DATA")
let $goods := for $item in $rss/rss/channel/item
return dictionary(
("description", $item/title/text()/convert-html(.)),
("doubleClickURL", $item/link/text()/convert-html(.))
)
return dictionary(
("Internet.SearchResultsTable.selectedRows", null()),
("Internet.DetailHTMLView.HTMLData", ""),
("Internet.SearchResultsTable.dataValue", $goods),
("Internet.NetworkArrows.animating", false())
)
Go ahead now and test our changes in Sherlock.
Get more Sherlock Channels
We want the user to be able to go to a site to find more Sherlock
Channels instantly, to do that, we'll add this trigger and code:
<trigger path="Internet.GetMore.action" language="JavaScript">
System.OpenURL("http://sherlock3.homeunix.com");
</trigger>
This simply opens the URL in whatever application or browser the user
has chosen.
The Enlarge and Shrink Font buttons
One problem users with limited eye site or high resolution monitors
might have is the small size of the font in the HTMLView. To help them, we
want to allow the font size to changed with either of our two
buttons. Amazingly, we don't have to write a single line of code to do
this. Open up IB, hold down the control key, and drag the Enlarge Font
button towards the HTMLView. The button won't actually move, but you'll
see a line being drawn between the two. After you release the mouse
button, the Connections pane for the button will open. In the column on
the right side, choose "makeFontBigger:" and click the Connect button in
the lower right corner. Repeat these steps for the Shrink Font button,
except choose "makeFontSmaller:" in the Connections pane.
The About Button
We want users to find out more about the channel and its developer, so
we put a button where they can click and have a sheet/panel come down and
tell give them that information. But first we need to create the sheet in
IB.
In the Windows pane of the widgets palette drag the Panel into the
Channel.nib window. You need to set a path for the panel, so open it up
(by double clicking on it) and open up it's Sherlock pane. For the name
enter "about", you'll notice that in the Data store path it only shows
about and not Internet. This is because you have a entirely separate
window. Drag a button into the lower right corner, name it "OK" and give a
path of ok. I would also suggest making it equivalent to
return. You can now add whatever else you want, but be sure to add some
text for the user to read to find out how to reach you. Add these triggers
to your code:
<trigger path="Internet.about.action" language="JavaScript">
DataStore.Notify("about.beginSheet");
</trigger>
<trigger path="about.ok.action" language="JavaScript">
DataStore.Notify("about.endSheet");
</trigger>
These triggers open up the sheet when the About button and close it when the sheet's OK button is clicked.
Calling the search from a URL
One neat feature of a Sherlock is that you can have an installed
channel perform a specific action from a URL. Try sherlock://com.apple.yellowPages?query=sushi. This
URL calls the Yellow Pages channel with a search for sushi. We want users
and Web developers to be able to offer a similar URL to search with our
channel.
To do that, we need to put some code in a trigger at the
URL.complete path. Remember that we should never repeat two
significant pieces of code. But, thankfully, the main search code is
already in a trigger prefixed with DATA, so all we need to do
is write code to grab the query from the URL and call the search. Here's
how:
<trigger language="JavaScript" path="URL.complete">
query = DataStore.Get("URL.query");
DataStore.Set("Internet.MainQueryField.objectValue", query);
DataStore.Set("Internet.NetworkArrows.animating", true);
DataStore.Notify("DATA.action.performSearch");
</trigger>
This code is called when someone uses a URL like sherlock://com.mac.stevej.macosxhints?query=sherlock
(be sure to use your identifier). The first line in the trigger grabs the
query from the URL, the second sets the search text field to
it, the third starts the network arrows spinning, the fourth calls the
search. Sherlock automatically un-escapes any escaped characters for
you.
Getting Help
Though creating the above channel step-by-step will work fine, you'll
undoubtedly want to create your own, unique channel. You'll also (most
likely) run into a few problems along the way. First read through the
official documentation. Though a large part of it will be review of this
article, you'll probably learn some things that will be useful to
you. Remember that the reference part of the documentation will be your
best friend when you're trying to figure things out. In case the
documentation doesn't help you, there's plenty of places to ask other
developers for help:
- Apple's sherlock-channel-development mailing list has many excellent developers on it who would, most likely, be able to answer any questions you might have about developing channels.
- I'll be personally answering questions on the forums below, so you can
certainly ask questions there
Getting your Channel to the World
After you've created your channel you need to share it with the
world. The first step is to post it online. You ISP usually includes web
space with your account, you can use your iDisk/.mac to host it, or you
can use the Sherlock Channels
free channel hosting. One thing to consider is that you want to your
channel to be on a site you will own for the foreseeable future. Your
don't want your users to have to deal with a channel that doesn't work,
resubscribing, etc., just because you moved to a different ISP or web
host.
After you post it, you'll also want to have it listed in a channel
directory. I would recommend Sherlock Channels, a site
devoted to listing Sherlock 3 channels.
Happy hacking!
Harold Martin
is a freelance software developer and author. Visit him at
his blog.
Return to the Mac Innovators Contest.
Return to the Mac DevCenter.

Comment on this article
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 10 of 10.
-
Sherlock 3 not compatible with 10.2+ .nib files
2004-04-12 23:27:31
spullara
[Reply | View]
-
The HTMLVeiw?
2003-06-27 10:01:53
anonymous2
[Reply | View]
-
Using Channel Outside Project Builder
2003-04-10 16:55:41
anonymous2
[Reply | View]
-
Using Channel Outside Project Builder
2003-06-13 14:49:22
anonymous2
[Reply | View]
-
lost...
2003-04-04 05:19:26
anonymous2
[Reply | View]
-
lost...
2003-06-13 14:52:45
anonymous2
[Reply | View]
-
url encoding the query for searching
2003-04-01 06:04:37
cymantic
[Reply | View]
|