Author: awiner
Date: Fri May 29 22:06:03 2009
New Revision: 780123
URL: http://svn.apache.org/viewvc?rev=780123&view=rev
Log:
Enabling server-to-client handoff for @autoUpdate. Patch from Lev Epshteyn.
Modified:
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data-context/datacontext.js
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/container.js
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java
Modified:
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data-context/datacontext.js
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data-context/datacontext.js?rev=780123&r1=780122&r2=780123&view=diff
==============================================================================
---
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data-context/datacontext.js
(original)
+++
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-data-context/datacontext.js
Fri May 29 22:06:03 2009
@@ -60,8 +60,9 @@
* @param {Function(Array.<string>)} callback Function to call when a
* listener is fired.
* @param {booelan} oneTimeListener Remove this listener after first
callback?
+ * @param {boolean} fireIfReady Instantly fire this if all data is available?
*/
- var registerListener = function(keys, callback, oneTimeListener) {
+ var registerListener = function(keys, callback, oneTimeListener,
fireIfReady) {
var oneTime = !!oneTimeListener;
var listener = {keys : {}, callback : callback, oneTime: oneTime};
@@ -79,7 +80,7 @@
listeners.push(listener);
// Check to see if this one should fire immediately.
- if (keys !== '*' && isDataReady(listener.keys)) {
+ if (fireIfReady && keys !== '*' && isDataReady(listener.keys)) {
window.setTimeout(function() {
maybeFireListener(listener, keys);
}, 1);
@@ -179,7 +180,7 @@
* listener is fired.
*/
registerListener : function(keys, callback) {
- registerListener(keys, callback, false);
+ registerListener(keys, callback, false, true);
},
/**
@@ -190,7 +191,18 @@
* @param {Function(Array.<string>)} callback Function to call when a
*/
registerOneTimeListener_ : function(keys, callback) {
- registerListener(keys, callback, true);
+ registerListener(keys, callback, true, true);
+ },
+
+ /**
+ * Private version of registerListener which allows listeners to be
+ * registered that do not fire initially, but only after a data change.
+ * Exposed because needed by opensocial-templates.
+ * @param {string|Array.<string>} keys Key or set of keys to listen on.
+ * @param {Function(Array.<string>)} callback Function to call when a
+ */
+ registerDeferredListener_ : function(keys, callback) {
+ registerListener(keys, callback, false, false);
},
/**
Modified:
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/container.js
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/container.js?rev=780123&r1=780122&r2=780123&view=diff
==============================================================================
---
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/container.js
(original)
+++
incubator/shindig/trunk/features/src/main/javascript/features/opensocial-templates/container.js
Fri May 29 22:06:03 2009
@@ -182,7 +182,7 @@
for (var i = 0; i < nodes.length; ++i) {
var node = nodes[i];
if (os.Container.isTemplateType_(node.type)) {
- var name = node.getAttribute('tag') || node.getAttribute('name');
+ var name = node.getAttribute('tag');
if (!name || name.length < 0) {
var template = os.compileTemplate(node, name);
if (template) {
@@ -220,11 +220,13 @@
var template = inlined[i].template;
var node = inlined[i].node;
var id = '_T_' + template.id;
+ var rendered = true;
var el = doc.getElementById(id);
if (!el) {
el = doc.createElement('div');
el.setAttribute('id', id);
node.parentNode.insertBefore(el, node);
+ rendered = false;
}
// Only honor @before and @require attributes if the opensocial-data
@@ -247,9 +249,13 @@
var keys = requiredData.split(/[\, ]+/);
var callback = os.Container.createRenderClosure(template, el);
if ("true" == node.getAttribute("autoUpdate")) {
- opensocial.data.DataContext.registerListener(keys, callback);
+ if (rendered) {
+ opensocial.data.getDataContext().registerDeferredListener_(keys,
callback);
+ } else {
+ opensocial.data.getDataContext().registerListener(keys, callback);
+ }
} else {
- opensocial.data.DataContext.registerOneTimeListener_(keys, callback);
+ opensocial.data.getDataContext().registerOneTimeListener_(keys,
callback);
}
} else {
template.renderInto(el, null, context);
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java?rev=780123&r1=780122&r2=780123&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java
Fri May 29 22:06:03 2009
@@ -86,6 +86,9 @@
/** Specifies what template libraries to load */
static final Object REQUIRE_LIBRARY_PARAM = "requireLibrary";
+
+ /** Enable client support? **/
+ static final Object CLIENT_SUPPORT_PARAM = "client";
static private final Logger logger =
Logger.getLogger(TemplateRewriter.class.getName());
@@ -206,6 +209,15 @@
TemplateContext templateContext = new TemplateContext(gadget,
content.getPipelinedData());
boolean needsFeature = executeTemplates(templateContext, content,
templates, registry);
+ // Check if a feature param overrides our guess at whether the
client-side
+ // feature is needed.
+ String clientOverride = f.getParams().get(CLIENT_SUPPORT_PARAM);
+ if ("true".equalsIgnoreCase(clientOverride)) {
+ needsFeature = true;
+ } else if ("false".equalsIgnoreCase(clientOverride)) {
+ needsFeature = false;
+ }
+
Element head = (Element) DomUtil.getFirstNamedChildNode(
content.getDocument().getDocumentElement(), "head");
postProcess(templateContext, needsFeature, head, templates, libraries);
@@ -351,12 +363,10 @@
boolean needsFeature = false;
List<Element> templates = Lists.newArrayList();
for (Element element : allTemplates) {
- String name = element.getAttribute("name");
String tag = element.getAttribute("tag");
String require = element.getAttribute("require");
- if (!"".equals(name) ||
- !checkRequiredData(require, pipelinedData.keySet())) {
+ if (!checkRequiredData(require, pipelinedData.keySet())) {
// Can't be processed on the server at all; keep client-side
processing
needsFeature = true;
} else if ("".equals(tag)) {
@@ -371,19 +381,25 @@
gadget.getContext().getLocale(),
gadget.getContext().getIgnoreCache());
MessageELResolver messageELResolver = new MessageELResolver(expressions,
bundle);
+ int autoUpdateID = 0;
for (Element template : templates) {
DocumentFragment result = processor.get().processTemplate(
template, templateContext, messageELResolver, registry);
- template.getParentNode().insertBefore(result, template);
// TODO: sanitized renders should ignore this value
if ("true".equals(template.getAttribute("autoUpdate"))) {
// autoUpdate requires client-side processing.
- // TODO: give client-side processing some hope of finding the
pre-rendered content
needsFeature = true;
+ Element span = template.getOwnerDocument().createElement("span");
+ String id = "template_auto" + (autoUpdateID++);
+ span.setAttribute("id", "_T_" + id);
+ template.setAttribute("name", id);
+ template.getParentNode().insertBefore(span, template);
+ span.appendChild(result);
} else {
+ template.getParentNode().insertBefore(result, template);
template.getParentNode().removeChild(template);
}
- }
+ }
MutableContent.notifyEdit(content.getDocument());
}
return needsFeature;
Modified:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java?rev=780123&r1=780122&r2=780123&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java
Fri May 29 22:06:03 2009
@@ -81,9 +81,6 @@
private static final String CONTENT_REQUIRE_MISSING =
"<script type='text/os-template' require='foo'>Hello,
${user.name}</script>";
- private static final String CONTENT_WITH_NAME =
- "<script type='text/os-template' name='myTemplate'>Hello,
${user.name}</script>";
-
private static final String CONTENT_WITH_TAG =
"<script type='text/os-template' xmlns:foo='#foo' tag='foo:Bar'>Hello,
${user.name}</script>";
@@ -161,14 +158,7 @@
testExpectingNoTransform(getGadgetXml(CONTENT_REQUIRE_MISSING), "missing
data");
testFeatureNotRemoved();
}
-
- @Test
- public void nameAttributePresent() throws Exception {
- // Don't render templates with a @name
- testExpectingNoTransform(getGadgetXml(CONTENT_WITH_NAME), "with @name");
- testFeatureNotRemoved();
- }
-
+
@Test
public void tagAttributePresent() throws Exception {
// Don't render templates with a @tag
@@ -192,6 +182,8 @@
content.getContent().indexOf("Hello, John") > 0);
assertTrue("Template tag was removed",
content.getContent().contains("text/os-template"));
+ assertTrue("ID span was not created",
+ content.getContent().contains("<span id=\"_T_template_auto0\">"));
testFeatureNotRemoved();
}
@@ -247,6 +239,17 @@
testFeatureRemoved();
}
+ @Test
+ public void testClientOverride() throws Exception {
+ // Should normally remove feature
+ testExpectingTransform(getGadgetXml(CONTENT_PLAIN, true, "true"), "keep
client");
+ testFeatureNotRemoved();
+
+ // Should normally keep feature
+ testExpectingNoTransform(getGadgetXml(CONTENT_WITH_TAG, true, "false"),
"remove client");
+ testFeatureRemoved();
+ }
+
private void testFeatureRemoved() {
assertTrue("Feature wasn't removed",
gadget.getRemovedFeatures().contains("opensocial-templates"));
@@ -302,8 +305,17 @@
}
private static String getGadgetXml(String content, boolean requireFeature) {
+ return getGadgetXml(content, requireFeature, null);
+ }
+
+ private static String getGadgetXml(String content, boolean requireFeature,
+ String clientParam) {
String feature = requireFeature ?
- "<Require feature='opensocial-templates'/>" : "";
+ "<Require feature='opensocial-templates'" +
+ (clientParam != null ?
+ ("><Param name='client'>" + clientParam + "</Param></Require>")
+ : "/>")
+ : "";
return "<Module>" + "<ModulePrefs title='Title'>"
+ feature
+ " <Locale>"