XForms/Calculator

From Wikibooks, open books for an open world
Jump to navigation Jump to search

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[edit]

The 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:

  1. are to multiply the value of the display buffer by 10 (shifting the digits over by one) and then to add a five.
  2. 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.

References[edit]

Hixie's Natural Log

Next Page: Crime Profile | Previous Page: Invoice Manager

Home: XForms