XForms/Calculator
< XForms
One of the classic examples of a web application program is a calculator.
Program
edit<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xf="http://www.w3.org/2002/xforms"
xmlns:ev="http://www.w3.org/2001/xml-events">
<head>
<!-- Licensed by the w3c.org under the GPL2 -->
<title>Calculator Sample</title>
<xf:model>
<xf:instance>
<equation xmlns="">
<display>0</display>
<displaybuffer>0</displaybuffer>
<first>0</first>
<second>0</second>
<memory>0</memory>
<result />
</equation>
</xf:instance>
</xf:model>
<style type="text/css">
table {
border: thin outset;
}
td {
border: thin inset;
}
.display {
text-align: right;
}
</style>
</head>
<body>
<p>A simple calculator</p>
<table>
<tr>
<td colspan="6" class="display">
<xf:output ref="/equation/display" />
</td>
</tr>
<tr>
<td>
<xf:output ref="/equation/memory">
<xf:label>M:</xf:label>
</xf:output>
</td>
<td />
<td />
<td />
<td colspan="2">
<xf:trigger>
<xf:label>Clear</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/first" value="0" />
<xf:setvalue ref="/equation/second" value="0" />
<xf:setvalue ref="/equation/result" value="0" />
<xf:setvalue ref="/equation/display" value="0" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
<xf:toggle case="add" />
</xf:action>
</xf:trigger>
</td>
</tr>
<tr>
<td>
<xf:trigger>
<xf:label>MC</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/memory" value="0" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>7</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10 + 7" />
<xf:setvalue ref="/equation/display" value="/equation/displaybuffer" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>8</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10 + 8" />
<xf:setvalue ref="/equation/display" value="/equation/displaybuffer" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>9</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10 + 9" />
<xf:setvalue ref="/equation/display" value="/equation/displaybuffer" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>/</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/first" value="/equation/display" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
<xf:toggle case="divide" />
</xf:action>
</xf:trigger>
</td>
<td />
</tr>
<tr>
<td>
<xf:trigger>
<xf:label>MR</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/display" value="/equation/memory" />
<xf:setvalue ref="/equation/displaybuffer" value="/equation/memory" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>4</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10 + 4" />
<xf:setvalue ref="/equation/display" value="/equation/displaybuffer" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>5</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10 + 5" />
<xf:setvalue ref="/equation/display" value="/equation/displaybuffer" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>6</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10 + 6" />
<xf:setvalue ref="/equation/display" value="/equation/displaybuffer" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>*</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/first" value="/equation/display" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
<xf:toggle case="multiply" />
</xf:action>
</xf:trigger>
</td>
</tr>
<tr>
<td>
<xf:trigger>
<xf:label>MS</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/memory" value="/equation/display" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>1</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10 + 1" />
<xf:setvalue ref="/equation/display" value="/equation/displaybuffer" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>2</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10 + 2" />
<xf:setvalue ref="/equation/display" value="/equation/displaybuffer" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>3</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10 + 3" />
<xf:setvalue ref="/equation/display" value="/equation/displaybuffer" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>-</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/first" value="/equation/display" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
<xf:toggle case="subtract" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>1/x</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/display" value="1 div /equation/display" />
</xf:action>
</xf:trigger>
</td>
</tr>
<tr>
<td>
<xf:trigger>
<xf:label>M+</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/memory" value="/equation/memory + /equation/display" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>0</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10" />
<xf:setvalue ref="/equation/display" value="/equation/displaybuffer" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:trigger>
<xf:label>+/-</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/display" value="/equation/display * -1" />
</xf:action>
</xf:trigger>
</td>
<td />
<td>
<xf:trigger>
<xf:label>+</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/first" value="/equation/display" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
<xf:toggle case="add" />
</xf:action>
</xf:trigger>
</td>
<td>
<xf:switch>
<xf:case id="add" selected="true">
<xf:trigger>
<xf:label>=</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/second" value="/equation/displaybuffer" />
<xf:setvalue ref="/equation/result" value="/equation/first + /equation/second" />
<xf:setvalue ref="/equation/display" value="/equation/result" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
</xf:action>
</xf:trigger>
</xf:case>
<xf:case id="subtract">
<xf:trigger>
<xf:label>=</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/second" value="/equation/displaybuffer" />
<xf:setvalue ref="/equation/result" value="/equation/first - /equation/second" />
<xf:setvalue ref="/equation/display" value="/equation/result" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
</xf:action>
</xf:trigger>
</xf:case>
<xf:case id="multiply">
<xf:trigger>
<xf:label>=</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/second" value="/equation/displaybuffer" />
<xf:setvalue ref="/equation/result" value="/equation/first * /equation/second" />
<xf:setvalue ref="/equation/display" value="/equation/result" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
</xf:action>
</xf:trigger>
</xf:case>
<xf:case id="divide">
<xf:trigger>
<xf:label>=</xf:label>
<xf:action ev:event="DOMActivate">
<xf:setvalue ref="/equation/second" value="/equation/displaybuffer" />
<xf:setvalue ref="/equation/result" value="/equation/first div /equation/second" />
<xf:setvalue ref="/equation/display" value="/equation/result" />
<xf:setvalue ref="/equation/displaybuffer" value="0" />
</xf:action>
</xf:trigger>
</xf:case>
</xf:switch>
</td>
</tr>
</table>
</body>
</html>
Discussion
editThe calculator program is about 300 lines long. But much of the code is just telling what actions should happen when a button gets pressed.
The model of the calculator is very simple. Just a five variables:
- display - the visible display
- displaybuffer - a secondary display that is not visible
- first - the first operator
- second - the second operator
- memory - used by the memory function
Here is the trigger for the "5" button:
<td> <xf:trigger> <xf:label>5</xf:label> <xf:action ev:event="DOMActivate"> <xf:setvalue ref="/equation/displaybuffer" value="/equation/displaybuffer * 10 + 5" /> <xf:setvalue ref="/equation/display" value="/equation/displaybuffer" /> </xf:action> </xf:trigger> </td>
The actions for the trigger is to do just two things:
- are to multiply the value of the display buffer by 10 (shifting the digits over by one) and then to add a five.
- copy the value of the display buffer to the display.
There are also scientific function calculators but they have a smiliar structure.
It is interesting to compare a JavaScript version to the XForms version. The JavaScript version is shorter but the JavaScript can take advantage of some of the tools that are available to JavaScript.