Caution! This page contains information on advanced practices. New and non-technical users may wish to request support in this area.
This page provides resources for using Xslate, the system IntelliSurvey uses to customize template files in the File Manager. The first section is a general syntax reference for Xslate. Below that are a list of Xslate functions supported by IntelliSurvey and other resources.
Xslate syntax documentation
This section is an edited copy of content from the Xslate Kolon documentation.
A brief background: Xslate supports multiple syntaxes. Two of these are Kolon and Metakolon. IntelliSurvey template files use Metakolon. The Xslate documentation specifies that the syntactical difference between Kolon and Metakolon is that you need to use [%
instead of <:
and %%
instead of :
. This section, via find/replace for those characters, converts the Kolon syntax into Metakolon syntax.
Variable access
Variable access:
[% $var %]
Field access:
[% $var.0 %] [% $var.field %] [% $var.accessor %] [% $var["field"] %] [% $var[0] %]
Variables may be HASH references, ARRAY references, or objects. Because $var.field
and $var["field"]
are the same semantics, $obj["accessor"]
syntax may call object methods.
Literals
Special:
%% nil # as undef, indicating "nothing" %% true # as the integer 1 %% false # as the integer 0
String:
%% "foo\n" # the same as perl %% 'foo\n' # the same as perl
Number:
%% 42 %% 3.14 %% 0xFF # hex %% 0777 # octal %% 0b1010 # binary
Array:
%% for [1, 2, 3] -> $i { ... }
Hash:
%% foo({ foo => "bar" })
Note that { ... }
is always parsed as hash literals, so you need not to use prefix:<+> as Perl sometimes requires:
%% {}.kv(); # ok %% +{}.kv(); # also ok
Expressions
Conditional operator (?:
):
%% $var.value == nil ? "nil" : $var.value
Relational operators (== != < <= > >=
):
%% $var == 10 ? "10" : "not 10" %% $var != 10 ? "not 10" : "10"
Note that ==
and !=
are similar to Perl's eq
and ne
except that $var == nil
is true iff $var is uninitialized, while other relational operators are numerical.
Arithmetic operators (+ - * / % min max
):
%% $var * 10_000 %% ($var % 10) == 0 %% 10 min 20 min 30 # 10 %% 10 max 20 max 30 # 30
Bitwise operators (prefix:<+^> +& +| +^
)
%% 0x1010 +| 0x3200 # bitwise or: 0x3210 %% 0x1010 +& 0x3200 # bitwise and: 0x1000 %% 0x1010 +^ 0x3200 # bitwise xor: 0x0210 %% +^0x1010 # bitwise neg: 0xFFFFEFEF (on 32 bit system)
Logical operators (! && || // not and or
)
%% $var >= 0 && $var <= 10 ? "ok" : "too smaller or too larger" %% $var // "foo" # as a default value
String operators (~
)
%% "[" ~ $var ~ "]" # concatenation
The operator precedence is very like Perl's:
. () [] prefix:<!> prefix:<+> prefix:<-> prefix:<+^> * / % x +& + - ~ +| +^ prefix:<defined> < <= > >= == != | && || // min max ?: not and or
Constants (or binding)
You can define lexical constants with constant
, which requires a bare word, and my
, which requires a variable name.
%% constant FOO = 42; %% my $foo = 42;
These two statements have the same semantics, so you cannot modify $foo
.
%% my $foo = 42; $foo = 3.14; # compile error!
Loops
There arefor
loops that are like Perl's foreach
.
%% # iterate over an ARRAY reference %% for $data -> $item { [[% $item.field %]] %% } %% # iterate over a HASH reference %% # You must specify how to iterate it (.keys(), .values() or .kv()) %% for $data.keys() -> $key { [% $key %]=[% $data[$key] %] %% }
And the for
statement can take else
block:
%% for $data -> $item { [[% $item.field %]] %% } %% else { Nothing in data %% }
The else
block is executed if $data is an empty array or nil.
You can get the iterator index in for
statements as $~ITERATOR_VAR
:
%% for $data -> $item { %% if ($~item % 2) == 0 { Even (0, 2, 4, ...) %% } %% else { Odd (1, 3, 5, ...) %% } %% }
$~ITERATOR_VAR
is a pseudo object, so you can access its elements via the dot-name syntax.
%% for $data -> $i { %% $~i # 0-origin iterator index (0, 1, 2, ...) %% $~i.index # the same as $~i %% $~i.count # the same as $~i + 1 %% if ($~i.index % 2) == 0 { even %% } %% else { odd %% } %% $~i.cycle("even", "odd") # => "even" -> "odd" -> "even" -> "odd" ... %% }
Note: Supported iterator elements (e.g., $~i.index
) are
index :Int count :Int body :ArrayRef size :Int max_index :Int is_first :Bool is_last :Bool peek_next :Any peek_prev :Any cycle(...) :Any
while
loops are also supported in the same semantics as Perl's:
%% # $obj might be an iteratable object %% while $dbh.fetch() -> $item { [[% $item.field %]] %% }
while defined expr -> $item
is interpreted as while defined(my $item = expr)
for convenience.
%% while defined $dbh.fetch() -> $item { [[% $item # $item can be false-but-defined %]] %% }
Loop control statements, namely next
and last
, are also supported in both for
and while
loops.
%% for $data -> $item { %% last if $item == 42 ... %% }
Conditional statements
There are if-else
and given-when
conditional statements.
if-else
:
%% if $var == nil { $var is nil. %% } %% else if $var != "foo" { # elsif is okay $var is not nil nor "foo". %% } %% else { $var is "foo". %% } %% if( $var >= 1 && $var <= 10 ) { $var is 1 .. 10 %% }
Note that if
doesn't require parens, so the following code is okay:
%% if ($var + 10) == 20 { } # OK
given-when
(also known as switch statement):
%% given $var { %% when "foo" { it is foo. %% } %% when ["bar", "baz" ] { it is either bar or baz. %% } %% default { it is not foo nor bar. %% } %% }
You can specify the topic variable.
%% given $var -> $it { %% when "foo" { it is foo. %% } %% when $it == "bar" or $it == "baz" { it is either bar or baz. %% } %% }
Functions and filters
You can register functions via function
or module
options for Text::Xslate->new()
.
Once you have registered functions, you can call them with the ()
operator. infix:<|>
is also supported as a syntactic sugar to ()
.
%% f() # without args %% f(1, 2, 3) # with args %% 42 | f # the same as f(42)
Functions are just Perl's subroutines, so you can define dynamic functions (a.k.a. dynamic filters), which is a subroutine that returns another subroutine:
# code sub mk_indent { my($prefix) = @_; return sub { my($str) = @_; $str =~ s/^/$prefix/xmsg; return $str; } } my $tx = Text::Xslate->new( function => { indent => \&mk_indent, }, ); %%# template %% $value | indent("> ") # Template-Toolkit like %% indent("> ")($value) # This is also valid
There are several built-in functions, which you cannot redefine:
%% $var | mark_raw # marks it as a raw string %% $var | raw # synonym to mark_raw %% $var | unmark_raw # removes "raw" marker from it %% $var | html # does html-escape to it and marks it as raw %% $var | dump # dumps it with Data::Dumper
Note that you should not use mark_raw
in templates because it can easily introduce security holes just like type casting in C. If you want to generate HTML components dynamically, e.g. by HTML form builders, application code should be responsible to make strings as marked raw
.
Note: Xslate/Metakolon is a Perl-based language but has its own eccentricities. Here are a couple examples of how this might surprise Perl users:
- variables aren't stateful, so you can't update their values once they've been defined.
- to concatenate strings, use the tilde character "~" instead of the period "." character.
Links to supported methods
The Files tool displays template files (e.g., table.tx) that are parsed and rendered using the Xslate engine. This section contains visual reminders of methods one might employ and links to the author's pages for some of the supported methods.
https://metacpan.org/pod/release/GFUJI/Text-Xslate-3.1.0/lib/Text/Xslate/Bridge/Star.pm
- lc($str)
- uc($str)
- substr($str, $offset, $len)
- sprintf($fmt, args...)
- rx($regex_pattern)
- match($str, $pattern)
- replace($str, $pattern, $replacement)
- split($str [, $pattern [, $limit]])
https://metacpan.org/pod/release/GFUJI/Text-Xslate-3.3.4/lib/Text/Xslate/Manual/Builtin.pod
- METHODS
- LOOP VARIABLES
- FILTERS/FUNCTIONS
Custom Xslate methods
Besides the default functions available in Text::Xslate, IntelliSurvey has custom functions it supports. Below is a collection of descriptions.
[% get_appid() %]
Use this to return the survey ID (e.g., for a script reference or something). Example:
require(['/s/[% get_appid() %]/some_script_in_surveys_s_folder.js'], function(fn) { // fn() ... code that uses this script... })
get_value('QPATH')
This pulls variables' values from the respondent's survey record to use in logic within template files. Note that in v6.6, we would simply type $QPATH
whereas in v7, we use this function with the dollar sign removed and quote marks added.
render
Render a hashref of data. The data is expected to have a 'template_file' key, and that is used as the template. The input data is then rendered with that template. The $extra input is merged with the data, if passed. This is meant as a convenient way for .tx files to include extra data, since any calls to 'render' from a .tx file are carried over.
get_v
Get the value for an attribute, used by _mark_attrs and _mark_obj_attrs (which are the 'attrs' and 'obj_attrs' functions, respectively, in the templates). This does special processing if the value is an array reference, combining the elements of the array into one string. If an element is undefined, it is skipped. Moreover, if an element of the array is a hash reference, the key of the hash is the value to combine, and the value of the hash is the test condition. For example:
attrs( class = ['i-header', { '-vscale' = $vscale}, {' i-opt' = $opt } ], type = 'text' )
The 'class' attribute value will be 'i-header' if both $vscale and $opt are not true, 'i-header-vscale' if $vscale is true, and 'i-header-vscale i-opt' if both $vscale and $opt are true.
noescape
When you put a variable or string in a template code block [% var_or_string %], it will be escaped automatically for you. If you do not want the Template engine to autoescape, you can use [% noescape($var_or_string) %].
obj_attrs()
Helper function provided in template to wrap JS object attributes, given key/value pairs corresponding to name of an attribute and its value. If the value is undefined for the attribute, returns an empty string. A special case exists for a string beginning with function\s*\( or new\s, or simply true,or false: the value isn't escaped.
attrs()
Helper function provided in template to wrap element attributes, given key/value pairs corresponding to name of an attribute and its value. If the value is undefined for the attribute, returns an empty string. A special case exists for 'multiple', 'checked', etc.: if those attributes have defined values, they will return without the value because they're boolean attributes.
int()
Helper function provided in template to return either the numeric version of the value or zero. This helps for casting when testing a value that may not be defined.
seq()
Helper function provided in template to return a sequence from 1 to the end given. If two parameters are passed, first value is considered where to start sequence from, and the second value is where to end. Cf. man (1) seq
str()
Helper function provided in template to return either the stringified version of the value or just an empty string. This helps to avoid "'XXXX' is not defined" exceptions when we don't really care
strftime()
Helper function provided in template to return a stringified version of a date, other than its default.
is_pulldown()
Returns true if the given type is a pulldown.
get_resource()
e.g., get_resource('PROGRESS'), get_resource('JS_REQUIRED')
Pass through to IS::Resources::Survey to return the string in the current language. Similar to _render, we use the $current_theme to get the language.
get_admin_resource()
e.g., get_admin_resource('TESTMODERESPDATA', get_trans_id() )
Pass through to IS::Resources::Admin for the current language. Similar to _render, we use the $current_theme to get the language.
get_css_ids()
Return the list of css ids for the $current_theme.
get_theme_id()
Returns theme ID, e.g., 'dd'.
get_trans_id()
Returns respondent's trans_id.
get_appid()
Returns survey ID.
get_iter()
Returns an iterator object from IS::Utility::Iter.
my $iter = get_iter(); # Get the iterator $iter.set_cur(1); # Set the start number to 1 (default: 0) $iter.set_step(5); # Set the step to 5 (default: 1) $iter.incr(); # Step up $iter.get_cur(); # Return the current number, 6 $iter.decr(); # Step down # Note: anything other than get_cur() and get_step() returns void so as not to pollute the template with their return values.
Template variables
These variables can be requested and rendered from within most templates.
Note: Each template file will have a different set of data to work with. Add the following to acceptable areas of a template file to see what variables are available at that point in the hierarchy:
<pre>[% __ROOT__ | dump %]</pre>
"acceptable areas" are where HTML is expected to be rendered.
In a template that uses cascade
, HTML can be rendered in an area that refers to a block named in the original template file.
appid | current survey ID |
autopage | Boolean (e.g., 1). True by default, unless the autopage: n tag is present. |
charset | e.g., 'UTF-8' |
enctype | e.g., 'application/x-www-form-urlencoded' |
focus | Boolean (e.g., 1). True by default, unless the focus: n tag is present. |
page_num | e.g., 1 |
percent_done | e.g., 0 |
post_uri | e.g., 'https://demo70.intellisurvey.com/run/mysurvey/SOMEIDHERE' |
progress | Boolean (e.g., 1). True by default, unless the progress: n tag is present. |
render_items |
(large collection of objects and arrays) e.g.,
|
require_js | e.g., 1 |
Pass comma-separated list to a tx file
This syntax is useful when you're passing a comma separated list to a .tx file that you need to convert to an array to be used in a for loop. This method is used in programming conjoints.
%% my $columns = split( $col_order, rx(",") ); ... %% for $columns -> $column {
The accompanying line for the template data looks like this:
template_data: <<EOTD my $opt1 = fetch_row( name => 'advanced_demos.datastack', row => $QCJ2_VERS*1000 + %%ID%%*10 + 1 ); my $opt2 = fetch_row( name => 'advanced_demos.datastack', row => $QCJ2_VERS*1000 + %%ID%%*10 + 2 ); my $opt3 = fetch_row( name => 'advanced_demos.datastack', row => $QCJ2_VERS*1000 + %%ID%%*10 + 3 ); return { opt1 => $opt1, opt2 => $opt2, opt3 => $opt3, col_order => $QCJ2_COL_ORDER_%%ID%% } EOTD
Comments
0 comments
Please sign in to leave a comment.