Install the server!
Solutions and a troubleshooting guide at the back.
Quick reference guide
The tutorial is written in XForms
But first a very quick introduction to XForms
An XForm is an XHTML document with an extra element in the <head> element, called <model> that contains 'instances' of data and details of that data, such as types and relationships, and then a group of 'controls' in the body of the XHTML that use that data:
<?xml-stylesheet href="../xsltforms/xsltforms.xsl" type="text/xsl"?> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://www.w3.org/1999/xhtml" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xf="http://www.w3.org/2002/xforms"> <head> <title>Your title here</title> <style type="text/css"> CSS is used for styling </style> <model xmlns="http://www.w3.org/2002/xforms"> <instance>...</instance> <instance>...</instance> other stuff </model> </head> <body> <group xmlns="http://www.w3.org/2002/xforms"> Controls go here. </group> </body> </html>
The file has to be served up as an XML type: the easiest way is to use the file extension ".xhtml".
Within the model element in the head, you have one or more instances of data:
<instance> <data xmlns=""> <product></product> <amount></amount> <due></due> <delivered></delivered> </data> </instance>
Types can be assigned to the elements using bind elements, just after the </instance>:
<bind ref="amount" type="integer"/> <bind ref="due" type="date"/>
The default type if you don't specify one is "string".
Input controls, which you put in the body of an XForm, bind to an element in an instance. For example:
<input ref="product"><label>product</label></input> <input ref="amount"><label>amount</label></input> <input ref="due"><label>due</label></input>
The controls adapt to the type.
By default an input updates the instance when you tab out of the control or
otherwise leave it, but it will update at each keystroke if you add an
attribute incremental="true"
:
<input ref="product" incremental="true"><label>product</label></input>
The 'delivered' element in the instance hasn't been given a type, nor an input control attached.
Make it type 'boolean', and add an input control for it.
The output control uses a ref
attribute in the same way to
access elements and attributes in an instance; for full expressions you use a
value
attribute:
<output ref="price"/> <output ref="product/@amount"/> <output value="product/@amount * price"/>
In both cases there is an optional label:
<output value="product/@amount * price"><label>Total price</label></output>
Both ref
and value
are XPath expressions, which
are like URLs in many ways: use "/" to go down, use ".." to go up. "@" for
attributes, and "." for 'the current thing'.
So to access the amount attribute you use
ref="product/@amount"
.
Move the 'delivered' element so that it is an attribute of the 'due' element
Hint: there are 4 places you have to change:
In the previous examples, the instance contained its values inline, but you can obtain values from files, or over the internet:
<instance resource="currencies.xml"/> <instance resource="http://www.floatrates.com/daily/eur.xml"/>
By default, outputs directly after each other will abut.
You can add characters between the outputs:
<output ref="a"/>, <output ref="b"/>
or combine them using value="..."
and the
concat(...)
function:
<output value="concat('(', a, ', ', b, ')')"/>
or use CSS:
<output class="line" ref="a"/>
Output what the publication date is, and how many <item> elements there are.
If there is a repeated element in an instance, the repeat control treats each in turn:
<repeat ref="country"> <output ref="name"/>: <output ref="capital"/> </repeat>
Within the repeat you use the context of the containing expression; you say:
<output ref="name"/>
and not
<output ref="country/name"/>
This is true throughout, whereever controls are nested.
In the currencies XForm, for each currency (each <item> element), output the value of the <title> element.
You can have more than one instance. To identify which is which you give
them id
attributes:
<instance id="currencies" resource="currencies.xml"/> <instance id="search"> <search xmlns=""> <q/> </search> </instance>
To say which instance you are referring to, you use the
instance
function with the id:
<input ref="instance('search')/q"><label>Search</label></input>
You don't have to do this for the textually first instance, which is always the default instance.
You can select a subset of nodes by filtering:
<repeat ref="nodeset[condition]">...
For example, since the function contains(a, b)
reports if a
contains b anywhere in its content, then
<repeat ref="item[contains(., instance('search')/q)]">
repeats only over those item elements whose content contains the value in the q element in the search instance.
In the currencies XForm add a search field, and only display those items that match the search field.
Hint 1: You can copy and paste a lot of what you need from the page.
Hint 2: Use incremental="true"
on the input control.
Sometimes you want to offer the user a choice of values to input, rather than having to type them in.
There are two controls, select
, which allows you to select a
set of values, and select1
for a single value. We will just treat
select1
for now.
<select1 ref="result"> <label>Currency</label> <item> <label>Euro</label> <value>EUR</value> </item> <item> <label>Great Britain Pound</label> <value>GBP</value> </item> ...etc... </select1>
This can be displayed in three different ways, using
<select1 ref="result" appearance="full">
or appearance="compact", appearance="minimal".
In the currencies XForm where you added a search field, now add a
select1
.
The ref of the select1 (the result in other words) should be the element you used for the search field.
It is possible to get the items for the select1
from an
instance. For example with this instance:
<instance id="currencies"> <valutas xmlns=""> <valuta name="Euro">EUR</valuta> <valuta name="Great Britain Pound">GBP</valuta> ...etc... </valutas> </instance>
you can create a select1
like this:
<select1 ref="result"> <label>Currency</label> <itemset ref="instance('currencies')/valuta"> <label ref="@name"/> <value ref="."/> </itemset> </select1>
Watch out for the context of the itemset!
In the currencies XForm where you added a select1, make the select1 dynamic, where the items are loaded from the currencies instance.
The labels should come from the <targetName> elements, and the values from the <targetCurrency> elements.
The ref of the select1 (the result in other words) should still be the element you used for the search field.
Hint: Don't forget to use instance('currencies')
on the
<itemset>.
Most things happen automatically; however events are dispatched that you can listen for and react to.
One of the most useful is xforms-ready
, that announces that the
XForm has started up and is initialised. You can listen for this in order to
initialise values.
To listen for an event, you use an action
element with the name
of the event you want to react to:
<action ev:event="xforms-ready"/> ... </action>
An action element should be placed within the element that the event is
dispatched to. In the case of xforms-ready
, that is the
model
element.
Within the action element you can put elements that cause things to happen
(also called actions). A useful one is setvalue
, that sets a value
in an instance. For example
<action ev:event="xforms-ready"/> <setvalue ref="time" value="local-dateTime()"/> </action>
Change the given XForm so that it only displays the time, and not the date and time.
Hint: The function substring(s, start, length)
returns the
string s from position start, of the given length in characters.
Note: Because the event attribute uses a namespace prefix, that has to be declared somewhere, usually on the <html> element at the top: xmlns:ev="http://www.w3.org/2001/xml-events"
A trigger is a control which by default looks like a button. Whenever the
user clicks on it, a DOMActivate
event is dispatched to it. For
example:
<trigger> <label>Click me</label> <action ev:event="DOMActivate"> <setvalue ref="count" value=". + 1"/> </action> </trigger>
appearance="minimal" makes a trigger just text, and not a button.
Add a trigger to the previous exercise that updates the time whenever it is clicked on.
<instance id="currencies" resource="http://www.floatrates.com/daily/eur.xml"/>
At initialisation, XForms goes out over the internet, gets the resource, parses the XML and puts the result into the instance.
This is a just a tiny bit of submission.
Submission allows you to send data from an instance to a server, collect the result, and store it somewhere. It has three essential parts:
A <submission>
element, which is put in the
<model>
element, specifies these things, but only actually
does them when you tell it to.
It has a lot of available attributes to cover a lot of options. Principally:
ref="..."
(default is
the default instance); if you don't want to send any data, but just get
data from somewhere, you use serialization="none"
;resource="..."
;replace="..."
; if you don't care about any data returned, you
use replace="none"
.So here is a submission that describes the same as the instance above:
<submission resource="http://www.floatrates.com/daily/eur.xml" serialization="none" method="get" replace="instance" instance="currencies"/>
As another example, this replaces the whole page with the resource, the equivalent of the <load> action in the previous section:
<submission resource="events-exercise.xhtml" serialization="none" method="get" replace="all"/>
You can give a submission a name with an id attribute:
<submission id="geteur" ...
The easiest way is a submit control:
<submit submission="geteur"> <label>Submit</label> </submit>
which will initiate the submission called 'geteur'. This is actually just a shorthand for
<trigger> <label>Submit</label> <action ev:event="DOMActivate"> <send submission="geteur"/> </action> </trigger>
Take the currencies XForm, and add two submit controls, one that loads
http://www.floatrates.com/daily/eur.xml
into the currencies
instance, and the other that loads
http://www.floatrates.com/daily/usd.xml
(if you don't have an
internet connection, use the file usd.xml
instead).
Hint: you need two <submission> elements.
After a submission is complete, an event is sent to the <submission>
element, either xforms-submit-done
, which says that the submission
was successful, and which you can usually ignore, or
xforms-submit-error
, which is a good idea to listen for:
<submission resource="whatever" ...> <action ev:event="xforms-submit-error"> ... </action> </submission>
Some events when they happen have extra information available, which you can
access with the event(...)
function. For example, for
xforms-submit-error
, amongst others has:
error-type
, telling the type of error,error-message
, if there are more details,response-status-code
from the server, (e.g. 404 for file not
found),response-reason-phrase
from the server, such as "file not
found", andresource-uri
, which URI was used for the submission.You can do various things with this information, such as display it with a
<message>
action:
<submission resource="..." ...> <action ev:event="xforms-submit-error"> <message>Submission error. error-type: <output value="event('error-type')"/> error-message: <output value="event('error-message')"/> etc </message> </action> </submission>
or store the values in an error instance:
<submission resource="whatever" ...> <action ev:event="xforms-submit-error"> <setvalue ref="instance('errors')/type" value="event('error-type')"/> ... etc ... </action> </submission>
Take the currencies XForm, now with two submits, and add actions to both
submission elements to catch xforms-submit-error
.
Try it out by changing one of the submissions so that the resource attribute has a non-existent or rubbish URL, or no resource attribute at all. What happens?
It is possible to specify that a value in an instance depends on other values; whenever any of those values change, the dependent value gets updated.
<bind ref="subtotal" calculate="../amount * ../price"/> <bind ref="tax" calculate="../subtotal * (../taxrate div 100)"/> <bind ref="shipping" calculate="if(../subtotal > 0 and ../subtotal < ../shippingrate/@below, ../shippingrate, 0)"/> <bind ref="total" calculate="round((../subtotal + ../tax + ../shipping)*100) div 100"/>
The ".."s are needed because of the context.
Go back to the currencies XForm with the two submissions and a
select1
at the top.
<code/>
,
and <url/>
. select1
, and point the ref
to the
new code
element. url
element that calculates
the URL "http://www.floatrates.com/daily/eur.xml" etc from the
code
element. select1
.Hint 1: use concat(...)
.
Hint 2: Since calculations are in attributes with " double quotes, enclose strings in calculations with ' single quotes.
Hint 3: The currency code is in capitals, but the URL in lowercase. Use the
lower-case(s)
function to return the lowercase version of the
currency code.
Hint 4: Don't forget to use instance('search')
in the bind
element.
To inject a value into an element, you use the <output>
control. For example, the previous section used an output in a label:
<label>Tax at <output ref="../taxrate"/>%</label>
However, sometimes you want to inject a value into an attribute.
For this there are Attribute Value Templates (AVTs).
Suppose you want to output a balance with a red background if it is negative, and in green otherwise. So you define two CSS rules for negative and positive values, and then on the output, add an AVT to calculate which class to use:
<output class="{if(balance < 0, 'negative', 'positive')}" ref="balance"/>
Go back to the currencies XForm again.
submission
elements and change the
resource
attribute so that it uses an AVT with the value of
the newly added <url>
element. <submit>
elements, and change the
other to refer to the remaining submission.submit
control so that it
displays the value of the <code>
element. Reminder: Don't forget to use instance('search')
.
Hint: You should initialise <code>
with EUR, otherwise
initially the submit button will look odd.
If you have a control bound to a value (i.e. with a ref
),
whenever that value changes, whether by this control or another, an
xforms-value-changed
event is sent to the control. So you can
listen for that event, and respond to it.
<input ref="balance"> <label>Balance</label> <action ev:event="xforms-value-changed"> <setvalue ref="../changes" value=". + 1"/> <setvalue ref="../time" value="local-dateTime()"/> </action> </input>
Go back to the currencies XForm again.
Add an action for xforms-value-changed
to the top select1. The
action should do a <send/>
for the submission.
Delete the <submit/>
control.
Hint: you can put the action element after the itemset
element.
You can add a new element to an instance using the
<insert/>
action:
<trigger> <label>+</label> <hint>Add an element</hint> <action ev:event="DOMActivate"> <insert ref="buy"/> </action> </trigger>
The default is just to duplicate the last element.
You may want to clear the content of the newly added element:
<trigger> <label>+</label> <hint>Add an element</hint> <action ev:event="DOMActivate"> <insert ref="buy"/> <setvalue ref="buy[last()]"/> </action> </trigger>
If the list is empty, an insert won't do anything, because there is no last element to duplicate. The more general case takes the element from elsewhere, for example a separate instance:
<instance id="blank"> <data xmlns=""> <buy/> </data> </instance>
and then the action is:
<action ev:event="DOMActivate"> <insert ref="buy" origin="instance('blank')/buy"/> </action>
The default is to add it at the end of the list, but you can insert new elements anywhere. For example, before the first element:
<insert ref="buy[1]" position="before" origin="instance('blank')/buy"/>
Also "after".
Add a trigger to add elements to the supplied To-do list XForm
Deleting elements is very similar. The only thing to watch out for is that
<delete ref="buy"/>
deletes ALL the buy elements. If there's more than one, and you want to delete just one, you have to specify which:
<delete ref="buy[1]"/> <delete ref="buy[last()]"/> <delete ref="buy[. = 'cheese']"/>
One way is to delete the current element:
<repeat ref="buy"> <input class="inline" ref="."/> <trigger> <label>×</label> <hint>delete</hint> <action ev:event="DOMActivate"> <delete ref="."/> </action> </trigger> </repeat>
Add a trigger to delete entries on the previous exercise.
When a value has a calculate
property it becomes 'readonly'.
<bind ref="total" type="double" calculate="sum(../value)"/>
This means that if you attach it to an input control or similar, the control won't let you change the value.
While this is the default behaviour for calculate, it is also possible to make other values readonly if you need. Either always:
<bind ref="price" readonly="true()"/>
or conditionally, for example:
<bind ref="price" readonly="../edit != true()"/>
Change the previous exercise so that once a task has been marked "done", the other fields are no longer editable.
To save data, you use submission
, this time to put data
somewhere, rather than get it:
<submission id="save" \ method="put" resource="list.xml" ref="instance('list')" replace="none"/>
This saves the data in the instance into a file called list.xml.
To get it back, we do the reverse:
<submission id="restore" method="get" resource="list.xml" replace="instance" instance="list" serialization="none"/>
Be aware that not all servers accept the PUT method.
Add submissions and submit controls to the previous exercise to save and restore.
For extra points, add handlers for xforms-submit-error
.
You can specify the conditions under which certain data is relevant. For instance, a credit-card number is only relevant when paying by credit card:
<bind ref="cc" relevant="../payment='credit'"/>
In an address the state is only relevant if the country is the USA:
<bind ref="state" relevant="../country='USA'"/>
Relevance has two main effects:
relevant="false"
on the submission element).It's worth noting that there's another sort of relevance as well: if a control is bound to an element that just isn't there (for instance if it's been deleted), the control is also not shown. If the element gets re-inserted, the control will return.
A similar data property is required
.
If a value is required and hasn't (yet) got a value, it is displayed in a different way.
By default you can't submit data containing empty required values (it gets
reported as a validation error); again, you can override this with
validate="false"
on the submission element.
<bind ref="city" required="true()"/> <bind ref="state" required="../country='USA'"/>
Relevance has priority over required: if something is required but not relevant, it's not a problem if it hasn't got a value.
Change the previous example so that only tasks that have not been completed are shown.
Hint: The easiest way to see if a value x is true or not is with "x !=
'true'
" or "x != true()
". This is because booleans can have
three states: true, false, or not set.
You've seen how to bind a type to a value:
<bind ref="low" type="integer"/>
and how to require that a value is not empty:
<bind ref="name" required="true()"/>
There is a third property you can attach, a constraint:
<bind ref="low" type="integer" constraint=". > 1 and . < ../high"/> <bind ref="high" type="integer" constraint=". < 100"/>
These three properties determine whether a value is valid or not. If a value is not valid, controls attached to it will be displayed accordingly.
Data that contains non-valid values cannot be submitted (unless you add
validate="false"
to the submission element).
Any control that is bound to a value can include an alert
element to warn about validity:
<input ref="low" incremental="true"> <label>low</label> <alert>must be an integer, above 1 and below <output ref="../high"/></alert> </input> <input ref="high" incremental="true"> <label>high</label> <alert>must be an integer, below 100 and above <output ref="../low"/></alert> </input>
Add suitable alerts to the supplied XForm.
Extra points for making it adapt to the situation.
Hint: translate(a, b, '')
returns the string a with all
examples of the characters in b removed.
Dates and times have an interesting representation,
local-dateTime(): 2023-04-22T04:09:34+02:00 local-date(): 2023-04-22+02:00
The clever thing about this representation is that dates
and
dateTimes
are lexically ordered, so you can compare them with the
usual < and > operators.
For instance, you have a list of values recorded on certain dates, sorted on the date:
<values> <value date="2023-05-01">100</value> <value date="2023-06-19">98</value> <value date="2023-07-21">102</value> <value date="2023-08-23">101</value> </values>
If you want to add a new value at the right place in the list, you insert it AFTER the elements whose dates are less:
<insert ref="value[@date < instance('new')/value/@date]" position="after" origin="instance('new')/value" />
or equivalently before items whose dates are greater:
<insert ref="value[@date > instance('new')/value/@date]" position="before" origin="instance('new')/value" />
Go back to the To-do list XForm, and add a class with an AVT to the output that shows up overdue tasks.
Hint: Use a CSS rule like ".overdue input {background-color: yellow}"
The save control for the To Do list allows you to save the data to a file, but is there some way to know whether it needs to be saved, in other words whether an instance has changed?
We've met the xforms-value-changed
event already, and as we
mentioned, it gets dispatched to the control that is bound to the value, so we
put the handler in the content of that element:
<input ref="name"> <label>Name</label> <action ev:event="xforms-value-changed"> <setvalue ref="instance('admin')/changed">true</setvalue> </action> </input>
However, many events 'bubble': from the element they are dispatched to they move up the tree of elements. So if we do this:
<action ev:event="xforms-value-changed"> <setvalue ref="instance('admin')/changed">true</setvalue> </action> <input ref="name"><label>Amount</label></input> <input ref="street"><label>Street</label></input>
then the action will receive the events from both inputs. It won't be able to tell them apart, but it will know that something has changed. So here we do that for our earlier example of a shopping list, with the action positioned within the repeat control:
<repeat ref="buy"> <action ev:event="xforms-value-changed"> <setvalue ref="instance('admin')/changed">true</setvalue> </action> ...
You have to be careful you put it so that you catch just the events you are interested in. If you put it above the <repeat> then you also catch events from controls at the same level as the repeat.
The events for elements being inserted and deleted are sent to the instance
itself, and are xforms-insert
and xforms-delete
.
Clearly, you can't put the action inside the instance content, since it would
then appear to be part of the instance data, so you use the instance id:
<action ev:event="xforms-insert" ev:listener="list">
It otherwise works just the same. (In fact you can do this with all <action> elements if you wish). So now the complete picture is:
<action ev:event="xforms-insert" ev:listener="list"> <setvalue ref="instance('admin')/changed">true</setvalue> </action> <action ev:event="xforms-delete" ev:listener="list"> <setvalue ref="instance('admin')/changed">true</setvalue> </action> ... <repeat ref="buy"> <action ev:event="xforms-value-changed"> <setvalue ref="instance('admin')/changed">true</setvalue> </action> ...
Go back to the To-do list XForm with the save button, and make it so that the save button's label indicates whether the data should be saved or not.
Hint: Don't forget to clear the changed value after a successful save or restore (listen for the xforms-submit-done event).
Warning: You may want to use <output value=""/>
and not
<output ref=""/>
in the label, because a ref
generates an xforms-value-changed
event whenever its referenced
value changes, and value=""
doesn't.
The <select>
control was mentioned earlier. It is just
like <select1>
except that you can select several values,
and they are combined into a string:
<select ref="shopping"> <label>Shopping</label> <item> <label>Bread</label> <value>bread</value> </item> <item> <label>Butter</label> <value>butter</value> </item> ... etc. ... </select> <output ref="shopping"><label>Result</label></output>
Just like with select1, you can get the items from an instance with
<itemset>
in exactly the same way:
<instance id="items"> <data xmlns=""> <buy>bread</buy> <buy>butter</buy> <buy>milk</buy> <buy>cheese</buy> </data> </instance> ... <select ref="shopping"> <label>Shopping</label> <itemset ref="instance('items')/buy"> <label ref="."/> <value ref="."/> </itemset> </select>
From a data-structuring point of view, instead of a result like
<shopping>Bread Bananas</shopping>
it can be far more useful to have structured data, like
<shopping> <buy>Bread</buy> <buy>Bananas</buy> </shopping>
To achieve this we only have to change one thing: we change the
<value>
into <copy>
:
<select ref="shopping"> <itemset ref="instance('items')/buy"> <label ref="."/> <copy ref="."/> </itemset> </select>
Then when an item is selected, rather than just the value of the element being used, the whole element is used, including attributes and child elements. Selecting and deselecting is then like a restricted version of insert and delete.
This means that we also have to change how the result is output, from:
<output ref="shopping"/>
to:
<repeat ref="shopping/buy"> <output ref="."/> </repeat>
Take the supplied example of the To Do List and create a <select> control for deciding which tasks to do today. Take the data from the to-do-list file. Use the name element for the labels.
When we talked about relevance, we pointed out that controls that are not relevant are not displayed. You can put this to good use. For instance:
<instance> <data xmlns=""> <visible>yes</visible> </data> </instance> <bind ref="visible" relevant=". = 'yes'"/> ... <group ref="visible"> All content in this group is only visible when 'visible' has the value 'yes'. </group> <trigger> <label><output value="if(visible = 'yes', 'hide', 'show')"/></label> <action ev:event="DOMActivate"> <setvalue ref="visible" value="if(. = 'yes', 'no', 'yes'"/> </action> </trigger>
However, there is another way to control visibility: the <switch> control:
<switch> <case id="home"> ... </case> <case id="products"> ... </case> ... </switch>
With this control, exactly one of the cases is visible at any time; by
default initially the first one. To change cases, you use the action
<toggle case="..."/>
:
<trigger> <label>Home</label> <action ev:event="DOMActivate"> <toggle case="home"/> </action> </trigger>
Take any To Do List, and make it so that the button to add a new task uses <switch> to expose a separate dialogue to enter details of the new task before adding it to the list. In that dialogue include an [add] button, and a [cancel] button.
It is also possible to dispatch and catch your own events. You dispatch them
with the <dispatch>
action to an element with a particular
id:
<dispatch name="SAVE" targetid="element"/>
and catch them in the normal way.
For instance, if there are several places where you are catching an event and performing the same action, you could instead dispatch your own event to a central place to handle the event.
A good example is catching changes to an instance, which as you know can be
reported by xforms-insert
, xforms-delete
, and
xforms-value-changed
events.
So you can do:
<dispatch name="CHANGE" targetid="M" ev:event="xforms-insert"/> <dispatch name="CHANGE" targetid="M" ev:event="xforms-delete"/> <dispatch name="CHANGE" targetid="M" ev:event="xforms-value-changed"/>
and then catch it in the model with id M:
<model id="M"> ... <action ev:event="CHANGE"> ... </action> </model>
It is possible to dispatch an event with a delay, measured in milliseconds:
<dispatch name="SAVE" targetid="M" delay="10000"/>
which will dispatch the SAVE event after 10 seconds. For instance, suppose we want to make a clock. At start up we send a TICK event:
<action ev:event="xforms-ready"> <dispatch name="TICK" targetid="M"/> </action>
We respond to that event by setting a value to the current time, and dispatching another TICK with a delay of a second:
<action ev:event="TICK"> <setvalue ref="time" value="local-dateTime()"/> <dispatch name="TICK" targetid="M" delay="1000"/></action>
Then if we output the time value, it will update every second.
In the last version of the To-do list you changed it so that the save button's label indicates whether the data should be saved or not.
Now make it so that if the button already indicates that the data should be saved, do nothing more, but otherwise change it and dispatch a SAVE event with a delay of let's say 10 seconds.
When you catch the SAVE event, if the button still indicates the data needs to be saved, save the data automatically.
We've come to the end of this tutorial.
The next place to look is Introduction to XForms which has links to a number of larger examples of the development of XForms apps, such as games, calendars, the use of SVG, and several others.