prepare for reactive attributes

This commit is contained in:
David Greenspan
2013-10-29 20:08:15 -07:00
parent de71913aa8
commit 91eedfb8ec
2 changed files with 65 additions and 3 deletions

View File

@@ -6,6 +6,8 @@
// Tags are created using tag functions, e.g. `DIV(P({id:'foo'}, "Hello"))`.
// If a tag function is given a first argument that is an object and not a
// Tag or an array, that object is used as element attributes (`attrs`).
// The attrs argument may be of the form `{$attrs: function () { ... }}` in
// which case the function becomes the `attrs` attribute of the tag.
//
// Tag functions for all known tags are available as `UI.Tag.DIV`,
// `UI.Tag.SPAN`, etc., and you can define new ones with
@@ -36,7 +38,11 @@ var makeTagFunc = function (name) {
// see whether first argument is truthy and not an Array or Tag
var attrsGiven = (optAttrs && (typeof optAttrs === 'object') &&
(typeof optAttrs.splice !== 'function'));
var tag = new Tag(attrsGiven ? optAttrs : null);
var attrs = (attrsGiven ? optAttrs : null);
if (attrsGiven && (typeof attrs.$attrs === 'funciton'))
attrs = attrs.$attrs;
var tag = new Tag(attrs);
tag.push.apply(tag, (attrsGiven ?
Array.prototype.slice.call(arguments, 1) :
arguments));
@@ -112,15 +118,21 @@ var materialize = function (node, parent, before) {
// Tag or array
if (node.tagName) {
if (node.tagName === 'CharRef') {
checkCharRef(node);
insert(document.createTextNode(node.attrs.str), parent, before);
} else if (node.tagName === 'Comment') {
checkComment(node);
insert(document.createComment(sanitizeComment(node[0])), parent, before);
} else if (node.tagName === 'EmitCode') {
throw new Error("EmitCode node can only be processed by toCode");
} else {
var elem = document.createElement(node.tagName);
if (node.attrs) {
_.each(node.attrs, function (v, k) {
var attrs = node.attrs;
// XXX make attributes reactive!
if (typeof attrs === 'function')
attrs = attrs();
_.each(attrs, function (v, k) {
elem.setAttribute(k, attributeValueToString(v));
});
}
@@ -234,6 +246,29 @@ var attributeValueToCode = function (v) {
}
};
// checks that a pseudoDOM node with tagName "CharRef" is well-formed.
var checkCharRef = function (charRef) {
if (typeof charRef.attrs === 'function')
throw new Error("Can't have a reactive character reference (CharRef)");
var attrs = charRef.attrs;
if ((! attrs) || (typeof attrs.html !== 'string') ||
(typeof attrs.str !== 'string') || (! attrs.html) || (! attrs.str))
throw new Error("CharRef should have simple string attributes " +
"`html` and `str`.");
if (charRef.length)
throw new Error("CharRef should have no content");
};
// checks that a pseudoDOM node with tagName "Comment" is well-formed.
var checkComment = function (comment) {
if (comment.attrs)
throw new Error("Comment can't have attributes");
if (comment.length !== 1 || (typeof comment[0] !== 'string'))
throw new Error("Comment should have exactly one content item, a simple string");
};
// Convert the pseudoDOM `node` into static HTML.
var toHTML = function (node) {
var result = "";
@@ -244,8 +279,10 @@ var toHTML = function (node) {
if (node.tagName) {
// Tag
if (node.tagName === 'CharRef') {
checkCharRef(node);
result += node.attrs.html;
} else if (node.tagName === 'Comment') {
checkComment(node);
result += '<!--' + sanitizeComment(node[0]) + '-->';
} else if (node.tagName === 'EmitCode') {
throw new Error("EmitCode node can only be processed by toCode");
@@ -253,7 +290,10 @@ var toHTML = function (node) {
var casedTagName = properCaseTagName(node.tagName);
result += '<' + casedTagName;
if (node.attrs) {
_.each(node.attrs, function (v, k) {
var attrs = node.attrs;
if (typeof attrs === 'function')
attrs = attrs();
_.each(attrs, function (v, k) {
result += ' ' + k + '=' + attributeValueToQuotedString(v);
});
}
@@ -325,6 +365,11 @@ var toCode = function (node) {
if (node.tagName === 'EmitCode') {
result += node[0];
} else {
if (node.tagName === 'Comment')
checkComment(node);
else if (node.tagName === 'CharRef')
checkCharRef(node);
result += 'UI.Tag.' + node.tagName + '(';
var argStrs = [];
if (node.attrs) {

View File

@@ -68,6 +68,7 @@ Tinytest.add("ui - render2 - basic", function (test) {
Tinytest.add("ui - render2 - closures", function (test) {
// Reactively change a text node
(function () {
var R = ReactiveVar('Hello');
var test1 = P(function () { return R.get(); });
@@ -83,4 +84,20 @@ Tinytest.add("ui - render2 - closures", function (test) {
test.equal(canonicalizeHtml(div.innerHTML), "<p>World</p>");
})();
// Reactively change an array of text nodes
(function () {
var R = ReactiveVar(['Hello', ' World']);
var test1 = P(function () { return R.get(); });
test.equal(toHTML(test1), '<p>Hello World</p>');
var div = document.createElement("DIV");
materialize(test1, div);
test.equal(canonicalizeHtml(div.innerHTML), "<p>Hello World</p>");
R.set(['Goodbye', ' World']);
Deps.flush();
test.equal(canonicalizeHtml(div.innerHTML), "<p>Goodbye World</p>");
})();
});