Hackito Ergo Sum was my first blog and I never intended to post regularly. I write a long article every few months and when I’m happy with it I post it. Often in the middle of the night.
Then I started a new blog and wanted to post twice a week every week. The only way I could manage that was to write the posts a month or two in advance and schedule them. WordPress has a handy feature for scheduling individual posts, but almost from the beginning I had problems. WordPress only lets you edit post dates one at a time and you can’t see other posts while you’re doing it. That makes it very easy to have gaps in your calendar where you have nothing and overlaps where you have too many posts.
My solution to this problem was to create an editorial calendar plugin for WordPress. A place where I could easily see all of my posts in a calendar and drag and drop them around to change the dates. This article will show you how that plugin works and help you take your JQuery programming to the next level.
The plugin and all of the code in this article is made available under the Apache 2.0. This plugin is also the result of work from multiple people including my wife and many others.
The basics of the plugin are pretty simple. There are three files.
edcal.js runs in the browser,
edcal.css defines the styles for the calendar, and
The calendar has three fundamental operations.
- Align the grid
- Display posts
- Change post dates
We also let users change a few other details about the posts, but those are ancillary to the primary function of the calendar.
Most online calendars show you one month at a time. The impressive Google calendar works this way.
One month at a time is fine for managing your appointments, but it isn’t right for blog posts. Blogs care much more about days of the week than months. You want to see partial months more often. If you want to drag a post to next Thursday you don’t care that it is in a different month. The WordPress calendar is a continuous calendar. You can move from one week to the next without worrying about what month it is. I stole the idea from an old version of Microsoft Outlook.
The continuous calendar is a pretty basic grid with just seven columns; one for each day of the week. As you can tell from my other articles, I have an affinity for grids. It would be simple to create one grid that was 7 by 11 (I’ll explain why 11 in just a moment), it wouldn’t perform very well.
Every cell in my calendar is a
div that gets aligned with CSS. This is much more manageable that generated many nested
table tags. I use one grid that is 11 rows and 1 column to lay out each week. I then lay out each week in a separate grid. This makes some important performance improvements when the user scrolls the grid.
Making the continuous calendar work requires efficient scrolling. When a user scrolls down into the future I have to add a new row to the grid for every week. If they scrolled too far the grid would get too big and the browser would run out of memory and crash. To avoid that problem I need to remove a week from the opposite side of the calendar every time I add a new one.
Removing and adding the rows the user is looking at causes the UI to flicker in an unsettling way. It also makes it respond more slowly. This calendar is managed with five visible rows and three extra rows on the top and bottom. When the user scrolls down I remove weeks from the top and add them at the bottom without making the browser redraw them. The process works in reverse when the user scrolls up into the past.
The calendar doesn’t just show dates. It shows posts and information associated with them. For a small blog with relatively low traffic it would be fine to load all the post data when the page loads. However, if I have any hope of supporting bloggers like Chris Brogan who’ve posted every day for years I need to load the data a little at a time.
When a user scrolls the calendar I start a
timer. If they stop scrolling long enough for the
timer to expire I load the posts for the new visible dates. The
timer is important because scrolling with the mouse wheel generates a large number of events in a small period of time and I don’t want to look for all of those dates. I also cache the data about the posts I get back from the server so I only have to load it once.
Every time the user scrolls the calendar I’m adding new dates to the list of dates I want post data for. I then make AJAX calls back to the server to get the data. This is very similar to the way Google Maps loads new squares on the map.
Handling the Drop
The calendar gets drag and drop support from the JQuery UI library. This lets us track which post a user is dragging and where they drop it. Once they drop a post on a new date we need to update the server with the new date for that post. This can be a little tricky because of the need for concurrency checking.
Imagine that Alice and Bob are both editing the calendar. They both load it at the same time, but Alice goes off to lunch and Bob hangs around moving posts. When Alice gets back from lunch she will have a very outdated view of the data, and neither she nor her browser will have any idea what Bob was up to. If Alice tries to move a post Bob has already moved she will overwrite his change. That would be bad.
Concurrency checking makes sure that Alice will never overwrite Bob’s change. The calendar uses optimistic concurrency checking. It assumes that Alice and Bob running into a problem doesn’t happen very often (that is the optimistic part), so it just shows an error when it does. The calendar handles this checking by sending the old date along with the new date every time it tries to move a post. If the old date from the browser doesn’t match the current date on the server then you have a problem.
When the server is done either modifying the post or determining that there was an error it returns a new chunk of JSON data with the updated post information. The calendar then updates the post in the HTML just to make sure it has the latest data.
That is the basics of what the calendar does. It functions as a data queue with a timer to deter latency. All requests back to the server are totally stateless and all the data operations include concurrency checking and error detection. All to make sure the user doesn’t get into any trouble.
I know there wasn’t any code in this article, but the plugin is open source and has copious comments. You can download the source code from http://plugins.svn.wordpress.org/editorial-calendar/trunk.
I hope this article has helped you plan out AJAX applications, given you a better understanding of the DOM data model, and made you think a little bit. If you have further questions or see anywhere I could do something better please leave a comment and let me know.