summaryrefslogtreecommitdiff
path: root/jqueryui/js/jquery-ui-accessible-datepicker.js
diff options
context:
space:
mode:
Diffstat (limited to 'jqueryui/js/jquery-ui-accessible-datepicker.js')
-rw-r--r--jqueryui/js/jquery-ui-accessible-datepicker.js235
1 files changed, 235 insertions, 0 deletions
diff --git a/jqueryui/js/jquery-ui-accessible-datepicker.js b/jqueryui/js/jquery-ui-accessible-datepicker.js
new file mode 100644
index 0000000..ef7561c
--- /dev/null
+++ b/jqueryui/js/jquery-ui-accessible-datepicker.js
@@ -0,0 +1,235 @@
+/*! jQuery UI Accessible Datepicker extension
+* (to be appended to jquery-ui-*.custom.min.js)
+*
+* @licstart The following is the entire license notice for the
+* JavaScript code in this page.
+*
+* Copyright 2014 Kolab Systems AG
+*
+* The JavaScript code in this page is free software: you can
+* redistribute it and/or modify it under the terms of the GNU
+* General Public License (GNU GPL) as published by the Free Software
+* Foundation, either version 3 of the License, or (at your option)
+* any later version. The code is distributed WITHOUT ANY WARRANTY;
+* without even the implied warranty of MERCHANTABILITY or FITNESS
+* FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
+*
+* As additional permission under GNU GPL version 3 section 7, you
+* may distribute non-source (e.g., minimized or compacted) forms of
+* that code without the copy of the GNU GPL normally required by
+* section 4, provided you include this license notice and a URL
+* through which recipients can access the Corresponding Source.
+*
+* @licend The above is the entire license notice
+* for the JavaScript code in this page.
+*/
+
+(function($, undefined) {
+
+// references to super class methods
+var __newInst = $.datepicker._newInst;
+var __updateDatepicker = $.datepicker._updateDatepicker;
+var __connectDatepicker = $.datepicker._connectDatepicker;
+var __showDatepicker = $.datepicker._showDatepicker;
+var __hideDatepicker = $.datepicker._hideDatepicker;
+
+// "extend" singleton instance methods
+$.extend($.datepicker, {
+
+ /* Create a new instance object */
+ _newInst: function(target, inline) {
+ var that = this, inst = __newInst.call(this, target, inline);
+
+ if (inst.inline) {
+ // attach keyboard event handler
+ inst.dpDiv.on('keydown.datepicker', '.ui-datepicker-calendar', function(event) {
+ // we're only interested navigation keys
+ if ($.inArray(event.keyCode, [ 13, 33, 34, 35, 36, 37, 38, 39, 40]) == -1) {
+ return;
+ }
+ event.stopPropagation();
+ event.preventDefault();
+ inst._hasfocus = true;
+
+ var activeCell;
+ switch (event.keyCode) {
+ case $.ui.keyCode.ENTER:
+ if ((activeCell = $('.' + that._dayOverClass, inst.dpDiv).get(0) || $('.' + that._currentClass, inst.dpDiv).get(0))) {
+ that._selectDay(inst.input, inst.selectedMonth, inst.selectedYear, activeCell);
+ }
+ break;
+
+ case $.ui.keyCode.PAGE_UP:
+ that._adjustDate(inst.input, -that._get(inst, 'stepMonths'), 'M');
+ break;
+ case $.ui.keyCode.PAGE_DOWN:
+ that._adjustDate(inst.input, that._get(inst, 'stepMonths'), 'M');
+ break;
+
+ default:
+ return that._cursorKeydown(event, inst);
+ }
+ })
+ .attr('role', 'region')
+ .attr('aria-labelledby', inst.id + '-dp-title');
+ }
+ else {
+ var widgetId = inst.dpDiv.attr('id') || inst.id + '-dp-widget';
+ inst.dpDiv.attr('id', widgetId)
+ .attr('aria-hidden', 'true')
+ .attr('aria-labelledby', inst.id + '-dp-title');
+
+ $(inst.input).attr('aria-haspopup', 'true')
+ .attr('aria-expanded', 'false')
+ .attr('aria-owns', widgetId);
+ }
+
+ return inst;
+ },
+
+ /* Attach the date picker to an input field */
+ _connectDatepicker: function(target, inst) {
+ __connectDatepicker.call(this, target, inst);
+
+ var that = this;
+
+ // register additional keyboard events to control date selection with cursor keys
+ $(target).unbind('keydown.datepicker-extended').bind('keydown.datepicker-extended', function(event) {
+ var inc = 1;
+ switch (event.keyCode) {
+ case 109:
+ case 173:
+ case 189: // "minus"
+ inc = -1;
+ case 61:
+ case 107:
+ case 187: // "plus"
+ // do nothing if the input does not contain full date string
+ if (this.value.length < that._formatDate(inst, inst.selectedDay, inst.selectedMonth, inst.selectedYear).length) {
+ return true;
+ }
+ that._adjustInstDate(inst, inc, 'D');
+ that._selectDateRC(target, that._formatDate(inst, inst.selectedDay, inst.selectedMonth, inst.selectedYear));
+ return false;
+
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.DOWN:
+ // unfold datepicker if not visible
+ if ($.datepicker._lastInput !== target && !$.datepicker._isDisabledDatepicker(target)) {
+ that._showDatepicker(event);
+ event.stopPropagation();
+ event.preventDefault();
+ return false;
+ }
+
+ default:
+ if (!$.datepicker._isDisabledDatepicker(target) && !event.ctrlKey && !event.metaKey) {
+ return that._cursorKeydown(event, inst);
+ }
+ }
+ })
+ .attr('autocomplete', 'off');
+ },
+
+ /* Handle keyboard event on datepicker widget */
+ _cursorKeydown: function(event, inst) {
+ inst._keyEvent = true;
+
+ var isRTL = inst.dpDiv.hasClass('ui-datepicker-rtl');
+
+ switch (event.keyCode) {
+ case $.ui.keyCode.LEFT:
+ this._adjustDate(inst.input, (isRTL ? +1 : -1), 'D');
+ break;
+ case $.ui.keyCode.RIGHT:
+ this._adjustDate(inst.input, (isRTL ? -1 : +1), 'D');
+ break;
+ case $.ui.keyCode.UP:
+ this._adjustDate(inst.input, -7, 'D');
+ break;
+ case $.ui.keyCode.DOWN:
+ this._adjustDate(inst.input, +7, 'D');
+ break;
+ case $.ui.keyCode.HOME:
+ // TODO: jump to first of month
+ break;
+ case $.ui.keyCode.END:
+ // TODO: jump to end of month
+ break;
+ }
+
+ return true;
+ },
+
+ /* Pop-up the date picker for a given input field */
+ _showDatepicker: function(input) {
+ input = input.target || input;
+ __showDatepicker.call(this, input);
+
+ var inst = $.datepicker._getInst(input);
+ if (inst && $.datepicker._datepickerShowing) {
+ inst.dpDiv.attr('aria-hidden', 'false');
+ $(input).attr('aria-expanded', 'true');
+ }
+ },
+
+ /* Hide the date picker from view */
+ _hideDatepicker: function(input) {
+ __hideDatepicker.call(this, input);
+
+ var inst = this._curInst;;
+ if (inst && !$.datepicker._datepickerShowing) {
+ inst.dpDiv.attr('aria-hidden', 'true');
+ $(inst.input).attr('aria-expanded', 'false');
+ }
+ },
+
+ /* Render the date picker content */
+ _updateDatepicker: function(inst) {
+ __updateDatepicker.call(this, inst);
+
+ var activeCell = $('.' + this._dayOverClass, inst.dpDiv).get(0) || $('.' + this._currentClass, inst.dpDiv).get(0);
+ if (activeCell) {
+ activeCell = $(activeCell);
+ activeCell.attr('id', inst.id + '-day-' + activeCell.text());
+ }
+
+ // allow focus on main container only
+ inst.dpDiv.find('.ui-datepicker-calendar')
+ .attr('tabindex', inst.inline ? '0' : '-1')
+ .attr('role', 'grid')
+ .attr('aria-readonly', 'true')
+ .attr('aria-activedescendant', activeCell ? activeCell.attr('id') : '')
+ .find('td').attr('role', 'gridcell').attr('aria-selected', 'false')
+ .find('a').attr('tabindex', '-1');
+
+ $('.ui-datepicker-current-day', inst.dpDiv).attr('aria-selected', 'true');
+
+ inst.dpDiv.find('.ui-datepicker-title')
+ .attr('id', inst.id + '-dp-title')
+
+ // set focus again after update
+ if (inst._hasfocus) {
+ inst.dpDiv.find('.ui-datepicker-calendar').focus();
+ inst._hasfocus = false;
+ }
+ },
+
+ _selectDateRC: function(id, dateStr) {
+ var target = $(id), inst = this._getInst(target[0]);
+
+ dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
+ if (inst.input) {
+ inst.input.val(dateStr);
+ }
+ this._updateAlternate(inst);
+ if (inst.input) {
+ inst.input.trigger("change"); // fire the change event
+ }
+ if (inst.inline) {
+ this._updateDatepicker(inst);
+ }
+ }
+});
+
+}(jQuery));