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 = '<div 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 = '<div class="fieldset__datetimepicker__wrapper"></div>'; 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 const selected = pickerObject.get('select'); 259 if (!selected) { 260 pickerObject.set('highlight', new Date(), { muted: true }); 261 } 262 263 const $holder = pickerObject.$holder; 264 const inputId = $inputTextBox.attr('id'); 265 266 const inputLabel = ($('label[for="' + inputId + '"]').text() || '').trim(); 267 268 const labelText = inputLabel || $inputTextBox.attr('aria-label') || $inputTextBox.attr('title') || 'Date picker calendar'; 269 270 //Use the application role so screen readers pass arrow keys to the custom keyboard interaction. 271 $holder.attr({ 272 'tabindex': allowDatePickerFocus ? '0' : '-1', 273 'role': 'application', 274 'aria-label': labelText 275 }); 276 277 $holder.find('.picker__nav--prev, .picker__nav--next, .picker__button--today, .picker__button--clear, .picker__button--close').attr('tabindex', '-1'); 278 279 //Only ONE focusable element 280 $holder.find('.picker__day').attr('tabindex', '-1'); 281 $holder.find('.picker__day').attr('aria-hidden', 'true'); 282 283 //Set aria-selected 284 $holder.find('.picker__day').each(function () { 285 const $day = $(this); 286 $day.attr('aria-selected', $day.hasClass('picker__day--selected') ? 'true' : 'false'); 287 }); 288 289 //Set active descendant 290 setTimeout(updateActiveDescendant, 0); 291 292 $holder.off('keydown.grid').on('keydown.grid', function (e) { 293 if (e.keyCode === keyCodes.tab) { 294 const $controls = getPickerControls($holder); 295 const currentIndex = $controls.index(e.target); 296 297 if (currentIndex !== -1) { 298 if (e.shiftKey && currentIndex === 0) { 299 e.preventDefault(); 300 $holder.focus(); 301 return; 302 } 303 304 if (!e.shiftKey && currentIndex < $controls.length - 1) { 305 e.preventDefault(); 306 $controls.eq(currentIndex + 1).focus(); 307 } 308 309 if (!e.shiftKey && currentIndex === $controls.length - 1) { 310 e.preventDefault(); 311 // Capture the next real page control before closing because picker DOM removal can disrupt native Tab focus. 312 const $nextTabbableElement = getNextTabbableElement(); 313 isTabbingAwayFromDatePicker = true; 314 allowDatePickerFocus = false; 315 setTimeout(function () { 316 pickerObject.close(true); 317 setTimeout(function () { 318 $nextTabbableElement.focus(); 319 }, 0); 320 }, 0); 321 } 322 323 return; 324 } 325 326 if ($holder.is(e.target) || $holder.has(e.target).length) { 327 e.preventDefault(); 328 enablePickerControlTabbing(); 329 (e.shiftKey ? $controls.last() : $controls.first()).focus(); 330 } 331 332 return; 333 } 334 335 if ($holder.is(e.target) && e.keyCode === 32) { 336 e.preventDefault(); 337 $holder.find('.picker__day--highlighted:not(.picker__day--disabled)').click(); 338 return; 339 } 340 341 const $target = $(e.target); 342 if ($target.is('.picker__nav--prev, .picker__nav--next') && (e.keyCode === keyCodes.downArrow || e.
342which === keyCodes.downArrow || e.key === 'ArrowDown' || e.key === 'Down')) { 343 e.preventDefault(); 344 $holder.focus(); 345 return; 346 } 347 348 if ($target.is('.picker__nav--prev, .picker__nav--next') && e.keyCode === 32) { 349 e.preventDefault(); 350 $target.click(); 351 } 352 353 setTimeout(() => { 354 updateActiveDescendant(); 355 356 $holder.find('.picker__day').attr('aria-selected', 'false'); 357 358 const $selected = $holder.find('.picker__day--selected'); 359 if ($selected.length) { 360 $selected.attr('aria-selected', 'true'); 361 } 362 }, 0); 363 }); 364 365 $holder.off('click.grid', '.picker__nav--prev, .picker__nav--next').on('click.grid', '.picker__nav--prev, .picker__nav--next', function () { 366 const $target = $(this); 367 if (!$target.hasClass('picker__nav--disabled')) { 368 restorePickerControlFocus($target.hasClass('picker__nav--prev') ? '.picker__nav--prev' : '.picker__nav--next'); 369 } 370 }); 371 372 // Add a slight delay to ensure that the flyout has been added to the dom and the flyoutHeight is nonzero 373 setTimeout(function () { 374 // Get the position of the datepicker flyout relative to the top of the document 375 var flyoutTop = pickerObject.$holder[0].getBoundingClientRect().top; 376 377 // Get the height of the datepicker flyout 378 var flyoutHeight = pickerObject.$holder[0].offsetHeight; 379 380 // Get the height of the viewport 381 var viewportHeight = window.innerHeight; 382 383 // Calculate the amount of scrolling needed to make the entire datepicker flyout visible 384 var scrollAmount = flyoutTop + flyoutHeight - viewportHeight; 385 386 // If the datepicker flyout is not fully visible, scroll the viewport down to make it visible 387 if (scrollAmount > 0) { 388 window.scrollTo(0, scrollAmount + window.scrollY); 389 } 390 }, 50); 391 }); 392 393 function focusInput() { 394 $inputTextBox.off("focus", openPicker); 395 $inputTextBox.focus(); 396 setTimeout(function () { 397 $inputTextBox.on("focus", openPicker); 398 }, 0); 399 } 400 401 pickerObject.on('close', function () { 402 //Trigger the change event on the picker object's element. 403 window.parent.$(dateTextboxSelector).change(); 404 $inputTextBox.attr("aria-expanded", "false"); 405 pickerObject.$holder.attr('tabindex', '-1'); 406 pickerObject.$holder.find('.picker__nav--prev, .picker__nav--next, .picker__button--today, .picker__button--clear, .picker__button--close').attr('tabindex', '-1'); 407 if (!isTabbingAwayFromDatePicker) { 408 focusInput(); 409 } 410 isTabbingAwayFromDatePicker = false; 411 allowDatePickerFocus = false; 412 }); 413 414 // setDate is a helper function that should only be called from within this module. 415 function setDate(dateAsString, placeholder) { 416 if (dateAsString === '') { 417 pickerObject.set('clear', null, null); 418 } else { 419 if (pickerObject.component.settings.format == 'dd/mm/yyyy') { 420 var splitDate = autoCompleteDate(dateAsString, pickerObject.component.settings.format).split('/'); 421 var date = splitDate[1] + '/' + splitDate[0] + '/' + splitDate[2]; 422 var epochDate = Date.parse(date); 423 } else { 424 var epochDate = Date.parse(autoCompleteDate(dateAsString, pickerObject.component.settings.format)); 425 } 426 if (!epochDate) { 427 return; 428 } else { 429 var parsedDate = new Date(epochDate); 430 pickerObject.set('select', 431 [parsedDate.getFullYear(), parsedDate.getMonth(), parsedDate.getDate()]); 432 } 433 } 434 } 435 436 function updateActiveDescendant() { 437 const $holder = pickerObject.$holder; 438 const $active = $holder.find('.picker__day--highlighted'); 439 $holder.find('.picker__day').attr('aria-hidden', 'true'); 440 441 if ($active.length) { 442 $active.attr('aria-hidden', 'false'); 443 $holder.attr('aria-activedescendant', $active.attr('id')); 444 445 const dateValue = $active.data('pick'); 446 if (dateValue) { 447 const date = new Date(dateValue); 448 449 const formatted = date.toLocaleDateString('en-US', { 450 month: 'long', 451 day: 'numeric', 452 year: 'numeric' 453 }); 454 455 // override for active cell 456 $active.attr('aria-label', formatted); 457 } 458 } 459 } 460 461 var intervalID; 462 463 //Moves the date picker further up the DOM so it isn't overlapped https://civicplus.tpondemand.com/entity/22312 464 function adjustDTPicker() { 465 if (intervalID === 0) { 466 return; 467 } 468 469 var elemPicker = $('.picker--opened')[0]; 470 if (elemPicker) { 471 $(elemPicker).appendTo('#cpDatePickerWrapper'); 472 elemPicker.style.fontSize = 'inherit'; 473 } 474 475 clearInterval(intervalID); 476 477 $('.cpDatePickerElevate').each(function () { 478 var $dateInput = $(this); 479 if ($dateInput.is(':focus')) { 480 adjustFlyoutPosition($dateInput, $('#cpDatePickerWrapper')); 481 } 482 }); 483 484 intervalID = 0; 485 } 486 487 if ($('#cpDatePickerWrapper').length > 0) { 488 $(window).resize(function () { 489 $('.cpDatePickerElevate').each(function () { 490 var $dateInput = $(this); 491 if ($dateInput.is(':focus')) { 492 adjustFlyoutPosition($dateInput, $('#cpDatePickerWrapper')); 493 } 494 }); 495 }); 496 } 497 498 function openPicker() { 499 allowDatePickerFocus = false; 500 // We have to set the tab-index of the "today", "close", and "clear" buttons, so tabbing through fields works properly. 501 $('.picker__button--close').attr('tabindex', -1); 502 $('.picker__button--today').attr('tabindex', -1); 503 $('.picker__button--clear').attr('tabindex', -1); 504 // pickerObject.$holder.find('[tabindex="-1"]').attr('tabindex', 0); 505 pickerObject.open(false); 506 if ($('#cpDatePickerWrapper').length > 0) 507 intervalID = setInterval(adjustDTPicker, 50); 508 } 509 510 $inputTextBox.on({ 511 change: function () { 512 setDate(this.value); 513 }, 514 focus: openPicker, 515 click: openPicker, 516 keydown: function (e) { 517 if (e.keyCode === keyCodes.tab) { 518 isTabbingAwayFromDatePicker = true; 519 pickerObject.$holder.attr('tabindex', -1); 520 pickerObject.$holder.find('[tabindex="0"]').attr('tabindex', -1); 521 pickerObject.close(); 522 isTabbingAwayFromDatePicker = false; 523 } 524 if (e.keyCode === keyCodes.downArrow) { 525 e.preventDefault(); 526 allowDatePickerFocus = true; 527 pickerObject.open(); 528 setTimeout(function () { 529 enablePickerControlTabbing(); 530 pickerObject.$holder.attr('tabindex', '0').focus(); 531 }, 0); 532 } 533 }, 534 blur: function (e) {
535 // If user shifts focus from the input element to its datepicker, then we do not want to close the datepicker. 536 if (!allowDatePickerFocus || !e.relatedTarget?.closest(".picker")) { 537 pickerObject.close(true); 538 $inputTextBox.attr("aria-expanded", "false"); 539 } 540 }, 541 }); 542 543 pickerObject.on('set', function () { 544 $inputTextBox.val(this.get('value')); 545 $inputTextBox.attr('value', this.get('value')); 546 547 setTimeout(updateActiveDescendant, 0); 548 }); 549 550 // Priming initialization if there was a value specified for the input 551 if ($inputTextBox.val() !== '') { 552 setDate($inputTextBox.val()); 553 } 554 555 556 return pickerObject; 557 } 558 559 function insertColonIfOverFourChars(inputString) { 560 if (inputString != null && inputString.length > 3 && inputString.indexOf(":") < 0) { 561 inputString = inputString.slice(0, 2) + ":" + inputString.slice(2, inputString.length); 562 } 563 return inputString; 564 } 565 566 function autoCompleteDate(dateAsString, formatString) { 567 try { 568 var fdts = null; 569 var currentDate = new Date(); 570 571 //guard clauses 572 if (formatString == null || formatString.length === 0) { 573 formatString === "mm/dd/yyyy"; 574 } 575 if ((dateAsString == null || dateAsString.length == 0 || dateAsString.indexOf(".") != -1) && formatString.toLowerCase == "dd/mm/yyyy") { 576 return ('0' + currentDate.getDate()).slice(-2) + '/' + ('0' + (currentDate.getMonth() + 1)).slice(-2) + '/' + currentDate.getFullYear(); 577 } else if (dateAsString == null || dateAsString.length == 0 || dateAsString.indexOf(".") != -1) { 578 return ('0' + (currentDate.getMonth() + 1)).slice(-2) + '/' + ('0' + currentDate.getDate()).slice(-2) + '/' + currentDate.getFullYear(); 579 } 580 581 fdts = dateAsString.split("/"); 582 583 if (fdts != null) { 584 //switch mm/dd/yyyy to be dd/mm/yyyy for logic further down 585 if (formatString.toLowerCase() !== "dd/mm/yyyy") { 586 var day = fdts[1]; 587 var month = fdts[0]; 588 fdts[0] = day; 589 fdts[1] = month; 590 } 591 592 if (fdts[0] == null || fdts[0] === '0' || fdts[0].length < 1 || fdts[0].length > 2) { 593 fdts[0] = currentDate.getDate(); 594 } 595 if (fdts[1] == null || fdts[1] === '0' || fdts[1].length < 1 || fdts[1].length > 2) { 596 fdts[1] = currentDate.getMonth() + 1; 597 } 598 if (fdts[2] == null || (fdts[2].length !== 2 && fdts[2].length !== 4)) { 599 //For last year's date it also adds time at the end of date so extracting year 600 fdts[2] = !isNaN(fdts[2].substr(0, 4)) ? fdts[2].substr(0, 4) : currentDate.getFullYear(); 601 } else if (fdts[2].length === 2) { 602 var yr = fdts[2]; 603 var crDate = new Date(); 604 var crCtry = crDate.getFullYear(); 605 var pstCnry = crCtry - 100; 606 crCtry = crCtry + ' '; 607 pstCnry = pstCnry + ' '; 608 crCtry = crCtry.substr(0, 2); 609 pstCnry = pstCnry.substr(0, 2); 610 if (yr >= 32) 611 yr = pstCnry + yr; 612 else 613 yr = crCtry + yr; 614 fdts[2] = yr; 615 } 616 } 617 if (formatString.toLowerCase() === "dd/mm/yyyy") { 618 dateAsString = ('0' + fdts[0]).slice(-2) + '/' + ('0' + fdts[1]).slice(-2) + '/' + fdts[2]; 619 } else { 620 dateAsString = ('0' + fdts[1]).slice(-2) + '/' + ('0' + fdts[0]).slice(-2) + '/' + fdts[2]; 621 } 622 return dateAsString; 623 } catch (e) { 624 return ""; 625 } 626 } 627 628 function autoTimeFormat(inputString) { 629 630 const regex1 = /^(0?[1-9]|1[0-2]) ?([ap]m)$/i; //eg: 5AM -> 5:00 AM 631 const regex2 = /^(\d{1,2})(\d{2}) ?([ap]m)$/i; //eg: 500AM -> 5:00 AM 632 const regex3 = /^(\d{1,2})(?::(\d{2}))?\s*([ap]m)?$/i; //eg: 05:00 am or 5:00 am -> 5:00 AM 633 634 if (regex1.test(inputString)) { 635 return inputString.replace(regex1, (match, hour, period) => { 636 const hourNum = parseInt(hour); 637 const formattedHour = hourNum === 12 ? 12 : hourNum % 12; // Convert to 12-hour format 638 const formattedMinute = "00"; // Add 00 as minutes 639 const formattedPeriod = period.toUpperCase();
639 // Convert period to uppercase 640 return `${formattedHour}:${formattedMinute} ${formattedPeriod}`; 641 }); 642 } 643 else if (regex2.test(inputString)) { 644 return inputString.replace(regex2, (match, hour, minute, period) => { 645 const hourNum = parseInt(hour); 646 const formattedHour = hourNum === 12 ? 12 : hourNum % 12; // Convert to 12-hour format 647 const formattedMinute = minute.padStart(2, "0"); // Pad minute with zero if necessary 648 const formattedPeriod = period.toUpperCase(); // Convert period to uppercase 649 return `${formattedHour}:${formattedMinute} ${formattedPeriod}`; 650 }); 651 } 652 else if (regex3.test(inputString)) { 653 return inputString.replace(regex3, (match, hour, minute, period) => { 654 const hourNum = parseInt(hour); 655 const formattedHour = hourNum === 12 ? 12 : hourNum % 12; // Convert to 12-hour format 656 const formattedMinute = minute ? minute : "00"; // Set minute to "00" if not provided 657 const formattedPeriod = period ? period.toUpperCase() : ""; // Convert period to uppercase if provided 658 return `${formattedHour}:${formattedMinute} ${formattedPeriod}`; 659 }); 660 } 661 else { 662 return inputString; 663 } 664 } 665})(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.