OLD | NEW |
(Empty) | |
| 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 tem
plates 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 vari
able 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 DO
M nodes |
| 28 * |
| 29 * substitute(template, substitutions) - take a template and variable mappin
g 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 } |
OLD | NEW |