# Advanced

The following documentation goes into more details on how to use the personalization and templating engine.

You will learn about builtin filters, how to control whitespace if you include `if`/`else` statements, what kind of comparison you can use and more.

## Filters

### Sidenote - filters with arguments

You may have noticed some filters take arguments. When this is the case, there is two way to give the arguments:

* Using a named argument as such: `join(separator=',')`
* Directly giving the value as such: `join(',')`

In the following reference we will denote arguments that can be given unnamed as such: `argName?: type` where the `?` indicates the argument is optional.

### General filters

<details>

<summary>formatDate</summary>

**Input type**\
date

**Arguments**

* `pattern`: string or (`dateStyle`: string, `timeStyle`: string)

**Optional arguments**

* `timezone`: string, `locale`: string

**Return type**\
string

**Description**\
Formats a date using either the pattern provided or the combined `dateStyle`/`timeStyle`.\
Ex: `{{ installation_date|formatDate(pattern: 'yyyy-MM-dd') }}` will result in **2025-01-01**

Ex: `{{ installation_date|formatDate(dateStyle: 'LONG', timeStyle: 'SHORT') }}` will result in **Wednesday, January 1, 2025 12:00 AM**

The `timezone` argument allows you to format the date into a specific timezone.\
Ex: `{{ installation_date|formatDate(dateStyle: 'SHORT', timeStyle: 'LONG', timezone: 'Europe/Paris') }}` will result in **1/1/25 9:00:00 AM CET**

The `locale` argument allows you to format the date using a specific locale. A locale controls region-specific behavior when formatting a date. For example, with the US locale, the time will be suffixed with **AM** or **PM**, but not with the UK locale.\
Ex: `{{ installation_date|formatDate(dateStyle: 'SHORT', timeStyle: 'LONG', locale: 'UK') }}` will result in **01/01/25 09:00:00 CET**

</details>

### Filters for numbers

<details>

<summary>abs</summary>

**Input type**\
number or duration

**Return type**\
int

**Description**\
Returns the absolute value of a number, duration, or distance.

**Example**\
Ex: `{{ -13|abs }}` will result in **13**

</details>

<details>

<summary>round</summary>

**Input type**\
number or duration or distance

**Return type**\
int

**Description**\
Returns the closest `int` of a number, duration, or distance, rounding up.

**Examples**\
Ex: `{{ 46.8|round }}` will result in **47**\
Ex: `{{ 46.3|round }}` will result in **46**

</details>

<details>

<summary>ceil</summary>

**Input type**\
number or duration or distance

**Return type**\
int

**Description**\
Returns the closest `int` that is greater than the number, duration, or distance.

**Example**\
Ex: `{{ 46.2|ceil }}` will result in **47**

</details>

<details>

<summary>floor</summary>

**Input type**\
number or duration or distance

**Return type**\
int

**Description**\
Returns the closest `int` that is lower than the number, duration, or distance.

**Example**\
Ex: `{{ 46.8|floor }}` will result in **46**

</details>

<details>

<summary>formatNumber</summary>

**Input type**\
number

**Arguments**\
\&#xNAN;*none*

**Optional arguments**

* `decimals`: number
* `locale`: string

**Return type**\
string

**Description**\
Formats a number.

For a `weight` float attribute of value `26.5` and a user using a US locale:\
Ex: `{{ weight|formatNumber }}` will result in **26.5**

The `decimals` argument allows you to control how many decimals should be printed:\
Ex: `{{ weight|formatNumber(decimals: 2) }}` will result in **26.50**

The `locale` argument allows you to format the number using a specific locale. A locale controls region-specific behavior when formatting numbers, such as setting the appropriate decimal separator. By default, the user's locale will be used.\
Ex: `{{ weight|formatNumber(locale: 'fr', decimals: 2) }}` will result in **26,50**

</details>

<details>

<summary>formatCurrency</summary>

**Input type**\
number

**Arguments**\
\&#xNAN;*none*

**Optional arguments**

* `decimals`: number
* `locale`: string
* `symbol`: string

**Return type**\
string

**Description**\
Formats a number as currency.

For a `price` float attribute of value `2406.5` and a user using a US locale:\
Ex: `{{ price|formatCurrency }}` will result in **¤ 2,406.50**

The `symbol` argument controls the currency symbol:\
Ex: `{{ price|formatCurrency(symbol: '$') }}` will result in **$ 2,406.50**

The `decimals` argument allows you to control how many decimals should be printed:\
Ex: `{{ price|formatCurrency(symbol: '$', decimals: 3) }}` will result in **$ 2,406.500**

The `locale` argument allows you to format the number using a specific locale. A locale controls region-specific behavior when formatting numbers, such as setting the appropriate decimal separator.

*Note: The locale has no impact on the currency symbol. By default, the user's locale will be used.*

Ex: `{{ price|formatCurrency(symbol: '$', locale: 'fr') }}` will result in **2 406,50 $**\
Ex: `{{ price|formatCurrency(symbol: '€', locale: 'fr') }}` will result in **2 406,50 €**

</details>

### Filters for strings only

<details>

<summary>lower</summary>

**Input type**\
string

**Return type**\
string

**Description**\
Converts a string to lowercase.

**Example**\
Ex: `{{ VINCENT|lower }}` will result in **vincent**

</details>

<details>

<summary>upper</summary>

**Input type**\
string

**Return type**\
string

**Description**\
Converts a string to uppercase.

**Example**\
Ex: `{{ vincent|upper }}` will result in **VINCENT**

</details>

<details>

<summary>capitalize</summary>

**Input type**\
string

**Return type**\
string

**Description**\
Converts the first letter to uppercase and all other letters to lowercase.

**Example**\
Ex: `{{ "john Smith"|capitalize }}` will result in **John smith**

</details>

<details>

<summary>title</summary>

**Input type**\
string

**Return type**\
string

**Description**\
Converts the first letter of each word to uppercase and all other letters to lowercase.

**Example**\
Ex: `{{ "johN smith"|title }}` will result in **John Smith**

</details>

<details>

<summary>append</summary>

**Input type**\
string

**Arguments**

* `text?`: string

**Return type**\
string

**Description**\
Appends the text to the value.

**Example**\
Ex: `{{ john|append(' smith') }}` will result in **john smith**

</details>

<details>

<summary>prepend</summary>

**Input type**\
string

**Arguments**

* `text?`: string

**Return type**\
string

**Description**\
Prepends the text to the value.

**Example**\
Ex: `{{ john|prepend('smith ') }}` will result in **smith john**

</details>

### Filters for tag collections only

<details>

<summary>join</summary>

**Input type**\
tag collection

**Arguments**

* `separator?`: string

**Return type**\
string

**Description**\
Returns a string which is the concatenation of all tag values separated by the provided separator.

**Example**\
Ex: `{{ c.interests|join(' ') }}` will result in **sports politics music**\
(for someone with `["sports", "politics", "music"]` in their `c.interests` tag collection)

</details>

<details>

<summary>first</summary>

**Input type**\
tag collection

**Return type**\
string

**Description**\
Returns the first tag value.

**Example**\
Ex: `{{ c.interests|first }}` will result in **sports**\
(for someone with `["sports", "politics", "music"]` in their `c.interests` tag collection)

</details>

<details>

<summary>last</summary>

**Input type**\
tag collection

**Return type**\
string

**Description**\
Returns the last tag value.

**Example**\
Ex: `{{ c.interests|last }}` will result in **music**\
(for someone with `["sports", "politics", "music"]` in their `c.interests` tag collection)

</details>

<details>

<summary>contains</summary>

**Input type**\
tag collection

**Arguments**

* `element?`: string

**Return type**\
boolean

**Description**\
Returns `true` if the tag collection contains the given argument.

**Example**\
Ex: `{{ c.interests|contains('politics') }}` will result in **true**\
(for someone with `["sports", "politics", "music"]` in their `c.interests` tag collection)

</details>

### Chaining filters

It is possible to chain filters to produce more complex results, however you need to make sure the input and output types are compatible between filters.

For example, you can't call `formatDate` on a number or even a string, the type has to be a date. Look at the table above to know what filters are compatible.

Here is a valid example of chaining multiple filters:

```
Your next exam is on {{ t.exams|last|date|formatDate('yyyy-MM-dd') }}
```

Given a user with this tag collection `t.exams`: `["2017-10-09T14:53:54Z", "2017-10-11T16:53:54Z"]` the example evalutes to:

```
Your next exam is on 2017-10-11
```

As you can see we take the `last` value of the tag collection which returns a `string`, then pass that to `date` which parses it and returns a `date` type. Finally we pass that to `formatDate`.

## Expression

An expression is something that returns any kind of value. It can a math operation, a comparison, a function call, a reference to an attribute or tag or even a literal value.

Examples:

```
<div data-gb-custom-block data-tag="set"></div>

```

Expression are used in:

* `if`/`else if` conditions
* variable assignment
* expression output `{{ ... }}`

## Comparison

### Type comparison rules

When comparing two values, they have to be of the same type otherwise it won't work.

The rules are as follows:

* A `string` can only be compared with another `string`
* A `date` can only be compared with another `date`
* A `duration` can be compared with another `duration` or a `number`. When comparing with a `number` it is treated as days; in other words `{{ $myDuration == 3 }}` is equivalent to `{{ $myDuration == 3d }}`.
* A `distance` can be compared with another `distance` or a `number`. When comparing with a `number` it is treated as meters; in other words `{{ $myDistance == 200 }}` is equivalent to `{{ $myDistance == 200m }}`
* A `number` can be compared with another `number` or a `boolean`.

### Operators

* `==` compares two values for equality
* `!=` compares two values for inequality
* `>` returns true if the left hand side is greater than the right hand side
* `>=` returns true if the left hand side is greater than or equal to the right hand side
* `<` returns true if the left hand side is lower than the right hand side
* `<=` returns true if the left hand side is lower than or equal to the right hand side

### Combining comparisons

As in any programming languages, you can combine comparisons easily:

* `and` returns true if both the left hand side and the right and side are true
* `or` returns true if either the left hand side is true or the right hand side is true
* `not` negates a statement
* `(` and `)` to group expressions.

## Data types

### Standard

Standard types include:

* *string*. You can write a literal string by using a `'` character like this: `'This is a string'`. To use a `'` inside your string you need to double it like this: `'It''s great'`
* *integer*. You can write a literal integer like this: `230`.
* *float*. You can write a literal float like this: `20.30`
* *boolean*. A boolean is either `true` or `false`

### Date

Date is a special type that can't be created by a literal. However they are produced in a couple of cases:

* if an attribute is a date (that is either a `NSDate` or a `java.util.Date` for iOS and Android respectively).
* by converting a UNIX timestamp (number of seconds since January 1, 1970): `{{ 1491814800|date|formatDate('yyyy-MM') }}`
* by using the keyword `now` which returns the date at the time of execution

### Duration

Duration is a special integer with a time unit. Units can be:

* days: `40d`
* hours: `24h`
* minutes: `30m`
* seconds: `46s`

### Distance

Distance is a special integer with a distance unit. It is also always positive. Units can be:

* meters: `5600m`
* kilometers: `83km`

### Casting rules

You can cast values into different types by using a *casting operation*, provided the types are compatible. The casting operation looks like this:

```
{{ c.my_string_attribute|float|int }}
```

Casting looks exactly like a *filter* but it simply converts the original value to the type if the rules allow it.

The rules of casting are as follows:

| from / to | string | int | float | bool | date | distance | duration |
| --------- | ------ | --- | ----- | ---- | ---- | -------- | -------- |
| string    |        | ✓   | ✓     | ✓    | ✓1   | ✓        | ✓        |
| int       | ✓      |     | ✓     | ✓    | ✓2   | ✓        | ✓        |
| float     | ✓      | ✓   |       | ✓    | X    | ✓        | ✓        |
| bool      | ✓      | ✓   | ✓     |      | X    | X        | X        |
| date      | ✓      | ✓2  | X     | X    |      | X        | X        |
| distance  | ✓3     | ✓   | ✓     | ✓    | X    |          | X        |
| duration  | ✓4     | ✓   | ✓     | ✓    | X    | X        |          |

**1** The string has to follow the pattern `yyyy-MM-dd'T'HH:mm:ss` or the resulting date will be empty.

**2** Casting from `date` to `int` will return the UNIX timestamp in seconds. Casting from `int` to `date` will treat the integer as a UNIX timestamp in seconds.

**3**

Rules:

* casting from `string` to `distance` will treat the string as an integer representing the distance in meters.
* casting from `int` to `distance` will treat the integer as the distance in meters.
* casting from `float` to `distance` will remove the decimal part and treat it as an integer representing the distance in meters.
* casting from `boolean` to `distance` will treat `false` as 0 and `true` as 1.

You can pass a conversion distance unit as a parameter to the `distance` filter.

Valid distance units are detailed above.

Examples:

* `{{ '100'|distance }}` will result into the distance `100m`
* `{{ '12km'|distance('m') }}` will result into the distance `12000m`
* `{{ 2000|distance('km') }}` will result into the distance `2000km`
* `{{ 43.20440|distance }}` will result into the distance `43m`
* `{{ true|distance('km') }}` will result into the distance `1km` (not that you'd ever do that)

**4**

Rules:

* casting from `string` to `duration` will treat the string as an integer representing the duration in days.
* casting from `int` to `duration` will treat the integer as the duration in days.
* casting from `float` to `duration` will remove the decimal part and treat it as an integer representing the duration in days.
* casting from `boolean` to `duration` will treat `false` as 0 and `true` as 1.

You can pass a conversion duration unit as a parameter to the `duration` filter.

Valid duration units are detailed above.

Examples:

* `{{ '100'|duration }}` will result into the duration `100d`
* `{{ '100h'|duration }}` will result into the duration `100h`
* `{{ '48h'|duration('d') }}` will result into the duration `2d`
* `{{ 405|duration() }}` will result into the duration `405d`
* `{{ 43.409|duration('m') }}` will result into the duration `43m`
* `{{ true|duration('s') }}` will result into the duration `1s`

### Math rules

Math operations on `integers` and `float`s work as you would expect:

```
{{ (10 + 2) / 2 - (5 * 20) }}
```

Will evaluate to:

```
-94
```

There are a couple of rules for operations on `date`s, `duration`s and `distance`s:

* adding or subtracting a `duration` from a `date` will return a new `date`.
* subtracting two `date`s will return a `duration`.
* all operations between a `duration` and a `number` are allowed and will return a `duration` in the original unit.
* all operations between a `distance` and a `number` are allowed and will return a `distance` in the original unit.

Some *unusual* operators are available for your convenience:

* `//` divides two numbers and returns the truncated integer result. Example: `{{ 20 // 7 }}` evaluates to `2`.
* `%` returns the remainder of the integer division of two numbers. Example: `{{ 11 % 7 }}` evaluates to `4`.
* `**` raises the left hand side to the power of the right hand size. Example: `{{ 2 ** 3 }}` evaluates to `8`.

## Whitespace control

Up until now we haven't really talked about controlling how whitespaces are included - or not - in the message after the template has been rendered.

By whitespace we mean new line characters and leading spaces before either an expression or a statement.

Having control on this behaviour is important so that you can better structure your template and not have to write them all on the same line to avoid having newlines in your output.

### Default rules

The default rules are as follows:

* Expressions (`{{ ... }}`) strips the leading whitespaces if the output of the expression is empty

Example:

```
Hello {{ c.first_name }}!
```

With a `c.first_name` attribute defined, this evaluates to:

```
Hello Vincent!
```

Note the space after `Hello` is kept.

With no `c.first_name` attribute defined, it evaluates to:

```
Hello!
```

Note that there is only one space: the space after `Hello` has been removed

* Statements (`{% ... %}`) always strips the trailing newlines

Example:

```
<div data-gb-custom-block data-tag="set" data-0='hh' data-1='hh' data-2='hh' data-3='hh' data-4='hh' data-5='hh'></div>

```

```
Good 
```

!

This evaluates to:

```
Good morning!
```

Depending on the current time it will change to `afternoon` or `evening`.

### Forced behaviour

If the default behaviour doesn't suit you, you can force the behaviour with the following syntax:

* `{{+ +}}` or `{%+ +%}` will force every whitespace to be kept
* `{{- -}}` or `<div data-gb-custom-block data-tag="-"></div>` will force every whitespace to be stripped

You can mix and match of course: `{{+ ... -}}` is completely valid.

Example:

```
Hello {{- c.first_name }}!
```

Now that we forcefully remove the leading whitespace, with a `c.first_name` defined it evaluates to:

```
HelloVincent!
```

Another example:

```
Hello {{+ c.first_name }}!
```

Here we forcefully keep the leading whitespace, with no `c.first_name` defined it evaluates to:

```
Hello !
```

Note the space is kept.

## Custom Audience Data

The Custom Audience API (v1.1) supports attaching attributes and tags to installation IDs.

When a campaign targets such an audience, they can be used in your message by using the `{{ customAudienceAttribute(<audience name>, <attribute name>) }}` expression.

#### Example

Take the following Custom Audience API call, ran on the `SAMPLE-LEVELUP` audience:

```json
{
  "ids": [
    {
      "action": "add",
      "id": "INSTALL-ID-1",
      "attributes": {
        "account_level": 20
      }
    }
  ]
}
```

The following message:

```
Congratulations, you have reached level {{ customAudienceAttribute('SAMPLE-LEVELUP', 'account_level') }}!
```

will result in:

```
Congratulations, you have reached level 20!
```

## Audience Data

The Audience API supports attaching attributes and tags to profiles.

When a campaign or automation targets such an audience, they can be used in your message by using the `{{ audienceAttribute(<audience name>, <attribute name>) }}` expression.

#### Example

Take the following Audience API Update call:

```json
{
  "name": "EXAMPLE",
  "ids": [
    {
      "action": "add",
      "id": "CUSTOM-ID-1",
      "attributes": {
        "account_level": 20
      }
    }
  ]
}
```

The following message:

```
Congratulations, you have reached level {{ audienceAttribute('EXAMPLE', 'account_level') }}!
```

will result in:

```
Congratulations, you have reached level 20!
```

\
var onTitleClick = function(event) {\
&#x20;   var methodName = event.target.dataset.method;\
&#x20;   document.querySelector(".method-details\[data-method='" + methodName + "']")\
&#x20;       .classList.toggle("method-shown");\
}\
\
var titles = \[].slice.call(document.getElementsByClassName("method-title"));\
\
for (var i = 0; i < titles.length; i++) {\
&#x20;   titles\[i].onclick = onTitleClick;\
}<br>
