Witty syntax

A Witty template consists of text (which can be regular text, HTML, XML, etc) and Witty instructions. Witty instructions can delineate a block (forevery, if, component) or be a standalone instruction. Comments can be inserted between '[!' and '!]' (exclamation points inside the square brackets). As comments can span multiple lines and do not stop at the first close bracket without an exclamation point, comments can also be used to disable sections of Witty code.

A simple example of Witty code follows:

<title>

  [title] [! standalone instruction, prints the document title !]

</title>

[if birthday] [! block instruction !]

  Happy Birthday

[/if] [! ends the above block instruction !]

As all Witty instructions start with a '[' (open bracket), you should use a double open bracket if you want a real open bracket in your code (eg '[['). This may lead to problems with for example embedded JavaScript - there might be a lot of brackets to escape. We recommend storing JavaScript into separate files wherever possible, but when this is impossible, you can use the open bracket.

For example, consider this code, which fills a JavaScript array using a HareScript record array:

<script type="text/javascript" language="javascript">
  var popup = new Array([num_items]);

  [forevery popup_item]
    popup [[ [seqnr] ] = '[text:java]';
  [/forevery]
</script>

The spaces between popup, the JavaScript brackets and the Witty code were added for clarity - they are not required. The ':java' specifies that the string should be encoded for use in Java(Script) - more on this later.

Cell name lookups

Witty instructions that expect a cell name follow specific lookup rules to find a record containing that cell. This is done using a system of scopes - every time your code 'opens' a record by using a forevery or if block, this record is added as a new level of scope during the execution of that block. Whenever you specify a cell name, the Witty engine first looks for that cell in the most recently opened record. If it cannot find the cell there, it will look in the parent block, until it reaches the record originally passed to the Witty execution command (RunWitty or RunWittyComponent)

You can specify cells from a record contained in a cell by using the dot syntax, as shown below:

[cellname.subcellname]     [! get a cell from the record 'cellname' !]

[cellname.subcellname.subsubcellname]  [! get a cell from a record in a record !]

This allows you to directly access data inside a record, without having to open the record using an if or forevery statement. The cell name lookup rules only apply to the first cellname specified - as soon as a cell with a matching name was found, the Witty engine will look for the sub cell names inside this record (just like normal HareScript would)

HareScript code invoked using a function pointer in a Witty variable can use the GetWittyVariable function to lookup a cell in the Witty data, using the above lookup rules. Additionally, any record passed to CallWittyComponent is also added as a new scope level.

Embedding external data

Most Witty instructions will only embed external data. There are two syntaxes to embed external data: one that automatically selects the proper encoding based on the Witty encoding (see Data encoding), or by explicitly specifying the encoding:

[cellname]            [! Print data with the default encoding !]

[cellname:encoding]   [! Print data with the specified encoding !]

What exactly is printed depends on the type of the cell to which cellname refers:

Cell Type

Effect

Integer

The value of the integer is printed

String

The string is printed, using the specified encoding

Function ptr

The function pointer is executed, and any Print statements done by the function are included in the Witty output. Output from a function pointer is never encoded!

The following encodings are supported for strings:

Encoding

Effect

none

No encoding (prints the string unmodified)

base16

Base-16 encoding (hexadecimal)

base64

Base-64 encoding (MIME)

value

Value encoding: escape all quotes, <, >, & and linefeeds as HTML entities (&#62;....)

xml

XML encoding: a synonym for value encoding

html

HTML encoding: like value encoding, but remove carriage returns and encode linefeeds as '<br />' tags

xhtml

A synonym for HTML encoding

url

URL parameter encoding: encode data for use as parameter in a URL

java

Java/JavaScript encoding: escape all quotes, backslashes and control characters with a backslash.

If-blocks

An if-block conditionally enters the following block, if the cell evaluates to true. An if-block can optionally contain an else-block, which is executed when the specified cell evaluates to false:

[if cellname] text if true [/if]

[if cellname] text if true [else] text if false [/if]

The text inside both the if-blocks and else-blocks may also contain other Witty instructions.

An if instruction supports cells of the following types - note that it supports more types directly than a plain if statement would in HareScript, but that you cannot build any expressions:

Cell name 

Effect

Boolean

Evaluates to true if the cell itself contains a true value

Integer

Evaluates to true if the cell contains a non-zero value

String

Evaluates to true if the cell contains a non-empty string

Record

Evaluates to true if the record is not a non-existing record (does not contain a default value). Additionally, the record is added as a new scope for cell name lookup

Function PTR

Evaluates to true if the cell contains a function or macro

Array

Evaluates to true if the array is not empty

Forevery-blocks

A forevery instruction repeats the containing block for every record contained in the record array that is specified as its argument. A forevery-block does not support any other type and does not allow an [else] block:

[forevery cellname]    [! cellname must reference a record array cell !]
  text to repeat
[/forevery]

A forevery instruction processes its array sequentially, and adds the current record as a new scope level for cell name lookups inside the forevery block. You can thus directly access the cells inside the current record, and should not access those cells using the original cellname passed to the forevery instruction.

Inside a forevery-block, you can use a [seqnr] instruction to print the current (zero-based) element number. You can also use the following cell names to an if statement inside a forevery-block - they are automatically generated for every record array passed to a forevery instruction:


Cell name

Effect

First

Evaluates to true if this is the first element in the record array

Last

Evaluates to true if this is the last element in the record array

Odd

Evaluates to true if seqnr would give an odd value (ie: it iterates false, true, false, true,... in a forevery-block)

As an example, the following code formats a record array of hyperlinks with alternating background colors:

[! links is our record array of links, containing a 'link' and 'title' cell !]

 [! links is our record array of links, containing a 'link' and 'title' cell !]

[forevery links]
  [if first]
    <table border="1">
  [/if]

      <tr bgcolor="[if odd]#EEEEEE[else]#FFFFFF[/if]">
        <th>
          Link [seqnr]
        </th>
        <td>
          <a href="[link]">[title]</a>
        </td>
      </tr>

  [if last]
    </table>
  [/if]
[/forevery]

Components and embed

Components allow you to mark blocks as reusable, similar to HareScript macros. Code inside a component is never directly executed when it is encountered by a Witty engine, and can be placed anywhere you want in the Witty template. This allows you to put all components together at the beginning or end, or to put them at the position they would normally be embedded so you can see the context of the component.

Components are executed using the Witty embed instruction.The syntax of a component is just like that of any other block statement, except that the parameter passed to the component instruction is not that of a cell, but a name which can be used later to refer to the component:

[component componentname]
  component text
[/component]

...
[! to later embed this component: !]
[embed componentname]

Components have access to the record scopes as they were at the location where the embed or HareScript component commands were invoked. Placing a component block inside an if or forevery block does not give a component access to the records opened by these statements, nor will they be repeated or influenced by the evaluation result of the if statement.

Comments