Table of Contents:
Here are a few easy steps to create a simple timeline. Open up your favorite text or HTML editor and start creating an HTML file.
In your HTML code, link to Timeline's Javascript API code as follows:
<html>
<head>
...
<script src="http://simile.mit.edu/timeline/api/timeline-api.js" type="text/javascript"></script>
...
</head>
<body>
...
</body>
</html>
Create a div element in your HTML code, e.g.
<div id="my-timeline" style="height: 150px; border: 1px solid #aaa"></div>You should give it an ID as well as fix its height. You can optionally set its borders—this usually makes the timeline look better.
Add two event handlers, onload and onresize, to the body element:
<body onload="onLoad();" onresize="onResize();"> ... </body>Then write the following code in a script block or a separate Javascript file:
var tl; function onLoad() { var bandInfos = [ Timeline.createBandInfo({ width: "70%", intervalUnit: Timeline.DateTime.MONTH, intervalPixels: 100 }), Timeline.createBandInfo({ width: "30%", intervalUnit: Timeline.DateTime.YEAR, intervalPixels: 200 }) ]; tl = Timeline.create(document.getElementById("my-timeline"), bandInfos); } var resizeTimerID = null; function onResize() { if (resizeTimerID == null) { resizeTimerID = window.setTimeout(function() { resizeTimerID = null; tl.layout(); }, 500); } }Note that if we put the code to construct the timeline in the onload handler to make sure that when we start to use Timeline's API, all its code has been loaded. That code creates a horizontal timeline (below) with 2 bands: in the top band, a month spans 100 pixels (approximately, since a month here refers to 30 days while not every month is exactly 30 days long); and in the bottom band, a year spans 200 pixels. The top band takes up 70% of the timeline's height, and the bottom band 30%. Note that the two bands scroll independently.
To make the two bands scroll in synchrony, and then to make the bottom band highlights the visible time span of the top band, add the following code (highlighted):
function onLoad() {
var bandInfos = [
Timeline.createBandInfo({
width: "70%",
intervalUnit: Timeline.DateTime.MONTH,
intervalPixels: 100
}),
Timeline.createBandInfo({
width: "30%",
intervalUnit: Timeline.DateTime.YEAR,
intervalPixels: 200
})
];
bandInfos[1].syncWith = 0;
bandInfos[1].highlight = true;
tl = Timeline.create(document.getElementById("my-timeline"), bandInfos);
}
If you try to pan one band, the other is scrolled as well.
To add events to the timeline, create an event source and load it with data from an XML file:
function onLoad() { var eventSource = new Timeline.DefaultEventSource(); var bandInfos = [ Timeline.createBandInfo({ eventSource: eventSource, date: "Jun 28 2006 00:00:00 GMT", width: "70%", intervalUnit: Timeline.DateTime.MONTH, intervalPixels: 100 }), Timeline.createBandInfo({ eventSource: eventSource, date: "Jun 28 2006 00:00:00 GMT", width: "30%", intervalUnit: Timeline.DateTime.YEAR, intervalPixels: 200 }) ]; bandInfos[1].syncWith = 0; bandInfos[1].highlight = true; tl = Timeline.create(document.getElementById("my-timeline"), bandInfos); Timeline.loadXML("example1.xml", function(xml, url) { eventSource.loadXML(xml, url); }); }The date field is there to make sure the timeline starts out showing the events immediately without requiring the user to pan first. Here is the resulting timeline with 3 events:
Take a look at example1.xml. There are 3 types of events: a duration, an instantaneous event with an imprecise starting time, and an instantaneous event with a precise starting time. Click on the events to see how their bubbles are rendered based on the data in the XML file. For the exact format of such XML files, refer to the documentation on event sources. Note that loading XML files is only one way in which you can add events to timelines.
Looking at the previous timeline, it is obvious that the lower band looks denser, and it will become a lot denser a lot quicker than the upper band should we add more events. The lower band acts as a zoomed-out overview for the upper band and it does not have to show as much detail as the upper band. We can turn off the rendering of text as well as condense the event markings vertically:
function onLoad() { var eventSource = new Timeline.DefaultEventSource(); var bandInfos = [ Timeline.createBandInfo({ eventSource: eventSource, date: "Jun 28 2006 00:00:00 GMT", width: "70%", intervalUnit: Timeline.DateTime.MONTH, intervalPixels: 100 }), Timeline.createBandInfo({ showEventText: false, trackHeight: 0.5, trackGap: 0.2, eventSource: eventSource, date: "Jun 28 2006 00:00:00 GMT", width: "30%", intervalUnit: Timeline.DateTime.YEAR, intervalPixels: 200 }) ]; bandInfos[1].syncWith = 0; bandInfos[1].highlight = true; tl = Timeline.create(document.getElementById("my-timeline"), bandInfos); Timeline.loadXML("example1.xml", function(xml, url) { eventSource.loadXML(xml, url); }); }The lower band of the timeline below does not show text and its event markers are also smaller. But note that the third event is vertically aligned with the first event in the lower band, but it is on its own track in the upper band. We will address this problem later.
By now you must have realized that Timeline.createBandInfo() fills in default settings, which can be overridden, for constructing a band in a timeline. What Timeline.createBandInfo() does is something like this (in pseudo-code):
Timeline.createBandInfo = function(params) { return { width: params.width, eventSource: params.eventSource (or null by default), timeZone: params.timeZone (or 0 by default), ether: new Timeline.LinearEther({ interval: the number of milliseconds in params.intervalUnit, pixelsPerInterval: params.intervalPixels, centersOn: params.date (or the current date by default) }), etherPainter: new Timeline.GregorianEtherPainter({ unit: params.intervalUnit, theme: params.theme (or the default theme) }), eventPainter: new Timeline.DurationEventPainter({ showText: params.showEventText (or true by default), theme: the same theme above, trackHeight: params.trackHeight (or the default track height in the theme), trackGap: params.trackHeight (or the default track gap in the theme), layout: new Timeline.StaticTrackBasedLayout({ eventSource: the same eventSource above, ether: the same ether above, showText: the same showText value above, theme: the same theme above }) }) } };In other words, Timeline.createBandInfo() takes an object whose fields store initialization settings and returns yet another object whose fields stores initialization settings that Timeline.create() can understand. Timeline.createBandInfo() does the work of routing each initialization setting that you give it to the appropriate place(s). For example, params.intervalUnit is used referenced twice above, once to construct an ether and once to construct an ether painter. Whatever default setting that Timeline.createBandInfo() doesn't provide is provided by the theme.
The ether of a band dictates how time is mapped onto the pixel space: how many pixels a time span takes up. The ether painter makes this mapping visible to the user by painting various markings on the background of the band, e.g., "Jun", "Jul", "2005", "2006". The event painter, obviously, paints the events that are fed to it by the ether source. The Timeline.DurationEventPainter uses a layout to determine how to distribute the events among several tracks such that events don't overlap one another.
The object returned by Timeline.createBandInfo() can be fixed before we feed it into Timeline.create(). We have already done that to sync the two bands. This time, we will take the layout from the upper band and set it into the lower band so that the event is positioned on the same track in both bands:
function onLoad() {
var eventSource = new Timeline.DefaultEventSource();
var bandInfos = [
Timeline.createBandInfo({
eventSource: eventSource,
date: "Jun 28 2006 00:00:00 GMT",
width: "70%",
intervalUnit: Timeline.DateTime.MONTH,
intervalPixels: 100
}),
Timeline.createBandInfo({
eventSource: eventSource,
date: "Jun 28 2006 00:00:00 GMT",
width: "30%",
intervalUnit: Timeline.DateTime.YEAR,
intervalPixels: 200
})
];
bandInfos[1].syncWith = 0;
bandInfos[1].highlight = true;
bandInfos[1].eventPainter.setLayout(bandInfos[0].eventPainter.getLayout());
tl = Timeline.create(document.getElementById("my-timeline"), bandInfos);
Timeline.loadXML("example1.xml", function(xml, url) { eventSource.loadXML(xml, url); });
}
Now note that the last event is on its own track in the lower band
(just like in the upper band) although it can stay on the same
track as the first even in the lower band without resulting in any
overlap.
Now we load example2.xml, which contains a few more details for "Trip to Beijing" and discover that the days starting on August 2, 2006, are quite cramped:
To solve this problem, we will distort the time of those days, producing the effect of zooming in. Because we want time to flow differently than before—we want time spans to be mapped to pixels in a different way, we need a different kind of ether (and a different kind of ether painter to go with it):
function onLoad() { var eventSource = new Timeline.DefaultEventSource(); var bandInfos = [ Timeline.createHotZoneBandInfo({ zones: [ { start: "Aug 01 2006 00:00:00 GMT-0500", end: "Sep 01 2006 00:00:00 GMT-0500", magnify: 10, unit: Timeline.DateTime.WEEK }, { start: "Aug 02 2006 00:00:00 GMT-0500", end: "Aug 04 2006 00:00:00 GMT-0500", magnify: 7, unit: Timeline.DateTime.DAY }, { start: "Aug 02 2006 06:00:00 GMT-0500", end: "Aug 02 2006 12:00:00 GMT-0500", magnify: 5, unit: Timeline.DateTime.HOUR } ], timeZone: -5, eventSource: eventSource, date: "Jun 28 2006 00:00:00 GMT", width: "70%", intervalUnit: Timeline.DateTime.MONTH, intervalPixels: 100 }), Timeline.createBandInfo({ timeZone: -5, eventSource: eventSource, date: "Jun 28 2006 00:00:00 GMT", width: "30%", intervalUnit: Timeline.DateTime.YEAR, intervalPixels: 200 }) ]; bandInfos[1].syncWith = 0; bandInfos[1].highlight = true; bandInfos[1].eventPainter.setLayout(bandInfos[0].eventPainter.getLayout()); tl = Timeline.create(document.getElementById("my-timeline"), bandInfos); Timeline.loadXML("example1.xml", function(xml, url) { eventSource.loadXML(xml, url); }); }In the resulting timeline below, the whole month of August 2006 is stretched out 10 times, showing weekly intervals; the two days of August 2nd and August 3rd are stretched out another 7 times; and then the time between 6am to noon on August 2nd is stretched out another 5 times, showing hourly intervals. All this stretching is done to the upper band only, so if you pan the upper band, observe how the lower band's highlight grows and shrinks.
Of course, panning the lower band over the hot zones of the upper band now makes the upper band a little jumpy. We can distort the lower band to reduce this effect:
function onLoad() { var eventSource = new Timeline.DefaultEventSource(); var bandInfos = [ Timeline.createHotZoneBandInfo({ zones: [ { start: "Aug 01 2006 00:00:00 GMT-0500", end: "Sep 01 2006 00:00:00 GMT-0500", magnify: 10, unit: Timeline.DateTime.WEEK }, { start: "Aug 02 2006 00:00:00 GMT-0500", end: "Aug 04 2006 00:00:00 GMT-0500", magnify: 7, unit: Timeline.DateTime.DAY }, { start: "Aug 02 2006 06:00:00 GMT-0500", end: "Aug 02 2006 12:00:00 GMT-0500", magnify: 5, unit: Timeline.DateTime.HOUR } ], timeZone: -5, eventSource: eventSource, date: "Jun 28 2006 00:00:00 GMT", width: "70%", intervalUnit: Timeline.DateTime.MONTH, intervalPixels: 100 }), Timeline.createHotZoneBandInfo({ zones: [ { start: "Aug 01 2006 00:00:00 GMT-0500", end: "Sep 01 2006 00:00:00 GMT-0500", magnify: 20, unit: Timeline.DateTime.WEEK } ], timeZone: -5, eventSource: eventSource, date: "Jun 28 2006 00:00:00 GMT", width: "30%", intervalUnit: Timeline.DateTime.YEAR, intervalPixels: 200 }) ]; bandInfos[1].syncWith = 0; bandInfos[1].highlight = true; bandInfos[1].eventPainter.setLayout(bandInfos[0].eventPainter.getLayout()); tl = Timeline.create(document.getElementById("my-timeline"), bandInfos); Timeline.loadXML("example1.xml", function(xml, url) { eventSource.loadXML(xml, url); }); }
The resulting timeline below still needs a few more iteration to make it smooth. But I hope this has been a good starting point for you.