mirror of
https://github.com/servo/servo.git
synced 2025-06-24 00:54:32 +01:00
214 lines
No EOL
6.1 KiB
JavaScript
214 lines
No EOL
6.1 KiB
JavaScript
/*
|
|
* Template code
|
|
*
|
|
* A template is just a javascript structure. An element is represented as:
|
|
*
|
|
* [tag_name, {attr_name:attr_value}, child1, child2]
|
|
*
|
|
* the children can either be strings (which act like text nodes), other templates or
|
|
* functions (see below)
|
|
*
|
|
* A text node is represented as
|
|
*
|
|
* ["{text}", value]
|
|
*
|
|
* String values have a simple substitution syntax; ${foo} represents a variable foo.
|
|
*
|
|
* It is possible to embed logic in templates by using a function in a place where a
|
|
* node would usually go. The function must either return part of a template or null.
|
|
*
|
|
* In cases where a set of nodes are required as output rather than a single node
|
|
* with children it is possible to just use a list
|
|
* [node1, node2, node3]
|
|
*
|
|
* Usage:
|
|
*
|
|
* render(template, substitutions) - take a template and an object mapping
|
|
* variable names to parameters and return either a DOM node or a list of DOM nodes
|
|
*
|
|
* substitute(template, substitutions) - take a template and variable mapping object,
|
|
* make the variable substitutions and return the substituted template
|
|
*
|
|
*/
|
|
|
|
function is_single_node(template)
|
|
{
|
|
return typeof template[0] === "string";
|
|
}
|
|
|
|
function substitute(template, substitutions)
|
|
{
|
|
if (typeof template === "function") {
|
|
var replacement = template(substitutions);
|
|
if (replacement)
|
|
{
|
|
var rv = substitute(replacement, substitutions);
|
|
return rv;
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
else if (is_single_node(template))
|
|
{
|
|
return substitute_single(template, substitutions);
|
|
}
|
|
else
|
|
{
|
|
return filter(map(template, function(x) {
|
|
return substitute(x, substitutions);
|
|
}), function(x) {return x !== null;});
|
|
}
|
|
}
|
|
expose(substitute, "template.substitute");
|
|
|
|
function substitute_single(template, substitutions)
|
|
{
|
|
var substitution_re = /\${([^ }]*)}/g;
|
|
|
|
function do_substitution(input) {
|
|
var components = input.split(substitution_re);
|
|
var rv = [];
|
|
for (var i=0; i<components.length; i+=2)
|
|
{
|
|
rv.push(components[i]);
|
|
if (components[i+1])
|
|
{
|
|
rv.push(substitutions[components[i+1]]);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
var rv = [];
|
|
rv.push(do_substitution(String(template[0])).join(""));
|
|
|
|
if (template[0] === "{text}") {
|
|
substitute_children(template.slice(1), rv);
|
|
} else {
|
|
substitute_attrs(template[1], rv);
|
|
substitute_children(template.slice(2), rv);
|
|
}
|
|
|
|
function substitute_attrs(attrs, rv)
|
|
{
|
|
rv[1] = {};
|
|
for (name in template[1])
|
|
{
|
|
if (attrs.hasOwnProperty(name))
|
|
{
|
|
var new_name = do_substitution(name).join("");
|
|
var new_value = do_substitution(attrs[name]).join("");
|
|
rv[1][new_name] = new_value;
|
|
};
|
|
}
|
|
}
|
|
|
|
function substitute_children(children, rv)
|
|
{
|
|
for (var i=0; i<children.length; i++)
|
|
{
|
|
if (children[i] instanceof Object) {
|
|
var replacement = substitute(children[i], substitutions);
|
|
if (replacement !== null)
|
|
{
|
|
if (is_single_node(replacement))
|
|
{
|
|
rv.push(replacement);
|
|
}
|
|
else
|
|
{
|
|
extend(rv, replacement);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
extend(rv, do_substitution(String(children[i])));
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
function make_dom_single(template)
|
|
{
|
|
if (template[0] === "{text}")
|
|
{
|
|
var element = document.createTextNode("");
|
|
for (var i=1; i<template.length; i++)
|
|
{
|
|
element.data += template[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var element = document.createElement(template[0]);
|
|
for (name in template[1]) {
|
|
if (template[1].hasOwnProperty(name))
|
|
{
|
|
element.setAttribute(name, template[1][name]);
|
|
}
|
|
}
|
|
for (var i=2; i<template.length; i++)
|
|
{
|
|
if (template[i] instanceof Object)
|
|
{
|
|
var sub_element = make_dom(template[i]);
|
|
element.appendChild(sub_element);
|
|
}
|
|
else
|
|
{
|
|
var text_node = document.createTextNode(template[i]);
|
|
element.appendChild(text_node);
|
|
}
|
|
}
|
|
}
|
|
|
|
return element;
|
|
}
|
|
|
|
|
|
|
|
function make_dom(template, substitutions)
|
|
{
|
|
if (is_single_node(template))
|
|
{
|
|
return make_dom_single(template);
|
|
}
|
|
else
|
|
{
|
|
return map(template, function(x) {
|
|
return make_dom_single(x);
|
|
});
|
|
}
|
|
}
|
|
|
|
function render(template, substitutions)
|
|
{
|
|
return make_dom(substitute(template, substitutions));
|
|
}
|
|
expose(render, "template.render");
|
|
|
|
function expose(object, name)
|
|
{
|
|
var components = name.split(".");
|
|
var target = window;
|
|
for (var i=0; i<components.length - 1; i++)
|
|
{
|
|
if (!(components[i] in target))
|
|
{
|
|
target[components[i]] = {};
|
|
}
|
|
target = target[components[i]];
|
|
}
|
|
target[components[components.length - 1]] = object;
|
|
}
|
|
|
|
function extend(array, items)
|
|
{
|
|
Array.prototype.push.apply(array, items);
|
|
} |