In this guide you will learn how to create a building block for use in Popfly. It is recommended that you use a development environment other than Popfly for creating your block, since the JavaScript editor currently lacks some basic functionality, such as syntax highlighting. (We’re working on that.) We recommend Microsoft Visual Web Developer 2008 Express with Popfly Explorer. It can be freely downloaded from:
http://msdn.microsoft.com/vstudio/express/vwd/A block is a piece of middleware that is contained in a single JavaScript file (.js), which provides methods for user generated code to invoke. It also has an associated XML manifest file which describes more about the block. A block may also make use of resource files such as XAML files, images, etc.
A block can act as a middleman between externally provisioned services such as web services, or it may simply be a library of useful functions, e.g. a function that calculates the area of a circle given a radius. A block can also act as a display surface: something which takes data from other blocks and displays it in a meaningful manner, and allows the user to interact with it. In this guide we will use Soapbox (
http://soapbox.msn.com/) as the example web service for which we are building a block.
EditPrerequisites
Before you begin building blocks you might need to have some knowledge of the following things:
1) Programming – While knowledge of Javascript would be handy it isn’t that hard to pick up if you’re familiar with another language.
If you want to build a block that uses another service like say Soapbox you’ll also need to familiarize yourself with that.
EditJavaScript Class Layout
As a user may decide to have many blocks included in a single web page, so it’s important that your JavaScript is encapsulated in an object and that you don’t use global functions, with the exception of event hooks for Silverlight. You can have as many class definitions as you need, but there should be one for your block and as many as you need for objects that are returned from operations.
Rule 1: Make classes, not scripts
The code above declares a function SoapBoxClass which we will extend by modifying its prototype as shown below.
EditOperations
Functions that you want exposed to the user need to be defined in the class prototype; this is done by creating a new entry in the prototype along with the function definition.
e.g.:
JavaScript:
SoapBoxClass.prototype.search = function (<parameter>) {
};
Each block needs to have a class definition. e.g.:
JavaScript:
function SoapBoxClass(){
}
The following is an example of a function declared in the global namespace. Doing something like this is bad in Popfly. This is bad because you have no control over what other functions might exist in the global namespace, and as such your block could behave unpredictably.
JavaScript:
function degtorad(deg){
return deg * (2 * Math.PI) / 360;
}
Also make sure you declare variables, in Javascript you can get away with not declaring and doing something like:
JavaScript:
function degtorad(deg){
x = Math.Pi * 2;
}
While you’ll be able use x it’s actually been declared in the global namespace not within the scope of the function, so now if anyone else’s block uses x like that your variable will get all mixed up. So instead make sure you do something like:
JavaScript:
function degtorad(deg){
var x = Math.Pi * 2;
}
EditInteracting with Popfly
Since a block is middleware, you’ll need to make calls into Popfly to get data and to interact with the user.
EditGetting Data from a Web Server
Most components will take data from a 3rd party, such as a website. If you call a website that resides on a domain other than Popfly, the browser’s security sandbox will either prompt the user for permission or throw an error with no prompting. So that this doesn’t occur, Popfly provides two methods for components to retrieve data from any server: getXml and getText.
EditgetXml - XML Data
JavaScript:
<xmlObject> = environment.GetXml(<url>);
xmlObject - is the http response loaded and parsed into an XML document.
url - is the URL of the server plus an parameters to be passed to the server, e.g.
http://blogs.msdn.com/coding4fun/rss.aspx?Tags=c4fnews
e.g.:
JavaScript:
var url = "http://soapbox.msn.com/rss.aspx?searchTerm="+searchTerm;
var resultXML = environment.getXml(url);
EditgetText - Text Data
JavaScript:
text = environment.GetText(<url>)
text - is the body of the http response.
url - is the URL for which the http request is to be made to, e.g. http://blogs.msdn.com/coding4fun/
EditUsing Your Own Data
Popfly exposes an API that allows block authors to store data in a simple file. We place no limitation on the type of data, however, you will need to interact with it from Javascript so you might want to look at JSON and XML. These files are public so anyone can anonymously add to it and read it. Therefore, your block should not be storing important or personal information. It’s also important that your block handles the case when the user first runs their mashup and the files are not present; the user should always be able to use your block regardless of the existence of data. These functions are not available in preview time and can only be used at run time. You can check the environment.previewTime property which is a bool; if the value is true then the mashup is being run in preview time otherwise it is being run in the normal runtime environment.
EditStoring
JavaScript:
environment.addAnonymousData(fileName, stringData, callback);
fileName- Is the name of the file e.g. "comment.txt"
StringData - The string to be saved, Popfly is agnostic of format, it will append the string to the file (creating one if there isn't one already) so you could write data as a comma separated list as JSON, XML, a base64 encoded string or anything really.
callback - the function that should be invoked once the request has completed should have a signature like:
JavaScript:
function loadComplete(result);
Where result is a bool indicating that request was successful if true and false if not. Ideally your call back should be an anonymous function like the:
JavaScript:
environment.addAnonymousData(fileName, stringData, function(result){});
Or a pointer to a member function for the class, like the example below:
JavaScript:
SampleClass.prototype.OnSave= function() {
environment.addAnonymousData("layout.txt", "<value>some data</value>" ,delegate(this, this.OnSaveCompleted));
}
SampleClass.prototype.OnSaveCompleted = function(result) {
if (result)
{
alert("Save successfull!");
}
}
Or a pointer to a member function for the class, like the example below:
JavaScript:
SampleClass.prototype.OnSave= function() {
environment.addAnonymousData("layout.txt", "<value>some data</value>" ,delegate(this, this.OnSaveCompleted));
SampleClass.prototype.OnSaveCompleted = function(result) {
if (result)
{
alert("Save successfull!");
}
}
EditLoading
JavaScript:
environment.loadAnonymousData(fileName, callback);
fileName - Is the name of the file e.g. "comment.txt"
callback - the function to invoke once the data has been fetched. The function should have a signature like:
function loadComplete(result)
Where result is the string loaded from the file.
JavaScript:
SampleClass.prototype.OnLoad = function() {
environment.loadAnonymousData("layout.txt", delegate(this, this.OnLoadCompleted));
}
SampleClass.prototype.OnLoadCompleted = function(result) {
if (result != "")
{
alert(result);
}
}
EditClearing
JavaScript:
environment.clearAnonymousData(fileName, callback);
fileName - Is the name of the file e.g. "comment.txt"
callback - the function to invoke once the data has been cleared the function should have a signature like:
function clearComplete(result)
Where result is a bool indicating that request was successful if true and false if not.
JavaScript:
SampleClass.prototype.OnClear = function() {
environment.clearAnonymousData("layout.txt", delegate(this, this.OnClearCompleted));
}
SampleClass.prototype.OnClearCompleted = function(result) {
if (result != "")
{
alert(result);
}
}
text - is the body of the http response.
url - is the URL for which the http request is to be made to, e.g. http://blogs.msdn.com/coding4fun/
EditOutput Results
Results can be output in two ways: either as an object returned be it a single object or an array of objects, or as HTML to be added to the output page. Since Popfly is all about taking data from multiple sources and combining it to make ‘mashups’, we would much prefer that all the functions you write return objects. Blocks for displaying information are exempt from this suggested requirement.
EditReturning Objects
If you want to return an object, it needs to have a class definition, or be returned as a JavaScript object or an array. To declare the public properties of each block you can dynamically create them by using the this keyword, as illustrated below:
JavaScript:
function SoapBoxVideo(title, link, pubDate, description, category, playURL, streamingURL, copyright, thumbnailURL, credits, toStringHTML)
{
this.Title = title;
this.Link = link;
this.DatePublished = pubDate;
this.Description = description;
this.Category = category;
this.ThumbnailURL = thumbnailURL;
this.PlayURL = playURL;
this.Copyright = copyright;
this.ThumbnailURL = thumbnailURL;
this.Credits = credits;
this.Copyright = copyright;
this.StreamingURL = streamingURL;
this.DescriptionHTML = toStringHTML;
}
Subsequently, the method
getFeaturedVideos will return an array of SoapBoxVideo objects, as below:
JavaScript:
SoapBoxVideo.prototype.getFeaturedVideos = function() {
var soapBoxVideoArray = new Array();
soapBoxVideoArray[0] = new SoapBoxVideo(“Foo Bar”);
return soapBoxVideoArray;
};
You could also return an array of objects in just the same way:
JavaScript:
var awesomeValues = new Array();
return awesomeValues;
EdittoString()
Although most objects should not output HTML directly, Popfly might want to display the output in the browser. All of your return objects should implement a toString function, which will return a HTML string that represents the object.
e.g.:
JavaScript:
SoapBoxVideo.prototype.toString = function(){
var html = "";
if(this.Description)
{
html += "<p>"+ this.Description +"</p>";
}
return html;
};
EditExceptions & Errors
As general rule a block should not have any exception handling. It is better to let the block continue to run. Catching exceptions should be done in rare cases. If you want to pass an error message to the user, for example if an input was invalid, throw an exception with the string you want to be displayed. Avoid using
alert() messages if at all possible As they steal the users focus and as your block maybe used on other sites outside of Popfly that can make things worse.
EditMetadata
Please see the
Block Metadata page.
EditBlock Metadata Type System
Please see the
Block Metadata Type System page.
EditCategorising Your Block
In order to make sure your block shows up in the right categories you will want to tag it when you save it using one of the tags listed on the
Block Categorie page.