Latest release: v1.2.2
Make a beautiful embeddable booking widget in minutes.
Uses FullCalendar with a custom theme for dynamic calendar rendering with available timeslots fetched from Timekit (through the Javascript SDK). The shown appointment slots can be booked with automatic calendar invites sent to both host and visitor. Integrates with Google Calendar for automatic availability.
Maintainer: Lasse Boisen Andersen (la@timekit.io). PR's are welcome!
Visit booking.timekit.io to set up your account and generate a config.
Booking.js is meant as an easy to use, drop-in script that does it's job without any coding required. It's made for the browser and is quite similar to Stripe's Checkout.js.
This repo is mainly for community contributions and the curious soul that would like to customize the widget beyond settings provided in the wizard.
Stuff you need to load:
- jQuery - primarily because it's a requisite for FullCalendar
Bundled together with the library:
- fullCalendar - a customizable and flexible event calendar built for the browser
- moment - parse, validate, manipulate, and display dates in JavaScript
- timekit-js-sdk - JavaScript SDK for the Timekit.io API
To ensure that we can push out updates, improvements and bugfixes to the library, you should load the library through our CDN. It's hosted on Amazon Cloudfront so it's snappy enough for production.
The simplest and most universally compatible usage is with autoload:
<div id="bookingjs">
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.timekit.io/bookingjs/1/booking.min.js" defer></script>
<script type="text/javascript">
window.timekitBookingConfig = {
email: 'marty.mcfly@timekit.io',
apiToken: 'bNpbFHRmrfZbtS5nEtCVl8sY5vUkOFCL',
calendar: '8687f058-5b52-4fa4-885c-9294e52ab7d4',
name: 'Marty McFly',
avatar: '../misc/avatar-mcfly.png'
};
</script>
</div>
If you intent to run multiple instances or want more control:
<div id="bookingjs">
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script type="text/javascript" src="//cdn.timekit.io/bookingjs/1/booking.min.js"></script>
<script type="text/javascript">
var widget = new TimekitBooking();
widget.init({
email: 'marty.mcfly@timekit.io',
apiToken: 'bNpbFHRmrfZbtS5nEtCVl8sY5vUkOFCL',
calendar: '8687f058-5b52-4fa4-885c-9294e52ab7d4'
});
</script>
</div>
See /examples
for more implementation examples.
The widget connects to the Timekit API behind the scenes and requires a Timekit account.
You can either connect with a Google account (recommended) or create a plain account (you'd have to enter availability and pull out events through the API). Visit the setup wizard here.
The apiToken
setting is the key part here. When you specify domain and generate credentials in the setup wizard, you get a special client-token with limited access. It's only capable of hitting certain endpoints so your account stays secure when using the widget in a public browser environment.
Booking.js is made for various use-cases, so it's really extensible and customizable. We augment all the intrinsic options so you can overwrite them as needed, e.g. Timekit FindTime options or FullCalendar settings.
{
// Required
email: '', // Your Timekit user's email (used for auth)
apiToken: '', // Your Timekit user's apiToken (as generated through the wizard)
calendar: '', // Your Timekit calendar ID that bookings should end up in
// Optional
targetEl: '#bookingjs', // Which element should we the library load into
name: '', // Display name to show in the header and timezone helper
avatar: '', // Provide an image URL for a circular image avatar
autoload: true, // Auto initialization if config object is found on window var
includeStyles: true, // Inject fullCalendar and library styles in <head>
showCredits: true, // Display a "Powered by Timekit" attribution footer (thanks!)
// Internationalization
localization: {
showTimezoneHelper: true, // Should the timezone difference helper (bottom) be shown?
timeDateFormat: '12h-mdy-sun' // For EU-style formatting, use '24h-dmy-mon'
}
// Timekit JS SDK (see below)
timekitConfig: { ... },
// Timekit FindTime endpoint (see below)
timekitFindTime: { ... },
// Timekit CreateEvent endpoint (see below)
timekitCreateEvent: { ... },
// FullCalendar options (see below)
fullCalendar: { ... },
// Register callbacks on events
callbacks: {
findTimeStarted: function(args) {},
findTimeSuccessful: function(response) {},
findTimeFailed: function(response) {},
createEventStarted: function(args) {},
createEventSuccessful: function(response) {},
createEventFailed: function(response) {},
getUserTimezoneStarted: function(args) {},
getUserTimezoneSuccesful: function(response) {},
getUserTimezoneFailed: function(response) {},
fullCalendarInitialized: function() {},
renderCompleted: function() {},
showBookingPage: function() {},
closeBookingPage: function() {},
submitBookingForm: function() {}
}
}
Timekit SDK
You can pass any of the Timekit JS SDK settings directly to the widget. This is mostly revelant if you're building a tighter integration with Timekit and have your own app registered on the platform.
timekitConfig: {
app: 'bookingjs' // Default
}
Timekit Find Time
The Find Time algorithm is a powerful query tool for availability. Booking.js is calling the endpoint [POST]/findtime
through the JS SDK and takes all the arguments as mentioned on the official docs. The most powerful aspect are the filters. By default, there's no filters applied.
There's only three default arguments out of the box:
timekitFindTime: {
future: '4 weeks', // Default, how long time into the future that timeslots should be returned
length: '1 hour', // Default, the duration of the bookable timeslots
emails: [config.email], // Inserted from the "email" setting in the general config
},
Timekit Create Event
When booking an event, the widget will call the [POST]/events
endpoint through the JS SDK, with the following settings:
timekitCreateEvent: {
where: 'Online', // Default, you may want to customize this to a specific location, TBD or whatever fits
invite: true, // Default, makes sure that participants (the visitor) is sent a Google invite
my_rsvp: 'needsAction', // Default, makes sure that the host also will be able to RSVP to the created event
start: data.start, // Inserted from the chosen timeslot
end: data.end, // Inserted from the chosen timeslot
what: config.name + ' x '+ data.name, // Inserted based on the host and visitors names (you can replace it with a static string)
calendar_id: config.calendar, // Inserted from the "calendar" setting in the general config
participants: [config.email, data.email], // Inserted based on host and visitors ()
description: data.comment || '' // Inserted based on the visitor's supplied comment
},
FullCalendar
You can supply and override all the FullCalendar settings:
fullCalendar: {
header: {
left: '',
center: '',
right: 'today, prev, next'
},
views: {
agenda: {
displayEventEnd: false
}
},
allDaySlot: false,
scrollTime: '08:00:00',
timezone: 'local',
defaultView: sizing.view, // Inserted based on the current width of the widget
height: sizing.height, // Inserted based on the current width of the widget
eventClick: function(event), // Handled internally in Booking.js (overwrite if you want to replace the booking page)
windowResize: function(view) // Recalculates the view and height based on the widget's width (if resized)
}
See below for FullCalendar language support.
Localization
For quick localization of time/date formats, we provide a simple "preset" setting, timeDateFormat
, that sets a range of different FullCalendar and localization settings.
By default, it's set to "12-hour clock, M/D/Y date format, Sunday first day of week" (12h-mdy-sun
). It can be changed to "24-hour clock, D/M/Y date format, Monday first day of week" (24h-dmy-mon
).
localization: {
timeDateFormat: '12h-mdy-sun', // Default, alternative mode "24h-dmy-mon",
bookingDateFormat: 'MMMM D, YYYY', // Override the default date format on the booking page
bookingTimeFormat: 'h:mma' // Override the default time format on the booking page
},
For full language support, FullCalendar also takes a "lang" option, accompanied by a language file. Make sure to use defer attribute on a script tag loading the language file if you are deferring booking.js, language file should be loaded after booking.js, but before initialization.
Remember to set localization.timeDateFormat
to false so it doesn't override the language file's settings. See /examples/language.htm
for full implementation example.
fullCalendar: {
lang: 'de'
},
localization: {
timeDateFormat: false
}
After you instantiated the widget, you can control it with the following methods:
var widget = new TimekitBooking();
widget.init(config); // Initalizes the widget with the given config
widget.render(); // Re-renders the widget with it's instance config
widget.setConfig(config); // Push a new config into it (call render() afterwards)
widget.getConfig(); // Returns the current config
widget.destroy(); // Cleans the DOM element and empty config
widget.fullCalendar(action); // Direct access to FullCalendar's own method (for advanced use)
See Issues for feature requests, bugs etc.