opensocial-templates-spec

Overview
We're adding templates to OpenSocial!


This document describes a proposal for how we can add templating support to OpenSocial.  It's orgainzed into the following sections:

  • Goals. Goals and requirements for adding templates to OpenSocial
  • Proposal Outline. A brief overview of the proposal.
  • Examples. Walk-through of the proposed functionality using sample gadgets.
  • Templates Proposal. The proposal. Note that this is still fairly high-level - there will be a number details to work through to get to a final spec.

If you're interested in getting a flavor for how templates would work without reading too much, you can:



Goals

The goals and requirements we are aiming to meet with the proposal:

  • Easy to develop with
    • Ability to create templates by copying and pasting HTML and making minor modifications
    • From discussions with developers and container partners, the assumption is that this means using a markup language
  • Secure
    • XSS free unless developers explicitly work around XSS protections
    • Use of innerHTML strongly discouraged and may be prohibited
  • Capable of creating HTML for most applications
    • Should be able to create entire application UI using templating.
    • Can bind to data returned from the OpenSocial APIs as well as from custom variables the developer has defined
    • Can display HTML elements conditionally
    • Can iterate through data and display HTML content for each item in a list
  • Plugs into OpenSocial
    • Can render templates on the client in JavaScript
    • Can render at least a subset of templates on the server (required for activity streams and messages, useful for performance)
  • Easy to define templates
    • Can implement a template definition using other templates. I.e.: A friend picker template might use the standard button template.
    • Can implement a template definition using JavaScript. This might only be available for library writers, but many common UI elements will require significant JavaScript functionality to build.


Proposal Outline
OpenSocial templates will be a feature that you can add to your gadget using <Require feature="opensocial-templates"/>. The templates are are XML fragments that can contain both raw HTML and custom tags.


Templates will be embedded into HTML documents and gadget HTML content sections using a <script> tag. In the content section of a gadget, you would see:

  <script type="text/os-template">

    Welcome, ${Top.Viewer.Name}

  </script>


After loading the initial social data via a call to the OpenSocial APIs, you will call opensocial.template.renderAll() with the data to render all of these templates. These templates will be displayed in the same location in the document as the original <script> tags. Example:

  <script type="text/javascript">

    opensocial.template.renderAll(socialDataFromOpenSocialApiCall);

  </script>

 

We also intend to support putting the template XML directly into the gadget spec instead of in a <script> tag.  There is not a concrete proposal on this functionality yet, but one option is to add these templates as children of the <Require> tag.

 

Expressions

Expressions can be embedded into text nodes and attributes the template XML using the syntax ${Expr} - see ${Top.Viewer.Name} above. This is the same syntax we are using for expressions in Activity Stream templates.

 

The variables in these expressions can be evaluated against OpenSocial objects (i.e. the Viewer) or against raw JSON objects passed into the templating system when rendering. 

Tag Libraries

There will be a standard set of OpenSocial tags prefixed (by convention) with "os:".


However the templating system supports multiple markup libraries. It will be extensible and agnostic to the specific tags libraries used, so containers, library developers, and gadget authors can all define their own namespaced tag libraries. For example, Google may create a standard YouTube player using <goog:YouTube>, or a library author could create the same functionality using <mylib:YouTube/>.


We are working on a concrete proposal for the initial set of OpenSocial tags.


Flow Control

The templating language supports showing repeated elements and showing elements conditionally.


Conditional elements use an "if" attribute on the element - <div if="${Top.Score gt $Top.OldHighScore}">You have the new high score!</div>, and repeated elements use a "repeat" attribute: <div repeat="${Top.ViewerFriends}">Your friend is: ${Cur.Name}</div>. Both HTML elements and custom tags can use these flow control constructs. 


Defining Templates

Templates can be defined by adding a name attribute to the <script> tag or surrounding XML element. This template can be called by using an element with the same name.


Definition:

  <script type="text/os-template" name="os:HelloWorld">

    <div class="hello">Hello World!</div>

  </script>

 

and usage:

  <script type="text/os-template">

    <os:HelloWorld/>

  </script>


Parameters can be passed into these templates, both as attributes and as child XML elements. This is discussed in more detail in Calling Templates and Passing in Content.


Examples

We'll walk through some of the key proposed functionality of OpenSocial templates with the following examples:

You can install all of the sample gadgets on iGoogle. These sample gadgets are slightly modified - they use <script src=""/> to load the a demo JS file directly.

 

We have also created a standalone demo page with a number of examples of the types of templates we'd like to build.



1. Hello World

This is the simplest possible template. It doesn't actually do anything useful, but highlights how templates can be inserted into a document.

  • <script type="text/template"> defines an inline template section.
  • You call opensocial.template.processAll() to process all of  the inline templates
    • These resulting HTML will put into the document in the same location as the template.
  • Templates can be raw HTML, as in the example below.
    • On the following pages, we will show how we add more complex template constructs into the HTML.
  • Data is passed into opensocial.template.processAll(). More on this on the upcoming examples.
  • Inside the <script> tag, the template content is a valid XML fragment.

 

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Hello World" height="100">

    <Require feature="opensocial-0.7"/>

    <Require feature="opensocial-templates"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[

 
      <script type="text/os-template">
        <div style="font-size: 20px">Hello world!</div>
      </script>

      
      <script type="text/javascript">
        // loadSocialData() is a function that calls OpenSocial APIs and returns your data
        loadSocialData(function(socialData) {
          opensocial.template.processAll(socialData);
        });
      </script>
    ]]>
  </Content>
</Module>



2. Variable Substitution

Here's an example of passing data into templates and using variable substitution:

  • Data is passed in to opensocial.template.processAll(data).
    • This data can be raw JSON data or data returned from OpenSocial APIs.
    • We will only show examples using raw JSON for now. 
  • Expressions of form ${expr} are evaluated against this data
    • ${Top.Viewer.Name} in the example below translates to the JavaScript expression "data['Viewer']['Name'].
  • ${Top} refers the global context passed into the template processing system.
    • We may allow leaving out "Top" if it isn't ambiguous, so the example would be ${Viewer.Name}.
    • However we are using this convention for all of the examples for clarity


<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Hello World" height="100">

    <Require feature="opensocial-0.7"/>

    <Require feature="opensocial-templates"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[

 
      <script type="text/os-template">
        <div style="font-size: 20px">Hello ${Top.Viewer.Name}!</div>
      </script>

 
      <script type="text/javascript">

        var data = {

          Viewer: {

            Name:  'Opensocial  Bob'

          }

        }; 
        opensocial.template.processAll(data);
      </script>
    ]]>
  </Content>
</Module>



3. Using OpenSocial Tags

This example shows how to call an OpenSocial tag and also shows how this tag would be implemented by a container.

  • OpenSocial tags are all namespaced XML tags.
    • Default mapping is to "os:", this can be changed in the JavaScript.
    • Example below is <os:ShowPerson> tag.
  • We will have a core set of OpenSocial tags that every container that supports OpenSocial templates must implement.
    • [Proposal upcoming]
  • Parameters can be passed into OpenSocial tags as attributes or as child elements.
    • Example of passing in as attribute below - <os:ShowPerson person="${Top.Viewer}"/>
    • Note that these attributes can be expressions
    • See Passing in Content for examples of passing in child element parameters. 
  • The implementation of <os:ShowPerson> shows the special variable ${My}.
    • This variable refers to attributes and elements in the XML tag used when calling the template.
    • So <os:ShowPerson person="${Top.Viewer}"> will create a variable named ${My.person} when processing the template.

 

<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Hello World" height="100">

    <Require feature="opensocial-0.7"/>

    <Require feature="opensocial-templates"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script type="text/os-template">
        Hello <os:ShowPerson person="${Top.Viewer}"/>
      </script>
  
      <script type="text/javascript">
        // loadSocialData() is a function that calls OpenSocial APIs and returns your data
        loadSocialData(function(socialData) {
          opensocial.template.processAll(socialData);
        });
      </script>

 

      <!-- Sample definition of os:ShowPerson.  -->

      <!-- This would normally be in the container libraries -->
      <script type="text/os-template" name="os:ShowPerson">
          <img width="32" height="32" if="${My.person.ThumbnailUrl}"
              src="${My.person.ThumbnailUrl}" style="padding-right: 5px"/>
          <a href="${My.person.ProfileUrl}">${My.person.Name}</a>
      </script> 

    ]]>
  </Content>
</Module>



4. Conditional Content

This example shows how to create conditional content.

  • Tags with an "if" attribute will only be displayed if the value of the "if" attribute evaluates to true.
  • The value of the if attribute can be an expression.
  • Expressions are defined using JSP Expression Language, with a couple of minor modifications.
    • This means that you can use most standard operators (comparison, arithmetic, etc.)
    • Some operators have a alternate name for use in XML
      • ${a < b} becomes ${a lt b}
      • ${a > b} becomes ${a gt b}
      • ${a && b} becomes ${a and b}
      • ... and a few others 
  • Also note how you are not required to pass social data into templates - in this example, we just pass in a simple JSON object. 


<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Hello World" height="100">

    <Require feature="opensocial-0.7"/>

    <Require feature="opensocial-templates"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script type="text/os-template">
        <div if="${Top.YourScore == Top.HighScore}">You have the high score of ${Top.YourScore}!</div>
        <div if="${Top.YourScore != Top.HighScore}">Your score of ${Top.YourScore} is not the high score :(</div>
      </script>
      
      <script type="text/javascript">
        opensocial.template.processAll({HighScore: 150, YourScore: 150});
      </script>

    ]]>
  </Content>
</Module>



5. Repeated Elements

This example shows how to create repeated UI elements based on OpenSocial data or your own data.

  • Tags with an "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
  • Other planned support (not shown):
    • The ability to rename ${Cur} to a different variable name: <tag repeat="${Foos}" var="Foo">
    • The ability to access variables related to list iteration
      • ${Context.Count} Would be the # of items in the current list
      • ${Context.Index} would be the index of the current item.


<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Hello World" height="100">

    <Require feature="opensocial-0.7"/>

    <Require feature="opensocial-templates"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script type="text/template">
        <div repeat="${Top.ViewerFriends}">
          <os:ShowPerson person="${Cur}"/>
        </div>
      </script>
      
      <script type="text/javascript">
        // loadSocialData() is a function that calls OpenSocial APIs and returns your data
        loadSocialData(function(socialData) {
          opensocial.template.processAll(socialData);
        });
      </script>
      
      <!-- Sample definition of os:ShowPerson  -->
      <!-- This would normally be in the container libraries -->
      <script type="text/template" name="os:ShowPerson">
          <img width="32" height="32" if="${My.person.ThumbnailUrl}"
              src="${My.person.ThumbnailUrl}" style="padding-right: 5px"/>
          <a href="${My.person.ProfileUrl}">${My.person.Name}</a>
      </script>

    ]]>
  </Content>
</Module>



6. Passing in Content

This example shows how to pass in content into templates:

  • Child elements of tags that call templates are passed in as parameters to the template.
  • These child elements can have tags inside of them - see the <os:Content> tag below with <os:ShowPerson> inside.
  • The implementation of <os:CollapsibleBox> below shows the special element <RenderAll/>.
    • <RenderAll/> is used to display the content in a child element passed into the template.
    • Example from below is <RenderAll content="${My.os:Title}"/>.
    • The content attribute can be any expression that evaluates to content.


<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Hello World" height="100">

    <Require feature="opensocial-0.7"/>

    <Require feature="opensocial-templates"/>
  </ModulePrefs>
  <Content type="html">
    <![CDATA[
      <script type="text/os-template">
        <os:CollapsibleBox collapsed="${Top.Hiding}">
          <os:Title>I'm hiding something...</os:Title>
          <os:Content>
            <div style="font-size:40px">Boo!</div>
            <div>from <os:ShowPerson person="${Top.Viewer}"/></div>
          </os:Content>
        </os:CollapsibleBox>

      </script>
      
      <script type="text/javascript">
        // loadSocialData() is a function that calls OpenSocial APIs and returns your data
        loadSocialData(function(socialData) {
          opensocial.template.processAll(socialData);
        });
      </script>
      
      
      <!-- Sample definition of os:CollapsibleBox and os:ShowPerson  -->
      <!-- This would normally be defined in the container libraries -->
      <script type="text/os-template" name="os:CollapsibleBox">
        <div class="collapsible-titlebox" id="collapser-${Context.UniqueId}">
          <div style="float:right">
            <a if="${My.collapsed}" href="javascript:void(0)" onclick="toggleCollapser('${Context.UniqueId}')"
                id="collapser-show-${Context.UniqueId}">show</a>
            <a if="${!My.collapsed}" href="javascript:void(0)" onclick="toggleCollapser('${Context.UniqueId}')"
                id="collapser-show-${Context.UniqueId}">hide</a>
          </div>
          <span class="title">
            <RenderAll content="${My.os:Title}"/>
          </span>
        </div>
        <div class="collapsible-content" id="collapser-content-${Context.UniqueId}" style="${My.collapsed ? 'display:none' : 'display:hidden'}">
          <RenderAll content="${My.os:Content}"/>
        </div>
      </script>
      <style type="text/css">
        .collapsible-titlebox {padding: 5px 8px; background-color: #DDF; border: 1px solid #AAF;}
        .collapsible-titlebox .title {font-weight: bold; }
        .collapsible-content {padding: 5px 8px; border: 1px solid #AAF;}
      </style>
      <script type="text/javascript">
        function toggleCollapser(id) {
          var content = document.getElementById('collapser-content-' + id);
          var show = document.getElementById('collapser-show-' + id);
          
          var visible = content.style.display != 'none';
          var newVisible = !visible;
          
          if (newVisible) {
            content.style.display = 'block';
            show.innerHTML = 'hide';
          } else {
            content.style.display = 'none';
            show.innerHTML = 'show';
          }
        }
      </script>
      <script type="text/os-template" name="os:ShowPerson">
          <img width="32" height="32" if="${My.person.ThumbnailUrl}"
              src="${My.person.ThumbnailUrl}" style="padding-right: 5px"/>
          <a href="${My.person.ProfileUrl}">${My.person.Name}</a>
      </script>

    ]]>
  </Content>
</Module>



7. Custom Templates

Gadget developers and library authors can define their own custom templates.

  • Tags to call templates can have child elements with any content in them.
    • This content is passed into the template, it can display the content of these elements as makes sense to render the template.
  • These child elements can have tags inside of them - see <os:ShowPerson> in the <os:Content> tag below.
  • The implementation of the template can call <RenderAll content="${Expr}"> to display the content in these child elements.
    • Example from below is <RenderAll content="${My.os:Title}"/>
  • Other planned support (not shown):
    • Ability to render all child content with <RenderAll/> and no "content" attribute.
      • This is useful for inline tag replacements - i.e. <os:Link>Some text here</os:Link> as a replacement for the <a> tag
    • Ability to iterate over passed in content and access variables from each tag.
      • You can call <tag repeat="${My.os:ListItem}"/> to repeat over every item in a list.
      • In this case, the ${Cur} variable will be equal to the content tag.


<?xml version="1.0" encoding="UTF-8"?>
<Module>
  <ModulePrefs title="Hello World" height="100">

    <Require feature="opensocial-0.7"/>

    <Require feature="opensocial-templates"/>
  </ModulePrefs>
  <Content type="html">
      <script type="text/os-template" name="ShowMyHighScore">
        <div style="color: ${My.color}">Your high score is ${Top.YourScore}!</div>
      </script>
      
      <script type="text/os-template">
        <ShowMyHighScore color="red"/>
      </script>
      
      <script type="text/javascript">
        opensocial.template.processAll({YourScore: 880});
      </script>

    ]]>
  </Content>
</Module>



Proposal

The proposal is organized into the following sections:



Basic 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">
    <div style="font-size: 20px">Hello world!</div>
  </script>


We will have the ability to define template markup libraries, which will map to XML namespaces. The set of common OpenSocial functionality will be implemented as an XML namespace with the common prefix "os:".



Expressions
Expressions can be embedded into the template XML using the syntax ${Expr}. This is the same syntax we are using to identify expressions Activity Stream templates.  Example template content with an expression:


  <div>Hello ${Top.Viewer.Name}</div>


Expressions are defined using JSP Expression Language, with a couple of minor modifications. This allows for expressions that are raw variable references, as in the example above, or expressions with operators:

 

  <div>Next step is ${Top.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="${Top.Viewer}"/> 

 

The rules for how to evaluate variables are covered in the next section. 



Variables

Data is passed in to 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).


"Top" refers to the data context passed in via Javascript, so ${Top} will return the data passed into the template processing system.


Variables are accessed using Foo.Bar (or Foo.Bar.Baz) notation. ".Bar" maps to getting the property "Bar" on the JSON object, so if Top and Viewer are JSON objects, then

  ${Top.Viewer.Name} 

        evaluates to

  data['Viewer']['Name']


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

  ${Top.Viewer.Name} 

        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.


Open Issues:

  • We will want to support a Foo[Bar] notation as well, so that you can look up map elements by key. This may need a different method signature than getProp(), as we want to be able to get collection properties as well as members - i.e. ViewerFriends.Count, ViewerFriends.First, and ViewerFriends[Id].


Special Variables

There are a number of special variables that are available as part of template processing.

 

These will be discussed in more detail where they are used, but a quick overview:

  • ${Top} refers to the data context passed into the template rendering - i.e. opensocial.template.renderAll(data) -> ${Top} == data.
  • ${Context} is a holding area for additional variables needed when processing templates. This will include:
    • ${Context.TemplateId} A unique ID of the template being rendered. 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} refers to the current item being processed in the list that is being processed via a "repeat" tag.
  • ${My} refers to data that is passed into a template.

Special variable names are optional - expressions will be evaluated against each of the special variable contexts, and the first matching variable will be the result of expression evaluation. The order of precedence is $My, $Cur, $Context, and then $Top. So ${Cur.Name} == ${Name} unless there exists ${My.Name}. The following examples show why this is helpful:

 

Example if special variables are required

  <div repeat="${Top.ViewerFriends}">

    <div>{$Cur.Name}"</div>

    <div repeat="${Cur.Phone}">${Cur.Number} of type {$Cur.Type}</div>

  </div>

 

Example if they are optional:
  <div repeat="${ViewerFriends}">

    <div>{$Name}"</div>

    <div repeat="${Phone}">${Number} of type {$Type}</div>

  </div>



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" name="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" name="myapp:HelloWorld">

    <div style="color: ${My.myapp:MessageStyle.color}">Your message is: ${My.message}</div>

  </script>


  <script type="text/os-template">

    <myapp:HelloWorld message="Hello World">

      <myapp: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.

 


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 <div> will only be displayed if Top.YourScore == Top.HighScore:

  

  <div if="${Top.YourScore == Top.HighScore}">You have the high score of ${Top.YourScore}!</div>


The contents of an <If> element are only displayed if the "expr" attribute evaluates to true. The example above rewritten using the <If> element:


  <If expr="${Top.YourScore == Top.HighScore}>
    <div>You have the high score of ${Top.YourScore}!</div>

  </If>

 

The if attribute can also be used on:

  • Custom tags: <os:ShowPerson person="${Top.Owner}" if="${Top.HasOwner}"/>
  • Repeated elements: <div repeat="${Top.ViewerFriends}" if="${Cur.ProfileUrl}">Link to: <a href="${Cur.ProfileUrl}">${Cur.Name}</a>
  • Passed in content: <os:Button><os:Title>Button Title</os:Title><os:Help if="${Top.Viewer.NeedsHelp}">Some help text</os:Help></os:Button>

 Open Issues

  • Type coercion - when does a string or number evaluate to true?
  • Lowercase <if>? Capitalized to keep consistent with <RenderAll/>, but possibly all system tags should be lower case.


Repeated Elements

Tags can be rendered multiple times based on evaluation of an expression. 

Tags with an "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.

 

Example: 

  <div repeat="${Top.ViewerFriends}">

    Your friend's name is ${Cur.Name}
  </div>

 

In addition, developers will be able to set he current item to a different variable name, by adding a "var" attribute. Example:

  <div repeat="${Top.ViewerFriends}" var="Friend">

    Your friend's name is ${Friend.Name}
  </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 <Repeat> element displayed once for each item in evaluating the expression in the "expr" attribute.  The current item in the repeated list will be put into the ${Cur} variable, and an @var attribute can also be used to rename the iteration variable.


  <Repeat expr="${Top.ViewerFriends}">

    <div>

      Your friend's name is ${Cur.Name}
    </div>

  </Repeat>
 

 and

 

   <Repeat expr="${Top.ViewerFriends}" var="Friend">

    <div>

      Your friend's name is ${Friend.Name}
    </div>

  </Repeat>


 

Open Issues:

  • <repeat> vs. <Repeat> 


Passing in Content
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 <RenderAll content="${Expr}"/>, where ${Expr} evaluates to a content node.

 

Example of defining a template that uses <RenderAll/> and calling this template:


      <script type="text/template">
        <myapp:BoxWithTitle>
          <os:Title>This is the title</os:Title>
          <os:Content>
            <div style="font-size:40px">Boo!</div>
            <div>from <os:ShowPerson person="${Top.Viewer}"/></div>
          </os:Content>
        </os:CollapsibleBox>
      </script>

 

      <script type="text/template" name="myapp:BoxWithTitle">

        <div class="box-title"><RenderAll content="${My.os:Title}"/></div>

        <div class="box-content"><RenderAll content="${My.os:Content}"/></div>
      </script>


Note that the passed in content can include other template tags. These tags are displayed as part of calling <RenderAll/>.

 

You can also call <RenderAll/> 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/template">
        This is a <myapp:JsLink action="alert('boo')">javascript link</a>.
      </script>

 

      <script type="text/template" name="myapp:BJsLink">

        <a href="#" onclick="${My.action}; return false;"><RenderAll/></a>
      </script>

 


Template Libraries

Templates can be packaged into a standalone XML file of the following format:

 

      <?xml version="1.0" encoding="UTF-8"?>
      <Templates xmlns:os="http://opensocial.org/templates">
        <Template name="os:HelloWorld">
          <div style="font-size: 20px">Hello World!</div>
        </Template>
        <Template name="os:ShowPerson">
            <img width="32" height="32" if="${My.person.ThumbnailUrl}"
                src="${My.person.ThumbnailUrl}" style="padding-right: 5px"/>
            <a href="${My.person.ProfileUrl}" target="_top">${My.person.Name}</a>
        </Template>
      </Templates>


When this template library is loaded, a template is created for each of the <Template> child nodes of the top level <Templates> element.

 

Containers should load the <os:*> template tags by default, and it is not required that they define this as a standalone XML file. Other tag libraries can be loaded by default in a container - for example, Google might have a set of <goog:*> tags available.


Developers can refrence additional template libraries using opensocial.template.registerTemplateLibrary(uri). The contents of these libraries are guaranteed to be available for processing by opensocial.template.processAll() - this function will wait for libraries to load before rendering the templates.

 

Issues:

  • Do we want to support defining templates inside the gadget spec? How would these be referenced?
  • How can we preload / inline templates for performance?
    Can template libraries be referenced in messages / activity streams? This seems like a useful feature but probably one that can come in a later spec enhancement.


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="LISTEN_TO_THIS_SONG">
${Subject.DisplayName} told ${Owner.DisplayName} to listen to a song!
</msg>
</messagebundle>

Also note that in OpenSocial API 0.8, you will be able to inline <msg> elements into the gadget directly without creating separate files for each locale.


Tags in OpenSocial templates that can be replaced with localized content will have an ID attribute that references a message in the message bundle. These tags will also include default content that is displayed in development mode and 

 

On processing of templates for a given locale, the content of localized tags will be replaced with matching content from the message bundle before processing. Localizable tags can also include default content - this content is displayed in development mode and also when there is no matching message in the message bundle.


There are two open issues for which we are finalizing a proposal - please feel free to comment on the spec mailing list if you have opinions:

  • Do we have a separate <Message> element for localized text, or can we identify localized text blocks with an attribute?
    • Or do we do both?
    • Example with <Message> tag: <Message key="HELLO_WORLD">Hello world!</Message>
    • Example with @msg attribute: <div msg="HELLO_WORLD">Hello world!</div>
  • How do we pass is parameters to messages? Options:
    • Pass in parameters as child XML elements. This has the advantage of being very explicit, and is validatable XML, but has the disadvantage of being verbose. Example:
        <Message key="GAVE_GIFT">
          <Param key="gift" value="${Top.GiftName}">
          <DefaultText>Thanks for the ${gift}.</DefaultText>
        </Message>

    • Pass in parameters as attributes. This is more compact for developers. However it creates an hard-to-validate XML element, as it must support any attribute. Example:
        <Message key="GAVE_GIFT" gift="${Top.GiftName}">Thanks for the ${gift}.</Message>

    • Implied parameters. This is very easy for developers but may be harder to define proper substitution rules. Example:
        <Message key="GAVE_GIFT">Thanks for the ${Top.GiftName}.</Message>

Issues:

  • Do we want to support defining templates inside the gadget spec? How would these be referenced?
  • How can we preload / inline templates for performance?
    Can template libraries be referenced in messages / activity streams? This seems like a useful feature but probably one that can come in a later spec enhancement.


JavaScript in Templates
Listed below are are four ways that JavaScript can be defined and called from OpenSocial templates. All JavaScript used in templates may be rewritten using Caja or ignored by the container processing the templates, both for security and for performance reasons.


1. JavaScript functions can be called in event handlers

Example:

  <a href="#" onclick="doSomething();return false">Do something</div>.

 

These attributes can also use expressions, so you can have:

  <a href="#" onclick="${action}; return false">Do Something</a>

 

 

2. JavaScript functions can be defined in <JavaScript> tags

These are functionally equivalent to <script> tags, but are needed for syntax reasons when writing templates inside of <script type="text/os-template">. Example:

  <script type="text/os-template name="AlertButton">

    <JavaScript id="AlertButtonJs">

      function showAlert() {

        alert('Warning! Warning! Danger! Danger!');

      }

    </JavaScript>

    <button onclick="showAlert()">Click for alert</button>

  </script>


 

3. Inline JavaScript calls can be defined in <JavaScript> tags. Example:

  <script type="text/os-template name="RegisteredButton">

    <button id="${Context/Id}">text</button>

    <JavaScript>

      callSomeFunctionThatRegistersButton('${Context/Id}');

    </JavaScript>

  </script>

 

 

4. External JavaScript libraries can be loaded using <JavaScript> tags. Example: 

  <script type="text/os-template name="AlertButton">

    <JavaScript src="http://www.opensocial.org/showAlert.js"/>

    <button onclick="showAlert()">Click for alert</button>

  </script>

 

 

The following rules are followed when processing <JavaScript> tags.

  • JavaScript tags with an @src attribute (<JavaScript src=""/>) include the script source before template content is added to the HTML DOM. Each distinct @src URL is included exactly once.
  • JavaScript tags with an @id attribute (<JavaScript id="">function foo() {...}</JavaScript>) are evaluated after all template content is added to the HTML DOM, and only evaluated once per unique ID encountered.
    • This means that you should only have functions inside the tag.
  • JavaScript tags without an @src or @id attribute are evaluated after all template content is added, and once for every time the template containing the <JavaScript> tag was rendered.