Owen Leonard
f892ae8bf1
This patch is a proof of concept demonstrating how jQueryUI date & time pickers could be replaced using the Flatpickr library (https://flatpickr.js.org/). NEW: I've modified the default configuration of Flatpickr instances so that a "Clear date" link is automatically appended. This eliminates the need to add a button to the markup and event handling for each case. NEW: Date/time formatting should be corrected in this revised patch. The patch modifies three pages as test cases: - Circulation -> Renew (with SpecifyDueDates enabled), to demonstrate date and time selection. - NEW: You can also test the datepicker shown when you renew an on-hold item. This demonstrates a configuration which requires that the selection be after today. - Administration -> Patron categories -> New category, to demonstrate a calendar-only date picker enforcing a date after today. - NEW: Reports -> Patrons. The "Date of birth" fields are linked so that the second cannot be before the first. I've made some customizations to the default Flatpickr library's CSS and incorporated it into staff-global.scss, so you must rebuild the staff client SCSS (https://wiki.koha-community.org/wiki/Working_with_SCSS_in_the_OPAC_and_staff_client). Signed-off-by: David Nind <david@davidnind.com> Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io> Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
230 lines
8.1 KiB
JavaScript
230 lines
8.1 KiB
JavaScript
/* global debug sentmsg __ dateformat_pref dateformat_string bidi calendarFirstDayOfWeek */
|
|
/* exported DateTime_from_syspref flatpickr_weekdays flatpickr_months */
|
|
var MSG_PLEASE_ENTER_A_VALID_DATE = ( __("Please enter a valid date (should match %s).") );
|
|
if (debug > 1) {
|
|
alert("dateformat: " + dateformat_pref + "\ndebug is on (level " + debug + ")");
|
|
}
|
|
|
|
function is_valid_date(date) {
|
|
// An empty string is considered as a valid date for convenient reasons.
|
|
if (date === '') return 1;
|
|
var dateformat = dateformat_string;
|
|
if (dateformat == 'us') {
|
|
if (date.search(/^\d{2}\/\d{2}\/\d{4}($|\s)/) == -1) return 0;
|
|
dateformat = 'mm/dd/yy';
|
|
} else if (dateformat == 'metric') {
|
|
if (date.search(/^\d{2}\/\d{2}\/\d{4}($|\s)/) == -1) return 0;
|
|
dateformat = 'dd/mm/yy';
|
|
} else if (dateformat == 'iso') {
|
|
if (date.search(/^\d{4}-\d{2}-\d{2}($|\s)/) == -1) return 0;
|
|
dateformat = 'yy-mm-dd';
|
|
} else if (dateformat == 'dmydot') {
|
|
if (date.search(/^\d{2}\.\d{2}\.\d{4}($|\s)/) == -1) return 0;
|
|
dateformat = 'dd.mm.yy';
|
|
}
|
|
try {
|
|
flatpickr.parseDate(date, dateformat);
|
|
} catch (e) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
function get_dateformat_str(dateformat) {
|
|
var dateformat_str;
|
|
if (dateformat == 'us') {
|
|
dateformat_str = 'mm/dd/yyyy';
|
|
} else if (dateformat == 'metric') {
|
|
dateformat_str = 'dd/mm/yyyy';
|
|
} else if (dateformat == 'iso') {
|
|
dateformat_str = 'yyyy-mm-dd';
|
|
} else if (dateformat == 'dmydot') {
|
|
dateformat_str = 'dd.mm.yyyy';
|
|
}
|
|
return dateformat_str;
|
|
}
|
|
|
|
function validate_date(dateText, inst) {
|
|
if (!is_valid_date(dateText)) {
|
|
var dateformat_str = get_dateformat_str( dateformat_pref );
|
|
alert(MSG_PLEASE_ENTER_A_VALID_DATE.format(dateformat_str));
|
|
inst.clear();
|
|
}
|
|
}
|
|
|
|
function Date_from_syspref(dstring) {
|
|
var dateX = dstring.split(/[-/.]/);
|
|
if (debug > 1 && sentmsg < 1) {
|
|
sentmsg++;
|
|
alert("Date_from_syspref(" + dstring + ") splits to:\n" + dateX.join("\n"));
|
|
}
|
|
if (dateformat_pref === "iso") {
|
|
return new Date(dateX[0], (dateX[1] - 1), dateX[2]); // YYYY-MM-DD to (YYYY,m(0-11),d)
|
|
} else if (dateformat_pref === "us") {
|
|
return new Date(dateX[2], (dateX[0] - 1), dateX[1]); // MM/DD/YYYY to (YYYY,m(0-11),d)
|
|
} else if (dateformat_pref === "metric") {
|
|
return new Date(dateX[2], (dateX[1] - 1), dateX[0]); // DD/MM/YYYY to (YYYY,m(0-11),d)
|
|
} else if (dateformat_pref === "dmydot") {
|
|
return new Date(dateX[2], (dateX[1] - 1), dateX[0]); // DD.MM.YYYY to (YYYY,m(0-11),d)
|
|
} else {
|
|
if (debug > 0) {
|
|
alert("KOHA ERROR - Unrecognized date format: " + dateformat_pref);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function DateTime_from_syspref(date_time) {
|
|
var parts = date_time.split(" ");
|
|
var date = parts[0];
|
|
var time = parts[1];
|
|
parts = time.split(":");
|
|
var hour = parts[0];
|
|
var minute = parts[1];
|
|
|
|
if (hour < 0 || hour > 23) {
|
|
return 0;
|
|
}
|
|
if (minute < 0 || minute > 59) {
|
|
return 0;
|
|
}
|
|
|
|
var datetime = Date_from_syspref(date);
|
|
|
|
if (isNaN(datetime.getTime())) {
|
|
return 0;
|
|
}
|
|
|
|
datetime.setHours(hour);
|
|
datetime.setMinutes(minute);
|
|
|
|
return datetime;
|
|
}
|
|
|
|
/* Instead of including multiple localization files as you would normally see with
|
|
jQueryUI we expose the localization strings in the default configuration */
|
|
jQuery(function ($) {
|
|
$.datepicker.regional[''] = {
|
|
closeText: __("Done"),
|
|
prevText: __("Prev"),
|
|
nextText: __("Next"),
|
|
currentText: __("Today"),
|
|
monthNames: [__("January"), __("February"), __("March"), __("April"), __("May"), __("June"),
|
|
__("July"), __("August"), __("September"), __("October"), __("November"), __("December")
|
|
],
|
|
monthNamesShort: [__("Jan"), __("Feb"), __("Mar"), __("Apr"), __("May"), __("Jun"),
|
|
__("Jul"), __("Aug"), __("Sep"), __("Oct"), __("Nov"), __("Dec")
|
|
],
|
|
dayNames: [__("Sunday"), __("Monday"), __("Tuesday"), __("Wednesday"), __("Thursday"), __("Friday"), __("Saturday")],
|
|
dayNamesShort: [__("Sun"), __("Mon"), __("Tue"), __("Wed"), __("Thu"), __("Fri"), __("Sat")],
|
|
dayNamesMin: [__("Su"), __("Mo"), __("Tu"), __("We"), __("Th"), __("Fr"), __("Sa")],
|
|
weekHeader: __("Wk"),
|
|
dateFormat: dateformat_string,
|
|
firstDay: calendarFirstDayOfWeek,
|
|
isRTL: bidi,
|
|
showMonthAfterYear: false,
|
|
yearSuffix: ''
|
|
};
|
|
$.datepicker.setDefaults($.datepicker.regional['']);
|
|
});
|
|
|
|
/* jQuery Validator plugin custom method
|
|
This allows you to check that a given date falls after another.
|
|
It is required that a message be defined.
|
|
|
|
Syntax:
|
|
$("#form_id").validate({
|
|
rules: {
|
|
input_name_of_later_date_field: {
|
|
is_date_after: "#input_id_of_earlier_date_field"
|
|
},
|
|
},
|
|
messages: {
|
|
input_name_of_later_date_field: {
|
|
is_date_after: _("Validation error to be shown, i.e. End date must come after start date")
|
|
}
|
|
}
|
|
});
|
|
*/
|
|
|
|
jQuery.validator.addMethod("is_date_after",
|
|
function (value, element, params) {
|
|
var from = Date_from_syspref($(params).val());
|
|
var to = Date_from_syspref(value);
|
|
return to > from;
|
|
});
|
|
|
|
jQuery.validator.addMethod("date_on_or_after",
|
|
function (value, element, params) {
|
|
var from = Date_from_syspref($(params).val());
|
|
var to = Date_from_syspref(value);
|
|
return to >= from;
|
|
});
|
|
|
|
var flatpickr_weekdays = {
|
|
shorthand: [ __("Sun"), __("Mon"), __("Tue"), __("Wed"), __("Thu"), __("Fri"), __("Sat")],
|
|
longhand: [ __("Sunday"), __("Monday"), __("Tuesday"), __("Wednesday"), __("Thursday"), __("Friday"), __("Saturday") ]
|
|
};
|
|
|
|
var flatpickr_months = {
|
|
shorthand: [ __("Jan"), __("Feb"), __("Mar"), __("Apr"), __("May"), __("Jun"), __("Jul"), __("Aug"), __("Sep"), __("Oct"), __("Nov"), __("Dec")],
|
|
longhand: [ __("January"), __("February"), __("March"), __("April"), __("May"), __("June"), __("July"), __("August"), __("September"), __("October"), __("November"), __("December")]
|
|
};
|
|
|
|
$(document).ready(function () {
|
|
|
|
$.datepicker.setDefaults({
|
|
showOn: "both",
|
|
buttonImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAT0lEQVQ4jWNgoAZYd/LVf3IwigGkAuwGLE4hDg9eA4il8RqADVdtLYVjZLVEuwDZAKJcgKxh+zkyXIBuI8lhgG4jOqZdLJACMAygKDNRAgBj9qOB+rWnhAAAAABJRU5ErkJggg==",
|
|
buttonImageOnly: true,
|
|
buttonText: __("Select date"),
|
|
changeMonth: true,
|
|
changeYear: true,
|
|
showButtonPanel: true,
|
|
showOtherMonths: true,
|
|
selectOtherMonths: true,
|
|
yearRange: "c-100:c+10"
|
|
});
|
|
|
|
$("#dateofbirth").datepicker({
|
|
yearRange: "c-100:c"
|
|
});
|
|
|
|
$(".futuredate").datepicker({
|
|
minDate: 1, // require that hold suspended until date is after today
|
|
});
|
|
|
|
$(".datepicker").datepicker({
|
|
onClose: function (dateText, inst) {
|
|
validate_date(dateText, inst);
|
|
},
|
|
}).on("change", function () {
|
|
if (!is_valid_date($(this).val())) {
|
|
$(this).val("");
|
|
} else {
|
|
var the_date = $.datepicker.parseDate(dateformat_string, $(this).val());
|
|
$(this).datepicker("setDate",the_date);
|
|
}
|
|
});
|
|
// http://jqueryui.com/demos/datepicker/#date-range
|
|
var dates = $(".datepickerfrom, .datepickerto").datepicker({
|
|
changeMonth: true,
|
|
numberOfMonths: 1,
|
|
onSelect: function (selectedDate) {
|
|
var option = this.id == "from" ? "minDate" : "maxDate",
|
|
instance = $(this).data("datepicker");
|
|
var date = $.datepicker.parseDate(
|
|
instance.settings.dateFormat ||
|
|
$.datepicker._defaults.dateFormat,
|
|
selectedDate, instance.settings);
|
|
dates.not(this).datepicker("option", option, date);
|
|
},
|
|
onClose: function (dateText, inst) {
|
|
validate_date(dateText, inst);
|
|
},
|
|
}).on("change", function () {
|
|
if (!is_valid_date($(this).val())) {
|
|
$(this).val("");
|
|
}
|
|
});
|
|
});
|