Ski School Scheduler in JavaScript

In this blog post we will create the following timetable that shows the registered ski classes for a ski school week per week:

Ski School Scheduler

We use MindFusion JavaScript Scheduler to create the timetable. We use the BaseForm class to create the custom form that allows us to choose the level of the skier.

This is not possible in the standard appointment form that comes with the scheduling library.

I. General Settings

We need to add a DIV element with an id at the location where we want the timetable to appear on the page. Note that the size of the DIV determines the size of the calendar:

<div style="position: absolute; width: 100%; height: 97%;">



<div id="calendar" style="height: 97%; width: 100%;">
</div>


</div>

MindFusion.Scheduling requires a reference to MindFusion.Scheduling:

<script src="MindFusion.Scheduling.js" type="text/javascript"></script>
<script src="SkiSchoolSchedule.js" type="text/javascript"></script>
<script src="SkiStudentForm.js" type="text/javascript"></script>

We also add a reference to two JavaScript files that will contain the source code for our application: SkiSchoolSchedule and SkiStudentForm. Finally we add a reference to the CSS file that defines the theme used by the timetable – business.css:

 	<link rel="stylesheet" type="text/css" href="themes/business.css">

The Scheduling library offers a variety of CSS themes, which you can customize or use as a template to create new ones.

II. The Timetable

In the code-behind file we add a mapping to MindFusion.Scheduling namespace and then create the Calendar instance using the id of the DIV element that will render it:

var p = MindFusion.Scheduling;

// create a new instance of the calendar
calendar = new p.Calendar(document.getElementById("calendar"));

// set the view to Timetable, which displays the allotment of resources to distinct hours of a day
calendar.currentView = p.CalendarView.Timetable;
//set the theme to business as referenced
calendar.theme = "business";

We also set the currentView property to CalendarView and specify the theme – business as the name of the CSS file that we referenced.

The timetableSettings class exposes a dates property where we add the initial dates that will appear in the view:

//get the current date
var currDay = p.DateTime.today();
calendar.timetableSettings.dates.clear();

/* add dates to the timetable in such manner that always a full week
rom Mo to Su will be visible */
for(var i = 1; i &lt; 8; i++)
{
	calendar.timetableSettings.dates.add(currDay.addDays(-1 * currDay.dayOfWeek + i));
}

We get the current date and add in a cycle all 7 days of the week that contains it. We set the scrollStep property to 7, which indicates the number of days that will appear by initial click on one of the navigation arrows in the header:

// set the number of days to scroll with when a navigation button is clicked
calendar.timetableSettings.scrollStep = 7;

We will also use the startTime and endTime properties to indicate the start and and time of the timetable for each day. Those properties show the time as interval added to the start of the current day, in minutes. Thus a startTime value of 300 means the timetable starts 5 hours (5*60 min.) after midnight of the respective day:

// set the start time to 8:00 AM
calendar.timetableSettings.startTime = 480;
// set the end time to 18:00 PM
calendar.timetableSettings.endTime = 1020;

III. The Custom Form

We don’t want to use the standard form for creating appointments. We will create a custom one, you can see the difference between them at this image:

Ski School Scheduler

The left one is the custom form while to the right you can see the standard New Appointment form for calendar events.

The custom form for creating and editing ski lessons derives from the BaseForm class. Here is its constructor:

var SkiStudentForm = function (calendar, item, type)
{
        p.BaseForm.call(this, calendar, item);

	this._id = "SkiStudentForm";
	this._type = type;
	
	if(type == "new")
	this.headerText = "New Skiing Class";
else
	this.headerText = "Edit Skiing Class";

	this.levels = [{ value: 0, text: "beginner" }, { value: 1, text: "intermediate" }, { value: 2, text: "advanced" }];
}

We want each form to have a reference to the Calendar, to the Item that was created and a type. The type is simply a string and we recognize two types: new and edit e.g. whether we create a new class or edit an existing one.

Note that in the constructor we initialize a new variable called levels, which will provide data for the combo box with options for the skiing level of the student.

Then we call the prototype methods of the class and the constructor so we can initialize instances of SkiStudentForm:

SkiStudentForm.prototype = Object.create(p.BaseForm.prototype);
SkiStudentForm.prototype.constructor = SkiStudentForm;

The two methods that are responsible for drawing the contents of a custom BaseForm and its buttons are drawContent and drawButtons

We start with the drawContent method, where we create the first row:

SkiStudentForm.prototype.drawContent = function ()
{
	p.BaseForm.prototype.drawContent.call(this);

	var content = this.content;

	var row = this.row();
	row.className = "header-row";
	row.innerText = "From";
	content.appendChild(row);

	row = this.row();
	row.className = "data-row";
	row.innerHTML = this.item.startTime.toString("dddd, MMMM d,  HH:00", this.formatInfo);
	content.appendChild(row);
        ........................
}

We call the prototype of the drawContent method and we add a new row element. The row is an empty DIV. Then we add another row, this one contains a string. The string represents the start time of the Item .

We create the combo box using the createDropDownList method. Before we create the combo box we add a new DIV with the label. Then we add another DIV and the dropDownList element to it:

// create a drop-down list for status
row = this.row();
row.className = "header-row";
row.innerHTML = "Level";
content.appendChild(row);

var control = this.createDropDownList({ id: "level", items: this.levels, initValue: this.item.tag, addEmptyValue: false });
control.element.style.width = "200px";
this.addControl(control);

row = this.row();
row.className = "input-row";
row.appendChild(control.element);
content.appendChild(row);

The drawButtons method that we implement overrides the buttons of the BaseForm with new ones, styled as we want. Here the code for the Save button:

// override BaseForm's drawButtons method to create form buttons
SkiStudentForm.prototype.drawButtons = function ()
{
       var thisObj = this;

	var btnSave = this.createButton({
		id: "btnSave",
		text: "&#x2714;",
		events: { "click": function click(e)
		{
			return thisObj.onSaveButtonClick(e);
		}
		}
	});
btnSave.element.className = "form-button-save";

We use the createButton method to create the button, give it an id and assign a text to it. We also indicate the the click event will be handled by an implementation of the default onSaveButtonClick event for BaseForm

The Cancel button is the same, just the CSS styling for it is different. Both buttons use custom CSS class, which are assigned to them through the className property of HTML Dom elements. Here is the code for the appearance of the two buttons:

.form-buttons
{
	color: #fff;
	font-size: x-large;
	text-align: center;
	margin-top: 20px !important;
}
.form-button-save
{
	width: 80%;
	padding: 10px;
	background-color: #31bd41 !important;
}
.form-button-cancel
{
	width: 20%;
	padding: 10px;
	background-color: #ce0000 !important;
}

We create and render an instance of the form with the following lines of code:

var form = new SkiStudentForm(sender, item, "new");
form.showForm();

Here we create the form from an event handler for the Calendar class – we will look at that in the next section.

IV. Events

We show the student appointment form when a selection of cells is make. In order to do this we handle the selectionEnd method of the Calendar class:

// handle the selectionEnd event to show the custom form for item creation
calendar.selectionEnd.addEventListener(handleSelectionEnd);
function handleSelectionEnd(sender, args)
{	
	/* we create a new item with the selected start and end time
	var item = new p.Item();
	item.startTime = args.startTime;
	item.endTime = args.endTime;	
	item.tag = 0;	

	// create and show the custom form
	var form = new SkiStudentForm(sender, item, "new");
	form.showForm();
}

We create a new Item and we set its startTime and endTime to the start and end of the selected cell range. Then we use the current Calendar, which is provided as a sender, the newly created Item and “new” as type to create an instance of the SkiStudentForm and render it.

What shall we do if we want to use the custom form to edin an existing appointment? We will handle the itemDoubleClick event of the Calendar and show the SkiStudentForm. In this case we will get the Item that we want to edit and provide it as a parameter to the SkiStudentForm instance. We also change the type to be “edit”:

function handleItemDoubleClick(sender, args)
{	
	// show the custom form with data from the clicked item
	var form = new SkiStudentForm(sender, args.item, "edit");
	form.showForm();
}

These were the most important parts of the ski school scheduler application. The application has a few more, which we did not mention here but you can check them in code. The complete code, together with the themes and libraries used is available at:

Download the Ski School Scheduler Application

You can post technical questions, comments and recommendations about MindFusion Scheduling for JavaScript at the library online forum.

About Scheduling for JavaScript: MindFusion Js Scheduler is the right solution for all applications that need to render interactive timetables, rich event calendars, lists with appointments or resources. Fully responsive, highly customizable and easy to integrate, you can quickly program the JavaScript scheduling library according to your needs. The library supports a variety of export options, styling through themes, 6 calendar views and much more. Find out more at https://mindfusion.eu/javascript-scheduler.html

Interactive Event Timetable in JavaScript

This blog post describes the main steps on how to create a schedule table, which shows the allocation of college rooms to different courses. Users can filter the courses by lecturer(s).

I. Initial Setup

We start by copying the JavaScript scheduler files that we’ll use in the directory of our project. These are:

  • MindFusion.Scheduling.js – represents the Js Scheduler library
  • MindFusion.Scheduling-vsdoc.js – provides Intellisense support
    standard.css – in a subdirectory “themes”, this is the CSS theme styling of the resource table
  • planner_lic.txt – paste your license key here to disable the trial version label.

We create then 2 more files specifically for our application:

  • ResourceView.html – the web page of the application
  • ResourceView.js – the JavaScript code that implements the dynamic features of our application.

II. The HTML Page

In the head section of our web page we first create a reference to the theme file:

 	 	<link rel="stylesheet" type="text/css" href="themes/standard.css">

At the end of the web page, just before the closing </body> tag we add a reference to the Scheduling.js file that contains the scheduling features and the ResourceView.js files that we’ll write for the application:

<script src="MindFusion.Scheduling.js" type="text/javascript"></script>
<script src="ResourceView.js" type="text/javascript"></script>

The calendar library requires an HTML <div> element, which is used to render it. We add one:

<div id="calendar" style="height: 100%; width: 100%;"></div>

It is important that you add an id to this <div> because we need to reference it in the JS code behind file.

III. Basic JavaScript Settings

At the top of the JavaScript code-behind file we add a reference to the Intellisense file. We also create a mapping to MindFusion.Scheduling namespace:

/// <reference path="MindFusion.Scheduling-vsdoc.js"></reference>
var p = MindFusion.Scheduling

Then we create the calendar object. We need a reference to the <div> element that will render it:

// create a new instance of the calendar
calendar = new p.Calendar(document.getElementById("calendar"));

For this sample we will use the ResourceView The currentView property specifies that. In addition, we set the count of visible cells in the calendar to 7. That is done through the resourceViewSettings property of the calendar

// set the view to ResourceView, which displays the distribution of resources over a period of time
calendar.currentView = p.CalendarView.ResourceView;

// set the number of visible cells to 7
calendar.resourceViewSettings.visibleCells = 7;

The itemSettings proeprty lets us customize the items in the schedule We use titleFormat and tooltipFormat to specify how the title and tooltip of each item will be rendered. Both properties use special format strings:

  • %s – the start date will be rendered
  • %e – the end date of the item will be rendered
  • %d – the details of the item will be rendered.

You can specify the way dates and time are formatted by adding the desired format in brackets:

// show hours on items
calendar.itemSettings.titleFormat = "%s[HH:mm] - %e[HH:mm] %h";
calendar.itemSettings.tooltipFormat = "%d";

Then we set the theme of the calendar to standard, whose css file we referenced in the web page:

calendar.theme = "standard";

and we make one more adjustment – the name of contacts will be taken from the last name of the person. Possible valies are “F”, “M” and “L” – for first, middle and last name.

calendar.contactNameFormat = "L";

IV. Resources

When the calendar initially loads there are several contacts and locations available. The objects that represent them are instances of the Contact and Location classes. After we create them we add them to the contacts and locations collections of the calendar schedule.

var resource;

// Add professor names to the schedule.contacts collection.
resource = new p.Contact();
resource.firstName = "Prof. William";
resource.lastName = "Dyer";
calendar.schedule.contacts.add(resource);

resource = new p.Location();
resource.name = "Room D";
calendar.schedule.locations.add(resource);

Now, when the user creates a new course they will see the Contact and Location in the Options pane of the “Create Item” form:

V. Items

The items are instances of the Item class. They represent the classes of the different lecturers. We use the startTime and endTime properties of Item to specify when the class takes place. The subject property gives the name of the class:

//always start with the current date
var date = p.DateTime.today();

item = new p.Item();
item.startTime = p.DateTime.addHours(date.addDays(1), 14);
item.endTime = p.DateTime.addHours(item.startTime, 1);
item.subject = "Classical Mechanics";

We use the location and contacts properties to set where the lecture takes place and who teaches it. Note that the contacts property is of type collection and we can assign several lecturers to one class:

item.location = calendar.schedule.locations.items()[0];
item.contacts.add(calendar.schedule.contacts.items()[0]);

We get the location and the contact from the schedule’s lists with locations and contacts We must also set the details of the item – they will be rendered as a tooltip, if you remember. We want the tooltip to show the two names of the lecturer and the location. Here is how we must define it:

item.details = item.contacts.items()[0].firstName + " " +
item.contacts.items()[0].lastName + " - " + item.location.name;

We must add the item to the items collection of the schedule we render the calendar render the calendar

calendar.render();

VI. Events

When users create new items we must set their details to tell the name and the location of the new class. We handle the itemCreating event to do this:

// attach handler - creating an item
calendar.itemCreating.addEventListener(handleItemCreating); 

function handleItemCreating(sender, args) {
    handleItemModified(sender, args);
    if (args.item.contacts.count() &gt; 0) {
        //the details field is used by the tooltip
        args.item.details = args.item.contacts.items()[0].firstName + " " +
                args.item.contacts.items()[0].lastName;

        if (args.item.location != null)
            args.item.details += " - " + args.item.location.name;
    }

}

The itemCreating event provides an instance of the ItemModifyingEventArgs class as a second argument to the handler method. There we use the item property that tells us which item is being modified. We then take the desired contact and Location information from the contacts and location properties of the item.

When a new course item is dragged to another location we must change its color accordingly. We do this by handling the itemModified event.

// attach handler - modifying an item
calendar.itemModified.addEventListener(handleItemModified);

The diferent background color of the items is achieved by custom CSS classes. We use the cssClass property of the Item class. The CSS styles are defined in the <HEAD> section of the web page:

 .mfp-planner.standard .itemClass1 .mfp-item {
            background-color: 	#0c71af;
        }

.mfp-planner.standard .itemClass2 .mfp-item {
            background-color: #f81e1e;
        }
...........

The handler method checks the new location and assigns the appropriate CSS style:

function handleItemModified(sender, args)
{
    // you don't have to check any other conditions like dragging to another room, 
    // as it will stay the same color if you make other changes (other than dragging to a different room)
    if (args.item != null){
        switch (args.item.location.name) {
            case "Room A":  //we can also implement it with for
                args.item.cssClass = 'itemClass1';
                console.log("a");
                break;
            case "Room B":
                args.item.cssClass = 'itemClass2';
                break;
            case "Room C":
                args.item.cssClass = 'itemClass3';
                break;
            case "Room D":
                args.item.cssClass = 'itemClass1';
                break;
            case "Room E":
                args.item.cssClass = 'itemClass2';
                break;
            case "Room F":
                args.item.cssClass = 'itemClass3';
                break;
            default:
                args.item.cssClass = 'itemClass1';
        }
    }
}

The item property of the args parameter of the handler method provides access to the item that was modified.

VII. Filtering Professors

We want to add one last feature to our application. We want the user to be able to render courses only by a given professor.

We first add checkboxes with the names of the lecturers. Each checkbox has the same handler method for the click event:

<input id="dyer" checked="checked" name="subscribe" type="checkbox" value="Dyer>
<label for=">Prof. William Dyer

<input id="fletcher" checked="checked" name="subscribe" type="checkbox" value="Fletcher">
<label for="fletcher">Prof. Ann Fletcher</label>
...........................

The handler method needs to look at two cases. The first case is when the class is taught by a single professor. In this case we cycle through all items and make the item visible or not depending on whether the check box with the name of the professor is checked:

// if there is at least one present professor from the lecture professors, the lecture will not disappear
function handleClick(cb) {
for (var i = 0; i &lt; calendar.schedule.items.count(); i++) {
        var item = calendar.schedule.items.items()[i]; //we iterate through every element
        if (item.contacts.count() == 1) {
            if (item.contacts.items()[0].lastName == cb.value)
                item.visible = cb.checked;
        }
      }
.......................
}

In the second case we look at courses that are taught by more than one lecturer. In this case we show the item if the checkbox with the name of at least one of the lecturers is selected:

else if (item.contacts.count() &gt; 1) {
for (var j = 0; j &lt; item.contacts.count() ; j++) {
                if (item.contacts.items()[j].lastName == cb.value) { // the checked/unchecked professor is in the contacts of this item
                    if (cb.checked == true) item.visible = true; // if there is a check, the item must be visible
                    else { // if there is no check, we see if there is at least one professor in the list of contacts of the item
                        item.visible = professorPresent(item);
                    }

                }
            }
        }

Finally we repaint the calendar:

// repaint the calendar
this.calendar.repaint(true);

Here the professorPresent method checks if at least one of the check boxes with professors that are present as lecturers in the item that we provide as argument are selected:

// return true if even 1 professor from the item's contacts is present, false otherwise
function professorPresent(item) {
    console.log(item.contacts.count());
    for (var j = 0; j &lt; item.contacts.count() ; j++) {
        var checkBoxId = item.contacts.items()[j].lastName.toLowerCase();
        var checkBox = document.getElementById(checkBoxId);
        if (checkBox!= null &amp;&amp; checkBox.checked == true) {
            return true;
        }
    }
    return false;
}

And that’s the end of this blog post. Here is a link to download the complete source code of this application:

Download The Sample Resource View Application

About MindFusion JavaScript Scheduler: MindFusion Js Scheduler is the complete solution for all applications that need to render interactive timetables, event schedules or appointment calendars. Fully responsive, highly customizable and easy to integrate, you can quickly program the JavaScript scheduling library according to your needs. Find out more at https://mindfusion.eu/javascript-scheduler.html