Passed
Push — master ( b3f1b0...eb8ea3 )
by Fabio
19:42
created

TDatePicker::getDropDownCssClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
1
<?php
2
/**
3
 * TDatePicker class file.
4
 *
5
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
6
 * @link https://github.com/pradosoft/prado
7
 * @license https://github.com/pradosoft/prado/blob/master/LICENSE
8
 */
9
10
namespace Prado\Web\UI\WebControls;
11
12
use Prado\Exceptions\TNotSupportedException;
13
use Prado\Prado;
14
use Prado\TPropertyValue;
15
use Prado\Web\Javascripts\TJavaScript;
16
use Prado\Web\UI\TControl;
17
use Prado\Util\TSimpleDateFormatter;
18
use Prado\I18N\core\CultureInfo;
19
20
/**
21
 *
22
 * TDatePicker class.
23
 *
24
 * TDatePicker displays a text box for date input purpose.
25
 * When the text box receives focus, a calendar will pop up and users can
26
 * pick up from it a date that will be automatically entered into the text box.
27
 * The format of the date string displayed in the text box is determined by
28
 * the <b>DateFormat</b> property. Valid formats are the combination of the
29
 * following tokens,
30
 *
31
 * ```
32
 *  Character Format Pattern (en-US)
33
 *  -----------------------------------------
34
 *  d          day digit
35
 *  dd         padded day digit e.g. 01, 02
36
 *  M          month digit
37
 *  MM         padded month digit
38
 *  MMMM       localized month name, e.g. March, April
39
 *  yy         2 digit year
40
 *  yyyy       4 digit year
41
 *  -----------------------------------------
42
 * ```
43
 *
44
 * TDatePicker has four <b>Mode</b> to show the date picker popup.
45
 *
46
 *  # <b>Basic</b> -- Only shows a text input, focusing on the input shows the
47
 *                    date picker. This way you can access the popup using only
48
 *                    the keyboard. Note that because of this, TAB-bing through
49
 *                    this control will automatically select the current date if
50
 *                    no previous date was selected. If you close the popup (eg.
51
 *                    pressing the ESC key) you'll need to un-focus and re-focus
52
 *                    the control again for the popup to reappear.
53
 *  # <b>Clickable</b> -- Only shows a text input, clicking on the input shows the
54
 *                    date picker. This mode solves the two small problems of the
55
 *                    Basic mode. It was first introduced in Prado 3.2.
56
 *  # <b>Button</b> -- Shows a button next to the text input, clicking on the
57
 *                     button shows the date, button text can be by the
58
 *                     <b>ButtonText</b> property
59
 *  # <b>ImageButton</b> -- Shows an image next to the text input, clicking on
60
 *                          the image shows the date picker, image source can be
61
 *                          change through the <b>ButtonImageUrl</b> property.
62
 *
63
 * The <b>CssClass</b> property can be used to override the css class name
64
 * for the date picker panel. <b>CalendarStyle</b> property sets the packages
65
 * styles available. E.g. <b>default</b>.
66
 *
67
 * The <b>InputMode</b> property can be set to "TextBox" or "DropDownList" with
68
 * default as "TextBox".
69
 * In <b>DropDownList</b> mode, in addition to the popup date picker, three
70
 * drop down list (day, month and year) are presented to select the date .
71
 *
72
 * The <b>PositionMode</b> property can be set to "Top" or "Bottom" with default
73
 * as "Bottom". It specifies the position of the calendar popup, relative to the
74
 * input field.
75
 *
76
 * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
77
 * @author Carl G. Mathisen <[email protected]>
78
 * @since 3.0
79
 */
80
class TDatePicker extends TTextBox
81
{
82
	/**
83
	 * Script path relative to the TClientScriptManager::SCRIPT_PATH
84
	 */
85
	public const SCRIPT_PATH = 'datepicker';
86
87
	/**
88
	 * @var TDatePickerClientScript validator client-script options.
89
	 */
90
	private $_clientScript;
91
	/**
92
	 * AutoPostBack is not supported.
93
	 * @param mixed $value
94
	 */
95
	public function setAutoPostBack($value)
96
	{
97
		throw new TNotSupportedException(
98
			'tdatepicker_autopostback_unsupported',
99
			$this::class
100
		);
101
	}
102
103
	/**
104
	 * @return string the format of the date string
105
	 */
106
	public function getDateFormat()
107
	{
108
		return $this->getViewState('DateFormat', 'dd-MM-yyyy');
109
	}
110
111
	/**
112
	 * Sets the format of the date string.
113
	 * @param string $value the format of the date string
114
	 */
115
	public function setDateFormat($value)
116
	{
117
		$this->setViewState('DateFormat', $value, 'dd-MM-yyyy');
118
	}
119
120
	/**
121
	 * @return bool whether the calendar window should pop up when the control receives focus
122
	 */
123
	public function getShowCalendar()
124
	{
125
		return $this->getViewState('ShowCalendar', true);
126
	}
127
128
	/**
129
	 * Sets whether to pop up the calendar window when the control receives focus
130
	 * @param bool $value whether to show the calendar window
131
	 */
132
	public function setShowCalendar($value)
133
	{
134
		$this->setViewState('ShowCalendar', TPropertyValue::ensureBoolean($value), true);
135
	}
136
137
	/**
138
	 * Gets the current culture.
139
	 * @return string current culture, e.g. en_AU.
140
	 */
141
	public function getCulture()
142
	{
143
		return $this->getViewState('Culture', '');
144
	}
145
146
	/**
147
	 * Sets the culture/language for the date picker.
148
	 * @param string $value a culture string, e.g. en_AU.
149
	 */
150
	public function setCulture($value)
151
	{
152
		$this->setViewState('Culture', $value, '');
153
	}
154
155
	/**
156
	 * @param TDatePickerInputMode $value input method of date values
157
	 */
158
	public function setInputMode($value)
159
	{
160
		$this->setViewState('InputMode', TPropertyValue::ensureEnum($value, TDatePickerInputMode::class), TDatePickerInputMode::TextBox);
161
	}
162
163
	/**
164
	 * @return TDatePickerInputMode input method of date values. Defaults to TDatePickerInputMode::TextBox.
165
	 */
166
	public function getInputMode()
167
	{
168
		return $this->getViewState('InputMode', TDatePickerInputMode::TextBox);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getViewSta...ckerInputMode::TextBox) also could return the type string which is incompatible with the documented return type Prado\Web\UI\WebControls\TDatePickerInputMode.
Loading history...
169
	}
170
171
	/**
172
	 * @param TDatePickerMode $value calendar UI mode
173
	 */
174
	public function setMode($value)
175
	{
176
		$this->setViewState('Mode', TPropertyValue::ensureEnum($value, TDatePickerMode::class), TDatePickerMode::Basic);
177
	}
178
179
	/**
180
	 * @return TDatePickerMode current calendar UI mode.
181
	 */
182
	public function getMode()
183
	{
184
		return $this->getViewState('Mode', TDatePickerMode::Basic);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getViewSta...TDatePickerMode::Basic) also could return the type string which is incompatible with the documented return type Prado\Web\UI\WebControls\TDatePickerMode.
Loading history...
185
	}
186
	/**
187
	 * @param string $value the image url for "Image" UI mode.
188
	 */
189
	public function setButtonImageUrl($value)
190
	{
191
		$this->setViewState('ImageUrl', $value, '');
192
	}
193
194
	/**
195
	 * @return string the image url for "Image" UI mode.
196
	 */
197
	public function getButtonImageUrl()
198
	{
199
		return $this->getViewState('ImageUrl', '');
200
	}
201
202
	/**
203
	 * @param string $value set the calendar style
204
	 */
205
	public function setCalendarStyle($value)
206
	{
207
		$this->setViewState('CalendarStyle', $value, 'default');
208
	}
209
210
	/**
211
	 * @return string current calendar style
212
	 */
213
	public function getCalendarStyle()
214
	{
215
		return $this->getViewState('CalendarStyle', 'default');
216
	}
217
218
	/**
219
	 * @param string $value Additional Css class name applied to dropdowns in DropDownList mode.
220
	 */
221
	public function setDropDownCssClass($value)
222
	{
223
		$this->setViewState('DropDownCssClass', $value);
224
	}
225
226
	/**
227
	 * @return string Additional Css class name applied to dropdowns in DropDownList mode.
228
	 */
229
	public function getDropDownCssClass()
230
	{
231
		return $this->getViewState('DropDownCssClass');
232
	}
233
234
	/**
235
	 * Set the first day of week, with 0 as Sunday, 1 as Monday, etc.
236
	 * @param int $value 0 for Sunday, 1 for Monday, 2 for Tuesday, etc.
237
	 */
238
	public function setFirstDayOfWeek($value)
239
	{
240
		$this->setViewState('FirstDayOfWeek', TPropertyValue::ensureInteger($value), 1);
241
	}
242
243
	/**
244
	 * @return int first day of the week
245
	 */
246
	public function getFirstDayOfWeek()
247
	{
248
		return $this->getViewState('FirstDayOfWeek', 1);
249
	}
250
251
	/**
252
	 * @return string text for the date picker button. Default is "...".
253
	 */
254
	public function getButtonText()
255
	{
256
		return $this->getViewState('ButtonText', '...');
257
	}
258
259
	/**
260
	 * @param string $value text for the date picker button
261
	 */
262
	public function setButtonText($value)
263
	{
264
		$this->setViewState('ButtonText', $value, '...');
265
	}
266
267
	/**
268
	 * @param int $value date picker starting year, default is 2000.
269
	 */
270
	public function setFromYear($value)
271
	{
272
		$this->setViewState('FromYear', TPropertyValue::ensureInteger($value), (int) (@date('Y')) - 5);
273
	}
274
275
	/**
276
	 * @return int date picker starting year, default is -5 years
277
	 */
278
	public function getFromYear()
279
	{
280
		return $this->getViewState('FromYear', (int) (@date('Y')) - 5);
281
	}
282
283
	/**
284
	 * @param int $value date picker ending year, default +10 years
285
	 */
286
	public function setUpToYear($value)
287
	{
288
		$this->setViewState('UpToYear', TPropertyValue::ensureInteger($value), (int) (@date('Y')) + 10);
289
	}
290
291
	/**
292
	 * @return int date picker ending year, default +10 years
293
	 */
294
	public function getUpToYear()
295
	{
296
		return $this->getViewState('UpToYear', (int) (@date('Y')) + 10);
297
	}
298
299
	/**
300
	 * @param TDatePickerPositionMode $value calendar UI position
301
	 */
302
	public function setPositionMode($value)
303
	{
304
		$this->setViewState('PositionMode', TPropertyValue::ensureEnum($value, TDatePickerPositionMode::class), TDatePickerPositionMode::Bottom);
305
	}
306
307
	/**
308
	 * @return TDatePickerPositionMode current calendar UI position.
309
	 */
310
	public function getPositionMode()
311
	{
312
		return $this->getViewState('PositionMode', TDatePickerPositionMode::Bottom);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getViewSta...erPositionMode::Bottom) also could return the type string which is incompatible with the documented return type Prado\Web\UI\WebControls\TDatePickerPositionMode.
Loading history...
313
	}
314
315
	/**
316
	 * @return int current selected date from the date picker as timestamp, NULL if timestamp is not set previously.
317
	 */
318
	public function getTimeStamp()
319
	{
320
		if (trim($this->getText()) === '') {
321
			return null;
322
		} else {
323
			return $this->getTimeStampFromText();
324
		}
325
	}
326
327
	/**
328
	 * Sets the date for the date picker using timestamp.
329
	 * @param float $value time stamp for the date picker
330
	 */
331
	public function setTimeStamp($value)
332
	{
333
		if ($value === null || (is_string($value) && trim($value) === '')) {
0 ignored issues
show
introduced by
The condition is_string($value) is always false.
Loading history...
334
			$this->setText('');
335
		} else {
336
			$date = TPropertyValue::ensureFloat($value);
337
			$formatter = new TSimpleDateFormatter($this->getDateFormat());
338
			$this->setText($formatter->format($date));
339
		}
340
	}
341
342
	/**
343
	 * Returns the timestamp selected by the user.
344
	 * This method is required by {@see \Prado\IDataRenderer}.
345
	 * It is the same as {@see getTimeStamp()}.
346
	 * @return int the timestamp of the TDatePicker control.
347
	 * @see getTimeStamp
348
	 * @since 3.1.2
349
	 */
350
	public function getData()
351
	{
352
		return $this->getTimeStamp();
353
	}
354
355
	/**
356
	 * Sets the timestamp represented by this control.
357
	 * This method is required by {@see \Prado\IDataRenderer}.
358
	 * It is the same as {@see setTimeStamp()}.
359
	 * @param int $value the timestamp of the TDatePicker control.
360
	 * @see setTimeStamp
361
	 * @since 3.1.2
362
	 */
363
	public function setData($value)
364
	{
365
		$this->setTimeStamp($value);
366
	}
367
368
	/**
369
	 * @return string the date string.
370
	 */
371
	public function getDate()
372
	{
373
		return $this->getText();
374
	}
375
376
	/**
377
	 * @param string $value date string
378
	 */
379
	public function setDate($value)
380
	{
381
		$this->setText($value);
382
	}
383
384
	/**
385
	 * Gets the TDatePickerClientScript to set the TDatePicker event handlers.
386
	 *
387
	 * The date picker on the client-side supports the following events.
388
	 * # <tt>OnDateChanged</tt> -- raised when the date is changed.
389
	 *
390
	 * You can attach custom javascript code to each of these events
391
	 *
392
	 * @return TDatePickerClientScript javascript validator event options.
393
	 */
394
	public function getClientSide()
395
	{
396
		if ($this->_clientScript === null) {
397
			$this->_clientScript = $this->createClientScript();
398
		}
399
		return $this->_clientScript;
400
	}
401
402
	/**
403
	 * @return TDatePickerClientScript javascript validator event options.
404
	 */
405
	protected function createClientScript()
406
	{
407
		return new TDatePickerClientScript();
408
	}
409
410
	/**
411
	 * Returns the value to be validated.
412
	 * This method is required by \Prado\Web\UI\IValidatable interface.
413
	 * @return int|string the integer timestamp if valid, otherwise the original text.
414
	 */
415
	public function getValidationPropertyValue()
416
	{
417
		if (($text = $this->getText()) === '') {
418
			return '';
419
		}
420
		$date = $this->getTimeStamp();
421
		return $date == null ? $text : $date;
422
	}
423
424
	/**
425
	 * Publish the date picker Css asset files.
426
	 * @param mixed $param
427
	 */
428
	public function onPreRender($param)
429
	{
430
		parent::onPreRender($param);
431
		$this->publishCalendarStyle();
432
		$this->registerCalendarClientScriptPre();
433
	}
434
435
	/**
436
	 * Renders body content.
437
	 * This method overrides parent implementation by adding
438
	 * additional date picker button if Mode is Button or ImageButton.
439
	 * @param \Prado\Web\UI\THtmlWriter $writer writer
440
	 */
441
	public function render($writer)
442
	{
443
		if ($this->getInputMode() == TDatePickerInputMode::TextBox) {
0 ignored issues
show
introduced by
The condition $this->getInputMode() ==...ickerInputMode::TextBox is always false.
Loading history...
444
			parent::render($writer);
445
			$this->renderDatePickerButtons($writer);
446
		} else {
447
			$this->renderDropDownListCalendar($writer);
448
			if ($this->hasDayPattern()) {
449
				$this->renderClientControlScript($writer);
450
				$this->renderDatePickerButtons($writer);
451
			}
452
		}
453
	}
454
455
	/**
456
	 * Renders the date picker popup buttons.
457
	 * @param mixed $writer
458
	 */
459
	protected function renderDatePickerButtons($writer)
460
	{
461
		if ($this->getShowCalendar()) {
462
			switch ($this->getMode()) {
463
				case TDatePickerMode::Button:
464
					$this->renderButtonDatePicker($writer);
465
					break;
466
				case TDatePickerMode::ImageButton:
467
					$this->renderImageButtonDatePicker($writer);
468
					break;
469
			}
470
		}
471
	}
472
473
	/**
474
	 * Loads user input data. Override parent implementation, when InputMode
475
	 * is DropDownList call getDateFromPostData to get date data.
476
	 * This method is primarly used by framework developers.
477
	 * @param string $key the key that can be used to retrieve data from the input data collection
478
	 * @param array $values the input data collection
479
	 * @return bool whether the data of the component has been changed
480
	 */
481
	public function loadPostData($key, $values)
482
	{
483
		if ($this->getInputMode() == TDatePickerInputMode::TextBox) {
0 ignored issues
show
introduced by
The condition $this->getInputMode() ==...ickerInputMode::TextBox is always false.
Loading history...
484
			return parent::loadPostData($key, $values);
485
		}
486
		$value = $this->getDateFromPostData($key, $values);
487
		if (!$this->getReadOnly() && $this->getText() !== $value) {
488
			$this->setText($value);
489
			return true;
490
		} else {
491
			return false;
492
		}
493
	}
494
495
	/**
496
	 * Loads date from drop down list data.
497
	 * @param string $key the key that can be used to retrieve data from the input data collection
498
	 * @param array $values the input data collection
499
	 * @return string the date selected
500
	 */
501
	protected function getDateFromPostData($key, $values)
502
	{
503
		$date = @getdate();
504
505
		$pattern = $this->getDateFormat();
506
		$pattern = str_replace(['MMMM', 'MMM'], ['MM', 'MM'], $pattern);
507
		$formatter = new TSimpleDateFormatter($pattern);
508
509
		$order = $formatter->getDayMonthYearOrdering();
510
511
		if (isset($values[$key . '$day'])) {
512
			$day = (int) ($values[$key . '$day']);
513
		} elseif (in_array('day', $order)) {
514
			$day = $date['mday'];
515
		} else {
516
			$day = 1;
517
		}
518
519
		if (isset($values[$key . '$month'])) {
520
			$month = (int) ($values[$key . '$month']) + 1;
521
		} else {
522
			$month = $date['mon'];
523
		}
524
525
		if (isset($values[$key . '$year'])) {
526
			$year = (int) ($values[$key . '$year']);
527
		} else {
528
			$year = $date['year'];
529
		}
530
531
		$s = new \DateTime();
532
		$s->setDate($year, $month, $day);
533
		$s->setTime(0, 0, 0);
534
		$date = $s->getTimeStamp();
535
		//$date = @mktime(0, 0, 0, $month, $day, $year);
536
537
		$pattern = $this->getDateFormat();
538
		$pattern = str_replace(['MMMM', 'MMM'], ['MM', 'MM'], $pattern);
539
540
		$formatter = new TSimpleDateFormatter($pattern);
541
		return $formatter->format($date);
542
	}
543
544
	/**
545
	 * Get javascript date picker options.
546
	 * @return array date picker client-side options
547
	 */
548
	protected function getDatePickerOptions()
549
	{
550
		$options['ID'] = $this->getClientID();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.
Loading history...
551
		$options['InputMode'] = $this->getInputMode();
552
		$options['Format'] = $this->getDateFormat();
553
		$options['FirstDayOfWeek'] = $this->getFirstDayOfWeek();
554
		if (($cssClass = $this->getCssClass()) !== '') {
555
			$options['ClassName'] = $cssClass;
556
		}
557
		$options['CalendarStyle'] = $this->getCalendarStyle();
558
		$options['FromYear'] = $this->getFromYear();
559
		$options['UpToYear'] = $this->getUpToYear();
560
		switch ($this->getMode()) {
561
			case TDatePickerMode::Basic:
562
				break;
563
			case TDatePickerMode::Clickable:
564
				$options['TriggerEvent'] = "click";
565
				break;
566
			default:
567
				$options['Trigger'] = $this->getDatePickerButtonID();
568
				break;
569
		}
570
		$options['PositionMode'] = $this->getPositionMode();
571
572
		$options = array_merge($options, $this->getCulturalOptions());
573
		if ($this->_clientScript !== null) {
574
			$options = array_merge(
575
				$options,
576
				$this->_clientScript->getOptions()->toArray()
577
			);
578
		}
579
		return $options;
580
	}
581
582
	/**
583
	 * Get javascript localization options, e.g. month and weekday names.
584
	 * @return array localization options.
585
	 */
586
	protected function getCulturalOptions()
587
	{
588
		if ($this->getCurrentCulture() == 'en') {
589
			return [];
590
		}
591
592
		$info = $this->getLocalizedCalendarInfo();
593
		$options['MonthNames'] = $info->findInfo('calendar/gregorian/monthNames/format/wide');
0 ignored issues
show
Comprehensibility Best Practice introduced by
$options was never initialized. Although not strictly required by PHP, it is generally a good practice to add $options = array(); before regardless.
Loading history...
594
		$options['AbbreviatedMonthNames'] = $info->findInfo('calendar/gregorian/monthNames/format/abbreviated');
595
		$options['ShortWeekDayNames'] = $info->findInfo('calendar/gregorian/dayNames/format/abbreviated');
596
597
		return $options;
598
	}
599
600
	/**
601
	 * @return string the current culture, falls back to application if culture is not set.
602
	 */
603
	protected function getCurrentCulture()
604
	{
605
		$app = $this->getApplication()->getGlobalization(false);
606
		return $this->getCulture() == '' ?
607
				($app ? $app->getCulture() : 'en') : $this->getCulture();
0 ignored issues
show
introduced by
$app is of type Prado\I18N\TGlobalization, thus it always evaluated to true.
Loading history...
608
	}
609
610
	/**
611
	 * @return \Prado\I18N\core\CultureInfo date time format information for the current culture.
612
	 */
613
	protected function getLocalizedCalendarInfo()
614
	{
615
		//expensive operations
616
		$culture = $this->getCurrentCulture();
617
		$info = new CultureInfo($culture);
618
		return $info;
619
	}
620
621
	/**
622
	 * Renders the drop down list date picker.
623
	 * @param mixed $writer
624
	 */
625
	protected function renderDropDownListCalendar($writer)
626
	{
627
		if ($this->getMode() == TDatePickerMode::Basic) {
0 ignored issues
show
introduced by
The condition $this->getMode() == Prad...\TDatePickerMode::Basic is always false.
Loading history...
628
			$this->setMode(TDatePickerMode::ImageButton);
629
		}
630
		parent::addAttributesToRender($writer);
631
		$writer->removeAttribute('name');
632
		$writer->removeAttribute('type');
633
		$writer->addAttribute('id', $this->getClientID());
634
635
		if (strlen($class = $this->getCssClass()) > 0) {
636
			$writer->addAttribute('class', $class);
637
		}
638
		$writer->renderBeginTag('span');
639
640
		$date = new \DateTime();
641
		$date->setTimeStamp($this->getTimeStampFromText());
642
		$this->renderCalendarSelections($writer, $date);
643
644
		//render a hidden input field
645
		$writer->addAttribute('name', $this->getUniqueID());
646
		$writer->addAttribute('type', 'hidden');
647
		$writer->addAttribute('value', $this->getText());
648
		$writer->renderBeginTag('input');
649
650
		$writer->renderEndTag();
651
		$writer->renderEndTag();
652
	}
653
654
	protected function hasDayPattern()
655
	{
656
		$formatter = new TSimpleDateFormatter($this->getDateFormat());
657
		return ($formatter->getDayPattern() !== null);
658
	}
659
660
	/**
661
	 * Renders the calendar drop down list depending on the DateFormat pattern.
662
	 * @param \Prado\Web\UI\THtmlWriter $writer the Html writer to render the drop down lists.
663
	 * @param \DateTime $date the current selected date
664
	 */
665
	protected function renderCalendarSelections($writer, $date)
666
	{
667
		$formatter = new TSimpleDateFormatter($this->getDateFormat());
668
669
		foreach ($formatter->getDayMonthYearOrdering() as $type) {
670
			if ($type == 'day') {
671
				$this->renderCalendarDayOptions($writer, $date->format('j'));
672
			} elseif ($type == 'month') {
673
				$this->renderCalendarMonthOptions($writer, $date->format('n'));
674
			} elseif ($type == 'year') {
675
				$this->renderCalendarYearOptions($writer, $date->format('Y'));
676
			}
677
		}
678
	}
679
680
	/**
681
	 * Gets the date from the text input using TSimpleDateFormatter
682
	 * @return int current selected date timestamp
683
	 */
684
	protected function getTimeStampFromText()
685
	{
686
		$pattern = $this->getDateFormat();
687
		$pattern = str_replace(['MMMM', 'MMM'], ['MM', 'MM'], $pattern);
688
		$formatter = new TSimpleDateFormatter($pattern);
689
		return $formatter->parse($this->getText());
690
	}
691
692
	/**
693
	 * Renders a drop down lists.
694
	 * @param \Prado\Web\UI\THtmlWriter $writer the writer used for the rendering purpose
695
	 * @param array $options list of selection options
696
	 * @param null|mixed $selected selected key.
697
	 */
698
	private function renderDropDownListOptions($writer, $options, $selected = null)
699
	{
700
		foreach ($options as $k => $v) {
701
			$writer->addAttribute('value', $k);
702
			if ($k == $selected) {
703
				$writer->addAttribute('selected', 'selected');
704
			}
705
			$writer->renderBeginTag('option');
706
			$writer->write($v);
707
			$writer->renderEndTag();
708
		}
709
	}
710
711
	/**
712
	 * Renders the day drop down list options.
713
	 * @param \Prado\Web\UI\THtmlWriter $writer the writer used for the rendering purpose
714
	 * @param null|mixed $selected selected day.
715
	 */
716
	protected function renderCalendarDayOptions($writer, $selected = null)
717
	{
718
		$days = $this->getDropDownDayOptions();
719
		$writer->addAttribute('id', $this->getClientID() . TControl::CLIENT_ID_SEPARATOR . 'day');
720
		$writer->addAttribute('name', $this->getUniqueID() . TControl::ID_SEPARATOR . 'day');
721
		$writer->addAttribute('class', 'datepicker_day_options ' . $this->getDropDownCssClass());
722
		if ($this->getReadOnly() || !$this->getEnabled(true)) {
723
			$writer->addAttribute('disabled', 'disabled');
724
		}
725
		$writer->renderBeginTag('select');
726
		$this->renderDropDownListOptions($writer, $days, $selected);
727
		$writer->renderEndTag();
728
	}
729
730
	/**
731
	 * @return array list of day options for a drop down list.
732
	 */
733
	protected function getDropDownDayOptions()
734
	{
735
		$formatter = new TSimpleDateFormatter($this->getDateFormat());
736
		$days = [];
737
		$requiresPadding = $formatter->getDayPattern() === 'dd';
738
		for ($i = 1; $i <= 31; $i++) {
739
			$days[$i] = $requiresPadding ? str_pad($i, 2, '0', STR_PAD_LEFT) : $i;
740
		}
741
		return $days;
742
	}
743
744
	/**
745
	 * Renders the month drop down list options.
746
	 * @param \Prado\Web\UI\THtmlWriter $writer the writer used for the rendering purpose
747
	 * @param null|mixed $selected selected month.
748
	 */
749
	protected function renderCalendarMonthOptions($writer, $selected = null)
750
	{
751
		$info = $this->getLocalizedCalendarInfo();
752
		$writer->addAttribute('id', $this->getClientID() . TControl::CLIENT_ID_SEPARATOR . 'month');
753
		$writer->addAttribute('name', $this->getUniqueID() . TControl::ID_SEPARATOR . 'month');
754
		$writer->addAttribute('class', 'datepicker_month_options ' . $this->getDropDownCssClass());
755
		if ($this->getReadOnly() || !$this->getEnabled(true)) {
756
			$writer->addAttribute('disabled', 'disabled');
757
		}
758
		$writer->renderBeginTag('select');
759
		$this->renderDropDownListOptions(
760
			$writer,
761
			$this->getLocalizedMonthNames($info),
762
			$selected - 1
763
		);
764
		$writer->renderEndTag();
765
	}
766
767
	/**
768
	 * Returns the localized month names that depends on the month format pattern.
769
	 * "MMMM" will return the month names, "MM" or "MMM" return abbr. month names
770
	 * and "M" return month digits.
771
	 * @param \Prado\I18N\core\CultureInfo $info localized date format information.
772
	 * @return array localized month names.
773
	 */
774
	protected function getLocalizedMonthNames($info)
775
	{
776
		$formatter = new TSimpleDateFormatter($this->getDateFormat());
777
		switch ($formatter->getMonthPattern()) {
778
			case 'MMM': return $info->findInfo('calendar/gregorian/monthNames/format/abbreviated');
779
			case 'MM':
780
				$array = [];
781
				for ($i = 1; $i <= 12; $i++) {
782
					$array[$i - 1] = $i < 10 ? '0' . $i : $i;
783
				}
784
				return $array;
785
			case 'M':
786
				$array = [];
787
				for ($i = 1; $i <= 12; $i++) {
788
					$array[$i - 1] = $i;
789
				}
790
				return $array;
791
			default:
792
				return $info->findInfo('calendar/gregorian/monthNames/format/wide');
793
		}
794
	}
795
796
	/**
797
	 * Renders the year drop down list options.
798
	 * @param \Prado\Web\UI\THtmlWriter $writer the writer used for the rendering purpose
799
	 * @param null|mixed $selected selected year.
800
	 */
801
	protected function renderCalendarYearOptions($writer, $selected = null)
802
	{
803
		$years = [];
804
		for ($i = $this->getFromYear(); $i <= $this->getUpToYear(); $i++) {
805
			$years[$i] = $i;
806
		}
807
		$writer->addAttribute('id', $this->getClientID() . TControl::CLIENT_ID_SEPARATOR . 'year');
808
		$writer->addAttribute('name', $this->getUniqueID() . TControl::ID_SEPARATOR . 'year');
809
		$writer->addAttribute('class', 'datepicker_year_options ' . $this->getDropDownCssClass());
810
		if ($this->getReadOnly() || !$this->getEnabled(true)) {
811
			$writer->addAttribute('disabled', 'disabled');
812
		}
813
		$writer->renderBeginTag('select');
814
		$this->renderDropDownListOptions($writer, $years, $selected);
815
		$writer->renderEndTag();
816
	}
817
818
	/**
819
	 * Gets the ID for the date picker trigger button.
820
	 * @return string unique button ID
821
	 */
822
	protected function getDatePickerButtonID()
823
	{
824
		return $this->getClientID() . 'button';
825
	}
826
827
	/**
828
	 * Adds an additional button such that when clicked it shows the date picker.
829
	 * @param \Prado\Web\UI\THtmlWriter $writer
830
	 */
831
	protected function renderButtonDatePicker($writer)
832
	{
833
		$writer->addAttribute('id', $this->getDatePickerButtonID());
834
		$writer->addAttribute('type', 'button');
835
		$writer->addAttribute('class', $this->getCssClass() . ' TDatePickerButton');
836
		$writer->addAttribute('value', $this->getButtonText());
837
		if (!$this->getEnabled(true)) {
838
			$writer->addAttribute('disabled', 'disabled');
839
		}
840
		$writer->renderBeginTag("input");
841
		$writer->renderEndTag();
842
	}
843
844
	/**
845
	 * Adds an additional image button such that when clicked it shows the date picker.
846
	 * @param \Prado\Web\UI\THtmlWriter $writer
847
	 */
848
	protected function renderImageButtonDatePicker($writer)
849
	{
850
		$url = $this->getButtonImageUrl();
851
		$url = empty($url) ? $this->getAssetUrl('calendar.png') : $url;
852
		$writer->addAttribute('id', $this->getDatePickerButtonID());
853
		$writer->addAttribute('src', $url);
854
		$writer->addAttribute('alt', ' ');
855
		$writer->addAttribute('class', $this->getCssClass() . ' TDatePickerImageButton');
856
		if (!$this->getEnabled(true)) {
857
			$writer->addAttribute('disabled', 'disabled');
858
		}
859
		$writer->addAttribute('type', 'image');
860
		$writer->addAttribute('onclick', 'return false;');
861
		$writer->renderBeginTag('input');
862
		$writer->renderEndTag();
863
	}
864
865
	/**
866
	 * @param string $file date picker asset file in the self::SCRIPT_PATH directory.
867
	 * @return string date picker asset url.
868
	 */
869
	protected function getAssetUrl($file = '')
870
	{
871
		$base = $this->getPage()->getClientScript()->getPradoScriptAssetUrl();
872
		return $base . '/' . self::SCRIPT_PATH . '/' . $file;
873
	}
874
875
	/**
876
	 * Publish the calendar style Css asset file.
877
	 * @return string Css file url.
878
	 */
879
	protected function publishCalendarStyle()
880
	{
881
		$url = $this->getAssetUrl($this->getCalendarStyle() . '.css');
882
		$cs = $this->getPage()->getClientScript();
883
		if (!$cs->isStyleSheetFileRegistered($url)) {
884
			$cs->registerStyleSheetFile($url, $url);
885
		}
886
		return $url;
887
	}
888
889
	/**
890
	 * Add the client id to the input textbox, and register the client scripts.
891
	 * @param \Prado\Web\UI\THtmlWriter $writer writer
892
	 */
893
	protected function addAttributesToRender($writer)
894
	{
895
		parent::addAttributesToRender($writer);
896
		$writer->addAttribute('id', $this->getClientID());
897
	}
898
899
	/**
900
	 * Registers the javascript code to initialize the date picker.
901
	 */
902
	protected function registerCalendarClientScriptPre()
903
	{
904
		if ($this->getShowCalendar()) {
905
			$cs = $this->getPage()->getClientScript();
906
			$cs->registerPradoScript("datepicker");
907
		}
908
	}
909
910
	protected function renderClientControlScript($writer)
911
	{
912
		if ($this->getShowCalendar()) {
913
			$cs = $this->getPage()->getClientScript();
914
			if (!$cs->isEndScriptRegistered('TDatePicker.spacer')) {
915
				$spacer = $this->getAssetUrl('spacer.gif');
916
				$code = "Prado.WebUI.TDatePicker.spacer = '$spacer';";
917
				$cs->registerEndScript('TDatePicker.spacer', $code);
918
			}
919
920
			$options = TJavaScript::encode($this->getDatePickerOptions());
921
			$code = "new Prado.WebUI.TDatePicker($options);";
922
			$cs->registerEndScript("prado:" . $this->getClientID(), $code);
923
		}
924
	}
925
}
926