日付をカレンダーから選択して年月日をinputに設定できるJQueryのdatepicker。
日付入力欄がある画面を作る場合、フォーマットの問題もあるのでかなり重宝する。
これを日付選択ではなく、年月を選択して月初日や月末日を入力欄に設定したいとの要件があったため対応。
その時のソースをせっかくなのでメモしておく。
#修正箇所については後日説明を追記したいと思うが、取り急ぎソースのみ貼り付け
JQueryのDatepickerを年月指定のカレンダー選択入力に変える
(function( factory ) {
if ( typeof define === "function" && define.amd ) {
// AMD. Register as an anonymous module.
define([ "jquery" ], factory );
} else {
// Browser globals
factory( jQuery );
}
}(function( $ ) {
/*!
* monthpicker
* customized jQuery-UI's datepicker.
* It must be included jquery-ui.js or jquery-ui.min.js, if you use this.
*/
$.extend($.ui, { monthpicker: { version: "based on datepicker 1.11.4" } });
var datepicker_instActive;
function datepicker_getZindex( elem ) {
var position, value;
while ( elem.length && elem[ 0 ] !== document ) {
// Ignore z-index if position is set to a value where z-index is ignored by the browser
// This makes behavior of this function consistent across browsers
// WebKit always returns auto if the element is positioned
position = elem.css( "position" );
if ( position === "absolute" || position === "relative" || position === "fixed" ) {
// IE returns 0 when zIndex is not specified
// other browsers return a string
// we ignore the case of nested elements with an explicit value of 0
// <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
value = parseInt( elem.css( "zIndex" ), 10 );
if ( !isNaN( value ) && value !== 0 ) {
return value;
}
}
elem = elem.parent();
}
return 0;
}
function Monthpicker() {
this._curInst = null; // The current instance in use
this._keyEvent = false; // If the last event was a key event
this._disabledInputs = []; // List of date picker inputs that have been disabled
this._datepickerShowing = false; // True if the popup picker is showing , false if not
this._inDialog = false; // True if showing within a "dialog", false if not
this._mainDivId = "ui-monthpicker-div"; // The ID of the main datepicker division
this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
this._appendClass = "ui-datepicker-append"; // The name of the append marker class
this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
this.regional = []; // Available regional settings, indexed by language code
this.regional[""] = { // Default regional settings
closeText: "Done", // Display text for close link
prevText: "Prev", // Display text for previous month link
nextText: "Next", // Display text for next month link
currentText: "Today", // Display text for current month link
monthNames: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'], // Names of months for drop-down and formatting
monthNamesShort: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'], // For formatting
dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
weekHeader: "Wk", // Column header for week of the year
dateFormat: 'yy/mm/dd', // See format options on parseDate
firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
isRTL: false, // True if right-to-left language, false if left-to-right
showMonthAfterYear: false, // True if the year select precedes month, false for month then year
yearSuffix: "年" // Additional text to append to the year in the month headers
};
this._defaults = { // Global defaults for all the date picker instances
showOn: "focus", // "focus" for popup on focus,
// "button" for trigger button, or "both" for either
showAnim: "fadeIn", // Name of jQuery animation for popup
showOptions: {}, // Options for enhanced animations
defaultDate: null, // Used when field is blank: actual date,
// +/-number for offset from today, null for today
appendText: "", // Display text following the input box, e.g. showing the format
buttonText: "...", // Text for trigger button
buttonImage: "", // URL for trigger button image
buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
hideIfNoPrevNext: false, // True to hide next/previous month links
// if not applicable, false to just disable them
navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
gotoCurrent: false, // True if today link goes back to current selection instead
changeMonth: false, // True if month can be selected directly, false if only prev/next
changeYear: false, // True if year can be selected directly, false if only prev/next
yearRange: "c-10:c+10", // Range of years to display in drop-down,
// either relative to today's year (-nn:+nn), relative to currently displayed year
// (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
showOtherMonths: true, // True to show dates in other months, false to leave blank
selectOtherMonths: true, // True to allow selection of dates in other months, false for unselectable
showWeek: false, // True to show week of the year, false to not show it
calculateWeek: this.iso8601Week, // How to calculate the week of the year,
// takes a Date and returns the number of the week for it
shortYearCutoff: "+10", // Short year values < this are in the current century,
// > this are in the previous century,
// string value starting with "+" for current year + value
minDate: '2000/01/01', // The earliest selectable date, or null for no limit
maxDate: '9999/12/31', // The latest selectable date, or null for no limit
duration: "fast", // Duration of display/closure
beforeShowDay: null, // Function that takes a date and returns an array with
// [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
// [2] = cell title (optional), e.g. $.datepicker.noWeekends
beforeShow: null, // Function that takes an input field and
// returns a set of custom settings for the date picker
onSelect: null, // Define a callback function when a date is selected
onChangeMonthYear: null, // Define a callback function when the month or year is changed
onClose: null, // Define a callback function when the datepicker is closed
numberOfMonths: 1, // Number of months to show at a time
showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
stepMonths: 12, // Number of months to step back/forward
stepBigMonths: 12, // Number of months to step back/forward for the big links
altField: "", // Selector for an alternate field to store selected dates into
altFormat: "", // The date format to use for the alternate field
constrainInput: true, // The input is constrained by the current date format
showButtonPanel: true, // True to show button panel, false to not show it
autoSize: false, // True to size the input for the date format, false to leave as is
disabled: false // The initial disabled state
};
$.extend(this._defaults, this.regional[""]);
this.regional.en = $.extend( true, {}, this.regional[ "" ]);
this.regional[ "en-US" ] = $.extend( true, {}, this.regional.en );
this.dpDiv = monthpicker_bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
}
$.extend(Monthpicker.prototype, $.datepicker.constructor.prototype, {
/* Class name added to elements to indicate already configured with a date picker. */
markerClassName: "hasMonthpicker",
//Keep track of the maximum number of rows displayed (see #7043)
maxRows: 4,
/* Make attachments based on settings. */
_attachments: function(input, inst) {
var showOn, buttonText, buttonImage,
appendText = this._get(inst, "appendText"),
isRTL = this._get(inst, "isRTL");
if (inst.append) {
inst.append.remove();
}
if (appendText) {
inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
input[isRTL ? "before" : "after"](inst.append);
}
input.unbind("focus", this._showDatepicker);
if (inst.trigger) {
inst.trigger.remove();
}
showOn = this._get(inst, "showOn");
if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
input.focus(this._showDatepicker);
}
if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
buttonText = this._get(inst, "buttonText");
buttonImage = this._get(inst, "buttonImage");
inst.trigger = $(this._get(inst, "buttonImageOnly") ?
$("<img/>").addClass(this._triggerClass).
attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
$("<button type='button'></button>").addClass(this._triggerClass).
html(!buttonImage ? buttonText : $("<img/>").attr(
{ src:buttonImage, alt:buttonText, title:buttonText })));
input[isRTL ? "before" : "after"](inst.trigger);
inst.trigger.click(function() {
if ($.monthpicker._datepickerShowing && $.monthpicker._lastInput === input[0]) {
$.monthpicker._hideDatepicker();
} else if ($.monthpicker._datepickerShowing && $.monthpicker._lastInput !== input[0]) {
$.monthpicker._hideDatepicker();
$.monthpicker._showDatepicker(input[0]);
} else {
$.monthpicker._showDatepicker(input[0]);
}
return false;
});
}
},
/* Update or retrieve the settings for a date picker attached to an input field or division.
* @param target element - the target input field or division or span
* @param name object - the new settings to update or
* string - the name of the setting to change or retrieve,
* when retrieving also "all" for all instance settings or
* "defaults" for all global defaults
* @param value any - the new value for the setting
* (omit if above is an object or to retrieve a value)
*/
_optionDatepicker: function(target, name, value) {
var settings, date, minDate, maxDate,
inst = this._getInst(target);
if (arguments.length === 2 && typeof name === "string") {
return (name === "defaults" ? $.extend({}, $.monthpicker._defaults) :
(inst ? (name === "all" ? $.extend({}, inst.settings) :
this._get(inst, name)) : null));
}
settings = name || {};
if (typeof name === "string") {
settings = {};
settings[name] = value;
}
if (inst) {
if (this._curInst === inst) {
this._hideDatepicker();
}
date = this._getDateDatepicker(target, true);
minDate = this._getMinMaxDate(inst, "min");
maxDate = this._getMinMaxDate(inst, "max");
datepicker_extendRemove(inst.settings, settings);
// reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
inst.settings.minDate = this._formatDate(inst, minDate);
}
if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
inst.settings.maxDate = this._formatDate(inst, maxDate);
}
if ( "disabled" in settings ) {
if ( settings.disabled ) {
this._disableDatepicker(target);
} else {
this._enableDatepicker(target);
}
}
this._attachments($(target), inst);
this._autoSize(inst);
this._setDate(inst, date);
this._updateAlternate(inst);
this._updateDatepicker(inst);
}
},
/* Handle keystrokes. */
_doKeyDown: function(event) {
var onSelect, dateStr, sel,
inst = $.monthpicker._getInst(event.target),
handled = true,
isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
inst._keyEvent = true;
if ($.monthpicker._datepickerShowing) {
switch (event.keyCode) {
case 9: $.monthpicker._hideDatepicker();
handled = false;
break; // hide on tab out
case 13: sel = $("td." + $.monthpicker._dayOverClass + ":not(." +
$.monthpicker._currentClass + ")", inst.dpDiv);
if (sel[0]) {
$.monthpicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, 1, sel[0]);
}
onSelect = $.monthpicker._get(inst, "onSelect");
if (onSelect) {
dateStr = $.monthpicker._formatDate(inst);
// trigger custom callback
onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
} else {
$.monthpicker._hideDatepicker();
}
return false; // don't submit the form
case 27: $.monthpicker._hideDatepicker();
break; // hide on escape
case 33: $.monthpicker._adjustDate(event.target, (event.ctrlKey ?
-$.monthpicker._get(inst, "stepBigMonths") :
-$.monthpicker._get(inst, "stepMonths")), "M");
break; // previous month/year on page up/+ ctrl
case 34: $.monthpicker._adjustDate(event.target, (event.ctrlKey ?
+$.monthpicker._get(inst, "stepBigMonths") :
+$.monthpicker._get(inst, "stepMonths")), "M");
break; // next month/year on page down/+ ctrl
case 35: if (event.ctrlKey || event.metaKey) {
$.monthpicker._clearDate(event.target);
}
handled = event.ctrlKey || event.metaKey;
break; // clear on ctrl or command +end
case 36: if (event.ctrlKey || event.metaKey) {
$.monthpicker._gotoToday(event.target);
}
handled = event.ctrlKey || event.metaKey;
break; // current on ctrl or command +home
case 37: if (event.ctrlKey || event.metaKey) {
$.monthpicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
}
handled = event.ctrlKey || event.metaKey;
// -1 day on ctrl or command +left
if (event.originalEvent.altKey) {
$.monthpicker._adjustDate(event.target, (event.ctrlKey ?
-$.monthpicker._get(inst, "stepBigMonths") :
-$.monthpicker._get(inst, "stepMonths")), "M");
}
// next month/year on alt +left on Mac
break;
case 38: if (event.ctrlKey || event.metaKey) {
$.monthpicker._adjustDate(event.target, -7, "D");
}
handled = event.ctrlKey || event.metaKey;
break; // -1 week on ctrl or command +up
case 39: if (event.ctrlKey || event.metaKey) {
$.monthpicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
}
handled = event.ctrlKey || event.metaKey;
// +1 day on ctrl or command +right
if (event.originalEvent.altKey) {
$.monthpicker._adjustDate(event.target, (event.ctrlKey ?
+$.monthpicker._get(inst, "stepBigMonths") :
+$.monthpicker._get(inst, "stepMonths")), "M");
}
// next month/year on alt +right
break;
case 40: if (event.ctrlKey || event.metaKey) {
$.monthpicker._adjustDate(event.target, +7, "D");
}
handled = event.ctrlKey || event.metaKey;
break; // +1 week on ctrl or command +down
default: handled = false;
}
} else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
$.monthpicker._showDatepicker(this);
} else {
handled = false;
}
if (handled) {
event.preventDefault();
event.stopPropagation();
}
},
/* Filter entered characters - based on date format. */
_doKeyPress: function(event) {
var chars, chr,
inst = $.monthpicker._getInst(event.target);
if ($.monthpicker._get(inst, "constrainInput")) {
chars = $.monthpicker._possibleChars($.monthpicker._get(inst, "dateFormat"));
chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
}
},
/* Synchronise manual entry and field/alternate field. */
_doKeyUp: function(event) {
var date,
inst = $.monthpicker._getInst(event.target);
if (inst.input.val() !== inst.lastVal) {
try {
date = $.monthpicker.parseDate($.monthpicker._get(inst, "dateFormat"),
(inst.input ? inst.input.val() : null),
$.monthpicker._getFormatConfig(inst));
if (date) { // only if valid
$.monthpicker._setDateFromField(inst);
$.monthpicker._updateAlternate(inst);
$.monthpicker._updateDatepicker(inst);
}
}
catch (err) {
}
}
return true;
},
/* Pop-up the date picker for a given input field.
* If false returned from beforeShow event handler do not show.
* @param input element - the input field attached to the date picker or
* event - if triggered by focus
*/
_showDatepicker: function(input) {
input = input.target || input;
if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
input = $("input", input.parentNode)[0];
}
if ($.monthpicker._isDisabledDatepicker(input) || $.monthpicker._lastInput === input) { // already here
return;
}
var inst, beforeShow, beforeShowSettings, isFixed,
offset, showAnim, duration;
inst = $.monthpicker._getInst(input);
if ($.monthpicker._curInst && $.monthpicker._curInst !== inst) {
$.monthpicker._curInst.dpDiv.stop(true, true);
if ( inst && $.monthpicker._datepickerShowing ) {
$.monthpicker._hideDatepicker( $.monthpicker._curInst.input[0] );
}
}
beforeShow = $.monthpicker._get(inst, "beforeShow");
beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
if(beforeShowSettings === false){
return;
}
datepicker_extendRemove(inst.settings, beforeShowSettings);
inst.lastVal = null;
$.monthpicker._lastInput = input;
$.monthpicker._setDateFromField(inst);
if ($.monthpicker._inDialog) { // hide cursor
input.value = "";
}
if (!$.monthpicker._pos) { // position below input
$.monthpicker._pos = $.monthpicker._findPos(input);
$.monthpicker._pos[1] += input.offsetHeight; // add the height
}
isFixed = false;
$(input).parents().each(function() {
isFixed |= $(this).css("position") === "fixed";
return !isFixed;
});
offset = {left: $.monthpicker._pos[0], top: $.monthpicker._pos[1]};
$.monthpicker._pos = null;
//to avoid flashes on Firefox
inst.dpDiv.empty();
// determine sizing offscreen
inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
$.monthpicker._updateDatepicker(inst);
// fix width for dynamic number of date pickers
// and adjust position before showing
offset = $.monthpicker._checkOffset(inst, offset, isFixed);
inst.dpDiv.css({position: ($.monthpicker._inDialog && $.blockUI ?
"static" : (isFixed ? "fixed" : "absolute")), display: "none",
left: offset.left + "px", top: offset.top + "px"});
if (!inst.inline) {
showAnim = $.monthpicker._get(inst, "showAnim");
duration = $.monthpicker._get(inst, "duration");
inst.dpDiv.css( "z-index", datepicker_getZindex( $( input ) ) + 1 );
$.monthpicker._datepickerShowing = true;
if ( $.effects && $.effects.effect[ showAnim ] ) {
inst.dpDiv.show(showAnim, $.monthpicker._get(inst, "showOptions"), duration);
} else {
inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
}
if ( $.monthpicker._shouldFocusInput( inst ) ) {
inst.input.focus();
}
$.monthpicker._curInst = inst;
}
},
/* Generate the date picker content. */
_updateDatepicker: function(inst) {
this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
datepicker_inst