Статьи

Создание мобильного календаря событий с DHTMLX

В этом руководстве описывается, как создать мобильный календарь на основе HTML5 для отслеживания конференций и событий, которые запускаются на телефонах iOS и Android, с использованием мобильной версии dhtmlxScheduler (с открытым исходным кодом, GPL). В конце пользователи смогут добавлять и редактировать события, выбирать местоположение конференции на Картах Google и просматривать события в виде дня, месяца или списка.


Заключительная демонстрация включает календарь событий (мы также назовем его планировщиком), который отображает события в трех стандартных представлениях: список, день и месяц. Пока конечный пользователь добавляет, удаляет или редактирует события, приложение календаря сохраняет изменения на сервере и обновляет базу данных. Демонстрация также автоматически определяет используемый языковой стандарт и соответствующим образом адаптирует интерфейс календаря. Конечный пользователь может установить местоположение события в Картах Google двумя способами: ввести адрес или указать его в Картах Google.


Сначала загрузите последнюю версию мобильной версии dhtmlxScheduler. Он основан на среде DHTMLX Touch и распространяется по лицензии с открытым исходным кодом (GNU GPL).

Мобильный планировщик DHTMLX имеет так называемую многоуровневую архитектуру. Он отображает набор экранов, которые переключаются между собой. Каждый экран представляет собой единственное представление, где вы можете добавить любой контент. По умолчанию планировщик содержит следующие представления:

  • Экран предварительного просмотра события (отображает краткую информацию о событии)
  • Форма сведений (используется для редактирования сведений о событии)
  • Форма редактирования начала / окончания (встроенные датчики для выбора даты начала и окончания события)
  • Представления календаря: список (повестка дня), день и месяц

Предопределенные представления можно настраивать (определения по умолчанию можно найти в документации ). Также есть возможность добавлять новые пользовательские виды.


Мы начнем с необходимых файлов кода для отображения мобильного планировщика на странице: dhxscheduler_mobile.js и dhxscheduler_mobile.css .

Добавьте следующий код на свою страницу:

1
2
3
4
5
6
7
dhx.ready(function(){
        dhx.ui.fullScreen();
        dhx.ui({
            view: «scheduler»,
            id: «scheduler»
        });
 });

Вот что делают эти строки кода:

  • вызов функции dhx.ready() гарантирует, что код, размещенный внутри, вызывается после того, как страница была полностью проанализирована, защищая ее от потенциальных ошибок Это необязательно, но я призываю вас использовать его.
  • dhx.fullScreen() активирует полноэкранный режим.
  • dhx.ui({}) — это конструктор объектов для планировщика (используется в библиотеке DHTMLX Touch).
  • view: "scheduler" — устанавливает компонент для рендеринга («scheduler» — это жестко запрограммированное имя мобильного планировщика).
  • id — это необязательный параметр, который устанавливает идентификатор объекта. Мы указали этот параметр, так как будем ссылаться на этот объект позже.

После того, как мы включили необходимые файлы и добавили приведенный выше код, календарь отображается на странице:

Календарь мобильных событий - месяц

Планировщик имеет простой и понятный интерфейс, поэтому вы можете использовать его как есть. Он достаточно гибкий, поэтому вы можете настроить его внешний вид так, как вам нужно.


Для загрузки данных в календарь мы будем использовать метод load() . Поскольку мы собираемся загрузить данные с сервера, мы должны установить единственный параметр (путь к скрипту сервера).

Чтобы обеспечить возможность сохранения данных обратно на сервер, нам нужно определить еще один параметр в конструкторе объектов — save . Нам также нужно указать тот же серверный скрипт, который мы указали в методе load() .

1
2
3
4
5
6
dhx.ui({
              view: «scheduler»,
            id: «scheduler»,
            save:»data/data.php»
});
$$(«scheduler»).load(«data/data.php»);

Чтобы обратиться к планировщику через его идентификатор, мы используем запись $$("scheduler") .

Чтобы обратиться к представлениям планировщика (или элементам представлений), вы должны написать полную цепочку наследования свойств зависимостей: $$('scheduler"').$$('view_id').$$('viewElement_id')... You можете проверить идентификаторы элементов в соответствующей документации .

Файл data.php содержит логику связи клиент-сервер для интеграции со стороной сервера. Чтобы определить логику, мы будем использовать специальный помощник dhtmlxConnector, который реализует всю рутину (существуют версии для Java, .NET, PHP и ColdFusion, и его можно использовать с виджетами пользовательского интерфейса dhtmlx). Вы можете получить более подробную информацию об использовании dhtmlxConnector здесь .

Мы будем использовать версию PHP и создадим следующий код в файле data.php :

1
2
3
4
5
6
7
<?php
     include (‘../connectors/scheduler_connector.php’);
     $res=mysql_connect(«localhost»,»root»,»»);
     mysql_select_db(«schedulertest»);
     $scheduler = new JSONSchedulerConnector($res);
     $scheduler->render_table(«events_map»,»event_id»,»start_date,end_date,event_name,details,event_location,lat,lng»);
?>

Строки кода делают следующее:

  • Первая строка содержит необходимый файл кода.
  • Следующие две строки устанавливают соединение с сервером и открывают указанную базу данных SQL.
  • Последние две строки создают объект коннектора ($ scheduler) и конфигурируют данные для извлечения.

В прилагаемом демонстрационном пакете вы можете найти файл дампа базы данных «schedulertest».

По завершении этого шага у нас есть календарь, заполненный нашими демонстрационными данными:

Календарь мобильных событий - загружен данными

На этом этапе мы сделаем наш календарь способным адаптироваться к конкретному языку и региону.

Во-первых, нам нужно указать объект локали (в библиотеке он имеет жестко заданное имя — «локали» ), который будет определять все метки, используемые в календаре.

Мы создадим объект в отдельном файле, чтобы не «переполнять» основной файл .html. Файл будет выглядеть так:

1
2
3
4
5
var locales ={
    «de»: {…},
    «en»: {..},
    …
}

Чтобы увидеть полную версию объекта «locales» , откройте файл locales.js, включенный в папку «codebase» пакета загрузки. В нашем примере мы включили локали только для двух языков (английский и немецкий). При необходимости вы можете добавить локаль любого другого языка в этот файл.

Затем мы включаем файл locales.js на странице:

1
<script charset=»utf-8″ type=»text/javascript» src=»../mobile_scheduler/codebase/locales.js»></script>

Затем добавьте следующий код в HTML-файл:

1
2
3
4
5
6
7
//put this code at the very beginning of the page and not into the dhx.ready() function
var locale = (navigator.language || navigator.systemLanguage || navigator.userLanguage ||’en’).substr(0, 2).toLowerCase();
        if (!locales[locale])
            locale = ‘en’;
 
        scheduler.locale.labels=locales[locale];
        dhx.Date.Locale = locales[locale].calendar;

Строки кода делают следующее:

  • Первая строка получает языковую версию браузера.
  • Следующие несколько строк устанавливают локаль в зависимости от значения, возвращаемого первой строкой.
  • scheduler.locale.labels устанавливает язык общих меток в планировщике.
  • dhx.Date.Locale устанавливает языковой стандарт для календарей, используемых в планировщике.

Календарь с немецким языком выглядит так:

Мобильный календарь событий - немецкий язык

Разве не было бы замечательно, если бы пользователи могли видеть место, где происходит событие? Вот список шагов, необходимых для предоставления такой возможности в вашем приложении:

  • Создайте отдельный вид для Карт Google
  • Добавьте кнопку, нажав на нее, пользователь откроет вид Карты
  • Добавьте функцию обработчика «onclick», которая будет отвечать за отображение Google Maps с соответствующим маркером события на нем.
  • Добавьте информацию о месте события на экран предварительного просмотра

Мы начнем с создания представления карт. Для нашего первого шага мы добавим еще один файл на странице:

1
<script type=»text/javascript» src=»http://maps.google.com/maps/api/js?libraries=places&amp;sensor=true»></script>

Затем нам нужно добавить новый вид, который будет отображать Google Maps. В библиотеке DHTMLX Touch есть компонент googlemap, который значительно упрощает интеграцию с Google Maps ( соответствующая документация ).

Вот наш вид Google Maps:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
scheduler.config.views.push({
            id:»locationView»,
            rows:[
                {
                    view:»toolbar»,
                    css:»dhx_topbar»,
                    elements:[
                        {
                            view:’button’,
                            inputWidth: 100,
                            css:»cancel»,
                            label: scheduler.locale.labels.icon_back,
                            click: «$$(‘scheduler’).$$(‘views’).back()»
                        }
                    ]
                },
                { view:»googlemap»,
                    id:»mymap»
                }
            ]
        });

Некоторые пояснения по поводу приведенного выше кода:

  • Команда scheduler.config.views.push добавляет новое представление в существующую коллекцию просмотра нескольких планировщиков.
  • rows:[{}] размещает элементы по вертикали. Каждый объект представляет собой отдельный ряд. Вид состоит из панели инструментов и Google Maps.
  • $$('scheduler').$$('views') ссылается на объект с несколькими представлениями. Метод back () переключает мульти-просмотр к ранее активному виду.

Вид карты будет выглядеть так:

Календарь мобильных событий - Просмотр карты

Теперь нам нужно добавить кнопку на панели инструментов. Наиболее подходящим является тот, который отображает детали для выбранного события.

1
2
3
4
5
6
//place this code after the ‘localization’ block (step 4)
scheduler.config.selected_toolbar = [
            {view:’button’, inputWidth:scheduler.xy.icon_back, css:»cancel», id:»back»,align:»left»,label:scheduler.locale.labels.icon_back},
            {view:’button’, width:100, id:»location»,align:»right», label:scheduler.locale.labels.label_location, click:»showLocation»}, //our new new button
            {view:’button’, width:70, id:»edit»,align:»right»,label:scheduler.locale.labels.icon_edit}
        ];

Приведенный выше код является определением панели инструментов по умолчанию (вы можете найти его в документации библиотеки ) и нашей новой кнопкой с именем Location .

Когда мы локализуем наше приложение, все добавляемые нами ярлыки должны быть каким-то образом названы и добавлены в файл locales.js . Например, теперь мы добавим кнопку с названием «Местоположение». Итак, в файле locales.js мы добавляем параметр label_location:"Location" а затем устанавливаем для метки атрибута кнопки значение scheduler.locale.labels.label_location .

Атрибут click устанавливает имя функции-обработчика, которая будет вызываться при нажатии кнопки.

Вот так должен выглядеть экран с подробностями события, когда мы добавили кнопку « Расположение» :

Календарь мобильных событий - кнопка «Расположение»

Прежде чем перейти к основному коду, давайте добавим на страницу переменную с именем ‘marker’ и назначим ее экземпляру маркера Google Maps. Мы определяем эту переменную как глобальную, потому что нам нужен только один маркер на странице (наши события должны иметь только одно местоположение).

1
2
//put this code at the very beginning of the page
 var marker = new google.maps.Marker({});

Исполняемая функция или обработчик onClick содержит следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function showLocation(){
            if (marker!=null){
                /*shows the view of multiview*/
                $$(«scheduler»).$$(«locationView»).show();
                /*necessary to resize google map*/
                $$(«scheduler»).$$(«locationView»).resize();
 
                /*event data*/
                var eventId = $$(«scheduler»).getCursor();
                var item = $$(«scheduler»).item(eventId);
 
                /*LatLng point*/
                var point = new google.maps.LatLng(item.lat,item.lng);
 
                var map = $$(«scheduler»).$$(«mymap»).map;
                map.setZoom(6);
                map.setCenter(point);
                google.maps.event.clearListeners(map, «click»);
 
                marker.position= point;
                marker.map = map;
                marker.title = item.event_location;
                marker.setMap(map);
            }
        };

Опять же, позвольте мне объяснить, что делают эти строки кода:

  • Первые строки показывают вид, созданный на первом подэтапе, и изменяют его размер, чтобы заполнить весь экран.
  • Следующие строки получают объект события, на котором в данный момент находится курсор. Обратите внимание, что библиотека DHTMLX использует концепцию «курсора» во внутренней логике. Таким образом, чтобы обеспечить правильную обработку, вы должны работать с «курсором» при получении текущего выбранного элемента. В большинстве случаев метод getCursor() возвращает текущий выбранный элемент. Есть только одно исключение: когда вы удаляете событие, планировщик удаляет выделение, но удерживает курсор и указывает на несуществующее событие. Будьте осторожны с этим!
  • Вторая строка использует Google Maps API для создания точки на основе указанных координат (координат событий, которые хранятся в базе данных). Узнайте больше о Google Mas API .
  • $$("scheduler").$$("mymap") ссылается на представление «googlemap». Свойство «карта» возвращает объект Google Maps.
  • Последние линии увеличивают масштаб, центрируют карту и устанавливают маркер в указанной точке.

Чтобы добавить информацию о местоположении на экран предварительного просмотра, мы должны переопределить шаблон экрана по умолчанию. Чтобы нам не пришлось переписывать весь шаблон (который довольно большой), мы воспользуемся хитростью:

01
02
03
04
05
06
07
08
09
10
11
12
13
var default_temp = scheduler.templates.selected_event;
 
scheduler.templates.selected_event = function(obj)
{
    var html = default_temp(obj);
if (html!=»»){
    html = html.replace(/<\/div>$/,»»);
    html += «<div class=’event_title’>»+scheduler.locale.labels.label_location+»</div>»;
    html += «<div class=’event_text’>»+obj.event_location+»</div>»;
    html += «</div>»;
}
return html;
};

Вот что мы сделали с приведенным выше фрагментом кода:

  • Переменная default_temp содержит шаблон экрана по умолчанию.
  • ‘wrapper’ — это новый элемент div для хранения информации о местоположении.
  • 'event_text' — это предопределенный класс CSS, используемый в шаблоне по умолчанию, мы используем его для обеспечения единообразия отображаемой информации о событиях.
  • scheduler.locale.labels.label_location — это метка, которую мы добавили на предыдущем шаге («Местоположение», в английском языке).

Теперь экран предварительного просмотра выглядит следующим образом (с добавленной информацией о местоположении):

Мобильный календарь событий - Экран предварительного просмотра событий

Теперь наше приложение может отображать местоположение события. Но как насчет редактирования местоположения события или установки местоположения для новых событий?

Теперь нам нужно разрешить пользователям устанавливать / редактировать местоположение события и предоставлять два разных способа ввода адреса при вводе текста и прямого наведения на карту. Вот что нам нужно сделать:

  • Добавить элементы управления в форму редактирования
  • Предоставить обработчики, которые обрабатывают входящие данные

Мы должны добавить как минимум два элемента управления в форму редактирования события: один представляет собой текстовый ввод для ввода адреса события, а второй элемент управления может быть кнопкой, чтобы, щелкнув по ней, пользователь мог открыть карту и установить точку вправо. на карте.

Мы возьмем форму редактирования по умолчанию, а затем добавим упомянутые элементы ( список доступных элементов управления ):

01
02
03
04
05
06
07
08
09
10
11
12
scheduler.config.form = [
            {view:»text», label:scheduler.locale.labels.label_event, id:»text», name:’text’},
            {view:»text», label:scheduler.locale.labels.label_details, id:’details’},
            {view:»datetext», label:scheduler.locale.labels.label_start, id:’start_date’,name:’start_date’, dateFormat:scheduler.config.form_date},
            {view:»datetext», label:scheduler.locale.labels.label_end, id:’end_date’,name:’end_date’, dateFormat:scheduler.config.form_date},
            {view:»toggle», id:’allDay’, label:»», options: [{value:»0″,label:scheduler.locale.labels.label_time},{value:»1″,label:scheduler.locale.labels.label_allday}], align: «right»,value:»0″},
            //custom ‘location’ sections
            {view:»text», label:scheduler.locale.labels.label_location, id:»event_location»},
            {view:’button’, id:»setLocation», label:scheduler.locale.labels.label_locate, click:»setLocation»},
            {view:»text», label:»Latitude», id:’lat’, hidden:true},
            {view:»text», label:»Longitude», id:’lng’, hidden:true}
];

Мы только что добавили пять новых элементов в форму редактирования события:

  • Текстовое поле для ввода адреса вручную, элемент с id:"event_location".
  • Кнопка, которую пользователи используют для открытия Google Карт и установки точки ( id:"setLocation" ). Элемент имеет атрибут «click», который позволяет нам назначить для него функцию-обработчик события «onclick» (мы назвали его «setLocation»).
  • Два скрытых поля («Широта» и «Долгота») для хранения географических координат точки. Следует отметить, что мобильный планировщик автоматически сохраняет данные о событиях, когда пользователь нажимает кнопку «Сохранить». Планировщик получает данные для события из входных данных, определенных в форме редактирования. Вот почему мы добавили эти поля, но скрыли их, так как они действительно не имеют никакого значения для конечных пользователей и нужны только для визуализации хранимого контента в местах БД на Картах Google.
  • Поле для заметок ( id:"details" ). Это полностью необязательное поле. Мы добавляем его только для того, чтобы пользователи могли добавлять заметки о предстоящих событиях. Поле имеет связанный предопределенный параметр в объекте локали.

Итак, теперь у нас есть форма добавления / редактирования события, подобная этой:

Мобильный календарь событий - Форма редактирования события

Прежде чем указывать исполняемую функцию для ввода, мы должны определить событие, которое вызовет функцию. Библиотека позволяет нам использовать встроенные события или любые собственные события HTML. Мы выбрали событие 'onFocusOut' которое происходит после того, как элемент теряет фокус.

Чтобы прикрепить событие к входу, мы добавим следующую команду в dhx.ready(function(){..} :

1
dhx.event($$(‘scheduler’).$$(«event_location»).$view, «focusout», setPlaceCoordinates);
  • dhx.event — это помощник, который присоединяет функцию-обработчик события для элемента HTML.
  • $$('scheduler').$$("event_location") ссылается на вход. $view возвращает объект просмотра.
  • setPlaceCoordinates() возьмет адрес, введенный пользователем, определит его координаты (для сохранения в БД) и отобразит маркер адреса на карте.

Функция setPlaceCoordinates() имеет следующую реализацию:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
function setPlaceCoordinates(){
            if (marker!=null){
                var eventId = $$(«scheduler»).getCursor();
 
                var geocoder = new google.maps.Geocoder();
                var address = $$(‘scheduler’).$$(«event_location»).getValue();
 
                if (address !=»»){
                     geocoder.geocode( { ‘address’: address}, function(results, status) {
                        if (status == google.maps.GeocoderStatus.OK) {
                            $$(‘scheduler’).$$(«lat»).setValue(results[0].geometry.location.Xa);
                            $$(‘scheduler’).$$(«lng»).setValue(results[0].geometry.location.Ya);
                        } else {
                            dhx.alert(«Unfortunately,your location is not found.»);
                            if ($$(‘scheduler’).$$(«lat»)==»»){
                                $$(‘scheduler’).$$(«lat»).setValue(51.477840);
                                $$(‘scheduler’).$$(«lng»).setValue(-0.001492);
                                $$(‘scheduler’).$$(«event_location»).setValue(«Blackheath Avenue London, Greenwich, Greater London SE10 8XJ, UK»);
                            } else{
                                if (eventId!=null){
                                    var item = $$(«scheduler»).item(eventId);
                                    $$(‘scheduler’).$$(«event_location»).setValue(item.event_location);
                                }
                            }
                        }
                     });
                }
            }
        };

Давайте рассмотрим порядок, в котором интерпретатор выполняет код обработчика:

  • Используя команду $$("scheduler").getCursor() , интерпретатор получает объект события, для которого открыта форма редактирования.
  • Затем активирует службу геокодирования (которая преобразует адреса, такие как «Берлин, Германия», в географические координаты) и берет введенный адрес из ввода (var address).

Корневое условное выражение if-else проверяет значение текстового поля «Местоположение»:

  • Если значение является пустой строкой, поиск пропускается.
  • Если пользователь ввел какое-то значение, оно передается в службу Google Maps.
  • Если какой-либо адрес найден, интерпретатор записывает свои координаты в скрытые поля «Широта» и «Долгота».
  • Если введенный адрес не существует или пользователь закрывает форму редактирования до того, как служба завершит поиск, приложение предупреждает сообщение, информирующее о неудачных результатах, и во входных данных сохраняет координаты, сохраненные для события в базе данных.
  • Условное выражение if ($$('scheduler').$$("lat")==""){} else{} используется для проверки того, хранится ли рассматриваемое событие в БД или является новым событием (потому что, если событие новое, интерпретатор не может взять свои координаты из БД, и произойдет ошибка). Если событие новое и поиск не завершен, приложение назначает координаты события Гринвичской королевской обсерватории.

Исполняемая функция или обработчик onClick, который происходит, когда пользователь нажимает кнопку «Найти на карте», содержит следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
        function setLocation(){
            if (marker!=null){
                 /*shows the view of multiview*/
                $$(«scheduler»).$$(«locationView»).show();
                /*necessary to resize google map*/
                $$(«scheduler»).$$(«locationView»).resize();
                var point;
                var eventId = $$(«scheduler»).getCursor();
 
                 if (eventId!=null){
                     var item = $$(«scheduler»).item(eventId);
                    /*LatLng point*/
                     point = new google.maps.LatLng(item.lat,item.lng);
                    marker.title = item.event_location;
                 } else{
                    point = new google.maps.LatLng(51.477840, -0.001492);
                    marker.title = «Blackheath Avenue London, Greenwich, Greater London SE10 8XJ, UK»;
            }
 
            var map = $$(«scheduler»).$$(«mymap»).map;
            map.setZoom(6);
            map.setCenter(point);
 
            marker.position= point;
            marker.map = map;
            marker.setMap(map);
 
            google.maps.event.addListener(map, «click», function (e) {
 
                    var request = {
                        location:e.latLng,
                        radius:’1′
                    };
                    service = new google.maps.places.PlacesService(map);
                    service.search(request, function(results, status){
                        if (status == google.maps.places.PlacesServiceStatus.OK) {
                            this.service.getDetails({ reference: results[0].reference }, function(details, status) {
                                if (status == google.maps.places.PlacesServiceStatus.OK) {
                                    $$(‘scheduler’).$$(«lat»).setValue(details.geometry.location.Xa);
                                    $$(‘scheduler’).$$(«lng»).setValue(details.geometry.location.Ya);
 
                                    marker.title = details.formatted_address;
                                    marker.position= e.latLng;
                                    marker.map = map;
                                    marker.setMap(map);
                                    $$(‘scheduler’).$$(«event_location»).setValue(marker.title);
                                }
                            });
                        }
                    });
 
            });
}
    };

Строки кода делают следующее:

  • Первые строки показывают вид с Google Maps в полноэкранном режиме и получают объект события.
  • Первое условное выражение if-else проверяет, редактирует ли пользователь существующее событие или создает новое. Эта проверка делается для того, чтобы установить начальный маркер на карте.
  • Если пользователь редактирует существующее событие, код генерирует точку с координатами из БД.
  • Если пользователь создает новое событие, код назначает ему координаты Гринвичской королевской обсерватории.
  • $$("scheduler").$$("mymap") ссылается на представление «googlemap». Свойство map возвращает объект Google Maps.
  • Следующие строки увеличивают масштаб, центрируют карту и устанавливают маркер в указанной точке.
  • Команда google.maps.event.addListener() добавляет функцию для обработки кликов, сделанных пользователем на карте. Подробности об используемом API см. В веб-службах API Google Mas.

Еще один момент для упоминания: на этом шаге мы добавим функцию для обработки кликов карты. Но если пользователь только просматривает местоположение события, у него не должно быть возможности его изменить (шаг 5). Таким образом, наряду с добавлением прослушивателя «click» в функцию setLocation, мы отключим его для режима «preview» (функция showLocation).

Добавьте эту команду в существующий код функции showLocation() :

1
2
3
4
function showLocation(){
         google.maps.event.clearListeners(map, «click»);
    …
};

Это конец этого урока! Теперь вы можете скачать окончательный демонстрационный пакет, чтобы увидеть, как все работает и подходит друг другу в календаре событий, который мы создали.


С ростом использования мобильных телефонов нет необходимости говорить, насколько важно иметь мобильную версию веб-сайта или приложения. Если вам нужен календарь событий, который можно просматривать и редактировать онлайн на телефонах, то мобильная версия dhtmlxScheduler может значительно сэкономить время, поскольку она предлагает готовый к использованию пользовательский интерфейс календаря и набор базовых функций. Лицензия GNU GPL с открытым исходным кодом позволяет бесплатно использовать планировщик на веб-сайтах и ​​во внутренних приложениях.