blob: 6b4af40615607d823680f1f44819e7b56089763f [file] [log] [blame]
This is the Wookie templating system that can be used to create
consistent user interface widgets. Using this system you can
create a widget with just a few lines of code and then, in order
make a change to the UI of all widgets, simply change the template.
By using these templates you will ensure that your users have a
consistent experience but also have an accessible experience since
these templates are designed with accessibility in mind.
* Installation
To use these templates you need to install Apache Ant. Note, that if
you install Ant on Ubuntu using apt-get you will also need to install
ant-options (this is included on other platforms).
* About Templates
The templates directory contains all the wookie templates. There are,
at the time of writing, two types of template, those that use JQuery
Mobile and those that use Enyo. Enyo templates have names in the form
of "enyoTemplteName" and JQueryMobile have no precursor to their name
(at some point in the future we will probably make the JQuery Mobile
templates conform to the same nameing standard,
e.g. "jqmTemplateName".
In all cases the appropriate "base" template of is the root of all
templates using that framework. Any edit to the template will be
reflected in all widgets that are generated after the edit (excpet
where the edited behaviour has been overridden by a child template).
** Status
The Enyo templates currently use Enyo 2.0 which does not yet have any
UI components. Therefore the Enyo templates are pretty think on the
ground with respect to styling. You can, of course, style everything
using CSS but since the Enyo project predicts a release of the UI
components in "about a month" (from launch in mid Jan 2012) it's
probably best to wait.
The JQuery Mobile templates have been used to build a number of
widgets. They are fairly flexible but the current Ant based build
system presents some limitations and unneccessary
complexity. Furthermore, JQuery Mobile is designed for use only on
mobile platforms. This can present some issues when being displayed on
a desktop machine.
Moving forwards we intend to continue experimenting with both the Enyo
and JQuery Mobile templates. At this stage both implementations should
be considered Alpha quality.
* Creating Widgets from Templates
Regardless of which framework you use the basic techniques for
building widgets is the same. Of course, when you start building real
widgets you will be using techniques specific to the chosen framework,
but for the purposes of this introduction we will not be addressing
these details.
** Building your first template based widget
This section describes how to build your widgets. We start off with a
simple hello world widget and then work through a number of
customisations.
*** Hello World Widget
In this example we will create a simple widget that says hello to the
world (what else would the first widget be?)
* cd WOOKIE_HOME/widgets/templates/widgets
* NB if you want to build your widgets elsewhere then see the advanced topic section below.
* mkdir helloWorld
* create a file called "./widget.properties
* open widget.properties in your favourite editor and add the
following lines (not including bullets):
* template.name=base
* widget.shortname=HelloWorld
* widget.name=Hello World
* widget.description=A very friendly widget to demonstrate how easy it is to build a widget from templates.
* widget.id=http://www.apache.org/widgets/HelloWorld
* Create a file called "content_primary.html" and the following content
* <p>Hello World!</p>
That's it, you have built your first template based widget. Now you
need to generate and deploy it:
* Ensure that Wookie is running locally
* cd [WOOKIE_HOME]/widgets/templates/widgets
* ant generate-all-widgets -Dwidget.include=helloWorld
* take a look at your new widget in your local instance of Wookie
***Note:*** while this example creates the widget in the source tree
of Wookie you should not develop your widgets here under normal
circumstances. Instead you should define your own directory for
widgets within your own project directory, see Advanced Topics, below.
**** What did we just do?
This section describes in some detail what you did above.
***** Widget properties
Widgets are defined by a set of properties. The first set of
properties are used by all widgets and are defined in
"widget.properties" the next are defined in
"../common/widget.properties" the last are the "default.widget.properties"
provided by the template. You created a widget directory and defined
the "widget.properties" file by copying the default properties
provided by the template system.
Most of these properties should be self explanatory, with the
exception of the "template.name". This is the name of the template
your widget will be based on, for this demo we left that unchanged and
defined a few of the basic properties that are used to ensure the user
knows what a widget is when looking at the widget store.
***** Widget content
The default widget properties configures the widget, but do not tell
the template what content to display. Widget templates are intended to
be self-documenting. You can find out what content a template expects
to be provided by reading the appropriate files in the template. At
some point in the future we will generate real documentation from
these template files, but for now you need to do the leg work.
To discover what properties are available to the template we look in
"/templates/base/template_build.xml" and examine the "_init_template"
target. You may see entries such as:
<loadfile property="content.primary"
srcFile="content_primary.html"
failonerror="false">
This entry is saying that a property called "content.primary" is being
defined by the contents of a file called "content_primary.html". In other
words, you should provide a "content_primary.html" file in your widget
definition. This property will be used wherever "${content.primary}"
appears in the template definition. Take a look in the templates
"index.html" to see this in action.
***** Building the widget
The command "ant generate-all-widgets" builds and deploys all your widgets
to a locally running Wookie server. Once this command has successfully
executed you can examine your widget in action.
The property "widget.include" defines a matcher for the widgets which
are to be generated.
** Adding some style
This section builds on the previous tutorial. We will add some simple
styling to the Hello World widget.
Starting in your widget definition directory:
* mkdir style
* create file "style/helloworld.add.css"
* add the following to your css file
* .content-primary { background: yellow; }
* cd ..
* ant generate-all-widgets
Now take a look at your styled hello world widget.
**** What did we just do?
Any css file in the "style" directory with a ".add" name will be added
to template provided stylesheets. This allows you to define new styles
and also to override other styles defined by the templates you are using.
You can also add css files without the ".add". These will be copied
into your widget as separate files. However, the template will be
unaware of these stylesheets so this is only useful if you have some
mechansims by which your widget will be aware of them such as a
javascript theme switcher.
Note that you can use tokens defined in the widget.properties file in
your CSS. Simply use "${property.name}" in your file.
** Being dynamic
This section continues to build on the previous tutorial. In this
section we will add a tiny bit of Javascript that will ask the users
name and say hello to them personally.
Starting in the root directory of your widget do the following:
* mkdir scripts
* create a file called "scripts/user_controller.js"
* Add the content in the Javascript section below to the "user_controller.js" file
* Change the contents of "content_primary.html" to:
* <p>Hello <span id="name">World</span></p>
* cd ..
* ant generate-all-widgets
* Now take a look at your new widget in Wookie
*** Javascript
This is what you need in your "scripts/helloworld_controller.js" file:
var ${widget.shortname}_user_controller = {
init:function() {
name = prompt("Please enter your name","Your Name");
$('#name').html(name);
}
};
$('#home').live('pageshow',function(event) {
${widget.shortname}_user_controller.init();
});
*** What did we just do?
Files in the script folder that end with a "_controller.js" are added
to the "controller.js" files provided by the templates being used. In
this file you can define the controller for your widget.
Note that you must use the ${widget.shortname} token and add a widget
unique string to the object name in order to ensure the controller
object is given a unique name.
We also register an action to take when the page is loaded. In this
case we define function in the controller that prompts for the users
name and replaces the word "world" in the content with that name.
Note that you can add additional libraries to your widget by placing
them in a "lib" directory in the root of the widget directory. Be sure
to add any copyright notices and licenses to a "NOTICE" file and
"legal" directory respectfully.
*** Overriding Template functionality
The scripts provided by your widget implementation can override the
functionality provided by the template. For example, the message
template provides a simple interface for creating and sending
messages. However, there is no way the template can implement the
message send functionality since each widget will use a different
delivery mechanism.
For this reason the ${widget.shortname}_message_controller provides
the following function:
send:function(subject, message) {
alert("Send message:\n\n" + subject + "\n\n" + message);
}
If you want to implement a true message widget you need to override
this function in your widget javascript.
To do this you simply add code such as the following to your widgets
WIDGET_controller.js file:
${widget.shortname}_message_controller.send = function(subject, message) {
// message send code here
}
** Tiled Templates
The Base Template provides functionality that allows it to provide a
separate view that is used when the viewport is below a certain
size. This view can provide reduced information and is designed for
use in views that have multiple widgets on display at the same time.
By default the maximum height and widget for a tiled viewport is 310
pixels. That is, if the viewport is 311 or more pixels in both width
and height it will display the content defined in the #home div of the
page definition. If the viewport is 310 or less pixels in either
height or width then the content in the #tile div will be displayed.
To customise the tile content your widget should provide a
content_tile.html file.
To customise the maximum viewport size for the tile view set the
properties "widget.tile.max.height" and "widget.tile.max.width" (both
default to 310).
** Common Files
When building a family of related widgets you are likely to provide a
set of common files that will be reused by multiple widgets. These can
be placed in a directory called "common" in the same directory as your
widget definitions. All files in this folder will be copied, with
token replacement, into each widget.
Because we use token replacement in these files too, it is possible
for each widget to customise the common files appropriately.
** Advanced Topics
*** Your own widgets directories
You can create a widgets directory anywhere you want it. To create
your own directory for widget definitions simply create the directory
and copy [WOOKIE_HOME]/widgets/templates/widgets_build.xml into your
new directory with the name build.xml.
Now edit your build.xml file so that the value of the
"wookie.root.dir" property so that it points to the root of your
Wookie folder. You should also edit the name property of the root
project element.
Each of your widget definitions will then be contained in a sub-folder
of the directory you created above. That is, you will have a directory
structure like this:
+- widgets
|
+- build.xml
+- widgetDefinition1
| |
| +- widget.properties
| +- ...
|
+- widgetDefinition2
|
+- widget.properties
+- ...
*** Multiple widget directories
You can, optionally, have multiple widget sub-directories in your main
widget directory, each of which can be deployed individually or as a
whole. In order to do this create your additional widget directories
and then edit your build.xml file in your main widgets directory to
ensure that they all get built when calling the target
"generate-all-widgets". There is an exmple in the build.xml file you
created above.
*** Deploying to alternative locations
You can change the location widgets are deployed to by setting the
property "widget.deploy.dir" in the command line or in the widgets
build.xml file.
*** Deploying in compressed or expanded form
By default widgets are deployed in compressed ".wgt" files
only. However, if you want to deploy in uncompressed (expanded) format
you can do so by setting the propery "widget.deploy.expanded" in the
command line or in the widgets build.xml file.
To supress deployment in compressed form it is not quite so easy. You
must first override the widgets_build.xml file (see "Your own widgets
directories" above). In this file you will need to comment out the following line:
<property name="widget.deploy.compressed" value="true"/>
* Building templatised widgets
To build and deploy the widgets run "ant generate-all-widgets" from
within your widgets directory which will generate all known widgets.
Alternatively you can use "ant generate-widgets" which will
ask you which widget definitions directory you wish to generate from,
*** Building selected widgets
When you have lots of widgets it can be useful to build just one, or a
set of related widgets. There are two ways to do this, you can provide
a pattern to filter widgets to be built or you can keep your widgets
in directories and build one directory at a time.
**** Filtering widgets to be built
Adding the following to your ant command will filter all widgets:
-Dwidget.include=WIDGET_PATTERN
where "WIDGET_PATTERN" is an include pattern that describes which
widgets should be generated and deployed. For example,
"ant generate-widgets -Dwidget.include=*browse"
will generate all widgets in a specified directory that have a name ending in "browse".
*** Building single directories
"ant generate-widgets" will ask you to specify which widget directory
is to be used. To avoid having to answer this question you can specify
it in the command line as follows:
"-Dwidget.definitions.dir=DIRECTORY"
* Define new templates
To create a new template simply create a directory in the templates
folder with an appropriate template name and copy
"base/template_build.xml" (for JQuery Mobile template) or
"enyo/base/template_build.xml" (for an Enyo based template) and edit
as follows:
* name attribute of the <project ...> element
* "_generate_from_parent_templates" target (see below)
Next you will need to add any resources the template needs.
*** Template Dependencies
Template can have dependencies on other templates. This allows us to
build up complex templates whilst still ensuring they are easy to maintain.
To define a templates parents simply edit the
"_generate_from_parent_templates" targtet in the
"template_build.xml". The "templates/base/template_build.xml" file has
a commented out example of how to do this.
When generating a widget from a template the parent templates are
processed first. Any resources that a subsequent template provides
will override resources provided by the parent templates. That is, the
most recently produced resource wins.
In general all templates will have the "base" template as a parent
(see below). The "base" template provides the bare minimum we need for
a W3C widget.
*** Configuring templates
By using properties we can create templates that can be customised. A
template will define any number of properties in "template.properties".
These properties can then be reused in any of the files by inserting
"${property.name}". When the template is used to create a widget these
properties will be expanded.
Alternatively properties values can be provided in files. This is
useful for properties that are intended to contain lots of information
or formatting, such as HTML snippets.
Default values for properties are provided in the templates
"deafault.widget.properties" file. This file also serves as documentation.
**** Template content
Each template will define some content that needs to be provided by
the widget definition. This content will be defined in files provided
by the widget definition (see below). The template must define what
these files are and must load them in the "_init_template" target of
the "template_build.xml" file. See the base template for an example of
how to do this.
Note that when a template has parent templates it will inherit all of
the customisation points of each parent. Therefore each widget
definition must provide all the necessary files before it can be
generated.
You can use tokens in the content that will be replaced when a widget
is generated. For example, you can write:
<p>Welcome to the ${widget.name}</p>
You can also call methods in javascript provided by any of the
templates used to create the widget, for example:
<form method="post"
onsubmit="javascript:${widget.shortname}_auth_controller.authenticate(username, password)">
FIXME - bad practice to have JS in HTML file - shoudl be added with load event or JQ/JQM mechanisms. As you point out above
can use tokens in JS
What tokens are available depends on the templates used, but in
generally anything that appears in the widget.properties file can be
used as a token.
*** CSS
**** Default CSS
Each template provides a minimum of CSS styles to allow widgets to be
consistent in their styling. Your own templates and widgets based on
these can override these styles as necessary, see below.
**** Media Queries
The root "default.widget.properties" file provides a number of useful
properties for performing media queries in your CSS and
Javascript. These properties define many common form factors in
various orientations. Please check the file for the full list.
To use these in your CSS simply include them as properties. for example:
#+BEGIN_SRC CSS
@media ${widget.media.screen.wide} {
/* Styles */
}
#+END_SRC
To use them in your javascript you can use the JQuery Mobile media function, for example:
#+BEGINE_SRC Javascript
if ($.mobile.media("${widget.media.screen.wide}")) {
$('#searchPanel').trigger('expand');
}
#+END_SRC
**** Custom CSS
Each template can provide complete CSS files which will be copied into
widgets just like any other resources. In addition templates can add
to or modify CSS rules defined in parent templates.
To create additive CSS simply add files with names in the form of
"foo.add.css". The contents of these files will be added to the start
of a final CSS file called "all.css".
The base template provides a link to the "all.css" in its "index.html" file. Therefore, if your template
should extends the base template you do not need to explicitly include "all.css". However, any additional
CSS files you provide will not be included automatically so you will need to provide a new "index.html" with
appropriate style references.
Note, there is a feature request to improve the handling of CSS provided by sub-templates or widgets see:
https://issues.apache.org/jira/browse/WOOKIE-276
*** Scripts
Each template can provide one or more controller Javascript
definitions. The controller code is where the UI and the data
model are wired together.
Templates should provide any controller code in files named
"PROJECTNAME_controller.js". When a widget is
generated from a template all its controller javascript files, along
with those defined in any parents, are concatenated into a single
"controller.js" file. This allows each template to define Javascript
controller objects.
It is important that
each controller object has a unique name. Therefore, we recommend
that you define your controllers using tokens that will be replaced
during widget generation. For example, the login template defines an
"auth_controller.js" which provides a controller for authorisation
activities. This file defines an object as follows:
var ${widget.shortname}_auth_controller = {
basic_auth:function(username, password) {
...
}
}
We then need to use the same token replacement approach when this
object is used. For example, calling the basid_auth function defined
above is done with "${widget.shortname}_auth_controller.basic_auth(...)"
You can also provide other JS files by adding them to the scripts
directory.
As with CSS the default index.html provided by the base template loads
the controller.js file. It does not load other JS you might provide in
other templates. In order to to ensure these are loaded you need to
provide a new index.html with the appropriate <script...> tags.
A feature request has been made to enhance this handling of Javascript,
see https://issues.apache.org/jira/browse/WOOKIE-277
*** Test widgets
When creating a new template you should also create a new test
widget. Test widgets are used to demonstrate and test the
functionality of widget templates.
To create a new test widget simply create a new directory in the
"testWidgets" directory, using a name of the form "fooTestWidget"
where "foo" is replaced by the name of the template under test. Then
copy the "widget.properties" file from the most appropriate existing
test widget (usually this will be one of the new templates parents).
You'll need to edit, at least:
* "template.name"
* "widget.shortname"
* "widget.name" properties.
You will also need to add any new new properties and content
descriptions that are necessary for the widget to work.
**** Building test widgets
To test changes to the templates run "ant generate-test-widgets". This
will generate and deploy one or more test widgets using the latest
versions of the templates.
In order to make the build faster in a development process you can
define which templates are to be built using the "widget.include"
property discussed in the next section.
* Widget API
The base template provides a shim implementation of the widget API
used in Wookie (window.widget.*). This shim is only loaded if an api
object has not been injected by the host enviroment, a happens when
the widget is deployed by Wookie.
If you want to build a custom version of the widget for a given
platform you can provide your own implementation of the widget API by
injecting your own window.widget object at runtime or by providing
your own implementation in widget_api.js at build time.