tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

template.js (6214B)


      1    /*
      2     * Template code
      3     *
      4     * A template is just a javascript structure. An element is represented as:
      5     *
      6     * [tag_name, {attr_name:attr_value}, child1, child2]
      7     *
      8     * the children can either be strings (which act like text nodes), other templates or
      9     * functions (see below)
     10     *
     11     * A text node is represented as
     12     *
     13     * ["{text}", value]
     14     *
     15     * String values have a simple substitution syntax; ${foo} represents a variable foo.
     16     *
     17     * It is possible to embed logic in templates by using a function in a place where a
     18     * node would usually go. The function must either return part of a template or null.
     19     *
     20     * In cases where a set of nodes are required as output rather than a single node
     21     * with children it is possible to just use a list
     22     * [node1, node2, node3]
     23     *
     24     * Usage:
     25     *
     26     * render(template, substitutions) - take a template and an object mapping
     27     * variable names to parameters and return either a DOM node or a list of DOM nodes
     28     *
     29     * substitute(template, substitutions) - take a template and variable mapping object,
     30     * make the variable substitutions and return the substituted template
     31     *
     32     */
     33 
     34    function is_single_node(template)
     35    {
     36        return typeof template[0] === "string";
     37    }
     38 
     39    function substitute(template, substitutions)
     40    {
     41        if (typeof template === "function") {
     42            var replacement = template(substitutions);
     43            if (replacement)
     44            {
     45                var rv = substitute(replacement, substitutions);
     46                return rv;
     47            }
     48            else
     49            {
     50                return null;
     51            }
     52        }
     53        else if (is_single_node(template))
     54        {
     55            return substitute_single(template, substitutions);
     56        }
     57        else
     58        {
     59            return filter(map(template, function(x) {
     60                                  return substitute(x, substitutions);
     61                              }), function(x) {return x !== null;});
     62        }
     63    }
     64    expose(substitute, "template.substitute");
     65 
     66    function substitute_single(template, substitutions)
     67    {
     68        var substitution_re = /\${([^ }]*)}/g;
     69 
     70        function do_substitution(input) {
     71            var components = input.split(substitution_re);
     72            var rv = [];
     73            for (var i=0; i<components.length; i+=2)
     74            {
     75                rv.push(components[i]);
     76                if (components[i+1])
     77                {
     78                    rv.push(substitutions[components[i+1]]);
     79                }
     80            }
     81            return rv;
     82        }
     83 
     84        var rv = [];
     85        rv.push(do_substitution(String(template[0])).join(""));
     86 
     87        if (template[0] === "{text}") {
     88            substitute_children(template.slice(1), rv);
     89        } else {
     90            substitute_attrs(template[1], rv);
     91            substitute_children(template.slice(2), rv);
     92        }
     93 
     94        function substitute_attrs(attrs, rv)
     95        {
     96            rv[1] = {};
     97            for (name in template[1])
     98            {
     99                if (attrs.hasOwnProperty(name))
    100                {
    101                    var new_name = do_substitution(name).join("");
    102                    var new_value = do_substitution(attrs[name]).join("");
    103                    rv[1][new_name] = new_value;
    104                };
    105            }
    106        }
    107 
    108        function substitute_children(children, rv)
    109        {
    110            for (var i=0; i<children.length; i++)
    111            {
    112                if (children[i] instanceof Object) {
    113                    var replacement = substitute(children[i], substitutions);
    114                    if (replacement !== null)
    115                    {
    116                        if (is_single_node(replacement))
    117                        {
    118                            rv.push(replacement);
    119                        }
    120                        else
    121                        {
    122                            extend(rv, replacement);
    123                        }
    124                    }
    125                }
    126                else
    127                {
    128                    extend(rv, do_substitution(String(children[i])));
    129                }
    130            }
    131            return rv;
    132        }
    133 
    134        return rv;
    135    }
    136 
    137    function make_dom_single(template)
    138    {
    139        if (template[0] === "{text}")
    140        {
    141            var element = document.createTextNode("");
    142            for (var i=1; i<template.length; i++)
    143            {
    144                element.data += template[i];
    145            }
    146        }
    147        else
    148        {
    149            var element = document.createElement(template[0]);
    150            for (name in template[1]) {
    151                if (template[1].hasOwnProperty(name))
    152                {
    153                    element.setAttribute(name, template[1][name]);
    154                }
    155            }
    156            for (var i=2; i<template.length; i++)
    157            {
    158                if (template[i] instanceof Object)
    159                {
    160                    var sub_element = make_dom(template[i]);
    161                    element.appendChild(sub_element);
    162                }
    163                else
    164                {
    165                    var text_node = document.createTextNode(template[i]);
    166                    element.appendChild(text_node);
    167                }
    168            }
    169        }
    170 
    171        return element;
    172    }
    173 
    174 
    175 
    176    function make_dom(template, substitutions)
    177    {
    178        if (is_single_node(template))
    179        {
    180            return make_dom_single(template);
    181        }
    182        else
    183        {
    184            return map(template, function(x) {
    185                           return make_dom_single(x);
    186                       });
    187        }
    188    }
    189 
    190    function render(template, substitutions)
    191    {
    192        return make_dom(substitute(template, substitutions));
    193    }
    194    expose(render, "template.render");
    195 
    196 function expose(object, name)
    197 {
    198  var components = name.split(".");
    199  var target = window;
    200  for (var i=0; i<components.length - 1; i++)
    201  {
    202    if (!(components[i] in target))
    203    {
    204      target[components[i]] = {};
    205    }
    206    target = target[components[i]];
    207  }
    208  target[components[components.length - 1]] = object;
    209 }
    210 
    211 function extend(array, items)
    212 {
    213  Array.prototype.push.apply(array, items);
    214 }