Saturday, April 09, 2011

Extending the jQuery UI datepicker with new shortcuts

The jQuery UI datepicker has a set of default shortcuts to move to the next/previous month (PageDown/PageUp), to today's date (Ctrl+Home), etc. After working with QuickBooks for so long, I got used to the QuickBooks date control shortcuts that use letters instead of Ctrl+key combination like the jQuery UI datepicker. These shortcuts are very intuitive, e.g. the letter W is used for the first day of the Week and the letter K is used for the last day of the weeK. Here, we will show how to extend the datepicker to support the additional shortcuts below.

Shortcut Description
+ Next Day
- Previous Day
T Today
W First day of the Week
K Last day of the weeK
M First day of the Month
H Last day of the montH
Y First day of the Year
R Last day of the yeaR

The ideal solution would be to use the widget factory to subclass the datepicker and extend the _doKeyPress function with the new shortcuts. However, the datepicker does not use the widget factory in jquery-ui 1.8, see more details here. The solution is to extend the datepicker with a new function customKeyPress and then bind it to the datepicker keypress event.
/*!
 * Extensions for jQuery UI 1.8.7 datepicker
 *
 * Copyright 2011, Luis Rocha
 * Released under the MIT license.
 *
 */

// Extends datepicker with shortcuts for today, begin and end of the year and month.
$.extend($.datepicker, { customKeyPress: function (event) {
    var inst = $.datepicker._getInst(event.target);
    var c = String.fromCharCode(event.which).toLowerCase();
    switch (c) {
        case "y":
            // First day of the (y)ear.
            if (inst.selectedDay == 1 && inst.selectedMonth == 0) {
                // First day of previous year.
                $.datepicker._adjustDate(event.target, -12, "M");
            } else if (inst.selectedMonth == 0) {
                // First day of the current year.
                $.datepicker._adjustDate(event.target, 1 - inst.selectedDay, "D");
            } else {
                // First day of the current year.
                inst.selectedDay = 1;
                $.datepicker._adjustDate(event.target, -inst.selectedMonth, "M");
            }
            break;
        case "r":
            // Last day of the yea(r).
            if (inst.selectedDay == 31 && inst.selectedMonth == 11) {
                // Last day of next year.
                $.datepicker._adjustDate(event.target, +12, "M");
            } else if (inst.selectedMonth == 11) {
                // Last day of current year.
                $.datepicker._adjustDate(event.target, 31 - inst.selectedDay, "D");
            } else {
                // Last day of current year.
                inst.selectedDay = 31;
                $.datepicker._adjustDate(event.target, 11 - inst.selectedMonth, "M");
            }
            break;
        case "m":
            // First day of the (m)onth.
            if (inst.selectedDay == 1) {
                $.datepicker._adjustDate(event.target, -1, "M");
            } else {
                $.datepicker._adjustDate(event.target, 1 - inst.selectedDay, "D");
            }
            break;
        case "h":
            // Last day of the mont(h).
            var end = $.datepicker._getDaysInMonth(inst.selectedYear, inst.selectedMonth);
            if (inst.selectedDay == end) {
                var month = (inst.selectedMonth + 1) % 11;
                var year = inst.selectedYear + Math.floor((inst.selectedMonth + 1) / 11);
                inst.selectedDay = $.datepicker._getDaysInMonth(year, month);
                $.datepicker._adjustDate(event.target, +1, "M");
            } else {
                $.datepicker._adjustDate(event.target, end - inst.selectedDay, "D");
            }
            break;
        case "w":
            // First day of the (w)eek.
            var date = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay);
            var offset = (date.getDay() > 0 ? date.getDay() : 7);
            $.datepicker._adjustDate(event.target, -offset, "D");
            break;
        case "k":
            // Last day of the wee(k).
            var date = new Date(inst.selectedYear, inst.selectedMonth, inst.selectedDay);
            var offset = (date.getDay() < 6 ? 6 - date.getDay() : 7);
            $.datepicker._adjustDate(event.target, offset, "D");
            break;
        case "t":
            // Today (same as Ctrl+Home).
            $.datepicker._gotoToday(event.target);
            break;
        case "+":
            // Increase day (same as Ctrl+Right).
            $.datepicker._adjustDate(event.target, +1, 'D');
            break;
        case "-":
            // Decrease day (same as Ctrl+Left).
            $.datepicker._adjustDate(event.target, -1, 'D');
            break;
    }
}
});
The following code snippet shows how to bind the customKeyPress function to the datepicker keypress:
$(function() {
    $("#datepicker").datepicker().keypress(function (event) { 
        $.datepicker.customKeyPress(event);
    });
});
Once the datepicker is refactored to use the widget factory (maybe in jquery-ui 1.9??), I will update this code to use the widget factory solution instead. For future updates, see https://github.com/lerocha/jquery-ui-extensions.

A demo of this extended datepicker is available here.

2 comments:

hosting said...

It was really a valuable coding u have provided for us..It helps me a lot to clarify many errors in jquery..It was amazing to know with new shortcuts..Thanks ! hosting

web design company said...

nice coding ...Its amazing that u have given that valuable coding part in ur blog !! web design company

Spring Boot Configuration Properties Localization

Spring Boot allows to externalize application configuration by using properties files or YAML files. Spring Profiles  provide a way to segr...