working optimizer, Session.gettifier for demo

This commit is contained in:
David Greenspan
2013-11-19 11:47:49 -08:00
parent d9db8d81a0
commit af8f5aa7fe
4 changed files with 89 additions and 45 deletions

View File

@@ -13,6 +13,8 @@ HTML = {
EmitCode: makeTagFunc('EmitCode'),
// e.g. `Special({ ... stuff ... })`
Special: makeTagFunc('Special'),
// e.g. `Raw("<br>")`
Raw: makeTagFunc('Raw'),
asciiLowerCase: asciiLowerCase,
properCaseTagName: properCaseTagName,

View File

@@ -90,6 +90,14 @@ var checkComment = function (comment) {
throw new Error("Comment should have exactly one content item, a simple string");
};
// checks that a pseudoDOM node with tagName "Raw" is well-formed.
var checkRaw = function (raw) {
if (raw.attrs)
throw new Error("Raw can't have attributes");
if (raw.length !== 1 || (typeof raw[0] !== 'string'))
throw new Error("Raw should have exactly one content item, a simple string");
};
// checks that a pseudoDOM node with tagName "Comment" is well-formed.
var checkEmitCode = function (node) {
if (node.attrs)
@@ -122,6 +130,9 @@ typeOf = function (node) {
} else if (node.tagName === 'Special') {
checkSpecial(node);
return 'special';
} else if (node.tagName === 'Raw') {
checkRaw(node);
return 'raw';
} else {
return 'tag';
}

View File

@@ -1196,17 +1196,23 @@ Spacebars.parse2 = function (input) {
var optimize = function (tree) {
var pushRawHTML = function (array, html, dontCoallesce) {
if ((! dontCoallesce) && array.length > 0 &&
array[array.length - 1].tagName === 'Raw') {
array[array.length - 1][0] += html;
var pushRawHTML = function (array, html) {
var N = array.length;
if (N > 0 && array[N-1].tagName === 'Raw') {
array[N-1][0] += html;
} else {
array.push(HTML.Raw(html));
}
};
var optimizeArrayParts = function (array, optimizePartsFunc, dontCoallesce) {
var isPureChars = function (html) {
return (html.indexOf('&') < 0 && html.indexOf('<') < 0);
};
var optimizeArrayParts = function (array, optimizePartsFunc, forceOptimize) {
var result = null;
if (forceOptimize)
result = [];
for (var i = 0, N = array.length; i < N; i++) {
var part = optimizePartsFunc(array[i]);
if (part !== null) {
@@ -1215,31 +1221,40 @@ var optimize = function (tree) {
// This is our first special item. Stringify the other parts.
result = [];
for (var j = 0; j < i; j++)
pushRawHTML(result, UI.toHTML(array[j]), dontCoallesce);
pushRawHTML(result, UI.toHTML(array[j]));
}
result.push(part);
} else {
// just plain HTML found
if (result !== null) {
// we've already found something special, so convert this to Raw
pushRawHTML(result, UI.toHTML(array[i]), dontCoallesce);
pushRawHTML(result, UI.toHTML(array[i]));
}
}
}
if (result !== null) {
// clean up unnecessary HTML.Raw wrappers around pure character data
for (var j = 0; j < result.length; j++) {
if (result[j].tagName === 'Raw' &&
isPureChars(result[j][0]))
// replace HTML.Raw with simple string
result[j] = result[j][0];
}
}
return result;
};
var optimizeAttributeValueParts = function (v) {
// If we have nothing special going on, returns `null` (so that the
// parent can optimize). Otherwise returns a replacement for `v`
// with optimized parts.
var doesAttributeValueHaveSpecials = function (v) {
var type = HTML.typeOf(v);
if (type === 'null' || type === 'string' || type === 'charref') {
return null;
return false;
} else if (type === 'special') {
return v;
return true;
} else if (type === 'array') {
return optimizeArrayParts(v, optimizeAttributeValueParts);
for (var i = 0; i < v.length; i++)
if (doesAttributeValueHaveSpecials(v[i]))
return true;
return false;
} else {
throw new Error("Unexpected node in attribute value: " + v);
}
@@ -1262,46 +1277,27 @@ var optimize = function (tree) {
} else if (type === 'tag') {
var mustOptimize = false;
var newChildren = optimizeArrayParts(node, optimizeParts);
var newAttrs = null;
if (node.attrs) {
var attrs = node.attrs;
if (typeof attrs === 'function') {
newAttrs = attrs;
mustOptimize = true;
} else {
var attrNames = [];
var attrValues = [];
_.each(attrs, function (v, n) {
attrNames.push(n);
attrValues.push(v);
});
if (newChildren) {
// forced by special children of tag to optimize now.
// trick optimizeArrayParts into doing that by adding
// a fake attrValue that won't be picked up when we
// iterate over attrNames.
attrValues.push(function () {});
}
var newValues = optimizeArrayParts(attrValues,
optimizeAttributeValueParts,
true);
if (newValues) {
newAttrs = {};
for (var i = 0; i < attrNames.length; i++)
newAttrs[attrNames[i]] = newValues[i];
for (var k in attrs) {
if (doesAttributeValueHaveSpecials(attrs[k])) {
mustOptimize = true;
break;
}
}
}
}
if ((! newAttrs) && (! newChildren))
var newChildren = optimizeArrayParts(node, optimizeParts, mustOptimize);
if (newChildren === null)
return null;
if (! newChildren)
newChildren = [HTML.Raw(UI.toHTML(Array.prototype.slice.call(node)))];
var newTag = HTML.getTag(node.tagName).apply(null, newChildren);
newTag.attrs = newAttrs;
newTag.attrs = node.attrs;
return newTag;
} else if (type === 'array') {
@@ -1321,7 +1317,40 @@ var optimize = function (tree) {
};
};
return optimizeParts(tree) || HTML.Raw(UI.toHTML(tree));
var optTree = optimizeParts(tree);
if (optTree !== null)
// tree was optimized in parts
return optTree;
optTree = HTML.Raw(UI.toHTML(tree));
if (isPureChars(optTree[0]))
return optTree[0];
return optTree;
};
var specialsToSessionGet = function (node) {
if (UI.isComponent(node)) {
return node;
} else {
var type = HTML.typeOf(node);
if (type === 'tag') {
// potential optimization: don't always create a new tag
var newChildren = _.map(Array.prototype.slice.call(node), specialsToSessionGet);
var newTag = HTML.getTag(node.tagName).apply(null, newChildren);
newTag.attrs = node.attrs;
return newTag;
} else if (type === 'array') {
return _.map(node, specialsToSessionGet);
} else if (type === 'special') {
if (node.attrs.type !== 'DOUBLE')
return node;
return HTML.EmitCode('function () { return Session.get("' + node.attrs.path.join('.') + '"); }');
} else {
return node;
}
};
};
Spacebars.compile2 = function (input) {
@@ -1335,6 +1364,8 @@ Spacebars.compile2 = function (input) {
tree = optimize(tree);
tree = specialsToSessionGet(tree);
var code = '(function () { var self = this; return ';
code += UI.toCode(tree);

View File

@@ -594,7 +594,7 @@ var toCode = function (node) {
var type = HTML.typeOf(node);
if (type === 'emitcode') {
result += node[0];
} else if (type === 'comment' || type === 'charref' ||
} else if (type === 'comment' || type === 'charref' || type === 'raw' ||
type === 'tag' || type === 'special') {
var isNonTag = (type !== 'tag');