1/** 2 cp.datepicker.js provides an JavaScript Date picker control. 3 */ 4var cp = cp || {}; 5 6cp.datetimepicker = (function ($) { 7 var keyCodes = { 8 tab: 9, 9 downArrow: 40 10 }; 11 12 // These are the publicly-accessible methods 13 if (!$) { return; } 14 return { 15 createTimePicker: createTimePicker, 16 createDatePicker: createDatePicker, 17 }; 18 19 20 // getOptionsFromHtmlAttributes parses through any attribute in the user-specific HTML date or time picker and 21 // overwrites the values in "options" with values corresponding to "data-cp-datetimepicker-{optionName}". The 22 // "optionName" portion of the attribute should correspond to a property of the pickadate or pickatime 23 // options element. 24 function getOptionsFromHtmlAttributes($inputElement, originalOptions) { 25 $($inputElement[0].attributes).each(function () { 26 if (this.name.match('^data-cp-datetimepicker-')) { 27 var optionName = this.name.replace('data-cp-datetimepicker-', ''); 28 var originalValue = originalOptions[optionName]; 29 if (!originalValue) { 30 // We only want to set the option value if it was not already set in the JavaScript options. 31 // That is, the JavaScript options should always win over the HTML5 options. 32 33 var optionVal = this.value; 34 if (parseInt(optionVal)) { 35 optionVal = parseInt(optionVal); 36 } 37 38 originalOptions[optionName] = optionVal; 39 } 40 } 41 }); 42 return originalOptions; 43 } 44 45 // createTimePicker creates a new time picker object out of the DOM object specified by timeTextboxSelector. 46 // The timeTextboxSelector object must be an HTML INPUT element. 47 function createTimePicker(timeTextboxSelector, options) { 48 49 // The actualy Input Time component will be a hidden field. The DOM object in the source HTML 50 // will be the editable text field for the user. 51 52 options = options || {}; 53 54 var $inputTextBox = $(timeTextboxSelector); 55 56 if (!($inputTextBox) || $inputTextBox.length === 0) { 57 // We got passed a bad selector 58 console.log('ERR: cp.datetimepicker :: Could not find element: ' + timeTextboxSelector); 59 return; 60 } 61 62 // Set any options that were specified directly from within the HTML tag. 63 options = getOptionsFromHtmlAttributes($inputTextBox, options); 64 65 //Wrap fieldSetSelector inside div if wrapInsideDiv is true 66 var fieldSetSelector = '<fieldset class="fieldset__datetimepicker__wrapper">'; 67 68 if (options.wrapInsideDiv) { 69 fieldSetSelector = '<div class="t-widget t-timepicker">' + fieldSetSelector + '</div>'; 70 } 71 72 $fieldset = $(fieldSetSelector); 73 $inputTextBox.wrap($fieldset); 74 75 var $hiddenTimeInput = $('<input type="text" class="cp_input_datetime" tabindex="-1"/>'); 76 $hiddenTimeInput.attr('data-cp-time-textbox-selector', timeTextboxSelector); 77 78 $hiddenTimeInput.attr('aria-hidden', true); 79 80 $hiddenTimeInput.insertAfter($inputTextBox); 81 82 // We need to force "editable" in the options, otherwise things start to break. 83 options.editable = true; 84 $hiddenTimeInput.pickatime(options); 85 86 var pickerObject = $hiddenTimeInput.pickatime('picker'); // reference to the pickatime object 87 $inputTextBox.attr("role", "combobox"); 88 $inputTextBox.attr("aria-haspopup", "listbox"); 89 $inputTextBox.attr("aria-expanded", "false"); 90 $inputTextBox.attr("aria-controls", pickerObject.$root.attr("id")); 91 92 // Bugfix - https://civicplus.tpondemand.com/entity/182182-211-agenda-center-module-main-page 93 // Move aria-owns from the hidden <input> to the interactive one 94 $inputTextBox.attr("aria-owns", pickerObject.$node.attr("aria-owns")); 95 pickerObject.$node.removeAttr("aria-owns"); 96 97 $inputTextBox.on({ 98 change: function () { 99 var inputTime = this.value; 100 pickerObject.set('customSelect', insertColonIfOverFourChars(inputTime), options); 101 }, 102 keydown: function (e) { 103 if (e.keyCode === keyCodes.downArrow) { 104 e.preventDefault(); 105 pickerObject.open(true); 106 } 107 }, 108 focus: function (e) { 109 if (e.relatedTarget !== pickerObject.$holder[0]) { 110 // If the previously focused control was the picker object, don't open it again after closing it 111 pickerObject.open(false); 112 } 113 // We have to set the tab-index of the "clear" button, so tabbing through fields works properly. 114 $('.picker__button--clear').attr('tabindex', -1); 115 }, 116 blur: function (e) {
117 // If user shifts focus from the input element to its datepicker, then we do not want to close the datepicker. 118 if (!e.relatedTarget?.closest(".picker")) { 119 pickerObject.close(true); 120 } 121 } 122 }); 123 124 pickerObject.on({ 125 open: function () { 126 $inputTextBox.attr("aria-expanded", "true"); 127 }, 128 close: function () { 129 $inputTextBox.attr("aria-expanded", "false"); 130 $inputTextBox.focus(); 131 }, 132 set: function (e) { 133 var inputVal = $inputTextBox.val(); 134 var selectedVal = autoTimeFormat(inputVal); 135 if (typeof e.clear === "object" || (typeof e.select !== "undefined" && (!isNaN(e.select) || !isNaN(e.select.pick))) || (e.customSelect && !selectedVal.match(/^([1-9]|1[012])(:[0-5]\d) [APap][mM]$/))) { 136 selectedVal = this.get('value') === '' ? '12:00 AM' : this.get('value'); 137 } 138 $inputTextBox.val(selectedVal); 139 $inputTextBox.attr('value', selectedVal); 140 // closing the selection area if the user had picked a time. 141 if (!e.relatedTarget?.closest(".picker")) { 142 pickerObject.close(true); 143 $inputTextBox.attr("aria-expanded", "false"); 144 } 145 } 146 }); 147 148 // Priming initialization if there was a value specified for the input 149 if ($inputTextBox.val() !== '') { 150 pickerObject.set('select', $inputTextBox.val(), options); 151 } 152 153 $(pickerObject.$root).find('.picker__list').attr('aria-label', 'Time List'); 154 155 return pickerObject; 156 } 157 158 // createDatePicker creates a new date picker object using the element specified by "dateTextboxSelector". 159 // The dateTextboxSelector object must be an HTML INPUT element. 160 function createDatePicker(dateTextboxSelector, options) { 161 // The actualy Input Date component will be a hidden field. The DOM object in the source HTML 162 // will be the editable text field for the user. 163 options = options || {}; 164 165 var $inputTextBox = $(dateTextboxSelector); 166 167 if (!($inputTextBox) || $inputTextBox.length === 0) { 168 // We got passed a bad selector 169 console.log('ERR: cp.datetimepicker :: Could not find element: ' + dateTextboxSelector); 170 return; 171 } 172 173 // Set any options that were specified directly from within the HTML tag. 174 options = getOptionsFromHtmlAttributes($inputTextBox, options); 175 var dateAsString = $inputTextBox.val(); 176 if (options.format == 'dd/mm/yyyy' && dateAsString.length > 0) { 177 var splitDate = autoCompleteDate(dateAsString, options.format).split('/'); 178 $inputTextBox.val(splitDate[1] + '/' + splitDate[0] + '/' + splitDate[2]); 179 } 180 181 if (options.useNativeControls) { 182 $inputTextBox.prop('type', 'date'); 183 return; 184 } 185 186 var $hiddenDateInput = $('<input type="text" class="cp_input_datetime" tabindex="-1"/>'); 187 $hiddenDateInput.attr('data-cp-date-textbox-selector', dateTextboxSelector); 188 189 $hiddenDateInput.attr('aria-hidden', true); 190 var fieldsetSelector = '<fieldset class="fieldset__datetimepicker__wrapper"></fieldset>'; 191 if (options.wrapInsideDiv) 192 fieldsetSelector = '<div class="t-widget t-datepicker">' + fieldsetSelector + '</div>'; 193 194 $fieldset = $(fieldsetSelector); 195 $fieldset.attr("data-cp-datetimepicker-selector", dateTextboxSelector); 196 $inputTextBox.wrap($fieldset); 197 $hiddenDateInput.insertAfter($inputTextBox); 198 199 // We have to force the editable option, otherwise things start to break. 200 options.editable = true; 201 202 $hiddenDateInput.pickadate(options); 203 204 var pickerObject = $hiddenDateInput.pickadate('picker'); // reference to the pickadate object 205 $inputTextBox.attr("role", "combobox"); 206 $inputTextBox.attr("aria-haspopup", "dialog"); 207 $inputTextBox.attr("aria-expanded", "false"); 208 $inputTextBox.attr("aria-controls", pickerObject.$root.attr("id")); 209 210 // Bugfix - https://civicplus.tpondemand.com/entity/182182-2
21011-agenda-center-module-main-page 211 // Move aria-owns from the hidden <input> to the interactive one 212 $inputTextBox.attr("aria-owns", pickerObject.$node.attr("aria-owns")); 213 pickerObject.$node.removeAttr("aria-owns"); 214 215 var allowDatePickerFocus = false; 216 var isTabbingAwayFromDatePicker = false; 217 218 // Keep only the visible picker action controls in the keyboard path; date cells are tracked through aria-activedescendant. 219 function getPickerControls($holder) { 220 return $holder.find('.picker__nav--prev, .picker__nav--next, .picker__button--today, .picker__button--clear, .picker__button--close').filter(function () { 221 return !$(this).hasClass('picker__nav--disabled') && $(this).attr('aria-hidden') !== 'true'; 222 }); 223 } 224 225 function enablePickerControlTabbing() { 226 getPickerControls(pickerObject.$holder).attr('tabindex', '0'); 227 } 228 229 function restorePickerControlFocus(selector) { 230 setTimeout(function () { 231 updateActiveDescendant(); 232 enablePickerControlTabbing(); 233 pickerObject.$holder.find(selector).first().focus(); 234 }, 0); 235 } 236 237 function getNextTabbableElement() { 238 const $focusableElements = $('a[href], area[href], input:not([disabled]):not([type="hidden"]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), iframe, object, embed, [tabindex], [contenteditable=true]').filter(function () { 239 const $element = $(this); 240 const tabindex = $element.attr('tabindex'); 241 242 return $element.is(':visible') && tabindex !== '-1' && $element.attr('aria-hidden') !== 'true' && this !== pickerObject.$holder[0] && !$.contains(pickerObject.$holder[0], this); 243 }); 244 245 const currentInputIndex = $focusableElements.index($inputTextBox); 246 if (currentInputIndex !== -1 && currentInputIndex < $focusableElements.length - 1) { 247 return $focusableElements.eq(currentInputIndex + 1); 248 } 249 250 return $(); 251 } 252 253 // Add handler to make sure datepicker is visible when shown 254 pickerObject && pickerObject.on('open', function () { 255 $inputTextBox.attr("aria-expanded", "true"); 256 $inputTextBox.attr('aria-live', 'off'); 257 $inputTextBox.attr('aria-atomic', 'false'); 258 $inputTextBox.attr('role', 'textbox'); 259 260 const selected = pickerObject.get('select'); 261 if (!selected) { 262 pickerObject.set('highlight', new Date(), { muted: true }); 263 } 264 265 const $holder = pickerObject.$holder; 266 const inputId = $inputTextBox.attr('id'); 267 268 const inputLabel = ($('label[for="' + inputId + '"]').text() || '').trim(); 269 270 const labelText = inputLabel || $inputTextBox.attr('aria-label') || $inputTextBox.attr('title') || 'Date picker calendar'; 271 272 //Use the application role so screen readers pass arrow keys to the custom keyboard interaction. 273 $holder.attr({ 274 'tabindex': allowDatePickerFocus ? '0' : '-1', 275 'role': 'application', 276 'aria-label': labelText 277 }); 278 279 $holder.find('.picker__nav--prev, .picker__nav--next, .picker__button--today, .picker__button--clear, .picker__button--close').attr('tabindex', '-1'); 280 281 //Only ONE focusable element 282 $holder.find('.picker__day').attr('tabindex', '-1'); 283 $holder.find('.picker__day').attr('aria-hidden', 'true'); 284 285 //Set aria-selected 286 $holder.find('.picker__day').each(function () { 287 const $day = $(this); 288 $day.attr('aria-selected', $day.hasClass('picker__day--selected') ? 'true' : 'false'); 289 }); 290 291 //Set active descendant 292 setTimeout(updateActiveDescendant, 0); 293 294 $holder.off('keydown.grid').on('keydown.grid', function (e) { 295 if (e.keyCode === keyCodes.tab) { 296 const $controls = getPickerControls($holder); 297 const currentIndex = $controls.index(e.target); 298 299 if (currentIndex !== -1) { 300 if (e.shiftKey && currentIndex === 0) { 301 e.preventDefault(); 302 $holder.focus(); 303 return; 304 } 305 306 if (!e.shiftKey && currentIndex < $controls.length - 1) { 307 e.preventDefault(); 308 $controls.eq(currentIndex + 1).focus(); 309 } 310 311 if (!e.shiftKey && currentIndex === $controls.length - 1) { 312 e.preventDefault(); 313 // Capture the next real page control before closing because picker DOM removal can disrupt native Tab focus. 314 const $nextTabbableElement = getNextTabbableElement(); 315 isTabbingAwayFromDatePicker = true; 316 allowDatePickerFocus = false;
317 setTimeout(function () { 318 pickerObject.close(true); 319 setTimeout(function () { 320 $nextTabbableElement.focus(); 321 }, 0); 322 }, 0); 323 } 324 325 return; 326 } 327 328 if ($holder.is(e.target) || $holder.has(e.target).length) { 329 e.preventDefault(); 330 enablePickerControlTabbing(); 331 (e.shiftKey ? $controls.last() : $controls.first()).focus(); 332 } 333 334 return; 335 } 336 337 if ($holder.is(e.target) && e.keyCode === 32) { 338 e.preventDefault(); 339 $holder.find('.picker__day--highlighted:not(.picker__day--disabled)').click(); 340 return; 341 } 342 343 const $target = $(e.target); 344 if ($target.is('.picker__nav--prev, .picker__nav--next') && (e.keyCode === keyCodes.downArrow || e.which === keyCodes.downArrow || e.key === 'ArrowDown' || e.key === 'Down')) { 345 e.preventDefault(); 346 $holder.focus(); 347 return; 348 } 349 350 if ($target.is('.picker__nav--prev, .picker__nav--next') && e.keyCode === 32) { 351 e.preventDefault(); 352 $target.click(); 353 } 354 355 setTimeout(() => { 356 updateActiveDescendant(); 357 358 $holder.find('.picker__day').attr('aria-selected', 'false'); 359 360 const $selected = $holder.find('.picker__day--selected'); 361 if ($selected.length) { 362 $selected.attr('aria-selected', 'true'); 363 } 364 }, 0); 365 }); 366 367 $holder.off('click.grid', '.picker__nav--prev, .picker__nav--next').on('click.grid', '.picker__nav--prev, .picker__nav--next', function () { 368 const $target = $(this); 369 if (!$target.hasClass('picker__nav--disabled')) { 370 restorePickerControlFocus($target.hasClass('picker__nav--prev') ? '.picker__nav--prev' : '.picker__nav--next'); 371 } 372 }); 373 374 // Add a slight delay to ensure that the flyout has been added to the dom and the flyoutHeight is nonzero 375 setTimeout(function () { 376 // Get the position of the datepicker flyout relative to the top of the document 377 var flyoutTop = pickerObject.$holder[0].getBoundingClientRect().top; 378 379 // Get the height of the datepicker flyout 380 var flyoutHeight = pickerObject.$holder[0].offsetHeight; 381 382 // Get the height of the viewport 383 var viewportHeight = window.innerHeight; 384 385 // Calculate the amount of scrolling needed to make the entire datepicker flyout visible 386 var scrollAmount = flyoutTop + flyoutHeight - viewportHeight; 387 388 // If the datepicker flyout is not fully visible, scroll the viewport down to make it visible 389 if (scrollAmount > 0) { 390 window.scrollTo(0, scrollAmount + window.scrollY); 391 } 392 }, 50); 393 }); 394 395 function focusInput() { 396 $inputTextBox.off("focus", openPicker); 397 $inputTextBox.focus(); 398 setTimeout(function () { 399 $inputTextBox.on("focus", openPicker); 400 }, 0); 401 } 402 403 pickerObject.on('close', function () { 404 //Trigger the change event on the picker object's element. 405 window.parent.$(dateTextboxSelector).change(); 406 $inputTextBox.attr("aria-expanded", "false"); 407 pickerObject.$holder.attr('tabindex', '-1'); 408 pickerObject.$holder.find('.picker__nav--prev, .picker__nav--next, .picker__button--today, .picker__button--clear, .picker__button--close').attr('tabindex', '-1'); 409 if (!isTabbingAwayFromDatePicker) { 410 focusInput(); 411 } 412 isTabbingAwayFromDatePicker = false; 413 allowDatePickerFocus = false; 414 }); 415 416 // setDate is a helper function that should only be called from within this module. 417 function setDate(dateAsString, placeholder) { 418 if (dateAsString === '') { 419 pickerObject.set('clear', null, null); 420 } else { 421 if (pickerObject.component.settings.format == 'dd/mm/yyyy') { 422 var splitDate = autoCompleteDate(dateAsString, pickerObject.component.settings.format).split('/'); 423 var date = splitDate[1] + '/' + splitDate[0] + '/' + splitDate[2]; 424 var epochDate = Date.parse(date); 425 } else { 426 var epochDate = Date.parse(autoCompleteDate(dateAsString, pickerObject.component.settings.format)); 427 } 428 if (!epochDate) { 429 return; 430 } else { 431 var parsedDate = new Date(epochDate); 432 pickerObject.set('select', 433 [parsedDate.getFullYear(), parsedDate.getMonth(), parsedDate.getDate()]); 434 } 435 } 436 } 437 438 function updateActiveDescendant() { 439 const $holder = pickerObject.$holder; 440 const $active = $holder.find('.picker__day--highlighted'); 441 $holder.find('.picker__day').attr('aria-hidden', 'true'); 442 443 if ($active.length) { 444 $active.attr('aria-hidden', 'false'); 445 $holder.attr('aria-activedescendant', $active.attr('id')); 446 447 const dateValue = $active.data('pick'); 448 if (dateValue) { 449 const date = new Date(dateValue); 450 451 const formatted = date.toLocaleDateString('en-US', { 452 month: 'long', 453 day: 'numeric', 454 year: 'numeric' 455 }); 456 457 // override for active cell 458 $active.attr('aria-label', formatted); 459 } 460 } 461 } 462 463 var intervalID; 464 465 //Moves the date picker further up the DOM so it isn't overlapped https://civicplus.tpondemand.com/entity/22312 466 function adjustDTPicker() { 467 if (intervalID === 0) { 468 return; 469 } 470 471 var elemPicker = $('.picker--opened')[0]; 472 if (elemPicker) { 473 $(elemPicker).appendTo('#cpDatePickerWrapper'); 474 elemPicker.style.fontSize = 'inherit'; 475 } 476 477 clearInterval(intervalID); 478 479 $('.cpDatePickerElevate').each(function () { 480 var $dateInput = $(this); 481 if ($dateInput.is(':focus')) { 482 adjustFlyoutPosition($dateInput, $('#cpDatePickerWrapper')); 483 } 484 }); 485 486 intervalID = 0; 487 } 488 489 if ($('#cpDatePickerWrapper').length > 0) { 490 $(window).resize(function () { 491 $('.cpDatePickerElevate').each(function () { 492 var $dateInput = $(this); 493 if ($dateInput.is(':focus')) { 494 adjustFlyoutPosition($dateInput, $('#cpDatePickerWrapper')); 495 } 496 }); 497 }); 498 } 499 500 function openPicker() { 501 allowDatePickerFocus = false; 502 // We have to set the tab-index of the "today", "close", and "clear" buttons, so tabbing through fields works properly. 503 $('.picker__button--close').attr('tabindex', -1); 504 $('.picker__button--today').attr('tabindex', -1); 505 $('.picker__button--clear').attr('tabindex', -1); 506 // pickerObject.$holder.find('[tabindex="-1"]').attr('tabindex', 0); 507 pickerObject.open(false); 508 if ($('#cpDatePickerWrapper').length > 0) 509 intervalID = setInterval(adjustDTPicker, 50); 510 } 511 512 $inputTextBox.on({ 513 change: function () { 514 setDate(this.value); 515 }, 516 focus: openPicker, 517 click: openPicker, 518 keydown: function (e) { 519 if (e.keyCode === keyCodes.tab) { 520 isTabbingAwayFromDatePicker = true; 521 pickerObject.$holder.attr('tabindex', -1); 522 pickerObject.$holder.find('[tabindex="0"]').attr('tabindex', -1); 523 pickerObject.close(); 524 isTabbingAwayFromDatePicker = false; 525 } 526 if (e.keyCode === keyCodes.downArrow) { 527 e.preventDefault(); 528 allowDatePickerFocus = true; 529 pickerObject.open(); 530 setTimeout(function () { 531 enablePickerControlTabbing(); 532 pickerObject.$holder.attr('tabindex', '0').focus(); 533 }, 0); 534 } 535 }, 536 blur: function (e) {
537 // If user shifts focus from the input element to its datepicker, then we do not want to close the datepicker. 538 if (!allowDatePickerFocus || !e.relatedTarget?.closest(".picker")) { 539 pickerObject.close(true); 540 $inputTextBox.attr("aria-expanded", "false"); 541 } 542 }, 543 }); 544 545 pickerObject.on('set', function () { 546 $inputTextBox.val(this.get('value')); 547 $inputTextBox.attr('value', this.get('value')); 548 549 setTimeout(updateActiveDescendant, 0); 550 }); 551 552 // Priming initialization if there was a value specified for the input 553 if ($inputTextBox.val() !== '') { 554 setDate($inputTextBox.val()); 555 } 556 557 558 return pickerObject; 559 } 560 561 function insertColonIfOverFourChars(inputString) { 562 if (inputString != null && inputString.length > 3 && inputString.indexOf(":") < 0) { 563 inputString = inputString.slice(0, 2) + ":" + inputString.slice(2, inputString.length); 564 } 565 return inputString; 566 } 567 568 function autoCompleteDate(dateAsString, formatString) { 569 try { 570 var fdts = null; 571 var currentDate = new Date(); 572 573 //guard clauses 574 if (formatString == null || formatString.length === 0) { 575 formatString === "mm/dd/yyyy"; 576 } 577 if ((dateAsString == null || dateAsString.length == 0 || dateAsString.indexOf(".") != -1) && formatString.toLowerCase == "dd/mm/yyyy") { 578 return ('0' + currentDate.getDate()).slice(-2) + '/' + ('0' + (currentDate.getMonth() + 1)).slice(-2) + '/' + currentDate.getFullYear(); 579 } else if (dateAsString == null || dateAsString.length == 0 || dateAsString.indexOf(".") != -1) { 580 return ('0' + (currentDate.getMonth() + 1)).slice(-2) + '/' + ('0' + currentDate.getDate()).slice(-2) + '/' + currentDate.getFullYear(); 581 } 582 583 fdts = dateAsString.split("/"); 584 585 if (fdts != null) { 586 //switch mm/dd/yyyy to be dd/mm/yyyy for logic further down 587 if (formatString.toLowerCase() !== "dd/mm/yyyy") { 588 var day = fdts[1]; 589 var month = fdts[0]; 590 fdts[0] = day; 591 fdts[1] = month; 592 } 593 594 if (fdts[0] == null || fdts[0] === '0' || fdts[0].length < 1 || fdts[0].length > 2) { 595 fdts[0] = currentDate.getDate(); 596 } 597 if (fdts[1] == null || fdts[1] === '0' || fdts[1].length < 1 || fdts[1].length > 2) { 598 fdts[1] = currentDate.getMonth() + 1; 599 } 600 if (fdts[2] == null || (fdts[2].length !== 2 && fdts[2].length !== 4)) { 601 //For last year's date it also adds time at the end of date so extracting year 602 fdts[2] = !isNaN(fdts[2].substr(0, 4)) ? fdts[2].substr(0, 4) : currentDate.getFullYear(); 603 } else if (fdts[2].length === 2) { 604 var yr = fdts[2]; 605 var crDate = new Date(); 606 var crCtry = crDate.getFullYear(); 607 var pstCnry = crCtry - 100; 608 crCtry = crCtry + ' '; 609 pstCnry = pstCnry + ' '; 610 crCtry = crCtry.substr(0, 2); 611 pstCnry = pstCnry.substr(0, 2); 612 if (yr >= 32) 613 yr = pstCnry + yr; 614 else 615 yr = crCtry + yr; 616 fdts[2] = yr; 617 } 618 } 619 if (formatString.toLowerCase() === "dd/mm/yyyy") { 620 dateAsString = ('0' + fdts[0]).slice(-2) + '/' + ('0' + fdts[1]).slice(-2) + '/' + fdts[2]; 621 } else { 622 dateAsString = ('0' + fdts[1]).slice(-2) + '/' + ('0' + fdts[0]).slice(-2) + '/' + fdts[2]; 623 } 624 return dateAsString; 625 } catch (e) { 626 return ""; 627 } 628 } 629 630 function autoTimeFormat(inputString) { 631 632 const regex1 = /^(0?[1-9]|1[0-2]) ?([ap]m)$/i; //eg: 5AM -> 5:00 AM 633 const regex2 = /^(\d{1,2})(\d{2}) ?([ap]m)$/i; //eg: 500AM -> 5:00 AM 634 const regex3 = /^(\d{1,2})(?::(\d{2}))?\s*([ap]m)?$/i; //eg: 05:00 am or 5:00 am -> 5:00 AM 635 636 if (regex1.test(inputString)) { 637 return inputString.replace(regex1, (match, hour, period) => { 638 const hourNum = parseInt(hour); 639 const formattedHour = hourNum === 12 ? 12 : hourNum % 12; // Convert to 12-hour format 640 const formattedMinute = "00"; // Add 00 as minutes 641 const formattedPeriod = period.toUpperCase();
641 // Convert period to uppercase 642 return `${formattedHour}:${formattedMinute} ${formattedPeriod}`; 643 }); 644 } 645 else if (regex2.test(inputString)) { 646 return inputString.replace(regex2, (match, hour, minute, period) => { 647 const hourNum = parseInt(hour); 648 const formattedHour = hourNum === 12 ? 12 : hourNum % 12; // Convert to 12-hour format 649 const formattedMinute = minute.padStart(2, "0"); // Pad minute with zero if necessary 650 const formattedPeriod = period.toUpperCase(); // Convert period to uppercase 651 return `${formattedHour}:${formattedMinute} ${formattedPeriod}`; 652 }); 653 } 654 else if (regex3.test(inputString)) { 655 return inputString.replace(regex3, (match, hour, minute, period) => { 656 const hourNum = parseInt(hour); 657 const formattedHour = hourNum === 12 ? 12 : hourNum % 12; // Convert to 12-hour format 658 const formattedMinute = minute ? minute : "00"; // Set minute to "00" if not provided 659 const formattedPeriod = period ? period.toUpperCase() : ""; // Convert period to uppercase if provided 660 return `${formattedHour}:${formattedMinute} ${formattedPeriod}`; 661 }); 662 } 663 else { 664 return inputString; 665 } 666 } 667})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.