Skip to content

Instantly share code, notes, and snippets.

@Shadowfiend
Forked from mjtko/nativeTemplateEngine.js
Created June 20, 2011 05:23
Show Gist options
  • Save Shadowfiend/1035165 to your computer and use it in GitHub Desktop.
Save Shadowfiend/1035165 to your computer and use it in GitHub Desktop.
knockout "native" template engine
ko.nativeTemplateEngine = function () {
function nodeListToArray(nodeList) {
var array = [];
for (var i = 0; i < nodeList.length; ++i)
array[i] = nodeList[i];
return array;
}
// monkey patch to convert possible NodeList into Array before iterating
ko.utils._setDomNodeChildren = ko.utils.setDomNodeChildren;
ko.utils.setDomNodeChildren = function (domNode, childNodes) {
var childNodesArray = nodeListToArray(childNodes);
ko.utils._setDomNodeChildren(domNode, childNodesArray);
};
// adapted from MooTools.Element
//
// This is necessary to allow us to easily deal with table
// fragment templates.
var setHtml = (function(){
var tableTest = (function() {
try {
var table = document.createElement('table');
table.innerHTML = '<tr><td></td></tr>';
return false;
} catch (e) {
return false;
}
})();
var wrapper = document.createElement('div');
var translations = {
table: [1, '<table>', '</table>'],
select: [1, '<select>', '</select>'],
tbody: [2, '<table><tbody>', '</tbody></table>'],
tr: [3, '<table><tbody><tr>', '</tr></tbody></table>']
};
translations.thead = translations.tfoot = translations.tbody;
var empty = function(target) {
var node;
while ( node = target.firstChild ) {
node.parentNode.removeChild(node);
}
return target;
}
return function(target,html){
var wrap = (!tableTest && translations[target.tagName.toLowerCase()]);
if (wrap){
var first = wrapper;
first.innerHTML = wrap[1] + html + wrap[2];
for (var i = wrap[0]; i--;) first = first.firstChild;
empty(target);
var node;
while (node = first.firstChild) {
target.appendChild(node);
}
} else if (! tableTest) {
// Deal with IE's penchant for dropping script tags if they are
// the first thing in the innerHTML.
var first = wrapper;
first.innerHTML = 'a' + html;
first = first.firstChild;
empty(target);
var node;
while (node = first.nextSibling) {
target.appendChild(node);
}
} else {
target.innerHTML = html;
}
};
})();
var templates = {};
var parseMemoCommentText = function (memoCommentText) {
var match = memoCommentText.match(/^<!--(\[ko_memo\:.*?\])-->$/);
return match ? match[1] : null;
};
var render = function(data,node,target) {
while (node) {
var nodeOut;
if ( node.tagName === 'SCRIPT' &&
node.getAttribute('data-generator') === 'ko.nativeTemplateEngine' ) {
// this needs to be evaluated within the context of
// 'data' in order to get data bindings to work
// correctly when not prefixed with 'data.'
with(data)
nodeOut = document.createComment(parseMemoCommentText(eval(node.innerHTML)));
} else {
nodeOut = node.cloneNode(false);
render(data,node.firstChild,nodeOut);
}
target.appendChild(nodeOut);
node = node.nextSibling;
}
return target;
};
this['getTemplateNode'] = function (template) {
var templateNode = document.getElementById(template);
if (templateNode == null)
throw new Error("Cannot find template with ID=" + template);
return templateNode;
};
this['renderTemplate'] = function (templateId, data, options) {
return nodeListToArray(render(data,templates[templateId].firstChild,document.createElement("div")).childNodes);
};
this['isTemplateRewritten'] = function (templateId) {
return templates[templateId] !== undefined;
};
this['rewriteTemplate'] = function (templateId, rewriterCallback) {
var templateNode = this['getTemplateNode'](templateId);
// elide templateNode from the DOM - no longer needed
templateNode.parentNode.removeChild(templateNode);
// we have to deal with anything that contains <tr>
// separately, as we can't append <tr> elements to a <div>.
//
// references:
// http://stackoverflow.com/questions/5090031/regex-get-tr-tags/5091399#5091399
var tabular = templateNode.innerHTML.match(/<tr[\s\S]*?<\/tr>/g);
var template = document.createElement( ( tabular ? 'tbody' : 'div' ) );
// Use our custom setHtml (adapted from MooTools) in order to
// work around readonly tbody under IE.
//
// references:
// http://stackoverflow.com/questions/4729644/cant-innerhtml-on-tbody-in-ie/4729743#4729743
setHtml(template,rewriterCallback(templateNode.innerHTML));
templates[templateId] = template;
};
this['createJavaScriptEvaluatorBlock'] = function (script) {
return '<script data-generator="ko.nativeTemplateEngine" type="text/javascript">// <![CDATA[\n' + script + '\n// ]]></script>';
};
};
ko.nativeTemplateEngine.prototype = new ko.templateEngine();
// Use this one by default
ko.setTemplateEngine(new ko.nativeTemplateEngine());
ko.exportSymbol('ko.nativeTemplateEngine', ko.nativeTemplateEngine);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment