GrazrScript Tutorial

Procedural programming

All of the programming we have been doing with GrazrScript so far has been declarative. We can simply add a tag for a validation rule or a feed URL, and the language will take care of all the details. Sometimes, however, it is useful to be able to write instructions that have more control over what is done. This is accomplished in most programming languages with commands like IF and LOOP. GrazrScript offers this type of procedural programming through the <grazr:script> tag, which lets you embed JavaScript commands directly into an OPML file. This is analogous to the way JavaScript can be embedded into HTML Web pages.

Hello World

In keeping with programming tradition, let's examine a trivial program that says hello as our first example of the script tag. The format of the script tag is to use <grazr:script> to open a script block and </grazr:script> to close it. In between these two tags you are able to write normal JavaScript, with some limitations that will be listed shortly. The only other housekeeping issue is that all text within a script block must be placed between HTML comments. This is to guarantee XML compatibility when other programs try to read these files. The combination of the script tags and the comments make what goes on within a script block invisible to any program that doesn't support GrazrScript.

<grazr:script>
<!--
outline.writeText( "Hello, " + first_name );
-->
</grazr:script>

The script block is placed within a form template, and is run when the matching form is completed. In this simple example, the user can enter their name and submit the form. The template for this form then runs, which causes the code within the script block to run. The only command within this script block is outline.writeText( "Hello, " + first_name );, which adds a text node at this point of the result.

http://docs.grazr.com/script/tutorial/hello.xml
<?xml version="1.0" encoding="UTF-8"?>
<opml version="2.0" xmlns:grazr="http://docs.grazr.com/script/spec/1.0">
<head>
<title>Hello World</title>
</head>
<body>
<grazr:form name="helloform">
Enter your first name: <input type="text" name="first_name" />
<input type="submit" value="Ok" />
<grazr:rule field="first_name" required="true" />
</grazr:form>

<grazr:formtemplate name="helloform">
<grazr:script><!--
outline.writeText( "Hello, " + first_name );
--></grazr:script>
</grazr:formtemplate>

<grazr:formresult text="Response" name="helloform" />

</body>
</opml>

Try running this program before we look more closely at the details of the outline.writeText() instruction. Enter your first name and click Ok.


Notice that when you click Ok the progress indicator appears in the widget. Script blocks are actually run on the server, as is the rest of GrazrScript. This is an important security feature, because it prevents any JavaScript within the application from ever running within your browser. After a script block is done, only the resulting OPML makes it to your browser. In this program outline.writeText() adds a new OPML node into the result portion of the file, and this is displayed by the widget. In effect, a script block "prints" data for the user by outputting OPML. If you had entered Sally as your name, the result that was added by the script block would have been <outline text="Hello, Sally" />. We'll see several variations of outline.writeText() later for the other types of OPML you need to create.

Experienced JavaScript programmers will recognize the analogy between outline.writeText() and JavaScript's traditional document.write(). This similarity is deliberate. The script block doesn't run within your browser, so it has no access to the normal document object. Instead GrazrScript coders build up an OPML document represented by the outline object. But we are getting ahead of ourselves with all this object stuff. You'll find script blocks easy to use, even if you have never programmed in JavaScript.

Why Use Scripting?

So why do you need to be able to do procedural programming within an OPML file? One reason is to modify the URL of a feed based on user input. We have already seen how this can be done without programming using a substitution variable. But how do we handle user input when there is more than a simple substitution? One classic programming problem is the conditional use of an extra value. For example, let's create a GrazrScript app that displays news about different baseball teams. We can have a listbox that displays the major teams, and also allow entry of the lesser teams with a separate input field. The tricky part is knowing when to use the value of the "other" input field. Here is one solution using the if construct.

http://docs.grazr.com/script/tutorial/baseball.xml
<?xml version="1.0" encoding="UTF-8"?>
<opml version="2.0" xmlns:grazr="http://docs.grazr.com/script/spec/1.0">
<head>
<title>Baseball Search #1</title>
</head>
<body>
<grazr:form name="teamform">
<strong>Baseball Search</strong>
<input type="submit" value="Ok" /><br />
Favorite team: <select name="team">
<option value="Red Sox">Red Sox</option>
<option value="Yankees">Yankees</option>
</select>
Choose another team: <input type="text" name="other" />
</grazr:form>

<grazr:formtemplate name="teamform">
<grazr:script><!--
if (other != "") {
team=other;
}
--></grazr:script>
<outline type="rss" text="%team% News"
xmlUrl="http://news.google.com?q=baseball+%team%&amp;output=rss" />
</grazr:formtemplate>
<grazr:formresult text="Latest stories:" name="teamform" />

</body>
</opml>

The listbox entry is placed in a variable called team, and the optional entry is placed in a variable called other. The script block in this program says that if the value of the other variable is not blank, then its value shoud be assigned to the team variable. This means that when the other variable is filled in, its value will be used in the substitution instead of the value of the listbox.

<grazr:script>
<!--
if (other != "") {
team=other;
}
-->
</grazr:script>

The syntax rules for the procedural code within the script block are easy to learn, because they are standard JavaScript. You can use any JavaScript book to discover that != means not equal, and that conditions in the if are entered within a pair of parentheses. When in doubt, just assume that standard JavaScript rules apply. We'll examine the parts of JavaScript that are not available in a script block in a later part of this tutorial.

Time to test this form's logic. Try selecting a team from the listbox and then clicking Ok. You will see that this team is used in the Google News query. Try the form again with a different baseball team entered in the other field. As desired, the value of the other team input is used in the query.


Multiple Script Blocks

You can embed more than one script block within a template. The result of each script block will appear at that point in the results. Here is an enhanced version of the baseball search that adds an extra feed to the result for Red Sox fans. The new script block tests to see if the current team is equal to "Red Sox" (in JavaScript equality is tested with ==), and then uses outline.writeFeed() to add a new feed node.

<grazr:script>
<!--
if (team=="Red Sox") {
outline.writeFeed("Psst, buddy. Here's some extra Red Sox news from the Boston Globe:",
"http://www.boston.com/sports/baseball/redsox?mode=rss_10");
}
-->
</grazr:script>

Outline.writeFeed() will create a feed node with the matching text and URL:

<outline type="rss" 
text="Psst, buddy. Here's some extra Red Sox news from the Boston Globe:",
xmlUrl="http://www.boston.com/sports/baseball/redsox?mode=rss_10" />

You can see how outline.writeText() and outline.writeFeed() simplify the work of creating OPML.

http://docs.grazr.com/script/tutorial/baseball2.xml
<?xml version="1.0" encoding="UTF-8"?>
<opml version="2.0" xmlns:grazr="http://docs.grazr.com/script/spec/1.0">
<head>
<title>Baseball Search #2</title>
</head>
<body>
<grazr:form name="teamform">
<strong>Baseball Search</strong>
<input type="submit" value="Ok" /><br />
Favorite team: <select name="team">
<option value="Red Sox">Red Sox</option>
<option value="Yankees">Yankees</option>
</select>
Choose another team: <input type="text" name="other" />
</grazr:form>

<grazr:formtemplate name="teamform">
<grazr:script><!--
if (other != "") {
var team=other;
}
--></grazr:script>
<outline type="rss" text="%team% News"
xmlUrl="http://news.google.com?q=baseball+%team%&amp;output=rss" />
<grazr:script><!--
if (team=="Red Sox") {
outline.writeFeed("Psst, buddy. Here's some extra Red Sox news from the Boston Globe:",
"http://www.boston.com/sports/baseball/redsox?mode=rss_10");
}
--></grazr:script>
</grazr:formtemplate>

<grazr:formresult text="Latest stories:" name="teamform" />
</body>
</opml>

Now you can select the Red Sox and click Ok to see the extra feed. If you select another team, this extra feed is not displayed. This is the start of true feed application development, where you can use a GrazrScript application to construct OPML outlines specific to a user's needs.


Core JavaScript is Available

If you are an experienced programmer, you may have noticed a flaw in the logic of baseball2.xml. The second if condition tests for the team being equal to "Red Sox". That will work if the user selects this team from the listbox. But what if they type "red sox" into the other field, or even "ReD SoX"? The equality operator (==) is case sensitive, so these other variations on Red Sox won't be recognized. Programmers will realize that one common solution is to make the comparison using all lower case letters. Since script blocks have access to the full core JavaScript language, we can easily use the lower case version of the team variable with team.toLowerCase(). This is a simple example of what is available if you try exploring JavaScript commands embedded in GrazrScript.

<grazr:script>
<!--
if (team.toLowerCase()== "red sox") {
outline.writeFeed("Psst, buddy. Here's some extra Red Sox news from the Boston Globe:",
"http://www.boston.com/sports/baseball/redsox?mode=rss_10");
}
-->
</grazr:script>

http://docs.grazr.com/script/tutorial/baseball3.xml
<?xml version="1.0" encoding="UTF-8"?>
<opml version="2.0" xmlns:grazr="http://docs.grazr.com/script/spec/1.0">
<head>
<title>Baseball Search #3</title>
</head>
<body>
<grazr:form name="teamform">
<strong>Baseball Search</strong>
<input type="submit" value="Ok" /><br />
Favorite team: <select name="team">
<option value="Red Sox">Red Sox</option>
<option value="Yankees">Yankees</option>
</select>
Choose another team: <input type="text" name="other" />
</grazr:form>

<grazr:formtemplate name="teamform">
<grazr:script><!--
if (other != "") {
var team=other;
}
--></grazr:script>
<outline type="rss" text="%team% News"
xmlUrl="http://news.google.com?q=baseball+%team%&amp;output=rss" />
<grazr:script><!--
if (team.toLowerCase()=="red sox") {
outline.writeFeed("Psst, buddy. Here's some extra Red Sox news from the Boston Globe:",
"http://www.boston.com/sports/baseball/redsox?mode=rss_10");
}
--></grazr:script>
</grazr:formtemplate>

<grazr:formresult text="Latest stories:" name="teamform" />
</body>
</opml>

You can test this version by typing "red sox" with any capitalization into the other field.


Field typesCatching script errors