You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

jquery-ui-accessible-datepicker.js 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /*! jQuery UI Accessible Datepicker extension
  2. * (to be appended to jquery-ui-*.custom.min.js)
  3. *
  4. * @licstart The following is the entire license notice for the
  5. * JavaScript code in this page.
  6. *
  7. * Copyright 2014 Kolab Systems AG
  8. *
  9. * The JavaScript code in this page is free software: you can
  10. * redistribute it and/or modify it under the terms of the GNU
  11. * General Public License (GNU GPL) as published by the Free Software
  12. * Foundation, either version 3 of the License, or (at your option)
  13. * any later version. The code is distributed WITHOUT ANY WARRANTY;
  14. * without even the implied warranty of MERCHANTABILITY or FITNESS
  15. * FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
  16. *
  17. * As additional permission under GNU GPL version 3 section 7, you
  18. * may distribute non-source (e.g., minimized or compacted) forms of
  19. * that code without the copy of the GNU GPL normally required by
  20. * section 4, provided you include this license notice and a URL
  21. * through which recipients can access the Corresponding Source.
  22. *
  23. * @licend The above is the entire license notice
  24. * for the JavaScript code in this page.
  25. */
  26. (function($, undefined) {
  27. // references to super class methods
  28. var __newInst = $.datepicker._newInst;
  29. var __updateDatepicker = $.datepicker._updateDatepicker;
  30. var __connectDatepicker = $.datepicker._connectDatepicker;
  31. var __showDatepicker = $.datepicker._showDatepicker;
  32. var __hideDatepicker = $.datepicker._hideDatepicker;
  33. // "extend" singleton instance methods
  34. $.extend($.datepicker, {
  35. /* Create a new instance object */
  36. _newInst: function(target, inline) {
  37. var that = this, inst = __newInst.call(this, target, inline);
  38. if (inst.inline) {
  39. // attach keyboard event handler
  40. inst.dpDiv.on('keydown.datepicker', '.ui-datepicker-calendar', function(event) {
  41. // we're only interested navigation keys
  42. if ($.inArray(event.keyCode, [ 13, 33, 34, 35, 36, 37, 38, 39, 40]) == -1) {
  43. return;
  44. }
  45. event.stopPropagation();
  46. event.preventDefault();
  47. inst._hasfocus = true;
  48. var activeCell;
  49. switch (event.keyCode) {
  50. case $.ui.keyCode.ENTER:
  51. if ((activeCell = $('.' + that._dayOverClass, inst.dpDiv).get(0) || $('.' + that._currentClass, inst.dpDiv).get(0))) {
  52. that._selectDay(inst.input, inst.selectedMonth, inst.selectedYear, activeCell);
  53. }
  54. break;
  55. case $.ui.keyCode.PAGE_UP:
  56. that._adjustDate(inst.input, -that._get(inst, 'stepMonths'), 'M');
  57. break;
  58. case $.ui.keyCode.PAGE_DOWN:
  59. that._adjustDate(inst.input, that._get(inst, 'stepMonths'), 'M');
  60. break;
  61. default:
  62. return that._cursorKeydown(event, inst);
  63. }
  64. })
  65. .attr('role', 'region')
  66. .attr('aria-labelledby', inst.id + '-dp-title');
  67. }
  68. else {
  69. var widgetId = inst.dpDiv.attr('id') || inst.id + '-dp-widget';
  70. inst.dpDiv.attr('id', widgetId)
  71. .attr('aria-hidden', 'true')
  72. .attr('aria-labelledby', inst.id + '-dp-title');
  73. $(inst.input).attr('aria-haspopup', 'true')
  74. .attr('aria-expanded', 'false')
  75. .attr('aria-owns', widgetId);
  76. }
  77. return inst;
  78. },
  79. /* Attach the date picker to an input field */
  80. _connectDatepicker: function(target, inst) {
  81. __connectDatepicker.call(this, target, inst);
  82. var that = this;
  83. // register additional keyboard events to control date selection with cursor keys
  84. $(target).unbind('keydown.datepicker-extended').bind('keydown.datepicker-extended', function(event) {
  85. var inc = 1;
  86. switch (event.keyCode) {
  87. case 109:
  88. case 173:
  89. case 189: // "minus"
  90. inc = -1;
  91. case 61:
  92. case 107:
  93. case 187: // "plus"
  94. // do nothing if the input does not contain full date string
  95. if (this.value.length < that._formatDate(inst, inst.selectedDay, inst.selectedMonth, inst.selectedYear).length) {
  96. return true;
  97. }
  98. that._adjustInstDate(inst, inc, 'D');
  99. that._selectDateRC(target, that._formatDate(inst, inst.selectedDay, inst.selectedMonth, inst.selectedYear));
  100. return false;
  101. case $.ui.keyCode.UP:
  102. case $.ui.keyCode.DOWN:
  103. // unfold datepicker if not visible
  104. if ($.datepicker._lastInput !== target && !$.datepicker._isDisabledDatepicker(target)) {
  105. that._showDatepicker(event);
  106. event.stopPropagation();
  107. event.preventDefault();
  108. return false;
  109. }
  110. default:
  111. if (!$.datepicker._isDisabledDatepicker(target) && !event.ctrlKey && !event.metaKey) {
  112. return that._cursorKeydown(event, inst);
  113. }
  114. }
  115. })
  116. // fix https://bugs.jqueryui.com/ticket/8593
  117. .click(function (event) { that._showDatepicker(event); })
  118. .attr('autocomplete', 'off');
  119. },
  120. /* Handle keyboard event on datepicker widget */
  121. _cursorKeydown: function(event, inst) {
  122. inst._keyEvent = true;
  123. var isRTL = inst.dpDiv.hasClass('ui-datepicker-rtl');
  124. switch (event.keyCode) {
  125. case $.ui.keyCode.LEFT:
  126. this._adjustDate(inst.input, (isRTL ? +1 : -1), 'D');
  127. break;
  128. case $.ui.keyCode.RIGHT:
  129. this._adjustDate(inst.input, (isRTL ? -1 : +1), 'D');
  130. break;
  131. case $.ui.keyCode.UP:
  132. this._adjustDate(inst.input, -7, 'D');
  133. break;
  134. case $.ui.keyCode.DOWN:
  135. this._adjustDate(inst.input, +7, 'D');
  136. break;
  137. case $.ui.keyCode.HOME:
  138. // TODO: jump to first of month
  139. break;
  140. case $.ui.keyCode.END:
  141. // TODO: jump to end of month
  142. break;
  143. }
  144. return true;
  145. },
  146. /* Pop-up the date picker for a given input field */
  147. _showDatepicker: function(input) {
  148. input = input.target || input;
  149. __showDatepicker.call(this, input);
  150. var inst = $.datepicker._getInst(input);
  151. if (inst && $.datepicker._datepickerShowing) {
  152. inst.dpDiv.attr('aria-hidden', 'false');
  153. $(input).attr('aria-expanded', 'true');
  154. }
  155. },
  156. /* Hide the date picker from view */
  157. _hideDatepicker: function(input) {
  158. __hideDatepicker.call(this, input);
  159. var inst = this._curInst;
  160. if (inst && !$.datepicker._datepickerShowing) {
  161. inst.dpDiv.attr('aria-hidden', 'true');
  162. $(inst.input).attr('aria-expanded', 'false');
  163. }
  164. },
  165. /* Render the date picker content */
  166. _updateDatepicker: function(inst) {
  167. __updateDatepicker.call(this, inst);
  168. var activeCell = $('.' + this._dayOverClass, inst.dpDiv).get(0) || $('.' + this._currentClass, inst.dpDiv).get(0);
  169. if (activeCell) {
  170. activeCell = $(activeCell);
  171. activeCell.attr('id', inst.id + '-day-' + activeCell.text());
  172. }
  173. // allow focus on main container only
  174. inst.dpDiv.find('.ui-datepicker-calendar')
  175. .attr('tabindex', inst.inline ? '0' : '-1')
  176. .attr('role', 'grid')
  177. .attr('aria-readonly', 'true')
  178. .attr('aria-activedescendant', activeCell ? activeCell.attr('id') : '')
  179. .find('td').attr('role', 'gridcell').attr('aria-selected', 'false')
  180. .find('a').attr('tabindex', '-1');
  181. $('.ui-datepicker-current-day', inst.dpDiv).attr('aria-selected', 'true');
  182. inst.dpDiv.find('.ui-datepicker-title')
  183. .attr('id', inst.id + '-dp-title')
  184. // set focus again after update
  185. if (inst._hasfocus) {
  186. inst.dpDiv.find('.ui-datepicker-calendar').focus();
  187. inst._hasfocus = false;
  188. }
  189. },
  190. _selectDateRC: function(id, dateStr) {
  191. var target = $(id), inst = this._getInst(target[0]);
  192. dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
  193. if (inst.input) {
  194. inst.input.val(dateStr);
  195. }
  196. this._updateAlternate(inst);
  197. if (inst.input) {
  198. inst.input.trigger("change"); // fire the change event
  199. }
  200. if (inst.inline) {
  201. this._updateDatepicker(inst);
  202. }
  203. }
  204. });
  205. }(jQuery));