jQuery UI datepicker
Each calendar in the calendars
array is a localization object or a string that determines a localization object. Each localization object specifies a calendar system; the most common is the Gregorian calendar, though I created flexcal
to be able to use the Jewish calendar. Note that Wikipiedia calls it the "Hebrew calendar", and that seems to be the Unicode standard as well. I am convinced that is wrong; Hebrew is the language, Jewish is the calendar. It would be inappropriate to call the Islamic calendar, for instance, the Arabic calendar.
Be that as it may, the localization object also specifies the names of the months and the days of the week, and the way to format date and year numbers. It is assumed that there are a limited number of months and days in a week (a seven-day week is not assumed) so an array of names is appropriate. Dates in a month and years are assumed to be numeric, but can use numbering systems other than Latin numerals.
Each localization object contains the elements that are locale-specific; anything not included is taken from thel10n
option and its defaults. Most of the items and their names are taken from the jQuery UI datepicker.
name
Name of the calendar to display in the tab bar.
'flexcal'
calendar
monthNames
Array of month names that are displayed on the calendar.
['January','February','March','April','May','June', 'July','August','September','October','November','December']
monthNamesShort
Array of abbreviated month names. Not used in displaying the calendar, or in the simplest version of flexcal
at all. I plan to expand the format method to allow using these.
['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
dayNamesMin
Array of minimized names of the days of the week. This is what is displayed on the calendar.
['Su','Mo','Tu','We','Th','Fr','Sa']
dayNames
Array of names of the days of the week. Not used in displaying the calendar, or in the simplest version of flexcal
at all. I plan to expand the format method to allow using these.
['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
dayNamesShort
Array of abbreviated names of the days of the week. Not used in displaying the calendar, or in the simplest version of flexcal
at all. I plan to expand the format method to allow using these.
['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
prevText
Text to display on the "previous month" button. The stylesheet for the calendar replaces this with an icon and uses this text as the tooltip. Per the jQuery UI guidelines, it should not contain any arrows or chevrons.
'Previous'
nextText
Text to display on the "next month" button. The stylesheet for the calendar replaces this with an icon and uses this text as the tooltip. Per the jQuery UI guidelines, it should not contain any arrows or chevrons.
'Next'
isRTL
Boolean that should be true to display the calendar right-to-left.
false
firstDay
Number of the day of the week (0-indexed) that the calendar should start.
0
years
Function to convert a year number into a string to display. The caption of the calendar is monthNames[month]+' '+years(year)
function(n) {return n.toString()}
fromYears
Function to convert a string into a year number. If undefined, then only numbers can be parsed as years. Meant to be the inverse of years
. Not used in displaying the calendar, or in the simplest version of flexcal
at all. I plan to expand the parse method to allow using this.
undefined
dates
Function to convert a date number into a string to display. Each <td>
in the calendar has the text dates(date)
function(n) {return n.toString()}
fromdates
Function to convert a string into a date number. If undefined, then only numbers can be parsed as dates. Meant to be the inverse of dates
. Not used in displaying the calendar, or in the simplest version of flexcal
at all. I plan to expand the parse method to allow using this.
undefined
dateFormat
String that represents the format to use for converting from a string (the value of the attached text box) to a Date
object. See the full description below.
'm/d/yyyy'
In addition to localizing the calendar, flexcal
allows for localizing text in buttons and the like. Any element with a data attribute data-flexcal-l10n
will have the value of that attribute + 'Text' used as the key into the localization object, and the result is used as the HTML of the element. Thus
<span data-flexcal-l10n=today ></span>
will have its content replaced with l10n.todayText
for the current localization object.
The localizations included in flexcal
include todayText
and closeText
(along with prevText
and nextText
used in the calendars themselves). jQuery UI datepicker
calls the text for "today" currentText
, but I think that's wrong.
If setting the attribute from javascript, you can use $(...).attr('data-flexcal-l10n', 'today')
or use jQuery's data
method: $(...).data('flexcalL10n', 'today')
, remembering to camelCase the name of the data key to "flexcalL10n".
See the buttons page for examples.
A calendar system is represented by a function that takes a Date d
object and returns an object with the following fields:
first
Date
of the first day of the month containing d
.
last
Date
of the last day of the month containing d
.
prev
Date
of the day exactly one month before d
.
next
Date
of the day exactly one month after d
.
prevYear
Date
of the day exactly one year before d
.
nextYear
Date
of the day exactly one year after d
.
y
Number of the year of d
, in this calendar system. This is passed to years
for display.
m
Number (0-indexed) of the month of d
, in this calendar system. This is the index into monthNames
.
d
Number of the date of d
, in this calendar system. This is passed to dates
for display.
dow
0-indexed number of the day of the week of the first day of the month (not of d
. Note that this is not necessarily just first.getDay()
, since this calendar system may use some other "week" system.
toDate
Function that takes an object {y: y, m: m, d: d}
representing a date in this calendar and returns the corresponding Javascript Date
. Not currently used; I plan to expand the parse method to allow using this. If the values given are invalid, should either return new Date(undefined)
or do some error correcting the way new Date(y, m, d)
does, e.g. turning 0 dates into the last day of the previous month.
I implemented two calendar systems that I wanted, and they are stored in $.bililite.flexcal.calendars
: Gregorian and Jewish.
$.bililite.flexcal.calendars.gregorian
function(d){
var m = d.getMonth(), y = d.getFullYear(), date = d.getDate(), first = new Date (y, m, 1);
var prev = new Date (y, m-1, date), next = new Date (y, m+1, date);
if (prev.getDate() != date) prev = new Date (y, m, 0); // adjust for too-short months
if (next.getDate() != date) next = new Date (y, m+2, 0);
var nextYearDate = m == 1 && date == 29 ? 28 : date;
return {
first: first,
last: new Date (y, m+1, 0),
prev: prev,
next: next,
prevYear: new Date (y-1, m, nextYearDate),
nextYear: new Date (y+1, m, nextYearDate),
m: m,
y: y,
d: d,
dow: first.getDay(),
toDate: function (d) {return new Date (d.y, d.m, d.d)}
};
}
$.bililite.flexcal.calendars.jewish
Define a function
function addDay(d, n){
if (n === undefined) n = 1;
return new Date(d.getFullYear(), d.getMonth(), d.getDate()+n);
}
and functions civ2heb(d)
that takes a Javascript Date
and returns an object {y: y, m: m, d: d}
representing the Jewish date, and heb2civ(h)
which does the reverse. See the source code for details. The civ2heb
function also includes a daysinmonth
field that is the number of days in that Jewish month. Then the calendar function is:
function(d){
var h = civ2heb(d);
var roshchodesh = addDay(d, -h.d+1);
var daysinlastmonth = Math.max(civ2heb(addDay(roshchodesh,-1)).daysinmonth, h.d); // the min/max() correct for the possibility of other month being too short
var daysintonextmonth = Math.min(civ2heb(addDay(roshchodesh, h.daysinmonth)).daysinmonth, h.d);
return {
first: roshchodesh,
last: addDay(roshchodesh, h.daysinmonth-1),
prev: addDay(d, -daysinlastmonth),
next: addDay(roshchodesh, h.daysinmonth+daysintonextmonth-1),
prevYear: heb2civ($.extend({}, h, {y: h.y-1})),
nextYear: heb2civ($.extend({}, h, {y: h.y+1})),
m: h.m,
y: h.y,
d: h.d,
dow: roshchodesh.getDay(),
toDate: heb2civ
};
}
If a string is passed as the localization object, it is assumed to be the key into the $.bililite.flexcal.l10n
, which comes with three entries predefined: 'en'
(English localization of the Gregorian calendar), 'jewish'
(English localization of the Jewish calendar), and 'he-jewish'
(Hebrew localization of the Jewish calendar).
Note that the standard I am using is languageCode-calendarSystem
, where calendarSystem
is the index into the $.bililite.flexcal.calendars
array.
datepicker
Datepicker comes with a large number of localization files that can be easily used with flexcal
. You need to include the localization file, with something like <script src="https://cdn.rawgit.com/jquery/jquery-ui/master/ui/i18n/datepicker-fr.js"></script>
to get the French localization. flexcal
will then interpret 'fr'
as a reference to $.datepicker.regional['fr']
.
Note that all the datepicker
localizations are for the Gregorian calendar. Note also that including localization file sets the defaults for datepicker
to that language, so if you want to actually use datepicker
, you may want to call $.datepicker.setDefaults($.datepicker.regional[''])
to set them back. Note also that not all the fields that flexcal
uses are set in the datepicker
localization files, especially name
, so you may want to set that separately (see the calendars
example).
calendarspicker
Keith Wood is maintaining the jQuery plugin that eventually became the official jQuery UI datepicker. His version, however, handles multiple calendar systems, much like flexcal (though I like my plugin, of course). His code is on github, and the documentation is on his personal site. He has support for many calendar systems, and I wanted to let flexcal use that. I don't use his datepicker code, just the calendar systems.
To use it, include the basic calendar code with <script src="https://cdn.rawgit.com/kbwood/calendars/master/jquery.calendars.js"></script>
and all the localization files. To use the Arabic localization, include <script src="https://cdn.rawgit.com/kbwood/calendars/master/jquery.calendars-ar.js"></script>
and 'ar'
as the localization object. To use the Islamic calendar with Arabic localization, include both <script src="https://cdn.rawgit.com/kbwood/calendars/master/jquery.calendars.islamic.js"></script>
and <script src="https://cdn.rawgit.com/kbwood/calendars/master/jquery.calendars.islamic-ar.js"></script>
, and use 'islamic'
for the Islamic calendar with English localization, and 'ar-islamic'
for the Islamic calendar with Arabic localization.
Some localization names use the 2-letter language code plus a country code, like 'en-GB'
. flexcal
is smart enough to handle that.
Note that the name
field in these localization files is only the name of the calendar system, so you may want to set that separately.Note also that the prevText
and nextText
fields are not set in the calendar localization files, but in his datepicker localization files. So including that requires including the entire datepicker file, <script src="https://cdn.rawgit.com/kbwood/calendars/master/jquery.calendars.picker.js"></script>
and the localization file, <script src="https://cdn.rawgit.com/kbwood/calendars/master/jquery.calendars.picker-ar.js"></script>
. So as note to require including the whole datepicker (I assume you are using flexcal
!) I created a stub that can be included instead, jquery.calendars.picker-mock.js. You still have to include the picker localization file, like jquery.calendars.picker-ar.js.
Any string that cannot be interpreted as an index into an array of localization objects will be interpreted as simply a name. So 'French'
is interpreted as {name: 'French'}
. All actual localization names start with a lower case letter.
tol10n
As a utility function, localization objects can be created with a standalone function, $.bililite.flexcal.tol10n(name, defaultL10n)
. name
can be an object, which is taken as the actual localization object, or a string, which is interpreted as above. If it is an array, then each element is evaluated and the results $.extend
ed together. defaultL10n
must be an object, and it will be $.extend
ed with the result of evaluating name
. If defaultL10n
is undefined, $.bililite.flexcal.prototype.options.l10n
is used.
$.bililite.flexcal.tol10n('fr')
(if the datepicker localization file is included) returns
{
calendar: function(d) {...} // $.bililite.flexcal.calendars.gregorian,
closeText: "Fermer", // used by datepicker, not flexcal
currentText: "Aujourd'hui",
dateFormat: "dd/mm/yy",
dates: function (n) {return n.toString()},
dayNames: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
dayNamesMin: ["D","L","M","M","J","V","S"],
dayNamesShort: Array["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
firstDay: 1,
isRTL: false,
monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],
monthNamesShort: ["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."]
name: "flexcal", // from the default
nextText: "Suivant",
prevText: "Précédent",
showMonthAfterYear: false,
weekHeader: "Sem.",
yearSuffix: "",
years: function (n) {return n.toString()
}
And $.bililite.flexcal.tol10n(['fr', 'French', {dateFormat: 'yyyy-mm-dd'}])
returns
{
calendar: function(d) {...} // $.bililite.flexcal.calendars.gregorian,
closeText: "Fermer", // used by datepicker, not flexcal
currentText: "Aujourd'hui",
dateFormat: "yyyy-mm-dd", // from the third element
dates: function (n) {return n.toString()},
dayNames: ["dimanche", "lundi", "mardi", "mercredi", "jeudi", "vendredi", "samedi"],
dayNamesMin: ["D","L","M","M","J","V","S"],
dayNamesShort: Array["dim.", "lun.", "mar.", "mer.", "jeu.", "ven.", "sam."],
firstDay: 1,
isRTL: false,
monthNames:["janvier","février","mars","avril","mai","juin","juillet","août","septembre","octobre","novembre","décembre"],
monthNamesShort: ["janv.","févr.","mars","avr.","mai","juin","juil.","août","sept.","oct.","nov.","déc."]
name: "French", // from the second element
nextText: "Suivant",
prevText: "Précédent",
showMonthAfterYear: false,
weekHeader: "Sem.",
yearSuffix: "",
years: function (n) {return n.toString()
}
flexcal
as it stands has a minimal formatting/parsing capability. The formatting specification is based on that of Keith Wood's calendars, except for not using 2-digit years. It is also compatible with the jQuery UI formats.
The codes are:
d
: Date numberdd
: Date number, padded to two digitsm
: Month number (1-indexed. Note that internally (as with Date
) months are 0-based)mm
: Month number, padded to two digitsyy
: Year number. There is no option for a two-digit year.yyyy
: Year number, padded to four digits (which really shouldn't be relevant)Any other string is interpreted literally.
format
There are three utility functions, $.bililite.flexcal.format(d, format, l10n)
, $.bililite.flexcal.parse(s, format, l10n)
and $.bililite.flexcal.localize(s, l10n)
.
$.bililite.flexcal.format(d, l10n)
takes three arguments (none are optional), a Date
d, a formatting string and a localization object l10n
. l10n
has to be an object; to use a string, run it through tol10n
, as $.bililite.flexcal.format(new Date(2015,3,8), 'dd-mm-yyyy', $.bililite.flexcal.tol10n('fr'))
. It returns a string with the date formatted according to format
. In the last example, the result would be '08-04-2015'
.
parse
$.bililite.flexcal.parse(s, format, l10n)
is the reverse. It takes a string s
and tries to parse it according to l10n.dateFormat
. It tries to obey Postel's law, "Be conservative in what you send, be liberal in what you accept", and accept anything that looks like the format. It just looks for three numbers and interprets them in the order in the format string. A completely uninterpretable string returns new Date(NaN)
.
localize
$.bililite.flexcal.localize(s, l10n)
is for localizing individual strings. Just returns l10n[s+'Text'] || ''
.
I hope to write a more complete formatter/parser in the future (Issue #4).
See the examples for the calendars
option, and the examples in the introduction
The French Revolutionary calendar. This is just to show off how flexible the widget is; 10-day weeks and 5-day months are no problem.
archaicNumbers
The French Revolutionary Calendar example uses a utility function for translating normal numbers into Roman numerals. $.bililite.flexcal.archaicNumbers(arr)
takes an array of equivalent numbers and returns an object {format: formatFunction(n), parse: parseFunction(s)}
.
formatFunction(n)
takes a number and returns a string that is the equivalent. This function can be used for the dates
and years
options. So:
var convertToRoman = $.bililite.flexcal.archaicNumbers([
[1000, 'M'],
[900, 'CM'],
[500, 'D'],
[400, 'CD'],
[100, 'C'],
[90, 'XC'],
[50, 'L'],
[40, 'XL'],
[10, 'X'],
[9, 'IX'],
[5, 'V'],
[4, 'IV'],
[1, 'I']
]);
assert.equal (convertToRoman.format(2015), 'MMXV');
parseFunction(s)
does the reverse, parsing a string into a number. This function can be used for the fromDates
and fromYears
options.So:
assert.equal (convertToRoman.parse('MMXV'), 2015);
The parsing is very liberal in what it accepts and just ignores unrecognized characters. This may not be appropriate for some numeric systems and the output or input may need further processing.