3 of 3 found this page helpful

Working with XML Literals

    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!

    Table of Contents

    Introduction

    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!

    The Rules for XML Literals

    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.

    1. A string with XML is not an XML literal
    2. An XML literal is used the same way any other literal is used
    3. A single XML literal consists of a single outer element
    4. The contents of an XML literal must be valid DekiScript
    5. Use generic or specific closing tags
    6. Attributes with a constant value must be enclosed in quotes
    7. Element names, attribute names, and attribute values enclosed in parentheses will be evaluated as DekiScript
    8. Entities are supported in 9.08

    Rule 1: A string containing XML is not an XML literal

    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

    Rule 2: An XML literal is used the same way any other literal is used

    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

    {{
      var x = <p>"assign to a variable"</p>;
      typeof(x);
    }}

    xml The XML literal (<p> element) is assigned to the variable x.
     

    Rule 3: A single XML literal consists of one outer element

    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.

    Rule 4: The contents of an XML literal must be valid DekiScript

    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

    {{
      <p>
      "Add a line break"
      <br />
      "to the output"
      </p>
    }}

    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
    to the output

     

    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>;
    }}
    • 1
    • 2
    • 3
    • 4
    • 5
    Contents of <ul> is a valid script containing a single foreach loop.  The loop consists of a single statement, which is a <li> literal.

    Rule 5: Use generic or specific closing tags

    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>).

    Rule 6: Attributes must be enclosed in quotes to be evaluated as a constant

    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>
    }}
    a table!

    Rule 7: Element names, attribute names, and attribute values enclosed in parentheses will be evaluated as DekiScript

    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>;
    }}
    a 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

    {{
      var list_type = "ul";
      <(list_type)>
        <li> "item 1" </li>;
        <li> "item 2" </li>;
      </>;
    }}

    • item 1
    • item 2
    Element name is evaluated.  This is a good time to use generic closing tags.
    Right

    {{
      var attr_name="style";
      var attr_value="font-style: italic";
      <span (attr_name)=(attr_value)>
        "this is italic!"
      </span>;
    }}

    this is italic! Same thing with attribute names!
     

    Rule 8: XML entities are supported in 9.08

    Once you're writing XML, you might naturally be inclined to throw in some entities (e.g.: &nbsp;, &gt;, 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> &lt; </li>
      <li> &gt; </li>
      <li> &amp; </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> &para; "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> &para;; "paragraph" </b> }} ¶paragraph
    Wrong {{ <b> &para; &amp; </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> &para;; &amp;; </b> }} ¶& Always use that extra semicolon

    Bonus Topics

    Here are a couple of additional tidbits that are good to know.

    Entering JavaScript and CSS

    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.

    Semicolon Usage

    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

    Closing Tags

    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

    What this all looks like in Source Mode

    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:

    {{&nbsp;&lt;p&gt; "a simple paragraph" &lt;/p&gt;&nbsp;}}
    

    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.

    Example: Building a Table of Page Data

    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.

    Step 1: Create the table

    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:

    TitleViewCountLast 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.

    Step 2: Populating the table

    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:

    TitleViewCountLast Edited
    DekiScript Functions and Variables54510/23/2009 5:37 PM
    DekiScript HTML/XML Statements2419/28/2009 1:11 PM
    DekiScript Literals2113/18/2010 2:07 PM
    DekiScript Operators1246/12/2010 6:13 AM
    DekiScript Remote Function Calls1221/9/2009 10:15 AM
    DekiScript Statements1567/17/2008 11:25 PM
    DekiScript Syntax1786/17/2009 1:22 PM
    HTML Page Composition Model1706/17/2010 7:22 PM
    JavaScript Events & Messages (JEM)5947/7/2010 11:31 PM
    Wiki Functions and Variables5109/30/2009 10:08 PM
    XPath973/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.

    Step 3: Styling the rows

    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:

    TitleViewCountLast Edited
    DekiScript Functions and Variables54510/23/2009 5:37 PM
    DekiScript HTML/XML Statements2419/28/2009 1:11 PM
    DekiScript Literals2113/18/2010 2:07 PM
    DekiScript Operators1246/12/2010 6:13 AM
    DekiScript Remote Function Calls1221/9/2009 10:15 AM
    DekiScript Statements1567/17/2008 11:25 PM
    DekiScript Syntax1786/17/2009 1:22 PM
    HTML Page Composition Model1706/17/2010 7:22 PM
    JavaScript Events & Messages (JEM)5947/7/2010 11:31 PM
    Wiki Functions and Variables5109/30/2009 10:08 PM
    XPath973/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).

    Conclusion

    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.

    Was this page helpful?
    Tag page
    Viewing 5 of 5 comments: view all
    Nice page, Neil. Good info.
    Posted 12:01, 28 Oct 2009
    Thanks for posting. Question - when I view this page, I see this in the Result column for many of the tables:

    /content/body/div[2]/div[4]/table[1]/tbody/tr[2]/td[3]/span, line 1, column 18: invalid Primary
    Posted 09:52, 11 Dec 2009
    @imattdotnet
    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.
    Posted 11:59, 11 Dec 2009
    Terrific content. I am confused, however. How do you develop your DekiScript? If I copy and paste the "right" examples, they work fine in my instance. When I try to enter the examples manually, I get <p> paragraphs instead of <br /> and thus, the examples do not work. I'm trying to avoid the source view in the runtime editor but it seems I have to use it. Suggestions?
    Posted 16:32, 27 Dec 2009
    @bohappa:
    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.
    Posted 18:35, 30 Dec 2009
    Viewing 5 of 5 comments: view all
    You must login to post a comment.

    Copyright © 2011 MindTouch, Inc. Powered by