Introduction
What is OpenSocial Template?
OpenSocial Template (OST) provides a declarative way for gadget
developers to create templates by copying and pasting HTML, then making
minor modifications to that HTML. Because OST is part of OpenSocial, it
also provides a means to bind to the OpenSocial APIs and custom,
developer defined variables. OST accomplishes this through tag
libraries and special markup.
The OpenSocial Template spec is composed of three pieces:
- OpenSocial Template Part 1, Template Processing: Specifies how templates are processed and used by OpenSocial containers.
- OpenSocial Template Part 2, Standard Tags: Specifies the tag library to be implemented by OpenSocial Containers.
- OpenSocial Template Part 3, Data Pipelining: Specifies how templates make use of data pipelining.
These three sets of features are deeply intertwined. In order to
adequately support the templating functionality, containers need to
support all aspects to be truly useful to OpenSocial application
developers.
Terminology
The terminology used to describe Open Social Templates is defined in
the body of this specification. The key words MUST, MUST NOT, REQUIRED,
SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL,
when EMPHASIZED, are to be interpreted as described in [#rfc2119 [IETF RFC 2119]].
In addition, the terms defined in the following list are used in
building those definitions and in describing the actions of an OSML
processor:
- DataRequest Markup: A declarative syntax for creating,
parameterizing, and issuing DataRequests. There will be a one-to-one
mapping between possible DataRequest implementations and DataRequest
Markup implementations.
- OpenSocial Templating: A way to define UI output through reusable chunks of xml. A way to standardize UI elements between Containers.
- JavaScript/JS: The term JavaScript or JS is used to refer to what is more commonly known as [#ECMA262 [ECMAScript]].
Limitations
An OST implementation can fetch, inject, cache, and store data
requests for the application before any client side code executes. OST
does not represent a full replacement for JavaScript. OST assumes that
applications can fallback to JavaScript whenever a combination of
application and container cannot make use of a given OST feature.
Dependencies on Other Specifications
Other specifications on which this one depends are listed in Normative References.
Gadget Feature Name
OST creates a new feature name for use in the Gadgets Specification: opensocial-templates. When a
Module requires OST, the Module must contain this XML:
<ModulePrefs>
<Require feature="opensocial-templates"/>
</ModulePrefs>
A common usage of the feature would be with other OpenSocial features. Example:
<ModulePrefs title="Hello World">
<Require feature="opensocial-0.9"/>
<!-- Allows templates -->
<Require feature="opensocial-templates"/>
<!-- Allows multiple views to be defined and thus rendered client or server -->
<Require feature="views"/>
</ModulePrefs>
Template Format
The canonical template format is a well-formed XML document. This
document can include HTML tags which will be output directly when
rendering the template, and custom tags which will be evaluated when
rendering the template. Example:
<Template>
<div style="font-size: 20px">Hello world!</div>
</Template>
A common use case will be embedding this XML into an HTML document.
The following snippet can be embedded directly into a gadget
<Content> section or HTML document:
<script type="text/os-template" name="my:HelloWorld">
<div style="font-size: 20px">Hello world!</div>
</script>
Templates can alternatively be define template markup libraries,
which will map to XML namespaces. The set of common OpenSocial
functionality is defined in the in Part 2 of the Open Social
specification.
Expressions
Expressions can be embedded into the template XML using the syntax ${Expr}. Example template content with an expression:
<div>Hello ${Viewer.Name}</div>
Expressions are defined using a subset of the JSP Expression
Language, with a couple of minor modifications. We use a subset of the
JSP Expression Language specification. This allows for expressions that
are raw variable references, as in the example above, or expressions
with operators:
<div>Next step is ${Step + 1}</div>
JSP Expression Language allows you to use most standard operators
(comparison, arithmetic, etc.), although some operators have an
alternate name for use in XML. ${a < b} becomes ${a lt b}, ${a >
b} becomes ${a gt b}, ${a && b} becomes ${a and b}, etc.
Expressions are usually evaluated as strings. The only
exception is when expressions are in attributes without additional text
content, and in this case, the value of the expression is the object
referenced, and this object is passed to the template for processing.
In the following example, the viewer is passed in to the os:ShowPerson
template.
<os:ShowPerson person="${Viewer}"/>
Strings are escaped before inserting into the HTML document. By
default strings are HTML escaped, however based on context they may be
URL encoded or escaped as JavaScript strings, depending on context.
Variables
Data is passed into all calls to render templates - i.e.:
opensocial.template.processAll(data). The data passed in can be a JSON
object, or as an object with a "getProp" method (example below). The
templates will have two ‘types’ of variables. One set is a set of
global objects with reserved names. The other set includes everything
else or all other declared JavaScript variables.
Special Variables
Template processing reserves a small set of variables.
Top
${Top} refers to the data context passed into the template rendering
- i.e. opensocial.template.renderAll(data) -> ${Top} == data.
Context
${Context} is a holding area for additional variables needed when
processing templates. ${Context} will include the following set of
properties:
- ${Context.UniqueId} A unique ID of the template being rendered. This value is useful for generating HTML IDs.
- ${Context.Index} The index of the current item in the list that is being processed via a "repeat" tag.
- ${Context.Count} The count of items in the list that is being processed via a "repeat" tag.
Cur
${Cur} refers to the current item being processed within a repeater.
My
${My} refers to data that is passed into a template. Child elements
of tags that are used to call templates are passed as parameters to the
template and are accessible via the ${My} variable. If these elements
have content inside of them, this content can be rendered using
<os:Render content="tagName"/>.
Example of defining a template that uses <os:Render/> and calling this template:
<script type="text/os-template" tag="myapp:BoxWithTitle">
<div class="box-title"><os:Render content="title"/></div>
<div class="box-content"><os:Render content="body"/></div>
</script>
<script type="text/os-template">
<myapp:BoxWithTitle>
<title>This is the title</title>
<body>
<div style="font-size:40px">Boo!</div>
<div>from <os:ShowPerson person="${Top.Viewer}"/></div>
<body>
</myapp:BoxWithTitle>
</script>
Note that the passed in content can include other template tags. These tags are displayed as part of calling <os:Render/>.
You can also call <os:Render> without a "content"
attribute. This will render all children of the calling node - this is
a useful shorthand for tags with only one content section.
Example:
<script type="text/os-template" tag="myapp:JsLink">
<a href="#" onclick="${My.action}; return false;"><os:Render/></a>
</script>
<script type="text/os-template">
This is a <myapp:JsLink action="alert('boo')">javascript link</a>.
</script>
Precedence Rules for the Special Variables
Special variable names are optional. Expressions will be evaluated
against each of the special variable contexts. The first matching
variable will be the result of expression evaluation. The order of
precedence is $My, $Cur, $Context, and then $Top. For example,
${Cur.Name} == ${Name} unless there exists ${My.Name}. The following
examples show why this is helpful:
Example with special variables in place:
<div repeat="${Top.ViewerFriends}">
<div>${Cur.Name}"</div>
<div repeat="${Cur.Phone}">${Cur.Number} of type ${Cur.Type}</div>
</div>
Equivalent script using precedence rules:
<div repeat="${ViewerFriends}">
<div>${Name}"</div>
<div repeat="${Phone}">${Number} of type ${Type}</div>
</div>
"Top" refers to the data context passed in via Javascript, so ${Top} will return the data passed in during template rendering.
Variables are accessed using Foo.Bar (or Foo.Bar.Baz) notation.
.Bar maps to getting the property Bar on the JSON object. If Top and
Viewer are JSON objects, then
evaluates to
If an object has a getProp method, then this method is called
instead of using the property notation. So if Top and Viewer support
this interface, then
evaluates to
data.getProp('Viewer').getProp('Name')
OpenSocial core objects will be modified to add getProp() support so that you can access properties by name.
Variables can also be accessed using the foo[x] notation, where
x is a string, number, or expression that evaluates to a string or
number. If ${foo} is an expression that maps to the JS object bar, then
${foo[x]} maps to bar[x] if bar is JSON, or bar.getAt(x) if bar is an
object.
Calling Templates
Templates can be called by other templates. You can add a "name"
attribute to any template (both on the <Template> element or on
the <script> tag for embedded templates). This allows the
template to be called by using an XML element of the same name.
Example of defining a template and then calling it:
<script type="text/os-template" tag="myapp:HelloWorld">
<div style="font-size: 40px">Hello World</div>
</script>
<script type="text/os-template">
<myapp:HelloWorld/>
</script>
Parameters can be passed into templates as XML attributes or
elements. These parameters are accessed using the special variable
${My}:
<script type="text/os-template" tag="myapp:HelloWorld">
<div style="color: ${My.MessageStyle.color}">Your message is: ${My.message}</div>
</script>
<script type="text/os-template">
<myapp:HelloWorld message="Hello World">
<MessageStyle color="blue"/>
</myapp>
</script>
${My.foo} will first look for an attribute named foo in the calling template, and next will look for an element named foo.
Context Attribute
The scope of data to evaluate can be explicitly set with the
@context attribute. For example, to repeat over Interests of the first
object in Friends, you can use this expression:
<div context="Friends[0]">
<div repeat="Interests">
${Title}
</div>
</div>
Conditional Content
Content can be displayed conditionally based on evaluation of an expression.
Elements with an @if attribute will only be displayed if the if attribute evaluates to true. For example, the following will only be displayed if Top.YourScore == Top.HighScore:
<div if="Top.YourScore == Top.HighScore">You have the high score of ${Top.YourScore}!</div>
Note: An @if attribute takes only an expression, so the surrounding '${}' is allowed, but optional.
The contents of an <os:If> element are only displayed if
the condition attribute evaluates to true. The example above rewritten
using the <os:If> element:
<os:If condition="Top.YourScore == Top.HighScore">
<div>You have the high score of ${Top.YourScore}!</div>
</os:If>
The @if attribute can be used on any element within a template, some examples are as follows:
<!-- Custom tags -->
<os:ShowPerson person="${Top.Owner}" if="Top.HasOwner"/>
<!-- Repeated elements (see below) -->
<div repeat="Top.ViewerFriends" if="Cur.ProfileUrl">
Link to: <a href="${Cur.ProfileUrl}">${Cur.Name}</a>
</div>
<!-- Tag contents -->
<os:Button>
<title>Click me!</title>
<help if="Top.Viewer.NeedsHelp">You need help.</help>
</os:Button>
Repeated Elements
Tags can be rendered multiple times based on evaluation of an expression.
Tags with a @repeat attribute are displayed once for each item
in evaluating the expression in the repeat attribute. The current item
in the repeated list will be put into the ${Cur} variable. Note: As with @if, the ${} expression syntax is optional, but allowed.
Example:
<div repeat="Top.ViewerFriends">
Your friend's name is ${Cur.Name}
</div>
In addition, both the variable and the index can be renamed, by
adding a @var attribute and @index attribute respectively. Example:
<!-- Renaming variable $Cur -->
<div repeat="${Top.ViewerFriends}" var="Friend">
Your friend's name is ${Friend.Name}
</div>
<!-- Specifying an Index variable -->
<div repeat="rows" index="x">
<div repeat="cols" index="y">
<myapp:DrawPoint row="${x}" col="${y}"/>
</div>
</div>
There are two context variables available during list processing.
${Context.Count} is the count of items in the list and ${Context.Index}
is the index of the current item. Example:
<div repeat="${Top.ViewerFriends}">
Showing friend ${Context.Index} of ${Context.Count}:
Your friend's name is ${Cur.Name}
</div>
The contents of an <os:Repeat> element displayed once for each
item in evaluating the expression in the expression attribute. The
current item in the repeated list will be put into the ${Cur} variable,
and a @var attribute can also be used to rename the iteration variable.
<os:Repeat expression="${Top.ViewerFriends}">
<div>
Your friend's name is ${Cur.Name}
</div>
</os:Repeat>
and
<os:Repeat expression="${Top.ViewerFriends}" var="Friend">
<div>
Your friend's name is ${Friend.Name}
</div>
</os:Repeat>
Template Libraries
While inline templates can be useful for visualizing data,
eventually you may want to create standalone, reusable templates. In
that regard, templates can be packaged into a standalone XML file of
the following format:
<Templates xmlns:foo="http://foo.com/">
<Namespace prefix="foo" url="http://foo.com/"/>
<Style>
<!-- Set global CSS for your library here -->
.warning { color: red; }
</Style>
<JavaScript>
<!-- Define global functions for your library here -->
function usedByAllTemplates() { ... };
</JavaScript>
<!-- Simple declarative tag foo:bar -->
<Template tag="foo:bar">
<!-- Define markup for foo:bar here -->
</Template>
<!-- Complex tag foo:baz with local CSS and JavaScript -->
<TemplateDef tag="foo:baz">
<Template> <!-- Define markup for foo:baz here --> </Template>
<Style> <!-- Set CSS for foo:baz here --> </Style>
<JavaScript>
<!-- Define functions for the foo:baz template here -->
function usedByFooBaz() { ... };
</JavaScript>
</TemplateDef>
</Templates>
For example:
<?xml version="1.0" encoding="UTF-8"?>
<Templates xmlns:os="http://opensocial.org/templates">
<Namespace prefix="os" url="http://opensocial.org/templates">
<Style>
large-font: {
font-size: 20px;
}
</Style>
<Template name="os:HelloWorld">
<div style="large-font">Hello World!</div>
</Template>
<TemplateDef tag="os:ShowPerson">
<Style>
profile-image: {
padding-right: 5px;
width: 32px;
height: 32px;
}
</Style>
<Template>
<img if="${My.person.ThumbnailUrl}" src="${My.person.ThumbnailUrl}"
class="profile-image"/>
<a href="${My.person.ProfileUrl}" target="_top">${My.person.Name}</a>
</Template>
</TemplateDef>
</Templates>
Syntax:
- <Templates>: Declares all the namespaces used in
this library to the XML parser. You must put this tag at the outer-most
level of your XML file and list all the custom and pre-defined
namespaces that you use in this library. Use the syntax
xmlns:namespace_prefix="namespace_URL" for each namespace. For example,
if you use any tags in the OpenSocial namespace via the OpenSocial
Templates library, you must include xmlns:os="http://www.opensocial.org/".
- <Namespace>: Declares your custom namespace to
the OpenSocial Templates API. You should only create one namespace per
library file. Set the values of your namespace's prefix and URL using
the syntax prefix="namespace_prefix" url="namespace_URL"
- <Style>: Defines style settings in CSS for your
library (at the top-level of the XML file) or individual templates
(within <TemplateDef/> tags).
- <JavaScript>: Defines JavaScript functions for
your library (at the top-level of the XML file) or individual templates
(within <TemplateDef/> tags).
- <Template>: Sets declarative markup for a template.
- <TemplateDef>: Defines a more complex template,
which can enclose its own local <Template/>, <Style/>, and
<JavaScript/> tags.
Containers should load the <os:*> template tags by default,
and it is not required that they be defined as a standalone XML file.
Other tag libraries can be loaded by default in a container - for
example, Example.com might have a set of <example:*> tags
available.
Localization
Templates will have the ability to substitute localized text content on a per-language basis.
Gadgets already have a facility for defining localized messages.
In the gadget prefs, you can specify the URL for localized message
bundles on a per-language basis:
<ModulePrefs title="ListenToThis">
<Locale messages="http://www.listentostuff.com/messages.xml"/>
<Locale lang="de" messages="http://www.listentostuff.com/messages-DE.xml"/>
</ModulePrefs>
Each bundle is a list of <msg> elements with localized content inside:
<messagebundle>
<msg name="PLAY_SONG">Click here to play ${song.title}</msg>
</messagebundle>
Also note that in OpenSocial API 0.9, you will be able to inline
<msg> elements into the gadget directly without creating separate
files for each locale.
Localized messages will be accessible via the ${Msg} variable:
<a href="${song.url}">${Msg.PLAY_SONG}</a>
Any ${} markup in the message body will be honored - in the above
example, the ${song.title} reference in the message will be evaluated
at the time of template rendering. Repeaters and conditionals in
messages will not be processed. Variable markup will be evaluated in
the context in which the message is placed. (Note how the message in
the example references the ${song} variable available at the point of
message inclusion.)
It's important to realize that this sets ${Msg} apart from
other variable inclusion markup - variable references will NOT be
processed in any other output.
If a particular message is undefined for a locale and missing
from the default bundle (prefs.getMsg(key) returns null in these
cases), an error message should be rendered in its place for easy
debugging.
References
Normative References
IETF
RFC 2119
IETF (Internet Engineering Task Force). RFC 2119: Key words for use in RFCs to Indicate Requirement Levels. Scott Bradner, 1997. (See http://www.ietf.org/rfc/rfc2119.txt.)
Java Server Pages Expression Language
Kin-Man Chung, Pierre Delisle, Mark Roth, May 8, 2006. https://jsp.dev.java.net/spec/jsp-2_1-fr-spec-el.pdf.
Other References
ECMA-262
ECMA (European Computer Manufacturers Association). ECMA-262: ECMAScript Language Specification. 1999. (See http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf.)
- ↑ Opener on Data Pipelining at the Spec Group