-
-
Save Shadowfiend/1035165 to your computer and use it in GitHub Desktop.
knockout "native" template engine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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