Finished refactoring of the FormElement class.
[timetracker.git] / WEB-INF / lib / form / DateField.class.php
1 <?php
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.
10 // |
11 // | There are only two ways to violate the license:
12 // |
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).
16 // |
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).
20 // |
21 // | This license applies to this document only, not any other software
22 // | that it may be combined with.
23 // |
24 // +----------------------------------------------------------------------+
25 // | Contributors:
26 // | https://www.anuko.com/time_tracker/credits.htm
27 // +----------------------------------------------------------------------+
28
29 import('form.TextField');
30 import('DateAndTime');
31
32 class DateField extends TextField {
33   var $mWeekStartDay = 0;
34   var $mDateFormat  = "d/m/Y";
35   var $lToday      = "Today";
36
37   var $mDateObj;
38   var $class = 'DateField';
39
40   var $lCalendarButtons = array('today'=>'Today', 'close'=>'Close');
41
42   function __construct($name) {
43     $this->name  = $name;
44     $this->mDateObj  = new DateAndTime();
45
46     if (isset($GLOBALS["I18N"])) {
47       $this->localize($GLOBALS["I18N"]);
48     }
49   }
50
51   function localize($i18n)  {
52     global $user;
53         
54     $this->mDateObj->setFormat($user->date_format);
55
56     $this->mMonthNames = $i18n->monthNames;
57     $this->mWeekDayShortNames = $i18n->weekdayShortNames;
58     $this->lToday = $i18n->getKey('label.today');
59     $this->lCalendarButtons['today'] = $i18n->getKey('label.today');
60     $this->lCalendarButtons['close'] = $i18n->getKey('button.close');
61
62     $this->mDateFormat = $user->date_format;
63     $this->mWeekStartDay = $user->week_start;
64   }
65
66   // set current value taken from session or database
67   function setValueSafe($value)  {
68     if (isset($value) && (strlen($value) > 0)) {
69       $this->mDateObj->parseVal($value, DB_DATEFORMAT);
70       $this->value = $this->mDateObj->toString($this->mDateFormat); //?
71     }
72   }
73   // get value for storing in session or database
74   function getValueSafe() {
75     if (strlen($this->value)>0) {
76       $this->mDateObj->parseVal($this->value, $this->mDateFormat);  //?
77       return $this->mDateObj->toString(DB_DATEFORMAT);
78     } else {
79       return null;
80     }
81   }
82
83   function getHtml() {
84
85     if (!$this->isEnabled()) {
86       $html = htmlspecialchars($this->getValue()).
87         "<input type=\"hidden\" name=\"$this->name\" value=\"".htmlspecialchars($this->getValue())."\">\n";
88     } else {
89
90         if ($this->id=="") $this->id = $this->name;
91
92       $html = "";
93
94       // http://www.nsftools.com/tips/JavaScriptTips.htm#datepicker
95
96       $html .= "<style>
97             .dpDiv {}
98             .dpTable {font-family: Tahoma, Arial, Helvetica, sans-serif; font-size: 12px; text-align: center; color: #505050; background-color: #ece9d8; border: 1px solid #AAAAAA;}
99             .dpTR {}
100             .dpTitleTR {}
101             .dpDayTR {}
102             .dpTodayButtonTR {}
103             .dpTD {border: 1px solid #ece9d8;}
104             .dpDayHighlightTD {background-color: #CCCCCC;border: 1px solid #AAAAAA;}
105             .dpTDHover {background-color: #aca998;border: 1px solid #888888;cursor: pointer;color: red;}
106             .dpTitleTD {}
107             .dpButtonTD {}
108             .dpTodayButtonTD {}
109             .dpDayTD {background-color: #CCCCCC;border: 1px solid #AAAAAA;color: white;}
110             .dpTitleText {font-size: 12px;color: gray;font-weight: bold;}
111             .dpDayHighlight {color: 4060ff;font-weight: bold;}
112             .dpButton {font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif;font-size: 10px;color: gray;background: #d8e8ff;font-weight: bold;padding: 0px;}
113             .dpTodayButton {font-family: Verdana, Tahoma, Arial, Helvetica, sans-serif;font-size: 10px;color: gray;  background: #d8e8ff;font-weight: bold;}
114             </style>\n";
115       $html .= "<script>
116             var datePickerDivID = \"datepicker\";
117             var iFrameDivID = \"datepickeriframe\";
118
119             var dayArrayShort = new Array('".join("','",$this->mWeekDayShortNames)."');
120             var dayArrayMed = new Array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
121             var dayArrayLong = new Array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
122             var monthArrayShort = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
123             var monthArrayMed = new Array('Jan', 'Feb', 'Mar', 'Apr', 'May', 'June', 'July', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec');
124             var monthArrayLong = new Array('".join("','",$this->mMonthNames)."');
125
126             var defaultDateSeparator = \"".$this->mDateFormat[1]."\";
127             var defaultDateFormat = \"".$this->mDateFormat."\";
128             var dateSeparator = defaultDateSeparator;
129             var dateFormat = defaultDateFormat;
130             var startWeek = ".$this->mWeekStartDay.";
131
132           ";
133       $html .= "
134
135             function getStartWeekDayNumber(date) {
136               var res = date.getDay() - startWeek;
137               if (res < 0) {
138                 res += 7;
139               }
140               return res;
141             }
142
143             function displayDatePicker(dateFieldName, displayBelowThisObject, dtFormat, dtSep) {
144               var targetDateField = document.getElementsByName(dateFieldName).item(0);
145
146               if (!displayBelowThisObject) displayBelowThisObject = targetDateField;
147               if (dtSep)
148                 dateSeparator = dtSep;
149               else
150                 dateSeparator = defaultDateSeparator;
151
152               if (dtFormat)
153                 dateFormat = dtFormat;
154               else
155                 dateFormat = defaultDateFormat;
156
157               var x = displayBelowThisObject.offsetLeft;
158               var y = displayBelowThisObject.offsetTop + displayBelowThisObject.offsetHeight ;
159
160               var parent = displayBelowThisObject;
161               while (parent.offsetParent) {
162                 parent = parent.offsetParent;
163                 x += parent.offsetLeft;
164                 y += parent.offsetTop ;
165               }
166
167               drawDatePicker(targetDateField, x, y);
168             }
169             function drawDatePicker(targetDateField, x, y) {
170               var dt = getFieldDate(targetDateField.value );
171
172               if (!document.getElementById(datePickerDivID)) {
173                 var newNode = document.createElement(\"div\");
174                 newNode.setAttribute(\"id\", datePickerDivID);
175                 newNode.setAttribute(\"class\", \"dpDiv\");
176                 newNode.setAttribute(\"style\", \"visibility: hidden;\");
177                 document.body.appendChild(newNode);
178               }
179
180               var pickerDiv = document.getElementById(datePickerDivID);
181               pickerDiv.style.position = \"absolute\";
182               pickerDiv.style.left = x + \"px\";
183               pickerDiv.style.top = (y + 3) + \"px\";
184               pickerDiv.style.visibility = (pickerDiv.style.visibility == \"visible\" ? \"hidden\" : \"visible\");
185               pickerDiv.style.display = (pickerDiv.style.display == \"block\" ? \"none\" : \"block\");
186               pickerDiv.style.zIndex = 10000;
187
188               refreshDatePicker(targetDateField.name, dt.getFullYear(), dt.getMonth(), dt.getDate());
189             }
190             function refreshDatePicker(dateFieldName, year, month, day) {
191               var thisDay = new Date();
192
193               if ((month >= 0) && (year > 0)) {
194                 thisDay = new Date(year, month, 1);
195               } else {
196                 day = thisDay.getDate();
197                 thisDay.setDate(1);
198               }
199
200               var crlf = \"\\r\\n\";
201               var TABLE = \"<table cols=7 class='dpTable'>\" + crlf;
202               var xTABLE = \"</table>\" + crlf;
203               var TR = \"<tr class='dpTR'>\";
204               var TR_title = \"<tr class='dpTitleTR' width='150' align='center'>\";
205               var TR_days = \"<tr class='dpDayTR'>\";
206               var TR_todaybutton = \"<tr class='dpTodayButtonTR'>\";
207               var xTR = \"</tr>\" + crlf;
208               var TD = \"<td class='dpTD' onMouseOut='this.className=\\\"dpTD\\\";' onMouseOver=' this.className=\\\"dpTDHover\\\";' \";
209               var TD_title = \"<td colspan=5 class='dpTitleTD'>\";
210               var TD_buttons = \"<td class='dpButtonTD' width='50'>\";
211               var TD_todaybutton = \"<td colspan=7 class='dpTodayButtonTD'>\";
212               var TD_days = \"<td class='dpDayTD'>\";
213               var TD_selected = \"<td class='dpDayHighlightTD' onMouseOut='this.className=\\\"dpDayHighlightTD\\\";' onMouseOver='this.className=\\\"dpTDHover\\\";' \";
214               var xTD = \"</td>\" + crlf;
215               var DIV_title = \"<div class='dpTitleText'>\";
216               var DIV_selected = \"<div class='dpDayHighlight'>\";
217               var xDIV = \"</div>\";
218
219               var html = TABLE;
220
221               html += TR_title + '<td colspan=7>';
222               html += '<table width=\"250\">'+ TR_title;
223               html += TD_buttons + getButtonCodeYear(dateFieldName, thisDay, -1, \"&lt;&lt;\") + getButtonCode(dateFieldName, thisDay, -1, \"&lt;\") + xTD;
224               html += TD_title + DIV_title + monthArrayLong[ thisDay.getMonth()] + \" \" + thisDay.getFullYear() + xDIV + xTD;
225               html += TD_buttons + getButtonCode(dateFieldName, thisDay, 1, \"&gt;\") + getButtonCodeYear(dateFieldName, thisDay, 1, \"&gt;&gt;\") + xTD;
226               html += xTR + '</table>' + xTD;
227               html += xTR;
228
229               html += TR_days;
230               for(i = 0; i < dayArrayShort.length; i++)
231                 html += TD_days + dayArrayShort[(i + startWeek) % 7] + xTD;
232               html += xTR;
233
234               html += TR;
235
236               //var startD = (thisDay.getDay()-startWeek<0?6:thisDay.getDay()-startWeek);
237               var startD = getStartWeekDayNumber(thisDay);
238               for (i = 0; i < startD; i++)
239                 html += TD + \"&nbsp;\" + xTD;
240
241               do {
242                 dayNum = thisDay.getDate();
243                 TD_onclick = \" onclick=\\\"updateDateField('\" + dateFieldName + \"', '\" + getDateString(thisDay) + \"');\\\">\";
244
245                 if (dayNum == day)
246                   html += TD_selected + TD_onclick + DIV_selected + dayNum + xDIV + xTD;
247                 else
248                   html += TD + TD_onclick + dayNum + xTD;
249
250                 var startD = getStartWeekDayNumber(thisDay);
251
252                 if (startD == 6)
253                   html += xTR + TR;
254
255                 thisDay.setDate(thisDay.getDate() + 1);
256               } while (thisDay.getDate() > 1)
257
258               var startD = getStartWeekDayNumber(thisDay);
259               if (startD > 0) {
260                 for (i = 6; i >= startD; i--) {
261                   html += TD + \"&nbsp;\" + xTD;
262                 }
263               }              
264               html += xTR;
265
266               var today = new Date();
267               var todayString = \"Today is \" + dayArrayMed[today.getDay()] + \", \" + monthArrayMed[ today.getMonth()] + \" \" + today.getDate();
268               html += TR_todaybutton + TD_todaybutton;
269               html += \"<button class='dpTodayButton' onClick=\\\"refreshDatePicker('\" + dateFieldName + \"'); updateDateFieldOnly('\" + dateFieldName + \"', '\" + getDateString(new Date()) + \"');\\\">".$this->lCalendarButtons['today']."</button> \";
270               html += \"<button class='dpTodayButton' onClick='updateDateField(\\\"\" + dateFieldName + \"\\\");'>".$this->lCalendarButtons['close']."</button>\";
271               html += xTD + xTR;
272
273               html += xTABLE;
274
275               document.getElementById(datePickerDivID).innerHTML = html;
276               adjustiFrame();
277             }
278
279
280             function getButtonCode(dateFieldName, dateVal, adjust, label) {
281               var newMonth = (dateVal.getMonth () + adjust) % 12;
282               var newYear = dateVal.getFullYear() + parseInt((dateVal.getMonth() + adjust) / 12);
283               if (newMonth < 0) {
284                 newMonth += 12;
285                 newYear += -1;
286               }
287
288               return \"<button class='dpButton' onClick='refreshDatePicker(\\\"\" + dateFieldName + \"\\\", \" + newYear + \", \" + newMonth + \");'>\" + label + \"</button>\";
289             }
290
291             function getButtonCodeYear(dateFieldName, dateVal, adjust, label) {
292               var newMonth = dateVal.getMonth();
293               var newYear = dateVal.getFullYear() + adjust;
294
295               return \"<button class='dpButton' onClick='refreshDatePicker(\\\"\" + dateFieldName + \"\\\", \" + newYear + \", \" + newMonth + \");'>\" + label + \"</button>\";
296             }
297
298
299             function getDateString(dateVal) {\n";
300             if (isset($GLOBALS['i18n'])) {
301               $html .= "dateVal.locale = \"".$GLOBALS['i18n']->lang."\";\n";
302             }
303             $html .=  "return dateVal.strftime(dateFormat);
304             }
305
306             function getFieldDate(dateString) {
307               try {
308                 var dateVal = strptime(dateString, dateFormat);
309               } catch(e) {
310                 dateVal = new Date();
311               }
312               if (dateVal == null) {
313                 dateVal = new Date();
314               }
315               return dateVal;
316             }
317
318             function splitDateString(dateString) {
319               var dArray;
320               if (dateString.indexOf(\"/\") >= 0)
321                 dArray = dateString.split(\"/\");
322               else if (dateString.indexOf(\".\") >= 0)
323                 dArray = dateString.split(\".\");
324               else if (dateString.indexOf(\"-\") >= 0)
325                 dArray = dateString.split(\"-\");
326               else if (dateString.indexOf(\"\\\\\") >= 0)
327                 dArray = dateString.split(\"\\\\\");
328               else
329                 dArray = false;
330
331               return dArray;
332             }
333
334             function updateDateField(dateFieldName, dateString)  {
335               var targetDateField = document.getElementsByName(dateFieldName).item(0);
336               if (dateString)
337                 targetDateField.value = dateString;
338
339               var pickerDiv = document.getElementById(datePickerDivID);
340               pickerDiv.style.visibility = \"hidden\";
341               pickerDiv.style.display = \"none\";
342
343               adjustiFrame();
344               targetDateField.focus();
345
346               if ((dateString) && (typeof(datePickerClosed) == \"function\"))
347                 datePickerClosed(targetDateField);
348             }
349
350             function updateDateFieldOnly(dateFieldName, dateString)  {
351               var targetDateField = document.getElementsByName(dateFieldName).item(0);
352               if (dateString)
353                 targetDateField.value = dateString;
354             }
355
356             function adjustiFrame(pickerDiv, iFrameDiv) {
357               var is_opera = (navigator.userAgent.toLowerCase().indexOf(\"opera\") != -1);
358               if (is_opera)
359                 return;
360
361               try {
362                 if (!document.getElementById(iFrameDivID)) {
363                   var newNode = document.createElement(\"iFrame\");
364                   newNode.setAttribute(\"id\", iFrameDivID);
365                   newNode.setAttribute(\"src\", \"javascript:false;\");
366                   newNode.setAttribute(\"scrolling\", \"no\");
367                   newNode.setAttribute (\"frameborder\", \"0\");
368                   document.body.appendChild(newNode);
369                 }
370
371                 if (!pickerDiv)
372                   pickerDiv = document.getElementById(datePickerDivID);
373                 if (!iFrameDiv)
374                   iFrameDiv = document.getElementById(iFrameDivID);
375
376                 try {
377                   iFrameDiv.style.position = \"absolute\";
378                   iFrameDiv.style.width = pickerDiv.offsetWidth;
379                   iFrameDiv.style.height = pickerDiv.offsetHeight ;
380                   iFrameDiv.style.top = pickerDiv.style.top;
381                   iFrameDiv.style.left = pickerDiv.style.left;
382                   iFrameDiv.style.zIndex = pickerDiv.style.zIndex - 1;
383                   iFrameDiv.style.visibility = pickerDiv.style.visibility ;
384                   iFrameDiv.style.display = pickerDiv.style.display;
385                 } catch(e) {
386                 }
387
388               } catch (ee) {
389               }
390             }\n";
391       $html .= "</script>\n";
392
393       $html .= "\n\t<input type=\"text\"";
394       $html .= " name=\"$this->name\" id=\"$this->id\"";
395
396       if ($this->size!="")
397         $html .= " size=\"$this->size\"";
398
399       if ($this->style!="")
400          $html .= " style=\"$this->style\"";
401
402         $html .= " maxlength=\"50\"";
403
404       if ($this->on_change!="")
405          $html .= " onchange=\"$this->on_change\"";
406
407       if ($this->on_click!="")
408          $html .= " onclick=\"$this->on_click\"";
409
410       $html .= " value=\"".htmlspecialchars($this->getValue())."\"";
411       $html .= ">";
412       
413       if (APP_NAME)
414         $app_root = '/'.APP_NAME;
415
416       $html .= "&nbsp;<img src=\"".$app_root."/images/calendar.gif\" width=\"16\" height=\"16\" onclick=\"displayDatePicker('".$this->name."');\">\n";
417     }
418
419     return $html;
420   }
421 }