
When developing a project for a transport company engaged in passenger transportation, the task arose of implementing its analogue Google Calendar for embedding into the system.
For some reasons (deep integration into the project, communication with a bunch of different entities, full control over all parts of the code, etc.), using the solution from Google was irrational from many points of view.
So, the conditions of the problem:
- The interface should be as close as possible to the interface from Google (because before using it)
- Normal implementation of RFC 2445 , its parts regarding RRULE (repetition patterns)
- Fast speed of calculating the dates of events (in this case, flights) and their render in the browser
- Maximum use of existing libraries to reduce time spent.
If the topic is interesting or you have something to say, because work is still underway and this post affects only a small part - I ask for kat, I will be glad for meaningful advice.
Initially, before detailing, it was decided to determine the overall strategy for the work of this decision. An attempt to use ruby ​​and gem ice_cube failed because already quite a large amount of code was written in php and part of the project is not very kosher to make a different programming language. Well, performance problems (or my inexperience with RoR).
As a result, after reflection, the following was born:
- A slightly modified jQuery plugin FullCalendar will be responsible for rendering as a calendar .
- Creating recurring flights assigned to Scheduler.js from a set of FuelUX
- Storage in the database of repeat patterns will be implemented as “FREQ = DAILY; INTERVAL = 2; UNTIL = 20130130T230000Z;” to reduce the size of the database (because if you keep each flight separate and the end of repetitions is not assigned the number of individual flights tends to infinity)
- Conversion of repeat patterns to a set of flight dates will be implemented on the client side to offload server capacity.
')
The first thing that needs to be implemented is getting RRULE, converting to a set of dates and rendering using the FullCalendar plugin. After a brief search, the following conversion solution was found -
rrule.js, which works both in the browser and as an application on node.js, which further provides the ability to transfer to the server from the client.
Approximate way is clear - let's get started. I warn you right away, a prototype is being developed and the code is being written on the basis of speed indicators, not quality ones.
Suppose we have a json array of RRULE rules with the fields name, length, and the pattern of repetitions. We will drop it from the backend.
[{ "name": "Reccurence Event #1", "length": "120", "rrule": "DTSTART=20020201T083000Z;FREQ=WEEKLY;WKST=MO;BYDAY=WE,FR" }, { "name": "Reccurence Event #2", "length": "120", "rrule": "FREQ=MONTHLY;DTSTART=20000201T083000Z;WKST=MO;BYDAY=TU" }, { "name": "Reccurence Event #3", "length": "120", "rrule": "FREQ=DAILY;DTSTART=20000201T063000Z;WKST=MO;BYDAY=MO,FR" } ]
Initialize the arrays and create the rrule.js plugin objects from the RRULE strings:
var data = private_env.get_data(); var rules = new Array(); var occurs = new Array(); for (var k in data){ rules.push( { name: data[k].name, length: data[k].length, rrule: RRule.fromString(data[k].rrule) }); }
We get from our objects a list of dates for each flight, where DATE_START and DATE_END correspond to the beginning and end of the plank, for which we need to get them:
for (var k in rules){ occurs.push( { name: rules[k].name, length: rules[k].length, occurs: rules[k]['rrule'].beetween(DATE_START,DATE_END) }); }
Clear the calendar before the render:
$calendar.fullCalendar('removeEvents', function (event){ return true; });
And display our flights to the screen:
for (var k in occurs){ for (var i in occurs[k].occurs){ var event = { id: k, title: occurs[k].name, start: occurs[k].occurs[i], end: new Date(occurs[k].occurs[i].getTime()+(1000*60*occurs[k].length)), allDay: false }; $calendar.fullCalendar('renderEvent', event, 1, 0); } } this.fullCalendar('renderEvent',{allDay: false}, 0, 1);
We wrap all of the above in one function, for example, render (DATE_START, DATE_END) and call the FullCalendar plugin on the viewRender event:
... viewRender: function(view, element){ $(private_env.env_self).service('render', 'between', view.visStart, view.visEnd); } ...
At the moment we got about the following picture:
UPD :
ExampleI see no point in turning a post into sheets from code, I think the general idea is clear. Appearance and other work is not finalized.
A few words about creating and editing.
A large number of events in FullCalendar allows us to implement editing functionality. Required functionality:
- Clicking opens the flight edit form.
- When dragging, resize we ask for options for action. Editing a single flight, editing the entire chain of repetitions, editing future repetitions
The task is trivial and boils down to assigning functions to eventClick, eventDrop, eventResize events. By the way, the last two have the ability to cancel actions:
... revertFunc(); ...
The only nuance. If you select “Change only individual flight”, we cut “RRULE FREQ = MONTHLY; DTSTART = 20000201T083000Z; WKST = MO; BYDAY = TU” into three different patterns.
What happened before the flight, the current flight itself and what comes after. This is solved by changing the options in the DTSTART and UNTIL rules.
Regarding the creation of flights - Scheduler.js can output a string of repetitions according to RFC 2445, which is what we need:
... $('#myScheduler').scheduler('value') ...
To refine this hodgepodge of plug-ins is still to be quite a long time, but the direction to move is clear.
And thank you for your attention, if there is criticism or suggestions for improvement - I ask in the studio and I will be just glad.