Passed
Pull Request — master (#6612)
by
unknown
09:26
created

DatePicker::getElementJS()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 107
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 10
nc 2
nop 0
dl 0
loc 107
rs 9.9332
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Enums\ActionIcon;
6
use Chamilo\CoreBundle\Enums\ObjectIcon;
7
use Chamilo\CoreBundle\Framework\Container;
8
9
/**
10
 * Form element to select a date.
11
 */
12
class DatePicker extends HTML_QuickForm_text
13
{
14
    /**
15
     * @param string       $elementName
16
     * @param string|array $elementLabel
17
     * @param array        $attributes
18
     */
19
    public function __construct($elementName, $elementLabel = null, $attributes = null)
20
    {
21
        if (!isset($attributes['id'])) {
22
            $attributes['id'] = $elementName;
23
        }
24
        $attributes['class'] = 'p-component p-inputtext p-filled';
25
26
        parent::__construct($elementName, $elementLabel, $attributes);
27
        $this->_appendName = true;
28
    }
29
30
    /**
31
     * HTML code to display this datepicker.
32
     *
33
     * @return string
34
     */
35
    public function toHtml(): string
36
    {
37
        if ($this->_flagFrozen) {
38
            return $this->getFrozenHtml();
39
        }
40
41
        $id = $this->getAttribute('id');
42
        $value = $this->getValue();
43
44
        if (!empty($value)) {
45
            $value = api_format_date($value, DATE_FORMAT_LONG_NO_DAY);
46
        }
47
48
        $label = $this->getLabel();
49
        $settingRequiredFields = api_get_setting('registration.required_extra_fields_in_inscription', true);
50
        $requiredFields = 'false' !== $settingRequiredFields ? $settingRequiredFields : [];
51
52
        if (!empty($requiredFields) && $requiredFields['options']) {
53
            $requiredFields = $requiredFields['options'];
54
        }
55
        $variable = str_replace('extra_', '',$id);
56
        $requiredSymbol = '';
57
        if (!empty($requiredFields) && in_array($variable, $requiredFields)) {
58
            $requiredSymbol = '<span class="form_required">*</span>';
59
        }
60
61
        $this->setAttribute('placeholder', get_lang('Select date'));
62
63
        return '
64
        <label>'.$requiredSymbol.$label.'</label>
65
        <div id="'.$id.'_container" class="flex items-center mt-1 flatpickr-wrapper" data-wrap="true">
66
            <input '.$this->_getAttrString($this->_attributes).' value="'.$value.'" data-input>
67
            <div class="flex space-x-1 ml-2" id="button-addon3">
68
                <button class="btn btn--secondary-outline mr-2" type="button" data-toggle>
69
                  '.Display::getMdiIcon(ObjectIcon::AGENDA).'
70
                </button>
71
                <button class="btn btn--secondary-outline" type="button" data-clear>
72
                  '.Display::getMdiIcon(ActionIcon::CLOSE).'
73
                </button>
74
            </div>
75
        </div>
76
    '.$this->getElementJS();
77
    }
78
79
    /**
80
     * @param string $value
81
     */
82
    public function setValue($value)
83
    {
84
        if (empty($value)) {
85
            return;
86
        }
87
88
        $value = substr($value, 0, 16);
89
        $this->updateAttributes(
90
            [
91
                'value' => $value,
92
            ]
93
        );
94
    }
95
96
    /**
97
     * Get the necessary javascript for this datepicker.
98
     */
99
    private function getElementJS(): string
100
    {
101
        $localeCode = $this->getLocaleCode();
102
        $id = $this->getAttribute('id');
103
104
        $altFormat = ($localeCode === 'en') ? 'F d, Y' : 'd F, Y';
105
106
        return "<script>
107
            window.addEventListener('load', function () {
108
              var container = document.getElementById('{$id}_container');
109
              if (!container) return;
110
111
              function cap(s){ return s ? s.charAt(0).toUpperCase() + s.slice(1) : s; }
112
113
              // Build a flatpickr locale object using Intl (no external l10n files)
114
              function buildFlatpickrLocale(loc) {
115
                try {
116
                  var fmtWeekLong  = new Intl.DateTimeFormat(loc, { weekday: 'long' });
117
                  var fmtWeekShort = new Intl.DateTimeFormat(loc, { weekday: 'short' });
118
                  var fmtMonthLong = new Intl.DateTimeFormat(loc, { month: 'long' });
119
                  var fmtMonthShort= new Intl.DateTimeFormat(loc, { month: 'short' });
120
121
                  // Weekdays 0..6 starting on Sunday (as flatpickr expects)
122
                  var sun = new Date(Date.UTC(2020, 0, 5));
123
                  var weekdaysLong = [], weekdaysShort = [];
124
                  for (var i=0;i<7;i++){
125
                    var d = new Date(sun); d.setUTCDate(sun.getUTCDate()+i);
126
                    weekdaysLong.push(cap(fmtWeekLong.format(d)));
127
                    weekdaysShort.push(cap(fmtWeekShort.format(d)));
128
                  }
129
130
                  // Months 0..11
131
                  var monthsLong = [], monthsShort = [];
132
                  for (var m=0;m<12;m++){
133
                    var dm = new Date(Date.UTC(2020, m, 1));
134
                    monthsLong.push(cap(fmtMonthLong.format(dm)));
135
                    monthsShort.push(cap(fmtMonthShort.format(dm)));
136
                  }
137
138
                  // First day of week (fallback to Monday)
139
                  var firstDay = 1;
140
                  try {
141
                    if (window.Intl && Intl.Locale) {
142
                      var inf = new Intl.Locale(loc);
143
                      if (inf.weekInfo && inf.weekInfo.firstDay) {
144
                        firstDay = (inf.weekInfo.firstDay === 7) ? 0 : inf.weekInfo.firstDay; // 0=Sun
145
                      }
146
                    }
147
                  } catch(e){}
148
149
                  return {
150
                    weekdays: { shorthand: weekdaysShort, longhand: weekdaysLong },
151
                    months:   { shorthand: monthsShort,  longhand: monthsLong  },
152
                    firstDayOfWeek: firstDay,
153
                    weekAbbreviation: 'Wk',
154
                    rangeSeparator: ' \u2013 ',
155
                    time_24hr: true
156
                  };
157
                } catch(e) {
158
                  return 'en';
159
                }
160
              }
161
162
              function initialize() {
163
                try {
164
                  if (!window.flatpickr) return;
165
166
                  // If already initialized, destroy before re-init (in case something set EN earlier)
167
                  var input = container.querySelector('[data-input]');
168
                  if (input && input._flatpickr) { input._flatpickr.destroy(); }
169
170
                  var loc = buildFlatpickrLocale('{$localeCode}');
171
172
                  // Set as global default when possible
173
                  if (typeof flatpickr.localize === 'function' && typeof loc === 'object') {
174
                    flatpickr.localize(loc);
175
                  }
176
                  if (flatpickr.l10ns && typeof loc === 'object') {
177
                    flatpickr.l10ns['{$localeCode}'] = loc;
178
                  }
179
180
                  var instance = flatpickr('#{$id}_container', {
181
                    locale: loc,
182
                    altInput: true,
183
                    altFormat: '{$altFormat}',
184
                    enableTime: false,
185
                    dateFormat: 'Y-m-d',
186
                    time_24hr: true,
187
                    wrap: true
188
                  });
189
190
                  try {
191
                    if (instance && instance.l10n && typeof loc === 'object') {
192
                      Object.assign(instance.l10n, loc);
193
                      instance.redraw();
194
                    }
195
                  } catch(e){}
196
                } catch(e) {
197
                  console.error('[DatePicker] flatpickr init error', e);
198
                }
199
              }
200
201
              initialize();
202
203
              // Hide original label if present (kept from your original code)
204
              try {
205
                var lbl = document.querySelector('label[for=\"{$id}\"]');
206
                if (lbl) { lbl.style.display = 'none'; lbl.classList.add('datepicker-label'); }
207
              } catch(e){}
208
            });
209
            </script>";
210
    }
211
212
    /**
213
     * Returns a normalized 2-letter locale to be used by JS/Intl.
214
     * Priority: course > user > platform.
215
     */
216
    private function getLocaleCode(): string
217
    {
218
        // 1) platform default
219
        $raw = (string) api_get_language_isocode();
220
221
        // 2) user (if not anonymous)
222
        $user = api_get_user_info();
223
        if (is_array($user) && !empty($user['language']) && ANONYMOUS != $user['status']) {
224
            $raw = (string) $user['language'];
225
        }
226
227
        // 3) course (highest priority)
228
        $course = api_get_course_info();
229
        if (!empty($course) && !empty($course['language'])) {
230
            $raw = (string) $course['language'];
231
        }
232
233
        return $this->normalizeIsoKey($raw);
234
    }
235
236
    /**
237
     * Normalizes any ISO/custom-ISO to a base language code that Intl can handle safely.
238
     * Rules:
239
     *  - 'xx'              -> 'xx'
240
     *  - 'xx_YY'/'xx-YY'   -> 'xx'   (pt_PT -> pt, nn_NO -> nn, zh_TW -> zh)
241
     *  - 'xx_suffix*'      -> 'xx'   (de_german2 -> de, es_spanish -> es, fr_french2 -> fr)
242
     *  - 'longtag_ES'      -> 'es'   (ast_ES, eu_ES -> es)
243
     *  - otherwise         -> 'en'
244
     */
245
    private function normalizeIsoKey(string $raw): string
246
    {
247
        $s = strtolower(trim($raw));
248
        if ($s === '') {
249
            return 'en';
250
        }
251
252
        // unify separator
253
        $s = str_replace('-', '_', $s);
254
255
        // direct 2-letter language
256
        if (preg_match('/^[a-z]{2}$/', $s)) {
257
            return $s;
258
        }
259
260
        // 'xx_YY' or 'xx_anything' -> keep base 'xx'
261
        if (preg_match('/^([a-z]{2})_[a-z0-9]+$/', $s, $m)) {
262
            return $m[1];
263
        }
264
265
        // 'xx_suffix' with digits (custom like es_spanish, de_german2, ...)
266
        if (preg_match('/^([a-z]{2})_[a-z]+[0-9]*$/', $s, $m)) {
267
            return $m[1];
268
        }
269
270
        // long language tag followed by region, prefer 'es' if region is ES
271
        if (preg_match('/^[a-z]{3,}_(..)$/', $s, $m)) {
272
            return ($m[1] === 'es') ? 'es' : 'en';
273
        }
274
275
        // fallback: extract first 2 letters if available
276
        if (preg_match('/^([a-z]{2})/', $s, $m)) {
277
            return $m[1];
278
        }
279
280
        return 'en';
281
    }
282
}
283