
// ----------------------------------------------------------------------------
function selectDateStoredFormat( form, field, format, padding ) {

  out = '';

  for ( i = 0; i < format.length; i++ ) {

    switch ( format.substr(i, 2 ) ) {
      case '%Y':
        year = form[ field + 'year' ][ form[ field + 'year'  ].selectedIndex ].value;
        while ( year.length < 4 )
          year = '0' + year;
        out += year;
        i++;
        break;                                      
      case '%M':
        month = form[ field + 'month' ][ form[ field + 'month' ].selectedIndex ].value;
        while ( padding && ( month.length < 2 ) )
          month = '0' + month;
        out += month;
        i++;
        break;                                      
      case '%D':    
        day = form[ field + 'day' ][ form[ field + 'day'   ].selectedIndex ].value;
        while ( padding && ( day.length < 2 ) )
          day = '0' + day;
        out += day;
        i++;
        break;
      case '%T':
        timeshort = form[ field + 'timeshort'   ].value;
        while ( padding && ( timeshort.length < 5 ) )
          timeshort = '0' + timeshort;
        out += timeshort;
        i++;
        break;
      default:
        out += format.charAt(i);
        break;
    }

  }

  form[ field ].value = out;

}

// ----------------------------------------------------------------------------
function validDateTime( date_input, format, minimum, maximum ) {

// - day and month might be 1 or 2 chars long
// - the separator must be the - sign
// - function checks the followings also:
//   (1000 <= year <= 9999, 1 <= month <=12, 1 <= days <= days_in_month
//
// - valid examples:    2002-01-2,  2002-1-1, 1951-12-31, 2000-02-29
// - invalid examples:  2002-02-29, 2000-0-1, 200-0-1,    2000-abc

  // create a validation regexp

  formatcompiled = '^' + format + '$' ;
  formatcompiled = formatcompiled.replace( 'YYYY', '([0-9]{4})' );
  formatcompiled = formatcompiled.replace( 'YY', '([0-9]{2})' );
  formatcompiled = formatcompiled.replace( 'MM', '([0-9]{2})' );
  formatcompiled = formatcompiled.replace( 'M', '([0-9]{1,2})' );
  formatcompiled = formatcompiled.replace( 'DD', '([0-9]{2})' );
  formatcompiled = formatcompiled.replace( 'D', '([0-9]{1,2})' );
  formatcompiled = formatcompiled.replace( 'hh', '([0-9]{2})' );
  formatcompiled = formatcompiled.replace( 'h', '([0-9]{1,2})' );
  formatcompiled = formatcompiled.replace( 'mm', '([0-9]{2})' );
  formatcompiled = formatcompiled.replace( 'm', '([0-9]{1,2})' );
  formatcompiled = formatcompiled.replace( 'ss', '([0-9]{2})' );
  formatcompiled = formatcompiled.replace( 's', '([0-9]{1,2})' );
  regexp = new RegExp( formatcompiled );
  result = regexp.exec( date_input );
  
  if ( !result ) 
    // format itself failed
    return false;

  // if syntax is ok, the check the date
  // semantically

  // find element indexes and make a match with 
  // the regexp indexes

  indexes = Array(
    new Array( format.indexOf( 'YYYY' ), 'year' ),
    new Array( format.indexOf( 'M' ), 'month' ),
    new Array( format.indexOf( 'D' ), 'days' ),
    new Array( format.indexOf( 'h' ), 'hour' ),
    new Array( format.indexOf( 'm' ), 'min' ),
    new Array( format.indexOf( 's' ), 'sec' )
  );

  indexes.sort( indexcompare );

  regexindex = new Array();
  counter    = 1;

  for ( i = 0; i < indexes.length; i++ )
    if ( indexes[ i ][ 0 ] != -1 ) {
      regexindex[ indexes[ i ][ 1 ] ] = counter;
      counter++;
    }

  // at last... we know the regexp-indexes of 
  // the date components (eg 'YYYY' is Regex.$1 )
  // and so on

  year  = result[ regexindex[ 'year'  ] ];
  month = result[ regexindex[ 'month' ] ];
  days  = result[ regexindex[ 'days'  ] ];
  hour  = result[ regexindex[ 'hour'  ] ];
  min   = result[ regexindex[ 'min'   ] ];
  sec   = result[ regexindex[ 'sec'   ] ];

  daysOfMonth = new Array( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
  if ( ( null != year ) && ( ( year % 4 ) == 0 ) )
    daysOfMonth[1] = 29;

  if ( 
       ( ( null != month ) && ( ( month <= 0 ) || ( month > 12 ) ) ) ||
       ( ( null != days )  && ( ( days <= 0 )  || ( days > 31 ) ) )  ||
       ( ( month && ( days > daysOfMonth [ month - 1 ] ) ) ) ||
       ( ( null != hour )  && ( ( hour < 0 )   || ( hour > 23 ) ) )  ||
       ( ( null != min )   && ( ( min < 0 )    || ( min > 59 ) ) )   ||
       ( ( null != sec )   && ( ( sec < 0 )    || ( sec > 59 ) ) )
     )
    return false;

  if ( null != minimum ) {
    if ( null != month ) 
      month_num = month - 1;
    else
      month_num = null;

    date        = new Date( u(year), month_num, u(days), u(hour), u(min), u(sec) );
    minimumdate = new Date( minimum * 1000 );

    if ( date < minimumdate ) 
      return false;
  }

  if ( null != maximum ) {
    if ( null != month )
      month_num = month - 1;
    else
      month_num = null;

    date        = new Date( u(year), month_num, u(days), u(hour), u(min), u(sec) );
    maximumdate = new Date( maximum * 1000 );
    if ( date > maximumdate ) 
      return false;
  }

  return true;

}

// ----------------------------------------------------------------------------
function indexcompare( item1, item2 ) {

  if ( item1[ 0 ] < item2[ 0 ] )
    return -1;

  if ( item1[ 0 ] > item2[ 0 ] )
    return 1;

  return 0;

}

// ----------------------------------------------------------------------------
function u( value ) {

  return typeof( value ) == "undefined" ? null : value;

}

// ----------------------------------------------------------------------------
function clonefishErrors() {

  // class to encapsulate methods for displaying error messages.
  // handles alert() and innerHTML operations.

  // properties
  this.useAlert                    = false;
  this.useHTML                     = false;
  this.empty                       = true; // set by the class as an overall result flag

  this.errorMessages               = new Array();
  this.messageContainerLayout      = '%s';
  this.messageLayout               = '%s<br />';
  this.alertMessageContainerLayout = '%s';
  this.alertMessageLayout          = " - %s\n";

  // "Javascript Does Not Support Associative Arrays"
  // http://www.hunlock.com/blogs/Mastering_Javascript_Arrays
  // 
  // that's why we need the indexmap: this way we can avoid
  // later using the for...in construct, which also conflicts
  // prototype.js
  this.inputnameMap                = new Array();

  // to avoid the same messages stored multiple times
  this.messageMap                  = new Array();

  // methods
  this.addIf    = clonefishErrorsAddIf;
  this.add      = clonefishErrorsAdd;
  this.go       = clonefishErrorsGo;
  this.findDiv  = clonefishErrorsFindDiv;

}

// ----------------------------------------------------------------------------
function clonefishErrorsAddIf( inputname, condition, message ) {

  // clean up HTML element (might be filled by previous call)
  errordiv = this.findDiv( 'cf_error' + inputname );

  if ( errordiv ) {

    errordiv.innerHTML        = '';
    errordiv.style.visibility = 'hidden';
    errordiv.style.display    = 'none';

  }

  if ( !condition )
    this.add( inputname, message );

}

// ----------------------------------------------------------------------------
function clonefishErrorsAdd( inputname, message ) {

  // first let's check if this message has already been
  // stored
  for ( i = 0; i < this.messageMap.length; i++ )
    if ( message == this.messageMap[i] )
      return;

  // see note about associative arrays in clonefishErrors()

  // faster and easier to search in the name map 
  // when the names are joined
  inputnames = ',' + this.inputnameMap.join(',') + ',';

  // escaping regular expression for inputs with special characters
  // in their name (eg. [] for array handling in PHP)
  inputnameEscaped = inputname.replace( /([\[\]])/g, '\\$1' );

  // find name in name map

  if ( inputnames.search( ',' + inputnameEscaped + ',' ) == -1 ) {
    index                      = this.inputnameMap.length;
    this.inputnameMap[ index ] = inputname;
  }
  else {

    index = 0;
    for ( i = 0; i < this.inputnameMap.length; i++ ) {
      if ( this.inputnameMap[ i ] == inputname )
        index = i;
    }

  }

  // now store the message where it's needed

  if ( null != this.errorMessages[ index ] ) {

    messageindex = this.errorMessages[ index ]['messages'].length;
    this.errorMessages[ index ]['messages'][ messageindex ] = message;

  }
  else {

    // new input: add array and initialize 2nd dimension
    this.errorMessages[ index ] = new Array();
    this.errorMessages[ index ]['inputname'] = inputname;
    this.errorMessages[ index ]['messages']  = Array( message );

  }

  // store message to avoid repeating messages
  this.messageMap[ this.messageMap.length ] = message;

}

// ----------------------------------------------------------------------------
function clonefishErrorsGo() {

  alertmessage = '';
  this.empty   = true;

  while ( this.errorMessages.length > 0 ) {

    currentElement = this.errorMessages.shift();

    if ( this.useHTML ) {

      errordiv = this.findDiv( 'cf_error' + currentElement['inputname'] );

      if ( null != errordiv ) {

        if ( null != errordiv.innerHTML ) {

          // compile messages in layout
          message = '';

          for ( i = 0; i < currentElement['messages'].length; i++ )
            message =
              message +
              this.messageLayout.replace('%s', currentElement['messages'][ i ] );

          if ( message != '' ) {
            errordiv.innerHTML        =
              this.messageContainerLayout.replace('%s', message );
            errordiv.style.visibility = 'visible';
            errordiv.style.display    = 'block';
            this.empty                = false;
          }
          else {
            errordiv.innerHTML        = '';
            errordiv.style.visibility = 'hidden';
            errordiv.style.display    = 'none';
          }

        }
        else
          // no innerHTML support: fallback to simple alert
          this.useAlert = true;

      }
      else
        // HTML element not found by ID: fallback to simple alert
        this.useAlert = true;

    }

    if ( this.useAlert ) {

      for ( i = 0; i < currentElement[ 'messages' ].length; i++ ) {
        alertmessage = 
          alertmessage + 
          this.alertMessageLayout.replace(
            '%s', currentElement['messages'][ i ]
          )
        ;
        this.empty = false;
      }

    }

  }

  if ( !this.empty && this.useAlert && ( alertmessage.length > 0 ) )
    alert( this.alertMessageContainerLayout.replace('%s', alertmessage ) );

}

// ----------------------------------------------------------------------------
function clonefishErrorsFindDiv( id ) {

  if ( typeof( document.getElementById ) != "undefined" )
    return document.getElementById( id );
  else
    if ( typeof( document.all ) != "undefined" )
      return document.all[ id ];

  return false;

}

