Skip to content
MaxMotovilov edited this page Sep 17, 2010 · 5 revisions

Most of the expressive power behind CHT templates is provided by the dataflow architecture of jtlc & JXL. Q+ (Query Plus) is a simple textual representation of JXL abstract syntax trees, capable of encoding a restricted subset of all JXL programs. Q+ also slightly modifies the behavior of some of the JXL tags and adds certain new ones, as described below.

The syntax

A Q+ expression is essentially a linearization of a JXL tree, limited to no more than 2 children per node with the 2nd child (if present) required to be a leaf node. Thus each node may have two distinct arguments, arg p (predecessor) and arg o (own argument) which are mapped to the first and second argument of the corresponding JXL tag as follows:

  • If arg o is present, it becomes the first argument and then, if arg p is present, it becomes the second one;
  • Otherwise, arg p is the first argument (if present).

The actual syntax is as follows: tags are separated from arg o with a colon (':') character and individual stages of the pipeline are delimited with a vertical bar ('|') character. Spaces are ignored, unless they happen to be in a string literal where they would matter. Note that arg o should not be quoted even when it contains a string literal (e.g. in an argument of i18n:). Any balanced quotes (single or double) within arg o are preserved: this is a way to embed Javascript string literals into expr: expressions. To embed an unbalanced quote character or a '|' character into the string, prefix it with '\'. Note also that vertical bar characters contained within balanced brackets of any kind allowed in Javascript expressions (round, square or curly) are not interpreted as delimiters in the sense of Q+ so they do not have to be escaped.

The leftmost tag may be omitted together with ':', in which case — unless all that’s left is an identifier — the first stage is simply treated as a string literal interpreted according to the context:

  • In singleton mode, it is an expr:;
  • In iteration mode (i.e. if the entire expression is a generator), it is a query: following the JSON Query syntax.

Strictly speaking, the same rule applies to the own arguments as well, if the tag does not interpret the argument as a string literal. In most cases those will be evaluated in singleton mode and become expressions, each: being the only obvious exception.

All other stages in the pipeline must not omit the tag name, but may omit the argument together with the ':'.

Examples

  • $.firstName translates into expr("$.firstName") when in singleton mode or into query("$.firstName") when a generator;
  • expr:firstName always translates into expr("firstName");
  • from:ids|expr:$1\[$\].data|defined translates into defined( expr( "$1\[$\].data", from( expr( "ids" ) ) ) ).

JXL tags in Q+

  • acc: useless, since there is no way to pass more than 1 argument to expr;
  • arg: unnecessary due to different interpretation of $1 through $9 (see expr below);
  • bind: cannot be used since there’s no way to embed a function into Q+. Use filters or global functions instead;
  • current: use $ instead;
  • defined: can be used;
  • each: very limited in capability but can be used, for example, to nest queries — query:OuterQuery_@|each:@_InnerQuery;
  • expr: always interprets $1 through $9 as referring to arguments of the compiled template (equivalent of arg() in JXL);
  • from: can be used;
  • group: cannot be used in Q+. Use the CHT <?group?> element instead;
  • keys: can be used;
  • last: can be used;
  • many and one: technically can be used, but the application is hard to imagine;
  • query: can be used; caveat about $1 through $9 applies here as well;
  • quote: useless;
  • replace: can be used;
  • setkey: useless, since the dictionary sink is not available.

Q+ tags and built-in filters

lower and upper

Treat argument (usually arg p) as a string and convert it to lower or upper case respectively.

escapeText and escapeAttribute

Treat argument (usually arg p) as a string and apply the appropriate HTML escaping to it. Note that CHT parser inserts these tags automatically when the expression appears in the context of HTML text content or HTML attribute value respectively, so their explicit use is not likely.

raw

Prevents CHT from automatically inserting escapeText or escapeAttribute; should be the last tag in the expression.

i18n

Maps its argument (typically arg o) against the translation dictionary at compile time; use to insert localized strings into HTML arguments:

<img src="myimage.png" alt="{{i18n:My image}}">

The i18n can also substitute its arg p into the resulting string with [dojo.replace()|http://docs.dojocampus.org/dojo/replace] syntax:

<a href="/users/{{$.userName}}.html" title="{{$.userName|i18n:Go to {0}\'s home page}}">

ref

Inserts a Javascript expression that will evaluate to the value of the argument at the time toParsedDom() executes. This is the mechanism used to connect codebehind objects to the data objects during template processing:

dojo.declare( 'myWidget', null, {
  constructor: function( bag, elt ) {
    dojo.mixin( this, bag );
    dojo.connect( elt, 'onclick', this, this.onclick );
  },
  data: null,
  onclick: function() {
    // Do something with this.data
  }
} );
<? template useMyWidget ?>
  <a dojoType='myWidget' data='ref:$'>Use myWidget!</a>
<? /template ?>

The ref tag can also be used to inject references into scripts embedded into the template. Note, however, that once toParsedDom() has completed, the variables referred to by ref expressions go out of scope. Therefore the only legitimate use for ref}| injection in scripts is in startup scripts executed by @dojo.parser.parse() after the codebehind object has been instantiated:

<? template AuthorSelect ?>
  <select dojoType='dijit.form.ComboBox' onchange='this.newBookPane.onAuthorChanged( this, this.indexInAuthorsList )'>
    <script type="dojo/method">
      this.newBookPane={{ref:$1}};
      this.indexInAuthorsList = {{$1.authors.length}};
    </script>
    <option selected></option>
    <? foreach "from:$" ?>
      <option>{{firstName}} {{lastName}}</option>
    <? /foreach ?>
  </select>
<? /template ?>

In the example above the startup script is used to inject additional properties into the widget for subsequent use by the inline event handler. References could not have been injected into the handler itself because by the time it executes the ref expressions would be out of scope.

Custom filters

Whenever a tag name encountered in a Q+ expression does not match one of the built-in tags or filters, it is looked up in the filters dictionary supplied to the constructor of the dojox.jtlc.CHT instance. Two kinds of custom filters are supported: functions and expressions:

Functions

If the supplied custom filter is a function, it translates into an instance of bind with arg p (if present) as the first argument. Note that this behavior is the opposite of the built-in tags which may be confusing, but conforms to the JXL convention of evaluating the first argument of a function as a generator in iterative contexts. For example,

<? foreach "from:verbs|translateTo:'French'" ?>
  <? if $# ?>, <? /if ?>
  {{$}}
<? /foreach ?>

will generate a list of French translations from the array $.verbs, provided that translateTo(verb,language) does indeed work! Note that 'French' is quoted: otherwise it would have been evaluated as an expression $.French.

Expressions

If the supplied custom filter is a string, it translates into an instance of expr with the filter text as its first argument, arg p (if present) as the second and arg o as the third if it is present together with arg p, or as the second otherwise. Overall, the result is very similar to using a function filter except the expression is injected into the body of the compiled template inline.

The filter expression may refer to the arguments of the compiled template via $1 through $9 unless it has been invoked with both arg p and arg o, in which case arg o becomes $1 and $2 through $9 are undefined.

Global functions

If the match of a tag name against filters dictionary fails, Q+ attempts to find a global function with this name, using dojo.getObject, as a last resort. It will also do that for qualified tag identifiers (i.e. two or more names separated with a dot) which are never matched against built-in tags or custom filters. If successful, the tag will work as a function filter, that is, it’ll bind the global function to arg p and/or arg o.

Methods of global classes

A twist on the rule above is available for a simpler access to methods of global object classes such as Date:

from:timestamps|Date.toLocaleTimeString

is an effective equivalent of a longer and less readable

from:timestamps|expr:new Date($).toLocaleTimeString()

This construct can also be used with user-defined classes and methods but note that the resulting expression always instantiates a new object using the corresponding class constructor without checking whether its argument may already have the right type!