2 // +----------------------------------------------------------------------+
 
   3 // | Anuko Time Tracker
 
   4 // +----------------------------------------------------------------------+
 
   5 // | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
 
   6 // +----------------------------------------------------------------------+
 
   7 // | LIBERAL FREEWARE LICENSE: This source code document may be used
 
   8 // | by anyone for any purpose, and freely redistributed alone or in
 
   9 // | combination with other software, provided that the license is obeyed.
 
  11 // | There are only two ways to violate the license:
 
  13 // | 1. To redistribute this code in source form, with the copyright
 
  14 // |    notice or license removed or altered. (Distributing in compiled
 
  15 // |    forms without embedded copyright notices is permitted).
 
  17 // | 2. To redistribute modified versions of this code in *any* form
 
  18 // |    that bears insufficient indications that the modifications are
 
  19 // |    not the work of the original author(s).
 
  21 // | This license applies to this document only, not any other software
 
  22 // | that it may be combined with.
 
  24 // +----------------------------------------------------------------------+
 
  26 // | https://www.anuko.com/time_tracker/credits.htm
 
  27 // +----------------------------------------------------------------------+
 
  29 import('form.TextField');
 
  30 import('DateAndTime');
 
  32 class DateField extends TextField {
 
  33   var $mWeekStartDay = 0;
 
  34   var $mDateFormat  = "d/m/Y";
 
  35   var $lToday      = "Today";
 
  38   var $class = 'DateField';
 
  40   var $lCalendarButtons = array('today'=>'Today', 'close'=>'Close');
 
  42   function __construct($name) {
 
  44     $this->mDateObj  = new DateAndTime();
 
  46     if (isset($GLOBALS["I18N"])) {
 
  47       $this->setLocalization($GLOBALS["I18N"]);
 
  51   function setLocalization($i18n)  {
 
  54     FormElement::setLocalization($i18n);
 
  55     $this->mDateObj->setFormat($user->date_format);
 
  57     $this->mMonthNames = $i18n->monthNames;
 
  58     $this->mWeekDayShortNames = $i18n->weekdayShortNames;
 
  59     $this->lToday = $i18n->getKey('label.today');
 
  60     $this->lCalendarButtons['today'] = $i18n->getKey('label.today');
 
  61     $this->lCalendarButtons['close'] = $i18n->getKey('button.close');
 
  63     $this->mDateFormat = $user->date_format;
 
  64     $this->mWeekStartDay = $user->week_start;
 
  67   // set current value taken from session or database
 
  68   function setValueSafe($value)  {
 
  69     if (isset($value) && (strlen($value) > 0)) {
 
  70       $this->mDateObj->parseVal($value, DB_DATEFORMAT);
 
  71       $this->value = $this->mDateObj->toString($this->mDateFormat); //?
 
  74   // get value for storing in session or database
 
  75   function getValueSafe() {
 
  76     if (strlen($this->value)>0) {
 
  77       $this->mDateObj->parseVal($this->value, $this->mDateFormat);  //?
 
  78       return $this->mDateObj->toString(DB_DATEFORMAT);
 
  84   function toStringControl() {
 
  86     if (!$this->isEnabled()) {
 
  87       $html = htmlspecialchars($this->getValue()).
 
  88         "<input type=\"hidden\" name=\"$this->name\" value=\"".htmlspecialchars($this->getValue())."\">\n";
 
  91         if ($this->id=="") $this->id = $this->name;
 
  95       // http://www.nsftools.com/tips/JavaScriptTips.htm#datepicker
 
  99             .dpTable {font-family: Tahoma, Arial, Helvetica, sans-serif; font-size: 12px; text-align: center; color: #505050; background-color: #ece9d8; border: 1px solid #AAAAAA;}
 
 104             .dpTD {border: 1px solid #ece9d8;}
 
 105             .dpDayHighlightTD {background-color: #CCCCCC;border: 1px solid #AAAAAA;}
 
 106             .dpTDHover {background-color: #aca998;border: 1px solid #888888;cursor: pointer;color: red;}
 
 110             .dpDayTD {background-color: #CCCCCC;border: 1px solid #AAAAAA;color: white;}
 
 111             .dpTitleText {font-size: 12px;color: gray;font-weight: bold;}
 
 112             .dpDayHighlight {color: 4060ff;font-weight: bold;}
 
 113             .dpButton {font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif;font-size: 10px;color: gray;background: #d8e8ff;font-weight: bold;padding: 0px;}
 
 114             .dpTodayButton {font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif;font-size: 10px;color: gray;  background: #d8e8ff;font-weight: bold;}
 
 117             var datePickerDivID = \"datepicker\";
 
 118             var iFrameDivID = \"datepickeriframe\";
 
 120             var dayArrayShort = new Array('".join("','",$this->mWeekDayShortNames)."');
 
 121             var dayArrayMed = new Array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
 
 122             var dayArrayLong = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
 
 123             var monthArrayShort = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
 
 124             var monthArrayMed = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec');
 
 125             var monthArrayLong = new Array('".join("','",$this->mMonthNames)."');
 
 127             var defaultDateSeparator = \"".$this->mDateFormat[1]."\";
 
 128             var defaultDateFormat = \"".$this->mDateFormat."\";
 
 129             var dateSeparator = defaultDateSeparator;
 
 130             var dateFormat = defaultDateFormat;
 
 131             var startWeek = ".$this->mWeekStartDay.";
 
 136             function getStartWeekDayNumber(date) {
 
 137               var res = date.getDay() - startWeek;
 
 144             function displayDatePicker(dateFieldName, displayBelowThisObject, dtFormat, dtSep) {
 
 145               var targetDateField = document.getElementsByName(dateFieldName).item(0);
 
 147               if (!displayBelowThisObject) displayBelowThisObject = targetDateField;
 
 149                 dateSeparator = dtSep;
 
 151                 dateSeparator = defaultDateSeparator;
 
 154                 dateFormat = dtFormat;
 
 156                 dateFormat = defaultDateFormat;
 
 158               var x = displayBelowThisObject.offsetLeft;
 
 159               var y = displayBelowThisObject.offsetTop + displayBelowThisObject.offsetHeight ;
 
 161               var parent = displayBelowThisObject;
 
 162               while (parent.offsetParent) {
 
 163                 parent = parent.offsetParent;
 
 164                 x += parent.offsetLeft;
 
 165                 y += parent.offsetTop ;
 
 168               drawDatePicker(targetDateField, x, y);
 
 170             function drawDatePicker(targetDateField, x, y) {
 
 171               var dt = getFieldDate(targetDateField.value );
 
 173               if (!document.getElementById(datePickerDivID)) {
 
 174                 var newNode = document.createElement(\"div\");
 
 175                 newNode.setAttribute(\"id\", datePickerDivID);
 
 176                 newNode.setAttribute(\"class\", \"dpDiv\");
 
 177                 newNode.setAttribute(\"style\", \"visibility: hidden;\");
 
 178                 document.body.appendChild(newNode);
 
 181               var pickerDiv = document.getElementById(datePickerDivID);
 
 182               pickerDiv.style.position = \"absolute\";
 
 183               pickerDiv.style.left = x + \"px\";
 
 184               pickerDiv.style.top = (y + 3) + \"px\";
 
 185               pickerDiv.style.visibility = (pickerDiv.style.visibility == \"visible\" ? \"hidden\" : \"visible\");
 
 186               pickerDiv.style.display = (pickerDiv.style.display == \"block\" ? \"none\" : \"block\");
 
 187               pickerDiv.style.zIndex = 10000;
 
 189               refreshDatePicker(targetDateField.name, dt.getFullYear(), dt.getMonth(), dt.getDate());
 
 191             function refreshDatePicker(dateFieldName, year, month, day) {
 
 192               var thisDay = new Date();
 
 194               if ((month >= 0) && (year > 0)) {
 
 195                 thisDay = new Date(year, month, 1);
 
 197                 day = thisDay.getDate();
 
 201               var crlf = \"\\r\\n\";
 
 202               var TABLE = \"<table cols=7 class='dpTable'>\" + crlf;
 
 203               var xTABLE = \"</table>\" + crlf;
 
 204               var TR = \"<tr class='dpTR'>\";
 
 205               var TR_title = \"<tr class='dpTitleTR' width='150' align='center'>\";
 
 206               var TR_days = \"<tr class='dpDayTR'>\";
 
 207               var TR_todaybutton = \"<tr class='dpTodayButtonTR'>\";
 
 208               var xTR = \"</tr>\" + crlf;
 
 209               var TD = \"<td class='dpTD' onMouseOut='this.className=\\\"dpTD\\\";' onMouseOver=' this.className=\\\"dpTDHover\\\";' \";
 
 210               var TD_title = \"<td colspan=5 class='dpTitleTD'>\";
 
 211               var TD_buttons = \"<td class='dpButtonTD' width='50'>\";
 
 212               var TD_todaybutton = \"<td colspan=7 class='dpTodayButtonTD'>\";
 
 213               var TD_days = \"<td class='dpDayTD'>\";
 
 214               var TD_selected = \"<td class='dpDayHighlightTD' onMouseOut='this.className=\\\"dpDayHighlightTD\\\";' onMouseOver='this.className=\\\"dpTDHover\\\";' \";
 
 215               var xTD = \"</td>\" + crlf;
 
 216               var DIV_title = \"<div class='dpTitleText'>\";
 
 217               var DIV_selected = \"<div class='dpDayHighlight'>\";
 
 218               var xDIV = \"</div>\";
 
 222               html += TR_title + '<td colspan=7>';
 
 223               html += '<table width=\"250\">'+ TR_title;
 
 224               html += TD_buttons + getButtonCodeYear(dateFieldName, thisDay, -1, \"<<\") + getButtonCode(dateFieldName, thisDay, -1, \"<\") + xTD;
 
 225               html += TD_title + DIV_title + monthArrayLong[ thisDay.getMonth()] + \" \" + thisDay.getFullYear() + xDIV + xTD;
 
 226               html += TD_buttons + getButtonCode(dateFieldName, thisDay, 1, \">\") + getButtonCodeYear(dateFieldName, thisDay, 1, \">>\") + xTD;
 
 227               html += xTR + '</table>' + xTD;
 
 231               for(i = 0; i < dayArrayShort.length; i++)
 
 232                 html += TD_days + dayArrayShort[(i + startWeek) % 7] + xTD;
 
 237               //var startD = (thisDay.getDay()-startWeek<0?6:thisDay.getDay()-startWeek);
 
 238               var startD = getStartWeekDayNumber(thisDay);
 
 239               for (i = 0; i < startD; i++)
 
 240                 html += TD + \" \" + xTD;
 
 243                 dayNum = thisDay.getDate();
 
 244                 TD_onclick = \" onclick=\\\"updateDateField('\" + dateFieldName + \"', '\" + getDateString(thisDay) + \"');\\\">\";
 
 247                   html += TD_selected + TD_onclick + DIV_selected + dayNum + xDIV + xTD;
 
 249                   html += TD + TD_onclick + dayNum + xTD;
 
 251                 var startD = getStartWeekDayNumber(thisDay);
 
 256                 thisDay.setDate(thisDay.getDate() + 1);
 
 257               } while (thisDay.getDate() > 1)
 
 259               var startD = getStartWeekDayNumber(thisDay);
 
 261                 for (i = 6; i >= startD; i--) {
 
 262                   html += TD + \" \" + xTD;
 
 267               var today = new Date();
 
 268               var todayString = \"Today is \" + dayArrayMed[today.getDay()] + \", \" + monthArrayMed[ today.getMonth()] + \" \" + today.getDate();
 
 269               html += TR_todaybutton + TD_todaybutton;
 
 270               html += \"<button class='dpTodayButton' onClick=\\\"refreshDatePicker('\" + dateFieldName + \"'); updateDateFieldOnly('\" + dateFieldName + \"', '\" + getDateString(new Date()) + \"');\\\">".$this->lCalendarButtons['today']."</button> \";
 
 271               html += \"<button class='dpTodayButton' onClick='updateDateField(\\\"\" + dateFieldName + \"\\\");'>".$this->lCalendarButtons['close']."</button>\";
 
 276               document.getElementById(datePickerDivID).innerHTML = html;
 
 281             function getButtonCode(dateFieldName, dateVal, adjust, label) {
 
 282               var newMonth = (dateVal.getMonth () + adjust) % 12;
 
 283               var newYear = dateVal.getFullYear() + parseInt((dateVal.getMonth() + adjust) / 12);
 
 289               return \"<button class='dpButton' onClick='refreshDatePicker(\\\"\" + dateFieldName + \"\\\", \" + newYear + \", \" + newMonth + \");'>\" + label + \"</button>\";
 
 292             function getButtonCodeYear(dateFieldName, dateVal, adjust, label) {
 
 293               var newMonth = dateVal.getMonth();
 
 294               var newYear = dateVal.getFullYear() + adjust;
 
 296               return \"<button class='dpButton' onClick='refreshDatePicker(\\\"\" + dateFieldName + \"\\\", \" + newYear + \", \" + newMonth + \");'>\" + label + \"</button>\";
 
 300             function getDateString(dateVal) {\n";
 
 301             if (isset($GLOBALS['i18n'])) {
 
 302               $html .= "dateVal.locale = \"".$GLOBALS['i18n']->lang."\";\n";
 
 304             $html .=  "return dateVal.strftime(dateFormat);
 
 307             function getFieldDate(dateString) {
 
 309                 var dateVal = strptime(dateString, dateFormat);
 
 311                 dateVal = new Date();
 
 313               if (dateVal == null) {
 
 314                 dateVal = new Date();
 
 319             function splitDateString(dateString) {
 
 321               if (dateString.indexOf(\"/\") >= 0)
 
 322                 dArray = dateString.split(\"/\");
 
 323               else if (dateString.indexOf(\".\") >= 0)
 
 324                 dArray = dateString.split(\".\");
 
 325               else if (dateString.indexOf(\"-\") >= 0)
 
 326                 dArray = dateString.split(\"-\");
 
 327               else if (dateString.indexOf(\"\\\\\") >= 0)
 
 328                 dArray = dateString.split(\"\\\\\");
 
 335             function updateDateField(dateFieldName, dateString)  {
 
 336               var targetDateField = document.getElementsByName(dateFieldName).item(0);
 
 338                 targetDateField.value = dateString;
 
 340               var pickerDiv = document.getElementById(datePickerDivID);
 
 341               pickerDiv.style.visibility = \"hidden\";
 
 342               pickerDiv.style.display = \"none\";
 
 345               targetDateField.focus();
 
 347               if ((dateString) && (typeof(datePickerClosed) == \"function\"))
 
 348                 datePickerClosed(targetDateField);
 
 351             function updateDateFieldOnly(dateFieldName, dateString)  {
 
 352               var targetDateField = document.getElementsByName(dateFieldName).item(0);
 
 354                 targetDateField.value = dateString;
 
 357             function adjustiFrame(pickerDiv, iFrameDiv) {
 
 358               var is_opera = (navigator.userAgent.toLowerCase().indexOf(\"opera\") != -1);
 
 363                 if (!document.getElementById(iFrameDivID)) {
 
 364                   var newNode = document.createElement(\"iFrame\");
 
 365                   newNode.setAttribute(\"id\", iFrameDivID);
 
 366                   newNode.setAttribute(\"src\", \"javascript:false;\");
 
 367                   newNode.setAttribute(\"scrolling\", \"no\");
 
 368                   newNode.setAttribute (\"frameborder\", \"0\");
 
 369                   document.body.appendChild(newNode);
 
 373                   pickerDiv = document.getElementById(datePickerDivID);
 
 375                   iFrameDiv = document.getElementById(iFrameDivID);
 
 378                   iFrameDiv.style.position = \"absolute\";
 
 379                   iFrameDiv.style.width = pickerDiv.offsetWidth;
 
 380                   iFrameDiv.style.height = pickerDiv.offsetHeight ;
 
 381                   iFrameDiv.style.top = pickerDiv.style.top;
 
 382                   iFrameDiv.style.left = pickerDiv.style.left;
 
 383                   iFrameDiv.style.zIndex = pickerDiv.style.zIndex - 1;
 
 384                   iFrameDiv.style.visibility = pickerDiv.style.visibility ;
 
 385                   iFrameDiv.style.display = pickerDiv.style.display;
 
 392       $html .= "</script>\n";
 
 394       $html .= "\n\t<input type=\"text\"";
 
 395       $html .= " name=\"$this->name\" id=\"$this->id\"";
 
 398         $html .= " size=\"$this->size\"";
 
 400       if ($this->style!="")
 
 401          $html .= " style=\"$this->style\"";
 
 403         $html .= " maxlength=\"50\"";
 
 405       if ($this->on_change!="")
 
 406          $html .= " onchange=\"$this->on_change\"";
 
 408       if ($this->on_click!="")
 
 409          $html .= " onclick=\"$this->on_click\"";
 
 411       $html .= " value=\"".htmlspecialchars($this->getValue())."\"";
 
 415         $app_root = '/'.APP_NAME;
 
 417       $html .= " <img src=\"".$app_root."/images/calendar.gif\" width=\"16\" height=\"16\" onclick=\"displayDatePicker('".$this->name."');\">\n";