common-dustjs-helpers
is an npm module that offers a small library of helper functions for the Dust.js web-templating framework (and the LinkedIn fork of Dust.js). This library is largely complementary to LinkedIn's dustjs-helpers library.
The philosophy behind common-dustjs-helpers
is the same as that behind dust.js itself--namely that templates should contain presentation-level logic, free from side effects (and hence composable and capable of running in parallel). In particular, you will not find helper functions that allow you to execute arbitrary JavaScript.
NOTE: This repository follows the git flow branching model. The latest stable (released) version of the code should be found in the master
branch. (Individual releases will be tagged in that branch.) The latest unstable (un-released) version of the code should be found in the develop
branch.
common-dustjs-helpers
is packaged as an npm module under the name common-dustjs-helpers. To install the library, run:
npm install common-dustjs-helpers
or add something like:
"common-dustjs-helpers": "latest"
to the dependencies
section of your package.json
file.
To register the common helper functions with your Dust.js instance, require the module and then invoke the exportTo
method. For example:
var dust = require("dustjs-linked"); // or just "dustjs"
require("common-dustjs-helpers").exportTo(dust);
You can then use the Dust.js instance (dust
) as you normally would.
If you'd like to register the helper methods but not the filter, use exportHelpersTo
. If you'd like to register the filter(s) but not the helper methods, use exportFiltersTo
.
Note that exportTo
, exportHelpersTo
and exportFiltersTo
are also available as export_to
, export_helpers_to
and export_filters_to
.
@count | @deorphan | @downcase | @elements | @even | @filter | @first | @idx | @if | @index | @last | @odd | @random | @regexp | @repeat | @sep | @substring | @switch | @titlecase | @trim | @unless | @upcase
Emits the size (length) of an array, map or similar container.
Parameters:
of
,in
- reference to context variable to count
Example:
Context:
{ "foo": [1,2,3,4,5] }Template:
The array has {@count of=foo/} elements.
Output:
The array has 5 elements.
Another Example:
Context:
{ "bar": { "a":1, "b":2, "c":3 } }Template:
The map has {@count of=bar/} keys.
Output:
The map has 3 keys.
Replaces whitespace between the last two "words" of the body with
Parameters:
none
Example:
Template:
{@deorphan}The last word won't be left dangling.{/deorphan}.
Output:
The last word won't be left dangling.
Converts body text to lower case.
Parameters:
none
Example:
Context:
{ "name":"World" }Template:
{@downcase}Hello {name}.{/downcase}.
Output:
hello world.
Iterates over the key-value pairs in a map, exposing $key
, $value
and $idx
context variables inside the loop.
When the specified map is empty or undefined, the {:else}
block (if any) will be executed.
Parameters:
of
,in
- reference to context variable to iterate overidx
,index
- optional alternative name for the context variable that will contain the zero-based index of the element inside the loop; defaults to$idx
key
- optional alternative name for the context variable that will contain the key while inside the loop; defaults to$key
.value
- optional alternative name for the context variable that will contain the value while inside the loop; defaults to$value
.sort
- determines order by which the list of pairs will be iterated over. Note that numeric values will be sorted as numbers.- when
false
(the default) no sort will be applied. - when
true
the pairs will be sorted by key. - when a blank string (
""
), the pairs will be sorted by value. - when a non-blank string other than
true
orfalse
is used, the value part of each pair is assumed to be a map, and pairs will be sorted the specified attribute of the value-map.
- when
dir
- if sorting, setdir
equal tod
,dec
dsc
,desc
, ordescending
to reverse the order of the sort.
Example:
Context:
{ "themap": { "Y":2, "Z":1, "X":3 } }Template:
Unsorted: {@elements of=themap}{$idx}.{$key}={$value}; {/elements} By key: {@elements of=themap sort="true"}{$idx}.{$key}={$value}; {/elements} By value: {@elements of=themap sort=""}{$idx}.{$key}={$value}; {/elements}
Output:
Unsorted 0.Y=2; 1.Z=1; 2.X=3; By key: 0.X=3; 1.Y=2; 2.Z=1; By value: 0.Z=1; 1.Y=2; 2.X=3;
Note that sequence-based helper tags (@even
, @odd
, @first
, @last
, @index
, @idx
, @sep
, etc.) work within the @elements
helper in the same way that they do within the built-in {#foo}{/foo}
array-iterating blocks. The same statement should be true for any dust helper function that relies on the (standard) stack.index
and stack.of
properties of the dust context object.
Executes the body for even-indexed elements of a list, the {:else}
block for others.
Parameters:
none
Example:
Context:
{ "list":["A","B","C","D","E"] }Template:
{#list} * {@even}{.} is even.{:else}{.} is odd.{/even} {/list}
Output:
* A is even. * B is odd. * C is even. * D is odd. * E is even.
Dust.js includes several "filters" that convert the content of a context variable before emitting it. For example, the snippet:
{foo|uc}
will escape the value of the context variable foo
using JavaScript's encodeURIComponent
function.
The @filter
helper makes it possible to apply a filter to arbitrary text (not just the value of a variable).
For example, the snippet:
{@filter type="uc"}{foo}{/filter}
is equivalent to {foo|uc}
, and:
{@filter type="uc"}Some text before. {foo} Some text after.{/filter}
will apply the filter to all of the text within the body of the @filter
tag, not just the value of foo
.
Parameters:
type
- the filter type (in the base dustjs implementation, this includesh
,s
,js
,u
,uc
, but any registered filter value can be applied).
Example:
Context:
{ "name":"<em>World</em>" }Template:
{@filter type="h"}<blink>Hello {name}!</blink>{/filter}
Output:
<blink>Hello <em>World</em>!</blink>.
Executes the body for first element in a list or enumeration, the {:else}
block for others.
Parameters:
none
Example:
Context:
{ "list":["A","B","C","D","E"] }Template:
{#list} * {@first}{.} is first.{:else}{.} is not first.{/first} {/list}
Output:
* A is first. * B is not first. * C is not first. * D is not first. * E is not first.
This helper restores the functionality of the {@idx}
helper in the original dust.js. (This helper was subsequently removed in linkedin-dustjs.)
Quoting the original dust.js documentation:
The idx tag passes the numerical index of the current element to the enclosed block.
Parameters:
none
Example:
Context:
{ "names": [ "Moe", "Larry", "Curly" ] }Template:
{#names}{.}{@idx}{.}{/idx}{@sep}, {/sep}{/names}
Output:
Moe0, Larry1, Curly2
Note that this helper will only be added if an @idx
helper does not already exist. If @idx
is found in Dust's list of helper functions it will be left alone.
Also see the @sep
and @index
helpers.
This helper conditionally executes the tag body (or an {:else}
block, if any) based on a several types of predicate.
When the value
parameter is used by itself, the @if
helper will execute the body if the specified value is (or evaluates to):
- the boolean
true
- a positive, non-zero numeric value
- a string starting with
T
orY
(case-insensitive) - the string
on
(case-insensitive) - a non-empty array or object (map).
For example:
{@if value=foo}YES{:else}NO{/if}
will emit YES
for the following contexts:
{foo:true}
{foo:"true"}
{foo:"Y"}
{foo:1}
{foo:"1"}
{foo:2}
{foo:0.1}
{foo:"on"}
{foo:function() { return true; }}
{foo:[1,2]}
{foo:{bar:"xyzzy"}}
and NO
for the following contexts:
{foo:false}
{foo:"false"}
{foo:"N"}
{foo:0}
{foo:"0"}
{foo:-2}
{foo:-0.1}
{foo:"off"}
{foo:function() { return false; }}
{foo:[]}
{foo:{}}
{foo:null}
{bar:"anything"}
The is
parameter will compare the value of value
(a context variable or string, possibly including dust markup) to that of is
(a context variable or string, possibly including dust markup). If the values are equal (===
) the body will be evaluated, otherwise the {:else}
block (if any) will be evaluated.
The isnt
parameter will compare the value of value
(a context variable or string, possibly including dust markup) to that of is
(a context variable or string, possibly including dust markup). If the values are NOT equal (!==
) the body will be evaluated, otherwise the {:else}
block (if any) will be evaluated.
The isnt
parameter will compare the value of value
(a context variable or string, possibly including dust markup) to that of is
(a context variable or string, possibly including dust markup). If the values are NOT equal (!==
) the body will be evaluated, otherwise the {:else}
block (if any) will be evaluated.
The above
parameter will compare the value of value
(a context variable or string, possibly including dust markup) to that of above
(a context variable or string, possibly including dust markup) using the >
operator. If value > above
the body will be evaluated, otherwise the {:else}
block (if any) will be evaluated.
The below
parameter will compare the value of value
(a context variable or string, possibly including dust markup) to that of below
(a context variable or string, possibly including dust markup) using the <
operator. If value < below
the body will be evaluated, otherwise the {:else}
block (if any) will be evaluated.
The matches
parameter will compare the value of value
(a context variable or string, possibly including dust markup) to that of regular expression specified in matches
(a context variable or string, possibly including dust markup). If matched, the body will be evaluated, otherwise the {:else}
block (if any) will be evaluated.
When looping over an array, this helper emits the one-based index of the current element.
For example, given the context:
{ mylist: ["A","B","C"] }
The Dust.js snippet:
{#mylist}{.} is {@index/}.{@sep} {/sep}{/mylist}
yields:
A is 1. B is 2. C is 3.
When a body is provided, @index
sets the one-based index as the current context and evaluates the body as normal.
For example, the Dust.js snippet:
{#mylist}{.} {@index}is {.}{/index}.{@sep} {/sep}{/mylist}
also yields:
A is 1. B is 2. C is 3.
Note that {@index}{/index}
yields nothing. Only the {@index/}
syntax emits the index value without the {.}
tag.
Executes the body for last element in a list or enumeration, the {:else}
block for others.
Parameters:
none
Example:
Context:
{ "list":["A","B","C","D","E"] }Template:
{#list} * {@last}Finally, {.} is last.{:else}{.} is not last.{/last} {/list}
Output:
* A is not last. * B is not last. * C is not last. * D is not last. * Finally, E is last.
Executes the body for odd-indexed elements in a list or enumeration, the {:else}
block for others.
Parameters:
none
Example:
Context:
{ "list":["A","B","C","D","E"] }Template:
{#list} * {@odd}{.} is odd.{:else}{.} is even.{/odd} {/list}
Output:
* A is even. * B is odd. * C is even. * D is odd. * E is even.
The @random
helper emits a random integer in the specified range.
For example, the Dust.js code:
{@random min="1" max="100"}Your number is {.}.{/random}
might generate:
You number is 67.
(Or more generally, the value 67
will be any integer between 1
and 100
, inclusive, with a pseudo-random distribution.)
The random
helper accepts three parameters:
min
- the minimum value to generate (defaults to0
)max
- the minimum value to generate (defaults to1
)set
- when specified, the random value will be assigned to a context variable of the given name.
The @regexp
helper can be used to extract the part of a string that matches a given regular expression.
For example, given the context:
{
"foo":"http://example.com/"
}
the Dust.js snippet:
{@regexp string="{foo}" pattern="^(HTTPS?:)\/\/" flags="i"}
Protocol: {$[1]}
{/regexp}
yields:
Protocol: http
Similarly, given the context:
{
"foo":"The quick brown fox jumped over the lazy dogs."
}
the Dust.js snippet:
{@regexp string="{foo}" pattern="^(Th[^\s]+).*(j.mp.d).*(o.e.)"}
First: {$[1]}
Second: {$[2]}
Third: {$[3]}
{/regexp}
yields:
First: The
Second: jumped
Third: over
The regexp
helper accepts the following parameters:
-
string
- the text that the regular expression will be matched against. This may be a literal string or an expression containing one or more dust variables. -
pattern
- the regular expression that is used to perform the matching. The contents of this parameter follow the regular JavaScript regular-expression syntax, save that the leading and trailing/
characters are omitted. -
flags
- this optional parameter may contain any combination of the charactersi
,g
andm
. They specify flags that modify the regular expression (ignore-case, global and multi-line, respectively), as would typically appear after the final/
in a JavaScript regular expression literal. (For example, the regular expression/([aeiou])/ig
is equivalent topattern="([aeiou])" flags="ig"
. -
var
- this optional parameter specifies the context variable name used to access the numbered matches with the body of theregexp
tag. When omitted, the matching subexpressions will be accessed using the expression{$[n]}
(wheren
is the index of the subexpression, as seen above). Whenvar
is specified, the value ofvar
is inserted between the$
and[
. For example, givenvar="foo"
, the first matching subexpression will be accessed via{$foo[1]}
, the second via{$foo[2]}
and so on. (This is helpful as a way to disambiguate variables whenregexp
tags are nested.)
If the regular expression matches the specified string, the body of the regexp
tag will be evaluated. When the regular expression does not match the specified string, the {:else}
block of the regexp
tag (if any) will be executed.
If the "global" flag (g
) is set, the tag pair {#$}
{/$}
(or {#$<var>}
{/$<var>}
when var
is set) can be used to iterate over multiple matches.
For example, the Dust snippet:
{@regexp string="The quick brown fox jumped."
pattern="([aeiou])" flags="ig" var="M"}
Vowels: {#$M}{.}{@sep}, {/sep}{/$M}
{:else}
No vowels found.
{/regexp}
will evaluate to:
Vowels: e, u, i, o, o, u, e
The @repeat
helper will execute its body times
times (as if a list of times
bodies were passed to a section block).
For example the Dust.js snippet:
{@repeat times="3"}
Well{@sep}, {/sep}
{/repeat}
will resolve to:
Well, Well, Well
when evaluated.
Each time the zero-based numeric index is passed as the {.}
context. E.g., the Dust.js snippet:
{@repeat times="4"}
{.}
{@sep}, {/sep}
{/repeat}
will resolve to:
0, 1, 2, 3
Note that the times
parameter can be a context variable or literal numeric string.
Also note that sequence-based helper tags (@even
, @odd
, @first
, @last
, @index
, @idx
, @sep
, etc.) work within the @repeat
helper in the same way that they do within the built-in {#foo}{/foo}
array-iterating blocks. The same statement should be true for any dust helper function that relies on the (standard) stack.index
and stack.of
properties of the dust context object.
This helper restores the functionality of the {@sep}
helper in the original dust.js. (This helper was subsequently removed in later linkedin-dustjs releases.)
Quoting the original dust.js documentation:
The sep tag prints the enclosed block for every value except for the last. [...]
{#names}{.}{@idx}{.}{/idx}{@sep}, {/sep}{/names}
The template above might output something like:
Moe0, Larry1, Curly2
Note that this helper will only be added if the @sep
helper does not already exist. If @sep
is already found it will be left alone.
Also see the @idx
helper.
The "substring" helper extracts a substring from the specified parameter or the tag body.
For example, given the context:
{
"foo":"The quick brown fox jumped over the lazy dogs."
}
the Dust.js snippet:
{@substring of="{foo}" from="4" to="15"/}
and the Dust.js snippet:
{@substring from="4" to="15"}{foo}{/substring}
both yield:
quick brown
The substring
helper accepts the following parameters:
-
of
- the string from which to take the substring. Any Dust.js tags within this value will be evaluated as they normally would. Whenof
is missing, the body of thesubstring
tag is used. -
from
- the (zero-based) index of the first character of the string to include in the output. This value must be an integer. When missing, defaults to0
. When negative, the characters are counted from the end of the string. That is, a value offrom="-3"
is equivalent tofrom="<string.length>-3"
. -
to
- the (zero-based) index one larger than the last character of the string to include in the output. (That is, the substring will be the range of characters at positioni
wherefrom <= i < to
.) This value must be an integer. When missing, defaults to the length of the string. When negative, the characters are counted from the end of the string. That is, a value ofto="-3"
is equivalent toto="<string.length>-3"
.
Note that from
and to
can contain Dust.js variables, which will be evaluated before the paremeter value is read.
If from
and to
are out of sequence (that is, from > to
), they will be automatically swapped.
If from
or to
contains a non-integer value, it will be ignored (as if it was not set at all).
Here are a few more examples that illustrate the logic specified above:
With "of", "from" and "to": |{@substring of="{foo}" from="0" to="5"/}|
With "of" and "to": |{@substring of="{foo}" to="5"/}|
With "of", "from" and "to": |{@substring of="{foo}" from="32" to="46"/}|
With "of" and "from": |{@substring of="{foo}" from="32"/}|
With negative "from": |{@substring of="{foo}" from="-14"/}|
With negative "from" and "to": |{@substring of="{foo}" from="-14" to="-6"/}|
With "of" and "to": |{@substring of="{foo}" to="7"/}|
With "from" and "to": |{@substring from="0" to="5"}{foo}{/substring}|
yields:
With "of", "from" and "to": |The q|
With "of" and "to": |The q|
With "of", "from" and "to": |the lazy dogs.|
With "of" and "from": |the lazy dogs.|
With negative "from": |the lazy dogs.|
With negative "from" and "to": |the lazy|
With "from" and "to": |The q|
Implements a "switch" or "case" statement, executing one of several alternatives based some value.
The value to switch on is specified in the parameter on
, which can be a dust context variable, a literal string, or a literal string containing dust context variables.
The body to execute is determined by comparing the value of on
to the name of each block. Empty strings, null
and undefined
values trigger the primary body block (the unlabeled one). Non-empty values trigger the block with the corresponding name (e.g. the {:foo}
block will be evaluated when the value of on
resolved to foo
). When no block matches the value of on
, the {:else}
block, if any, is executed.
Parameters:
on
- the value to switch on.
Example:
Context:
{ "val":"xyzzy" }Template:
{@switch on=val} {:foo}The value was foo. {:bar}The value was bar. {:xyzzy}The value was xyzzy. {:else}The value was something else. {/switch}
Output:
The value was xyzzy.
Hints:
Sometimes you need to work-around the limitations of the strings that dustjs will accept within the {:block}
tag by modifying the value you are swithcing on.
For example, dustjs will not allow a numeric block label such as {:3}
, so you must add a prefix to both the value and block labels:
{@switch on="num{val}"}
{:num1}The number was one.
{:num2}The number was two.
{:num3}The number was three.
{/switch}
Converts the tag body text to title-case, upcasing the first letter of every "word".
Parameters:
none
Example:
Context:
{ "book":"a christmas carol" }Template:
Charles Dickens wrote "{@titlecase}{book}{/titlecase}".
Output:
Charles Dickens wrote "A Christmas Carol".
The @trim
helper removes leading and trailing whitespace from its body. Any Dust.js tags within the body will be evaluated as they normally would.
For example, given the context:
{ foo: ' example ' }
The Dust.js snippet:
This is an {@trim}{~n}{~s}{foo} {/trim}.
will resolve to:
This is an example.
The @unless
helper is identical to the @if
helper with the logic cases reversed. That is, @unless
will execute the {:else}
block (if any) when the condition evaluates to true
and the body block otherwise.
See the @if
helper for more details.
Converts body text to upper case.
Parameters:
none
Example:
Context:
{ "name":"World" }Template:
{@upcase}Hello {name}.{/upcase}.
Output:
HELLO WORLD.
- {|json} - escapes content for use within a JSON string (unlike the built-in
{|js}
which filters text to a JSON string or object.) (Note that generally you'll want to use something like{foo|json|s}
to prevent Dust's standard entity-encoding, etc. from occurring.)