Skip to content

Instantly share code, notes, and snippets.

@peterchon
Forked from jcgregorio/How_to_use.html
Last active August 29, 2015 14:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save peterchon/9c6419cf10b4ef99f342 to your computer and use it in GitHub Desktop.
Save peterchon/9c6419cf10b4ef99f342 to your computer and use it in GitHub Desktop.
HTML Templating using the HTML <template> element and exactly 100 lines of JS.
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<script src="templating.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>
<template id=t>
<div>
<p><a href="{{ url }}">{{ foo.bar.baz }} {{ quux }}</a>!</p>
<!--
Loop over arrays. Use data attributes of
data-repeat-[name]="{{ x.y.z }}" and reference
the values iterated over using {{ [name] }}.
For arrays the 'i' state variable is also set.
-->
<ul data-repeat-num="{{ list }}">
<li>{{ num }} {{ i }}</li>
</ul>
<!--
Loop over Objects. For objects the 'key' state variable is also set.
-->
<ul data-repeat-o="{{ anobj }}">
<li>{{ key }}={{ o.name }}</li>
</ul>
</div>
</template>
<script type="text/javascript" charset="utf-8">
var clone = document.importNode(document.querySelector('#t').content, true);
data = {
foo: { bar: { baz: "Hello"}},
quux: "World",
list: ["a", "b", "c"],
anobj: {
foo: {name: "Fred"},
bar: {name: "Barney"}
},
url: "http://example.com",
};
Expand(clone, data);
document.body.appendChild(clone);
</script>
</body>
</html>
// Copyright (c) 2014 Google Inc. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
(function(ns) {
var root = ns;
var re = /{{\s([\w\.]+)\s}}/;
function filterState(address, state) {
var mystate = state;
address.forEach(function(a) {
if (mystate.hasOwnProperty(a)) {
mystate = mystate[a];
} else {
throw a + " is not a valid property of " + JSON.stringify(mystate);
}
});
return mystate;
}
function ssplice(str, index, count, add) {
return str.slice(0, index) + add + str.slice(index + count);
}
function addressOf(s) {
if ((match = re.exec(s)) != null) {
return match[1].split(".");
} else {
return null;
}
}
function expandString(s, state) {
var match;
var found = false;
while ((match = re.exec(s)) != null) {
found = true;
address = match[1].split(".");
m = filterState(address, state);
s = ssplice(s, match.index, match[0].length, m);
}
if (found) {
return s;
}
return null;
}
function expand(e, state) {
if (e.nodeName == "#text") {
m = expandString(e.textContent, state);
if (m != null) {
e.textContent = m;
}
}
if (e.attributes != undefined) {
for (var i=0; i<e.attributes.length; i++) {
var attr = e.attributes[i];
if (attr.name.indexOf('data-repeat') === 0) {
var parts = attr.name.split('-');
if (parts.length != 3) {
throw "Repeat format is data-repeat-[name]. Got " + attr.name;
}
var name = parts[2];
var tpl = e.removeChild(e.firstElementChild);
var address = addressOf(attr.value);
if (address == null) {
throw attr.value + " doesn't contain an address.";
}
var childState = filterState(address, state);
if ('forEach' in childState) {
childState.forEach(function(item, i) {
var cl = tpl.cloneNode(true);
var instanceState = {};
instanceState[name] = item;
instanceState["i"] = i;
expand(cl, instanceState);
e.appendChild(cl);
});
} else {
Object.keys(childState).forEach(function(key) {
var cl = tpl.cloneNode(true);
var instanceState = {};
instanceState[name] = childState[key];
instanceState["key"] = key;
expand(cl, instanceState);
e.appendChild(cl);
});
}
} else {
m = expandString(attr.value, state);
if (m != null) {
e[attr.name] = m;
}
}
}
}
for (var i=0; i<e.childNodes.length; i++) {
expand(e.childNodes[i], state);
}
}
root.Expand = expand;
})(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment