Skip to main content

CircleScript: Content Replacement and Concatenation

This article is a syntax reference for CircleScript's core content assembly features, including tag syntax, $using directives, local definitions, iteration, and the .cmf file format.

If you are new to CircleScript, read the CircleScript Overview first to understand the underlying concepts before working through the syntax here.

 

In this article

  • Tag syntax
  • $using directives
  • Local definitions
  • Defaults: coalescence and guard
  • Iteration with $each
  • The .cmf file format
  • Other supported data formats

 

Tag syntax

CircleScript uses [$path] syntax to reference content. The path can point to a file in the CMS or to a locally defined variable.

SyntaxWhat it doesExample
[$path]Reference a file in the CMS or a local definition[$/headers/myheader.html]
[$functionName(args)]Call a built-in CircleScript function[$upper([$first-name])]
[$$$...$$]Define a nested scope[$$$color=blue [$content] $$]

 

$using directives

The $using directive makes a CMS directory or data file available for tag resolution within the current scope. Once declared, tags are resolved against the specified path before looking elsewhere.

$using /headers
 
[$myheader.html]
 
-> contents of "/headers/myheader.html"

Multiple $using directives can be stacked. If the same key is defined in more than one file, the last $using directive takes precedence within the current scope.

$using branding.cmf       // defines colors, images, fonts
$using fall-colors.cmf   // overrides colors defined in branding

$using directives stack and can be scoped. A $using declared inside a nested scope [$$$...$$] applies only within that scope.

 

Local definitions

Local definitions create variables within the current content scope. They are available from the point of definition through the end of the current scope.

$game=chess
 
Thanks for playing [$game]!
 
-> Thanks for playing chess!

Variables defined in a nested scope do not affect the outer scope.

$color=black
 
[$$$color=blue
Color inside scope: [$color]   // blue
$$]
 
Color outside scope: [$color]   // black

 

Defaults: coalescence and guard

Coalescence

Use the ?? operator to provide a fallback value when an expression is blank or missing.

Syntax:

<expression>??<fallback>

Example:

$name=[$first-name]??friend
 
Hello [$name]!
 
-> Hello friend!   // when first-name is blank

Coalescence expressions can be chained:

$display=[$nickname]??[$first-name]??friend

 

Guard

Use the ? operator to include content only when an expression is not blank.

Syntax:

?<guard-expression>:<conditional-content>

Example:

$level_message=?[-level-]:You are at level [-level-]!
 
[$level_message]
 
-> You are at level 7!   // when level = 7
-> (blank)               // when level is blank

 

AND and OR logic

Guard and coalesce syntax can be combined to implement simple boolean logic. Blank represents false; any non-blank value represents true.

AND example:

$p1=true
$p2=true
$p3=
 
$p1and2=?[$p1]:?[$p2]:yes??no
$p2and3=?[$p2]:?[$p3]:yes??no
 
1 and 2 : [$p1and2]   // yes
2 and 3 : [$p2and3]   // no

OR example:

$p1=true
$p2=
 
$p1or2=?[$p1][$p2]:yes??no
 
1 or 2 : [$p1or2]   // yes

 

Iteration with $each

The $each directive repeats a block of content for each item in a range or sequence. It stops automatically once its block produces no output (short-circuit evaluation).

 

Basic iteration

$each i:[1..3]
Repeat me!
 
-> Repeat me!Repeat me!Repeat me!

 

Iterator in path

$each i:[1..3]
[$content_[$i]]
 
-> [$content_1][$content_2][$content_3]

 

Implicit path iteration (shorthand)

[$content_*] is shorthand for [$content_[$i]] when used inside a $each directive.

$each content_[1..3]
[$content_*]
 
-> [$content_1][$content_2][$content_3]

 

Qualifying the each block

Use a guard expression to skip blank items and stop iteration cleanly.

$each content_[1..100]
?[$content_*]:<p>[$content_*]</p>
 
-> <p>[$content_1]</p><p>[$content_2]</p>... stops when [$content_N] is blank

 

Iteration in a sub-context

To loop through a block in the middle of other content, place the repeated block in a nested scope with a $each directive on the first line.

<table>
[$$$each row:[1..100]
?[$c[$row]_1]:<tr>[$$$each col:[1..100]
?[$c[$row]_[$col]]:<td>[$c[$row]_[$col]]</td>$$]</tr>
$$]</table>
 
-> <table>
   <tr><td>[$c1_1]</td><td>[$c1_2]</td>...</tr>
   <tr><td>[$c2_1]</td><td>[$c2_2]</td>...</tr>
   ...</table>

 

The .cmf file format

The .cmf (Content Manager Format) file is the primary format for storing key-value definitions used in CircleScript content assembly. It supports both short single-line definitions and multi-line HTML blocks.

 

Short definitions

Each definition starts in the first column with an identifier followed by an equals sign, with the value on the same line.

first-name=Sarah
product=Premium Membership
promo-code=SAVE20

 

Multi-line HTML blocks

When the equals sign is followed by a newline with no value on the same line, the value is pulled from the following lines until the next definition or the end of the file.

hero-banner=
<div class='hero'>
  <h1>Welcome back, [$first-name]!</h1>
  <p>Your exclusive offer: [$promo-code]</p>
</div>
 
footer-text=
<p>Unsubscribe at any time.</p>

 

IMPORTANT  If an attribute name or HTML element starts in the first column of a .cmf file, it will be interpreted as a new key definition rather than content. Indent HTML element attributes to avoid this.

 

Using a .cmf file in content

$using /data/mydata   // references /data/mydata.cmf
 
[$hero-banner]
[$footer-text]

 

Other supported data formats

CircleScript can also parse .csv, .tsv, .json, and .xml files into name-value pairs for use in content assembly.

 

.csv and .tsv

Tabular data is converted to name-value pairs using column headers. Columns that represent unique keys must be annotated with (key) in the header row.

// Header row:
event_id(key), title, description, location, date
 
// First data row:
E1, First Event, The first event, Boise Idaho, 2023-10-01
 
// Produces:
E1.event_id=E1
E1.title=First Event
E1.description=The first event
E1.location=Boise Idaho
E1.date=2023-10-01

 

.json

JSON objects and arrays are traversed and converted to name-value pairs by building up keys as the structure is parsed.

// Input: {a:1, b:2, c:3}
// Produces: a=1, b=2, c=3
 
// Input: {id: [1,2,3]}
// Produces: id1=1, id2=2, id3=3

 

.xml

XML is parsed into name-value pairs. Both element content and nested structures are accessible.

// Input:
<p><div><tt>foo</tt></div></p>
 
// Produces:
p1 = <p><div><tt>foo</tt></div></p>
p1.div1 = <div><tt>foo</tt></div>
p1.div1.inner = <tt>foo</tt>
p1.div1.text = foo
p1.div1.tt1 = <tt>foo</tt>
p1.div1.tt1.inner = foo
p1.div1.tt1.text = foo

 


 

 

Powered by Zendesk