This is the documentation of the new Dynarch Calendar (“JSCal2”). Note that it is not compatible with our old calendar project. The new calendar has less predefined ways to setup, and rather focuses on an extensive API that you can use to do what you want. Because it's a lesson I've learned that you can't please everybody, so it's better to provide lots of features so that people can please themselves. ;-)

Contents

Contents
Installation
Basic setup — constructor
Inline calendar example
Popup calendar example
onSelect event handler
Selection object
Disabling dates
Highlight special dates, date tooltips
Calendar object reference
moveTo(date, animated)
isDisabled(date)
toggleMenu()
refresh(noBody)
redraw()
setLanguage(code)
focus()
blur()
showAt(x, y, animated)
hide()
popup(anchor, align)
manageFields(trigger, inputField, dateFormat)
getTime()
setTime(time, [ nohooks ])
getHours()
getMinutes()
setHours(h)
setMinutes(m)
addEventListener(event, func)
removeEventListener(event, func)
Global (static) utility functions
Calendar.dateToInt(date)
Calendar.intToDate(numeric_value)
Calendar.printDate(date, format)
Calendar.parseDate(dateString, monthFirst, today)
Calendar.formatString(str, prop)

Installation

Unzip the archive and copy the "src" subdirectory into a path such that you can access jscal2.js using the following URL:

http://www.yourdomain.com/JSCal2/js/jscal2.js

(i.e. the “src” directory should become “JSCal2” in the document root of your website).

Here is a sample <head> section that loads the base CSS file, a color theme and the required JS files:

<!DOCTYPE html PUBLIC
          "-//W3C//DTD XHTML 1.0 Transitional//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="/JSCal2/css/jscal2.css" />
    <link rel="stylesheet" type="text/css" href="/JSCal2/css/border-radius.css" />
    <link rel="stylesheet" type="text/css" href="/JSCal2/css/gold/gold.css" />
    <script type="text/javascript" src="/JSCal2/js/jscal2.js"></script>
    <script type="text/javascript" src="/JSCal2/js/lang/en.js"></script>
  </head>

jscal2.css is essential and defines a minimal skin. You can optionally include border-radius.css to add round corners in browsers that support it (Firefox, Safari and Chrome) and load an additional color theme if you like (gold/gold.css).

The required JavaScript files that you need to load are jscal2.js and at least one language file (for example en.js).

Also note the DOCTYPE line. It is important for older versions of Internet Explorer to enter “standards mode”. IE8 should work fine without it.

Basic setup — constructor

Calendar.setup(args) simply constructs a new calendar and is equivalent to calling new Calendar(args). args is a JavaScript hash that can have the following properties:

All arguments are optional. However, you should provide one of cont (for inline calendars) or trigger for popup calendars, or else provide your own code to display a popup calendar. We'll see how this can be done later.

Inline calendar example

For example here is how we created the calendar on the front page:

<table style="float: left; margin: 0 1em 1em 0"><tr><td>

  <!-- element that will contain the calendar -->
  <div id="calendar-container"></div>

  <!-- here we will display selection information -->
  <div id="calendar-info" style="text-align: center; margin-top: 0.3em"></div>

</td></tr></table>
<script type="text/javascript">//<![CDATA[
Calendar.setup({
    cont          : "calendar-container",
    weekNumbers   : true,
    selectionType : Calendar.SEL_MULTIPLE,
    selection     : Calendar.dateToInt(new Date()),
    showTime      : 12,
    onSelect      : function() {
        var count = this.selection.countDays();
        if (count == 1) {
            var date = this.selection.get()[0];
            date = Calendar.intToDate(date);
            date = Calendar.printDate(date, "%A, %B %d, %Y");
            $("calendar-info").innerHTML = date;
        } else {
            $("calendar-info").innerHTML = Calendar.formatString(
                "${count:no date|one date|two dates|# dates} selected",
                { count: count }
            );
        }
    },
    onTimeChange  : function(cal) {
        var h = cal.getHours(), m = cal.getMinutes();
        // zero-pad them
        if (h < 10) h = "0" + h;
        if (m < 10) m = "0" + m;
        $("calendar-info").innerHTML = Calendar.formatString("Time changed to ${hh}:${mm}", {
            hh: h,
            mm: m
        });
    }
});
//]]></script>

Popup calendar example

Unlike inline calendars, a popup calendar isn't displayed at all times, but it pops up whenever it's needed, such as when the user presses a “select date” button. In popup mode you'll normally want to use the SEL_SINGLE (default) selection type, though it's perfectly possible to use multiple selection.

Here's an example of how to create a popup calendar:

<input id="calendar-inputField" /><button id="calendar-trigger">...</button>
<script>
    Calendar.setup({
        trigger    : "calendar-trigger",
        inputField : "calendar-inputField"
    });
</script>

onSelect event handler

You can supply a callback that gets triggered when the selection changes. For example, you might have noticed that the calendar doesn't disappear when you select a date in “popup” mode. Here's how we could make it disappear:

Here is the code that I used for the example above:

Calendar.setup({
    inputField : "calendar-field",
    trigger    : "calendar-trigger",
    onSelect   : function() { this.hide() }
});

Why doesn't it just close without us needing to add the onSelect handler? Because not everyone wants it to close—therefore, the new calendar just lets you define the behaviour by writing your own event handler.

Selection object

Each calendar object, as returned by Calendar.setup(args) or new Calendar(args), contains a "selection" property. This is an object that you can use to query, clear or reset the current selection.

See the documentation of Selection object.

Disabling dates

You can disable dates in two ways. First off, you can use the min and max arguments to the constructor in order to limit the period that the calendar will allow selection in. Example:

The above calendar allows selection between 8 April and 25 December 2009. Here's the code that we used:

Calendar.setup({
    cont: "sample1",
    min: 20090408,
    max: 20091225
});

You can also disable individual dates by supplying a callback function which receives a JS date object and returns true if that date must be disabled, or false otherwise. Here's an example which disables all Fridays:

Here is the code for the sample above:

Calendar.setup({
    cont: "sample2",
    disabled: function(date) {
        if (date.getDay() == 5) {
            return true;
        } else {
            return false;
        }
    }
});

You can write anything you want in the disabled() handler—just try to make it fast enough, as it will be called many times within milliseconds when the calendar is displayed. Here's one more example, suppose you have in your database a list of dates that should be disabled, just convert them to numeric value and build up a hash table, then your disabled() handler can very conveniently return true for them:

var DISABLED_DATES = {
    20090502: true,
    20090505: true,
    20090510: true,
    20090511: true
};
Calendar.setup({
    cont     : "sample3",
    disabled : function(date) {
        date = Calendar.dateToInt(date);
        return date in DISABLED_DATES;
    }
});

Highlight special dates, date tooltips

The calendar has a feature that allows you to highlight certain dates, or display tooltips when certain dates are hovered. To use this you need to provide a function (using the dateInfo constructor argument) that receives a JavaScript Date object. It can return null (or undefined) if the date isn't special, or a hash containing either or both of the following properties:

Here is an example that highlights 7 and 8 May 2009 in red, week 18..24 May 2009 in green and my birthday with a white background every year.

We got that with the code below:

var DATE_INFO = {
  20090507: { klass: "highlight", tooltip: "That was yesterday" },
  20090508: { klass: "highlight", tooltip: "And this is TODAY" }
};

function getDateInfo(date, wantsClassName) {
  var as_number = Calendar.dateToInt(date);
  if (String(as_number).indexOf("0308") == 4) {
    // my birthday :-p
    return { klass: "birthday", tooltip: "Happy birthday dear me!" };
  }
  if (as_number >= 20090518 && as_number <= 20090524)
    return {
      klass   : "highlight2",
      tooltip : "<div style='text-align: center'>%Y/%m/%d (%A)" +
                "<br />In the green week</div>" // formatted by printDate
    };
  return DATE_INFO[as_number];
};

var cal = Calendar.setup({
  cont     : "sample4",
  fdow     : 1,
  date     : 20090501,
  dateInfo : getDateInfo // pass our getDateInfo function
});

It's up to you how you write this function. In the sample above we showed how we can use an external variable (DATE_INFO) that holds the klass and tooltips, as well as converting the date to a number and easily check that it's within a certain interval (to highlight “the green week”). For my birthday I used a hack—convert the date to a string and check that it ends in "0308" (so it will match March 8, any year). The following CSS is included in this page:

.highlight { color: #f00 !important; font-weight: bold }
.highlight2 { color: #090 !important; font-weight: bold }
.birthday { background: #fff; font-weight: bold }
.birthday.DynarchCalendar-day-selected { background: #89f; font-weight: bold }

Calendar object reference

Assuming you went through the above and read the basics of Selection object you should know enough to use this calendar to full potential. For completeness, following where is detalied API documentation.

moveTo(date, animated)

Moves the calendar to the given date's year/month (this doesn't change the selection!). If you pass true for animated (default is false) and if animation wasn't disabled through the constructor, then it will animate the switch.

isDisabled(date)

Returns true if the given date (a JavaScript Date object) can be selected.

toggleMenu()

Shows or hides the menu.

refresh(noBody)

Refreshes the calendar body, title and year input field. If noBody is true then it will not refresh the body. This is used internally, see the next one if you need to redraw the calendar.

redraw()

Redisplays the whole calendar. You need to call this if you set certain arguments at run time (i.e. min, max, disabled, dateInfo, etc.). You don't need to call it if you modify the selection as it is refreshed automatically.

setLanguage(code)

Sets a new language for the calendar. Pass the language code. More info in internationalization.

focus()

Moves focus to the calendar. After you call this, the calendar handles keyboard.

blur()

Removes focus from the calendar.

showAt(x, y, animated)

Only for popup calendars. Shows the calendar at the given (x, y) position, optionally animated if animated is true and animation was not disabled through constructor.

hide()

Only for popup calendars. Hides the calendar.

popup(anchor, align)

You want to use this instead showAt() in order to display the calendar near a certain element. anchor is a reference to a DOM element, or the ID of an element where the calendar is anchored. align is optional (if not given, the one passed to constructor is used, which defaults to "Bl/ / /T/r".

The align decides where is the calendar displayed in relation to the given anchor element. It is formed of 5 parts joined by "/", in the following order:

  1. preferred alignment if there is enough room
  2. fallback alignment if it fails on the top
  3. fallback alignment if it fails on the right
  4. fallback alignment if it fails on the bottom
  5. fallback alignment if it fails on the left

Each of them can contain one or two characters. For example, "Bl" means put it completely to the bottom, and at the left (this actually means right-aligned!) to the anchor. See the DlPopup object in DynarchLIB for a comprehensive description of what the alignment characters mean. For the calendar, the significance is pretty much the same except for centering characters ("c"). The calendar uses a "C" for centering horizontally, and "M" for centering vertically; as this ambiguity now no longer exists, you can pass these characters in any order.

manageFields(trigger, inputField, dateFormat)

You can use this function to setup a single (popup) calendar instance for multiple input fields. See a simple demo. Call this against a calendar object and pass the ID (or a reference) to the trigger button, the input field and the date format as string.

getTime()

For a calendar configured to display the time selector, this function returns the currently selected time as an integer (hours * 100 + minutes). For example, if 9:45 pm is selected, this method returns 2145. See also getHours() and getMinutes() below.

setTime(time, [ nohooks ])

Sets the currently selected time. The time argument is in the same integer format. nohooks is optional (default false). If specified true then this method will not call the onTimeChange hooks. If unspecified or false the onTimeChange hooks will be called two arguments: a reference to the calendar object, and the new time as integer.

getHours()

Returns the currently selected hour as an integer (0 .. 23). Note that this method will return hours in 24h format regardless if the calendar is currently set in "12h" mode (i.e. when showTime == 12).

getMinutes()

Returns the currently selected minute as an integer (0 .. 59).

setHours(h)

Sets the hour in the time selector. The argument must be an integer (0 .. 23).

setMinutes(m)

Sets the minutes in the time selector. The argument must be an integer (0 .. 59). Note that this method does not force m to a multiple of minuteStep.

addEventListener(event, func)

Adds a handler for the given event. As of this writing, event can be "onSelect" and "onChange". Note that multiple handlers can exist for an event and they will be called in the order they were registered.

Update: new events added in version 1.5: "onTimeChange", "onFocus" and "onBlur".

removeEventListener(event, func)

Removes the given func handler from the list of registered hooks for the given event.

Global (static) utility functions

The following functions can be used globally, without a reference to a Calendar object.

Calendar.dateToInt(date)

Expects a JavaScript Date object in date and returns the numeric representation. For example, for May 8 2009 it will return 20090508.

Calendar.intToDate(numeric_value)

Takes a “numeric date value” and returns a JavaScript Date object. It's the reverse of Calendar.dateToInt().

Calendar.printDate(date, format)

Takes a JavaScript Date object in date and a format specifier and returns a string converting the following:

Calendar.parseDate(dateString, monthFirst, today)

This function tries to parse a meaningful date from dateString and return a JavaScript Date object. In ambiguous situations (i.e. 3-4-2009) it needs to know if the first digit is the month or the date, which is what monthFirst is for. Examples:

alert(Calendar.parseDate("3-4-2009", true)); // Mar 04 2009
alert(Calendar.parseDate("3-4-2009", false)); // Apr 03 2009

This function is very forgiving about the format and tries really hard to make up a full date. If it cannot find the year, it will use the year in today. Same goes for the month. It also supports named months, rather than numeric (in such case there is no ambiguity so monthFirst is ignored).

Calendar.formatString(str, prop)

A simple templating facility. str is a string template, which can contain variables, like this:

"Hello ${first_name} ${last_name}"

The values for the variables are given in prop. Here's an example:

alert(Calendar.formatString("Hello ${first_name} ${last_name}",
                            { first_name: "John",
                              last_name: "Doe" }));
// displays "Hello John Doe"