XForms/Calculator

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

One of the classic examples of a web application program is a calculator. This one is done with XForms and just a few lines of CSS to draw borders around the table and cells.

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