|
7 | 7 | for (const id of ids) elements.push(document.getElementById(id)); |
8 | 8 | for (const element of elements) { |
9 | 9 | if (element?.id.endsWith('_err_msg')) { |
10 | | - element?.classList.remove('d-none'); |
11 | | - } else { |
12 | | - element?.classList.add('is-invalid'); |
| 10 | + element.classList.remove('d-none'); |
| 11 | + } else if (element) { |
| 12 | + element.classList.add('is-invalid'); |
13 | 13 | if (!(element.id in event_listeners)) { |
14 | 14 | event_listeners[element.id] = hide_errors([], elements); |
15 | | - element?.addEventListener('change', event_listeners[element.id]); |
| 15 | + element.addEventListener('change', event_listeners[element.id]); |
16 | 16 | } |
17 | 17 | } |
18 | 18 | } |
|
23 | 23 | for (const id of ids) elements.push(document.getElementById(id)); |
24 | 24 | for (const element of elements) { |
25 | 25 | if (element?.id.endsWith('_err_msg')) { |
26 | | - element?.classList.add('d-none'); |
| 26 | + element.classList.add('d-none'); |
27 | 27 | if (element.id === 'select_set_err_msg' && 'set_table_id' in event_listeners) { |
28 | 28 | document |
29 | 29 | .getElementById('set_table_id') |
30 | 30 | ?.removeEventListener('change', event_listeners.set_table_id); |
31 | 31 | delete event_listeners.set_table_id; |
32 | 32 | } |
33 | | - } else { |
34 | | - element?.classList.remove('is-invalid'); |
| 33 | + } else if (element) { |
| 34 | + element.classList.remove('is-invalid'); |
35 | 35 | if (element.id in event_listeners) { |
36 | | - element?.removeEventListener('change', event_listeners[element.id]); |
| 36 | + element.removeEventListener('change', event_listeners[element.id]); |
37 | 37 | delete event_listeners[element.id]; |
38 | 38 | } |
39 | 39 | } |
|
174 | 174 | 'zh-HK': 'yyyy/L/d ah:mm' |
175 | 175 | }; |
176 | 176 |
|
177 | | - // Initialize the date/time picker for the import form. |
| 177 | + // Initialize the date/time picker for the import form and common date editor. |
| 178 | + const dateInputs = []; |
178 | 179 | const importDateShift = document.getElementById('import_date_shift'); |
179 | | - if (importDateShift) { |
180 | | - luxon.Settings.defaultLocale = importDateShift.dataset.locale ?? 'en'; |
| 180 | + if (importDateShift) dateInputs.push(importDateShift); |
| 181 | + const commonDateInput = document.getElementById('common-date'); |
| 182 | + if (commonDateInput) dateInputs.push(commonDateInput); |
| 183 | + |
| 184 | + for (const dateInput of dateInputs) { |
| 185 | + luxon.Settings.defaultLocale = dateInput.dataset.locale ?? 'en'; |
181 | 186 |
|
182 | 187 | // Compute the time difference between a time in the browser timezone and the same time in the course timezone. |
183 | 188 | // flatpickr gives the time in the browser's timezone, and this is used to adjust to the course timezone. |
|
189 | 194 | new Date(dateTime.toLocaleString('en-US')).getTime() - |
190 | 195 | new Date( |
191 | 196 | dateTime.toLocaleString('en-US', { |
192 | | - timeZone: importDateShift.dataset.timezone ?? 'America/New_York' |
| 197 | + timeZone: dateInput.dataset.timezone ?? 'America/New_York' |
193 | 198 | }) |
194 | 199 | ).getTime() |
195 | 200 | ); |
196 | 201 | }; |
197 | 202 |
|
198 | | - let fallbackDate = importDateShift.value |
199 | | - ? new Date(parseInt(importDateShift.value) * 1000 - timezoneAdjustment(parseInt(importDateShift.value))) |
| 203 | + let fallbackDate = dateInput.value |
| 204 | + ? new Date(parseInt(dateInput.value) * 1000 - timezoneAdjustment(parseInt(dateInput.value))) |
200 | 205 | : new Date(); |
201 | 206 |
|
202 | | - const fp = flatpickr(importDateShift.parentNode, { |
| 207 | + const fp = flatpickr(dateInput.parentNode, { |
203 | 208 | allowInput: true, |
204 | 209 | enableTime: true, |
205 | 210 | minuteIncrement: 1, |
|
216 | 221 | disableMobile: true, |
217 | 222 | wrap: true, |
218 | 223 | plugins: [ |
219 | | - new confirmDatePlugin({ confirmText: importDateShift.dataset.doneText, showAlways: true }), |
| 224 | + new confirmDatePlugin({ confirmText: dateInput.dataset.doneText, showAlways: true }), |
220 | 225 | new ShortcutButtonsPlugin({ |
221 | 226 | button: [ |
222 | 227 | { |
223 | | - label: importDateShift.dataset.todayText ?? 'Today', |
| 228 | + label: dateInput.dataset.todayText ?? 'Today', |
224 | 229 | attributes: { class: 'btn btn-sm btn-secondary ms-auto me-1 mb-1' } |
225 | 230 | }, |
226 | 231 | { |
227 | | - label: importDateShift.dataset.nowText ?? 'Now', |
| 232 | + label: dateInput.dataset.nowText ?? 'Now', |
228 | 233 | attributes: { class: 'btn btn-sm btn-secondary mx-auto mb-1' } |
229 | 234 | } |
230 | 235 | ], |
|
251 | 256 |
|
252 | 257 | // Make the alternate input left-to-right even for right-to-left languages. |
253 | 258 | this.altInput.dir = 'ltr'; |
| 259 | + |
| 260 | + // Move the id of the now hidden input onto the added input so the labels still work. |
| 261 | + this.altInput.id = this.input.id; |
| 262 | + this.input.removeAttribute('id'); |
254 | 263 | }, |
255 | 264 | parseDate(datestr, format) { |
256 | 265 | // Deal with the case of a unix timestamp. The timezone needs to be adjusted back as this is for |
|
278 | 287 | } |
279 | 288 | }); |
280 | 289 |
|
281 | | - importDateShift.nextElementSibling.addEventListener('keydown', (e) => { |
| 290 | + dateInput.nextElementSibling.addEventListener('keydown', (e) => { |
282 | 291 | if (e.key === ' ' || e.key === 'Enter') { |
283 | 292 | e.preventDefault(); |
284 | 293 | fp.open(); |
285 | 294 | } |
286 | 295 | }); |
287 | 296 | } |
| 297 | + |
| 298 | + if (commonDateInput) { |
| 299 | + document.getElementById('apply-common-date')?.addEventListener('click', () => { |
| 300 | + const dateTypeInput = document.getElementById('set-date-choice'); |
| 301 | + if (!dateTypeInput?.value) { |
| 302 | + show_errors(['choose_date_type_err_msg'], [dateTypeInput]); |
| 303 | + return; |
| 304 | + } |
| 305 | + |
| 306 | + if (!commonDateInput.value) { |
| 307 | + show_errors( |
| 308 | + ['choose_common_date_err_msg'], |
| 309 | + [commonDateInput.parentNode?._flatpickr?.input, commonDateInput.parentNode?._flatpickr?.altInput] |
| 310 | + ); |
| 311 | + return; |
| 312 | + } |
| 313 | + |
| 314 | + const selectedSets = Array.from(document.getElementsByName('apply_date_sets')).filter((c) => c.checked); |
| 315 | + if (!selectedSets.length) { |
| 316 | + show_errors(['select_set_err_msg'], []); |
| 317 | + event_listeners.set_table_id = hide_errors( |
| 318 | + ['set_table_id'], |
| 319 | + [document.getElementById('select_set_err_msg')] |
| 320 | + ); |
| 321 | + document.getElementById('set_table_id')?.addEventListener('change', event_listeners.set_table_id); |
| 322 | + } |
| 323 | + |
| 324 | + for (const set of selectedSets) { |
| 325 | + const inputPicker = document.getElementsByName(`set.${set.value}.${dateTypeInput.value}`)[0]?.parentNode |
| 326 | + ?._flatpickr; |
| 327 | + inputPicker?.setDate(commonDateInput.value, true); |
| 328 | + inputPicker?.close(); // The picker isn't actually open, but this triggers the onClose handler. |
| 329 | + } |
| 330 | + }); |
| 331 | + } |
288 | 332 | })(); |
0 commit comments