[Update Feb 2010: see new blog post for more advanced version]
I’m a lazy developer. I hate reinventing the wheel. Worse, I like stealing other people’s wheels! I guess that’s why I love JavaScript so much: no need to have access to any server, I can embed my script into this very blog (or into a Content Editor Web Part if I’m using SharePoint). I can point to a JavaScript library other geniuses have invented, and use their fancy functions against some of the enterprise data accessible through XML (RSS, Web services, REST, etc.), and voila! I can impress my boss in just an hour of work! In future articles, I’m going to show a few examples of amazing things you can do in JavaScript and enterprise data.
Starting with this example. Here I’m going to show how to visualize a SharePoint calendar with the
SIMILE Timeline widget.
This is what we’re going to use:
- SIMILE Timeline widget for the visualization.
- Darren’s SharePoint JavaScript API to access any SharePoint data. Any SharePoint list or document library data can be accessed through a web service, so Darren’s API makes accessing SharePoint data really easy (you can also use JQuery as I will show in future articles, although I like this one better). You will soon find yourself using this API in many scripts, so to improve its availability I suggest you copy Darren’s JavaScript files on your production server.
- And finally SharePoint calendar data itself. As I said, the events details are accessible in JavaScript through the calendar web service. To make life easier, all you need to provide is the RSS feed associated to your calendar (see bottom of the script). The feed URL contains both the path of your calendar and its list ID, which is all we need to access the web service.
So there you have it. Modify just the few lines where you see “myserver” (i.e. line 7, 8 9 and bottom of the script), insert the script into a Content Editor Web Part, and enjoy!
<script language="javascript" type="text/javascript">
var Timeline_urlPrefix = "http://simile.mit.edu/timeline/api/";
// Include these javascript files only if not loaded already by another web part
// In portals like a SharePoint page, you never know what script might already be loaded by the master page or other web parts
includeJSScript("http://simile.mit.edu/timeline/api/timeline-api.js");
includeJSScript("http://myserver/js/spapi/spapi_core.js");
includeJSScript("http://myserver/js/spapi/spapi_types.js");
includeJSScript("http://myserver/js/spapi/spapi_lists.js");
function includeJSScript(p_file) {
// before we insert this script, we need to check if it already exists
var bAlreadyExists = false;
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src == p_file) {
//scripts[i] is the one
bAlreadyExists = true;
break;
}
}
if (!bAlreadyExists) {
var v_script = document.createElement('script');
v_script.type = 'text/javascript';
v_script.src = p_file;
document.getElementsByTagName('head')[0].appendChild(v_script);
}
}
// Call the web service associated to the calendar to extract its items
function getCalendarListItems()
{
var lists = new SPAPI_Lists(ExtractWebSiteURL(MyList_RSS_Url));
var items = lists.getListItems(ExtractListID(MyList_RSS_Url),'',"<Query><OrderBy><FieldRef Name='EventDate' /></OrderBy></Query>",'',100);
if (items.status == 200)
{
var rows = items.responseXML.getElementsByTagName("z:row");
return rows;
}
else
{
return null;
}
}
var resizeTimerID = null;
function formatDateString(strDate)
{
var yearStr = strDate.substr(0, 4);
var monthStr = strDate.substr(5, 2);
var dayStr = strDate.substr(8, 2);
return monthStr + "/" + dayStr + "/" + yearStr + " " + strDate.substr(11);
}
// This is one of the most important functions. Once the calendar events are retrieved,
// we loop through them and add them to the timeline
function main()
{
var items = getCalendarListItems();
if (items == null)
{
document.getElementById("my-timeline").innerHTML = "Cannot get items from list <i>" + ExtractWebSiteURL(MyList_RSS_Url) + "/" + ExtractListID(MyList_RSS_Url) + "<i>";
return;
}
var eventSource = new Timeline.DefaultEventSource();
// for each event returned by the calednar web service, we create a timeline event
for (var i = 0; i < items.length; ++i)
{
var ows_EventDate = formatDateString(items[i].getAttribute("ows_EventDate"));
var ows_EndDate = formatDateString(items[i].getAttribute("ows_EndDate"));
var ows_Title = items[i].getAttribute("ows_Title");
var ows_Location = items[i].getAttribute("ows_Location");
var eventDate = new Date(ows_EventDate);
var endDate = new Date(ows_EndDate);
var event = new Timeline.DefaultEventSource.Event(
eventDate, //start
endDate, //end
eventDate, //latestStart
endDate , //earliestEnd
true, //instant (use FALSE if events are longer than a few hours of duration
ows_Title, //text
"<strong>Where? </strong>" + ows_Location + "<br><strong>When? </strong>" //description that appears in a bubble when user clicks on the event
);
eventSource.add(event);
}
// This is where we define 3 timelines. Advanced users can play with these parameters to use different timeline or timeline behaviors
// See http://code.google.com/p/simile-widgets/wiki/Timeline for more information
var theme = Timeline.ClassicTheme.create(); // create the theme
theme.event.bubble.width = 300; // modify this bubble size to fit your needs
theme.event.bubble.height = 170;
var bandInfos = [
Timeline.createBandInfo({
trackGap: 0.5,
width: "60%",
intervalUnit: Timeline.DateTime.WEEK,
intervalPixels: 100,
timeZone : 8,
eventSource: eventSource, theme:theme
}),
Timeline.createBandInfo({
showEventText: false,
trackHeight: 0.5,
trackGap: 0.2,
width: "25%",
intervalUnit: Timeline.DateTime.MONTH,
intervalPixels: 150,
timeZone : 8,
eventSource: eventSource
}),
Timeline.createBandInfo({
showEventText: false,
trackHeight: 0.5,
trackGap: 0.2,
width: "15%",
intervalUnit: Timeline.DateTime.YEAR,
intervalPixels: 400,
timeZone : 8,
eventSource: eventSource
})
];
bandInfos[1].syncWith = 0;
bandInfos[2].highlight = true;
bandInfos[2].syncWith = 1;
var timeLine = Timeline.create(document.getElementById("my-timeline"), bandInfos);
}
function ExtractWebSiteURL(sUrl) {
var index = sUrl.toLowerCase().indexOf("_layouts");
var MyCurrentPath = "";
if (index != -1) {
MyCurrentPath = sUrl.substring(0, index);
MyCurrentPath = MyCurrentPath.substring(0,MyCurrentPath.lastIndexOf('/'));
}
else { return null;}
return MyCurrentPath
}
function ExtractListID(sUrl) {
var index = sUrl.toLowerCase().indexOf("_layouts");
var DestinationListID = "";
if (index != -1) {
DestinationListID = unescape(sUrl.substring(index + 28, sUrl.length));
}
else { return null;}
return DestinationListID
}
// _spBodyOnLoadFunctionNames.push is a SharePoint function that insures that the script will be run only AFTER the page has been loaded
_spBodyOnLoadFunctionNames.push("main");
// ******************** Settings ********************
// URl of the RSS associated to your calendarvar
MyList_RSS_Url = "http://myserver/mysite/_layouts/listfeed.aspx?List=%7B3186664F%2D626C%2D4925%2D896B%2D53517E1D0244%7D";
</script>
<!-- Feel free to modify the following parameters: height, border, font -->
<div id="my-timeline" style="height: 120px; border: 1px solid #aaa; font-size: 9pt"></div>