Please note, this is a STATIC archive of website developer.mozilla.org from 03 Nov 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

Revision 913567 of KumaScript

  • Revision slug: MDN/Contribute/Tools/KumaScript
  • Revision title: KumaScript
  • Revision id: 913567
  • Created:
  • Creator: jswisher
  • Is current revision? No
  • Comment

Revision Content

Overview

On the Kuma platform that powers MDN, the template system for automating aspects of content on the wiki is called KumaScript. KumaScript is powered by server-side JavaScript, implemented using Node.js. This article provides basic information on how to use KumaScript.

For a detailed overview and Q&A of KumaScript, watch the MDN dev team's KumaScript Fireside Chat. KumaScript replaced DekiScript, which was the template language for MindTouch, the previous platform used by MDN.

What is KumaScript?

  • A way to reuse and localize content that appears repeatedly between documents (e.g., compatibility labels, section navigation, warning banners).
  • A way to build documents out of content pulled from other documents.
  • A way to fetch and include content from other web sites and services (e.g., Bugzilla).

What KumaScript is not

  • KumaScript does not support interactive scripting of the kind that can accept form submissions.
  • KumaScript does not have access to a database, files, or any other way to store information persistently.
  • KumaScript does not support site personalization based on the user currently logged in.
  • KumaScript does not have access to user information, only to the content and metadata of a wiki page being viewed.

Basics

KumaScript works by allowing trusted MDN users to write embedded JavaScript templates. These templates can be invoked in document content by any MDN author, through the use of macros.

A template looks like this:

<% for (var i = 0; i < $0; i++) { %> 
Hello #<%= i %> 
<% } %>

A macro looks like this:

\{{ hello("3") }}

The output of the macro looks like this:

Hello #0
Hello #1
Hello #2

Macro Syntax

KumaScript templates are invoked in document content with macros, like this:

\{{ templateName("arg0", "arg1", ..., "argN") }}

Macro syntax consists of these rules:

  • Macros start and end with \{{ and \}} characters.
  • The first part of the macro is the name of a template. This corresponds to a wiki page at /en-US/docs/Template:{name}  — editing and creating these pages requires a special permission that admins can grant to trusted editors.
  • A template can accept parameters, and this parameter list starts and ends with parentheses.
  • In a template, the parameters passed in are available inside the macro as the variables $0, $1, $2, and so on.
  • All non-numeric parameters must be in quotes. Numbers can be left unquoted.
  • The entire list of parameters is also available in a template as the variable arguments.

Using JSON as a macro parameter

There is one more semi-experimental feature available to macros: you can supply a JSON object for the first and only parameter, like so:

\{{ templateName({ "Alpha":"one", "Beta":["a","b","c"], "Foo":"http:\/\/mozilla.org\/" }) }}

The data from this macro is available in template code as an object in the $0 argument (e.g., $0.Alpha, $0.Beta, $0.Foo). This also allows you to express complex data structures in macro parameters that are hard or impossible to do with a simple list of parameters.

Note that this parameter style is very picky - it must adhere to JSON syntax exactly, which has some escaping demands that are easy to miss (e.g., all forward slashes are escaped). When in doubt, try running your JSON through a validator.

How to write "\{{"

Since the character sequence "\{{" is used to indicate the start of a macro, this can be troublesome if you actually just want to use "\{{" and "}}" in a page. It will probably produce DocumentParsingError messages.

In this case, you can escape the first brace with a backslash, like so: \\{

Template Syntax

KumaScript templates are processed by an embedded JavaScript template engine with a few simple rules:

  • Most text is treated as output and included in the output stream.
  • JavaScript variables and expressions can be inserted into the output stream with these blocks:
    • <%= expr %> — the value of a JavaScript expression is escaped for HTML before being included in output (e.g., characters like < and > are turned into &lt; and &gt;).
    • <%- expr %> — the value of a JavaScript expression is included in output without any escaping. (Use this if you want to dynamically build markup or use the results of another template that may include markup.)
    • It is an error to include semicolons inside these blocks.
  • Anything inside a <% %> block is interpreted as JavaScript. This can include loops, conditionals, etc.
  • Nothing inside a <% %> block can ever contribute to the output stream. But, you can transition from JS mode to output mode using <% %>—for example:
    <% for (var i = 0; i < $0; i++) { %>
    Hello #<%= i %>
    <% } %>
    

    Note how the JS is contained in <% ... %>, and output happens in the space between %> ... <%. The for loop in JS can begin in one <% %> block, transition to output mode, and finish up in a second <% %> JS block.

  • For more details on EJS syntax, check out the upstream module documentation.

Tips

Dates and times

It's important to note that the standard JavaScript {{jsxref("Date")}} constructor is overridden by the KumaScript Date interface. You can create a JavaScript Date by calling the KumaScript date.now() or date.parse() function.

Advanced Features

Beyond the basics, the KumaScript system offers some advanced features.

Environment variables

When the wiki makes a call to the KumaScript service, it passes along some context on the current document that KumaScript makes available to templates as variables:

env.path
The path to the current wiki document
env.url
The full URL to the current wiki document
env.id
A short, unique ID for the current wiki document
env.files
An array of the files attached to the current wiki document; each object in the array is as described under {{ anch("File objects") }} below
env.review_tags
An array of the review tags on the article ("technical", "editorial", etc.)
env.locale
The locale of the current wiki document
env.title
The title of the current wiki document
env.slug
The URL slug of the current wiki document
env.tags
An array list of tag names for the current wiki document
env.modified
Last modified timestamp for the current wiki document
env.cache_control
Cache-Control header sent in the request for the current wiki document, useful in deciding whether to invalidate caches

File objects

Each file object has the following fields:

title
The attachment's title
description
A textual description of the current revision of the file
filename
The file's name
size
The size of the file in bytes
author
The username of the person who uploaded the file
mime
The MIME type of the file
url
The URL at which the file can be found

Working with tag lists

The env.tags and env.review_tags variables return arrays of tags. You can work with these in many ways, of course, but here are a couple of suggestions.

Looking to see if a specific tag is set

You can look to see if a specific tag exists on a page like this:

if (env.tags.indexOf("tag") != −1) {
  // The page has the tag "tag"
}
Iterating over all the tags on a page

You can also iterate over all the tags on a page, like this:

env.tag.forEach(function(tag) {
  // do whatever you need to do, such as:
  if (tag.indexOf("a") == 0) {
    // this tag starts with "a" - woohoo!
  }
});

APIs and Modules

KumaScript offers some built-in utility APIs, as well as the ability to define new APIs in modules editable as wiki documents.

Built-in methods

This manually-maintained documentation is likely to fall out of date with the code. With that in mind, you can always check out the latest state of built-in APIs in the KumaScript source. But here is a selection of useful methods exposed to templates:

md5(string)
Returns an MD5 hex digest of the given string.
template("name", ["arg0", "arg1", ..., "argN"])
Executes and returns the result of the named template with the given list of parameters.
Used in templates like so: <%- template("warning", ["foo", "bar", "baz"]) %>.
This is a JavaScript function. So, if one of the parameters is an arg variable like $2, do not put it in quotes. Like this: <%- template("warning", [$1, $2, "baz"]) %>. If you need to call another template from within a block of code, do not use <% ... %>. Example: myvar = "<li>" + template("LXRSearch", ["ident", "i", $1]) + "</li>";
require(name)
Loads another template as a module; any output is ignored. Anything assigned to module.exports in the template is returned.
Used in templates like so: <% var my_module = require('MyModule'); %>.
cacheFn(key, timeout, function_to_cache)
Using the given key and cache entry lifetime, cache the results of the given function. Honors the value of env.cache_control to invalidate cache on no-cache, which can be sent by a logged-in user hitting shift-refresh.
request
Access to mikeal/request, a library for making HTTP requests. Using this module in KumaScript templates is not yet very friendly, so you may want to wrap usage in module APIs that simplify things.
log.debug(string)
Outputs a debug message into the script log on the page (i.e. the big red box that usually displays errors).

Built-in API modules

There's only one API built in at the moment, in the kuma namespace. You can see the most up to date list of methods under kuma from the KumaScript source code, but here are a few:

kuma.inspect(object)
Renders any JS object as a string, handy for use with log.debug(). See also: node.js util.inspect().
kuma.htmlEscape(string)
Escapes the characters &, <, >, " to &amp, &lt;, &gt;, &quot;, respectively.
kuma.url
See also: node.js url module.
kuma.fetchFeed(url)
Fetch an RSS feed and parse it into a JS object. See also: Template:InsertFeedLinkList

Creating modules

Using the built-in require() method, you can load a template as a module to share common variables and methods between templates. A module can be defined in a template like this:

<%
module.exports = {
    add: function (a, b) {
        return a + b;
    }
}
%>

Assuming this template is saved as /en-US/docs/Template:MathLib, you can use it in another template like so:

<%
var math_lib = require("MathLib");
%>
The result of 2 + 2 = <%= math_lib.add(2, 2) %>

And, the output of this template will be:

The result of 2 + 2 = 4

Auto-loaded modules

There are a set of modules editable as wiki templates that are automatically loaded and made available to every template. This set is defined in the configuration file for the KumaScript service - any changes to this requires an IT bug to edit configuration and a restart of the service.

For the most part, these attempt to provide stand-ins for legacy DekiScript features to ease template migration. But, going forward, these can be used to share common variables and methods between templates:

Note: You might notice that the DekiScript modules use a built-in method named buildAPI(), like so:

<% module.exports = buildAPI({
    StartsWith: function (str, sub_str) {
        return (''+str).indexOf(sub_str) === 0;
    }
}); %>

The reason for this is because DekiScript is case-insensitive when it comes to references to API methods, whereas JavaScript is strict about uppercase and lowercase in references. So, buildAPI() is a hack to try to cover common case variations in DekiScript calls found in legacy templates.

With that in mind, please do not use buildAPI() in new modules.

Tips and caveats

Debugging

A useful tip when debugging. You can use the log.debug() method to output text to the scripting messages area at the top of the page that's running your template. Note that you need to be really sure to remove these when you're done debugging, as they're visible to all users! To use it, just do something like this:

<%- log.debug("Some text goes here"); %>

You can, of course, create more complex output using script code if it's helpful.

Limitations of content migration from MindTouch

When we make the move to Kuma, we will migrate content from the old MindTouch-based wiki to the new Kuma-based one. This script will do some basic work to attempt to convert scripts. But, there are many common code patterns that migration can't fix.

So, this means that we'll need human intervention to carry template scripts over the rest of the way to being functional.

To find templates in need of review and repair, check here: /en...eview/template.

To find examples of templates that have already been repaired, check here: /en...s/tag/ks-fixed.

Check the template usage stats bug file attachments to help prioritize templates to fix. If you know your way around gzip and grep, this attachment (a 1.5MB tab-delimited file listing template/document pairs) can help tell you what templates are used on which pages.

As you repair templates, please uncheck the "Template" review checkbox and add the tag "ks-fixed", which will keep the above lists accurate.

You can also find templates in need of repair by simply browsing through wiki content and spotting where content looks garbled with code or otherwise incorrect. Editing the page should show you the name of a macro that's in need of help. You may also see scripting errors on pages, which should offer editing links to the templates causing issues.

Keyword Shortcut for quick template editing

If you spend time editing a lot of KumaScript templates (e.g., to fix partly-migrated legacy DekiScript templates), you may find this keyword shortcut bookmark handy:

Name edit mdn template
Location /en...mplate:%s$edit
Keyword te

Armed with this bookmarklet, you can copy the name of a template, open a new tab, and type T , E , Ctrl + V , Return to start editing the template. This helps when a template is misbehaving, yet not badly enough to cause a scripting error message.

Changing Locale Identifiers

The identifiers for various locales have changed from MindTouch to Kuma:

  • en -> en-US
  • cn -> zh-CN
  • zh_cn -> zh-CN
  • zh_tw -> zh-TW
  • pt -> pt-PT

This list should not change in the future, assuming we've not missed any. These locale identifier changes become significant in legacy DekiWIki templates.

URL pattern changes

The URL pattern for all wiki documents has changed:

  • /{locale}/{slug} -> /{locale}/docs/{slug}

So, for example:

  • /en/JavaScript -> /en-US/docs/JavaScript
  • /de/JavaScript -> /de/docs/JavaScript
  • /ja/JavaScript -> /ja/docs/JavaScript

To avoid breaking links, there is an attempt to automatically redirect requests to the legacy-style URLs to new-style URLs. But, efforts should be made to change links to the new-style URLs whenever possible.

Differences from DekiScript

It's useful to note a few changes from templates in DekiScript, in case you encounter these in migrated content:

  • No more <span class="script">
  • No more template. nor wiki.template prefixing
  • No more template({name}[, arguments]) syntax
  • Arguments must be quoted - e.g., bug(123456) becomes bug("123456") - unless you are calling the template from within another template, and the argument is one of the passed-in args like $1 in this example: template("LXRSearch", ["ident", "i", $1]).

Caching

KumaScript templates are heavily cached to improve performance. For the most part, this works great to serve up content that doesn't change very often. But, as a logged in user, you have two options to force a page to be regenerated, in case you notice issues with scripting:

  • Hit Refresh in your browser. This causes KumaScript to invalidate its cache for the content on the current page by issuing a request with a Cache-Control: max-age=0 header.
  • Hit Shift-Refresh in your browser. This causes KumaScript to invalidate cache for the current page, as well as for any templates or content used by the current page by issuing a request with a Cache-Control: no-cache header.

Cookbook

This section will list examples of common patterns for templates used on MDN, including samples of legacy DekiScript templates and their new KumaScript equivalents.

Force templates used on a page to be reloaded

It bears repeating: To force templates used on a page to be reloaded after editing, hit Shift-Reload. Just using Reload by itself will cause the page contents to be regenerated, but using cached templates and included content. A Shift-Reload is necessary to invalidate caches beyond just the content of the page itself.

Recovering from "Unknown Error"

Sometimes, you'll see a scripting message like this when you load a page:

Kumascript service failed unexpectedly: <class 'httplib.BadStatusLine'>

This is probably a temporary failure of the KumaScript service. If you Refresh the page, the error may disappear. If that doesn't work, try a Shift-Refresh. If, after a few tries, the error persists - file an IT bug for Mozilla Developer Network to ask for an investigation.

Broken wiki.languages() macros

On some pages, you'll see a scripting error like this:

Syntax error at line 436, column 461: Expected valid JSON object as the parameter of the preceding macro but...

If you edit the page, you'll probably see a macro like this at the bottom of the page:

\{{ wiki.languages({ "zh-tw": "zh_tw/Core_JavaScript_1.5_教學/JavaScript_概要", ... }) }}

To fix the problem, just delete the macro. Or, replace the curly braces on either side with HTML comments <!-- --> to preserve the information, like so:

<!-- wiki.languages({ "zh-tw": "zh_tw/Core_JavaScript_1.5_教學/JavaScript_概要", ... }) -->

Because Kuma supports localization differently, these macros aren't actually needed any more. But, they've been left intact in case we need to revisit the relationships between localized pages. Unfortunately, it seems like migration has failed to convert some of them properly.

Unconverted inline script blocks

Occasionally, you'll find some text like this at the bottom of a page, or even somewhere in the middle:

autoPreviousNext("JSGChapters");
wiki.languages({
  "fr": "fr/Guide_JavaScript_1.5/Expressions_rationnelles",
  "ja": "ja/Core_JavaScript_1.5_Guide/Regular_Expressions"
});

This is a script block that didn't get converted to a KumaScript macro during migration. It happens, unfortunately. If you switch to HTML source editing mode, you'll see this, a <pre class="script"> element:

<pre class="script" style="font-size: 16px;">
autoPreviousNext(&quot;JSGChapters&quot;);
wiki.languages({
  &quot;fr&quot;: &quot;fr/Guide_JavaScript_1.5/Expressions_rationnelles&quot;,
 &nbsp;&quot;ja&quot;: &quot;ja/Core_JavaScript_1.5_Guide/Regular_Expressions&quot;
});
</pre>

This is an inline script - previously allowed by DekiScript, no longer supported by KumaScript.

For this particular example, common to the JavaScript Guide, you can fix it by removing the wiki.languages part (see previous section) and change the autoPreviousNext() into a macro like so:

\{{ autoPreviousNext("JSGChapters") }}

If you see a block of code that's more complex than the above, you will need to create a new template, move the code there, and replace the code in its previous spot with a macro calling the new template.

Finding the Current Page's Language

In KumaScript, the locale of the current document is exposed as an environment variable:

var lang = env.locale;

In legacy DekiScript templates, coming up with the locale was a bit harder. You'll see chunks of code like this:

/* accepts as input one required parameter: MathML element to create an xref to */
var u = uri.parts(Page.uri);
var lang = string.tolower(u.path[0]);
if (string.contains(lang, "project") || string.contains(lang, "Project")) {
  lang = string.substr(lang, 8);
}
/* fall back to page.language on a user page */
else if (string.StartsWith(lang, "user:")) {
  lang = page.language;
}

Please replace code like the above with the new KumaScript example. The env.locale variable should be reliable and defined for every document.

Reading the contents of a page attachment

You can read the contents of an attached file by using the mdn.getFileContent() function, like this:

<%
  var contents = mdn.getFileContent(fileUrl);
  ... do stuff with the contents ...
%>

or

<%-mdn.getFileContent(fileObject)%>

In other words, you may specify either the URL of the file to read or as a file object. The file objects for a page can be accessed through the array env.files. So, for example, to embed the contents of the first file attached to the article, you can do this:

<%-mdn.getFileContent(env.files[0])%>
Note: You probably don't want to try to embed the contents of a non-text file this way, as the raw contents would be injected as text. This is meant to let you access the contents of text attachments.

If the file isn't found, an empty string is returned. There is currently no way to tell the difference between an empty file and a nonexistent one. But if you're putting empty files on the wiki, you're doing it wrong.

Localizing template content

Templates cannot be translated like other wiki pages. KumaScript only looks for templates in the en-US locale (i.e., /en-US/docs/Template:{name}), and does not look for templates that have been translated to another locale (i.e., /fr/docs/Template:{name}).

So the main way to output content tailored to the current document locale is to pivot on the value of env.locale. There are many ways to do this, but a few patterns are common in the conversion of legacy DekiScript templates:

If/else blocks in KumaScript

The KumaScript equivalent of this can be achieved with simple if/else blocks, like so:

<% if ("fr" == env.locale) { %>
<%- template("CSSRef") %> « <a title="Référence_CSS/Extensions_Mozilla" href="/fr/docs/Référence_CSS/Extensions_Mozilla">Référence CSS:Extensions Mozilla</a>
<% } else if ("ja" == env.locale) { %>
<%- template("CSSRef") %> « <a title="CSS_Reference/Mozilla_Extensions" href="/ja/docs/CSS_Reference/Mozilla_Extensions">CSS リファレンス:Mozilla 拡張仕様</a>
<% } else if ("pl" == env.locale) { %>
<%- template("CSSRef") %> « <a title="Dokumentacja_CSS/Rozszerzenia_Mozilli" href="/pl/docs/Dokumentacja_CSS/Rozszerzenia_Mozilli">Dokumentacja CSS:Rozszerzenia Mozilli</a>
<% } else if ("de" == env.locale) { %>
<%- template("CSSRef") %> « <a title="CSS_Referenz/Mozilla_CSS_Erweiterungen" href="/de/docs/CSS_Referenz/Mozilla_CSS_Erweiterungen">CSS Referenz: Mozilla Erweiterungen</a>
<% } else { %>
<%- template("CSSRef") %> « <a title="CSS_Reference/Mozilla_Extensions" href="/en-US/docs/CSS_Reference/Mozilla_Extensions">CSS Reference:Mozilla Extensions</a>
<% } %>
Legacy DekiScript

A similar way this was done in DekiScript was using <span>'s with lang="{locale}" attributes, like so:

<p><span lang="*" class="lang lang-*"><span class="script">CSSRef()</span> « <a title="en/CSS_Reference/Mozilla_Extensions" href="/en/CSS_Reference/Mozilla_Extensions">CSS Reference:Mozilla Extensions</a></span>
<span lang="en" class="lang lang-en"><span class="script">CSSRef()</span> « <a title="en/CSS_Reference/Mozilla_Extensions" href="/en/CSS_Reference/Mozilla_Extensions">CSS Reference:Mozilla Extensions</a>
<span lang="fr" class="lang lang-fr"><span class="script">CSSRef()</span> « <a title="fr/Référence_CSS/Extensions_Mozilla" href="/fr/Référence_CSS/Extensions_Mozilla">Référence CSS:Extensions Mozilla</a></span>
<span lang="ja" class="lang lang-ja"><span class="script">CSSRef()</span> « <a title="ja/CSS_Reference/Mozilla_Extensions" href="/ja/CSS_Reference/Mozilla_Extensions">CSS リファレンス:Mozilla 拡張仕様</a></span>
<span lang="pl" class="lang lang-pl"> <span class="script">CSSRef()</span> « <a title="pl/Dokumentacja_CSS/Rozszerzenia_Mozilli" href="/pl/Dokumentacja_CSS/Rozszerzenia_Mozilli">Dokumentacja CSS:Rozszerzenia Mozilli</a></span>
<span lang="de" class="lang lang-de"><span class="script">CSSRef()</span> « <a title="de/CSS_Referenz/Mozilla_CSS_Erweiterungen" href="/de/CSS_Referenz/Mozilla_CSS_Erweiterungen">CSS Referenz: Mozilla Erweiterungen</a></span></span></p>

This is no longer supported. If you encounter templates built using the legacy DekiScript approach, revise them to use the new KumaScript pattern.

Depending on what text editor is your favorite, you may be able to copy & paste from the browser-based editor and attack this pattern with a series of search/replace regexes to get you most of the way there.

My favorite editor is MacVim, and a series of regexes like this does the bulk of the work with just a little manual clean up following:

%s#<span#^M<span#g
%s#<span lang="\(.*\)" .*>#<% } else if ("\1" == env.locale) { %>#g
%s#<span class="script">template.Cssxref(#<%- template("Cssxref", [#
%s#)</span> </span>#]) %>

Your mileage may vary, and patterns change slightly from template to template. That's why the migration script was unable to just handle this automatically, after all.

String variables and switch

Rather than switch between full chunks of markup, you can define a set of strings, switch them based on locale, and then use them to fill in placeholders in a single chunk of markup:

<%
var s_title = 'Firefox for Developers';
switch (env.locale) {
    case 'de':
        s_title = "Firefox für Entwickler";
        break;
    case 'fr':
        s_title = "Firefox pour les développeurs";
        break;
    case 'es':
        s_title = "Firefox para desarrolladores";
        break;
};
%>
<span class="title"><%= s_title %></span>

You'll see examples of this in legacy DekiScript templates. For the most part, this pattern should work as-is, but you may need to adjust the expected values of locales (e.g., en, cn, pt become en-US, zh-CN, pt-PT respectively).

Use mdn.localString()

A recent addition to the Template:MDN:Common module is mdn.localString(), used like this:

<%
var s_title = mdn.localString({
  "en-US": "Firefox for Developers",
  "de": "Firefox für Entwickler",
  "es": "Firefox para desarrolladores"
});
%>
<span class="title"><%= s_title %></span>

This is more concise than the switch statement, and may be a better choice where a single string is concerned. However, if many strings need to be translated (e.g., as in CSSRef), a switch statement might help keep all the strings grouped by locale and more easily translated that way.

When the object does not have the appropriate locale, the value of "en-US" is used as the initial value.

Before and after examples

Carriage returns added here and there for clarity.

// Before: DOM0() template Dekiscript
<p><span class="lang lang-en" lang="en">DOM Level 0. Not part of any standard. </span>
<span class="lang lang-es" lang="es">DOM Nivel 0. No es parte de ninguna norma. </span>
<span class="lang lang-*" lang="*">DOM Level 0. Not part of any standard. </span>
<span class="lang lang-fr" lang="fr">DOM Level 0. Ne fait partie d'aucune spécification. </span>
<span class="lang lang-ja" lang="ja">DOM Level 0。どの標準にも属しません。 </span>
<span class="lang lang-pl" lang="pl">DOM Level 0. Nie jest częścią żadnego standardu. </span>
<span class="lang lang-zh-cn" lang="zh-cn">DOM Level 0 不属于任何标准.</span></p>

// After: Kumascript version
<% if ("fr" == env.locale) { %>
<p>DOM Level 0. Ne fait partie d'aucune spécification.</p>
<% } else if ("ja" == env.locale) { %>
<p>DOM Level 0。どの標準にも属しません。 </p>
<% } else if ("pl" == env.locale) { %>
<p>DOM Level 0. Nie jest częścią żadnego standardu.</p>
<% } else if ("es" == env.locale) { %>
<p>DOM Nivel 0. No es parte de ninguna norma.</p>
<% } else if ("zh-CN" == env.locale) { %>
<p>DOM Level 0 不属于任何标准.</p>
<% } else { %>
<p>DOM Level 0. Not part of any standard.</p>
<% } %>
// From ReleaseChannelInfo() template
// Before:
web.html("<p>Firefox " + $0 + ", based on Gecko " + $1 + ", will ship in " + $2 + ".
 This article provides information about the changes in this release that will 
 affect developers. Nightly builds of what will become Firefox " + $0 + " are "
  + web.link(url, "currently available") + " on the " + string.ToUpperFirst($3)
  + " channel.</p>");

// After:
<p>Firefox <%= $0 %>, based on Gecko <%= $1 %>, will ship in <%= $2 %>. This
 article provides information about the changes in this release that will 
 affect developers. Nightly builds of what will become Firefox <%= $0 %> 
 are <%- web.link(url, "currently available")%>  on the
 <%= string.ToUpperFirst($3) %> channel.</p>
// Before: old Dekiscript snippet
if ($1 && string.length($1)) {
  optionsText = optionsText + "<li>" + LXRSearch("ident", "i", $1) + "</li>";
}

// After: new Kumascript. Quote parameters to template() unless it is an arg variable (like $1).
if ($1 && string.length($1)) {
    optionsText = optionsText + "<li>" + template("LXRSearch", ["ident", "i", $1]) + "</li>";
}

// Note that template() within <% ... %> outputs nothing directly. If you want to call another
// template and display its output, use <%= %> or <%- %> like this:
<%- template("LXRSearch", ["ident", "i", $1]) %>

See also

Revision Source

<h2 id="Overview">Overview</h2>

<p>On the <a href="/en-US/docs/MDN/Kuma">Kuma</a> platform that powers MDN, the template system for automating aspects of content on the wiki is called <a class="link-https" href="https://github.com/mozilla/kumascript" title="https://github.com/mozilla/kumascript">KumaScript</a>. KumaScript is powered by server-side JavaScript, implemented using <a class="external" href="https://nodejs.org/" title="https://nodejs.org/">Node.js</a>. This article provides basic information on how to use KumaScript.</p>

<p>For a detailed overview and Q&amp;A of KumaScript, watch the MDN dev team's <a href="https://vreplay.mozilla.com/replay/showRecordDetails.html?sortBy=date&amp;viewCount=1&amp;currentPage=1&amp;groupBy=combo&amp;roomFilter=&amp;usernameFilter=&amp;searchFilter=&amp;usernameFullFilter=&amp;myManager=-1&amp;adminManager=0&amp;webCast=0&amp;command=&amp;recId=1082&amp;auxMessage=&amp;auxMessage1=&amp;lang=en&amp;langChanged=&amp;tenantFilter=&amp;securityTab=">KumaScript Fireside Chat</a>. KumaScript replaced DekiScript, which was the template language for MindTouch, the previous platform used by MDN.</p>

<h3 id="What_is_KumaScript">What is KumaScript?</h3>

<ul>
 <li>A way to reuse and localize content that appears repeatedly between documents (e.g., compatibility labels, section navigation, warning banners).</li>
 <li>A way to build documents out of content pulled from other documents.</li>
 <li>A way to fetch and include content from other web sites and services (e.g., Bugzilla).</li>
</ul>

<h3 id="What_KumaScript_is_not">What KumaScript is not</h3>

<ul>
 <li>KumaScript does not support interactive scripting of the kind that can accept form submissions.</li>
 <li>KumaScript does not have access to a database, files, or any other way to store information persistently.</li>
 <li>KumaScript does not support site personalization based on the user currently logged in.</li>
 <li>KumaScript does not have access to user information, only to the content and metadata of a wiki page being viewed.</li>
</ul>

<h2 id="Basics">Basics</h2>

<p>KumaScript works by allowing <a href="/en-US/docs/MDN/Community/Roles">trusted MDN users</a> to write <a class="link-https" href="https://github.com/visionmedia/ejs" title="https://github.com/visionmedia/ejs">embedded JavaScript templates</a>. These templates can be invoked in document content by <em>any MDN author</em>, through the use of macros.</p>

<p>A <a class="link-https" href="/en-US/docs/Template:hello" title="/en-US/docs/Template:hello">template</a> looks like this:</p>

<pre>
&lt;% for (var i = 0; i &lt; $0; i++) { %&gt; 
Hello #&lt;%= i %&gt; 
&lt;% } %&gt;</pre>

<p>A <a class="link-https" href="/en-US/docs/KumaTests/hello_test?raw=1" title="/en-US/docs/KumaTests/hello_test?raw=1">macro</a> looks like this:</p>

<pre>
\{{ hello("3") }}</pre>

<p>The <a class="link-https" href="/en-US/docs/KumaTests/hello_test" title="/en-US/docs/KumaTests/hello_test">output of the macro</a> looks like this:</p>

<pre>
Hello #0
Hello #1
Hello #2</pre>

<h3 id="Macro_Syntax">Macro Syntax</h3>

<p>KumaScript templates are invoked in document content with macros, like this:</p>

<pre>
\{{ templateName("arg0", "arg1", ..., "argN") }}
</pre>

<p>Macro syntax consists of these rules:</p>

<ul>
 <li>Macros start and end with <code>\{{</code> and <code>\}}</code> characters.</li>
 <li>The first part of the macro is the name of a template. This corresponds to a wiki page at <code>/en-US/docs/Template:{name}</code>&nbsp; — editing and creating these pages requires a special permission that admins can grant to trusted editors.</li>
 <li>A template can accept parameters, and this parameter list starts and ends with parentheses.</li>
 <li>In a template, the parameters passed in are available inside the macro as the variables <code>$0</code>, <code>$1</code>, <code>$2</code>, and so on.</li>
 <li>All non-numeric parameters must be in quotes. Numbers can be left unquoted.</li>
 <li>The entire list of parameters is also available in a template as the variable <code>arguments</code>.</li>
</ul>

<h4 id="Using_JSON_as_a_macro_parameter">Using JSON as a macro parameter</h4>

<p>There is one more semi-experimental feature available to macros: you can supply a JSON object for the first and only parameter, like so:</p>

<pre>
\{{ templateName({ "Alpha":"one", "Beta":["a","b","c"], "Foo":"http:\/\/mozilla.org\/" }) }}
</pre>

<p>The data from this macro is available in template code as an object in the <code>$0</code> argument (e.g., <code>$0.Alpha</code>, <code>$0.Beta</code>, <code>$0.Foo</code>). This also allows you to express complex data structures in macro parameters that are hard or impossible to do with a simple list of parameters.</p>

<p>Note that this parameter style is very picky - it must adhere to <a href="https://json.org/" title="https://json.org/">JSON syntax</a> exactly, which has some escaping demands that are easy to miss (e.g., all forward slashes are escaped). When in doubt, <a href="https://jsonlint.com/" title="https://jsonlint.com/">try running your JSON through a validator</a>.</p>

<h4 id="How_to_write">How to write "\{{"</h4>

<p>Since the character sequence "<code>\{{</code>" is used to indicate the start of a macro, this can be troublesome if you actually just want to use "<code>\{{</code>" and "<code>}}</code>" in a page. It will probably produce <code>DocumentParsingError</code> messages.</p>

<p>In this case, you can escape the first brace with a backslash, like so: <code>\\{</code></p>

<h3 id="Template_Syntax">Template Syntax</h3>

<p>KumaScript templates are processed by an <a class="link-https" href="https://github.com/visionmedia/ejs" title="https://github.com/visionmedia/ejs">embedded JavaScript template engine</a> with a few simple rules:</p>

<ul>
 <li>Most text is treated as output and included in the output stream.</li>
 <li>JavaScript variables and expressions can be inserted into the output stream with these blocks:
  <ul>
   <li><code>&lt;%= expr %&gt;</code> — the value of a JavaScript expression is escaped for HTML before being included in output (e.g., characters like <code>&lt;</code> and <code>&gt;</code> are turned into <code>&amp;lt;</code> and <code>&amp;gt;</code>).</li>
   <li><code>&lt;%- expr %&gt;</code> — the value of a JavaScript expression is included in output without any escaping. (Use this if you want to dynamically build markup or use the results of another template that may include markup.)</li>
   <li>It is an error to include semicolons inside these blocks.</li>
  </ul>
 </li>
 <li>Anything inside a <code>&lt;% %&gt;</code> block is interpreted as JavaScript. This can include loops, conditionals, etc.</li>
 <li>Nothing inside a <code>&lt;% %&gt;</code> block can ever contribute to the output stream. But, you can transition from JS mode to output mode using <code>&lt;% %&gt;</code>—for example:
  <pre>
&lt;% for (var i = 0; i &lt; $0; i++) { %&gt;
Hello #&lt;%= i %&gt;
&lt;% } %&gt;
</pre>

  <p>Note how the JS is contained in <code>&lt;% ... %&gt;</code>, and output happens in the space between <code>%&gt; ... &lt;%</code>. The for loop in JS can begin in one <code>&lt;% %&gt;</code> block, transition to output mode, and finish up in a second <code>&lt;% %&gt;</code> JS block.</p>
 </li>
 <li>For more details on EJS syntax, <a class="link-https" href="https://github.com/visionmedia/ejs" title="https://github.com/visionmedia/ejs">check out the upstream module documentation</a>.</li>
</ul>

<h3 id="Tips">Tips</h3>

<h4 id="Dates_and_times">Dates and times</h4>

<p>It's important to note that the standard JavaScript {{jsxref("Date")}} constructor is overridden by the KumaScript <code>Date</code> interface. You can create a JavaScript <code>Date</code> by calling the KumaScript <code>date.now()</code> or <code>date.parse()</code> function.</p>

<h2 id="Advanced_Features">Advanced Features</h2>

<p>Beyond the basics, the KumaScript system offers some advanced features.</p>

<h3 id="Environment_variables">Environment variables</h3>

<p>When the wiki makes a call to the KumaScript service, <a class="link-https" href="https://github.com/mozilla/kuma/blob/master/apps/wiki/kumascript.py#L130" title="https://github.com/mozilla/kuma/blob/master/apps/wiki/views.py#L537">it passes along some context on the current document</a> that KumaScript makes available to templates as variables:</p>

<dl>
 <dt><code>env.path</code></dt>
 <dd>The path to the current wiki document</dd>
 <dt><code>env.url</code></dt>
 <dd>The full URL to the current wiki document</dd>
 <dt><code>env.id</code></dt>
 <dd>A short, unique ID for the current wiki document</dd>
 <dt><code>env.files</code></dt>
 <dd>An array of the files attached to the current wiki document; each object in the array is as described under {{ anch("File objects") }} below</dd>
 <dt><code>env.review_tags</code></dt>
 <dd>An array of the review tags on the article ("technical", "editorial", etc.)</dd>
 <dt><code>env.locale</code></dt>
 <dd>The locale of the current wiki document</dd>
 <dt><code>env.title</code></dt>
 <dd>The title of the current wiki document</dd>
 <dt><code>env.slug</code></dt>
 <dd>The URL slug of the current wiki document</dd>
 <dt><code>env.tags</code></dt>
 <dd>An array list of tag names for the current wiki document</dd>
 <dt><code>env.modified</code></dt>
 <dd>Last modified timestamp for the current wiki document</dd>
 <dt><code>env.cache_control</code></dt>
 <dd><code>Cache-Control</code> header sent in the request for the current wiki document, useful in deciding whether to invalidate caches</dd>
</dl>

<h4 id="File_objects">File objects</h4>

<p>Each file object has the following fields:</p>

<dl>
 <dt><code>title</code></dt>
 <dd>The attachment's title</dd>
 <dt><code>description</code></dt>
 <dd>A textual description of the current revision of the file</dd>
 <dt><code>filename</code></dt>
 <dd>The file's name</dd>
 <dt><code>size</code></dt>
 <dd>The size of the file in bytes</dd>
 <dt><code>author</code></dt>
 <dd>The username of the person who uploaded the file</dd>
 <dt><code>mime</code></dt>
 <dd>The MIME type of the file</dd>
 <dt><code>url</code></dt>
 <dd>The URL at which the file can be found</dd>
</dl>

<h4 id="Working_with_tag_lists">Working with tag lists</h4>

<p>The <code>env.tags</code> and <code>env.review_tags</code> variables return arrays of tags. You can work with these in many ways, of course, but here are a couple of suggestions.</p>

<h5 id="Looking_to_see_if_a_specific_tag_is_set">Looking to see if a specific tag is set</h5>

<p>You can look to see if a specific tag exists on a page like this:</p>

<pre class="brush: js">
if (env.tags.indexOf("tag") != −1) {
  // The page has the tag "tag"
}
</pre>

<h5 id="Iterating_over_all_the_tags_on_a_page">Iterating over all the tags on a page</h5>

<p>You can also iterate over all the tags on a page, like this:</p>

<pre class="brush: js">
env.tag.forEach(function(tag) {
  // do whatever you need to do, such as:
  if (tag.indexOf("a") == 0) {
    // this tag starts with "a" - woohoo!
  }
});</pre>

<h3 id="APIs_and_Modules">APIs and Modules</h3>

<p>KumaScript offers some built-in utility APIs, as well as the ability to define new APIs in modules editable as wiki documents.</p>

<h4 id="Built-in_methods">Built-in methods</h4>

<p>This manually-maintained documentation is likely to fall out of date with the code. With that in mind, <a class="link-https" href="https://github.com/mozilla/kumascript/blob/master/lib/kumascript/api.js#L175" title="https://github.com/mozilla/kumascript/blob/master/lib/kumascript/api.js#L208">you can always check out the latest state of built-in APIs in the KumaScript source</a>. But here is a selection of useful methods exposed to templates:</p>

<dl>
 <dt><code>md5(string)</code></dt>
 <dd>Returns an MD5 hex digest of the given string.</dd>
 <dt><code>template("name", ["arg0", "arg1", ..., "argN"])</code></dt>
 <dd>Executes and returns the result of the named template with the given list of parameters.</dd>
 <dd>Used in templates like so: <code>&lt;%- template("warning", ["foo", "bar", "baz"]) %&gt;</code>.</dd>
 <dd>This is a JavaScript function. So, if one of the parameters is an arg variable like $2, do not put it in quotes. Like this: <code>&lt;%- template("warning", [$1, $2, "baz"]) %&gt;</code>. If you need to call another template from within a block of code, do not use <code>&lt;%</code> ... <code>%&gt;</code>. Example:&nbsp;<code>myvar = "&lt;li&gt;" + template("LXRSearch", ["ident", "i", $1]) + "&lt;/li&gt;";</code></dd>
 <dt><code>require(name)</code></dt>
 <dd>Loads another template as a module; any output is ignored. Anything assigned to <code>module.exports</code> in the template is returned.</dd>
 <dd>Used in templates like so: <code>&lt;% var my_module = require('MyModule'); %&gt;</code>.</dd>
 <dt><code>cacheFn(key, timeout, function_to_cache)</code></dt>
 <dd>Using the given key and cache entry lifetime, cache the results of the given function. Honors the value of <code>env.cache_control</code> to invalidate cache on <code>no-cache</code>, which can be sent by a logged-in user hitting shift-refresh.</dd>
 <dt><code>request</code></dt>
 <dd>Access to <a class="link-https" href="https://github.com/mikeal/request" title="https://github.com/mikeal/request"><code>mikeal/request</code></a>, a library for making HTTP requests. Using this module in KumaScript templates is not yet very friendly, so you may want to wrap usage in module APIs that simplify things.</dd>
 <dt><code>log.debug(string)</code></dt>
 <dd>Outputs a debug message into the script log on the page (i.e. the big red box that usually displays errors).</dd>
</dl>

<h4 id="Built-in_API_modules">Built-in API modules</h4>

<p>There's only one API built in at the moment, in the <code>kuma</code> namespace. You can see the most up to date list of methods under <code>kuma</code> from <a href="https://github.com/mozilla/kumascript/blob/master/lib/kumascript/api.js#L74">the KumaScript source code</a>, but here are a few:</p>

<dl>
 <dt><code>kuma.inspect(object)</code></dt>
 <dd>Renders any JS object as a string, handy for use with <code>log.debug()</code>. See also: <a href="https://nodejs.org/api/util.html#util_util_inspect_object_options">node.js <code>util.inspect()</code></a>.</dd>
</dl>

<dl>
 <dt><code>kuma.htmlEscape(string)</code></dt>
 <dd>Escapes the characters <code>&amp;, &lt;, &gt;, "</code> to <code>&amp;amp, &amp;lt;, &amp;gt;, &amp;quot;</code>, respectively.</dd>
 <dt><code>kuma.url</code></dt>
 <dd>See also: <a href="https://nodejs.org/api/url.html">node.js <code>url</code> module</a>.</dd>
 <dt><code>kuma.fetchFeed(url)</code></dt>
 <dd>Fetch an RSS feed and parse it into a JS object. See also: <a href="https://developer.mozilla.org/en-US/docs/Template:InsertFeedLinkList"><code>Template:InsertFeedLinkList</code></a></dd>
</dl>

<h4 id="Creating_modules">Creating modules</h4>

<p>Using the built-in <code>require()</code> method, you can load a template as a module to share common variables and methods between templates. A module can be defined in a template like this:</p>

<pre>
&lt;%
module.exports = {
    add: function (a, b) {
        return a + b;
    }
}
%&gt;
</pre>

<p>Assuming this template is saved as <code>/en-US/docs/Template:MathLib</code>, you can use it in another template like so:</p>

<pre>
&lt;%
var math_lib = require("MathLib");
%&gt;
The result of 2 + 2 = &lt;%= math_lib.add(2, 2) %&gt;
</pre>

<p>And, the output of this template will be:</p>

<pre>
The result of 2 + 2 = 4
</pre>

<h4 id="Auto-loaded_modules">Auto-loaded modules</h4>

<p>There are a set of modules editable as wiki templates that are automatically loaded and made available to every template. This set is defined in the configuration file for the KumaScript service - any changes to this requires an IT bug to edit configuration and a restart of the service.</p>

<p>For the most part, these attempt to provide stand-ins for legacy DekiScript features to ease template migration. But, going forward, these can be used to share common variables and methods between templates:</p>

<ul>
 <li><code>mdn.*</code> - <a class="link-https" href="/en-US/docs/Template:MDN:Common" title="/en-US/docs/Template:MDN:Common">Template:MDN:Common</a></li>
 <li><code>Date.*</code> - <a class="link-https" href="/en-US/docs/Template:DekiScript:Date" title="/en-US/docs/Template:DekiScript:Date">Template:DekiScript:Date</a></li>
 <li><code>Page.*</code> - <a class="link-https" href="/en-US/docs/Template:DekiScript:Page" title="/en-US/docs/Template:DekiScript:Page">Template:DekiScript:Page</a></li>
 <li><code>String.*</code> - <a class="link-https" href="/en-US/docs/Template:DekiScript:String" title="/en-US/docs/Template:DekiScript:String">Template:DekiScript:String</a></li>
 <li><code>Uri.*</code> - <a class="link-https" href="/en-US/docs/Template:DekiScript:Uri" title="/en-US/docs/Template:DekiScript:Uri">Template:DekiScript:Uri</a></li>
 <li><code>Web.*</code> - <a class="link-https" href="/en-US/docs/Template:DekiScript:Web" title="/en-US/docs/Template:DekiScript:Web">Template:DekiScript:Web</a></li>
 <li><code>Wiki.*</code> - <a class="link-https" href="/en-US/docs/Template:DekiScript:Wiki" title="/en-US/docs/Template:DekiScript:Wiki">Template:DekiScript:Wiki</a></li>
</ul>

<p><strong>Note:</strong> You might notice that the DekiScript modules use a built-in method named <code>buildAPI()</code>, like so:</p>

<pre>
&lt;% module.exports = buildAPI({
    StartsWith: function (str, sub_str) {
        return (''+str).indexOf(sub_str) === 0;
    }
}); %&gt;
</pre>

<p>The reason for this is because DekiScript is case-insensitive when it comes to references to API methods, whereas JavaScript is strict about uppercase and lowercase in references. So, <code>buildAPI()</code> is a hack to try to cover common case variations in DekiScript calls found in legacy templates.</p>

<div class="note">
<p>With that in mind, please do not use <code>buildAPI()</code> in new modules.</p>
</div>

<h2 id="Tips_and_caveats">Tips and caveats</h2>

<h3 id="Debugging">Debugging</h3>

<p>A useful tip when debugging. You can use the <code>log.debug()</code> method to output text to the scripting messages area at the top of the page that's running your template. Note that you need to be really sure to remove these when you're done debugging, as they're visible to all users! To use it, just do something like this:</p>

<pre>
&lt;%- log.debug("Some text goes here"); %&gt;
</pre>

<p>You can, of course, create more complex output using script code if it's helpful.</p>

<h3 id="Limitations_of_content_migration_from_MindTouch">Limitations of content migration from MindTouch</h3>

<p>When we make the move to Kuma, we will migrate content from the old MindTouch-based wiki to the new Kuma-based one. This script will do some basic work to attempt to convert scripts. But, there are many common code patterns that migration can't fix.</p>

<p>So, this means that we'll need human intervention to carry template scripts over the rest of the way to being functional.</p>

<p>To find templates in need of review and repair, <a class="link-https" href="/en-US/docs/needs-review/template" title="/en-US/docs/needs-review/template">check here</a>: <a class="link-https" href="/en-US/docs/needs-review/template" rel="freelink">/en...eview/template</a>.</p>

<p>To find examples of templates that have already been repaired, <a class="link-https" href="/en-US/docs/tag/ks-fixed" title="/en-US/docs/tag/ks-fixed">check here</a>: <a class="link-https" href="/en-US/docs/tag/ks-fixed" rel="freelink">/en...s/tag/ks-fixed</a>.</p>

<p>Check the <a class="link-https" href="https://bugzilla.mozilla.org/show_bug.cgi?id=714804">template usage stats bug</a> file attachments to help prioritize templates to fix. If you know your way around <code>gzip</code> and <code>grep</code>, <a class="link-https" href="https://bugzilla.mozilla.org/attachment.cgi?id=591545" title="https://bugzilla.mozilla.org/attachment.cgi?id=591545">this attachment</a> (a 1.5MB tab-delimited file listing template/document pairs) can help tell you what templates are used on which pages.</p>

<p>As you repair templates, please uncheck the "Template" review checkbox and add the tag "ks-fixed", which will keep the above lists accurate.</p>

<p>You can also find templates in need of repair by simply browsing through wiki content and spotting where content looks garbled with code or otherwise incorrect. Editing the page should show you the name of a macro that's in need of help. You may also see scripting errors on pages, which should offer editing links to the templates causing issues.</p>

<h3 id="Keyword_Shortcut_for_quick_template_editing">Keyword Shortcut for quick template editing</h3>

<p>If you spend time editing a lot of KumaScript templates (e.g., to fix partly-migrated legacy DekiScript templates), you may find this keyword shortcut bookmark handy:</p>

<table border="1" cellpadding="4" cellspacing="4" style="table-layout:fixed; width:100%">
 <tbody>
  <tr>
   <th width="10%">Name</th>
   <td><code>edit mdn template</code></td>
  </tr>
  <tr>
   <th width="10%">Location</th>
   <td><code><a class="link-https" href="/en-US/docs/Template:%s$edit" rel="freelink">/en...mplate:%s$edit</a></code></td>
  </tr>
  <tr>
   <th width="10%">Keyword</th>
   <td><code>te</code></td>
  </tr>
 </tbody>
</table>

<p>Armed with this bookmarklet, you can copy the name of a template, open a new tab, and type <kbd>T</kbd> , <kbd>E</kbd> , <kbd>Ctrl</kbd> + <kbd>V</kbd> , <kbd>Return</kbd> to start editing the template. This helps when a template is misbehaving, yet not badly enough to cause a scripting error message.</p>

<h3 id="Changing_Locale_Identifiers">Changing Locale Identifiers</h3>

<p>The identifiers for various locales have changed from MindTouch to Kuma:</p>

<ul>
 <li><code>en</code> -&gt; <code>en-US</code></li>
 <li><code>cn</code> -&gt; <code>zh-CN</code></li>
 <li><code>zh_cn</code> -&gt; <code>zh-CN</code></li>
 <li><code>zh_tw</code> -&gt; <code>zh-TW</code></li>
 <li><code>pt</code> -&gt; <code>pt-PT</code></li>
</ul>

<p>This list should not change in the future, assuming we've not missed any. These locale identifier changes become significant in legacy DekiWIki templates.</p>

<h3 id="URL_pattern_changes">URL pattern changes</h3>

<p>The URL pattern for all wiki documents has changed:</p>

<ul>
 <li><code>/{locale}/{slug}</code> -&gt; <code>/{locale}/docs/{slug}</code></li>
</ul>

<p>So, for example:</p>

<ul>
 <li><code>/en/JavaScript</code> -&gt; <code>/en-US/docs/JavaScript</code></li>
 <li><code>/de/JavaScript</code> -&gt; <code>/de/docs/JavaScript</code></li>
 <li><code>/ja/JavaScript</code> -&gt; <code>/ja/docs/JavaScript</code></li>
</ul>

<p>To avoid breaking links, there is an attempt to automatically redirect requests to the legacy-style URLs to new-style URLs. But, efforts should be made to change links to the new-style URLs whenever possible.</p>

<h3 id="Differences_from_DekiScript">Differences from DekiScript</h3>

<p>It's useful to note a few changes from templates in DekiScript, in case you encounter these in migrated content:</p>

<ul>
 <li>No more <code>&lt;span class="script"&gt;</code></li>
 <li>No more <code>template.</code> nor <code>wiki.template</code> prefixing</li>
 <li>No more <code>template({name}[, arguments])</code> syntax</li>
 <li>Arguments must be quoted - e.g., <code>bug(123456)</code> becomes <code>bug("123456")</code> - unless you are calling the template&nbsp;from within another template, and the argument is one of the passed-in args like <code>$1</code> in this example:&nbsp;<code>template("LXRSearch", ["ident", "i", $1])</code>.</li>
</ul>

<h3 id="Caching">Caching</h3>

<p>KumaScript templates are heavily cached to improve performance. For the most part, this works great to serve up content that doesn't change very often. But, as a logged in user, you have two options to force a page to be regenerated, in case you notice issues with scripting:</p>

<ul>
 <li>Hit Refresh in your browser. This causes KumaScript to invalidate its cache for the content on the current page by issuing a request with a <code>Cache-Control: max-age=0</code> header.</li>
 <li>Hit Shift-Refresh in your browser. This causes KumaScript to invalidate cache for the current page, as well as for any templates or content used by the current page by issuing a request with a <code>Cache-Control: no-cache</code> header.</li>
</ul>

<h2 id="Cookbook">Cookbook</h2>

<p>This section will list examples of common patterns for templates used on MDN, including samples of legacy DekiScript templates and their new KumaScript equivalents.</p>

<h3 id="Force_templates_used_on_a_page_to_be_reloaded">Force templates used on a page to be reloaded</h3>

<p>It bears repeating: To force templates used on a page to be reloaded after editing, hit Shift-Reload. Just using Reload by itself will cause the page contents to be regenerated, but using cached templates and included content. A Shift-Reload is necessary to invalidate caches beyond just the content of the page itself.</p>

<h3 id="Recovering_from_Unknown_Error">Recovering from "Unknown Error"</h3>

<p>Sometimes, you'll see a scripting message like this when you load a page:</p>

<pre>
Kumascript service failed unexpectedly: &lt;class 'httplib.BadStatusLine'&gt;</pre>

<p>This is probably a temporary failure of the KumaScript service. If you Refresh the page, the error may disappear. If that doesn't work, try a Shift-Refresh. If, after a few tries, the error persists - <a class="link-https" href="https://bugzilla.mozilla.org/enter_bug.cgi?product=mozilla.org&amp;format=itrequest">file an IT bug</a> for Mozilla Developer Network to ask for an investigation.</p>

<h3 id="Broken_wiki.languages()_macros">Broken wiki.languages() macros</h3>

<p>On some pages, you'll see a scripting error like this:</p>

<pre>
Syntax error at line 436, column 461: Expected valid JSON object as the parameter of the preceding macro but...
</pre>

<p>If you edit the page, you'll probably see a macro like this at the bottom of the page:</p>

<pre>
\{{ wiki.languages({ "zh-tw": "zh_tw/Core_JavaScript_1.5_教學/JavaScript_概要", ... }) }}
</pre>

<p>To fix the problem, just delete the macro. Or, replace the curly braces on either side with HTML comments <code>&lt;!-- --&gt;</code> to preserve the information, like so:</p>

<pre>
&lt;!-- wiki.languages({ "zh-tw": "zh_tw/Core_JavaScript_1.5_教學/JavaScript_概要", ... }) --&gt;
</pre>

<p>Because Kuma supports localization differently, these macros aren't actually needed any more. But, they've been left intact in case we need to revisit the relationships between localized pages. Unfortunately, it seems like migration has failed to convert some of them properly.</p>

<h3 id="Unconverted_inline_script_blocks">Unconverted inline script blocks</h3>

<p>Occasionally, you'll find some text like this at the bottom of a page, or even somewhere in the middle:</p>

<pre>
autoPreviousNext("JSGChapters");
wiki.languages({
  "fr": "fr/Guide_JavaScript_1.5/Expressions_rationnelles",
  "ja": "ja/Core_JavaScript_1.5_Guide/Regular_Expressions"
});
</pre>

<p>This is a script block that didn't get converted to a KumaScript macro during migration. It happens, unfortunately. If you switch to HTML source editing mode, you'll see this, a <code>&lt;pre class="script"&gt;</code> element:</p>

<pre>
&lt;pre class="script" style="font-size: 16px;"&gt;
autoPreviousNext(&amp;quot;JSGChapters&amp;quot;);
wiki.languages({
  &amp;quot;fr&amp;quot;: &amp;quot;fr/Guide_JavaScript_1.5/Expressions_rationnelles&amp;quot;,
 &amp;nbsp;&amp;quot;ja&amp;quot;: &amp;quot;ja/Core_JavaScript_1.5_Guide/Regular_Expressions&amp;quot;
});
&lt;/pre&gt;
</pre>

<p>This is an inline script - previously allowed by DekiScript, no longer supported by KumaScript.</p>

<p>For this particular example, common to the JavaScript Guide, you can fix it by removing the wiki.languages part (see <a href="/en-US/docs/Project:Introduction_to_KumaScript#Broken_wiki.languages()_macros" title="Project:Introduction_to_KumaScript#Broken_wiki.languages()_macros">previous section</a>) and change the autoPreviousNext() into a macro like so:</p>

<pre>
\{{ autoPreviousNext("JSGChapters") }}
</pre>

<p>If you see a block of code that's more complex than the above, you will need to create a new template, move the code there, and replace the code in its previous spot with a macro calling the new template.</p>

<h3 id="Finding_the_Current_Page's_Language">Finding the Current Page's Language</h3>

<p>In KumaScript, the locale of the current document is exposed as an environment variable:</p>

<pre>
var lang = env.locale;
</pre>

<p>In legacy DekiScript templates, coming up with the locale was a bit harder. You'll see chunks of code like this:</p>

<pre>
/* accepts as input one required parameter: MathML element to create an xref to */
var u = uri.parts(Page.uri);
var lang = string.tolower(u.path[0]);
if (string.contains(lang, "project") || string.contains(lang, "Project")) {
&nbsp; lang = string.substr(lang, 8);
}
/* fall back to page.language on a user page */
else if (string.StartsWith(lang, "user:")) {
&nbsp; lang = page.language;
}
</pre>

<p>Please replace code like the above with the new KumaScript example. The <code>env.locale</code> variable should be reliable and defined for every document.</p>

<h3 id="Reading_the_contents_of_a_page_attachment">Reading the contents of a page attachment</h3>

<p>You can read the contents of an attached file by using the <code>mdn.getFileContent()</code> function, like this:</p>

<pre>
&lt;%
&nbsp;&nbsp;var contents = mdn.getFileContent(fileUrl);
&nbsp; ... do stuff with the contents ...
%&gt;
</pre>

<p>or</p>

<pre>
&lt;%-mdn.getFileContent(fileObject)%&gt;
</pre>

<p>In other words, you may specify either the URL of the file to read or as a file object. The file objects for a page can be accessed through the array <code>env.files</code>. So, for example, to embed the contents of the first file attached to the article, you can do this:</p>

<pre>
&lt;%-mdn.getFileContent(env.files[0])%&gt;
</pre>

<div class="note"><strong>Note:</strong> You probably don't want to try to embed the contents of a non-text file this way, as the raw contents would be injected as text. This is meant to let you access the contents of text attachments.</div>

<p>If the file isn't found, an empty string is returned. There is currently no way to tell the difference between an empty file and a nonexistent one. But if you're putting empty files on the wiki, you're doing it wrong.</p>

<h3 id="Localizing_template_content">Localizing template content</h3>

<p>Templates cannot be translated like other wiki pages. KumaScript only looks for templates in the en-US locale (i.e., <code>/en-US/docs/Template:{name}</code>), and does not look for templates that have been translated to another locale (i.e., <code>/fr/docs/Template:{name}</code>).</p>

<p>So the main way to output content tailored to the current document locale is to pivot on the value of <code>env.locale</code>. There are many ways to do this, but a few patterns are common in the conversion of legacy DekiScript templates:</p>

<h4 id="Ifelse_blocks_in_KumaScript">If/else blocks in KumaScript</h4>

<p>The KumaScript equivalent of this can be achieved with simple if/else blocks, like so:</p>

<pre>
&lt;% if ("fr" == env.locale) { %&gt;
&lt;%- template("CSSRef") %&gt; « &lt;a title="Référence_CSS/Extensions_Mozilla" href="/fr/docs/Référence_CSS/Extensions_Mozilla"&gt;Référence CSS:Extensions Mozilla&lt;/a&gt;
&lt;% } else if ("ja" == env.locale) { %&gt;
&lt;%- template("CSSRef") %&gt; « &lt;a title="CSS_Reference/Mozilla_Extensions" href="/ja/docs/CSS_Reference/Mozilla_Extensions"&gt;CSS リファレンス:Mozilla 拡張仕様&lt;/a&gt;
&lt;% } else if ("pl" == env.locale) { %&gt;
&lt;%- template("CSSRef") %&gt; « &lt;a title="Dokumentacja_CSS/Rozszerzenia_Mozilli" href="/pl/docs/Dokumentacja_CSS/Rozszerzenia_Mozilli"&gt;Dokumentacja CSS:Rozszerzenia Mozilli&lt;/a&gt;
&lt;% } else if ("de" == env.locale) { %&gt;
&lt;%- template("CSSRef") %&gt; « &lt;a title="CSS_Referenz/Mozilla_CSS_Erweiterungen" href="/de/docs/CSS_Referenz/Mozilla_CSS_Erweiterungen"&gt;CSS Referenz: Mozilla Erweiterungen&lt;/a&gt;
&lt;% } else { %&gt;
&lt;%- template("CSSRef") %&gt; « &lt;a title="CSS_Reference/Mozilla_Extensions" href="/en-US/docs/CSS_Reference/Mozilla_Extensions"&gt;CSS Reference:Mozilla Extensions&lt;/a&gt;
&lt;% } %&gt;
</pre>

<h5 id="Legacy_DekiScript"><strong>Legacy DekiScript</strong></h5>

<p>A similar way this was done in DekiScript was using <code>&lt;span&gt;</code>'s with <code>lang="{locale}"</code> attributes, like so:</p>

<pre>
&lt;p&gt;&lt;span lang="*" class="lang lang-*"&gt;&lt;span class="script"&gt;CSSRef()&lt;/span&gt; « &lt;a title="en/CSS_Reference/Mozilla_Extensions" href="/en/CSS_Reference/Mozilla_Extensions"&gt;CSS Reference:Mozilla Extensions&lt;/a&gt;&lt;/span&gt;
&lt;span lang="en" class="lang lang-en"&gt;&lt;span class="script"&gt;CSSRef()&lt;/span&gt; « &lt;a title="en/CSS_Reference/Mozilla_Extensions" href="/en/CSS_Reference/Mozilla_Extensions"&gt;CSS Reference:Mozilla Extensions&lt;/a&gt;
&lt;span lang="fr" class="lang lang-fr"&gt;&lt;span class="script"&gt;CSSRef()&lt;/span&gt; « &lt;a title="fr/Référence_CSS/Extensions_Mozilla" href="/fr/Référence_CSS/Extensions_Mozilla"&gt;Référence CSS:Extensions Mozilla&lt;/a&gt;&lt;/span&gt;
&lt;span lang="ja" class="lang lang-ja"&gt;&lt;span class="script"&gt;CSSRef()&lt;/span&gt; « &lt;a title="ja/CSS_Reference/Mozilla_Extensions" href="/ja/CSS_Reference/Mozilla_Extensions"&gt;CSS リファレンス:Mozilla 拡張仕様&lt;/a&gt;&lt;/span&gt;
&lt;span lang="pl" class="lang lang-pl"&gt; &lt;span class="script"&gt;CSSRef()&lt;/span&gt; « &lt;a title="pl/Dokumentacja_CSS/Rozszerzenia_Mozilli" href="/pl/Dokumentacja_CSS/Rozszerzenia_Mozilli"&gt;Dokumentacja CSS:Rozszerzenia Mozilli&lt;/a&gt;&lt;/span&gt;
&lt;span lang="de" class="lang lang-de"&gt;&lt;span class="script"&gt;CSSRef()&lt;/span&gt; « &lt;a title="de/CSS_Referenz/Mozilla_CSS_Erweiterungen" href="/de/CSS_Referenz/Mozilla_CSS_Erweiterungen"&gt;CSS Referenz: Mozilla Erweiterungen&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
</pre>

<p>This is no longer supported. If you encounter templates built using the legacy DekiScript approach, revise them to use the new KumaScript pattern.</p>

<p>Depending on what text editor is your favorite, you may be able to copy &amp; paste from the browser-based editor and attack this pattern with a series of search/replace regexes to get you most of the way there.</p>

<p>My favorite editor is MacVim, and a series of regexes like this does the bulk of the work with just a little manual clean up following:</p>

<pre>
%s#&lt;span#^M&lt;span#g
%s#&lt;span lang="\(.*\)" .*&gt;#&lt;% } else if ("\1" == env.locale) { %&gt;#g
%s#&lt;span class="script"&gt;template.Cssxref(#&lt;%- template("Cssxref", [#
%s#)&lt;/span&gt; &lt;/span&gt;#]) %&gt;
</pre>

<p>Your mileage may vary, and patterns change slightly from template to template. That's why the migration script was unable to just handle this automatically, after all.</p>

<h4 id="String_variables_and_switch">String variables and switch</h4>

<p>Rather than switch between full chunks of markup, you can define a set of strings, switch them based on locale, and then use them to fill in placeholders in a single chunk of markup:</p>

<pre>
&lt;%
var s_title = 'Firefox for Developers';
switch (env.locale) {
    case 'de':
        s_title = "Firefox für Entwickler";
        break;
    case 'fr':
        s_title = "Firefox pour les développeurs";
        break;
    case 'es':
        s_title = "Firefox para desarrolladores";
        break;
};
%&gt;
&lt;span class="title"&gt;&lt;%= s_title %&gt;&lt;/span&gt;
</pre>

<p>You'll see examples of this in legacy DekiScript templates. For the most part, this pattern should work as-is, but you may need to adjust the expected values of locales (e.g., <code>en</code>, <code>cn</code>, <code>pt</code> become <code>en-US</code>, <code>zh-CN</code>, <code>pt-PT</code> respectively).</p>

<h4 id="Use_mdn.localString()">Use <code>mdn.localString()</code></h4>

<p>A recent addition to the <code>Template:MDN:Common</code> module is <code>mdn.localString()</code>, used like this:</p>

<pre>
&lt;%
var s_title = mdn.localString({
  "en-US": "Firefox for Developers",
  "de": "Firefox für Entwickler",
  "es": "Firefox para desarrolladores"
});
%&gt;
&lt;span class="title"&gt;&lt;%= s_title %&gt;&lt;/span&gt;
</pre>

<p>This is more concise than the switch statement, and may be a better choice where a single string is concerned. However, if many strings need to be translated (e.g., as in <a class="link-https" href="/en-US/docs/Template:CSSRef" title="/en-US/docs/Template:CSSRef">CSSRef</a>), a switch statement might help keep all the strings grouped by locale and more easily translated that way.</p>

<p>When the object does not have the appropriate locale, the value of "en-US" is used as the initial value.</p>

<h4 id="Before_and_after_examples">Before and after examples</h4>

<p>Carriage returns added here and there for clarity.</p>

<pre>
// Before: DOM0() template Dekiscript
&lt;p&gt;&lt;span class="lang lang-en" lang="en"&gt;DOM Level 0. Not part of any standard. &lt;/span&gt;
&lt;span class="lang lang-es" lang="es"&gt;DOM Nivel 0. No es parte de ninguna norma. &lt;/span&gt;
&lt;span class="lang lang-*" lang="*"&gt;DOM Level 0. Not part of any standard. &lt;/span&gt;
&lt;span class="lang lang-fr" lang="fr"&gt;DOM Level 0. Ne fait partie d'aucune spécification. &lt;/span&gt;
&lt;span class="lang lang-ja" lang="ja"&gt;DOM Level 0。どの標準にも属しません。 &lt;/span&gt;
&lt;span class="lang lang-pl" lang="pl"&gt;DOM Level 0. Nie jest częścią żadnego standardu. &lt;/span&gt;
&lt;span class="lang lang-zh-cn" lang="zh-cn"&gt;DOM Level 0 不属于任何标准.&lt;/span&gt;&lt;/p&gt;

// After: Kumascript version
&lt;% if ("fr" == env.locale) { %&gt;
&lt;p&gt;DOM Level 0. Ne fait partie d'aucune spécification.&lt;/p&gt;
&lt;% } else if ("ja" == env.locale) { %&gt;
&lt;p&gt;DOM Level 0。どの標準にも属しません。 &lt;/p&gt;
&lt;% } else if ("pl" == env.locale) { %&gt;
&lt;p&gt;DOM Level 0. Nie jest częścią żadnego standardu.&lt;/p&gt;
&lt;% } else if ("es" == env.locale) { %&gt;
&lt;p&gt;DOM Nivel 0. No es parte de ninguna norma.&lt;/p&gt;
&lt;% } else if ("zh-CN" == env.locale) { %&gt;
&lt;p&gt;DOM Level 0 不属于任何标准.&lt;/p&gt;
&lt;% } else { %&gt;
&lt;p&gt;DOM Level 0. Not part of any standard.&lt;/p&gt;
&lt;% } %&gt;</pre>

<pre>
// From ReleaseChannelInfo() template
// Before:
web.html("&lt;p&gt;Firefox " + $0 + ", based on Gecko " + $1 + ", will ship in " + $2 + ".
 This article provides information about the changes in this release that will 
 affect developers. Nightly builds of what will become Firefox " + $0 + " are "
  + web.link(url, "currently available") + " on the " + string.ToUpperFirst($3)
  + " channel.&lt;/p&gt;");

// After:
&lt;p&gt;Firefox &lt;%= $0 %&gt;, based on Gecko &lt;%= $1 %&gt;, will ship in &lt;%= $2 %&gt;. This
 article provides information about the changes in this release that will 
 affect developers. Nightly builds of what will become Firefox &lt;%= $0 %&gt; 
 are &lt;%- web.link(url, "currently available")%&gt;&nbsp; on the
 &lt;%= string.ToUpperFirst($3) %&gt; channel.&lt;/p&gt;</pre>

<pre>
// Before: old Dekiscript snippet
if ($1 &amp;&amp; string.length($1)) {
&nbsp; optionsText = optionsText + "&lt;li&gt;" + LXRSearch("ident", "i", $1) + "&lt;/li&gt;";
}

// After: new Kumascript. Quote parameters to template() unless it is an arg variable (like $1).
if ($1 &amp;&amp; string.length($1)) {
&nbsp;&nbsp;&nbsp; optionsText = optionsText + "&lt;li&gt;" + template("LXRSearch", ["ident", "i", $1]) + "&lt;/li&gt;";
}

// Note that template() within &lt;% ... %&gt; outputs nothing directly. If you want to call another
// template and display its output, use &lt;%= %&gt; or &lt;%- %&gt; like this:
&lt;%- template("LXRSearch", ["ident", "i", $1]) %&gt;</pre>

<h2 id="See_also">See also</h2>

<ul>
 <li><a href="/en-US/docs/Project:Getting_started_with_Kuma" title="Getting started with Kuma">Getting started with Kuma</a></li>
 <li><a href="/en-US/docs/Project:KumaScript_reference" title="Project:en/KumaScript reference">KumaScript reference</a></li>
 <li><a class="link-https" href="https://wiki.mozilla.org/MDN/Kuma" title="https://wiki.mozilla.org/MDN/Kuma">Kuma wiki</a></li>
</ul>
Revert to this revision