 day_opts = {
  day_of_month : {
   desc : 'Day of the month, 1 or 2 digits [1-31]',
   php  : 'j',
   mysql: '%e',
   extra: ''
  },
  day_of_month_2d : {
   desc : 'Day of the month, 2 digits [01-31]',
   php  : 'd',
   mysql: '%d',
   extra: ''
  },
  day_of_week_3a : {
   desc : 'Day of the week, 3 letter abbr. [Mon-Sun]',
   php  : 'D',
   mysql: '%a',
   extra: ''
  },
  day_of_week_full : {
   desc : 'Day of the week, full text [Monday-Sunday]',
   php  : 'l',
   mysql: '%W',
   extra: ''
  },
  day_of_week_iso : {
   desc : 'Day of the week, ISO-8601 [1=Mon; 7=Sun]',
   php  : 'N',
   mysql: '',
   extra: 'PHP 5.1.0+ only'
  },
  day_of_week_suff : {
   desc : 'Day of month 2-char suffix [st, nd, rd, th]',
   php  : 'S',
   mysql: '',
   extra: 'PHP only'
  },
  day_of_week_w_suff : {
   desc : 'Day of month w/ 2-char suffix [1st, 2nd, ...]',
   php  : '',
   mysql: '%D',
   extra: 'MySQL only'
  },
  day_of_week_w_sun0 : {
   desc : 'Day of the week, number [0=sun; 6=Sat] ',
   php  : 'w',
   mysql: '%w',
   extra: ''
  },
  day_of_year_0 : {
   desc : 'Day of the year [0-365]',
   php  : 'z',
   mysql: '',
   extra: 'PHP only'
  },
  day_of_year_1 : {
   desc : 'Day of the year [1-366]',
   php  : '',
   mysql: '%j',
   extra: 'MySQL only'
  }
 } // day_opts
  
 week_opts = {
  week_iso : {
   desc : 'Week number. Mon. first day of week (ISO8601) [01..53] ',
   php  : 'W',
   mysql: '%v',
   extra: 'PHP 4.1.0+'
  },
  week_sun_0 : {
   desc : 'Week number. Sun. first day of week [00..53] ',
   php  : '',
   mysql: '%U',
   extra: 'MySQL only'
  },
  week_mon_0 : {
   desc : 'Week number. Mon. first day of week [00..53] ',
   php  : '',
   mysql: '%u',
   extra: 'MySQL only'
  },
  week_sun_1 : {
   desc : 'Week number. Sun. first day of week [01..53] ',
   php  : '',
   mysql: '%V',
   extra: 'MySQL only'
  }
 }, // week_opts
 
 month_opts = {
  month_full : {
   desc : 'Month, full text [January-December]',
   php  : 'F',
   mysql: '%M',
   extra: ''
  },
  month_2 : {
   desc : 'Month, 2 digits [01-12]',
   php  : 'm',
   mysql: '%m',
   extra: ''
  },
  month_3a : {
   desc : 'Month, 3 letter abbr. [Jan-Dec]',
   php  : 'M',
   mysql: '%b',
   extra: ''
  },
  month : {
   desc : 'Month, 1 or 2 digits [1-12]',
   php  : 'n',
   mysql: '%c',
   extra: ''
  },
  daysinmonth : {
   desc : 'Days in month [28-31]',
   php  : 't',
   mysql: '',
   extra: 'PHP only'
  }
 } // month_opts
 
 year_opts = {
  year_4 : {
   desc : '4-digit year [e.g. 1961, 2009]',
   php  : 'Y',
   mysql: '%Y',
   extra: ''
  },
  year_2 : {
   desc : '2-digit year [00-99]',
   php  : 'y',
   mysql: '%y',
   extra: ''
  },
  year_iso : {
   desc : '4-digit year, ISO-8601 [e.g. 1961, 2009]',
   php  : 'o',
   mysql: '',
   extra: 'PHP 5.1.0+ only'
  },
  year_sun_first : {
   desc : 'Year for week where Sun is first day of week',
   php  : '%X',
   mysql: '',
   extra: 'PHP only'
  },
  year_mon_first : {
   desc : 'Year for week where Mon is first day of week',
   php  : '%x',
   mysql: '',
   extra: 'PHP only'
  },
  is_leap_year : {
   desc : 'Leap year [1=yes; 0=no]',
   php  : 'L',
   mysql: '',
   extra: 'PHP only'
  }
 } // year_opts
  
 time_opts = {
  hour_12 : {
   desc : 'Hour, 12-hour format 1 or 2 digits [1-12]',
   php  : 'g',
   mysql: '%l',
   extra: ''
  },
  hour_24 : {
   desc : 'Hour, 24-hour format 1 or 2 digits [0-23]',
   php  : 'G',
   mysql: '%k',
   extra: ''
  },
  hour_12_2 : {
   desc : 'Hour, 12-hour format 2 digits [01-12]',
   php  : 'h',
   mysql: '%h',
   extra: ''
  },
  hour_24_2 : {
   desc : 'Hour, 24-hour format 2 digits [00-23]',
   php  : 'H',
   mysql: '%H',
   extra: ''
  },
  minutes : {
   desc : 'Minutes, 2 digits [00-59]',
   php  : 'i',
   mysql: '%i',
   extra: ''
  },
  seconds : {
   desc : 'Seconds, 2 digits [00-59]',
   php  : 's',
   mysql: '%s',
   extra: ''
  },
  am_pm_lower : {
   desc : 'Lowercase Ante/Post meridiem [am, pm]',
   php  : 'a',
   mysql: '',
   extra: 'PHP only'
  },
  am_pm_upper : {
   desc : 'Uppercase Ante/Post meridiem [AM, PM]',
   php  : 'A',
   mysql: '%p',
   extra: ''
  },
  swatch : {
   desc : 'Swatch Internet time [000-999]',
   php  : 'B',
   mysql: '',
   extra: 'PHP only'
  },
  u_seconds : {
   desc : 'Microseconds [0-999999]',
   php  : 'u',
   mysql: '%f',
   extra: 'PHP: 5.2.2+ only'
  },
  time12 : {
   desc : 'Time, 12-hour (hh:mm:ss followed by AM or PM)',
   php  : '',
   mysql: '%r',
   extra: 'MySQL only'
  },
  time24 : {
   desc : 'Time, 24-hour (hh:mm:ss)',
   php  : '',
   mysql: '%T',
   extra: 'MySQL only'
  }
 } // time_opts
 timezone_opts = {
  tz_id : {
   desc : 'Timezone identifier [UTC, Europe/Berlin...]',
   php  : 'e',
   mysql: '',
   extra: 'PHP 5.1.0+'
  },
  is_dst_time : {
   desc : 'Date/time in DST [1=yes, 0=no]',
   php  : 'I',
   mysql: '',
   extra: 'PHP only'
  },
  gmt_diff_4 : {
   desc : 'Diff. to GMT hhmm format [e.g. +0200]',
   php  : 'O',
   mysql: '',
   extra: 'PHP only'
  },
  gmt_diff_5 : {
   desc : 'Diff. to GMT hh:mm format [e.g. +02:00]',
   php  : 'P',
   mysql: '',
   extra: 'PHP 5.1.3+ only'
  },
  tz_abbrev : {
   desc : 'Timezone abbreviation [EST, CET, …]',
   php  : 'T',
   mysql: '',
   extra: 'PHP only'
  },
  tz_offset : {
   desc : 'Timezone offset in seconds [-43200 - 50400]',
   php  : 'Z',
   mysql: '',
   extra: 'PHP only'
  }
 } // timezone_opts
 
 date_time_opts = {
  datetime_iso : {
   desc : 'ISO 8601 [ex. 2004-02-12T15:19:21+00:00]',
   php  : 'c',
   mysql: '',
   extra: 'PHP 5+ only'
  },
  datetime_rfc2822 : {
   desc : 'RFC 2822 [ex. Thu, 21 Dec 2000 16:01:07 +0200]',
   php  : 'r',
   mysql: '',
   extra: 'PHP only'
  },
  unixtime : {
   desc : 'Seconds since Unix Epoch',
   php  : 'U',
   mysql: '',
   extra: 'PHP only'
  }
 } // date_time_otps

  function what(num) {
   return ''+
    '<select id="what'+num+'" onChange="setDetail('+num+');">\n'+
    ' <option value="">(not used)<'+'/option>\n'+
    ' <option value="day">Day<'+'/option>\n'+
    ' <option value="week">Week<'+'/option>\n'+
    ' <option value="month">Month<'+'/option>\n'+
    ' <option value="year">Year<'+'/option>\n'+
    ' <option value="time">Time<'+'/option>\n'+
    ' <option value="timezone">Time zone<'+'/option>\n'+
    ' <option value="date_time">Date+time<'+'/option>\n'+
    ' <option value="literal">Literal<'+'/option>\n'+
    '<'+'/select>\n';
  }

  function pickopts(id) {
   switch(id) {
    case "day":       return day_opts;
    case "week":      return week_opts;
    case "month":     return month_opts;
    case "year":      return year_opts;
    case "time":      return time_opts;
    case "timezone":  return timezone_opts;
    case "date_time": return date_time_opts;
   } // switch
   return null;
  } // func pickopts
  
  function detail(what, num) {
   if ('' == what) {
    return '';
   }
   if ("literal" == what) {
    return '<input type="text" id="detail'+num+'" onKeyUp="updateExpressions();">\n';
   }
   var o = pickopts(what);
   if (o === null) {
    alert ("unknown what: " + what);
    return;
   }
   var retval = '<select id="detail'+num+'" onChange="updateExpressions();">\n';
   var desc;
   for (var f in o) {
    desc = o[f].desc;
    if (o[f].extra != '') {
     desc += ' -- ' + o[f].extra;
    }
    retval += '<option value="'+f+'">'+desc+'<'+'/option>\n';
   }
   retval += '<'+'/select>\n';
   return retval;
  } // func detail

  function setDetail(num) {
   $('c_'+num+'_detail').innerHTML = detail($('what'+num).value, num);
   updateExpressions();
  } // func setDetail

  function adddel(num) {
   return ''+
    '<a href="javascript:void(0);" onClick="add('+num+')"><img src="p1.jpg"><'+'/a>\n' +
    '<a href="javascript:void(0);" onClick="del('+num+')"><img src="m1.jpg"><'+'/a>\n';
  } // func adddel

  var rowcounter = 1;
  function add(num) {
   var tr = $('t').insertRow($('row'+num).rowIndex+1);
   tr.id = "row"+(++rowcounter);
   var cellcount = 0;
   /*
   td0 = tr.insertCell(cellcount++);
   td0.class = 'nodisp';
   td0.innerHTML = rowcounter;
   */
   td1 = tr.insertCell(cellcount++);
   td1.id = 'c_'+rowcounter+'_what';
   td1.innerHTML = what(rowcounter);
   td2 = tr.insertCell(cellcount++);
   td2.id = 'c_'+rowcounter+'_detail';
   td2.innerHTML = detail('');
   td3 = tr.insertCell(cellcount++);
   td3.id = 'c_'+rowcounter+'_addel';
   td3.innerHTML = adddel(rowcounter);
  } // func add

  function del(num) {
   $('t').deleteRow($('row'+num).rowIndex);
   updateExpressions()
  }
  
  function updateExpressions() {
   var tbl = $('t');
   var pattern = /_([0-9]+)_/
   var php = '';
   var mysql = '';
   for(var r in tbl.rows) {
    // alert(r);
    if (tbl.rows[r]) {
     for (var c in tbl.rows[r].cells) {
      var s = new String(tbl.rows[r].cells[c].id);
      if (s.endsWith('_what')) {
       var m = s.match(pattern);
       var what = $('what'+m[1]);
       if (what.value != '') {
        var detail = $('detail'+m[1]);
        if (detail.tagName.match(/SELECT/i)) {
         var opts = pickopts(what.value);
         php += opts[detail.value].php;
         mysql += opts[detail.value].mysql;
        }
        else if (detail.tagName.match(/INPUT/i) && 
                 detail.type.match(/TEXT/i)) {
         if (detail.value != '') {
          php += '\\'+detail.value.split('').join('\\');
         }
         mysql += detail.value;
        }
       } // what != '' ?
      } // if row w/ spec
     } // for c in cells
    } // for each row
   } // if rows
   $('php_result').value = '<?php echo date("' + php + '", [timestamp]); ?>';
   $('mysql_result').value = 'SELECT DATE_FORMAT(date, "' + mysql + '");';
   
   new Ajax.Request(
    'checkformats.php', 
    { method:'get',
      parameters: {php: php},
      onSuccess: function(transport) {
       $('php_test').innerHTML = transport.responseText;
      },
      onFailure: function(){
       $('php_test').innerHTML = '(Communication failure. please try again later)';
      }
    }
   );
   new Ajax.Request(
    'checkformats.php', 
    { method:'get',
      parameters: {mysql: mysql},
      onSuccess: function(transport) {
       $('mysql_test').innerHTML = transport.responseText;
      },
      onFailure: function(){
       $('mysql_test').innerHTML = '(Communication failure. please try again later)';
      }
    }
   );
  } // func updateExpressions