So, you've decided to create a module... Good! You're definitely in the right place! This document should take you from beginning to end in designing, building, uploading, and testing your module. Let's get started.

Designing a module is a little different from designing a regular Web page. Here are a few things to keep in mind:
We love microformats, mostly because we like making sense out of nonsense, and order out of chaos. Microformats provide a standards-compliant way to add meaning to otherwise meaningless or nebulous markup. A microformat gives us the ability to create human-readable and, more importantly, human-writable documents that we can also feed to machines to do intelligent things with them.
A machine is going to take your module, rip it into little pieces and put it back together in a format that it can use. In order to do that, it needs to know what pieces to look for and then what do with them. By using our microformat, you can build your module in almost complete seclusion, document it , and test it, all in one XHTML document. Cool, huh?
Because we love them so much, we decided to create our own microformat. We call it ModuleT. Our microformat allows you do what we said in the paragraph above: create a module, document it ,and test it, all in one XHTML document.
To get familiar with the microformat, we recommend you read through the profile and then check out the module skeleton and our cheesy example module. We also recommend using the skeleton as the base for whatever module you plan on building. It will save you time and effort, honest.
The most direct path to a valid module is the AOL ModuleT microformat profile. After you've used the microformat, check out our handy 10-point Module Validation Checklist.
If you read through the profile, you probably saw the section on proxy URLs. If you need to access an external server for your module, please make sure that you put that URL in a link in your manifest and make sure rel="proxy" is in that a tag.
Our proxy is, for now, pretty open. We'll blacklist URLs that do "evil" things. But we want to trust you as fellow developers not to go do "bad" things with the system.
This is where we get to the fun part. We thought it would be useful if modules could be more than just HTML, JavaScript, and CSS. We figured there would be folks out there who really liked Web services and XSL, and might want to use them in modules. There are a couple of ways you can do that with JavaScript, but the libraries are huge! Also, we figured there might be some existing Web services that people without a lot of JavaScript experience might want to take advantage of. So, in order to make ourselves happy, we came up with the idea of server-side modules! What does that mean? It means you can build a module that sucks in content from another Web site, and displays it in your module without writing any JavaScript at all! Cool, huh?
The server-side piece works by looking for some special attributes in your module manifest, and then grabbing the associated URLs, dumping the content inside those parts of your module, and replacing any default content they may contain.
You might want to think about doing a server-side module if:
If you fall into one of those two categories, welcome aboard the server-side module train! There are a couple of things you need to do in order to build a server-side module:
xmlns:pub="http://developer.iamalpha.com/xmlns" to the <html> tag in your module.pub:src="http://urltoyoursnippetorxml.com"pub:style="http://urltoyourxslfile.com"Let's say you had a very simple server-side module that grabs a small chunk of HTML and dumps it into your module. To do this (this isn't a complete manifest), you'd have something like the following:
<div class="body my-module" pub:src="http://mycoolsite.com/headlines.txt"></div>
If your list of headlines looked like this (it's a short list):
<ul>
<li><a href="http://site.com/story1">Story One</a></li>
</ul>
Your module would return this when shown to the user:
<div class="body my-module" pub:src="http://mycoolsite.com/headlines.txt">
<ul>
<li><a href="http://site.com/story1">Story One</a></li>
</ul>
</div>
Is that cool, or what? If you want to apply XSL to the content returned by your pub:src URL, then use pub:style to point to your XSL stylesheet.
Once you've created your module, you probably want to show it off, so go upload it! The instructions are pretty simple:
Things may not work right away when you upload your module. It happens. There are a couple tips we can give you that should help get you past some of the things that trip us up all the time.
html tag. Make sure your <html> tag has xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" in it.We mentioned them above, but here are the examples again in handy dandy list:
You've made it all the way through this document. And because you made it all the way to the end, we just want to say, "Thank you!" We're really excited about the possibilities for these modules, for what can be built and what could come out of it, and we hope that comes across in the content we've created here and in the FAQ.
Also, please check out the Module Best Practices page for more information and guidelines for writing CSS for your modules.
Don't forget our module workshops for step-by-step instructions on how to:
We appreciate your taking the time to read these documents, and hopefully you'll take some time to create a module and tell us how the process went either on the listserv, on the message board or post a comment on the blog. We hope you'll stick around and see this through. We're sure you won't be sorry!
This document is a complement to Creating A Module, Module Best Practices and Setting Up A Local Development Environment. It will take you from start to finish building a brand new module. Hopefully, by the end of this document, you'll be able to go build your own modules without looking back.
Every module should start from the skeleton. It gives you placeholders for all the important elements of a module, and makes sure you start with a solid foundation (which is why we called it "skeleton" to begin with).
If you haven't already looked at the source of the skeleton, here it is:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head profile="http://iamalpha.com/.developer/profile/index.html">
<title>Module Title</title>
<link rel="stylesheet" type="text/css" id="manifest" href="http://imakealpha/.developer/css/manifest.css"/>
</head>
<body class="module" id="module-name">
<h1>Module Title: Version <span class="version">1.0</span></h1>
<h2>Edit Interface</h2>
<div class="edit module-name">
</div>
<h2>View Interface</h2>
<div class="view module-name">
<h3 class="head"></h3>
<div class="body"></div>
</div>
<h3>Description</h3>
<p class="description">Description goes here</p>
<h3>Detail</h3>
<p class="detail">Detail goes here</p>
<h3>Author Information</h3>
<ul>
<li><a href="http://yourdomain.aol.com" rel="author">Your Name</a> - <a href="http://yourdomain.aol.com/contact" rel="contact">contact</a></li>
</ul>
</body>
</html>
In the following sections, we'll explain what each section of the document is and what it should be used for.
This is the view:
<div class="view module-name">
<h3 class="head"></h3>
<div class="body"></div>
</div>
The h3 is the title of the module that will be displayed to the user.
The div with a class of body will contain the contents of our module.
It's not required that you use h3 and div for the head and body of your module's view, but we recommend it. You could use any heading element (h1 - h6) for the head, and any block element (a list, paragraph, div, blockquote, etc) for the body, as long as they have the right class on them.
This is the module description:
<p class="description"></p>
As you can see, it's empty. This isn't how a final module should look. There should be some text there that describes your module to someone who might want to put that module on a web page somewhere.
A good rule of thumb is that the module description shouldn't contain any acronyms, buzzwords or things that might confuse the "average user". Your mileage may vary as to what "average" means to you, but use your best judgement.
Also, your description should be about a paragraph. If you need more than one paragraph, put your paragraphs in a div with the class "description".
This is the module detail:
<p class="detail"></p>
A module's detail should contain more information than the description, and may therefore be much longer than a single paragraph. It should contain whatever information you think a fellow developer might need or want to know about how your module works.
Here's what the author information looks like:
<ul>
<li><a href="http://yourdomain.aol.com" rel="author">Your Name</a> - <a href="http://yourdomain.aol.com/contact" rel="contact">contact</a></li>
</ul>
What's the point of writing a module if you don't get any credit? This is so both users and systems that use your modules know where to look for you if they want to send you presents, hire you, or make jokes at your expense.
Now that we're done describing the physical pieces of our module, we're going to build one, documenting each step along the way in a process that hopefully won't kill us by the time we reach the conclusion.
The big question is "What do we want our module to do?" Let's just pretend, for a moment, that we're a sports junkie, and would love to have a module that displayed the most recent sports headlines from AOL Sports? Yes, I know, it's blatantly partisan of us, but this is an example. You can substitute your own RSS feed if you want.
Since the feed we're using doesn't have full content, just headlines, that's going to make it easier, right?
Our design should be just as easy. We're going to have a module head, with a heading in it, probably "Latest Sports Headlines", and then an unordered list of headlines that link to the actual story. Couldn't be easier, right?
After we've got our design done (without opening Photoshop or drawing anything on napkins, mind you), it's time to think about how we're going to define our module in the manifest. Here's how we wrote it up:
The top five sports headlines from AOL Sports.
This module grabs the AOL Sports RSS feed via Ajax parses it, and displays five headlines.
And that all should look like this in our manifest:
<h3>Description</h3>
<p class="description">The top five sports headlines from <a href="http://sports.aol.com" rel="proxy">AOL Sports</a>.</p>
<h3>Detail</h3>
<p class="detail">This module grabs the <a rel="proxy" href="http://xml.news.aol.com/aolnewsrss.xml?cid=1690&amp;feedtype=manual">AOL Sports RSS feed</a> via AJAX, parses it, and displays five headlines.</p>
<h3>Author Information</h3>
<ul>
<li><a href="http://lawver,net" rel="author">Kevin Lawver</a> - <a href="mailto:alphablogger@aim.com">contact</a></li>
</ul>
Now that we've defined our module, it's time to actually build it. We want it to look something like this when we're all done:
Since we're going to get the headlines from the RSS feed, the markup for our module is actually pretty simple:
<div class="view top-sports-headlines-example">
<h3 class="head">Top Sports Headlines</h3>
<div class="body">
<ul class="top-sports-headlines-list">
<li>Loading...</li>
</ul>
</div>
</div>
Now, we have to do the hard part and go get our headlines! To do that, we're going to need to use the JavaScript SDK, and set up our local development environment.
Back? Good! Now we get to write some JavaScript - yippee!!
First things first, we need to create a JavaScript function that grabs our headlines and displays them. This is a little more complicated than it might be if you were building a regular Ajax function (or easier, I don't know, your mileage may vary), because you have to use our proxy because of XmlHttpRequest's security rules. To start, we're going to make sure that we create unique functions that aren't going to be replaced by someone else, so we'll start with this shell, which I saved as "top-sports-headlines.js" in the same directory as my manifest:
function topSportsHeadlines() {
}
function topSportsHeadlinesWrite() {
}
Pretty exciting, huh? Before we get ahead of ourselves though, we need to add a few things to our module manifest so we get what we think we're going to get when we actually have something to do. In the head of your manifest, add the following:
<script type="text/javascript" src="jssdk/lop.js"></script>
<script type="text/javascript" src="top-sports-headlines.js"></script>
The two jssdk files may be in a different location depending on where you installed them when you set up your local development environment, so those locations may need to change.
We also need to make sure our javascript runs, so inside your body tag, add the following:
onload="topSportsHeadlines();"
Now we need to actually write some javascript. We'll try to take this step-by-step:
We need to tell the SDK that we're using the Ajax stuff, and set up our success and error functions. They should look something like this:
function topSportsHeadlines() {
var headlineURL = "http://xml.news.aol.com/aolnewsrss.xml?cid=1690&feedtype=manual";
var success=function(xml) {
}
var error=function(xml) {
}
lop.Ajax.prototype.simpleGet(headlineURL,success,error,null);
}
This is the barest of bones for this function, and it won't actually show us anything if we try to run it. But, this will probably be the basis for almost any module you build with Ajax, so it's nice to see it this way.
We need to start filling out our skeleton with some muscles. First, we need to find the lists we're going to populate with the headlines, and decide if we even need to go get them (say, if we don't find any lists to populate). In people-talk, here's what we're trying to do:
top-sports-headlines-list.And here's the code that does that (new stuff is in blue):
function topSportsHeadlines() {
var headlineURL = "http://xml.news.aol.com/aolnewsrss.xml?cid=1690&feedtype=manual";
var container = document.getElementsByTagName("ul");
var lists = Array();
for ( var i=0; i<lists.length; i++ ) {
if ( lists.length > 0 ) {
var success=function(xml) {
}
var error=function(xml) {
}
lop.Ajax.prototype.simpleGet(headlineURL,success,error,null);
}
}
}
Now that we have our lists, it's time to define our error condition, and fill out our success callback with actual parsing magic. We also need to fill in the topSportsHeadlinesWrite() function. Again in people-talk, here's what we're trying to do:
And here's the code to do that (new stuff is green this time):
function topSportsHeadlines() {
var headlineURL = "http://xml.news.aol.com/aolnewsrss.xml?cid=1690&feedtype=manual";
var container = document.getElementsByTagName("ul");
var lists = Array();
for ( var i=0; i<lists.length; i++ ) {
if ( lists.length > 0 ) {
var success=function(xml) {
var dom=xml.responseXML;
var items =dom.getElementsByTagName("item");
for ( var i=0; i<items.length; i++ ) {
var item=items[i];
var title=item.getElementsByTagName("title")[0].childNodes[0].nodeValue;
var link=item.getElementsByTagName("link")[0].childNodes[0].nodeValue;
output+="<li><a href='" + link + "'>" + title + "</a></li>";
}
topSportsHeadlinesWrite(lists,output);
}
var error=function(xml) {
output = "<li class='error'>There was an error retrieving the headlines. <a href='javascript:topSportsHeadlines()'>Please try again</a>.</li>";
topSportsHeadlinesWrite(lists,output);
}
lop.Ajax.prototype.simpleGet(headlineURL,success,error,null);
}
}
}
function topSportsHeadlinesWrite(lists,output) {
for ( var i=0; i<lists.length; i++ ) {
lists[i].innerHTML = output;
}
}
There, you've now got a script that does everything we set out to do in the definition phase!
This module is actually available in the module gallery, and you can go play with it in the Playground or view the completed source. Hopefully, this tutorial helped clear up any confusion you had about creating modules, or filled in some blanks left by the other documents.
This was a relatively simple module, as far as AJAX modules go. We'll be posting tutorials on server-side and other kinds of modules in the near future. But right now, we need a nap.
Thank you for deciding to create a module! We really appreciate it and think it will be a lot of fun. But, before you get too far into building, please review these best practices and guidelines. Hopefully they'll save you some trouble and lead to a better module.
The first part in building a module is deciding what it’s going to do, how it’s going to do it, and what it’s going to look like. These are very important decisions, and will determine whether your module is awesome or not.
Taking inspiration from other widget/module products like Apple’s Dashboard, we think that modules should do one thing and do it well and efficiently. Design shouldn’t be overly complex and shouldn’t require a steep learning curve.
Building a module that does one thing “right” is harder than it sounds. Concentrate on solving the simplest problem, and then spin off other modules to solve other problems and do other things.
Now that you’ve designed your module, you have to build it. Since modules are going to exist as HTML, it’s time to think about what markup you’re going to use. We require that all modules validate (with a few minor exceptions, which we’ll get into later). “Valid” markup means that your module code:
There are, of course, other rules. Most Web development tools will allow you to build XHTML 1.0 documents and will even help you check for validity.
Make it easy on yourself and use our module skeleton to get started with modules. It provides a good valid base to start from. And once your module code is complete, validate it using the AOL ModuleT validator.
On top of validity, we prefer widgets that have semantic markup. What does that mean? It means that the markup implies meaning about the content of the module, not presentation, which will be taken care of in a style sheet. For example, if you have a list of names:
Kevin
Joe
Shawn
Paul
Ju
Kedar
Don't mark it up like this:
Kevin<br/>
Joe<br/>
Shawn<br/>
Paul<br/>
Ju<br/>
Kedar<br/>
Mark it up like a real list:
<ul>
<li>Kevin</li>
<li>Joe</li>
<li>Shawn</li>
<li>Paul</li>
<li>Ju</li>
<li>Kedar</li>
</ul>
For another example, to create a subheading on your page, don't mark it up like this:
<big>My Section Heading</big>
Mark it up using a specific subhead format:
<h3>My Section Heading</h3>
This may seem like extra work, but ultimately semantic markup means less markup and more options for styling when you get to writing the CSS for your module.
We also love the idea of microformats. In fact, the HTML document you create to submit your module is a microformat. We highly recommend you use existing microformats if they apply to your module, and try to follow the philosophy behind them if you’re doing something new.
Because users may pull more than one instance of a module onto their pages, using ids is not a good idea. Use classes instead. Also keep in mind that there may be many modules on a page, and they were undoubtedly created by developers with different ideas of good class names. What does this mean to you? It means you need to go back and look at the semantics section and apply it to your class names.
Here are some examples of bad class values:
Here are some good class names:
Classes should be just as semantic and descriptive as the markup you use. They should provide even more information about the data. Semantic markup is specific about a list item, where an <li> lets you know only that it’s a list item. If the class value is first-name, you now know that not only is this a list item, it’s someone’s first name.
We'll be presenting a list of reserved classes and ids in the near future. So, be on the lookout for that.
We’ve created a microformat for creating modules that provides you, the developer, a single document where you can build, test, and deliver your modules. This microformat is documented in its profile. You can find more information on microformats in general on the microformats Web site.
Microformats provide many benefits, but for us, the main one was providing a single place for development, testing and delivery. We’ve found over the years that if you require separate files for meta data or descriptions that they never get written. With the microformat, you can do everything in one file, which should lead to better data about modules, and better modules because they’ll be easier to test.
To check that you're following our microformat, make sure you use our AOL ModuleT validator.
For pages including modules, there will be two sets of CSS: module styles and page styles. Module styles should define properties needed for a module, and should be generic enough to be overridden easily by page styles. We’ll explain some of the best practices for doing that later on in this section. If you're in need of a little CSS refresher, we've also put together an overview of specificity and positioning that might help you make more sense of all of this.
Don't change the style of any generic HTML elements. You should declare styles that apply only to your module. To do this, begin all styles with your module name. For example, if your module's name is "Top Sports Headlines", the class on view and edit should be "top-sports-headlines". Here's an example:
<div class="view top-sports-headlines-example">
<h3 class="head">Top Sports Headlines</h3>
<div class="body">
<ul class="top-sports-headlines-list">
<li>Loading...</li>
</ul>
</div>
</div>
In order to keep all your styles "contained" within your module, all of the styles you declare should begin with ".top-sports-headlines". That way, you'll only select your module and its descendants, not things outside of it. For example, if we wanted to style the list, and list items in the module above, we'd write something like this:
.top-sports-headlines ul {
list-style:none;
}
.top-sports-headlines li {
padding:0 0 .5em .5em;
}
Hopefully, this is clear. If not, please leave us a message on the message board and we'll explain it some other way.
Simple is good. Simple can also be rather difficult when you’re not sure where your module will be on a page or what will be around it. But, it’s also important for the CSS you write around modules to be simple to allow for the greatest flexibility in styling the page as a whole.
Avoid styling:
The power of CSS lies in its ability to define selectors that can apply to different kinds of elements, and elements in different states (selectors). For modules, your selectors should be as simple and with as low a specificity as possible. This means that they should apply to your module in the least specific way possible. For example, if your module has a class of kevins-module, your styles should all start with .kevins-module, but not with body div.kevins-module. Make sense? This will allow you to create a pleasing and usable default style for your module, but allow for page style sheets to get more specific and override your styles without a lot of extra work.
Need more examples? OK! Here’s our module markup. This is a simple static content module (without the rest of the manifest) that contains the preamble to the Constitution.
<div class="module constitution-preamble">
<div class="view constitution-preamble">
<div class="head">
<h3>The Preamble To The Constitution Of The United States of America</h3>
</div>
<div class="body">
<p><strong>We the People</strong> of the United States,
in Order to form a more perfect Union, establish Justice,
insure domestic Tranquility, provide for the common defense,
promote the general Welfare, and secure the
Blessings of Liberty to ourselves and our Posterity,
do ordain and establish this Constitution for
the United States of America.</p>
</div>
</div>
</div>
The easiest way to make “We The People” italics instead of bold (if you’re into that sort of thing) you be the following:
.constitution-preamble strong {
font-style:italic;
font-weight:normal;
}
That selector is simple, less specific than it could be, yet still will apply only within your module. If a page style wanted all strong elements to be purple and bold, they could easily override the style:
div.module strong {
color:#c90;
font-weight:bold;
}
Just adding one more HTML element to the selector makes the new one more specific and will override the module one. See how easy that was? Again, check out the CSS overview for more information.
We've created a table of recommended specificity for creating styles for modules and pages. For more information about specificity, see the CSS overview. The levels are also explained in the overview.
| level | maximum specificity | minimum specificity | all selectors should start with: |
|---|---|---|---|
| skeleton | 0,1,1,2 | 0,0,0,1 | n/a |
| module | 0,0,5,n | 0,0,1,1 | .module-name (the class assigned when you created the module) |
| page | 0,n,n,n | 0,1,0,0 | #page (need to look @ this one again) |
| user | 0,n,n,n | 0,2,0,0 | #page #content (since all user styles will be around modules and all modules will be inside of #content) |
For this phase of development, we’re supporting Firefox 1.0.7-1.5 and Internet Explorer 6.0+. You should aim for those too, and you should try to support Apple’s Safari. It will be supported by the time we get to Beta.
Here are a couple of tips on performance:
And here are some performance standards to follow:
We've written a whole page on using JavaScript in modules, which should help you get on your way.
A search engine that indexes a page containing your module will not run the JavaScript on that page. Instead, it will index the HTML as it is received from the server. If you want your dynamic data to be indexed, you have two choices:
Our expectation is that a given module will live in only one locale. If you want a module to exist in multiple locales, create a different version of the module for each locale.
Once a module is on a page, it's there forever. There is no way to go back and update the HTML that is in the module.
If you want to allow yourself to make changes to your module, you have to design the module correctly. That means doing one of the following:
But note: You can change a module in these ways, but it isn't a good idea. Changing a module means that you change the user's page in ways that he did not expect. That's okay if you're fixing a bug. But think carefully about making any changes that will change the user's page.
For feeds used by modules taken out of the AIM Pages Publisher Tool, you can always know what user was viewing the page that requested the feed. But you can't always know what page the user was viewing when he requested that feed. And it is possible for someone to write JavaScript that hijacks your feed and uses it in a different context than you intended.
Let's look at an example. Suppose MyCommentModule uses a feed that allows users to add comments to a page via Ajax Bob adds MyCommentModule to his page, and viewers of that page can add comments.
But Vlad decides that he wants to spam Bob's comments page. So Vlad puts up a page that contains hidden JavaScript that uses the MyCommentModule Ajax feed to add comments to Bob's page. If Jill views Vlad's page, a comment is added to Bob's page that has Jill as its author.
In some cases, like a comment module, the results of this aren't too bad. But in some cases, such as a module that changes a back-end data store that should be secure, this is a potential problem.
The important note here is that module and feed security is tricky. You need to give it a lot of thought.
Your module should do something reasonable even if its feeds have problems. There are two parts to this:
A user may want to remove your module from his page. When that happens, the module receives a notification via the TBD event. Make sure you clean up any server-side resources associated with your module when you receive that event.
But don't move too quickly! You only want to clean up the server-side resources when the user saves the page without the module on it. When that happens, your module receives a TBD event. If you clean up things immediately, you run the risk of having the user delete the module but not saving the page. This means that the module is still on the page and needs its server-side resources.
Debugging XSL can be a whole lot of fun. Hopefully, you've got an XML editor that allows you to run your transformation, which should help some, but if you run into problems once you've uploaded your module, it can be a bit of a pain to troubleshoot what's going on.
Help is here! You can use this form to run your transforms through our cleverly named Transformer (we like to think of it as a lawnmower that turns into a nerd). If it fails, you'll get a really disgusting java error, but it will hopefully lead you to your real problem.
One thing to keep in mind is the Transformer expects you to output valid XML from your transformations, which means you have to have a root element in your output. This one bit us, hence creating this form. Out of tragedy comes hope, right?
Chances are, you'll want to do module development on your own machine. It's only logical, right? This document should make that process a little easier.
If you're going to use our local proxy or server-side module processor, you'll need Apache and PHP5 (or another Web server running PHP5 if you're into that sort of thing). We're not able to provide support for setting up your own Web server, but if you want to, this is probably the way to go. This tutorial over at webmasterstop is a good one for getting Apache and PHP5 up and running on a Windows machine. For OS X folks, you've already got a fine Apache install working for you disguised as web sharing. All you need is the PHP5 Apache module package from Marc Liyanage.
For PHP, we've found no better way to fetch URLs than the PHP class called Snoopy. So, our proxy for module development requires it. Download it and put in your the root directory of your Web server. Then, download our local proxy (remove the .txt extension) to the same directory, and you've just installed a local proxy!
To try it out, try http://localhost/proxy.php?url=http://aim.com (you may need to change that URL to match where you have things installed). That should give you some version of the AIM home page, and it probably looks really funky.
You'll probably want to have a local copy of the SDK, especially if you're going to use our local proxy to grab Ajax-y things. So, first off, go download it. Unpack it ,and throw it into your web root, or whatever directory under your web root that you're going to use for development.
If you're going to do Ajax stuff with the SDK, you'll need to make the following change to conf.js (line 2). It currently should say:
var modulePROXY="/.resource/Proxy?url=";
Change it to:
var modulePROXY="/proxy.php?url=";
(or whatever the URL is to your local proxy).
While playing around with how to do server-side modules, we came up with a prototype server-side module parser in PHP. We're giving it to you here free of charge, guarantee, or warranty (use it at your own risk) because we're swell. So, if you're interested in doing server-side module development, go get it and install it in the same directory where you've put the proxy and Snoopy. Also grab news_example.html and headlines.txt and put them in the same directory so you can test this out before you write your first module. The first time you go to parser.php in your Web browser, you should see a module that has five slightly offensive headlines in it. If you don't, you did something wrong... or we did.
To try out your server-side modules, the URLs in pub:src and pub:style must be absolute (containing http://... plus the full path to the resource), and you can't have any style attributes in the same element as pub:style (because of some funky namespace stuff in PHP5's DOM implementation). To test your module, go to http://localhost/parser.php?page=yourpage.html where "yourpage.html" is a file saved in the same directory as parser.php (again, you may need to change the URL if you installed in a directory other than your web root).
Both the local proxy and parser are not production quality, and therefore are pretty insecure. You should never install them in a production, world-accessible Web server, and should probably remove them if you're not going to use them. These are prototype, pre-alpha pieces of software and could be considered more dangerous than helpful (especially if used in the wrong way). We provide them here just to make your module development a bit easier.
If you've read through the workshops and are now looking for some more information on how JavaScript works inside modules, and what we recommend you do, you're in the right place! JavasScript is powerful stuff, and in the wrong hands, can do all kinds of damage. We certainly have—you have no idea how many times we've crashed browsers with one line of JavaScript. Here are some guidelines for avoiding that kind of frustration.
When you create your module, you set the onload attribute of the body to something. When your module is put on the page, that function is called with two arguments: instance and action. The instance is the id of the element that contains your module. The action is (for right now) either "view" or "edit". If your module has an edit interface, then your onload function is called when the edit pane is opened as well. These two arguments allow modules to find themselves on the page, and to know which set of functions to run.
It's imperative that you keep your onload function in the onload attribute. If it's somewhere else (using window.onload or dojo.event.connect), then the including page won't know where to find it, and your module won't know where it is on the page. Finding your module is harder than you think, especially considering that users may want to place multiple instances of your module on their pages.
If you're going to go through your module looking for other DOM elements, you should always start from the instance passed in by your onload function. That way, you're staying within your module and not running amok over the whole page (well, unless that's your intention, you maverick).
The other way to keep it local is to keep your functions sandboxed in an object. As you'll see in the skeleton, keeping things in an object is a good way to store configuration options for your module, and to keep tabs on the various instances of your module that might be on the page. By keeping the object name linked to your module-name (see the Creating A Module page for more information on that part of it), you're pretty much assuring yourself that no other module, or the page framework, will nuke your object and mess with your functions. Don't create generic function names like "debug," "init" or "go." If some other module developer uses the same function names, whoever gets loaded last wins that battle. Avoiding the fight is the safer bet here.
In many cases, your module may need to save data somewhere in the page. We've created a little API for doing that. There are three important functions you need to be aware of:
document.getElementById(instance), the key is the title of the piece of data you want to save (lsuch as "ur" or "count.") and the data is the string you want to save. Note: Everything gets saved as a string. If you need the data returned as an int, you can wrap getModuleData in parseInt(). Here's an example:
var page=new lop.Page(); //creating the object
page.setModuleData(module,key,data); // module is the DOM node for the module, key is the name of the setting, data is the value.
var url=page.getModuleData(module,"url"); // returns the set data or "null"
There are some things you really shouldn't do. Why? Mostly because they'll cause trouble with other modules and the page as whole, but also because they're just bad habits you should try to break.
Don't use window.onload. It wipes out every other onload function the page tries to run. This is not only just rude, it could break your module and everything else on the page. I know we've just given all the script kiddies out there ideas for chaos, but please don't do this. We recommend using the onload attribute of the body element, which we talk about somewhere else in this document.
Don't use generic function names. We can't emphasize too much how important this is. You can't create a module that depends on a function called "go" when there are ten other modules on the page that may also use that same function. Can't we all just get along?
The easiest way around this that we've found is to create an object for your module, and append functions to the object. Crazy, you say? Maybe so... but if your module is called "aim-fight," you could create an object at the top of your module's JavaScript called "aimFight" and append functions to it as shown below. There's more about this below in the skeleton section:
if ( !aimFight ) {
var aimFight={};
aimFight.go=function(url) {
document.location=url;
}
}
That's not so bad, is it?
Don't use generic global variables. Keep all your variables scoped to your object or function. Inside your functions, all your variables should be created with var. Example:
// good: var myvar=true;
// bad: myvar=true;
Just like generic functions, generic global variables could do all kinds of bad things to other modules and the rest of the page, and just like generic functions, you can keep variables you want to keep around in your object, which is global.
This can be an issue when you want to do development in your browser before uploading a module. djConfig holds things such as where Dojo should look for its packages and various other things. It can break the entire experience for users (and your module) if you rewrite it in the live environment. So, if you need to do it, we suggest using the skeleton, and if your standalone variable is true, rewrite djConfig. That way, you can flip just that variable before you upload and everything's fine.
We love Dojo. Unfortunately, including multiple copies of it in a page causes some problems, especially with drag and drop and getting packages. So, if you're developing a module, please include our copy of Dojo. You can grab it from our Content Delivery Network at the following URL:
http://o.aolcdn.com/iamalpha/.resource/jssdk/dojo-0.2.2/dojo.js
If you need to include a local copy because you're developing without a network connection, feel free to download it and include your local copy. Just remove it before you upload your module. Also, please don't use any other versions of Dojo in your module. That could cause all sorts of calamity.
I know, we all want to do it ourselves, but please, use Dojo. That's what it's there for. Dojo provides all kinds of neato things that you probably want to use, and it will save not only time (because you don't have to write it again) and your user's time because they don't have to download all of your code. They've already got Dojo—all you need to do is use it. We've got more info about Dojo in our introduction to Dojo, and there is a great deal of information about the different packages in the toolkit in their manual. We love it, and we think that you will too.
By now, we've built a few modules, and we've found that the following skeleton cuts down on development time, makes it easier to keep things contained, and makes sure we don't run over the functions of other modules:
if ( !moduleName ) {
var moduleName={}; // Creating a new object.
moduleName.standalone=false; // Setting a couple configuration options
moduleName.debug=false;
moduleName.instances=[]; // Creating an array to hold information about the various instances of your module that may end up on the page.
}
moduleName.init=function(instance,action) {}
moduleName.view=function(instance) {}
moduleName.edit=function(instance) {}
moduleName.save=function(form) {}
moduleName.dbg=function(msg) {
if (moduleName.debug) {
dojo.debug(msg)
}
}
If you use the skeleton, be sure to replace all the instances of "moduleName" with a camel-case version of your module name. If your module name is "joes-paragraph", then moduleName should be replaced with "joesParagraph". This way, you'll have a single object you can add properties to and be fairly sure that no other module will mess with them.
Let's go through the individual pieces now:
This section sets up your initial object if it doesn't exist yet. This is where you can set things such as whether or not your module is in debug or standalone modes, or any other properties you might need to set, such as URLs for different services, or other configuration settings. We always end up using standalone and debug, so we included them.
We'll talk more about this in the onload functions section, but this is the onload function for your module.
Depending on how complex your module is, you may be able to keep all of your code within moduleName.init. But, if that gets cumbersome, it's easy enough to break it up into its own function. For our modules, the view function gets module data, grabs any data we might need over the network, and displays things to the user.
Just like view, edit sets up the edit form. The one thing to remember about edit is that it's torn down and recreated every time you open the edit pane, so you can't just keep DOM node references around for edit. (We learned this the hard way.)
This is the "form handler" for the edit pane. We usually use Dojo's event system to make this function the onsubmit handler for the form. Here's how we do it (inside of moduleName.edit):
var module=dojo.byId(instance);
var f=module.getElementsByTagName("form")[0];
dojo.event.connect(f,"onsubmit",function(evt) { moduleName.save(evt.currentTarget); } );
It works so well, you almost don't even need to know what it's doing! Well, if you really want to know what it's doing and what else you can do with Dojo's event system, check out that link above.
This is a handy one to keep around while you're developing your module. You can put various calls to moduleName.dbg throughout your code, and if you've set moduleName.debug to true, you'll get your debug info. When you want to stop getting debug info, you just set debug back to false. You don't need to take out your debug messages or anything. Pretty handy, huh? There are fancier ways to do debug messages, but this is the simplest.
We all want to know what's going on inside our modules. It's natural. Unfortunately, users don't really want to see a bunch of alerts or other debug info messing with their page. So, how do you debug your module? We talked about this a little bit above in the skeleton section, but we've also created a special debug module that you can drag on your page which will give you a nicely formatted debug area where you can see all your debug messages right there on the page next to your module. It's called "debug-window" and you can drag it in. It allows you to set debug on or off so you don't have to see the debug messages if you don't want to.
There are also some very nicely done debugging tools out on the Internet. Here are some of our favorites:
JavaScript is better than sliced bread. We love it. But, it's also dangerous, especially in a shared environment where a lot of different scripts are flying around doing their own thing without a whole lot of knowledge about what else is on shared pages. If we're all careful about how we write it, we'll all be happy little module developers and our users will be happy little module users. And then we'll all eat cake! Hooray, cake!
We're working on documentation for our SDK, but for now, you can check out the JSDOC and our introduction to the Dojo Toolkit.
Dojo is the Open Source, portable JavaScript toolkit that lets you build interactive modules quickly, animate transitions, and construct Ajax requests with ease. We think Dojo is the most powerful and easiest to use Ajax and DHTML toolkit on the planet.
Here's what you'll need to know to get started with Dojo.
Dojo supports a wide variety of browsers:
Dojo offers several different editions for download, such as Ajax and I/O. When you download an edition of Dojo, you get a pre-built "profile" that includes multiple Dojo JavaScript files rolled into a single dojo.js file. Each edition packages different features into dojo.js. The dojo.js file lets you access all features that are part of the profile (and retrieve all the library functionality in one HTTP request). And dojo.js is the only thing you need to include into your application or page to use the capabilities built into the Dojo profiles that you downloaded. Awesome!
All editions provide the entire Dojo library within dojo/src. Any features that are not built into the dojo.js package can be dynamically loaded at runtime through dojo.require.
The Ajax edition of Dojo is currently available on the AOL Content Distribution Network (CDN), at:
http://o.aolcdn.com/iamalpha/.resource/jssdk/dojo-0.2.2/dojo.js
You can import this one script file into your HTML page and begin using Dojo immediately without having to download or install anything. This URL is the standard place for AOL apps to load Dojo. It puts the dojo.js file closer to the end user than serving it from your own Web server. Also, the more applications that refer to this location, the better the chances of it already being in the browser cache.
NOTE: Currently, dynamic loading of Dojo features does not work from the CDN (due to a security issue with accessing script files from two different domains). This issue is known and is actively being worked on. If you need to use functionality that is not built in to the Ajax profile (such as drag and drop, or XML parsing), you'll need to refer to your own instance of Dojo until this is resolved.
If you don't want to use an existing edition, you can create a custom build of dojo.js that includes only the functionality you need.To create a custom build, you'll need to download the full Dojo source tree, create a new profile under dojo/buildscripts/profiles, and then run the ant build task. For more information, refer to "Building Dojo" in dojo/README.
A custom build can reduce the download size of your application, but you should balance this benefit against using a shared Dojo instance from a well-known URL such as the AOL CDN. A shared instance could require your app to download some functionality that it doesn't need or use, and it increases the chances of the script file already being in use in the browser cache.
The code inside dojo.js is compressed using Dojo's script Compressor. This removes most of the white space and makes the script hard to read. But if you want to read the Dojo code, there is an uncompressed version at dojo/dojo.js.uncompressed.js. Of course, you should always import the compressed version into your HTML page. You can, and should, use Dojo's Compressor on your own application code for release builds. You can combine multiple script files into one file, and strip comments and white space. This will make for a significantly smaller and faster download (fewer bytes and fewer HTTP requests).
For more information on the Compressor, refer to Dojo Compressor Overview.
REMINDER: Make sure that all your code resources (script, HTML, and CSS) are served gzipped with the GNU compression program. Also, you should gzip all dynamic server responses using mod_gzip, or a similar Apache Web server external extension module.
While a certain subset of Dojo functionality is baked into each profile build, all of Dojo's capabilities are reachable using dojo.require statements (or by including source files manually). Modules of Dojo functionality are defined by the package system. It associates one or more physical JavaScript files with a logical target.
There are usually several targets for a given package, allowing you to include only specific targets, or to include the entire module (using the dot star notation). The Dojo package system looks a lot like Java's. For instance, if you've downloaded the I/O Edition, but want to start using the event system, you can use the dynamic loading system to pull it in at runtime:
dojo.require("dojo.event.*");
Or you can load a specific target:
dojo.require("dojo.event.browser");
Since dynamic loading is synchronous, a call to dojo.require will block until all the required code has been loaded. This means you can start using dynamically imported features right after the require statement, without having to wait for a callback. And, any dependencies that a required package might have are resolved automatically by Dojo. For example, Dojo will make sure that any code that the required package needs is also dynamically loaded.
The package system is driven by a manifest file called __package__.js that lives within the dojo/src directories. You can check this file to see what targets are defined for a given package.
Importing library features dynamically will be slightly slower than if the feature was included within the dojo.js file, but unless you are including many large packages, the difference should be minimal. If you are importing many packages dynamically you should consider creating a custom build of Dojo.
For more information on Dojo packages, refer to Getting Started With Packaging.
Ok, here we go.
First, you should import the dojo.js file into your HTML page. Dojo can be configured by creating a global object named djConfig before the Dojo library is included.
Dojo has built-in support for a minimal debugging console. You can specify a DIV to use for output using djConfig and then style the DIV to put it where you want (for example, using position and overflow).
Set djConfig.isDebug to true so that Dojo will log any internal error message to the console. You can also output your own debugging messages through dojo.debug().
NOTE: Dojo has a rich logging feature in dojo.log. By default, this API won't display log messages to the console. It seems most folks are simply using dojo.debug() instead.
<div id=dojoDebugOutput></div>
<script type="text/javascript">
var djConfig = {
isDebug: true,
debugContainerId : "dojoDebugOutput",
};
</script>
<script type="text/javascript" src="http://o.aolcdn.com/iamalpha/.resource/jssdk/dojo-0.2.2/dojo.js"></script>
<script type="text/javascript">
var onDojoLoad = function() {
dojo.debug("Ready to run my script...");
// do your thing...
}
dojo.addOnLoad(onDojoLoad);
</script>
This section is a tour of the Dojo packages, showing what is available and pointing out some of the most useful and interesting features.
dojo.require – ensures that the Dojo module has been loaded.dojo.byId – shorthand for document.getElementById.dojo.debug – outputs a string to the debug console.dojo.addOnLoad – callback when Dojo has finished loading.dojo.debugShallow – writes all properties of an object to the debug console.dojo.inherits – provides "classic" inheritence for JavaScript classes (see dojo.lang.extend, below).dojo.render.html.ie – describes the browser capabilities of the current hosting environment.dojo.render.os.win – describes the operating system capabilities of the current hosting environment.dojo.lang.hitch – runs a function in a given context; can be used to preserve context:
hitch( foo, "bar" ); // runs foo.bar() in the scope of foo
hitch(foo, myFunction); // runs myFunction in the scope of foo
When working with member functions, it's very useful to be able to associate a callback function with the scope of the member function, for example:
MyClass.prototype.myFunction = function() {
setInterval(function() { // 'this' pointer not correct }, 1000);
setInterval(dojo.lang.hitch(this, function() {
// 'this' pointer works
}, 1000);
}
dojo.lang.setTimeout – improves on window.setTimeout. Can pass arguments to the timer function, and set its context (this pointer):
setTimeout (Object context, function func, number delay[, arg1[, ...]]);
setTimeout (function func, number delay[, arg1[, ...]]);
dojo.lang.extend – extends a class by copying properties of a provided object to the class prototype. A compact syntax for adding methods and values to a class.
Original syntax:
foo = function() {};
foo.prototype.name = "foo";
foo.prototype.bar = function() { alert("bar"); };
With dojo.lang.extend:
dojo.lang.extend(foo, { name: "foo", bar: function() { alert("bar"); } };
Here's an example of subclassing an existing Dojo class. I create a new class called SliderDragSource, inherit functionality from dojo.dnd.HtmlDragSource, and then override the onDragStart method:
SliderDragSource = function(node, type, slider) {
this.slider = slider;
}
dojo.inherits(SliderDragSource, dojo.dnd.HtmlDragSource);
dojo.lang.extend( SliderDragSource, { onDragStart: function() {} } );
dojo.io covers many different transport layers (such as XMLHTTP and iframes) and provides a uniform and simple interface through the dojo.io.bind method. Here's a sample server call over XMLHTTP that provides JSON data in response:
var bindArgs = {
url: "http://path/to/myServerAPI.js",
mimetype: "text/json",
transport: "XMLHTTPTransport",
error: function(type, error, httpObj) {
// handle error
},
load: function(type, data, httpObj) {
// data object has JSON response
}
};
dojo.io.bind(bindArgs);
This code will call the server API at URL using the XMLHTTP transport. On success, Dojo calls the load handler back, and passes the JSON server response, resurrected as a JavaScript object. On error, Dojo calls the error handler. The httpObj is passed back so that HTTP error codes can be retrieved through httpObj.status.
For more information on the dojo.io.bind API, refer to this Dojo article.
dojo.event is a very powerful package. It gives you an easy way to add DOM event handlers, but also provides the ability to treat *any* function call as an event, and to create a loosely coupled publish and subscribe mechanism.
For DOM events, dojo.event.connect provides a uniform interface to setting events to the browser's event object. It resolves underlying bugs on different browsers and simplifies common tasks such as:
evt.preventDefault and evt.stopPropagation work correctly across browsers.Here's the basic syntax:
var handlerNode = dojo.byId("handler");
function handleOnClick(evt){
// evt parameter contains 'fixed' cross-browser event object
}
dojo.event.connect(handlerNode, "onclick", "handleOnClick");
Or, using an anonymous function:
var handlerNode = dojo.byId("handler");
dojo.event.connect(handlerNode, "onclick", function(evt){
// ...
});
DOM event handlers can be hard to write elegantly when they are within instance methods, since, when the callback occurs, the 'this' pointer refers to the DOM element that generated the event, not the context of the instance method. For example:
MyClass.prototype.addClickHandler = function() {
this.myMethod(); // 'this' pointer points to instance of MyClass
dojo.byId("button").onclick = function(evt) {
// 'this' pointer refers to "button" node, not MyClass instance
}
}
dojo.event.connect provides an elegant solution:
MyClass.prototype.addClickHandler = function() {
this.myMethod(); // 'this' pointer points to instance of MyClass
dojo.event.connect(dojo.byId("button"), "onclick", this, function(evt) {
this.myOtherMethod(); // works now
});
}
The same function can also be used to add a callback function whenever another function gets called. The last line of the example below will call obj.bar whenever obj.foo is called.
var obj = {
foo: function(){
alert("foo");
},
bar: function(){
alert("bar");
}
};
dojo.event.connect(obj, "foo", obj, "bar");
Finally, the event package contains a publish and subscribe feature using named "topics." This allows anonymous communication between components such as between a model and view in an MVC architecture. To connect the functions foo and bar above using topics, we would write this:
// set up our publisher
dojo.event.topic.registerPublisher("/example", obj, "foo");
// and at some point later, register our listener
dojo.event.topic.subscribe("/example", obj, "bar");
For more information about the DOM event handler and how to use dojo.event in your JavaScript, refer to this Dojo article.
dojo.dom – provides utility methods to manipulate DOM nodes, over and above what is provided by the browser DOMAPI:
dojo.dom.moveChildren – moves a set of nodes from one parent to another.dojo.dom.removeChildren – remove all child nodes from a node.dojo.dom.isDescendantOf – identifies if one node a descendent of another.dojo.dom.getFirstAncestorsByTag – looks for an ancestor node of tag type (for example. "img").dojo.dom.insertBefore – inserts new DOM child node.dojo.dom.insertAtPosition – inserts new DOM child node.dojo.style provides numerous functions for working with a node's style object, fixing many underlying bugs on various browsers. All code that performs pixel-wise measurement and manipulation of DOM nodes should prefer these clearly named cover methods over the browser's built-in functions:dojo.style.getInnerWidthdojo.style.getAbsolutePositiondojo.style.setOpacitydojo.html – provides utility functions for the browser environment:
There are many other useful modules within Dojo that we have not covered, such as:
dojo.stringdojo.animationdojo.fxdojo.xmlIt's critical that a JavaScript library be a very small download. We still have a large percentage of users on dialup, where 4 kB equals one second of user wait time. Dojo's JavaScript Compressor and custom build system are both a testament to needing to send as few bytes as possible to the browser.
Here are the code sizes for various build targets in the 0.2.2 version of Dojo (all numbers refer to gzipped code size).
dojo.lang).The Ajax build is representative of the functionality required for a moderately complex DHTML application. 35 kB is a substantial download, but the functionality provided will substantially reduce the amount of application-specific code (and, of course, development time).
The license for Dojo is designed to be straightforward and to allow the library to be widely used with no General Public License (GPL) type stickiness.
For more information on Dojo's software licensing history and policy, refer to this Dojo article.
Here are the development tools that we recommend:
Here are things you can do to learn more about Dojo:
dojo-interest mailing list.dojo/tests directory of the dojo source tree.dojo.js.uncompressed.js. NOTE: The grep command searches one or more input files for lines containing a match to a specified pattern.Here's a handy-dandy checklist to follow when creating modules that should help you get from beginning to end. If it helps, great. If not, please let us know on the message board.
index.htmlIf you run into trouble with any of those steps, come tell us.
What makes a module valid? Since we've been working on a validator for modules, we realized that we hadn't posted a human-readable list of what makes a module valid. There's the microformat profile, but it's not the easiest thing in the world to read or to figure out what's a child of what, and so forth. The module skeleton gives you a start. When you've completed your code, run through this 10-point list of requirements for a "valid" module:
allow-multiple to "true," neither the module's edit nor view should contain elements with ids.