This article is part of a series of Beginner's Guides designed to help new DekiScript users understand key concepts and avoid obvious pitfalls. If this article is helpful (or not), please let us know!
XML literals, new to DekiScript as of the Lyons release, are particularly interesting because they're a rather unusual language feature. They're used heavily in templates because they allow us, finally, to stay out of the HTML source editor (most of the time). They can be a bit confusing when you're first starting out with them, though, so we're going to boil down their usage to a very simple set of rules that should enable you to really understand how they work, and have you mastering them in no time.
So what is an XML literal anyway? It is just like a string or number or boolean literal: a value that can be inserted into your code. Unlike those other literal types, though, an XML literal can be a big sprawling creation. Let's start with a simple example:
var n = 5; // a numeric literal var s = "hi there"; // a string literal var b = true; // a boolean literal var x = <br />; // an XML literal
In each case, the DekiScript interpreter will parse the literal value, assign it to the variable, and set the "type" of the variable. Here we've used a simple little XML literal; we'll get to bigger stuff down below.
Here's a usage example:
{{ "First line"; <br />; "Second line"; }}
Which gives you this:
First line
Second line
In this case, each statement is a literal, two are strings and one is XML. All are output to the wiki page appropriately. Easy!
Of course, ability to simply insert a line break is nice but we want to go much further. So here are the rules for XML literals that will cover every use case, first in a summary list, and then explained in detail below.
This is basic, but let's get it out of the way straight off. This is not an XML literal:
{{ "<b> hello there </b>" }}
It will produce this:
<b> hello there </b>
XML inside a string is just a string of text, unless converted to XML with a function such as web.xml() or web.html(). In XML literals, the XML elements themselves are not quoted, but the contents are:
{{ <b> "hello there" </b> }}
which of course produces:
hello there
An XML literal can be passed to a function, assigned to a variable, evaluated and output to the page, or whatever. Anything you can do with a literal, you can do with an XML literal.
| Code | Result | Reason | |
| Right | {{ <b> "output to the page" </b> }} | output to the page | The XML literal is evaluated and echoed to the page |
| Right | {{ xml.text(<p> "pass it to a "; <i> "function" </i> </p>, ".//i" ); }} | function | The XML literal (<p> element) is passed to the xml.text() function as the first argument. |
| Right | {{ | xml | The XML literal (<p> element) is assigned to the variable x. |
Much like a string literal is a single entity surrounded by double-quotes, an XML literal is a single entity surrounded by an opening and closing tag (never mind what's in between those tags, which can be arbitrarily complex, including more XML literals) A complete <table>, for example, with everything inside it, would be a single literal.
So, whenever you enter a closing tag, you are ending an XML literal. If you want to lump a few together, wrap them in a <span> or a <div> or something.
| Code | Result | Reason | |
| Wrong | {{ var x=<b>"bold"</b><i>"italic"</i>; x; }} | EOF expected: /content/body/div[2]/div[3]/table/tbody/tr[2]/td[3]/span, line 1, column 33 (click for details) | Attempting to assign two XML literals to a single variable. |
| Right | {{ var x = <span> <b>"bold"</b><i>"italic"</i> </span>; x; }} | bolditalic | Wrapping in a <span> turns it into a single literal, which now works fine. |
This is the #1 trip point for new users. If you look in between the start and end tag of your XML literal, you should see a valid script. That could include DekiScript statements, more literals of all kinds (including XML literals), or anything, but it must obey the rules of DekiScript:
| Code | Result | Reason | |
| Wrong | {{ <p> hello there! </p> }} | invalid XmlNode: /content/body/div[2]/div[4]/table[1]/tbody/tr[2]/td[3]/span, line 1, column 11 (click for details) | Contents of <p> must be a valid script. Without quotes, "Hello" looks like a variable name or something; the interpreter doesn't know what to do with it. |
| Right | {{ <p> "hello there!" </p> }} | hello there! |
Here's the case that really causes problems: putting complex content into a literal without semicolons:
| Code | Result | Reason | |
| Wrong | {{ | invalid XmlNode: /content/body/div[2]/div[4]/table[2]/tbody/tr[2]/td[3]/p/span, line 3, column 6 (click for details) | Without semicolons, contents of <p> element is a series of values separate by a space, which is not a valid script. |
| Right | {{ <p> "Add a line break"; <br />; "to the output"; </p> }} | Add a line break |
Finally, because the contents must be valid DekiScript, that means we can also stick code in there:
| Code | Result | Reason | |
| Right | {{ <ul> foreach (var i in num.series(1,5)) <li> i </li>; </ul>; }} |
| Contents of <ul> is a valid script containing a single foreach loop. The loop consists of a single statement, which is a <li> literal. |
Instead of using a proper specific closing tag (e.g., </p>), you can use a "generic" closing tag (</>) and DekiScript will automatically use it to close the innermost open element. Sort of like a right-curly brace ( "}" ), except for XML literals. I prefer to stick with specific closing tags except when evaluating element names (see Rule 7).
| Code | Result | Reason | |
| Right | {{ <p> "Two different kinds of "; <b> "closing" </>; " tags"; </p>; }} | Two different kinds of closing tags | Generic closing tag closes innermost element (<b>). |
Well-formed XML already requires this, so it shouldn't be too hard to remember once you're aware of it:
| Code | Result | Reason | |
| Wrong | {{ <table border=1> <tr><td>"a table!"</td></tr> </table> }} | invalid XmlAttribute: /content/body/div[2]/div[6]/table/tbody/tr[2]/td[3]/span, line 1, column 15 (click for details) | Constant attributes must be in quotes |
| Right | {{ <table border="1"> <tr><td>"a table!"</td></tr> </table> }} |
|
You will commonly want to use DekiScript to create your attribute values. Just swap the quotes for parentheses, and you're there:
| Code | Result | Reason | |
| Wrong | {{ var border_width=1; <table border=border_width> <tr><td> "a table!" </td></tr> </table>; }} | invalid XmlAttribute: /content/body/div[2]/div[7]/table[1]/tbody/tr[2]/td[3]/span, line 2, column 18 (click for details) | Attributes enclosed in parentheses are interpreted as DekiScript. |
| Right | {{ var border_width=1; <table border=(border_width)> <tr><td> "a table!" </td></tr> </table>; }} |
|
But hey, you can do more: you can even have the attribute or element name be evaluated in DekiScript, once again just enclose it in parentheses:
| Code | Result | Reason | |
| Right | {{ |
| Element name is evaluated. This is a good time to use generic closing tags. |
| Right | {{ | this is italic! | Same thing with attribute names! |
Once you're writing XML, you might naturally be inclined to throw in some entities (e.g.: , >, etc.) In this case, your inclination would be right only if you're using 9.08 or later; entities are not supported in 9.02. Each entity is an XML literal unto itself. In the case of standard characters, though, you generally don't need to use them, because you can just enter the characters as string literals:
| Code | Result | Reason | |
| Right (in 9.08 or later) | {{ <ul> <li> < </li> <li> > </li> <li> & </li> </ul> }} |
| Entities are supported in XML literals in 9.08 |
| Right | {{ <ul> <li> "<" </li> <li> ">" </li> <li> "&" </li> </ul> }} |
|
XML entities come built-in with their own trailing semicolon, but that is not the semicolon that DekiScript is looking for to terminate a statement. Instead, you'll need an additional semicolon after the entity. Observe:
| Code | Result | Reason | |
| Wrong | {{ <b> ¶ "paragraph" <b> }} | EOF expected: /content/body/div[2]/div[8]/table[2]/tbody/tr[2]/td[3]/span, line 1, column 8 (click for details) | Need an additional semicolon to terminate a DekiScript statement after an entity. |
| Right | {{ <b> ¶; "paragraph" </b> }} | ¶paragraph | |
| Wrong | {{ <b> ¶ & </b> }} | invalid XmlNode: /content/body/div[2]/div[8]/table[2]/tbody/tr[4]/td[3]/span, line 1, column 12 (click for details) | This worked prior to 10.0! |
| Right | {{ <b> ¶; &; </b> }} | ¶& | Always use that extra semicolon |
Here are a couple of additional tidbits that are good to know.
You can use XML literals to enter JavaScript or style sheets on a page (provide you have UNSAFECONTENT permission!). By following The Rules, you (hopefully) will quickly conclude that the contents of the script or style sheet must be entered as a big string literal, since DekiScript should not and cannot interpret it. Examples:
| Code | Result | Reason | |
| Wrong | {{ <style type="text/css"> span.jsEx1 { font-weight:bold } </style>; <input class="jsEx1"/>; <div> "You typed: "; <span class="jsEx1"/>; </div>; <script type="text/javascript"> $(document).ready(function(){ $("input.jsEx1").keyup(function(){ $("span.jsEx1").html($(this).val()); }); }); </script>; }} | ":" expected: /content/body/div[3]/div[1]/div[1]/table/tbody/tr[2]/td[3]/span, line 2, column 23 (click for details) | Enclose the contents of the <script> or <style> element in quotes. |
| Right | {{ <style type="text/css"> ' span.jsEx1 { font-weight:bold } ' </style>; <input class="jsEx1"/>; <div> "You typed: "; <span class="jsEx1"/>; </div>; <script type="text/javascript"> ' $(document).ready(function(){ $("input.jsEx1").keyup(function(){ $("span.jsEx1").html($(this).val()); }); }); ' </script>; }} | You typed: |
How do you pass data from your DekiScript program to the JavaScript? Use json.emit(), and just remember that the contents of your <script> or <style> element is a DekiScript expression:
| Code | Result | Reason | |
| Right | {{ <input type="submit" value="My user name is..." id="emitEx"/>; <script type="text/javascript"> ' $(document).ready(function(){ $("#emitEx").click(function(){ alert("My user name is "+' ..json.emit(user.name).. '); }); }); ' </script>; }} | Content of <script> element is now three strings concatenated together. The user name is now hard-coded into the JavaScript on the page; it is evaluated by the wiki before the page is displayed in your browser. View the source of this page to convince yourself of this. |
Rules for using semicolons inside XML literals are a bit strange. It seems the DekiScript parser has no problem if you string together multiple XML literals without semicolons:
| Code | Result | Reason | |
| Right | {{ <p> "first paragraph" </p> <p> "second paragraph" </p> }} | first paragraph second paragraph | Semicolons between XML literals are optional. |
| Right | {{ <p> "first paragraph" </p>; <p> "second paragraph" </p>; }} | first paragraph second paragraph |
However, once you insert some other DekiScript in there and you're not careful, you can get into trouble. Fortunately, you can apply a simple usage rule and guarantee (I think) you'll never have a problem: to be safe, always put a semicolon after every closing tag. In practice, you're likely to slack off and omit some semicolons because you're brain shifts from DekiScript thinking to XML thinking, but if you store the rule in your head you'll at least know how to get yourself out of trouble:
| Code | Result | Reason | |
| Wrong | {{ <p>"first paragraph"</p> if ("I'm happy") <p>"second paragraph"</p> }} | ";" expected: /content/body/div[3]/div[2]/table[2]/tbody/tr[2]/td[3]/span, line 3, column 32 (click for details) | Lack of semicolons can get you into trouble if you insert DekiScript between literals! |
| Right | {{ <p>"first paragraph"</p>; if ("I'm happy") <p>"second paragraph"</p>; }} | first paragraph second paragraph |
DekiScript allows you to use a generic closing tag </> to close the innermost open element. This is a nice shorthand, but I for one prefer to use the full closing tag to help keep my code straight.
| Code | Result | Reason | |
| Right | {{ <p> "This shows the use of"; <b> " generic" </>; " closing tags" </> }} | This shows the use of generic closing tags | Close elements either with standard or "generic" closing tags. Note correct use of semicolons in this example! |
| Right | {{ <p> "This shows the use of"; <b> " standard" </b>; " closing tags" </p> }} | This shows the use of standard closing tags |
We said up top that one of the benefits of XML literals was that they were going to free us from the tyranny of the editor's HTML source mode. But what would we find if we took a peek?
Consider this bit of code:
{{ <p> "a simple paragraph" </p> }}
The source looks like this:
{{ <p> "a simple paragraph" </p> }}
Don't adjust your television set: this is completely correct and expected. The XML inside the DekiScript is being encoded using entities, because XML-in-DekiScript is just text as far as the web page is concerned. The DekiScript parser gets the text of the page (equivalent to xml.text(), which interprets all the entities), and then parsing XML out of the result. So you might say that XML is being double-interpreted, but who cares if it works!
So, not only do XML literals enable us to avoid the source editor, but they're almost going to force us to avoid it, because our code is now going to look like an incomprehensible mess.
Let's take a straightforward and very common example: building a table. In this case, we'll create a table of subpages of the DekiScript Reference home page, with various interesting page metadata. We'll do a bit of styling of the individual rows, depending on page characteristics. Note that for this example, for simplicity, we don't worry about such niceties as <thead> and <tbody> elements, we just throw everything into a bunch of <tr>s.
First, we need to create a table skeleton:
<table border="1" width="100%">
<tr style="font-weight:bold; background-color:#9d0303; color:white">
<td> "Title" </td>;
<td> "ViewCount" </td>;
<td> "Last Edited" </td>;
</tr>;
</table>;
This produces:
| Title | ViewCount | Last Edited |
Here we've created a single XML literal, which is a table. Inside the table are other XML literals. We've used a few attributes (border, width, and style) set to constant values and therefore enclosed in quotes. Finally, the contents of each <td> literal are string literals. For good measure, we put semicolons after each XML literal, but so far we don't strictly need any of them.
Now let's fill in the table. We want to create a table row for each subpage of the DekiScript Reference page. Here's the code:
<table border="1" width="100%">
<tr style="font-weight:bold; background-color:#9d0303; color:white">
<td> "Title" </td>;
<td> "ViewCount" </td>;
<td> "Last Edited" </td>;
</tr>;
foreach (var p in list.sort(map.values(wiki.getpage("DekiScript/Reference").subpages),"title")) {
<tr>
<td> web.link(p.uri, p.title) </td>;
<td> p.viewcount </td>;
<td> date.format(p.date, 'g') </td>;
</tr>;
}
</table>;
And here's the result:
| Title | ViewCount | Last Edited |
| DekiScript Functions and Variables | 545 | 10/23/2009 5:37 PM |
| DekiScript HTML/XML Statements | 241 | 9/28/2009 1:11 PM |
| DekiScript Literals | 211 | 3/18/2010 2:07 PM |
| DekiScript Operators | 124 | 6/12/2010 6:13 AM |
| DekiScript Remote Function Calls | 122 | 1/9/2009 10:15 AM |
| DekiScript Statements | 156 | 7/17/2008 11:25 PM |
| DekiScript Syntax | 178 | 6/17/2009 1:22 PM |
| HTML Page Composition Model | 170 | 6/17/2010 7:22 PM |
| JavaScript Events & Messages (JEM) | 594 | 7/7/2010 11:31 PM |
| Wiki Functions and Variables | 510 | 9/30/2009 10:08 PM |
| XPath | 97 | 3/16/2010 11:25 PM |
What we did here was pretty simple. After our header row, we inserted a loop that will generate a <tr> for each page we want to list. This loop is part of the contents of the <table> literal. The contents of each new <td> literal are, as always, little bits of valid DekiScript.
To finish up, let's highlight rows for pages that have comments. To do this, we'll add a DekiScript-evaluated style to the rows like this:
<table border="1" width="100%">
<tr style="font-weight:bold; background-color:#9d0303; color:white">
<td> "Title" </td>;
<td> "ViewCount" </td>;
<td> "Last Edited" </td>;
</tr>;
foreach (var p in list.sort(map.values(wiki.getpage("DekiScript/Reference").subpages),"title")) {
<tr style=(#p.comments ? "background-color:#dddddd" : "")>
<td> web.link(p.uri, p.title) </td>;
<td> p.viewcount </td>;
<td> date.format(p.date, 'g') </td>;
</tr>;
}
</table>;
And here's our final result:
| Title | ViewCount | Last Edited |
| DekiScript Functions and Variables | 545 | 10/23/2009 5:37 PM |
| DekiScript HTML/XML Statements | 241 | 9/28/2009 1:11 PM |
| DekiScript Literals | 211 | 3/18/2010 2:07 PM |
| DekiScript Operators | 124 | 6/12/2010 6:13 AM |
| DekiScript Remote Function Calls | 122 | 1/9/2009 10:15 AM |
| DekiScript Statements | 156 | 7/17/2008 11:25 PM |
| DekiScript Syntax | 178 | 6/17/2009 1:22 PM |
| HTML Page Composition Model | 170 | 6/17/2010 7:22 PM |
| JavaScript Events & Messages (JEM) | 594 | 7/7/2010 11:31 PM |
| Wiki Functions and Variables | 510 | 9/30/2009 10:08 PM |
| XPath | 97 | 3/16/2010 11:25 PM |
We only did one thing, which was to add a DekiScript attribute (style) to the generated <tr> element. The entire text between the parentheses is evaluated, so inside we must build the attribute value with DekiScript. Here, if the number of comments is non-zero, we set the background color to a light grey, else we leave the attribute blank (resulting in no style being applied).
XML literals are an unusual and powerful feature of DekiScript. If you understand the simple rules for working with them, you can avoid most of the pitfalls you'd otherwise encounter. Or, at least, know where to look when you make a mistake.
| Images 0 | ||
|---|---|---|
| No images to display in the gallery. |
Copyright © 2011 MindTouch, Inc. Powered by
/content/body/div[2]/div[4]/table[1]/tbody/tr[2]/td[3]/span, line 1, column 18: invalid Primary
Those are just illustrating the error messages you'll get from the "wrong" examples. What you're seeing is the xpath of the element on the page containing the DekiScript that is causing the error, followed by the (sketchy) error description.
Actually, I develop using <pre class="script"> blocks for the most part, but they're hard to embed clearly as examples. Inside double-braces, you need to do shift-enter to create multi-line scripts.