// -------------------------------------------------
// Establish the 'Form' namespace
// -------------------------------------------------

function Form() {}

// ----------------------------------
// Utility Methods
// ----------------------------------

Form.trim = function(theValue)
{
   return(theValue.replace(/^\s+/, '').replace(/\s+$/, ''));
}

Form.fmtErrMsg = function(value, label, msg)
{
   var s =
      "Invalid value: '" + value + "' for: '" + label + "'" +
      "\n" +
      "(" + msg + ")";

   return(s);
}

Form.valueRegexCheck = function(regex, data, msg)
{
   var theValue = Form.trim(data.element.value);

   if(!regex.test(theValue))
   {
      data.errMsg = Form.fmtErrMsg(theValue, data.label, msg);
      return(false);
   }

   return(true);
}

Form.checkedCheck = function(data)
{
   var theList =
      (data.element.length == null ? [ data.element ] : data.element);

   for(var i = 0; i < theList.length; i ++)
      if(theList[i].checked)
         return(true);

   data.errMsg = "No value selected for: '" + data.label + "'";

   return(false);
}

Form.selectCheck = function(data, chk, msg)
{
   if(!chk(data.element))
   {
      data.errMsg =
         "Invalid selection for: '" + data.label + "'" +
         "\n" +
         "(" + msg + ")";

      return(false);
   }

   return(true);
}

Form.fieldsCheck = function(theForm, theTable)
{
   for(var i = 0; i < theTable.length; i ++)
   {
      var theEntry = theTable[i];

      if(theForm.elements[theEntry.field] == null)
      {
         alert("Field not found: (" + theEntry.field + ")");
         return(false);
      }

      var data = {
         element: theForm.elements[theEntry.field],
         label  : theEntry.label,
         errMsg : ""
      };

      var ok = (theEntry.arg == null ?
         theEntry.valid(data) : theEntry.valid(data, theEntry.arg));

      if(!ok)
      {
         alert(data.errMsg);

         var d = null;

         if(data.element.length != null && data.element.options == null)
            d = data.element[0];
         else
            d = data.element;

         d.focus();

         return(false);
      }
   }

   return(true);
}

// ---------------------------------------------
// Form Element Validating Methods
// ---------------------------------------------

Form.txtIsInteger = function(data)
{
   return(
      Form.valueRegexCheck(
         /^-?[0-9]+$/, data, "integer expected"
      )
   );
}

Form.txtIsNonNegativeInteger = function(data)
{
   return(
      Form.valueRegexCheck(
         /^[0-9]+$/, data, "non-negative integer expected"
      )
   );
}

Form.txtIsPositiveInteger = function(data)
{
   return(
      Form.valueRegexCheck(
         /^[1-9][0-9]*$/, data, "positive integer expected"
      )
   );
}

Form.txtNumberCompare = function(
   data, parseFunction, isFunction, numberType, comparator, compareValue
)
{
   var ok = true;

   if(!isFunction(data))
   {
      ok = false;
   }
   else
   {
      var theValue = parseFunction(data.element.value);
      var map      = {};

      map['<']  = function(x, y) { return(x < y);  }
      map['>']  = function(x, y) { return(x > y);  }
      map['<='] = function(x, y) { return(x <= y); }
      map['>='] = function(x, y) { return(x >= y); }
      map['=']  = function(x, y) { return(x == y); }
      map['!='] = function(x, y) { return(x != y); }

      if(map[comparator] == null)
      {
         data.errMsg = "Invalid comparator: '" + comparator + "'";
         return(false);
      }

      if(!map[comparator](theValue, compareValue))
         ok = false;
   }

   if(!ok)
   {
      data.errMsg = Form.fmtErrMsg(
         data.element.value,
         data.label,
         "expected " + numberType + " " + comparator + " " + compareValue
      );

      return(false);
   }

   return(true);
}

Form.txtIntegerCompare = function(data, comparator, compareValue)
{
   return(
      Form.txtNumberCompare(
         data,
         parseInt,
         Form.txtIsInteger,
         "integer",
         comparator,
         compareValue
      )
   );
}

Form.txtIntegerLT = function(data, value)
{
   return(Form.txtIntegerCompare(data, "<", value));
}

Form.txtIntegerLE = function(data, value)
{
   return(Form.txtIntegerCompare(data, "<=", value));
}

Form.txtIntegerGT = function(data, value)
{
   return(Form.txtIntegerCompare(data, ">", value));
}

Form.txtIntegerGE = function(data, value)
{
   return(Form.txtIntegerCompare(data, ">=", value));
}

Form.txtIntegerEQ = function(data, value)
{
   return(Form.txtIntegerCompare(data, "=", value));
}

Form.txtIntegerNE = function(data, value)
{
   return(Form.txtIntegerCompare(data, "!=", value));
}

Form.txtIsReal = function(data)
{
   return(
      Form.valueRegexCheck(
         /^-?([0-9]+|[0-9]+\.[0-9]*|\.[0-9]+)$/,
         data,
         "real number expected"
      )
   );
}

Form.txtIsNonNegativeReal = function(data)
{
   if(!Form.txtIsReal(data) ||
      /^-/.test(data.element.value))
   {
      data.errMsg = Form.fmtErrMsg(
         data.element.value, data.label, "non-negative real expected"
      );

      return(false);
   }

   return(true);
}

Form.txtIsPositiveReal = function(data)
{
   if(!Form.txtIsNonNegativeReal(data) ||
      parseFloat(data.element.value) == 0)
   {
      data.errMsg = Form.fmtErrMsg(
         data.element.value, data.label, "positive real expected"
      );

      return(false);
   }

   return(true);
}

Form.txtRealCompare = function(data, comparator, compareValue)
{
   return(
      Form.txtNumberCompare(
         data,
         parseFloat,
         Form.txtIsReal,
         "real",
         comparator,
         compareValue
      )
   );
}

Form.txtRealLT = function(data, value)
{
   return(Form.txtRealCompare(data, "<", value));
}

Form.txtRealLE = function(data, value)
{
   return(Form.txtRealCompare(data, "<=", value));
}

Form.txtRealGT = function(data, value)
{
   return(Form.txtRealCompare(data, ">", value));
}

Form.txtRealGE = function(data, value)
{
   return(Form.txtRealCompare(data, ">=", value));
}

Form.txtRealEQ = function(data, value)
{
   return(Form.txtRealCompare(data, "=", value));
}

Form.txtRealNE = function(data, value)
{
   return(Form.txtRealCompare(data, "!=", value));
}

Form.txtIsEmail = function(data)
{
   return(
      Form.valueRegexCheck(
         /^\S+@\S+\.[a-z]{2,}$/i, data, "email address expected"
      )
   );
}

Form.radioChecked = function(data)
{
   return(Form.checkedCheck(data));
}

Form.checkboxCheckedAtLeast = function(data, minValue)
{
   var theList =
      (data.element.length == null ? [ data.element ] : data.element);

   var n = 0;

   for(var i = 0; i < theList.length; i ++)
      if(theList[i].checked)
         n++;

   if(n < minValue)
   {
      data.errMsg =
         "At least " + minValue + " box" +
         (minValue < 2 ? "" : "es") +
         " must be checked for: '" + data.label + "'";

      return(false);
   }

   return(true);
}

Form.checkboxChecked = function(data)
{
   return(Form.checkedCheck(data));
}

Form.selectNotEmpty = function(data)
{
   var obj = data.element[data.element.selectedIndex];

   if(obj.text == "" || obj.value == "")
   {
      data.errMsg =
         "No selection found for: '" + data.label + "'";

      return(false);
   }

   return(true);
}

Form.multiSelectAtLeast = function(data, minSelected)
{
   var n = 0;

   for(var i = 0; i < data.element.length; i ++)
      if(data.element[i].selected)
         n++;

   if(n < minSelected)
   {
      data.errMsg =
         "At least " + minSelected + " option" +
         (minSelected < 2 ? "" : "s") +
         " must be selected for: '" + data.label + "'";

      return(false);
   }

   return(n >= minSelected);
}

Form.multiSelectSelected = function(data)
{
   return(Form.multiSelectAtLeast(data, 1));
}
