IRScript: The Image Rodeo Scripting Language

Note: this section is intended for folks comfortable editing HTML files.

Last updated 4/19/04.

Overview

IRML is HTML with IRScript statements contained within SGML processing tags (<? ?>). Anything outside the tags is considered HTML and passed along to the resulting HTML file. Anything inside the SGML tags is parsed and interpreted as IRScript statements. The echo statement causes a string expression to be evaluated inserted into the HTML at the point where the echo statement appeared. This is more or less the same as server-side scripting systems such as PHP, JSP and ASP. Note, however, that IRScript doesn’t run on the web server - it runs when you build the web site in Image Rodeo.

You can include anything that’s legal in a HTML file, and even stuff that’s not legal, with the exception of anything that also uses the <? ?> tags (such as PHP). Image Rodeo doesn’t interpret the HTML at all - except to look for the SGML processing tags.

Unlike JavaScript, IRScript statements consist of all the text inside the SGML processing tags, no matter where they appear in the HTML. This means that IRScript can appear (and often does appear) inside quoted HTML strings. Consider this IRML fragment:

    <img src="thumb/<?echo uid;?>.jpg" width="<?echo tw;?>" height="<?echo th;?>">

The echo statement is used to write a string into the HTML stream at the point where it was found. When the web site is built, and the IRScript statements are executed, the fragment above could generate the following HTML:

    <img src="thumb/1.jpg" width="100" height="75">

Of course, the exact HTML generated depends on the values of the uid, tw and th variables.

Syntax

IRScript syntax is loosely based on JavaScript syntax, which itself is loosely based on "C" syntax. This is strictly to avoid defining a new syntax - you’ll find that many key features of JavaScript (such as functions) are missing. Here’s a quick sample:

    <? /* This is a comment. Note that // is not supported! * / ?>
    <? echo "hello, world"; ?>

Normally it makes sense to look at an IRML file as HTML with embedded IRScript tags, but it’s possible to turn that inside-out: you can consider an IRML file as IRScript with embedded HTML. In other words, instead of <?IRScript?> you have ?>HTML<?. (Of course, you’d have to start the ball rolling with a <? tag at the beginning of the file.)

Consider this fragment:

    <?for (i = 0; i < pages; i = i + 1) {?><a href="<?echo pages[i].uid;?>.html"><?echo pages[i].title;?></a> <?}?>

This rather complicated fragment can be indented to highlight the nested characteristics of both IRScript and HTML:

    <?for (i = 0; i < pages; i = i + 1) {?>
        <a href="<?echo pages[i].uid;?>.html">
            <?echo pages[i].title;?>
        </a>
    <?}?>

This is fairly readable, but it has the side effect of generating more white space - which the browser will remove, but which wastes a little space and bandwidth. If we wanted to avoid the white space, but maintain the indentation, we could turn the fragment inside-out. We also add comments:

    <?  for (i = 0; i < pages; i = i + 1) {
            ?><a href="<?             /* Start the <a> tag. */
            echo pages[i].uid;        /* Write the unique id. */
            ?>.html"><?               /* End the <a> tag. */
            echo pages[i].title;      /* Write the title. */
            ?></a> <?                 /* Now the </a> tag. */
        }
    ?>

Believe it or not, this version generates HTML identical to the first version.

As a further refinement we could substitute echo statements for the inside out >?HTML<? tags. Here’s the same fragment one more time:

    <?  for (i = 0; i < pages; i = i + 1) {
            echo '<a href="';         /* Start the <a> tag. */
            echo pages[i].uid;        /* Write the unique id. */
            echo '.html">';           /* End the <a> tag. */
            echo pages[i].title;      /* Write the title. */
            echo '</a> ';             /* Now the </a> tag. */
        }
     ?>

Which style you use depends on your personal preferences.

Statements

IRScript consists of a sequence of statements separated by semi-colors. You can combine statements in blocks using braces ({}). The braces don’t affect variable scoping - it’s always global.

In this and subsequent sections the <? ?> tags are omitted for clarity.

Assignment

Like many languages, IRScript assignment is a hybrid between a statement and an expression. In the simple form it looks like this:

    foo = 1;
    bar = foo + 1;

However, it also evaluates to an expression, so you can use forms such as:

    foo = bar = 2;

Echo

The echo statement is the workhorse of IRScript. It writes strings into the HTML stream being generated. Two examples:

    echo "Hello, world.";       /* Writes a string into the HTML stream. */
    echo foo;                   /* Writes the contents of a variable into the HTML stream. */

If-Else

If-else statements work just like "C" and JavaScript. The expression inside the ( ) is coerced to an integer if necessary. 0 means false; anything other integer means true. See type conversion.

    if (foo == 1)
        echo "Foo is 1";        /* Note that whitespace is not signficant. */

    if (foo == 1) {
        echo "Foo is 1";
    } else if (foo == 2) {
        echo "Foo is 2";
    } else {
        echo "I'm not sure what foo is.";
    }

For-Loop

For loops are similar to "C" or JavaScript. There’s an initializer expression, a test expression, and an increment expression. The test expression is coerced to an integer if necessary. 0 means false (stop looping), any other integer value means true (keep looping). Note that an empty expression evaluates to 0 (false) - unlike "C". Also note that there’s no break statement.

    for (i = 0; i < count; i = i + 1) {
        echo "Iteration #";
        echo i;
        echo "<p>";
    }   

Types

IRScript supports two simple types with fairly typical semantics: integer and string. The real power, though, lies in the access to Image Rodeo’s internal objects, which can be considered types. These object types are restricted, in the sense that you can’t create or modify objects from within an IRML file. We consider each type in turn.

Integers

There’s no boolean type, but IRScript applies boolean semantics to integers. Boolean expressions (if statements, etc.) consider 0 to be false, and any other integer to be true. Boolean expressions always return integers: 0 for false and 1 for true.

Integer literals have the usual forms: 0, -1, 122334, etc.

You can use the standard math operators on integers. See Operators.

Strings

Strings consist of sequences of Latin-1 characters (ISO 8859-1).

String literals may be surrounded by foot (') or inch (") marks, depending on the situation. (OK, I realize that everybody else calls them single and double quotes, but years of work with typographers have addled my brain.) Note that string literals can contain anything - including SGML processing tags (<? ?>)!

There are only two operators that work with strings: == (equality) and != (inequality). See Operators.

Arrays

Arrays are ordered collections of objects. IRScript does not allow you to create new arrays, so the only way to get an array is to reference an object property that returns an array. Also, you can’t modify an array, so you’ll never see the subscript operator used on the left-hand-side of the assignment (=) operator.

Once you have an array you can get at any element of the array using the subscript ([]) operator. The expression inside the brackets is converted to an integer if required. Arrays start at 0, not 1.

    echo myArray[0];    /* Note that arrays start with 0 - like "C" and JavaScript. */

There are no array literals.

Objects

An object is a bundle of properties. IRScript does not allow you to define new object types, but you do have access to four built-in Image Rodeo internal object types. Each object type has various properties that you can access using the dot (.) operator. Note that you can combine dot operators to “drill down” an object hierarchy, or even combine dot and subscript operators to create complex expressions. A few examples:

    firstPage = pages[0];
    titleOfFirstPage = firstPage.title;
    titleOfSecondPage = pages[1].title;
    titleOfTheTopLevelPage = document.indexPage.title;

The four built-in object types are as follows:

  1. Index page objects. There’s always at least one index page: the top-level index page that’s created when you create an Image Rodeo document.
  2. Photo page objects. Every photo imported into an Image Rodeo document causes a photo page object to be created.
  3. Document objects. From the perspective of IRScript there’s only one document: the document that we’re working with right now. You’ll find properties that apply to the entire document here.
  4. The application object refers to Image Rodeo itself. There’s only one of these objects. You’ll find a few global properties here.

In addition, there’s one abstract object type: the page. A page may be either an index page or a photo page. (O-O Programmers will note that photo page and index page subclass from page.)

There are no object literals.

A property that returns an object may return nothing at all - it’s as if you referenced an unassigned variable. (See Variables.) You can test for this by taking advantage of a quirk in type conversion: when converting to an integer, an object that exists will convert to 1 (true), while an object that doesn’t exist will convert to 0 (false). Consider the following fragment:

    if (previous) { echo '<a href="'; echo previous.uid; echo '.html">Previous</a>'; }

In this case the variable previous may or may not contain an object. If it does, it converts to 1, and the if expression evaluates to true, and the block is executed. If it doesn’t, it converts to 0, and the if expression evaluates to false.

AppleScript tip: the IRScript object types are identical to the AppleScript object types.

Type Conversion

All of the types may be converted to an integer or a string, although the conversion isn’t always meaningful. Conversion is based on context; there’s no explicit conversion operator. String conversions are sometimes used for debugging scripts: try <?echo pages;?>.

The following table summarzes the possible conversions:

From: Integer String Array Other objects
To: Integer - Interpreted as a number, then converted to an integer if required. The length of the array. 1 if there’s an object, 0 if there isn’t.
To: String A string, perhaps with a leading minus sign. - A long-ish description of the array contents. A short description of the object, typically with the title.

Objects and Properties

Page Object

The page object an abstract type, so you’ll never actually see a page object. Instead, you’ll see a photo page object or index page object. In standard object-oriented fashion we use the term page object to mean a photo page object or index page object.

If you have a page object and you want to find out what sort of object it is (photo or index), test the isIndex property like so:

    if (page.isindex) {
        echo "This is an index page.";
    } else {
        echo "This is a photo page.";
    }

As a convenience, the current page object is referred to as this.

All page objects have the following properties:

Page Properties AppleScript IRML Type
Unique identifier - unique within a document. This is used to generate file names, e.g., 1.jpg or 2.html. The top-level index page always has a uid of 0. uid uid Integer
Is this an index page? 1 for yes, 0 for no. is index isIndex Integer
The page title. Always starts out as the file name. title title String
The page caption. May be nil. caption caption String
The parent page for this page, i.e., the index page that contains a reference to this page. Nil if this is the top-level index page. n/a parent Index Page
The array of acestor pages, starting with this page's parent, and ending with the top-level index page. Might be empty. Since 1.2, but undocumented until 11/14/02. ancestors ancestors Array of Pages
The document object that contains this page. n/a document Document
The application. n/a application Application

The most important property is the uid property - this provides the linkage between the various files. All JPEG files generated for an object are named x.jpg where x is the uid of that object. HTML file names are actually pulled from the plist file for that template, but all should include the uid embedded somewhere in the name. By referencing the current object’s uid you can write HTML that references the other files in the web site.

Note that the only way to get to the document and application object is by referencing the appropriate property of a page object.

Photo Page Object

All photo page objects have these properties in addition to the page properties outlined above:

Photo Page Properties AppleScript IRML Type
The Unix path to the source file. source source String
The rotation angle. Only 4 numbers are valid: 0, 90, 180 and 270. rotation rotation Integer
Did we find the source file when we opened the document? 1 for yes and 0 for no. found found Integer
The original width of source image. All widths and heights are in pixels. ow ow Integer
The original height. oh oh Integer
The width of scaled thumbnail. tw tw Integer
The height of scaled thumbnail. th th Integer
The width of scaled photo page image (aka display image). dw dw Integer
The height of scaled photo page image. dh dh Integer
The width of printable image. pw pw Integer
The height of printable image. ph ph Integer
The previous photo page. Nil if this is the first photo on the parent page. n/a previous Photo Page
The next photo page. Nil if this is the last photo on the parent page. n/a next Photo Page

Index Page Object

All photo page objects have these properties in addition to the page properties outlined above:

Index Page Properties AppleScript IRML Type
The array of pages that this page contains. The order of the array is set by the user; index and page children pages may be intermingled. pages pages Array of Pages
Just like the pages array, but missing all the photo pages. n/a indexPages Array of Index Pages
Just like the pages array, but missing all the index pages. n/a photoPages Array of Photo Pages
A count of all photo pages accessable from this page, including all the photos accessable by contained index pages, etc. n/a totalPhotos Integer
A count of all index pages accessable from this page, including all the index pages accessable by contained index pages, etc. Does not include this object - i.e., it's possible for this property to return 0. New for 1.2.9. n/a totalIndices Integer
The first photo accessable from this page. If this page doesn't have any contained photo pages, then the index pages are checked. Nil if there are no photos at all - in which case totalphotos will be 0. n/a firstPhoto Photo Page

Document Object

The document object has the following properties:

Document Properties AppleScript IRML Type
The top-level index page for this document. index page indexPage Index Page
The number of thumbnail columns. columns columns Integer
The global thumbnail size. Global sizes are maximums in both width and height. thumbnail size thumbnailSize Integer
The global photo size. photo size photoSize Integer
The global print size. print size printSize Integer
Should we generated printable images? 1 for yes, 0 for no. printable printable Integer
Desired dpi for printable images. Dropped in version 1.2.3. print dpi printDpi Integer
Should we show titles on index pages? 1 for yes, 0 for no. show thumbnail titles showThumbnailTitles Integer
Should we show titles on photo pages? 1 for yes, 0 for no. show photo titles showPhotoTitles Integer
Unix path for the destination folder. build path buildPath String
The name of the template being used. template name templateName String
Unix path for the file containing this document. n/a fileName String
Just the file name of the file containing this document. n/a justFileName String

Application Object

The application object has the following properties:

Application Properties AppleScript IRML Type
Image Rodeo version, in X.Y format. version version String
Unix path for the user-installed Templates.plist file. user template path userTemplatePath String

Operators

IRScript supports the usual collection of integer operators, and a few odd ones for good measure. The usual collection of boolean operators is also supported, but note that there is no boolean type, so they operate on integers. There are no bit-wise operators. There are two string operators: == (equal) and != (not equal). (These operators are new for 1.2.8.) These string form of the operator is used if both operands are strings, otherwise the operands are coerced to integers and the integer operator is used.

The following table summarizes the list of operators:

Math
Addition +
Subtraction -
Multiply *
Divide /
Remainder %
Maximum #+
Minimum #-
Ceiling division /^
Negative -
Logical
Greater than >
Less than <
Equal ==
> or == >=
< or == <=
Not equal !=
Or ||
And &&
Not !
Special
Assignment =
Subscript []
Property .

Maximum and minimum are equivalent to max() and min(), respectively. Ceiling division is a shortcut for this expression:

    (lhs / (rhs + lhs - 1)/

Variables

Variables in IRScript are typeless, and do not have to be declared before being used. To create a variable simply assign something to it. It will hold that value until all the IRScript statements in an IRML file have been executed, then it will cease to exist. There’s no way to save values across IRML files.

All variables have global scope.

Access to the built-in objects is provided by adding all the properties for the active page to the global scope. IRScript runs in the context of an IRML file, and each file corresponds to a page object (either index or photo - this is set up in the plist file), so there’s always one and only one active page. All other objects are accessable from that active page.

When searching for variables, any user-defined variables are searched first. Watch out! It’s possible to lose access to the built-in objects by assigning values to user-defined variables of the same name.

Here’s an example:

    if (isIndex) {
        /* Must be an index page; dump the pages array. */
        echo pages;
    } else {
        /* Must be a photo page; dump the source path. */
        echo source;
    }
    
    /* We always have access to the document object, so now dump the pages array of the top-level index page. */
    echo document.indexpage.pages;
    
    /* Now assign something to pages. */
    pages = 1;
    
    /* We just lost access to the pages array! The next statement will inject the number 1 into the HTML stream. */
    echo pages;

It’s OK to reference a variable before it’s been assigned anything - it will simply return nothing. This ‘nothing’ value will be converted to 0 (integer) or the empty string ("") if necessary. Note that an array subscript on nothing returns nothing, and a property reference on nothing also returns nothing.


Download Screen Shots Documentation