# Markup.js **Repository Path**: mirrors_Jam3/Markup.js ## Basic Information - **Project Name**: Markup.js - **Description**: Powerful JavaScript templates - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-11-23 - **Last Updated**: 2026-03-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Markup.js — Powerful JavaScript templates Markup.js is a simple yet surprisingly powerful template system for JavaScript. ## Why Markup.js? Markup.js takes the pain out of converting structured data into HTML markup or other text formats. Its intuitive syntax and small footprint (only 1.9KB minified and gzipped) make it the perfect choice for your JavaScript app. Plus there are *no dependencies.* ## Usage Include ``. Markup.js has a single function: `Mark.up(template, context)`. Here's a basic example that shows how `template`, a string, is injected with properties of `context`, an object: ``` javascript var context = { name: { first: "John", last: "Doe" } }; var template = "Hi, {{name.first}}!"; var result = Mark.up(template, context); // "Hi, John!" ``` You can format any kind of objects, including functions with exposed properties: ``` javascript var context = { person: new Person("Adam") }; var template = "Hi, {{person.name}}!"; var result = Mark.up(template, context); // "Hi, Adam!" ``` ## Object notation You can access object properties with simple dot notation: ``` javascript var context = { name: "John Doe", addr: { street: "1 Maple Street", city: "Pleasantville", zip: { main: "12345", ext: "6789" } } }; var template = "{{name}} lives at {{addr.street}} in {{addr.city}}."; var result = Mark.up(template, context); // "John Doe lives at 1 Maple Street in Pleasantville." ``` Or you can use nested tags: ``` javascript var template = "{{name}} lives at {{addr}}{{street}} in {{city}}.{{/addr}}"; var result = Mark.up(template, context); // "John Doe lives at 1 Maple Street in Pleasantville." ``` Or you can use a combination of nested tags and dot notation: ``` javascript var template = "ZIP: {{addr}}{{zip.main}}-{{zip.ext}}{{/addr}}"; var result = Mark.up(template, context); // "ZIP: 12345-6789" ``` ## Array notation Array members can be accessed by index. For example: ``` javascript var context = { name: "John Doe", colors: ["Red", "Blue", "Green"] }; var template = "Favorite color: {{colors.0}}"; var result = Mark.up(template, context); // "Favorite color: Red" ``` You can mix array index notation and object property notation in the same expression: ``` javascript var context = { name: "John Doe", friends: [{name: "Bob"}, {name: "Fred"}] }; var template = "Best friend: {{friends.0.name}}"; var result = Mark.up(template, context); // "Best friend: Bob" ``` ## Loops If a tag resolves to an array, the array is iterated. A single dot refers to the current iteration context: ``` javascript var context = { name: "John Doe", brothers: ["Jack", "Joe", "Jim"] }; var template = ""; var result = Mark.up(template, context); // "" ``` ``` javascript var context = { user: { contacts: ["John", "Jane"] } }; var template = ""; var result = Mark.up(template, context); // "" ``` When looping through an array of objects, object properties can be referenced by name: ``` javascript var context = { name: "John Doe", sisters: [{name: "Jill"}, {name: "Jen"}] }; var template = ""; var result = Mark.up(template, context); // "" ``` Dot notation works inside loops as well: ``` javascript var context = { sisters: [ {name: {first: "Jill", last: "Doe"}}, {name: {first: "Jen", last: "Doe"}} ] }; var template = ""; var result = Mark.up(template, context); // "" ``` ### Loop counters Inside a loop, a single hash sign refers to the current iteration index (0...n-1) and a double hash sign refers to the current iteration count (1...n): ``` javascript var template = "{{sisters}} {{#}}-{{name.first}} {{/sisters}}"; // " 0-Jill 1-Jen " ``` ``` javascript var template = "{{sisters}} {{##}}-{{name.first}} {{/sisters}}"; // " 1-Jill 2-Jen " ``` This is useful for applying conditional formatting, as described below, and for creating numbered lists. ## Pipes Pipes are a powerful way to transform variables. Here's a simple example: ``` javascript var context = { name: "John Doe", alias: " J-Do ", phone: null, gender: "male", age: 33.33, vitals: [68, 162.5, "AB"], brothers: ["Jack", "Joe", "Jim"], sisters: [{name: "Jill"}, {name: "Jen"}], jiggy: true }; var template = "Name: {{name|upcase}}"; var result = Mark.up(template, context); // "Name: JOHN DOE" ``` A pipe can accept arguments. For example, the `blank` pipe accepts a value to display if the piped input is null or empty: ``` javascript var template = "Phone: {{phone|blank>N/A}}"; var result = Mark.up(template, context); // "Phone: N/A" ``` The `choose` pipe accepts two strings and returns one of them depending on whether the piped input is true or false: ``` javascript var template = "John is jiggy: {{jiggy|choose>Yes>No}}"; var result = Mark.up(template, context); // "John is jiggy: Yes" ``` Pipes can be applied to any kind of data structure: ``` javascript // get the second value in an array and round it var template = "Weight: {{vitals.1|round}} lbs."; var result = Mark.up(template, context); // "Weight: 163 lbs." ``` ``` javascript // sort an array of strings, then upcase each string var template = ""; var result = Mark.up(template, context); // "" ``` ``` javascript // reverse an array of objects, then chop each name property var template = ""; var result = Mark.up(template, context); // "" ``` ### Chaining pipes Variables can be passed through multiple pipes. Here are two simple examples: ``` javascript var template = "Alias: {{alias|trim|downcase}}"; var result = Mark.up(template, context); // "Alias: j-do" ``` ``` javascript var template = "Age: {{age|more>75|choose>Oldish>Youngish}}"; var result = Mark.up(template, context); // "Age: Youngish" ``` You can get very creative with pipes: ``` javascript var template = "Bros: {{brothers|sort|limit>2|join> & }}"; var result = Mark.up(template, context); // "Bros: Jack & Jim" ``` ### Built-in pipes Markup.js comes with more than 40 built-in pipes: `empty` (obj): Test for an empty array, empty string, null, undefined, or 0. `{{if apples|empty}}` `notempty` (obj): Test for the presence of a value. `{{if apples|notempty}}` or simply `{{if apples}}` `more` (obj, n): Test if a number, [iterator][1], or array is greater than n. `{{if articles|more>100}}` `{{if #|more>10}}` `less` (obj, n): Test if a number, [iterator][1], or array is less than n. `{{if age|less>21}}` `ormore` (obj, n): Test if a number, [iterator][1], or array is greater than or equal to n. `{{if age|ormore>18}}` `orless` (obj, n): Test if a number, [iterator][1], or array is less than or equal to n. `{{if age|orless>55}}` `between` (obj, n1, n2): Test if a number, [iterator][1] or array is between n1 and n2, inclusive. `{{if age|between>18>35}}` `equals` (obj, str): Test for equality (==). `{{if name|equals>Adam}}` `{{if age|equals>35}}` `notequals` (obj, str): Test for inequality (!=). `{{if name|notequals>Adam}}` `like` (str, str): Test for a pattern match (case-insensitive). `{{if name|like>Adam}}` `{{if name|like>a.*}}` `notlike` (str, str): Test for a non-match (case-insensitive). `{{if name|notlike>Adam}}` `blank` (str, str): Display a default value for a null or empty string. `{{title|blank>Untitled}}` `upcase` (str): Upper-case a string. `{{name|upcase}}` `downcase` (str): Lower-case a string. `{{name|downcase}}` `capcase` (str): Capitalize the first letter in each word. `{{title|capcase}}` `chop` (str, n): Chop a string to n chars followed by "..." if n < string length. `{{description|chop>100}}` `tease` (str, n): Chop a string to n words followed by "..." if n < word count. `{{summary|tease>15}}` `trim` (str): Trim leading and trailing white space from a string. `{{article|trim}}` `pack` (str): Trim and normalize white space in a string. `{{article|pack}}` `round` (num): Round a number. `{{age|round}}` `clean` (str): Strip HTML/XML tags from a string. `{{article|clean}}` `length` (obj): Get the length of an array, string, or [iterator][1]. `{{apples|length}}` `{{#|length}}` `size` (obj): Alias of length. `{{apples|size}}` `{{#|size}}` `reverse` (arr): Reverse an array.\* `{{articles|reverse}} ... {{/articles}}` `join` (arr [, str]): Join an array with "," or with the given token. `{{names|join> + }}` `limit` (arr, n1 [, n2]): Limit an array to n1 items beginning at index n2 (or 0). `{{contacts|limit>10}} ... {{/contacts}}` `split` (str [, str]): Split a string on "," or by the given token. `{{names|split>;}} {{.}} {{/names}}` `choose` (bool, str [, str]): Output one value if truthy, another if falsy. `{{user.passed|choose>Pass>Fail}}` `toggle` (obj, str, str [,str]): Switch one string value for another. `{{gender|toggle>M,F>Boy,Girl>N/A}}` `sort` (arr [, str]): Sort an array, optionally by object property name.\* `{{users|sort>firstname}} ... {{/users}}` `fix` (num, n): Format a number to n decimal places. `{{weight|fix>1}}` `mod` (num, n): Get the remainder of a number or [iterator][1] divided by n. `{{rows|mod>10}}` `divisible` (num, n): Test if a number or [iterator][1] is perfectly divisible by n. `{{if #|divisible>3}}` `even` (num): Test if a number or [iterator][1] is even. `{{if #|even}}` `odd` (num): Test if a number or [iterator][1] is odd. `{{if #|odd}}` `number` (str): Extract a number from a string (e.g. "$1,234.56" or "30px"). `{{price|number}}` `url` (str): URL-encode a string. `{{article.link|url}}` `bool` (obj): Cast an object to a boolean value. `{{user.geo_pref_flag|bool}}` `falsy` (obj): Test for falseness. `{{if expired|falsy}}` `first` (iterator): Test if an [iterator][1] is first. `{{if #|first}}` `last` (iterator): Test if an [iterator][1] is last. `{{if #|last}}` `call` (obj, func [, arg1, arg2, ...]): Call an object function. ([See doc below](#the-call-pipe)) `{{doggy|call>bark>5}}` `set` (obj, str): Set a variable for later use, outputting nothing. ([See doc below](#the-set-pipe)) `{{user.birthday|set>bday}}` `log` (obj): Log any variable to the console. ([See doc below](#logging)) `{{article.title|log}}` \* Source array is not modified. ### The 'call' pipe The `call` pipe lets you call a function on any object and pass it zero or more arguments: ``` javascript var context = { num: 1.23 }; var template = "{{num|call>toPrecision>5}}"; var result = Mark.up(template, context); // "1.2300" ``` ``` javascript function Dog() { var greeting = "Woof!"; this.bark = function (times) { ... }; } var context = { doggy: new Dog() }; var template = "{{doggy|call>bark>3}}"; var result = Mark.up(template, context); // "Woof! Woof! Woof!" ``` ### Writing custom pipes You can add your own pipes to Markup.js. A pipe is simply a function with one or more arguments. The first argument (required) is the piped value itself. Any additional arguments are strings. For example: ``` javascript Mark.pipes.shout = function (str, n) { return str + new Array(parseInt(n || 1) + 1).join("!"); }; var template = "{{exclamation|shout>5}}"; var result = Mark.up(template, { exclamation: "Bonsai" }); // "Bonsai!!!!!" ``` If you prefer, you can pass pipes into the optional `options` argument of `Mark.up`: ``` javascript var options = { pipes: { mypipe: function (str) { ... } } }; var result = Mark.up(template, context, options); ``` Note! All _optional_ pipe arguments are passed as strings. For example, in the expression `{{num|add>23>45}}`, "23" and "45" are strings. Therefore, you should cast data types as necessary in your custom pipes: ``` javascript // WRONG! 1 + "23" + "45" returns "12345" Mark.pipes.add = function (a, b, c) { return a + b + c; }; // RIGHT! 1 + "23" + "45" returns 69 Mark.pipes.add = function (a, b, c) { return a + parseInt(b) + parseInt(c); }; ``` ### Changing the argument delimiter You can change the argument delimiter from ">" to a character (or characters) of your choosing: ``` javascript Mark.delimiter = ":"; ``` The delimiter can also be set in the optional `options` argument of `Mark.up`: ``` javascript var options = { delimiter: ":" }; var result = Mark.up(template, context, options); ``` ### More pipes! Additional pipes are available in `src/extras` for your piping pleasure. (These are not included in markup.js.) ## IF and IF/ELSE statements IF statements are formatted as `{{if expression}} ... {{/if}}`, where *expression* is a boolean test with optional pipes: ``` javascript var template = "{{if brothers}} John has {{brothers|size}} brothers! {{/if}}" ``` ``` javascript var template = "{{if children|empty}} John has no kids. {{/if}}" ``` ``` javascript var template = "{{if age|more>75}} John is a ripe old {{age|round}}! {{/if}}" ``` ``` javascript var template = "{{if age|between>50>75}} John is middle aged. {{/if}}" ``` IF/ELSE statements work as you would expect: ``` javascript var template = "{{if speed|more>65}} Too fast! {{else}} Too slow! {{/if}}" ``` Pipes can be chained in IF statements, allowing for arbitrarily complex expressions: ``` javascript // test if weight in kgs is greater than 500 var template = "{{if weight|kgs|more>500}} Lighten up! {{/if}}" ``` IF and IF/ELSE statements work the same way inside loops: ``` javascript // show only users with email addresses var template = "{{users}} {{if email}} ... {{/if}} {{/users}}"; ``` IF and IF/ELSE statements can be nested: ``` javascript var template = "{{if age|more>100}} {{if gender|equals>male}} Old man! {{/if}} {{/if}}"; ``` ``` javascript var template = "{{if ...}} ... {{else}} {{if ...}} ... {{else}} ... {{/if}} {{/if}}"; ``` ### Testing loop counters You can use loop counters (# and ##) to apply conditional formatting: ``` javascript // show different content in even and odd rows var template = "{{users}} {{if #|even}} ... {{else}} ... {{/if}} {{/users}}"; ``` ``` javascript // print a table header every five rows starting at zero var template = "{{users}} {{if #|divisible>5}} ... {{/if}} ... {{/users}}"; ``` ``` javascript // print a table header every three rows after the tenth row var template = "{{users}} {{if ##|more>10|divisible>3}} ... {{/if}} ... {{/users}}"; ``` Certain pipes can be used to evaluate the position of the current iteration context or the size of the array itself. In these cases, # and ## are interchangeable: ``` javascript // do something on the first iteration var template = "{{users}} {{if #|first}} ... {{/if}} ... {{/users}}"; ``` ``` javascript // do something on the last iteration var template = "{{users}} {{if #|last}} ... {{/if}} ... {{/users}}"; ``` ``` javascript // test the size of the array during the loop var template = "{{users}} {{if #|size|more>100}} ... {{/if}} ... {{/users}}"; ``` ### Pipes in conditional expressions Boolean pipes, such as `between` or `more`, return the *inputted value* if the expression is true. Otherwise they return *false*. This way, pipes can be chained together to form complex AND statements. For example: ``` javascript // a custom pipe Mark.pipes.big = function (num) { return num > 1000000 ? num : false; }; var context = { salary: 5000000 }; var template = "{{if salary|big|even}} A nice round number! {{/if}}"; ``` In the above example, `salary|big|even` returns *5000000*, which resolves to *true*. *You should follow this convention if you write boolean pipes.* ## Includes You can include templates inside other templates. For example: ``` javascript Mark.includes.greeting = "My name is {{name|upcase}}!"; var template = "Hello! {{greeting}}"; var result = Mark.up(template, context); // "Hello! My name is JOHN DOE!" ``` You can even pipe the output of an included template: ``` javascript var template = "Hello! {{greeting|upcase}}"; var result = Mark.up(template, context); // "Hello! MY NAME IS JOHN DOE!" ``` As with custom pipes, includes can be passed into the optional `options` argument of `Mark.up`: ``` javascript var options = { pipes: { repeat: function () { ... } }, includes: { header: "
...
", footer: "
...
" } }; var result = Mark.up(template, context, options); ``` ### Functions as includes You can even include a *function* that returns a string when the template is processed: ``` javascript Mark.includes.status = function () { return "You are here: " + location.href; }; var template = "Welcome! {{status}}"; var result = Mark.up(template, context); // "Welcome! You are here: http://www.example.com/" ``` *Includes are accessible in the global scope of template execution and from one template to another. They take precedence over `context` variables with the same name, so be careful to avoid naming conflicts.* ## Global variables You can create global variables for use anywhere inside a template. For example: ``` javascript Mark.globals.img_width = 200; var template = "{{images}} {{/images}}"; ``` A global variable can be any kind of object. As with includes, global variables can be passed into the optional `options` argument of `Mark.up`: ``` javascript var options = { globals: { img_width: 200, img_height: 300 } }; var result = Mark.up(template, context, options); ``` ### The 'set' pipe The special `set` pipe lets you set a global variable *inside the template itself*: ``` text {{users|size|set>num_users}} {{if num_users|more>10}} ... {{/if}} ``` *Global variables are accessible from one template to another. They take precedence over includes and `context` variables with the same name, so be careful to avoid naming conflicts.* ## Backtick expressions Although it's rarely necessary, you might want to pass a context variable as an argument to a pipe. You can do this by enclosing the variable in backticks: ``` javascript var context = { "name": "John", "age": 50, "retirement_age": 55 }; var template = "{{if age|more>`retirement_age`}} Life of leisure {{/if}}"; ``` The statement within backticks can be a fully qualified expression, as in: ``` text {{if age|more>`spouse.age|times>2`}} Ewwwwww {{/if}} ``` This technique also applies to global variables: ``` text {{user.prefs.colors.0|set>favorite_color}} {{if other_color|equals>`favorite_color`}} Match! {{/if}} ``` As a best practice, business logic should stay in the business layer of your application. Compare the readability of the following expressions: ``` text {{if user.age|more>`user.retirement_age`}} {{if user.retired}} ``` ## White space Sometimes it's convenient to remove all white space between HTML or XML nodes in the final output. For example, you might want `
A
B
` to become `
A
B
`. To remove white space: ``` javascript Mark.compact = true; ``` Or, via the `options` argument: ``` javascript var options = { compact: true }; var result = Mark.up(template, context, options); ``` ## Undefined variables By default, if Markup does not find the specified variable, it will return `???`. You can modify this by setting the replacer variable. The possible values are as follows: `false` default, replaces with ??? `true` replaces with the original tag `[Custom String]` replaces with the string specified ``` javascript Mark.replacer = true; ``` Or, via the `options` argument: ``` javascript var options = { replacer: '' }; var result = Mark.up(template, context, options); ``` ## Logging You can log any variable to the console for debugging purposes with the `log` pipe: ``` text {{animals}} {{name|upcase|log}} {{/animals}} {{animals}} {{name|log|upcase}} {{/animals}} ``` ## Gotchas Here are some common traps to avoid: ### Ambiguous templates The following template is ambiguous because the first tag is unclosed: ``` javascript var template = "Adam has {{bros|length}} brothers: {{bros}}...{{/bros}}"; ``` In such cases, you should use a self-closing tag: ``` javascript var template = "Adam has {{bros|length /}} brothers: {{bros}}...{{/bros}}"; ``` ### Incorrect notation Markup.js uses dot notation, not bracket notation, for both objects and arrays: ``` javascript var context = { name: { first: "John", last: "Doe" }, colors: ["Red", "Blue", "Green"] }; // WRONG var template = "First name: {{name[first]}}"; // RIGHT var template = "First name: {{name.first}}"; // WRONG var template = "Favorite color: {{colors[0]}}"; // RIGHT var template = "Favorite color: {{colors.0}}"; ``` ### Use of quote marks in pipes Markup.js treats all piped variables as strings, so quote marks are treated like any other characters: ``` javascript // WRONG var template = "{{if name|like>'Adam'}} ..."; // RIGHT var template = "{{if name|like>Adam}} ..."; ``` ## Browser implementation You can implement Markup.js in a few different ways. The right strategy depends on many factors, including the speed and size of your app, the number of templates you're handling, and whether you want the templates to be reusable throughout your codebase. ### 1. Writing templates as JavaScript strings You can write templates as JavaScript string literals, as shown above. It's a good idea to put all your templates together in one file: ``` javascript // templates.js myapp.templates = { user_details: "
...
", user_sidebar: "
...
" }; ``` As your app grows, you might consider splitting up your templates by functional area and loading only some of them at a time: ``` javascript // templates-registration.js myapp.templates.registration = { reg_intro: "
...
", reg_error: "
...
" }; // templates-cart.js myapp.templates.cart = { cart_proceed: "
...
", cart_cancel: "
...
" }; ``` You can use jQuery to inject an evaluated template into a document element: ``` javascript var template = myapp.templates.user_sidebar; var context = user.data; $("#sidebar").html(Mark.up(template, context)); ``` Or, without jQuery: ``` javascript document.getElementById("sidebar").innerHTML = Mark.up(template, context); ``` ### 2. Embedding templates in <script> tags The above method can be unwieldy if you're dealing with large chunks of HTML. Instead, you might want to embed templates inside ` ... ``` Then extract the template from the `