--- ./drupal_cvs/webform.module	2005-03-09 12:42:59.408796800 +1100
+++ ./webform.module	2005-03-20 21:04:56.693428800 +1100
@@ -23,7 +21,7 @@

   switch ($section) {
   case 'admin/settings/webform':
-    $output = t("Webforms are forms and questionnaires. To add one select <strong>create content -&gt; webform</strong>. Below you can set diffrent security and debug options.");
+    $output = t("Webforms are forms and questionnaires. To add one select <strong>create content -&gt; webform</strong>. Below you can set different security and debug options.");
     break;
   case 'admin/help#webform':
     $output = t("<p>This module lets you create forms or questionnaires and define there content. Submissions from these forms are stored in the database and optionaly also send by e-mail to a predefined address.</p>
@@ -119,7 +114,7 @@
   // Lazy Initializer

   db_query("INSERT INTO {webform} (nid, confirm, email, email_from, email_subject) VALUES (%d, '%s', '%s', '%s', '%s')",
-           $node->nid, $node->confirm, $node->email, $node->email_from, $node->email_subject);
+           $node->nid, $node->confirmation, $node->email, $node->email_from, $node->email_subject);

   if( is_array($node->webformcomponents_name) && !empty($node->webformcomponents_name)) {
     foreach($node->webformcomponents_name as $key => $name) {
@@ -162,7 +157,7 @@
   _webform_database_lazy_update();

   db_query("UPDATE {webform} SET confirm = '%s', email = '%s', email_from = '%s', email_subject = '%s' ".
-           "WHERE nid = %d", $node->confirm, $node->email, trim($node->email_from), trim($node->email_subject), $node->nid);
+           "WHERE nid = %d", $node->confirmation, $node->email, trim($node->email_from), trim($node->email_subject), $node->nid);

   db_query("DELETE FROM {webform_component} WHERE nid = %d", $node->nid);

@@ -194,9 +189,9 @@
   db_query("DELETE FROM {webform} WHERE nid = %d", $node->nid);
   db_query("DELETE FROM {webform_component} WHERE nid = %d", $node->nid);
   _webform_role_node_delete($node->nid);
+  watchdog('webform','webform "' . $node->title . '" deleted');
 }

-
 function webform_load($node) {
   // load

@@ -204,7 +199,7 @@

   _webform_database_lazy_update();

-  $page = db_fetch_object(db_query("SELECT confirm, email, email_from, email_subject FROM {webform} WHERE nid = %d", $node->nid));
+  $page = db_fetch_object(db_query("SELECT confirm as confirmation, email, email_from, email_subject FROM {webform} WHERE nid = %d", $node->nid));

   $result = db_query('SELECT cid, name, type, value, extra, mandatory, weight '.
                      ' FROM {webform_component} '.
@@ -249,6 +245,15 @@
                          '<br />'.t("DEBUG for the webform module") . '<br />' .
                          t('Set this option to "Log submissions" to log all submissions in the watchdog. Set it to "Full debug" to print debug info on submission. You probably want to leave this option on "OFF".'));

+  $output .= form_checkbox(t("Show main webform menu"), "webform_show_main_menu",1,
+                         variable_get("webform_show_main_menu", true),
+                         t('Select the visibility of the main webform item on the main menu'));
+/*
+  $output .= form_checkbox(t("Show node analysis menu"), "webform_show_node_analysis_menu",1,
+                         variable_get("webform_show_node_analysis_menu", true),
+                         t('Select the visibility of the webform node analysis menu'));
+*/
+
   return $output;
 } // end function webform_settings

@@ -269,7 +274,7 @@
     $old_edit = unserialize($_POST['old_edit']);
     $edit = array_merge($_POST['edit'], $old_edit);
     // If we are adding a new componenet or removing a old one
-    // we have to do what the framework would do for us so we don't loose
+    // we have to do what the framework would do for us so we don't lose
     // information.
     $node = array2object($edit);

@@ -295,9 +300,9 @@
   }

   // Validate confirm field
-  if (isset($node->confirm)) {
-    if (!$node->confirm) {
-      form_set_error('confirm', t('You have to specify a confimation message.'));
+  if (isset($node->confirmation)) {
+    if (!$node->confirmation) {
+      form_set_error('confirmation', t('You have to specify a confimation message.'));
       return $false;
     }
   }
@@ -315,7 +320,6 @@

   $items = array();

-  // create content -> webfor
   $items[] = array('path' => 'node/add/webform', 'title' => t('webform'),
                    'access' => user_access('maintain webforms'));

@@ -330,12 +334,17 @@
                    'type' => MENU_CALLBACK,
                    'access' => ($user->uid == 1)); // Access only for the "admin" user.

-  // Submission listning
+
+  // Submission listening
+
+  if(variable_get("webform_show_main_menu",true)){
   $items[] = array('path' => 'webform', 'title' => t('webform'),
                    'callback' => 'webform_page',
                    'type' => MENU_NORMAL_ITEM,
                    'access' => user_access('maintain webforms'));
+  }

+  /*
   $items[] = array('path' => 'webform/table', 'title' => t('table'),
                    'callback' => 'webform_reporttable',
                    'type' => MENU_CALLBACK,
@@ -346,6 +355,40 @@
                    'type' => MENU_CALLBACK,
                    'access' => user_access('maintain webforms'));

+  */
+
+  if (arg(0) == 'node' && is_numeric(arg(1))) {
+    $node = node_load(array('nid' => arg(1)));
+    if ($node->nid) {
+
+
+      $items[] = array('path' => 'node/' . $node->nid . '/results', 'title' => t('results'),
+                             'callback' => 'webform_results', 'access' => user_access('view webforms'),
+                             'type' => MENU_LOCAL_TASK, 'weight' => 3);
+
+      $items[] = array('path' => 'node/' . $node->nid . '/results/clear', 'title' => t('clear'),
+                             'callback' => 'webform_results',
+                             'type' => MENU_CALLBACK,
+                             'access' => ($user->uid == 1)); // Access only for the "admin" user.
+
+      $items[] = array('path' => 'node/'.$node->nid.'/results/submissions', 'title' => t('submissions'),
+                             'callback' => 'webform_results', 'access' => user_access('maintain webforms'),
+                             'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => 4);
+
+      $items[] = array('path' => 'node/'.$node->nid.'/results/analysis', 'title' => t('analysis'),
+                             'callback' => 'webform_results', 'access' => user_access('maintain webforms'),
+                             'type' => MENU_LOCAL_TASK, 'weight' => 5);
+
+      $items[] = array('path' => 'node/'.$node->nid.'/results/table', 'title' => t('table'),
+                             'callback' => 'webform_results', 'access' => user_access('maintain webforms'),
+                             'type' => MENU_LOCAL_TASK, 'weight' => 6);
+
+      $items[] = array('path' => 'node/'.$node->nid.'/results/download', 'title' => t('download'),
+                             'callback' => 'webform_results', 'access' => user_access('maintain webforms'),
+                             'type' => MENU_LOCAL_TASK, 'weight' => 7);
+    }
+  }
+
   return $items;
 }

@@ -356,7 +399,7 @@
 function webform_link($type, $node = 0, $main = 0) {
   if ( $node->type == 'webform' ) {
     if ( $main == 1) {
-      $links[] = l(t('view form'), "node/$node->nid", array('title' => t('View this form.'), 'class' => 'read-more'));
+      $links[] = l(t('go to form'), "node/$node->nid", array('title' => t('View this form.'), 'class' => 'read-more'));
     }
     if (!$main && arg(0) == 'webform' && arg(1) == 'done') {
       $links[] = l(t('Go back to the form'), 'node/'.$node->nid);
@@ -396,10 +439,9 @@
                            $error["body"]. " ". t('Text to be shown as teaser and before the form.'), NULL, TRUE);
   $output .= filter_form('format', $node->format);

-  $output .= form_textarea(t("Confirmation message or redirect URL"), "confirm", $node->confirm, 20, 10,
-              $error["confirm"]." ".
-              t("Message to be shown upon successful submission or a relative path to redirect page (must start with ./)"),
-              NULL, TRUE);
+
+  $output .= form_textarea(t("Confirmation message or redirect URL"), "confirmation", $node->confirmation, 20, 10,
+                           $error["confirmation"]." ".t("Message to be shown upon successful submission or a relative path to redirect page (must start with ./)"), NULL, TRUE);

   // List all form components.
   if( is_array($node->webformcomponents_name) && !empty($node->webformcomponents_name)) {
@@ -501,7 +544,7 @@
     }else
     {
       $sid_to_display = 0;  // This user is not allowed to access a submitted node. Deny the attempt without alert.
-      watchdog('user', t('Unauthorized webform access attempt', array('%name' => "<em>$user->name</em>"))); // and log the attempt
+      watchdog('webform', t('Unauthorized webform access attempt', array('%name' => "<em>$user->name</em>"))); // and log the attempt
     }
   }

@@ -521,7 +564,7 @@

       if($sid_to_display){
         // It is view only so we append the tag HTML attributes to disable the input
-         $extra['attributes'] = array('dummy' => '0" DISABLED dummy="0');  // Rather ugly string required to workaround a bug in common.inc
+      	$extra['attributes'] = array('disabled' => 'disabled');  // Rather ugly string required to workaround a bug in common.inc
       }else
       {
         $extra['attributes'] = array();
@@ -547,8 +590,8 @@
   if ( $doSubmit && $_POST['webform_send']) {
     if(_webform_process_submit($node, $errors)) {
       // Check confirmation field to see if redirect should be to another node or a message
-      if(valid_url(trim($node->confirm))){
-        drupal_goto(trim($node->confirm));
+      if(valid_url(trim($node->confirmation))){
+        drupal_goto(trim($node->confirmation));
       }
       else {
         // Submission was successfully redirected to webform/done/<nid>
@@ -715,7 +758,7 @@
          || $referer['host'] != $base['host']
 	 ) {
       // The submission didn't originate from our own domain, return false
-      watchdog('error', t('Trying to post to webform from external domain.'),
+      watchdog('webform', t('Trying to post to webform from external domain.'),
                '<a href="'.$_SERVER['HTTP_REFERER'].'">'.t('Visit referer').'</a>');
       drupal_set_message(t('You are not allowed to post to this form from an external domain.'), 'error');
       return false;
@@ -732,7 +775,7 @@
     }
   }
   // Handle posting to the form and output status.
-  $confirm = $node->confirm;
+  $confirmation = $node->confirmation;   // What is this doing? Slated for removal.

   $reply = $_POST['edit']['submitted'];
   _webform_save_submission($node, $reply);
@@ -785,7 +828,7 @@
   }

   if (variable_get('webform_debug', 0) >= 1) {
-    watchdog('special',
+    watchdog('webform',
              t('Submission posted to ')."'$node->title'.<br />\n".nl2br(htmlentities($message)));

     //TODO: Add reference back to submitted webform
@@ -798,7 +841,7 @@
   if ( arg(2) ) {
     $node = node_load(array('nid' => arg(2)));
     if ( node_access('view', $node) ) {
-      $node->body = check_output($node->confirm, $node->format);
+      $node->body = check_output($node->confirmation, $node->format);
       print theme('page', theme('node', $node, FALSE, TRUE), $node->title);
     }
     else {
@@ -819,7 +862,7 @@
  * @param $value
  * @param $extra
  * @param $err
- * @param $nid ???
+ * @param $nid
  * @param $sid The Session ID to get the default values from or 0 or NULL to use the component defaults
  * @returns An HTML string sufficient to render the widget
  */
@@ -874,8 +918,8 @@
                          $value, $options,
                          _webform_filtervalues($extra['description']), $extra['attributes']['dummy'],
                          ($extra['multiple'] == 'Y'), $mandatory);
-    }
-    else {
+      }else
+      {
       if ( $extra['multiple'] == 'Y' ) {
         if ( !is_array($value) ) {
           $value = array($value);
@@ -953,7 +997,7 @@
     $message .= t('Submitted by user').": $user->name [$ip_address] \n";
   }
   else {
-    $message .= t('Submitted by anonymous user')." [$ip_address] \n";
+    $message .= t('Submitted by anonymous user').": [$ip_address] \n";
   }
   $message .= t('Submitted values are:')."\n";
   foreach($reply as $key => $value) {
@@ -1011,7 +1055,7 @@
 }

 function _webform_role_node_delete($nid) {
-  db_query("DELETE FROM {webform_role_node} WHERE nid = $nid");
+  db_query("DELETE FROM {webform_role_node} WHERE nid = %d",$nid);
 }

 function webform_page() {
@@ -1019,16 +1063,43 @@
   _webform_page();
 }

-function webform_reporttable() {
+/**
+ * function webform_results() is an allocator function that builds the page under the 'results' sub menu
+ * The function uses the URL tail to nominate internal content.
+ */
+function webform_results() {
   include_once('webform.inc');
-  _webform_reporttable();
-}

-function webform_reportanalysis() {
-  include_once('webform.inc');
-  _webform_reportanalysis();
+  $nid = arg(1);
+  $node = node_load(array('nid' => $nid));
+
+  $title = $node->title;
+
+  if( arg(2) == 'results') {
+
+    switch ( arg(3) ) {
+      case 'analysis':
+        $content = _webform_results_analysis($nid);
+        break;
+      case 'clear':
+        $content = _webform_results_clear($nid);
+        break;
+      case 'table':
+        $content = _webform_results_table($nid);
+        break;
+      case 'download':
+        $content = _webform_results_download($nid);
+        break;
+      case 'submissions':
+      default:
+        $content = _webform_results_submissions($nid);
+        break;
 }

+    print theme('page', $content, $title);
+
+  }
+}

 /**
  * Returns the version of this release of the webform module.
@@ -1049,10 +1120,18 @@

 /**
  * _webform_database_lazy_update - Lazy field adder for extra email fields
+ * The lazy update function adds any columns in the database that are not
+ * listed in its internal magic array.
+ * It was purpose constructed to add fields 'email_from' and 'email_subject' as part of the
+ * enhanced email patch.
+ * The function does not add or delete any data and the upgraded database is backwardly
+ * compatible with previous versions of webform.module
  */
 function _webform_database_lazy_update() {

 	$lazy_update_output = '';
+	$field_list = '';
+  $table_altered = false;

   // {webform}
 	$required_fields = array('nid' => false,'confirm' => false,'email' => false,'email_from' => false,'email_subject' => false);
@@ -1067,14 +1146,23 @@
 		if(!$required_field){
 			// Field does not exist so we need to create it.
 			switch($field_name){
-				case 'email_subject':
-				case 'email_from':
+				case 'email_subject':		// Contains the component name of the source field for the email subject
+				case 'email_from':			// Contains the component name of the source field for the email from address.
 					db_query("ALTER TABLE {webform} ADD COLUMN %s varchar(255)",$field_name);
+					$table_altered = true;
+          $field_list .= " $field_name ";
 					break;
 				default:
 			}
 		}
 	}
+
+	if($table_altered){
+	  $lazy_update_output = 'Database {webform} updated. Fields added: ' . $field_list;
+	  watchdog('webform',$lazy_update_output);
+	}
+
+	return $lazy_update_output;
 }

 /**
--- ./drupal_cvs/webform.inc	2005-03-09 12:45:38.677814400 +1100
+++ ./webform.inc	2005-03-20 21:05:08.380233600 +1100
@@ -19,35 +19,43 @@

   while ($node = db_fetch_object($result)) {
     $rows[] = array($node->title,
-                    l(t('analysis'),'webform/analysis/'.$node->nid),
-                    l(t('table'),'webform/table/'.$node->nid),
-                    l(t('download'),'webform/table/'.$node->nid . '/download'),
-                    l(t('edit'), 'node/'.$node->nid.'/edit'),
-                    l(t('clear'), 'webform/clear/'.$node->nid));
+                    l(t('submissions'),'node/' . $node->nid . '/results/submissions'),
+                    l(t('analysis'),'node/' . $node->nid . '/results/analysis'),
+                    l(t('table'),'node/' . $node->nid . '/results/table'),
+                    l(t('download'),'node/' . $node->nid . '/results/download'),
+                    //l(t('edit'), 'node/'.$node->nid.'/edit'),
+                    l(t('clear'), 'node/' . $node->nid . '/results/clear'));
+
   }
   $content = theme('table', $header, $rows);
   print theme('page', $content, $title);
 } // end function _webform_page

-function _webform_reporttable() {
-  $title = t('webform submissions');
+function _webform_results_clear($nid) {

-  if ( arg(2) ) {
-    // Get all the submitted values for the node
-    $query = 'SELECT sd.sid as sid, c.cid as cid, sd.name as name, sd.data as data'.
-      ' FROM '.
-      ' {webform_submitted_data} sd, {webform_component} c '.
-      ' WHERE sd.nid = c.nid '.
-      ' AND sd.nid = %d '.
-      ' GROUP BY sd.sid, sd.name '.
-      ' ORDER BY sd.sid, c.cid ';
+  if (arg(4) == "confirm") {

+    $query = 'DELETE FROM {webform_submitted_data} WHERE nid = %d';
+    $res = db_query($query, $nid);
+    $node = node_load(array('nid' => $nid));
+    $title = $node->title;

-    $res = db_query($query, arg(2));
+    watchdog('webform','webform "' . $title . '" entries cleared.');
+   drupal_goto('webform');
+  }
+  else {
+    $content = "<p>Do you really want to delete the submissions of this form?</p>".
+    l(t('yes'), "node/".$nid."/results/clear/confirm")." | ".
+    l(t('no'), "webform")
+    ."</p>";
+
+    //print theme('page', $content, $title);
+    return $content;
+  }
+}

-    if(arg(3) == 'download') {
+function _webform_results_download($nid) {

-      // TODO: Download the values as a CSV file.
       /**
        * The CSV requires that the data be presented in a flat file.  In order
        * to maximize useability to the Excel community and minimize subsequent
@@ -77,26 +85,45 @@
        *   ...............................................................................................................
        *   27 Feb 2005       ,1035,56 ,212   ,Yes       ,No      ,No      ,..,No      ,Yes       ,No      ,Yes     ,..,Yes     ,How is this?
        **/
-       $node_info = _webform_get_node_info(arg(2));
+  $node_info = _webform_get_node_info($nid);
        $title_string = $node_info->title;

-       $file_name = _webform_print_csv_string($title_string,arg(2),'file'); // This will default to printing the output which saves stack space
-
-  	  //$header_array = array("Content-type: text/html; charset=utf-8","Content-Disposition: attachment; filename=" . $title_string);
-	  //file_transfer($title_string,$header_array);
+  $file_name = _webform_records_string($title_string,$nid,'file'); // This will default to printing the output which saves stack space

        drupal_set_header("Content-type: text/plain; charset=utf-8");
        drupal_set_header("Content-Disposition: attachment; filename=" . $title_string . ".csv");

-       @readfile($file_name);
+  @readfile($file_name);  // The @ makes it silent
        exit(0);
     }
-    else {
-      // TODO: View the submitted entries as a table.
+
+/** function _webform_results_submissions() is a database accessor function designed to return lists
+ *  of submissions and data.
+ * @param $nid the node id of the webform
+ */
+function _webform_results_submissions($nid) {
+  $node_info = _webform_get_node_info($nid);
+  $title_string = $node_info->title;
+  $content = _webform_records_string($title_string,$nid,'submission_list');
+  return $content;
+}
+
+function _webform_results_table($nid) {
       $first = true;
       $previous = -1;
       $cell = array();

+  // Get all the submitted values for the node
+  $query = 'SELECT sd.sid as sid, c.cid as cid, sd.name as name, sd.data as data'.
+    ' FROM '.
+    ' {webform_submitted_data} sd, {webform_component} c '.
+    ' WHERE sd.nid = c.nid '.
+    ' AND sd.nid = %d '.
+    ' GROUP BY sd.sid, sd.name '.
+    ' ORDER BY sd.sid, c.cid ';
+
+  $res = db_query($query, $nid);
+
       $header[] = t('#');
       while ($field = db_fetch_object($res)) {
         if ( ($previous != -1)
@@ -125,26 +152,25 @@
       // and the last one ...
       $rows[] = array_merge(array($previous), $cell);

-      $content = theme('table', $header, $rows);
+  return theme('table', $header, $rows);
+

-  		//print theme('page', $content, $title);
-  		print theme('page', $content, $title);
-    }
-  }
 }

-/** _webform_print_csv_string - Decomposes the database to a flat file for a given SID.
- * @param $title - Title for this report. Will be printed as a heading.
+/** _webform_records_string - Decomposes the database to a flat file for a given SID.
+ * @param $title - Title for this report. Will be printed as a heading if appropriate.
  * @param $nid - The drupal node id representing the webform data that is to be extracted.
  * @param $print_now Whether to print the result as it is formed or store it to a string and return it.
  * @param $new_line - A string to be used to render a new line.  Defaults for print on windows based systems
  * @return a reference to a string representing the file in csv format.
  **/

-function _webform_print_csv_string($title_string,$nid,$mode = 'file',$new_line = "\r\n"){
+function _webform_records_string($title_string,$nid,$mode = 'file',$new_line = "\r\n"){
+
   // Because the components may have changed during the lifestyle of the form
   // and because we may be using a low version of MySQL et al. The query is done in two
-  // passes.  This would be more efficiently done using a VIEW or SUB-SELECT
+  // passes.  This would be more efficiently done using a VIEW or SUB-SELECT but we cannot guarantee
+  // that it is available.

   // The first query identifies the characteristics of components that are in the data set
   $component_query = 'SELECT wc.cid as cid, wc.name as name, wc.type as type, wc.value as value, '.
@@ -354,15 +384,14 @@
         $file_record = _webform_make_record_csv_string($record_array,$new_line);
         @fwrite($handle,$file_record);
       }
-    } // end if ($serial != 0)
+    }
   }

   //print "Main result = " . count($record_array) . " records.";
   switch($mode){
   case 'submission_list':
-    return (theme('table', $submission_list_header, $submission_list_array));
-    //print
-    return true;
+    $content = theme('table', $submission_list_header, $submission_list_array);
+    return $content;
   case 'print':
     return true;
   case 'string':
@@ -424,6 +453,7 @@
  * Pulls the information block for a given node
  * @param $nid - The node requested
  * @return - A structure containing the node info.
+ * TODO: Remove this function in favour of the core drupal alternative
  **/
 function _webform_get_node_info($nid){
   $node_search_result = db_query("SELECT nid, title FROM {node} WHERE type='webform' AND nid=%d",$nid);
@@ -432,11 +462,11 @@
 }


-/** _webform_reportanalysis - Provides simple analysis of a series of webform submission.
+/** _webform_results_analysis - Provides simple analysis of a series of webform submission.
  * @return to stdio a themeatic HTML rendering of the page.
  **/

-function _webform_reportanalysis() {
+function _webform_results_analysis($nid) {

   // The current defaults
   // TODO: Move some of these variables to the settings page.
@@ -513,7 +543,7 @@
                                            "WHERE sd.nid = %d AND (sd.name LIKE '%s' OR sd.name LIKE '%s') ".
                                            'GROUP By sd.data '.
                                            'ORDER BY sd.sid DESC',
-                                           arg(2), $uniqueid_query, $uniqueip_query);
+                                           $nid, $uniqueid_query, $uniqueip_query);

   // Set them up in a keyed array so we can find them quickly.

@@ -534,8 +564,8 @@
   unset($uniqueid_query,$uniqueip_query);

   // Pull the title of the form from node and make a nice heading for the page
-  $node_info = _webform_get_node_info(arg(2));
-  $title = "Results Analysis of $node_info->title";
+  //$node_info = _webform_get_node_info($nid);
+  //$title = $node_info->title";

   // Do the main query
   $query = 'SELECT sd.name as name, sd.sid as sid, c.cid as cid, c.type as type, sd.data as data, c.extra as extra, c.value as value '.
@@ -544,7 +574,7 @@
     '  AND sd.name = c.name '.
     '  AND sd.nid = %d '.
     'ORDER BY c.cid, sd.data';
-  $res = db_query($query, arg(2));
+  $res = db_query($query, $nid);

   // View the submitted entries as a list with tallies.
   $question_number = 0;
@@ -674,8 +703,14 @@
           // Check it is set because some may not exist by design, ie when the default is blank
           switch($subkey){
           case 'average':
+            $divisor = ($valid_records - $shadow_rows[$skey]['blank']);
+            if ($divisor !== 0) {
             $av = round($shadow_rows[$skey]['word_count'] / ($valid_records - $shadow_rows[$skey]['blank']),1);	// 1 decimal place
-            $rows[$rkey][2] = _build_tally(&$views_switches,&$view_settings,$av);
+            }
+            else {
+              $av = 0;
+            }
+            $rows[$rkey][2] = _build_tally($views_switches,$view_settings,$av);
             $rows[$rkey][3] = '';		// No percentages or bars on average wordcounts
             break;
           default:
@@ -895,7 +929,7 @@

   }

-  print theme('page', $content, $title);
+  return $content;
 }

 /** _build_tally - Creates an HTML string sufficient to render the current tally as
