r9669 - in helma-ng/trunk: apps/demo/skins modules/helma src/org/helma/template
[email protected] Thu, 23 Apr 2009 21:54:26 +0200 (CEST)
| Newsgroups | gmane.comp.java.helma.cvs |
|---|---|
| Message-ID | <20090423195426.919393D0D6@mia> |
Author: hannes
Date: 2009-04-23 21:54:26 +0200 (Thu, 23 Apr 2009)
New Revision: 9669
Modified:
helma-ng/trunk/apps/demo/skins/skins.html
helma-ng/trunk/modules/helma/skin.js
helma-ng/trunk/src/org/helma/template/MacroTag.java
Log:
Add new concept of submacros and use it to implement a handful of essential macros: for-in, if, set.
Details at http://dev.helma.org/trac/helma/changeset/9669
Modified: helma-ng/trunk/apps/demo/skins/skins.html
===================================================================
--- helma-ng/trunk/apps/demo/skins/skins.html 2009-04-23 19:54:23 UTC (rev 9668)
+++ helma-ng/trunk/apps/demo/skins/skins.html 2009-04-23 19:54:26 UTC (rev 9669)
@@ -15,55 +15,55 @@
<p>And the following subskin in skins.html:</p>
-<pre class="code"><% subskin 'hello' %>
+<pre class="code"><% subskin hello %>
Hello <% name %>!</pre>
<p>Your basic skin rendering macro then looks like this:</p>
-<pre class="code"><% render 'hello' %></pre>
+<pre class="code"><% render hello %></pre>
<p>And this is the output this produces:</p>
<p style="background-color: pink;">
- <% render 'hello' %>
+ <% render hello %>
</p>
<p>The render macro also lets you loop through arrays and other iterables:</p>
-<pre class="code"><% render 'hello' on=<% names %> as='name' %></pre>
+<pre class="code"><% for name in <% names %> render hello %></pre>
<p>This is what you should get:</p>
<p style="background-color: pink;">
- <% render 'hello' on=<%names%> as='name' %>
+ <% for name in <% names %> render hello %>
</p>
You can also bind values to the macro context in the tag:
-<pre class="code"><% render 'hello' bind={ name: "Julia" } %></pre>
+<pre class="code"><% set {name: Julia} render hello %></pre>
<p>This gets you</p>
<p style="background-color: pink;">
- <% render 'hello' bind={ name: 'Julia' } %>
+ <% set {name: Julia } render hello %>
</p>
<p>Or you can define the objects to loop through using object and array literals in the macro tag:</p>
-<pre class="code"><% render 'hello' on=['Gerlinde', 'Thomas'] as='name' %></pre>
+<pre class="code"><% for name in [Gerlinde, Ralph, Thomas] render hello %></pre>
<p>Produces:</p>
<p style="background-color: pink;">
-<% render 'hello' on=['Gerlinde', 'Thomas'] as='name' %>
+<% for name in [Gerlinde, Ralph, Thomas] render hello %>
</p>
<p>Finally, <a href="http://dev.helma.org/ng/helma.filters/">filters</a> let you do all kinds of post-processing
on the output of a macro.</p>
-<pre class="code"><% render 'hello' | uppercase %></pre>
+<pre class="code"><% render hello | uppercase %></pre>
<p>Renders:</p>
Modified: helma-ng/trunk/modules/helma/skin.js
===================================================================
--- helma-ng/trunk/modules/helma/skin.js 2009-04-23 19:54:23 UTC (rev 9668)
+++ helma-ng/trunk/modules/helma/skin.js 2009-04-23 19:54:26 UTC (rev 9669)
@@ -6,11 +6,11 @@
import('helma/logging', 'logging');
import('helma/system', 'system');
-export('render',
- 'createSkin',
- 'Skin');
+export('render', 'createSkin', 'Skin');
+var __shared__ = true;
var log = logging.getLogger(__name__);
+var skincache = false; // {}
system.addHostObject(org.helma.template.MacroTag);
@@ -46,7 +46,11 @@
* @param resource
*/
function createSkin(resourceOrString, scope) {
- log.debug("creating skin: " + resourceOrString);
+ if (this.skincache && resourceOrString in skincache) {
+ return skincache[resourceOrString];
+ }
+ if (log.isDebugEnabled())
+ log.debug("creating skin: " + resourceOrString);
var mainSkin = [];
var subSkins = {};
var currentSkin = mainSkin;
@@ -76,7 +80,10 @@
if (typeof(lastPart) === 'string' && lastPart.trim() === '') {
mainSkin.pop();
}
- return new Skin(mainSkin, subSkins, parentSkin);
+ var skin = new Skin(mainSkin, subSkins, parentSkin);
+ if (skincache)
+ skincache[resourceOrString] = skin;
+ return skin;
}
/**
@@ -121,20 +128,19 @@
return parts;
};
- var renderInternal = function renderInternal(parts, context) {
+ function renderInternal(parts, context) {
// extend context by globally provided filters. user-provided filters
// override globally defined ones
context = Object.merge(context, filters);
return [renderPart(part, context) for each (part in parts)].join('');
- };
+ }
- var renderPart = function renderPart(part, context) {
+ function renderPart(part, context) {
return part instanceof MacroTag && part.name ?
- evaluateMacro(part, context) :
- part;
- };
+ evaluateMacro(part, context) : part;
+ }
- var evaluateMacro = function evaluateMacro(macro, context) {
+ function evaluateMacro(macro, context) {
// evaluate the macro itself
var value = evaluateExpression(macro, context, '_macro');
if (value instanceof Array) {
@@ -150,12 +156,13 @@
value = evaluateExpression(filter, context, '_filter', value);
}
return value
- };
+ }
- var evaluateExpression = function evaluateExpression(macro, context, suffix, value) {
- log.debug('evaluating expression: ' + macro);
- if (builtins[macro.name]) {
- return builtins[macro.name](macro, context);
+ function evaluateExpression(macro, context, suffix, value) {
+ if (log.isDebugEnabled())
+ log.debug('evaluating expression: ' + macro);
+ if (builtin[macro.name]) {
+ return builtin[macro.name](macro, context);
}
var path = macro.name.split('.');
var elem = context;
@@ -182,54 +189,89 @@
}
// TODO: if filter is not found just return value as is
return value;
- };
+ }
- var isDefined = function isDefined(elem) {
+ function isDefined(elem) {
return elem !== undefined && elem !== null;
- };
+ }
- var isVisible = function isVisible(elem) {
+ function isVisible(elem) {
return elem !== undefined && elem !== null && elem !== '';
- };
+ }
// builtin macro handlers
- var builtins = {
- render: function builtinsRender(macro, context) {
+ var builtin = {
+ "render": function(macro, context) {
var skin = getEvaluatedParameter(macro.getParameter(0), context, 'render:skin');
- var bind = getEvaluatedParameter(macro.getParameter('bind'), context, 'render:bind');
- var on = getEvaluatedParameter(macro.getParameter('on'), context, 'render:on');
- var as = getEvaluatedParameter(macro.getParameter('as'), context, 'render:as');
+ return self.renderSubskin(skin, context);
+ },
+
+ "for": function(macro, context) {
+ if (macro.parameters.length < 4)
+ throw Error("not enough parameters in for-in macro");
+ if (macro.parameters[1] != "in")
+ throw Error("syntax error in for-in macro: expected in")
+ var name = getEvaluatedParameter(macro.parameters[0], context, 'for:name');
+ var list = getEvaluatedParameter(macro.parameters[2], context, 'for:list');
var subContext = context.clone();
- if (bind) {
- for (var b in bind) {
- subContext[b] = getEvaluatedParameter(bind[b], context, 'render:bind:value');
- }
+ var subMacro = macro.getSubMacro(3);
+ result = [];
+ for (var [index, value] in list) {
+ subContext['index'] = index
+ subContext[name] = getEvaluatedParameter(value, context, 'for:value');
+ result.push(evaluateMacro(subMacro, subContext));
}
- if (on) {
- var result = [];
- var subContext = context.clone();
- for (var [key, value] in on) {
- log.debug("key: " + value);
- subContext[('key')] = key;
- subContext[(as || 'value')] = value;
- result.push(self.renderSubskin(skin, subContext));
- }
- return result.join('');
+ var separator = getEvaluatedParameter(macro.getParameter("separator"),
+ context, 'for:separator');
+ if (separator != null) {
+ return result.join(separator);
+ }
+ return result;
+ },
+
+ "if": function(macro, context) {
+ if (macro.parameters.length < 2)
+ throw Error("not enough parameters in if macro");
+ var negated = (macro.parameters[0] == "not");
+ var condition;
+ if (negated) {
+ if (macro.parameters.length < 3)
+ throw Error("not enough parameters in if macro");
+ condition = getEvaluatedParameter(macro.parameters[1], context, 'if:condition');
} else {
- return self.renderSubskin(skin, subContext);
+ condition = getEvaluatedParameter(macro.parameters[0], context, 'if:condition');
}
+ if (negated ? !!condition : !condition) {
+ return "";
+ }
+ var subMacro = macro.getSubMacro(negated ? 2 : 1);
+ return evaluateMacro(subMacro, context);
+ },
+
+ "set": function(macro, context) {
+ if (macro.parameters.length < 2)
+ throw Error("not enough parameters in with macro");
+ var map = getEvaluatedParameter(macro.parameters[0], context, 'with:map');
+ var subContext = context.clone();
+ var subMacro = macro.getSubMacro(1);
+ for (var [key, value] in map) {
+ subContext[key] = getEvaluatedParameter(value, context, 'with:value');;
+ }
+ return evaluateMacro(subMacro, subContext);
}
};
- var getEvaluatedParameter = function getEvaluatedParameter(value, context, logprefix) {
- log.debug(logprefix + ': macro called with value: ' + value);
+ function getEvaluatedParameter(value, context, logprefix) {
+ if (log.isDebugEnabled())
+ log.debug(logprefix + ': macro called with value: ' + value);
if (value instanceof MacroTag) {
value = evaluateExpression(value, context, '_macro');
- log.debug(logprefix + ': evaluated value macro, got ' + value);
+ if (log.isDebugEnabled())
+ log.debug(logprefix + ': evaluated value macro, got ' + value);
}
return value;
- };
+ }
this.toString = function toString() {
return "[Skin Object]";
Modified: helma-ng/trunk/src/org/helma/template/MacroTag.java
===================================================================
--- helma-ng/trunk/src/org/helma/template/MacroTag.java 2009-04-23 19:54:23 UTC (rev 9668)
+++ helma-ng/trunk/src/org/helma/template/MacroTag.java 2009-04-23 19:54:26 UTC (rev 9669)
@@ -25,6 +25,7 @@
import java.util.List;
import java.util.Map;
import java.util.LinkedList;
+import java.util.Iterator;
/**
* A macro tag. Basically a list of unnamed parameters
@@ -32,7 +33,7 @@
*/
public class MacroTag extends ScriptableObject {
- String name;
+ Object name;
LinkedList<Object> args = new LinkedList<Object>();
Map<String,Object> namedArgs = new CaseInsensitiveMap<String,Object>();
MacroTag filter = null;
@@ -80,7 +81,7 @@
/**
* The name of the macro tag.
*/
- public String jsGet_name() {
+ public Object jsGet_name() {
return name;
}
@@ -104,10 +105,7 @@
*/
public Object jsGet_parameters() {
if (jsParams == null) {
- Context cx = Context.getCurrentContext();
- int size = args.size();
- Object[] values = args.toArray(new Object[size]);
- jsParams = cx.newArray(getTopLevelScope(this), values);
+ jsParams = new ScriptableList(getTopLevelScope(this), args);
}
return jsParams;
}
@@ -140,6 +138,22 @@
"parameterNames".equals(name) || super.has(name, start);
}
+ public Object jsFunction_getSubMacro(int start) {
+ MacroTag submacro = new MacroTag(start);
+ submacro.setParentScope(getParentScope());
+ submacro.setPrototype(getPrototype());
+ submacro.filter = filter;
+ submacro.namedArgs = new CaseInsensitiveMap<String,Object>(namedArgs);
+ submacro.args = new LinkedList<Object>();
+ if (start + 1 < args.size()) {
+ for (Iterator i = args.listIterator(start + 1); i.hasNext(); ) {
+ submacro.args.add(i.next());
+ }
+ }
+ submacro.name = args.get(start);
+ return submacro;
+ }
+
/**
* Get a named or unnamed parameter from the macro tag. This method takes a variable
* number of string or integer arguments. It evaluates the arguments as parameter
@@ -193,7 +207,7 @@
name = str;
}
- public String getName() {
+ public Object getName() {
return name;
}