common-dustjs-helpers v1.5.1
Common Dust.js Helpers
About
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.
Installing
common-dustjs-helpers is packaged as an npm module under the name common-dustjs-helpers. To install the library, run:
npm install common-dustjs-helpersor add something like:
"common-dustjs-helpers": "latest"to the dependencies section of your package.json file.
Using
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.
The Helpers
@count | @deorphan | @downcase | @elements | @even | @filter | @first | @idx | @if | @index | @last | @odd | @random | @regexp | @repeat | @sep | @substring | @switch | @titlecase | @trim | @unless | @upcase
@count
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.
@deorphan
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.
@downcase
Converts body text to lower case.
Parameters:
none
Example:
Context:
{ "name":"World" }Template:
{@downcase}Hello {name}.{/downcase}.Output:
hello world.
@elements
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$idxkey- 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
truethe pairs will be sorted by key. - when a blank string (
""), the pairs will be sorted by value. - when a non-blank string other than
trueorfalseis 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, setdirequal tod,decdsc,desc, ordescendingto 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.
@even
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.
@filter
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>.
@first
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.
@idx
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.
@if
This helper conditionally executes the tag body (or an {:else} block, if any) based on a several types of predicate.
@if value=true/false
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
TorY(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"}
@if value=foo is=bar
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.
@if value=foo isnt=bar
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.
@if value=foo isnt=bar
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.
@if value=foo above=bar
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.
@if value=foo below=bar
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.
@if value=foo matches=bar
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.
@index
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.
@last
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.
@odd
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.
@random
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.
@regexp
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: httpSimilarly, 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: overThe 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,gandm. 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])/igis equivalent topattern="([aeiou])" flags="ig".var- this optional parameter specifies the context variable name used to access the numbered matches with the body of theregexptag. When omitted, the matching subexpressions will be accessed using the expression{$[n]}(wherenis the index of the subexpression, as seen above). Whenvaris specified, the value ofvaris 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 whenregexptags 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@repeat
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, Wellwhen 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, 3Note 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.
@sep
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, Curly2Note 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.
@substring
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 brownThe 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. Whenofis missing, the body of thesubstringtag 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 positioniwherefrom <= 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|@switch
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}@titlecase
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".
@trim
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.@unless
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.
@upcase
Converts body text to upper case.
Parameters:
none
Example:
Context:
{ "name":"World" }Template:
{@upcase}Hello {name}.{/upcase}.Output:
HELLO WORLD.
The Filters
- {|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.)