Ok, "hacking" may be too strong a word, but I thought I'd share a fun little diversion I tried at lunch. I noticed that the MAX 2010 Scheduler oddly did not list session times. You could browse by day of course, but I prefer to browse by session title. I was also curious to see when my session was. Unfortunately, the date and times for each session were not available in that view.
I then decided to do what any self-respecting web developer would do. I opened up the Developer tools in Chrome (think Firebug) and became to monitor what was going on. I noticed that the data for the scheduler came from a few JSON requests. I took a look at them and found that the JSON data for sessions actually contained the date and times - the front end just wasn't using it. So I quickly decided to play with this data myself. I began by creating a simple ColdFusion file that would proxy the JSON requests for me. An AIR app wouldn't need this - but I wanted something done super quick. I came up with the following:
<cfset safeurls = "http://max.adobe.com/v1/events/ebdabc28-aab4-479f-86f3-6bd9d97b4cc7/speakers.json,http://max.adobe.com/v1/events/ebdabc28-aab4-479f-86f3-6bd9d97b4cc7/sessions.json"> <cfif url.u is "" or not isValid("url", url.u) or not listFindNoCase(variables.safeurls, url.u)>
<cfabort/>
</cfif> <cfif isNull(cacheGet("urlcache_#url.u#"))>
<cfhttp url="#url.u#" timeout="20">
<cfset cachePut("urlcache_#url.u#", cfhttp.filecontent,1)>
</cfif> <cfset data = cacheGet("urlcache_#url.u#")>
<cfcontent type="application/json" reset="true"><cfoutput>#data#</cfoutput>
<cfparam name="url.u" default="">
There isn't a lot here. Basically look for a URL to request in the URL scope (sorry if that sounds confusing) and validate the value against a list of URLs. Next - see if I have the request in the cache. If not, fetch it with cfhttp and store it for a day. Finally, get the data from the cache and output it to the screen. My front end was a bit more complex:
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
var speakers = []
var sessions = []
var searchReady = false function renderSession(session) {
var res = ""
res += "<div class='session'>"
res += "<h2>"+session.name+"</h2>"
res += "<b>Times:</b> "
if(session.instances.length == 0) res += "None Scheduled<br/>"
else {
for(var i=0; i<session.instances.length;i++) {
res += session.instances[i].date + " " + session.instances[i].time
if(i+1 < session.instances.length) res+= " and "
}
res += "<br/>" }
res += "<b>Speakers:</b> "
if(session.speakers.length == 0) res += "None Assigned<br/>"
else {
for(var i=0; i<session.speakers.length;i++) {
res += speakers[session.speakers[i]].name
if(i+1 < session.speakers.length) res+= " and "
}
res += "<br/>"
}
res += session.description res += "</div>"
return res
} function renderSessions() {
var totalSessions = 0 var filterTerm = $("#search").val()
filterTerm = $.trim(filterTerm.toLowerCase())
var s = ""
for(var i=0; i<sessions.length; i++) {
if(filterTerm == '' ||
(sessions[i].name.toLowerCase().indexOf(filterTerm) >= 0 || sessions[i].description.toLowerCase().indexOf(filterTerm) >= 0 )
) {
totalSessions++
s += renderSession(sessions[i])
}
}
s = "<h2>Sessions ("+totalSessions+")</h2>" + s
$("#sessions").html(s) } function loadSpeakers() { var speakerurl = "http://max.adobe.com/v1/events/ebdabc28-aab4-479f-86f3-6bd9d97b4cc7/speakers.json"
$.getJSON('load.cfm?u='+speakerurl, {}, function(res,code) {
for(var i=0; i<res.length; i++) {
var speaker = {"name":res[i].lastname +", "+res[i].firstname}
speakers[res[i].id] = speaker
}
loadSessions()
}) } function loadSessions() { var sessionurl = "http://max.adobe.com/v1/events/ebdabc28-aab4-479f-86f3-6bd9d97b4cc7/sessions.json"
$.getJSON('load.cfm?u='+sessionurl, {}, function(res,code) {
sessions = res
renderSessions()
$("#status").text("")
searchReady = true
}) } $(document).ready(function() {
$("#status").text("Please stand by. I'm loading a lot of data and doing really important, technical 'computer' stuff.")
//begin by loading speakers
loadSpeakers() $("#search").keyup(function() {
var val = $(this).val()
renderSessions()
})
}) </script>
<style>
#status {
font-style: italic;
}
.session {
width: 500px;
background-color: #ffff80;
padding-top: 0px;
padding-left: 5px;
padding-right: 5px;
margin-bottom: 10px;
} #menu {
float: left;
width: 300px;
background-color: #c0c0c0;
border: 0.1em solid #000000;
padding: 0px;
margin-top: 18px;
margin-right: 5px;
}
#sessions {
overflow:auto;
} </style> </head> <body>
<span id="status"></span> <div id="menu">
Search:<br/>
<input type="text" id="search"><br/>
</div> <div id="sessions">
</div> </body>
</html>
<html>
If you start looking in the document.ready block, you can see I begin by fetching my speakers. I store that result in a global variable. Once that's done, I then fetch my sessions. Once I have all my data, I can then render them. I wrote both a renderSessions() method and a renderSession() method. That may be overkill - but it worked out ok.
At that point - the only left was a simple search. I didn't bother with all the options the real system had. I just used a simple keyword search that matched against the title and the description. You can play with this here:
http://www.coldfusionjedi.com/demos/maxfix/
Obviously this was just built for fun. I wouldn't count on it working forever, nor would I be surprised if Adobe fixes the missing times in their next push. I mainly just wanted to show an example of how you could repurpose someone else's AJAX data for your own benefit. (And yes, now I know when I'm speaking - 3:30 on October 27th.)