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