// include file for web pages ... CD May 2006
//
// cloned from VB code of a decade earlier
//
// contents : 
//            AbsentField, ValueUnspec              ... (Forms validation)
//            editedData, FormDataHasChanged        ... (Forms management)
//            alteredPublish                        ... (Forms management)
//            FormContainer                         ... (Forms management)
//            jsf_isEmailAddr, jsf_isWebAddr, jsf_isValidDateString
//            jsf_ByRefArg, jsf_DateForDisplay
//            jsf_DateDiff  

//            EnQuoted, Trim, hasValue              ... (string handling)
//            SnapOldVal,ProperForm, Proper, SetACA ... (Automatic Case Adjustment)
//            SensitiveCaseDown, SensitiveCaseUp
//            onChangeEmail,onChangeWebAddr,onChangeDate
//            SetFocusToFieldId
//            customAlert, customConfirm
//            SelectedMandatoryValue
//            SelectedFromOptionList

var glb_AutoCase_inActive = false;
var glb_FormDataHasChanged = false;

// new stuff **************** start
function FormContainer(objFld)  {
   // return the form object that contains the field object 'objFld'
   return document.forms[0];
//   var obj = objFld, resp;
//   while ( true ) {
//      obj = obj.parent;
//      alert ("Name of obj is: " + obj.name + "   Method of obj is: " + obj.method);
//      if ( obj.method != "undefined" ) return obj;
//      }
   }

function jsf_isEmailAddr(str)    {
   return str.match(/^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/);
   }

function jsf_isWebAddr(str)    {
   // need to come up with some way of validating a web address
    return str.match(/(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/);
    return true;
   }

function jsf_isValidDateString(txt)  {
   // return true if txt is interpretable as a date
   // - if so, the decoded date is placed in 'response' - which is configured as a ByRefArg
   // optional 2nd arg : 'informal date' - allows "Aug 2003","1996", "14 May"
   // optional 3rd arg : 'extResponse' - configured as a ByRefArg - returns string "15 May 2007"
   // optional 4th arg : 'intResponse' - configured as a ByRefArg - returns date object


   function isInteger(s)  {
      for (var i = 0; i < s.length; i++)   {
         // Check that current character is number.
         var c = s.charAt(i);
         if (((c < "0") || (c > "9"))) return false;
         }
      // All characters are numbers.
      return true;
      }

   function ExtractedWord(argTxt,argWord,argDelim)  {
      var c, i = 0;
      var txt = argTxt.getValue();
      // skip over any white space, delimiters etc
      while ( true )  {
         if ( i >= txt.length ) {
            argWord.setValue( "" );
            argDelim.setValue( "" );
            argTxt.setValue( "" );
            return false;
            }
         c = txt.charAt(i);
         if ( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))  || ((c >= '0') && (c <= '9')) ) 
            break;
         i++;
         }
 
      // find the 'word' - i.e. look for some delimiter space, '/', '-', ','
      var st = i;
      while ( true )  {
         if ( i >= txt.length ) {
            argWord.setValue( txt.substr(st) );
            argDelim.setValue( "" );
            argTxt.setValue( "" );
            return true;
            }
         c = txt.charAt(i);
         if ( ! ( ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))  || ((c >= '0') && (c <= '9')) ) )
            break;
         i++;
         }
      argWord.setValue( txt.substr(st,i-st) );
      argDelim.setValue( txt.charAt(i) );
      argTxt.setValue( txt.substr(i+1) );
      return true;
      }

   function MonthDecoded(mthNm) {
      var resp = new Array();
      switch ( mthNm.toLowerCase() )  {
         case "jan":       resp[0] = 1; resp[1] = "Jan"; break;
         case "january":   resp[0] = 1; resp[1] = "January"; break;
         case "feb":       resp[0] = 2; resp[1] = "Feb"; break;
         case "february":  resp[0] = 2; resp[1] = "February"; break;
         case "mar":       resp[0] = 3; resp[1] = "Mar"; break;
         case "march":     resp[0] = 3; resp[1] = "March"; break;
         case "apr":       resp[0] = 4; resp[1] = "February"; break;
         case "april":     resp[0] = 4; resp[1] = "February"; break;
         case "may":       resp[0] = 5; resp[1] = "May"; break;
         case "may":       resp[0] = 5; resp[1] = "May"; break;
         case "jun":       resp[0] = 6; resp[1] = "Jun"; break;
         case "june":      resp[0] = 6; resp[1] = "June"; break;
         case "jul":       resp[0] = 7; resp[1] = "Jul"; break;
         case "july":      resp[0] = 7; resp[1] = "July"; break;
         case "aug":       resp[0] = 8; resp[1] = "Aug"; break;
         case "august":    resp[0] = 8; resp[1] = "August"; break;
         case "sep":       resp[0] = 9; resp[1] = "Sep"; break;
         case "september": resp[0] = 9; resp[1] = "September"; break;
         case "oct":       resp[0] = 10; resp[1] = "Oct"; break;
         case "october":   resp[0] = 10; resp[1] = "October"; break;
         case "nov":       resp[0] = 11; resp[1] = "Nov"; break;
         case "november":  resp[0] = 11; resp[1] = "November"; break;
         case "dec":       resp[0] = 12; resp[1] = "Dec"; break;
         case "december":  resp[0] = 12; resp[1] = "December"; break;
         default:          resp[0] = 0; resp[1] = ""; break;
         }
      return resp;
      }

   function ValidDayForMonth(dayNum, mnthNum, yearNum)  {
      if ( (mnthNum < 1) || (mnthNum > 12) ) return false;
      if ( (dayNum < 1) )  return false;
      if ( (dayNum > 31) )  return false;
      var daysLim = 31;
      switch ( mnthNum ) {
         case 9:
         case 4:
         case 6:
         case 11:
                 daysLim = 30;
                 break;
         case 2:
                 // February has 29 days in any year evenly divisible by four,
                 // EXCEPT for centurial years which are not also divisible by 400.
                 daysLim = (((yearNum % 4 == 0) && ( (!(yearNum % 100 == 0)) || (yearNum % 400 == 0))) ? 29 : 28 );
         }
      return ( (dayNum <= daysLim) );
      }

   function externalForm(dte)  {
      var mNames = new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
      var wk = dte.getDate() + " " + mNames[dte.getMonth()] + " " + dte.getFullYear();
      return wk;
      }

   function UpgradedYear(yearNum)  {
      var tmpDate = new Date();
      // if the given year is fairly low,, assume recent ... e.g. 2 or 3 ==> 2002, 2003
      // ... otherwise, assume older ... e.g. 15, 55 ==> 1915, 1955
      // to avoid constantly revising the code, assume that a 95 year backward window is required
      return yearNum + (( (tmpDate.getFullYear()-yearNum) >= 1995 )? 2000: 1900);
      }

   var informalDate = false;
   var extResponse = null;             // ByRefArg if specified
   var intResponse = null;             // ByRefArg if specified
   if ( arguments.length > 1 ) informalDate = arguments[1];
   if ( arguments.length > 2 ) extResponse = arguments[2];
   if ( arguments.length > 3 ) intResponse = arguments[3];

   var decodedAlphas = new Array,decodedAdelim = new Array, ptrDA = 0;
   var decodedNumerics = new Array, decodedNdelim = new Array,ptrDN = 0;
   var word = new String;
   var delim = new String;
   var mnthArr = new Array, mnthNum, dayNum, yearNum;
   var acceptedDate = new Date();
   var argTxt = new jsf_ByRefArg; argTxt.setValue(txt);
   var argWord = new jsf_ByRefArg; 
   var argDelim = new jsf_ByRefArg;
   var alphaFirst = false;

   // look at the given string and break up into numeric elements and non-numeric elements

   while ( true )  {
      if ( ! ExtractedWord(argTxt,argWord,argDelim) ) break;
      word = argWord.getValue();
      if (word == "") break;
      delim = argDelim.getValue();
      if ( isInteger(word) )  {
// alert("Debug: Word from string: "+word+" - Integer");
         ptrDN++; decodedNumerics[ptrDN] = word; decodedNdelim[ptrDN] = delim;
         } else {
// alert("Debug: Word from string: "+word+" - Alpha");
         alphaFirst = ( ptrDN == 0 );
         ptrDA++; decodedAlphas[ptrDA] = word; decodedAdelim[ptrDA] = delim;
         }
      }

   switch ( ptrDN )  {
      case 0:
                  // no numeric elements in the date string ... use current year
                  if ( ptrDA == 0 )  return false;
                  if ( !informalDate ) return false;

                  // try to make some sense of the string as a date
                  mnthArr = MonthDecoded(decodedAlphas[1]);
                  mnthNum = mnthArr[0];
                  if ( mnthNum == 0 ) return false;
                  yearNum = acceptedDate.getFullYear();
                  if ( extResponse != null ) { 
                     var resp = mnthArr[1] +" "+ yearNum + decodedAdelim[1];
                     for ( var i=2; i<=ptrDA; i++ ) {
                         resp += decodedAlphas[i] + decodedAdelim[i];
                         }
                     extResponse.setValue( resp );
                     }
                  return true;
      case 1:
                  // one numeric element ... ? 12 Oct ?? Apr 2002 ?
                  if ( ptrDA == 0 )  return false;

                  // maybe the date is "14 March" ... append current year
                  mnthArr = MonthDecoded(decodedAlphas[1]);
                  mnthNum = mnthArr[0];
                  if ( mnthNum == 0 ) return false;
                  dayNum = parseInt(decodedNumerics[1]);
                  if ( !informalDate ) {
                     yearNum = acceptedDate.getFullYear();
                     if ( !ValidDayForMonth(dayNum, mnthNum, yearNum) ) return false;
                     } else {
                     if ( dayNum > 31 ) {
                        yearNum = dayNum;
                        if ( yearNum < 100 )  yearNum = UpgradedYear(yearNum);
                        dayNum = null;
                        } else {
                        if ( alphaFirst )  {
                           // assume 'month year'
                           yearNum = UpgradedYear(dayNum);
                           dayNum = null;
                           } else {
                           yearNum = acceptedDate.getFullYear();
                           }
                        }
                     }
                  if ( extResponse != null ) {
                     if ( dayNum != null ) {
                        acceptedDate.setFullYear(yearNum, mnthNum-1, dayNum);
                        extResponse.setValue( externalForm(acceptedDate) );
                        if ( intResponse != null ) {
                            intResponse.setValue( acceptedDate.valueOf() );
                            }
                        } else {
                        var resp = mnthArr[1] +" "+ yearNum + decodedAdelim[1];
                        for ( var i=2; i<=ptrDA; i++ ) {
                            resp += decodedAlphas[i] + decodedAdelim[i];
                            }
                        extResponse.setValue( resp );
                        }
                     }
                  return true;
      case 2:
                  // two numeric elements ... ? 12 Oct 06 ?? 16 Apr 2002 ?? 1997 11 May  ?? June 15 1682 ? 
                  if ( ptrDA != 0 ) {
                     // maybe the date is "14 May 2006" ... check alpha
                     mnthArr = MonthDecoded(decodedAlphas[1]);
                     mnthNum = mnthArr[0];
                     if ( mnthNum != 0 )  {
                        dayNum = parseInt(decodedNumerics[1]);
                        if ( dayNum > 31 ) {
                           yearNum = dayNum;
                           dayNum = parseInt(decodedNumerics[2]);
                           } else {
                           yearNum = parseInt(decodedNumerics[2]);
                           }
                        if ( ValidDayForMonth(dayNum, mnthNum, yearNum) )  {
                           if ( yearNum < 100 )  yearNum = UpgradedYear(yearNum);
                           if ( extResponse != null ) { 
                              acceptedDate.setFullYear(yearNum, mnthNum-1, dayNum);
                              extResponse.setValue( externalForm(acceptedDate) );
                              if ( intResponse != null ) {
                                  intResponse.setValue( acceptedDate.valueOf() );
                                  }
                              }
                           return true;
                           }
                        }
                     }
                  // maybe the date is "14/5" + some alpha noise (like 'Monday') ... append current year
                  dayNum = parseInt(decodedNumerics[1]);
                  mnthNum = parseInt(decodedNumerics[2]);
                  if ( mnthNum > 12 ) {
                     mnthNum = parseInt(decodedNumerics[1]);
                     dayNum = parseInt(decodedNumerics[2]);
                     }
                  yearNum = acceptedDate.getFullYear();
                  if ( !ValidDayForMonth(dayNum, mnthNum, yearNum) ) return false;
                  if ( extResponse != null ) { 
                     acceptedDate.setFullYear(yearNum, mnthNum-1, dayNum);
                     extResponse.setValue( externalForm(acceptedDate) );
                     if ( intResponse != null ) {
                         intResponse.setValue( acceptedDate.valueOf() );
                         }
                     }
                  return true;
      case 3:

//                  dayNum = parseInt(decodedNumerics[1]);
//                  mnthNum = parseInt(decodedNumerics[2]);
//                  yearNum = parseInt(decodedNumerics[3]);  // expecting dd/mm/yy

// try another way ... 'cos parseInt does not handle leading zeros !!!
                  dayNum = 01 * decodedNumerics[1];
                  mnthNum = 01 * decodedNumerics[2];
                  yearNum = 01 * decodedNumerics[3];  // expecting dd/mm/yy
// alert("Debug: 3 numeric portions 1: d m y: "+dayNum+" "+mnthNum+" "+yearNum);
                  while ( true )  {
                     if ( yearNum >= 100 ) {
                        if ( mnthNum > 12 ) {
                           mnthNum = parseInt(decodedNumerics[1]);
                           dayNum = parseInt(decodedNumerics[2]);  // seems to be mm/dd/yyyy
                           }  // seems to be dd/mm/yyyy
                        break;  // finished with variations involving year > 100
                        }

                     if ( dayNum >= 100 ) {
                        yearNum = dayNum;
                        dayNum = parseInt(decodedNumerics[3]);  // try for yyyy/mm/dd
                        if ( mnthNum > 12 ) {
                           mnthNum = dayNum;
                           dayNum = parseInt(decodedNumerics[2]);  // seems to be yyyy/dd/mm
                           }
                        break;  // finished with variations involving day > 100
                        }

                     if ( mnthNum >= 100 ) {
                        yearNum = mnthNum;
                        mnthNum = parseInt(decodedNumerics[3]);  // seems to be dd/yyyy/mm
                        if ( mnthNum > 12 ) {
                           mnthNum = dayNum;
                           dayNum = parseInt(decodedNumerics[3]);  // seems to be mm/yyyy/dd
                           }
                        break;  // finished with variations involving month > 100
                        }
                     // none of the 3 elements is > 100      }
                     if ( mnthNum > 12 ) {
                        if ( dayNum <= 12 ) {
                           mnthNum = dayNum;
                           dayNum = parseInt(decodedNumerics[2]);  // seems to be mm/dd/yy
                           } else {
                           mnthNum = yearNum;
                           yearNum = parseInt(decodedNumerics[2]);  // seems to be dd/yy/mm
                           }
                        }
                     if ( yearNum < 100 )  yearNum = UpgradedYear(yearNum);
                     break;
                     }
// alert("Debug: 3 numeric portions 2: d m y: "+dayNum+" "+mnthNum+" "+yearNum);
                  if ( !ValidDayForMonth(dayNum, mnthNum, yearNum) ) return false;
                  if ( extResponse != null ) {
                     acceptedDate.setFullYear(yearNum, mnthNum-1, dayNum);
                     extResponse.setValue( externalForm(acceptedDate) );
                     if ( intResponse != null ) {
                         intResponse.setValue( acceptedDate.valueOf() );
                         }
// alert("3 numeric portions 3: d m y: "+externalForm(acceptedDate));
                     }
                  return true;
      }
   return false;
   }


//function ValidDate(y,m,d)  {
//   with (new Date(y, m, d))
//      return getMonth() == m && getDate() == d;
//   }

function jsf_ByRefArg()  {
    // dummy up a method of passing args 'by reference'
    this.array = new Array(1);
    this.setValue = function(v) { this.array[0] = v; }
    this.getValue = function()  { return this.array[0]; }
    }

function jsf_DateForDisplay(dte)  {
   // optional 2nd arg 'abbrev'
   // abbrev = false:  return "Saturday, 27 January, 1946"
   // abbrev = true:   return "27 January 1946"
   var abbrev,wk;
   if ( arguments.length > 1 ) abbrev = arguments[1]; else abbrev = false;
   var year = dte.getYear();
   if (year < 10)  { year  +=2000 } else { if (year < 1000)   year  +=1900; }
   var day = dte.getDay();
   var month = dte.getMonth();
   var daym = dte.getDate();
   if (daym < 10)  daym="0"+daym;
   var dayarray = new Array("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
   var montharray=new Array("January","February","March","April","May","June","July","August","September","October","November","December");
   if (abbrev) {
      wk = daym+" "+montharray[month].substr(0,3)+" "+year;
      }  else  {
      wk = dayarray[day]+", "+daym+" "+montharray[month]+", "+year;
      }
   return wk;
   }


// new stuff **************** end


function SetFocusToFieldId(FieldId)   {
   // try to find a field of the given 'id' ... if so, give it the focus

   var objTgtField = document.getElementById(FieldId);
//alert("Focus being moved to '"+objTgtField.name+"'");
   if ( objTgtField ) objTgtField.focus();
   return true;
   }

function ValueUnspec(TgtFieldName)   {
   // check that an item has been entered (i.e that there is some value on display ...
   // optional 2nd arg ... external field name

   var objTgtField = document.getElementById(TgtFieldName);
//

   if (typeof(objTgtField) != "undefined")  {
      if (objTgtField != null) {
         switch (objTgtField.type.toLowerCase())  {
            case "radio":
                             objTgtField = null;
                             var colElem = document.getElementsByName(TgtFieldName);
                             for (var i=0;i<colElem.length;i++)  {
                                switch (colElem[i].type.toLowerCase()) {
                                   case  "radio":  if (colElem[i].checked) return false;
                                                   break;
                                   default:
                                                   alert("Debug: unexpected type '"+ colElem[i].type +"'");
                                                   break; 
                                   }
                                if (objTgtField == null)  objTgtField = document.getElementById(colElem[i].id);
                                }
                             break;
            case "option":
                             alert("Debug: option type '"+ TgtFieldName +"'");
                             objTgtField = null;
                             var colElem = document.getElementsByName(TgtFieldName);
                             for (var i=0;i<colElem.length;i++)  {
                                switch (colElem[i].type.toLowerCase()) {
                                   case "option":  if (colElem[i].selected) return false;
                                                   break;
                                   default:
                                                   alert("Debug: unexpected type '"+ colElem[i].type +"'");
                                                   break; 
                                   }
                                if (objTgtField == null)  objTgtField = document.getElementById(colElem[i].id);
                                }
                             break;
            case "checkbox":
                             if (objTgtField.checked) return false;
                             break;
            default:
                             if ( arguments.length > 1)  {
                                return AbsentField(objTgtField,arguments[1]);
                                }  else  {
                                return AbsentField(objTgtField);
                                }
            }
         var fieldName = TgtFieldName;
         if ( arguments.length > 1)  fieldName = arguments[1];
         customAlert("","Please ensure that a value for '" + fieldName + "' is entered.");
         objTgtField.focus();
         return true;
         }
      }
//alert("Debug: ValueUnspec for "+TgtFieldName+"  obj is null: "+(objTgtField == null));
   // must be an option group or a select?
   objTgtField = null;
   var colElem = document.getElementsByName(TgtFieldName);
   for (var i=0;i<colElem.length;i++)  {
      switch (colElem[i].type.toLowerCase()) {
         case "option":  if (colElem[i].selected) return false;
                         break;
         case "radio":   if (colElem[i].checked) return false;
                         break;
         default:
                         alert("Debug: unexpected type '"+ colElem[i].type +"'");
                         break; 
         }
      if (objTgtField == null)  objTgtField = document.getElementById(colElem[i].id);
      }

   var fieldName = TgtFieldName;
   if ( arguments.length > 1)  fieldName = arguments[1];
   customAlert("","Please ensure that a value for '" + fieldName + "' is entered.");
   objTgtField.focus();
   return true;
   }

function AbsentField(objTgtField)   {
   // check that an item has been entered (i.e that there is some value on display ...
   // optional 2nd arg ... external field name

   if ( hasValue(objTgtField.value) )   return false;

   var fieldName;
   fieldName = new String();
   fieldName = objTgtField.name;
   if ( arguments.length > 1)  fieldName = arguments[1];
   customAlert("","Please ensure that a value for '" + fieldName + "' is entered.");
   objTgtField.focus();
   return true;
   }

function FormDataHasChanged() {
   return glb_FormDataHasChanged;
   }

function alteredPublish(pubVal) {

    // paint background colour of dependent fields on the form 

//these values must be kept in sync with gop_lib constants

//define("con_Publish_No_colr", "#FFFFFF");           // background white = visible only to Admin (and self) 
//define("con_Publish_Guild_colr", "#F0E68C");        // background gold = visible only to Guild Members  #F0E68C  Khaki  #FFD700  gold
//define("con_Publish_OK_colr", "#E0FFFF");           // background aquamarine = visible Universally

    var lv_i = 0, lv_j, lv_fieldName, lv_dependents, lv_dep_ctrl;
    if ( arguments.length > 1)  {
        lv_dependents = arguments[1];
        while ( true )  {
            // get each successive fieldname from the dependents string
            // and reset the bgcolor determined by the pubVal
            lv_j = lv_i;
            lv_i = lv_dependents.indexOf(",",lv_j);
            if ( lv_i == -1 )  {
                lv_fieldName = lv_dependents.substr(lv_j);
                } else {
                lv_fieldName = lv_dependents.substr(lv_j,lv_i-lv_j+1-1);
                }
            switch ( parseInt(pubVal.value) ) {
                case 0: 
                        document.getElementById(lv_fieldName).style.backgroundColor="#FFFFFF"; 
                        break;
                case 1: 
                        document.getElementById(lv_fieldName).style.backgroundColor="#F0E68C"; 
                        break;
                case 2: 
                        document.getElementById(lv_fieldName).style.backgroundColor="#E0FFFF"; 
                        break;
                break;
                }
            if ( lv_i == -1 ) break;
            lv_i++;
            }
        }
   return editedData();
   }

function editedData() {
   glb_FormDataHasChanged = true;
   return true;
   }

function EnQuoted(txtVal) {
    // produce a valid, quoted string
    var lv_i = 0, lv_j, lv_wk = "";
    while ( true )  {
        lv_j = lv_i;
        lv_i = txtVal.indexOf("\"",lv_j);
        if ( lv_i == -1 )  {
            lv_wk += txtVal.substr(lv_j);
            break;
            }
        lv_wk += txtVal.substr(lv_j,lv_i-lv_j+1-1) + "\\\"";
        lv_i++; 
        }
    return "\""+lv_wk +"\"";
    }

function Trim(txt) {
    var i = txt.length -1;
    while (i>=0) {
       if (txt.charAt(i) !=" ") break;
       i--;
       }
    if (i < 0) return "";
    var nd = i;
    i = 0;
    while (i<=(txt.length-1)) {
       if (txt.charAt(i) !=" ") break;
       i++;
       }
    var st = i;
    return txt.substring(st,nd+1);
    }

function hasValue(testVal) {
    // check that there is a non-null, non-empty value
    if ( testVal == undefined ) return false;
    if ( testVal == null ) return false;
    if ( typeof(testVal) == "string" ) {
        if ( testVal == "" ) return false;
        }
    return true;
    }

function SnapOldVal(objFld) {

   // on entry to the field, take a snapshot of the unmodified value, 
   // ... for use with ProperForm() and others

   // if the oldvalue property exists for this object, then exit
   // ... else create the oldvalue property
   if (typeof objFld.oldvalue == "undefined") {
       objFld.oldvalue = objFld.value;
       }
   }

function setACA(objTgtField)  {
   // toggle the value in glb_AutoCase_inActive
   glb_AutoCase_inActive = (objTgtField.value == false);
   return true;
   }

function SensitiveCaseUp(objFld) {
   var txt = objFld.value;
   var doIt = false;
   doIt = ((txt != "") && !glb_AutoCase_inActive);

   if (doIt) {
        if (typeof objFld.oldvalue != "undefined") {
            if (objFld.oldvalue.toLowerCase() == txt.toLowerCase() ) {
                //  the only thing that can have changed is the "case" of the
                //  letters, so leave it alone
                doIt = false;
                }
            }
        if (doIt) {
            objFld.value = objFld.value.toUpperCase();
            }
        }
//    return doIt;
    return editedData();
    }

function SensitiveCaseDown(objFld) {
   var txt = objFld.value;
   var doIt = false;
   doIt = ((txt != "") && !glb_AutoCase_inActive);

   if (doIt) {
        if (typeof objFld.oldvalue != "undefined") {
            if (objFld.oldvalue.toLowerCase() == txt.toLowerCase() ) {
                //  the only thing that can have changed is the "case" of the
                //  letters, so leave it alone
                doIt = false;
                }
            }
        if (doIt) {
            objFld.value = objFld.value.toLowerCase();
            }
        }
//    return doIt;
    return editedData();
    }

function CaseAdjustableValChanged(objFld) {
   ProperForm(objFld);
   return editedData();
   }

function ProperForm(objFld) {
// *******************************************
// Purpose:   Capitalise current value of control on form ... unless
//            the only change is a change of case, in which event
//            leave value alone, else user will get PO'd
// 
// Author:    CD
// Date:      Oct 8, 1994, 11:46
// Called by: AfterUpdate event of 'text-box'-type control
//            =ProperForm()
// *******************************************

   //var objFld = evnt.target;
   var txt = objFld.value;
   var doIt = false;
   doIt = ((txt != "") && !glb_AutoCase_inActive);
   // document.myForm.CaseUnAdjusted.value = objFld.name+": "+objFld.value;
   // document.myForm.CaseAdjusted.value = "";
   if (doIt) {
        if (typeof objFld.oldvalue != "undefined") {
            if (objFld.oldvalue.toLowerCase() == txt.toLowerCase() ) {
                //  the only thing that can have changed is the "case" of the
                //  letters, so leave it alone
                doIt = false;
                }
            }
        if (doIt) {
            objFld.value = Proper(objFld);
            // document.myForm.CaseAdjusted.value= objFld.value;
            }
        }
    return doIt;
    }

function Proper(objFld) {
// *******************************************
// Purpose:   Impose 'standardised' case conventions on the given string
//            the initial letter of each word is capitalised ...
//            except that
//            1.  Word is defined as a sequence of characters bounded by such as " ,.-'()/"
//            2.  Common linking words (on, and, the etc) are not capitalised
//            3.  Words of the form  Mac... and Mc... are also capitalised after the Mac or Mc
//            4.  Some testing is done to avoid obvious mistakes on place names
//                    ... 'shire', 'Betws-y-coed', 'Hampden-in-Arden' ...
// 
// Author:    CD
// Date:      Oct 8, 1994, 11:46
// Called by: General in-line code - most freq from ProperForm
// *******************************************

    var i, lenf, new_word = true, force_up = true;

    var strVal; // the original value - forced to lower
    var wk;     // the response being built
    var wka;    // the orig value with rhs padding

    if (objFld.value == "") return "";
    if (glb_AutoCase_inActive) return objFld.value;

    strVal = Trim(objFld.value.toLowerCase());

    lenf = strVal.length;
    var a = new Array(lenf);
    for (i = 0; i<lenf; i++) {
        a[i] = strVal.charAt(i);
        }
    i = 0; wk = "";
    wka = strVal + "......";      //  Avoid the silly VB fault in re testing literals against shorter strings

    while (i <= (lenf-1)) {
        switch ( a[i] ) {
            case " ": new_word = true; break;
            case ".": new_word = true; break;
            case ",": new_word = true; break;
            case "-": new_word = true; break;
            case "'": new_word = true; break;
            case "(": new_word = true; break;
            case ")": new_word = true; break;
            case "/": new_word = true; break;
            case "\"": new_word = true; break;
            default:
                    if (new_word) {
                        a[i] = a[i].toUpperCase();        //  force initial character of new word to UPPER case
                        //    check for the celtic surnames ... we don't really need to go overboard on all the
                        //    words in English starting with mac - this routine is usually used in the context
                        //    of name and address, company name, job title etc., so hit as few as possible
                        force_up = true;
                        if (strVal.substr(i, 3) == "mac") {
                            switch (Proper_Word(wka, i)) {
                                case "machin":          force_up = false; break;
                                case "machine":         force_up = false; break;
                                case "machines":        force_up = false; break;
                                case "mace":            force_up = false; break;
                                case "maces":           force_up = false; break;
                                case "macey":           force_up = false; break;
                                case "mackerel":        force_up = false; break;
                                case "macclesfield":    force_up = false; break;
                                }
                            if ( force_up ) {
                                if (lenf >= (i + 3)) a[i + 3] = a[i+3].toUpperCase();
                                }
                            } else {
                            if (strVal.substr(i, 2) == "mc") {
                                if (lenf >= (i + 2)) a[i + 2] = a[i+2].toUpperCase();
                                } else {
                                if ( Proper_UndoCapital(wka, i) ) a[i] = a[i].toLowerCase();
                                }
                            }
                        new_word = false;
                        }  // End If
            }  // End Select
        wk += a[i];
        i++ ;
        }  // Wend

    return wk;
    } // End Function

function Proper_UndoCapital(wka, i) {
// *******************************************
// Purpose:   The character in "wka$" pointed to by "i" is the 1st
//                character of a new word in the string.
//            It has so far been forced to upper case.
//            Can we find a reason to turn it back to lower case.
// 
// Author:    CD
// Date:      Oct 8, 1994, 11:46
// Called by: Proper
// *******************************************

    // Proper_UndoCapital = True
    if (i > 1) {
        //  So we are not at beginning of given string
        //  ... do we really need the 1st letter of word to be capitalised

        //   1st - are we interested in the previous character ?
        if (i >= 2) {
            if ( wka.substr(i - 2, 4) == "c/o " ) return true;           //  trap out on the 'o' part
            }
        switch ( wka.substr(i - 1, 3) ) {
            case " a ": return true; break;
            case "'t ": return true; break;       //  can't won't etc
            case "'a ": return true; break;       //  jusqu'a
            case "'s ": return true; break;
            case "'s.": return true; break;
            case "'s,": return true; break;
            case "'s;": return true; break;
            case "-y-": return true; break;
            }
        if ( wka.substr(i - 1, 4) == "-in-" ) return true;
        if ( wka.substr(i - 1, 6) == "-shire" ) return true;

        //    OK - then what about the word itself
        //    ... but not as 1st word of phrase, 'cos we're in the middle of string
        switch ( wka.substr(i, 3) ) {
            case "as ": return true; break;
            case "of ": return true; break;
            case "on ": return true; break;
            case "in ": return true; break;
            case "le ": return true; break;
            case "la ": return true; break;
            }
        switch ( wka.substr(i, 4) ) {
            case "and ": return true; break;
            case "for ": return true; break;
            case "the ": if ( ((wka.charAt(0) >= "0") && (wka.charAt(0) <= "9")) || (wka.charAt(0) == "\"") ) {
                            //  if the previous chars have been a number, then allow capital
                            //   e.g. 48a the willows
                            //  is 'the' the first word after the house number
                            var tmp = Trim(wka.substring(0,i-2+1)); // first part of string not including 'the'
                            if ( -1 != tmp.indexOf(" ") ) return true; break;  // more than 1 word prior
                            } else {
                            return true; break;
                            }
            }
        if ( wka.substr(i, 5) == "upon " ) return true;
        }

    //    and what if the word _is_ at the beginning of the phrase
    if ( wka.substr(i, 4) == "etc " ) return true;
    if ( wka.substr(i, 4) == "etc." ) return true;
    if ( wka.substr(i, 4) == "c/o " ) return true;      //  trap out on the 'c' part
    if ( wka.substr(i, 3) == "on-" ) return true;
    if ( wka.substr(i, 3) == "plc" ) return true;
    if ( wka.substr(i, 3) == "de " ) return true;
    if ( wka.substr(i, 3) == "en " ) return true;
    if ( wka.substr(i, 3) == "en-" ) return true;
    if ( wka.substr(i, 2) == "d'" ) return true;
    return false;
    }

function Proper_Word(txt, st) {
// *******************************************
// Purpose:   Return the 'word' in txt$ starting with the st'th character
//            'word' here is simplistic ... only contains letters
//
// Author:    CD
// Date:      Oct 8, 1994, 11:46
// Called by: Proper
// *******************************************

   var i = st;
   while ( i < txt.length ) {
      if ( (txt.charAt(i) < "a") || (txt.charAt(i) > "z") ) break;
      i++;
      }
    return txt.substring(st, i - 1+1);
    }

function jsf_DateDiff(dte1,dte2,respMode)  {
    // take dates in any form (typically string)
    // respMode determines the type of response:
    //      0 = years to 1dp
    //      1 = full text    
    var noise = new jsf_ByRefArg();
    var stdDate1 = new jsf_ByRefArg();
    var stdDate2 = new jsf_ByRefArg();
    var mm1, mm2, yy1, yy2, wk;
    var mDiff, yDiff, dDiff;
    if ( ! jsf_isValidDateString(dte1.value, false, noise, stdDate1) ) {
        return false;
        }
    wk = noise.getValue();
    var convDate1 = new Date(Date.parse(wk));

    if ( ! jsf_isValidDateString(dte2.value, false, noise, stdDate2) ) {
        return false;
        }
    wk = noise.getValue();
    var convDate2 = new Date(Date.parse(wk));
    if (arguments.length >3) {
       // repaint date value in abbrev format dd mmm yyyy
       if (arguments[3]) dte1.value = jsf_DateForDisplay(convDate1,true);
       }

    switch ( respMode )  {
        case 0:
                mm1 = 1 + convDate1.getMonth();
                mm2 = 1 + convDate2.getMonth();
                yy1 = convDate1.getFullYear();
                yy2 = convDate2.getFullYear();
                if (yy2 > yy1)  {
                    mDiff = mm2 - mm1;
                    yDiff = yy2 - yy1;
                    if (mDiff < 0) { yDiff--; mDiff = mDiff+12; }
                    dDiff = parseInt( (yDiff + (mDiff / 12)) * 10) / 10;
                    }  else  {
                    if (yy2 < yy1)  {
                        mDiff = mm1 - mm2;
                        yDiff = yy1 - yy2;
                        if (mDiff < 0) { yDiff--; mDiff = mDiff+12; }
                        dDiff = parseInt( (yDiff + (mDiff / 12)) * 10) / 10;
                        }  else  {
                        if (mm2 < mm1)  {
                            mDiff = mm1 - mm2;
                            yDiff = yy1 - yy2;
                            }  else  {
                            mDiff = mm2 - mm1;
                            yDiff = yy2 - yy1;
                            }
                        if (mDiff < 0) { yDiff--; mDiff = mDiff+12; }
                        dDiff = parseInt( (yDiff + (mDiff / 12)) * 10) / 10;
                        }
                    }
                break;
        case 1:
                break;
        }
    return dDiff;
    }

function onChangeEmail(objFld)  {
    if ( ! jsf_isEmailAddr(objFld.value) ) {
        if ( hasValue(objFld.value) ) {
            customAlert("","Incorrect format for email in '" +objFld.name+ "'.");
            return false;
            }
        }
    return editedData();
    }

function onChangeWebAddr(objFld)  {
    // don't allow http:// prefix ... presume that it is already present
    if ( -1 == (objFld.value.toLowerCase().indexOf("://")) ) {
        // there is no protocol identifier, so prefix with http://
        if (objFld.value != "") {
            objFld.value = "http://" + objFld.value;
            }
        }
    if ( ! jsf_isWebAddr(objFld.value) ) {
        if ( hasValue(objFld.value) ) {
            customAlert("","Incorrect format for web address in '" +objFld.name+ "'.");
            return false;
            }
        }
/*
    if ( -1 == idx ) {
        // there is no protocol identifier, so prefix with http://
        if (objFld.value <> "") { objFld.value = "http://" + objFld.value; }
        }
    alert("Test value is: "+ (objFld.value.toLowerCase().indexOf("://")));

    alert("onChangeWebAddr");
    alert("Test value is: "+ (objFld.value.indexOf("://")));
    alert("Test value is: "+ (objFld.value.indexOf("://")));
*/
    return editedData();
    }

function onChangeDate(objFld)  {
    // optional 2nd arg true/false ... allows incomplete date e.g. "Aug 2004"
    var respDate = new jsf_ByRefArg(), relax;
    if ( arguments.length > 1 ) relax = arguments[1]; else relax = false;
    if ( ! jsf_isValidDateString(objFld.value, relax, respDate) ) {
        if ( hasValue(objFld.value) ) {
            if ( ! relax ) {
                customAlert("","Incorrect format for date in '" +objFld.name+ "'.");
                return false;
                }
            }
        } else {
        if ( relax ) objFld.value = respDate.getValue();
        }
//   var objForm = FormContainer(objFld);
//   objForm.EchoDate.value = jsf_DateForDisplay(respDate.getValue());
    return editedData();
    }

function customAlert(winTitle,msg)  {
   // replacement for the built-in alert cmd
   alert(msg);
   }

function customConfirm(winTitle,msg)  {
   // replacement for the built-in confirm cmd
   return confirm(msg);
   }

function SelectedMandatoryValue(ctrlId, objName)  {
   var i = 0, retVal = false;
   var ctrl = document.getElementById(ctrlId);
   if ( ctrl != null)  {
      if ( typeof(ctrl.value) != "undefined" )  {
         if ( ctrl.value != "" )  {
             // if we arrive here, it's got a value
             retVal = ctrl.value ;
            }
         }
      }
   if (retVal === false)  customAlert("","There is currently no selected "+objName+".");
   return retVal;
   }

function SelectedFromOptionList(ctrlName, objName)   {
    var i = 0, retVal = false;
    var obj;

// ctrlName is the name of zero or more controls - e.g. name=ChosenLanguage 
// objName is a text item - e.g. Language, Specialism, Service Type

// obj might be any one of:
//     undefined     ( no Languages are defined for System )
//     scalar        ( one Language is defined for System )
//     array         ( several Languages are defined for System )
    var aryCtrl = document.getElementsByName(ctrlName);
    var popul = aryCtrl.length;
    switch (popul)  {
        case 0:
                    // if we arrive here, there are no Languages at all
                    customAlert("", "There is currently no "+objName+" to choose.");
                    retVal = false;
                    break;
        case 1:
                    // if we arrive here, it's a scalar
                    obj = aryCtrl[0];
                    retVal = obj.value;
                    obj.checked = true;
                    break;
        default:
                    // if we arrive here, it's an array
                    while (i < popul) {
                        obj = aryCtrl[i]
                        if ( obj.checked ) {
                            retVal = obj.value;
                            break;
                            }
                        i++;
                        }
                    if ( retVal == false )   {
                        customAlert("", "Please ensure that one "+objName+" is chosen.");
                        aryCtrl[0].focus();
                        }
                    break;
         }
    return retVal;
    }
