Walkhtrough / Turorial 1
How do do a simple mashup with mashupstations cross-domain proxy.
Actually this is a turorial on how to make a mashup period. If you have you own proxying server, everything else will apply just the same.
For this demo, I have just made everything into a single html file, js, css and all.
I use only two different services; a RSS feed from
Yahoo's "latest buzz" ∞page and
Yahoo's Image Search REST service. ∞ Both are called through mashupstations server (obviously!), but I have made a simple php wrapper for RSS feeds, since I couldn't find a good stuff in pure js. I made a small js api for the wrapper just to make things more complicated. The other service is proxied directly, i.e. I take care of the parsing myself in the page.
Anyway, the file begin with including the required javascript;
<html>
<head>
<title>MashupStation Simple mashup demo</title>
<script src="../../scripts/prototype.js"></script>
<script src="../../rss-api/rss-api-client.js"></script>
<script src="../../scripts/connect_test.js"></script>
The files can be referenced the same way from any user directory. Prototype is a standard easy-to-use prototype 1.5 javascript library. The rss-api-client is a simple wrapper which parses flat data from any rss source for easy js consumption. The connect.js is important, sicne it contains the callMashupStation(url, response, error) function which makes all the Ajax calls through our cross-domain proxy.
You *could* do this yourself, but the main functionality of connect.js is that it computes a md5 hash on parts of the page which must match the hash made when the page was uploaded to the server, so that not anyone can hijack you page and data (and devkeys).
And next, some HTML;
<h1>Mashup example 1 - The latest buzz on Yahoo in pictures</h1>
<div id="listarea" class="lista"> </div>
<div id="titlearea" class="titlearea"> </div>
<div id="slidearea" class="slidea"> </div>
Then comes the script section;
Which I usually start with the debug functions, which are removed if need be at a later date;
//----------------------------------------------------------------------------------------------------------------
// Debug functions, which is a must in any js-infected page. //----------------------------------------------------------------------------------------------------------------
// Show the debug window
function showDebug() {
window.top.debugWindow =
window.open("",
"Debug",
"left=0,top=0,width=500,height=700,scrollbars=yes,"
+"status=yes,resizable=yes");
window.top.debugWindow.opener = self;
// open the document for writing
window.top.debugWindow.document.open();
window.top.debugWindow.document.write(
"<HTML><HEAD><TITLE>Debug Window</TITLE></HEAD><BODY><PRE>\n");
}
// If the debug window exists, then write to it
function debug(text) {
if (window.top.debugWindow && ! window.top.debugWindow.closed) {
window.top.debugWindow.document.write(text+"\n");
}
}
After that I usually put supportive classes, meaning holders of data, so I can swing them around easier
//----------------------------------------------------------------------------------------------------------------
// This is just a support class, which I could have skipped over. However, they're really easy to do and it makes
// it less simple to shoot yourself in the foot when you have a small class which describe you internal data in the same file.
// I only use this in two places; When parsing the JSON output from the Yahoo image search, and in the next function
// called from there, which build the presentation.
//----------------------------------------------------------------------------------------------------------------
var yahoonewsitem = Class.create();
yahoonewsitem.prototype =
{
initialize : function(title, link, summary, content)
{
this.title = title;
this.link = link;
this.summary = summary;
this.content = content;
//debug("Creating new newsitem "+title+", "+link+", "+summary+", "+content);
}
}
Now we have constants for the code in question. Could easily have been put at the top instead, but this place seemed like a good idea at the time;
//----------------------------------------------------------------------------------------------------------------
// Some initialization stuff...
//----------------------------------------------------------------------------------------------------------------
//showDebug(); //Show or not show ye debug window
var NUMNEWS = 18; // How many results are we requesting from the yahoo buzz RSS feed?
var NUMPICS = 10; // How many results are we requesting from Yahoo Image search each time?
Then it is always good to isolate everything (if possible) in a starter function so that you're certain not to have a lot of dangling intialization stuff all over the page;
//----------------------------------------------------------------------------------------------------------------
// newthings is the starter function, which creates the call to get Yahoo buzz RSS feed info
// This will in turn generate callbacks to rsscb which then call one search reqeust to Yahoo Image search
// for each received RSS item (with the title as search query - golly am I smart or what? :) :p
//----------------------------------------------------------------------------------------------------------------
function newthings()
{
debug("newthings called");
removeChildrenFromNode($('listarea'));
debug("Calling yahoo buzz rss");
callRss("http://buzz.yahoo.com/feeds/buzzoverl.xml", rsscb); //Defined in the rss-api-client.js file included above
}
Next, some action. If all goes well, we get a slew of callbacks from the RSS api client to the next function, which should be understandable what with all the comments and all;
//----------------------------------------------------------------------------------------------------------------
// So, here we are. This is a callback from the rss-api-client, which in turn is a prototype Ajax callback function. What has happened also is that I've made a small
// cache to make sure that we only get the "latest" rss items and not all of them every time. So in the rss-api-client.js
// I first build an array of the latest (and all) news, and the callabck (being this very function at the moment) can then choose
// to get either all or just the new stuff.
//----------------------------------------------------------------------------------------------------------------
function rsscb() //Rss callback
{
var latest = rssGetLatest(); //Defined in rss-api-client.js
debug("Got "+latest.length+" items from yahoo buzz");
var i = NUMNEWS;
latest.each(function(r)
{
if (i > 0)
{
debug("Handling RSS item "+r.title);
var d = document.createElement('div');
Element.addClassName(d, "newsitem");
//------------------------------------------------------------------------------------------------------
// The following is not strictly necessary. I just needed a link to the news, but I was
// toying with having hideable immage galleries which could show themeselves on mouseovers, et.c.
// Ignore everythign except the link. Not however that the properties of the build news item are;
//
// 1. title - The Title of the RSS item, oddly enough
// 2. link - A Link to the original page or something that the RSS item is a representation of.
// 3. desc - A longer decription of the item in question - not used here.
//
//------------------------------------------------------------------------------------------------------
d.innerHTML = '<a href="'+r.link+'" class="newsitem" id=rssnews_'+(NUMNEWS -i)+'>'+r.title+'</a> ';
//debug("Attempting to append new item to listarea");
$('listarea').appendChild(d);
//debug("Attempting to call yahoosearch...");
var clean = r.title.slice(2); // Hackish way to remove the leading number and dot from the title, like "5. Shakira", et.c.
callYahooSearch(clean, NUMPICS, 1, "yscb");
}
i--;
});
}
Then it is time to do some real cross-domain ajax stuff; The interresting thing here (which you might not even need if you use dapps from dappit, for instance, is that you can hide you API keys for any service on your directory on mashupstation so that they don't show in the pages source. Could come in handy. Also, the path has to be given correctly, since that's where we look for the key, if used.
//----------------------------------------------------------------------------------------------------------------
// Calls the cross-domain proxy with a request to call the yahoo image search API.
// Note how we include the magic '!key1!' in the request-to-be-forwarded, which the cross-domain proxy
// will substitute for the contents of a file with the name "key1" in the directory described in the 'path' argument.
//----------------------------------------------------------------------------------------------------------------
function callYahooSearch(query, howmany, begin, callback)
{
debug("callYahooSearch called with query='"+query+"' howmany="+howmany+"begin="+begin);
if (query == "")
{
debug("Meh. Empty query!");
return;
}
// Build a query string.
url = 'http://search.yahooapis.com/ImageSearchService/V1/imageSearch?appid=!key1!&query='+query+'&results='+howmany+'&start='+begin+'&adult_ok=1&output=json&callback=yahooCallback';
pars = "proxy_url="+url;
// .. and off she goes
debug("callYahooSearch calling proxy with url "+url);
callMashupStation(url, yahooShowResponse, reportError); //Defined in connect.js
}
Next is the deault ajax callback function, which will be called if the Ajax call is successful. However, JSON makes the callback function get called by magic anyway, so we really don't need to do anything here.
ffunction yahooShowResponse(originalRequest)
{
debug("Response from server OK");
}
Here is the function we reffered to in our Ajax call. Since we specified output=json and this method name as callback, it gets called.
//----------------------------------------------------------------------------------------------------------------
// Handling the JSON response...
//----------------------------------------------------------------------------------------------------------------
function yahooCallback(jData)
{
var latest = new Array();
if (jData == null)
{
debug("There was a problem parsing search results.");
//debugger;
return;
}
//get the values out of the object
var result = jData.ResultSet.Result;
var ra =$A(result);
/debug("yahoocallback got back "+ra.length);
a.each(function(r)
{
if (r.nodeName != "#text")
{
var title = r.Title;
var thumblink = r.Thumbnail.Url;
var sum = r.Summary;
var cont = r.Url;
var n = new yahoonewsitem(title, thumblink, sum, cont);
//debug("yahoo search item "+r.Title);
latest.push(n);
}
});
yscb(latest); // Call back to some nice function which knows how to present the stuff.
}
A simple Error function is also a must;
//----------------------------------------------------------------------------------------------------------------
// This gets called it prototype got an error while processing our Ajax request.
//----------------------------------------------------------------------------------------------------------------
function reportError(request)
{
debug("Ajax error! Argh!!;"+request.responseText);
}
Then comes the presentation function which just throws everything around in a jumble, one picture after another - no formatting here! I'm sure - nay - convinced you can do something better than this.
//----------------------------------------------------------------------------------------------------------------
// The reason I broke up the return callback from the ajax request into two functiosn that call each other (i.e. yahooCallback and
// this, was that the first traverses the JSON structure and build a flat array for presentation and this functions
// *does* the presentation. Also, it was kind of longish as a monolithic function.
//----------------------------------------------------------------------------------------------------------------
function yscb(newnews)
{
var x = newnews.length;
debug("********** Yahoo search callback returned with "+x+" images");
if (x > 0)
{
var sd = document.createElement('div');
Element.addClassName(sd, "hoverbox");
newnews.each(function(n)
{
debug("Adding new image title="+n.title+", img.src="+n.link+", a.href="+n.content+", img.alt="+n.title);
var a = document.createElement('a'); // Wrap both normal and prev in an a
a.href=n.content;
a.target="_blank"; // Open in a separate window when clicked
var img = document.createElement('img');
img.src = n.link;
img.alt = n.title;
a.appendChild(img);
sd.appendChild(a);
//debug("Finished adding new slide "+n.title);
});
$('slidearea').appendChild(sd);
}
else
{
debug("yahoosearch has nothing new...");
}
}
...Aaaand then we kind of wraps everyhting up by calling the starter function and see where it runs. The we're done!
// Do the stuff were here to do
newthings();
</script>
</body>
</html>
OK. So I lied a little bit. I ommitted some css stuff, and some google ads, but other than that it's the actual page. If you are so inclined, you could check the adultok flag to 1, to see what kind of images yahoo produces on seemingly inocuous search terms :).
If you want to see the page in its entirity, right-click
here∞ to download it and tweak it as you see fit.
There are 35 comments on this page. [Display comments]