1function dayTripper() { 2 $(".ui-datepicker-trigger").click(function () { 3 setTimeout(function () { 4 var today = $(".ui-datepicker-today a")[0]; 5 6 if (!today) { 7 today = $(".ui-state-active")[0] || $(".ui-state-default")[0]; 8 } 9 10 // Hide the entire page (except the date picker) 11 // from screen readers to prevent document navigation 12 // (by headings, etc.) while the popup is open 13 // $("main").attr("id", "dp-container"); 14 // $("#dp-container").attr('aria-hidden','true'); 15 // $("#skipnav").attr("aria-hidden", "true"); 16 17 // Hide the "today" button because it doesn't do what 18 // you think it supposed to do 19 $(".ui-datepicker-current").hide(); 20 21 today.focus(); 22 datePickHandler(); 23 $(document).on( 24 "click", 25 "#ui-datepicker-div .ui-datepicker-close", 26 function () { 27 closeCalendar(); 28 } 29 ); 30 }, 0); 31 }); 32} 33 34function datePickHandler() { 35 var activeDate; 36 var container = document.getElementById("ui-datepicker-div"); 37 var input = document.getElementById("datepicker"); 38 39 if (!container || !input) { 40 return; 41 } 42 43 // $(container).find('table').first().attr('role', 'grid'); 44 45 container.setAttribute("role", "application"); 46 container.setAttribute("aria-label", "Calendar view date-picker"); 47 48 // the top controls: 49 var prev = $(".ui-datepicker-prev", container)[0], 50 next = $(".ui-datepicker-next", container)[0]; 51 52 // This is the line that needs to be fixed for use on pages with base URL set in head 53 next.href = "javascript:void(0)"; 54 prev.href = "javascript:void(0)"; 55 56 next.setAttribute("role", "button"); 57 next.removeAttribute("title"); 58 prev.setAttribute("role", "button"); 59 prev.removeAttribute("title"); 60 61 appendOffscreenMonthText(next); 62 appendOffscreenMonthText(prev); 63 64 // delegation won't work here for whatever reason, so we are 65 // forced to attach individual click listeners to the prev / 66 // next month buttons each time they are added to the DOM 67 $(next).on("click", handleNextClicks); 68 $(prev).on("click", handlePrevClicks); 69 70 monthDayYearText(); 71 72 $(container).on("keydown", function calendarKeyboardListener(keyVent) { 73 var which = keyVent.which; 74 var target = keyVent.target; 75 var dateCurrent = getCurrentDate(container); 76 77 if (!dateCurrent) { 78 dateCurrent = $("a.ui-state-default")[0]; 79 setHighlightState(dateCurrent, container); 80 } 81 82 if (27 === which) { 83 keyVent.stopPropagation(); 84 return closeCalendar(); 85 } else if (which === 9 && keyVent.shiftKey) { 86 // SHIFT + TAB 87 keyVent.preventDefault(); 88 if ($(target).hasClass("ui-datepicker-close")) { 89 // close button 90 $(".ui-datepicker-prev")[0].focus(); 91 } else if ($(target).hasClass("ui-state-default")) { 92 // a date link 93 $(".ui-datepicker-close")[0].focus(); 94 } else if ($(target).hasClass("ui-datepicker-prev")) { 95 // the prev link 96 $(".ui-datepicker-next")[0].focus(); 97 } else if ($(target).hasClass("ui-datepicker-next")) { 98 // the next link 99 activeDate = $(".ui-state-highlight") || $(".ui-state-active")[0]; 100 if (activeDate) { 101 activeDate.focus(); 102 } 103 } 104 } else if (which === 9) { 105 // TAB 106 keyVent.preventDefault(); 107 if ($(target).hasClass("ui-datepicker-close")) { 108 // close button 109 activeDate = $(".ui-state-highlight") || $(".ui-state-active")[0]; 110 if (activeDate) { 111 activeDate.focus(); 112 } 113 } else if ($(target).hasClass("ui-state-default")) { 114 $(".ui-datepicker-next")[0].focus(); 115 } else if ($(target).hasClass("ui-datepicker-next")) { 116 $(".ui-datepicker-prev")[0].focus(); 117 } else if ($(target).hasClass("ui-datepicker-prev")) { 118 $(".ui-datepicker-close")[0].focus(); 119 } 120 } else if (which === 37) { 121 // LEFT arrow key 122 // if we're on a date link... 123 if ( 124 !$(target).hasClass("ui-datepicker-close") && 125 $(target).hasClass("ui-state-default") 126 ) { 127 keyVent.preventDefault(); 128 previousDay(target); 129 } 130 } else if (which === 39) { 131 // RIGHT arrow key 132 // if we're on a date link... 133 if ( 134 !$(target).hasClass("ui-datepicker-close") && 135 $(target).hasClass("ui-state-default") 136 ) { 137 keyVent.preventDefault(); 138 nextDay(target); 139 } 140 } else if (which === 38) { 141 // UP arrow key 142 if ( 143 !$(target).hasClass("ui-datepicker-close") && 144 $(target).hasClass("ui-state-default") 145 ) { 146 keyVent.preventDefault(); 147 upHandler(target, container, prev); 148 } 149 } else if (which === 40) { 150 // DOWN arrow key 151 if ( 152 !$(target).hasClass("ui-datepicker-close") && 153 $(target).hasClass("ui-state-default") 154 ) { 155 keyVent.preventDefault(); 156 downHandler(target, container, next); 157 } 158 } else if (which === 13) { 159 // ENTER 160 if ($(target).hasClass("ui-state-default")) { 161 setTimeout(function () {
162 closeCalendar(); 163 }, 100); 164 } else if ($(target).hasClass("ui-datepicker-prev")) { 165 handlePrevClicks(); 166 } else if ($(target).hasClass("ui-datepicker-next")) { 167 handleNextClicks(); 168 } 169 } else if (32 === which) { 170 if ( 171 $(target).hasClass("ui-datepicker-prev") || 172 $(target).hasClass("ui-datepicker-next") 173 ) { 174 target.click(); 175 } 176 } else if (33 === which) { 177 // PAGE UP 178 moveOneMonth(target, "prev"); 179 } else if (34 === which) { 180 // PAGE DOWN 181 moveOneMonth(target, "next"); 182 } else if (36 === which) { 183 // HOME 184 var firstOfMonth = $(target) 185 .closest("tbody") 186 .find(".ui-state-default")[0]; 187 if (firstOfMonth) { 188 firstOfMonth.focus(); 189 setHighlightState(firstOfMonth, $("#ui-datepicker-div")[0]); 190 } 191 } else if (35 === which) { 192 // END 193 var $daysOfMonth = $(target).closest("tbody").find(".ui-state-default"); 194 var lastDay = $daysOfMonth[$daysOfMonth.length - 1]; 195 if (lastDay) { 196 lastDay.focus(); 197 setHighlightState(lastDay, $("#ui-datepicker-div")[0]); 198 } 199 } 200 $(".ui-datepicker-current").hide(); 201 }); 202} 203 204function closeCalendar() { 205 var container = $("#ui-datepicker-div"); 206 $(container).off("keydown"); 207 var input = $(".datepicker-access")[0]; 208 $(input).datepicker("hide"); 209 210 input.focus(); 211} 212 213function removeAria() { 214 // make the rest of the page accessible again: 215 $("#dp-container").removeAttr("aria-hidden"); 216 $("#skipnav").removeAttr("aria-hidden"); 217} 218 219/////////////////////////////// 220//////////////////////////// // 221///////////////////////// // // 222// UTILITY-LIKE THINGS // // // 223///////////////////////// // // 224//////////////////////////// // 225/////////////////////////////// 226function isOdd(num) { 227 return num % 2; 228} 229 230function moveOneMonth(currentDate, dir) { 231 var button = 232 dir === "next" ? $(".ui-datepicker-next")[0] : $(".ui-datepicker-prev")[0]; 233 234 if (!button) { 235 return; 236 } 237 238 var ENABLED_SELECTOR = "#ui-datepicker-div tbody td:not(.ui-state-disabled)"; 239 var $currentCells = $(ENABLED_SELECTOR); 240 var currentIdx = $.inArray(currentDate.parentNode, $currentCells); 241 242 button.click(); 243 setTimeout(function () { 244 updateHeaderElements(); 245 246 var $newCells = $(ENABLED_SELECTOR); 247 var newTd = $newCells[currentIdx]; 248 var newAnchor = newTd && $(newTd).find("a")[0]; 249 250 while (!newAnchor) { 251 currentIdx--; 252 newTd = $newCells[currentIdx]; 253 newAnchor = newTd && $(newTd).find("a")[0]; 254 } 255 256 setHighlightState(newAnchor, $("#ui-datepicker-div")[0]); 257 newAnchor.focus(); 258 }, 0); 259} 260 261function handleNextClicks() { 262 setTimeout(function () { 263 updateHeaderElements(); 264 prepHighlightState(); 265 $(".ui-datepicker-next").focus(); 266 $(".ui-datepicker-current").hide(); 267 }, 0); 268} 269 270function handlePrevClicks() { 271 setTimeout(function () { 272 updateHeaderElements(); 273 prepHighlightState(); 274 $(".ui-datepicker-prev").focus(); 275 $(".ui-datepicker-current").hide(); 276 }, 0); 277} 278 279function previousDay(dateLink) { 280 var container = document.getElementById("ui-datepicker-div"); 281 if (!dateLink) { 282 return; 283 } 284 var td = $(dateLink).closest("td"); 285 if (!td) { 286 return; 287 } 288 289 var prevTd = $(td).prev(), 290 prevDateLink = $("a.ui-state-default", prevTd)[0]; 291 292 if (prevTd && prevDateLink) { 293 setHighlightState(prevDateLink, container); 294 prevDateLink.focus(); 295 } else { 296 handlePrevious(dateLink); 297 } 298} 299 300function handlePrevious(target) { 301 var container = document.getElementById("ui-datepicker-div"); 302 if (!target) { 303 return; 304 } 305 var currentRow = $(target).closest("tr"); 306 if (!currentRow) { 307 return; 308 } 309 var previousRow = $(currentRow).prev(); 310 311 if (!previousRow || previousRow.length === 0) { 312 // there is not previous row, so we go to previous month... 313 previousMonth(); 314 } else { 315 var prevRowDates = $("td a.ui-state-default", previousRow); 316 var prevRowDate = prevRowDates[prevRowDates.length - 1]; 317 318 if (prevRowDate) { 319 setTimeout(function () { 320 setHighlightState(prevRowDate, container); 321 prevRowDate.focus(); 322 }, 0); 323 } 324 } 325} 326 327function previousMonth() { 328 var prevLink = $(".ui-datepicker-prev")[0]; 329 var container = document.getElementById("ui-datepicker-div"); 330 prevLink.click(); 331 // focus last day of new month 332 setTimeout(function () { 333 var trs = $("tr", container), 334 lastRowTdLinks = $("td a.ui-state-default", trs[trs.length - 1]), 335 lastDate = lastRowTdLinks[lastRowTdLinks.length - 1]; 336 337 // updating the cached header elements 338 updateHeaderElements(); 339 340 setHighlightState(lastDate, container); 341 lastDate.focus(); 342 }, 0); 343} 344 345///////////////// NEXT ///////////////// 346/** 347 * Handles right arrow key navigation 348 * @param {HTMLElement} dateLink The target of the keyboard event 349 */ 350function nextDay(dateLink) { 351 var container = document.getElementById("ui-datepicker-div"); 352 if (!dateLink) { 353 return; 354 } 355 var td = $(dateLink).closest("td"); 356 if (!td) { 357 return; 358 } 359 var nextTd = $(td).next(), 360 nextDateLink = $("a.ui-state-default", nextTd)[0]; 361 362 if (nextTd && nextDateLink) { 363 setHighlightState(nextDateLink, container); 364 nextDateLink.focus(); // the next day (same row) 365 } else { 366 handleNext(dateLink); 367 } 368} 369 370function handleNext(target) { 371 var container = document.getElementById("ui-datepicker-div"); 372 if (!target) { 373 return; 374 } 375 var currentRow = $(target).closest("tr"), 376 nextRow = $(currentRow).next(); 377 378 if (!nextRow || nextRow.length === 0) { 379 nextMonth(); 380 } else { 381 var nextRowFirstDate = $("a.ui-state-default", nextRow)[0]; 382 if (nextRowFirstDate) { 383 setHighlightState(nextRowFirstDate, container); 384 nextRowFirstDate.focus(); 385 } 386 } 387} 388 389function nextMonth() { 390 nextMon = $(".ui-datepicker-next")[0]; 391 var container = document.getElementById("ui-datepicker-div"); 392 nextMon.click(); 393 // focus the first day of the new month 394 setTimeout(function () { 395 // updating the cached header elements 396 updateHeaderElements(); 397 398 var firstDate = $("a.ui-state-default", container)[0]; 399 setHighlightState(firstDate, container); 400 firstDate.focus(); 401 }, 0); 402} 403
404/////////// UP /////////// 405/** 406 * Handle the up arrow navigation through dates 407 * @param {HTMLElement} target The target of the keyboard event (day) 408 * @param {HTMLElement} cont The calendar container 409 * @param {HTMLElement} prevLink Link to navigate to previous month 410 */ 411function upHandler(target, cont, prevLink) { 412 prevLink = $(".ui-datepicker-prev")[0]; 413 var rowContext = $(target).closest("tr"); 414 if (!rowContext) { 415 return; 416 } 417 var rowTds = $("td", rowContext), 418 rowLinks = $("a.ui-state-default", rowContext), 419 targetIndex = $.inArray(target, rowLinks), 420 prevRow = $(rowContext).prev(), 421 prevRowTds = $("td", prevRow), 422 parallel = prevRowTds[targetIndex], 423 linkCheck = $("a.ui-state-default", parallel)[0]; 424 425 if (prevRow && parallel && linkCheck) { 426 // there is a previous row, a td at the same index 427 // of the target AND theres a link in that td 428 setHighlightState(linkCheck, cont); 429 linkCheck.focus(); 430 } else { 431 // we're either on the first row of a month, or we're on the 432 // second and there is not a date link directly above the target 433 prevLink.click(); 434 setTimeout(function () { 435 // updating the cached header elements 436 updateHeaderElements(); 437 var newRows = $("tr", cont), 438 lastRow = newRows[newRows.length - 1], 439 lastRowTds = $("td", lastRow), 440 tdParallelIndex = $.inArray(target.parentNode, rowTds), 441 newParallel = lastRowTds[tdParallelIndex], 442 newCheck = $("a.ui-state-default", newParallel)[0]; 443 444 if (lastRow && newParallel && newCheck) { 445 setHighlightState(newCheck, cont); 446 newCheck.focus(); 447 } else { 448 // theres no date link on the last week (row) of the new month 449 // meaning its an empty cell, so we'll try the 2nd to last week 450 var secondLastRow = newRows[newRows.length - 2], 451 secondTds = $("td", secondLastRow), 452 targetTd = secondTds[tdParallelIndex], 453 linkCheck = $("a.ui-state-default", targetTd)[0]; 454 455 if (linkCheck) { 456 setHighlightState(linkCheck, cont); 457 linkCheck.focus(); 458 } 459 } 460 }, 0); 461 } 462} 463 464//////////////// DOWN //////////////// 465/** 466 * Handles down arrow navigation through dates in calendar 467 * @param {HTMLElement} target The target of the keyboard event (day) 468 * @param {HTMLElement} cont The calendar container 469 * @param {HTMLElement} nextLink Link to navigate to next month 470 */ 471function downHandler(target, cont, nextLink) { 472 nextLink = $(".ui-datepicker-next")[0]; 473 var targetRow = $(target).closest("tr"); 474 if (!targetRow) { 475 return; 476 } 477 var targetCells = $("td", targetRow), 478 cellIndex = $.inArray(target.parentNode, targetCells), // the td (parent of target) index 479 nextRow = $(targetRow).next(), 480 nextRowCells = $("td", nextRow), 481 nextWeekTd = nextRowCells[cellIndex], 482 nextWeekCheck = $("a.ui-state-default", nextWeekTd)[0]; 483 484 if (nextRow && nextWeekTd && nextWeekCheck) { 485 // theres a next row, a TD at the same index of `target`, 486 // and theres an anchor within that td 487 setHighlightState(nextWeekCheck, cont); 488 nextWeekCheck.focus(); 489 } else { 490 nextLink.click(); 491 492 setTimeout(function () { 493 // updating the cached header elements 494 updateHeaderElements(); 495
496 var nextMonthTrs = $("tbody tr", cont), 497 firstTds = $("td", nextMonthTrs[0]), 498 firstParallel = firstTds[cellIndex], 499 firstCheck = $("a.ui-state-default", firstParallel)[0]; 500 501 if (firstParallel && firstCheck) { 502 setHighlightState(firstCheck, cont); 503 firstCheck.focus(); 504 } else { 505 // lets try the second row b/c we didnt find a 506 // date link in the first row at the target's index 507 var secondRow = nextMonthTrs[1], 508 secondTds = $("td", secondRow), 509 secondRowTd = secondTds[cellIndex], 510 secondCheck = $("a.ui-state-default", secondRowTd)[0]; 511 512 if (secondRow && secondCheck) { 513 setHighlightState(secondCheck, cont); 514 secondCheck.focus(); 515 } 516 } 517 }, 0); 518 } 519} 520 521function onCalendarHide() { 522 closeCalendar(); 523} 524 525// add an aria-label to the date link indicating the currently focused date 526// (formatted identically to the required format: mm/dd/yyyy) 527function monthDayYearText() { 528 var cleanUps = $(".amaze-date"); 529 530 $(cleanUps).each(function (clean) { 531 // each(cleanUps, function (clean) { 532 clean.parentNode.removeChild(clean); 533 }); 534 535 var datePickDiv = document.getElementById("ui-datepicker-div"); 536 // in case we find no datepick div 537 if (!datePickDiv) { 538 return; 539 } 540 541 var dates = $("a.ui-state-default", datePickDiv); 542 $(dates) 543 .attr("role", "button") 544 .on("keydown", function (e) { 545 if (e.which === 32) { 546 e.preventDefault(); 547 e.target.click(); 548 setTimeout(function () { 549 closeCalendar(); 550 }, 100); 551 } 552 }); 553 $(dates).each(function (index, date) { 554 var currentRow = $(date).closest("tr"), 555 currentTds = $("td", currentRow), 556 currentIndex = $.inArray(date.parentNode, currentTds), 557 headThs = $("thead tr th", datePickDiv), 558 dayIndex = headThs[currentIndex], 559 daySpan = $("span", dayIndex)[0], 560 monthName = $(".ui-datepicker-month", datePickDiv)[0].innerHTML, 561 year = $(".ui-datepicker-year", datePickDiv)[0].innerHTML, 562 number = date.innerHTML; 563 564 if (!daySpan || !monthName || !number || !year) { 565 return; 566 } 567 568 // AT Reads: {month} {date} {year} {day} 569 // "December 18 2014 Thursday" 570 var dateText = 571 date.innerHTML + " " + monthName + " " + year + " " + daySpan.title; 572 // AT Reads: {date(number)} {name of day} {name of month} {year(number)} 573 // var dateText = date.innerHTML + ' ' + daySpan.title + ' ' + monthName + ' ' + year; 574 // add an aria-label to the date link reading out the currently focused date 575 date.setAttribute("aria-label", dateText); 576 }); 577} 578 579// update the cached header elements because we're in a new month or year 580function updateHeaderElements() { 581 var context = document.getElementById("ui-datepicker-div"); 582 if (!context) { 583 return; 584 } 585 586 // $(context).find('table').first().attr('role', 'grid'); 587 588 prev = $(".ui-datepicker-prev", context)[0]; 589 next = $(".ui-datepicker-next", context)[0]; 590 591 //make them click/focus - able 592 next.href = "javascript:void(0)"; 593 prev.href = "javascript:void(0)"; 594 595 next.setAttribute("role", "button"); 596 prev.setAttribute("role", "button"); 597 appendOffscreenMonthText(next); 598 appendOffscreenMonthText(prev); 599 600 $(next).on("click", handleNextClicks); 601 $(prev).on("click", handlePrevClicks); 602 603 // add month day year text 604 monthDayYearText(); 605} 606 607function prepHighlightState() { 608 var highlight; 609 var cage = document.getElementById("ui-datepicker-div"); 610 highlight = 611 $(".ui-state-highlight", cage)[0] || $(".ui-state-default", cage)[0]; 612 if (highlight && cage) { 613 setHighlightState(highlight, cage); 614 } 615} 616 617// Set the highlighted class to date elements, when focus is received 618function setHighlightState(newHighlight, container) { 619 var prevHighlight = getCurrentDate(container); 620 // remove the highlight state from previously 621 // highlighted date and add it to our newly active date 622 $(prevHighlight).removeClass("ui-state-highlight"); 623 $(prevHighlight).removeAttr("aria-current"); 624 $(newHighlight).attr("aria-current", "date"); 625 $(newHighlight).addClass("ui-state-highlight"); 626} 627 628// grabs the current date based on the highlight class 629function getCurrentDate(container) { 630 var currentDate = $(".ui-state-highlight", container)[0]; 631 return currentDate; 632} 633 634/** 635 * Appends logical next/prev month text to the buttons 636 * - ex: Next Month, January 2015 637 * Previous Month, November 2014 638 */ 639function appendOffscreenMonthText(button) { 640 var buttonText; 641 var isNext = $(button).hasClass("ui-datepicker-next"); 642 var months = [ 643 "january", 644 "february", 645 "march", 646 "april", 647 "may", 648 "june", 649 "july", 650 "august", 651 "september", 652 "october", 653 "november", 654 "december", 655 ]; 656 657 var currentMonth = $(".ui-datepicker-title .ui-datepicker-month") 658 .text() 659 .toLowerCase(); 660 var monthIndex = $.inArray(currentMonth.toLowerCase(), months); 661 var currentYear = $(".ui-datepicker-title .ui-datepicker-year") 662 .text() 663 .toLowerCase(); 664 var adjacentIndex = isNext ? monthIndex + 1 : monthIndex - 1; 665 666 if (isNext && currentMonth === "december") { 667 currentYear = parseInt(currentYear, 10) + 1; 668 adjacentIndex = 0; 669 } else if (!isNext && currentMonth === "january") { 670 currentYear = parseInt(currentYear, 10) - 1; 671 adjacentIndex = months.length - 1; 672 } 673 674 buttonText = isNext 675 ? "Next Month, " + firstToCap(months[adjacentIndex]) + " " + currentYear 676 : "Previous Month, " + 677 firstToCap(months[adjacentIndex]) + 678 " " + 679 currentYear; 680 681 $(button).find(".ui-icon").html(buttonText); 682} 683 684// Returns the string with the first letter capitalized 685function firstToCap(s) { 686 return s.charAt(0).toUpperCase() + s.slice(1); 687} 688 689/* 690 * This content is licensed according to the W3C Software License at 691 * https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 692 * 693 * File: DatePickerDialog.js 694 */ 695 696("use strict"); 697 698class DatePickerDialog { 699 constructor(cdp) { 700 this.buttonLabelChoose = "Choose Date"; 701 this.buttonLabelChange = "Change Date"; 702 this.dayLabels = [ 703 "Sunday", 704 "Monday", 705 "Tuesday", 706 "Wednesday", 707 "Thursday", 708 "Friday", 709 "Saturday", 710 ]; 711 this.monthLabels = [ 712 "January", 713 "February", 714 "March", 715 "April", 716 "May", 717 "June", 718 "July", 719 "August", 720 "September", 721 "October", 722 "November", 723 "December", 724 ]; 725
726 this.messageCursorKeys = "Cursor keys can navigate dates"; 727 this.lastMessage = ""; 728 729 this.textboxNode = cdp.querySelector('input[type="text"'); 730 this.buttonNode = cdp.querySelector(".group button"); 731 this.dialogNode = cdp.querySelector('[role="dialog"]'); 732 this.messageNode = this.dialogNode.querySelector(".dialog-message"); 733 734 this.monthYearNode = this.dialogNode.querySelector(".month-year"); 735 736 this.prevYearNode = this.dialogNode.querySelector(".prev-year"); 737 this.prevMonthNode = this.dialogNode.querySelector(".prev-month"); 738 this.nextMonthNode = this.dialogNode.querySelector(".next-month"); 739 this.nextYearNode = this.dialogNode.querySelector(".next-year"); 740 741 this.okButtonNode = this.dialogNode.querySelector('button[value="ok"]'); 742 this.cancelButtonNode = this.dialogNode.querySelector( 743 'button[value="cancel"]' 744 ); 745 746 this.tbodyNode = this.dialogNode.querySelector("table.dates tbody"); 747 748 this.lastRowNode = null; 749 750 this.days = []; 751 752 this.focusDay = new Date(); 753 this.selectedDay = new Date(0, 0, 1); 754 755 this.lastDate = -1; 756 757 this.isMouseDownOnBackground = false; 758 759 this.textboxNode.addEventListener( 760 "blur", 761 this.setDateForButtonLabel.bind(this) 762 ); 763 764 this.buttonNode.addEventListener( 765 "keydown", 766 this.handleButtonKeydown.bind(this) 767 ); 768 this.buttonNode.addEventListener( 769 "click", 770 this.handleButtonClick.bind(this) 771 ); 772 773 this.okButtonNode.addEventListener("click", this.handleOkButton.bind(this)); 774 this.okButtonNode.addEventListener( 775 "keydown", 776 this.handleOkButton.bind(this) 777 ); 778 779 this.cancelButtonNode.addEventListener( 780 "click", 781 this.handleCancelButton.bind(this) 782 ); 783 this.cancelButtonNode.addEventListener( 784 "keydown", 785 this.handleCancelButton.bind(this) 786 ); 787 788 this.prevMonthNode.addEventListener( 789 "click", 790 this.handlePreviousMonthButton.bind(this) 791 ); 792 this.nextMonthNode.addEventListener( 793 "click", 794 this.handleNextMonthButton.bind(this) 795 ); 796 this.prevYearNode.addEventListener( 797 "click", 798 this.handlePreviousYearButton.bind(this) 799 ); 800 this.nextYearNode.addEventListener( 801 "click", 802 this.handleNextYearButton.bind(this) 803 ); 804 805 this.prevMonthNode.addEventListener( 806 "keydown", 807 this.handlePreviousMonthButton.bind(this) 808 ); 809 this.nextMonthNode.addEventListener( 810 "keydown", 811 this.handleNextMonthButton.bind(this) 812 ); 813 this.prevYearNode.addEventListener( 814 "keydown", 815 this.handlePreviousYearButton.bind(this) 816 ); 817 this.nextYearNode.addEventListener( 818 "keydown", 819 this.handleNextYearButton.bind(this) 820 ); 821 822 document.body.addEventListener( 823 "pointerup", 824 this.handleBackgroundMouseUp.bind(this), 825 true 826 ); 827 828 // Create Grid of Dates 829 830 this.tbodyNode.innerHTML = ""; 831 for (let i = 0; i < 6; i++) { 832 const row = this.tbodyNode.insertRow(i); 833 this.lastRowNode = row; 834 for (let j = 0; j < 7; j++) { 835 const cell = document.createElement("td"); 836 837 cell.tabIndex = -1; 838 cell.addEventListener("click", this.handleDayClick.bind(this)); 839 cell.addEventListener("keydown", this.handleDayKeyDown.bind(this)); 840 cell.addEventListener("focus", this.handleDayFocus.bind(this)); 841 842 cell.textContent = "-1"; 843 844 row.appendChild(cell); 845 this.days.push(cell); 846 } 847 } 848 849 this.updateGrid(); 850 this.close(false); 851 this.setDateForButtonLabel(); 852 } 853 854 isSameDay(day1, day2) { 855 return ( 856 day1.getFullYear() == day2.getFullYear() && 857 day1.getMonth() == day2.getMonth() && 858 day1.getDate() == day2.getDate() 859 ); 860 } 861 862 isNotSameMonth(day1, day2) { 863 return ( 864 day1.getFullYear() != day2.getFullYear() || 865 day1.getMonth() != day2.getMonth() 866 ); 867 } 868 869 updateGrid() { 870 const fd = this.focusDay; 871 872 this.monthYearNode.textContent = 873 this.monthLabels[fd.getMonth()] + " " + fd.getFullYear(); 874 875 let firstDayOfMonth = new Date(fd.getFullYear(), fd.getMonth(), 1); 876 let dayOfWeek = firstDayOfMonth.getDay(); 877 878 firstDayOfMonth.setDate(firstDayOfMonth.getDate() - dayOfWeek); 879 880 const d = new Date(firstDayOfMonth); 881 882 for (let i = 0; i < this.days.length; i++) { 883 const flag = d.getMonth() != fd.getMonth(); 884 this.updateDate( 885 this.days[i], 886 flag, 887 d, 888 this.isSameDay(d, this.selectedDay) 889 ); 890 d.setDate(d.getDate() + 1); 891 892 // Hide last row if all dates are disabled (e.g. in next month) 893 if (i === 35) { 894 if (flag) { 895 this.lastRowNode.style.visibility = "hidden"; 896 } else { 897 this.lastRowNode.style.visibility = "visible"; 898 } 899 } 900 } 901 } 902 903 updateDate(domNode, disable, day, selected) { 904 let d = day.getDate().toString(); 905 if (day.getDate() <= 9) { 906 d = "0" + d; 907 } 908 909 let m = day.getMonth() + 1; 910 if (day.getMonth() < 9) { 911 m = "0" + m; 912 } 913 914 domNode.tabIndex = -1; 915 domNode.removeAttribute("aria-selected"); 916 domNode.setAttribute("data-date", day.getFullYear() + "-" + m + "-" + d); 917 918 if (disable) { 919 domNode.classList.add("disabled"); 920 domNode.textContent = ""; 921 } else { 922 domNode.classList.remove("disabled"); 923 domNode.textContent = day.getDate(); 924 if (selected) { 925 domNode.setAttribute("aria-selected", "true"); 926 domNode.tabIndex = 0; 927 } 928 } 929 } 930 931 moveFocusToDay(day) { 932 const d = this.focusDay; 933 934 this.focusDay = day; 935 936 if ( 937 d.getMonth() != this.focusDay.getMonth() || 938 d.getFullYear() != this.focusDay.getFullYear() 939 ) { 940 this.updateGrid(); 941 } 942 this.setFocusDay(); 943 } 944 945 setFocusDay(flag) { 946 if (typeof flag !== "boolean") { 947 flag = true; 948 } 949 950 for (let i = 0; i < this.days.length; i++) { 951 const dayNode = this.days[i]; 952 const day = this.getDayFromDataDateAttribute(dayNode); 953 954 dayNode.tabIndex = -1; 955 if (this.isSameDay(day, this.focusDay)) { 956 dayNode.tabIndex = 0; 957 if (flag) { 958 dayNode.focus(); 959 } 960 } 961 } 962 } 963 964 open() { 965 this.dialogNode.style.display = "block"; 966 this.dialogNode.style.zIndex = 2; 967 968 this.getDateFromTextbox(); 969 this.updateGrid(); 970 this.lastDate = this.focusDay.getDate(); 971 } 972 973 isOpen() { 974 return window.getComputedStyle(this.dialogNode).display !== "none"; 975 } 976 977 close(flag) { 978 if (typeof flag !== "boolean") { 979 // Default is to move focus to combobox 980 flag = true; 981 } 982 983 this.setMessage(""); 984 this.dialogNode.style.display = "none"; 985 986 if (flag) { 987 this.buttonNode.focus(); 988 } 989 } 990 991 changeMonth(currentDate, numMonths) { 992 const getDays = (year, month) => new Date(year, month, 0).getDate(); 993 994 const isPrev = numMonths < 0; 995 const numYears = Math.trunc(Math.abs(numMonths) / 12); 996 numMonths = Math.abs(numMonths) % 12; 997 998 const newYear = isPrev 999 ? currentDate.getFullYear() - numYears 1000 : currentDate.getFullYear() + numYears; 1001 1002 const newMonth = isPrev 1003 ? currentDate.getMonth() - numMonths 1004 : currentDate.getMonth() + numMonths; 1005 1006 const newDate = new Date(newYear, newMonth, 1); 1007 1008 const daysInMonth = getDays(newDate.getFullYear(), newDate.getMonth() + 1); 1009 1010 // If lastDat is not initialized set to current date 1011 this.lastDate = this.lastDate ? this.lastDate : currentDate.getDate(); 1012 1013 if (this.lastDate > daysInMonth) { 1014 newDate.setDate(daysInMonth); 1015 } else { 1016 newDate.setDate(this.lastDate); 1017 } 1018 1019 return newDate; 1020 } 1021 1022 moveToNextYear() { 1023 this.focusDay = this.changeMonth(this.focusDay, 12); 1024 this.updateGrid(); 1025 } 1026 1027 moveToPreviousYear() { 1028 this.focusDay = this.changeMonth(this.focusDay, -12); 1029 this.updateGrid(); 1030 } 1031 1032 moveToNextMonth() { 1033 this.focusDay = this.changeMonth(this.focusDay, 1); 1034 this.updateGrid(); 1035 } 1036 1037 moveToPreviousMonth() { 1038 this.focusDay = this.changeMonth(this.focusDay, -1); 1039 this.updateGrid(); 1040 } 1041 1042 moveFocusToNextDay() { 1043 const d = new Date(this.focusDay); 1044 d.setDate(d.getDate() + 1); 1045 this.lastDate = d.getDate(); 1046 this.moveFocusToDay(d); 1047 } 1048 1049 moveFocusToNextWeek() { 1050 const d = new Date(this.focusDay); 1051 d.setDate(d.getDate() + 7); 1052 this.lastDate = d.getDate(); 1053 this.moveFocusToDay(d); 1054 } 1055 1056 moveFocusToPreviousDay() { 1057 const d = new Date(this.focusDay); 1058 d.setDate(d.getDate() - 1); 1059 this.lastDate = d.getDate(); 1060 this.moveFocusToDay(d); 1061 } 1062 1063 moveFocusToPreviousWeek() { 1064 const d = new Date(this.focusDay); 1065 d.setDate(d.getDate() - 7); 1066 this.lastDate = d.getDate(); 1067 this.moveFocusToDay(d); 1068 } 1069 1070 moveFocusToFirstDayOfWeek() { 1071 const d = new Date(this.focusDay); 1072 d.setDate(d.getDate() - d.getDay()); 1073 this.lastDate = d.getDate(); 1074 this.moveFocusToDay(d); 1075 } 1076 1077 moveFocusToLastDayOfWeek() { 1078 const d = new Date(this.focusDay); 1079 d.setDate(d.getDate() + (6 - d.getDay())); 1080 this.lastDate = d.getDate(); 1081 this.moveFocusToDay(d); 1082 } 1083 1084 // Day methods 1085 1086 isDayDisabled(domNode) { 1087 return domNode.classList.contains("disabled"); 1088 } 1089 1090 getDayFromDataDateAttribute(domNode) { 1091 const parts = domNode.getAttribute("data-date").split("-"); 1092 return new Date(parts[0], parseInt(parts[1]) - 1, parts[2]); 1093 } 1094 1095 // Textbox methods 1096 1097 setTextboxDate(domNode) { 1098 let d = this.focusDay; 1099 1100 if (domNode) { 1101 d = this.getDayFromDataDateAttribute(domNode); 1102 // updated aria-selected 1103 this.days.forEach((day) => 1104 day === domNode 1105 ? day.setAttribute("aria-selected", "true") 1106 : day.removeAttribute("aria-selected") 1107 ); 1108 } 1109 1110 this.textboxNode.value = 1111 d.getMonth() + 1 + "/" + d.getDate() + "/" + d.getFullYear(); 1112 this.setDateForButtonLabel(); 1113 } 1114 1115 getDateFromTextbox() { 1116 const parts = this.textboxNode.value.split("/");
1117 const month = parseInt(parts[0]); 1118 const day = parseInt(parts[1]); 1119 let year = parseInt(parts[2]); 1120 1121 if ( 1122 parts.length === 3 && 1123 Number.isInteger(month) && 1124 Number.isInteger(day) && 1125 Number.isInteger(year) 1126 ) { 1127 if (year < 100) { 1128 year = 2000 + year; 1129 } 1130 this.focusDay = new Date(year, month - 1, day); 1131 this.selectedDay = new Date(this.focusDay); 1132 } else { 1133 // If not a valid date (MM/DD/YY) initialize with todays date 1134 this.focusDay = new Date(); 1135 this.selectedDay = new Date(0, 0, 1); 1136 } 1137 } 1138 1139 setDateForButtonLabel() { 1140 const parts = this.textboxNode.value.split("/"); 1141 1142 if ( 1143 parts.length === 3 && 1144 Number.isInteger(parseInt(parts[0])) && 1145 Number.isInteger(parseInt(parts[1])) && 1146 Number.isInteger(parseInt(parts[2])) 1147 ) { 1148 const day = new Date( 1149 parseInt(parts[2]), 1150 parseInt(parts[0]) - 1, 1151 parseInt(parts[1]) 1152 ); 1153 1154 let label = this.buttonLabelChange; 1155 label += ", " + this.dayLabels[day.getDay()]; 1156 label += " " + this.monthLabels[day.getMonth()]; 1157 label += " " + day.getDate(); 1158 label += ", " + day.getFullYear(); 1159 this.buttonNode.setAttribute("aria-label", label); 1160 } else { 1161 // If not a valid date, initialize with "Choose Date" 1162 this.buttonNode.setAttribute("aria-label", this.buttonLabelChoose); 1163 } 1164 } 1165 1166 setMessage(str) { 1167 function setMessageDelayed() { 1168 this.messageNode.textContent = str; 1169 } 1170 1171 if (str !== this.lastMessage) { 1172 setTimeout(setMessageDelayed.bind(this), 200); 1173 this.lastMessage = str; 1174 } 1175 } 1176 1177 // Event handlers 1178 1179 handleOkButton(event) { 1180 let flag = false; 1181 1182 switch (event.type) { 1183 case "keydown": 1184 switch (event.key) { 1185 case "Tab": 1186 if (!event.shiftKey) { 1187 this.prevYearNode.focus(); 1188 flag = true; 1189 } 1190 break; 1191 1192 case "Esc": 1193 case "Escape": 1194 this.close(); 1195 flag = true; 1196 break; 1197 1198 default: 1199 break; 1200 } 1201 break; 1202 1203 case "click": 1204 this.setTextboxDate(); 1205 this.close(); 1206 flag = true; 1207 break; 1208 1209 default: 1210 break; 1211 } 1212 1213 if (flag) { 1214 event.stopPropagation(); 1215 event.preventDefault(); 1216 } 1217 } 1218 1219 handleCancelButton(event) { 1220 let flag = false; 1221 1222 switch (event.type) { 1223 case "keydown": 1224 switch (event.key) { 1225 case "Esc": 1226 case "Escape": 1227 this.close(); 1228 flag = true; 1229 break; 1230 1231 default: 1232 break; 1233 } 1234 break; 1235 1236 case "click": 1237 this.close(); 1238 flag = true; 1239 break; 1240 1241 default: 1242 break; 1243 } 1244 1245 if (flag) { 1246 event.stopPropagation(); 1247 event.preventDefault(); 1248 } 1249 } 1250 1251 handleNextYearButton(event) { 1252 let flag = false; 1253 1254 switch (event.type) { 1255 case "keydown": 1256 switch (event.key) { 1257 case "Esc": 1258 case "Escape": 1259 this.close(); 1260 flag = true; 1261 break; 1262 1263 case "Enter": 1264 this.moveToNextYear(); 1265 this.setFocusDay(false); 1266 flag = true; 1267 break; 1268 } 1269 1270 break; 1271 1272 case "click": 1273 this.moveToNextYear(); 1274 this.setFocusDay(false); 1275 break; 1276 1277 default: 1278 break; 1279 } 1280 1281 if (flag) { 1282 event.stopPropagation(); 1283 event.preventDefault(); 1284 } 1285 } 1286 1287 handlePreviousYearButton(event) { 1288 let flag = false; 1289 1290 switch (event.type) { 1291 case "keydown": 1292 switch (event.key) { 1293 case "Enter": 1294 this.moveToPreviousYear(); 1295 this.setFocusDay(false); 1296 flag = true; 1297 break; 1298 1299 case "Tab": 1300 if (event.shiftKey) { 1301 this.okButtonNode.focus(); 1302 flag = true; 1303 } 1304 break; 1305 1306 case "Esc": 1307 case "Escape": 1308 this.close(); 1309 flag = true; 1310 break; 1311 1312 default: 1313 break; 1314 } 1315 1316 break; 1317 1318 case "click": 1319 this.moveToPreviousYear(); 1320 this.setFocusDay(false); 1321 break; 1322 1323 default: 1324 break; 1325 } 1326 1327 if (flag) { 1328 event.stopPropagation(); 1329 event.preventDefault(); 1330 } 1331 } 1332 1333 handleNextMonthButton(event) { 1334 let flag = false; 1335 1336 switch (event.type) { 1337 case "keydown": 1338 switch (event.key) { 1339 case "Esc": 1340 case "Escape": 1341 this.close(); 1342 flag = true; 1343 break; 1344 1345 case "Enter": 1346 this.moveToNextMonth(); 1347 this.setFocusDay(false); 1348 flag = true; 1349 break; 1350 } 1351 1352 break; 1353 1354 case "click": 1355 this.moveToNextMonth(); 1356 this.setFocusDay(false); 1357 break; 1358 1359 default: 1360 break; 1361 } 1362 1363 if (flag) { 1364 event.stopPropagation(); 1365 event.preventDefault(); 1366 } 1367 } 1368 1369 handlePreviousMonthButton(event) { 1370 let flag = false; 1371 1372 switch (event.type) { 1373 case "keydown": 1374 switch (event.key) { 1375 case "Esc": 1376 case "Escape": 1377 this.close(); 1378 flag = true; 1379 break; 1380 1381 case "Enter": 1382 this.moveToPreviousMonth(); 1383 this.setFocusDay(false); 1384 flag = true; 1385 break; 1386 } 1387 1388 break; 1389 1390 case "click": 1391 this.moveToPreviousMonth(); 1392 this.setFocusDay(false); 1393 flag = true; 1394 break; 1395 1396 default: 1397 break; 1398 } 1399 1400 if (flag) { 1401 event.stopPropagation(); 1402 event.preventDefault(); 1403 } 1404 } 1405 1406 handleDayKeyDown(event) { 1407 let flag = false; 1408 1409 switch (event.key) { 1410 case "Esc": 1411 case "Escape": 1412 this.close(); 1413 break; 1414 1415 case " ": 1416 this.setTextboxDate(event.currentTarget); 1417 flag = true; 1418 break; 1419 1420 case "Enter": 1421 this.setTextboxDate(event.currentTarget); 1422 this.close(); 1423 flag = true; 1424 break; 1425 1426 case "Tab": 1427 this.cancelButtonNode.focus(); 1428 if (event.shiftKey) { 1429 this.nextYearNode.focus(); 1430 } 1431 this.setMessage(""); 1432 flag = true; 1433 break; 1434 1435 case "Right": 1436 case "ArrowRight": 1437 this.moveFocusToNextDay(); 1438 flag = true; 1439 break; 1440 1441 case "Left": 1442 case "ArrowLeft": 1443 this.moveFocusToPreviousDay(); 1444 flag = true; 1445 break; 1446 1447 case "Down": 1448 case "ArrowDown": 1449 this.moveFocusToNextWeek(); 1450 flag = true; 1451 break; 1452 1453 case "Up": 1454 case "ArrowUp": 1455 this.moveFocusToPreviousWeek(); 1456 flag = true; 1457 break; 1458 1459 case "PageUp": 1460 if (event.shiftKey) { 1461 this.moveToPreviousYear(); 1462 } else { 1463 this.moveToPreviousMonth(); 1464 } 1465 this.setFocusDay(); 1466 flag = true; 1467 break; 1468 1469 case "PageDown": 1470 if (event.shiftKey) { 1471 this.moveToNextYear(); 1472 } else { 1473 this.moveToNextMonth(); 1474 } 1475 this.setFocusDay(); 1476 flag = true; 1477 break; 1478 1479 case "Home": 1480 this.moveFocusToFirstDayOfWeek(); 1481 flag = true; 1482 break; 1483 1484 case "End": 1485 this.moveFocusToLastDayOfWeek(); 1486 flag = true; 1487 break; 1488 } 1489 1490 if (flag) { 1491 event.stopPropagation(); 1492 event.preventDefault(); 1493 } 1494 } 1495 1496 handleDayClick(event) { 1497 if (!this.isDayDisabled(event.currentTarget) && event.which !== 3) { 1498 this.setTextboxDate(event.currentTarget); 1499 this.close(); 1500 } 1501 1502 event.stopPropagation(); 1503 event.preventDefault(); 1504 } 1505 1506 handleDayFocus() { 1507 this.setMessage(this.messageCursorKeys); 1508 } 1509 1510 handleButtonKeydown(event) { 1511 if (event.key === "Enter" || event.key === " ") { 1512 this.open(); 1513 this.setFocusDay(); 1514 1515 event.stopPropagation(); 1516 event.preventDefault(); 1517 } 1518 } 1519 1520 handleButtonClick(event) { 1521 if (this.isOpen()) { 1522 this.close(); 1523 } else { 1524 this.open(); 1525 this.setFocusDay(); 1526 } 1527 1528 event.stopPropagation(); 1529 event.preventDefault(); 1530 } 1531
1532 handleBackgroundMouseUp(event) { 1533 if ( 1534 !this.buttonNode.contains(event.target) && 1535 !this.dialogNode.contains(event.target) 1536 ) { 1537 if (this.isOpen()) { 1538 this.close(false); 1539 event.stopPropagation(); 1540 event.preventDefault(); 1541 } 1542 } 1543 } 1544} 1545 1546// Initialize menu button date picker 1547 1548window.addEventListener("load", function () { 1549 const datePickers = document.querySelectorAll(".datepicker-access"); 1550 1551 datePickers.forEach(function (dp) { 1552 new DatePickerDialog(dp); 1553 }); 1554});
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.