[google-jstemplate commit] r8 - trunk

7 views
Skip to first unread message

codesite...@google.com

unread,
Jul 3, 2008, 12:34:41 PM7/3/08
to google-j...@googlegroups.com
Author: me...@google.com
Date: Thu Jul 3 09:34:11 2008
New Revision: 8

Modified:
trunk/jsevalcontext.js
trunk/jstemplate.js
trunk/jstemplate_test.js

Log:
integrate latest changes

Modified: trunk/jsevalcontext.js
==============================================================================
--- trunk/jsevalcontext.js (original)
+++ trunk/jsevalcontext.js Thu Jul 3 09:34:11 2008
@@ -27,6 +27,15 @@
var VAR_index = '$index';
var VAR_this = '$this';
var VAR_context = '$context';
+var VAR_top = '$top';
+
+
+/**
+ * The name of the global variable which holds the value to be
returned if
+ * context evaluation results in an error.
+ * Use JsEvalContext.setGlobal(GLOB_default, value) to set this.
+ */
+var GLOB_default = '$default';


/**
@@ -50,9 +59,9 @@
* context is the object whose property the parent object is. Null for the
* context of the root object.
*
- * @constructor
+ * @see jsevalcontext.h.js
*/
-function JsEvalContext(opt_data, opt_parent) {
+JsEvalContext.prototype.constructor_ = function(opt_data, opt_parent) {
var me = this;

/**
@@ -87,7 +96,7 @@
* @type Object
*/
me.vars_[VAR_this] = opt_data;
-
+
/**
* The entire context structure is exposed as a variable so it can be
* passed to javascript invocations through jseval.
@@ -108,7 +117,14 @@
* @type {Object|null}
*/
me.data_ = getDefaultObject(opt_data, STRING_empty);
-}
+
+ if (!opt_parent) {
+ // If this is a top-level context, create a variable reference to
the data
+ // to allow for accessing top-level properties of the original context
+ // data from child contexts.
+ me.vars_[VAR_top] = me.data_;
+ }
+};


/**
@@ -127,7 +143,7 @@
* global variables in general apply also here. (Hence the name
* "global", and not "global var".)
* @param {string} name
- * @param {Object} value
+ * @param {Object|null} value
*/
JsEvalContext.setGlobal = function(name, value) {
JsEvalContext.globals_[name] = value;
@@ -135,6 +151,13 @@


/**
+ * Set the default value to be returned if context evaluation results
in an
+ * error. (This can occur if a non-existent value was requested).
+ */
+JsEvalContext.setGlobal(GLOB_default, null);
+
+
+/**
* A cache to reuse JsEvalContext instances. (IE6 perf)
*
* @type Array.<JsEvalContext>
@@ -196,7 +219,7 @@
} catch (e) {
log('jsexec EXCEPTION: ' + e + ' at ' + template +
' with ' + exprFunction);
- return null;
+ return JsEvalContext.globals_[GLOB_default];
}
};

@@ -254,7 +277,7 @@
/**
* Evaluates a string expression within the scope of this context
* and returns the result.
- *
+ *
* @param {string} expr A javascript expression
* @param {Element} opt_template An optional node to serve as "this"
*

Modified: trunk/jstemplate.js
==============================================================================
--- trunk/jstemplate.js (original)
+++ trunk/jstemplate.js Thu Jul 3 09:34:11 2008
@@ -71,6 +71,7 @@
var CHAR_asterisk = '*';
var CHAR_dollar = '$';
var CHAR_period = '.';
+var CHAR_ampersand = '&';
var STRING_div = 'div';
var STRING_id = 'id';
var STRING_asteriskzero = '*0';
@@ -154,7 +155,6 @@
*/
JstProcessor.jstcache_ = {};

-
/**
* The neutral cache entry. Used for all nodes that don't have any
* jst attributes. We still set the jsid attribute on those nodes so
@@ -168,6 +168,37 @@


/**
+ * Map from concatenated attribute string to jstid.
+ * The key is the concatenation of all jst atributes found on a node
+ * formatted as "name1=value1&name2=value2&...", in the order defined by
+ * JST_ATTRIBUTES. The value is the id of the jstcache_ entry that can
+ * be used for this node. This allows the reuse of cache entries in cases
+ * when a cached entry already exists for a given combination of attribute
+ * values. (For example when two different nodes in a template share
the same
+ * JST attributes.)
+ * @type Object
+ */
+JstProcessor.jstcacheattributes_ = {};
+
+
+/**
+ * Map for storing temporary attribute values in prepareNode_() so
they don't
+ * have to be retrieved twice. (IE6 perf)
+ * @type Object
+ */
+JstProcessor.attributeValues_ = {};
+
+
+/**
+ * A list for storing non-empty attributes found on a node in prepareNode_().
+ * The array is global since it can be reused - this way there is no
need to
+ * construct a new array object for each invocation. (IE6 perf)
+ * @type Array
+ */
+JstProcessor.attributeList_ = [];
+
+
+/**
* Prepares the template: preprocesses all jstemplate attributes.
*
* @param {Element} template
@@ -211,23 +242,61 @@
* returns an object with no properties (the jscache_[0] entry).
*/
JstProcessor.prepareNode_ = function(node) {
+ // If the node already has a cache property, return it.
if (node[PROP_jstcache]) {
return node[PROP_jstcache];
}

- // NOTE(mesch): We avoid gratuitous object creation here, and only
- // create an instance if we set properties on it. (IE6 perf)
- var jstcache = null;
+ // If it is not found, we always set the PROP_jstcache property on
the node.
+ // Accessing the property is faster than executing getAttribute().
If we
+ // don't find the property on a node that was cloned in
jstSelect_(), we
+ // will fall back to check for the attribute and set the property
+ // from cache.
+
+ // If the node has an attribute indexing a cache object, set it as a property
+ // and return it.
+ var jstid = domGetAttribute(node, ATT_jstcache);
+ if (jstid != null) {
+ return node[PROP_jstcache] = JstProcessor.jstcache_[jstid];
+ }
+
+ var attributeValues = JstProcessor.attributeValues_;
+ var attributeList = JstProcessor.attributeList_;
+ attributeList.length = 0;

+ // Look for interesting attributes.
+ for (var i = 0, I = jsLength(JST_ATTRIBUTES); i < I; ++i) {
+ var name = JST_ATTRIBUTES[i][0];
+ var value = domGetAttribute(node, name);
+ attributeValues[name] = value;
+ if (value != null) {
+ attributeList.push(name + "=" + value);
+ }
+ }
+
+ // If none found, mark this node to prevent further inspection, and return
+ // an empty cache object.
+ if (attributeList.length == 0) {
+ domSetAttribute(node, ATT_jstcache, STRING_zero);
+ return node[PROP_jstcache] = JstProcessor.jstcache_[0];
+ }
+
+ // If we already have a cache object corresponding to these attributes,
+ // annotate the node with it, and return it.
+ var attstring = attributeList.join(CHAR_ampersand);
+ if (jstid = JstProcessor.jstcacheattributes_[attstring]) {
+ domSetAttribute(node, ATT_jstcache, jstid);
+ return node[PROP_jstcache] = JstProcessor.jstcache_[jstid];
+ }
+
+ // Otherwise, build a new cache object.
+ var jstcache = {};
for (var i = 0, I = jsLength(JST_ATTRIBUTES); i < I; ++i) {
var att = JST_ATTRIBUTES[i];
var name = att[0];
var parse = att[1];
- var value = domGetAttribute(node, name);
+ var value = attributeValues[name];
if (value != null) {
- if (!jstcache) {
- jstcache = {};
- }
jstcache[name] = parse(value);
if (MAPS_DEBUG) {
jstcache.jstAttributeValues = jstcache.jstAttributeValues || {};
@@ -236,22 +305,12 @@
}
}

- if (jstcache) {
- var jstid = STRING_empty + ++JstProcessor.jstid_;
- domSetAttribute(node, ATT_jstcache, jstid);
- JstProcessor.jstcache_[jstid] = jstcache;
- } else {
- domSetAttribute(node, ATT_jstcache, STRING_zero);
- jstcache = JstProcessor.jstcache_[0];
- }
+ jstid = STRING_empty + ++JstProcessor.jstid_;
+ domSetAttribute(node, ATT_jstcache, jstid);
+ JstProcessor.jstcache_[jstid] = jstcache;
+ JstProcessor.jstcacheattributes_[attstring] = jstid;

- // We always set the PROP_jstcache property on the node. Accessing
- // the property is faster than executing getAttribute(). If we don't
- // find the property on a node that was cloned in jstSelect_(), we
- // will fall back to check for the attribute and set the property
- // from cache.
- assert(jstcache);
- return node[PROP_jstcache] = /** @type Object */(jstcache);
+ return node[PROP_jstcache] = jstcache;
};


@@ -657,7 +716,7 @@
JstProcessor.prototype.jstVars_ = function(context, template, values) {
for (var i = 0, I = jsLength(values); i < I; i += 2) {
var label = values[i];
- var value = /** @type {Object?} */(context.jsexec(values[i+1], template));
+ var value = context.jsexec(values[i+1], template);
context.setVariable(label, value);
}
};
@@ -685,7 +744,7 @@
JstProcessor.prototype.jstValues_ = function(context, template,
values) {
for (var i = 0, I = jsLength(values); i < I; i += 2) {
var label = values[i];
- var value = /** @type {Object?} */(context.jsexec(values[i+1], template));
+ var value = context.jsexec(values[i+1], template);

if (label.charAt(0) == CHAR_dollar) {
// A jsvalues entry whose name starts with $ sets a local
@@ -832,7 +891,7 @@
*/
function jstGetTemplateOrDie(name, opt_loadHtmlFn) {
var x = jstGetTemplate(name, opt_loadHtmlFn);
- assert(x != null);
+ check(x != null);
return /** @type Element */(x);
}


Modified: trunk/jstemplate_test.js
==============================================================================
--- trunk/jstemplate_test.js (original)
+++ trunk/jstemplate_test.js Thu Jul 3 09:34:11 2008
@@ -262,13 +262,13 @@
}
// Success
element = jstGetTemplate('template',
- curryMethod(returnHtmlWithId, 'template'));
+ partial(returnHtmlWithId, 'template'));
assertTrue("Expected jstGetTemplate('template') to return a dom element",
!!element);

// Failure
element = jstGetTemplate('asdf',
- curryMethod(returnHtmlWithId, 'zxcv'));
+ partial(returnHtmlWithId, 'zxcv'));
assertFalse("Expected jstGetTemplate('zxcv') to return null",
!!element);
}
@@ -324,4 +324,16 @@
assertEquals(1, context.getVariable('$baz'));
assertTrue(context.getVariable('bar'));
assertUndefined(context.getVariable('foobar'));
+}
+
+
+function testCacheReuse() {
+ var template = document.createElement('div');
+ document.body.appendChild(template);
+ template.innerHTML =
+ '<div jsvars="foo:\'foo\';bar:true;$baz:1"></div>' +
+ '<span jsvars="foo:\'foo\';bar:true;$baz:1"></span>';
+ JstProcessor.prepareTemplate_(template);
+ assertEquals(template.firstChild.getAttribute(ATT_jstcache),
+ template.lastChild.getAttribute(ATT_jstcache));
}

Reply all
Reply to author
Forward
0 new messages