Site design using Prototype

The internet is full of tutorials explaining those little tricks about AJAX, how to handle XHRequests and all that low level stuff, but nobody tells you how to design the entire application, nobody gives you the overview on how all these things should work together. What good is knowing all those fragments if the developer is unable to put them together to a real use? We have libraries to abstract from the Browser dependant things like actually doing XMLHTTPRequests, and we should concentrate on higher level design to give our clients (or visitors) good and usefull applications.

General web site design have evolved over the years so knowing design decisions that modern designers make can be important web page design knowledge; when creating a site it would probably be a mistake to let web design become anything other thanhigh priority.

This is why in this tutorial I’ll try to give you an idea on how the actual design works. I’ll be heavily relying on my last tutorial for the layout of the final application, but we’ll go much more into detail about the decisions we take. Some parts may be applicable to non-Web-2.0 Web design and some even come in handy when designing completely non-web related applications

User Requirements

This step is as important as the coding itself, if I’d start right with the code without first clarifying where I want to go, I’d be wasting time! So there are three important questions that I have to answer:

  1. What? What do I (or my boss) want the application to be, what functionality is a must, what is an optional, what are “nice to have” features?
  2. Why? Does what I have found that I want to implement fit into the context or would it fit better if I’d change it a bit?
  3. When? Do I have to implement everything right now, or may I add some new features one after another, possibly by releasing it before everything is implemented? But much more important: In what order will I have to implement the functionality?

There is not really a point that is more important than the others, take your time documenting and analysing the problems that you expect and fix some priorities, it will greatly speed up the implementation later.

HP0-505 exam provides perfect knowledge about all kinds of HP technology fundamentals. HP0-678 exam has another name to be known as Implementing HP Enterprise Virtual Array Solutions. NS0-111, latest Network Appliance Storage Associate Exam certification is very advantageous for the data and networking administrators to get more knowledge and expertise. RCDD, Registered Communications Distribution Designer) certification is one of the standard certification exams. The main objective of S10-200 exams is to develop technical skills and knowledge how to manage the storage infrastructure effectively. CISA is also known as Certified Information Systems Auditor whose value and standard is well-acknowledged globally.

What & Why

We plan to make a community site so the obvious things for a community is that it has to be attractive, easy to use and most important “feel alive”, nobody really wants to be part of a dead community where some casual visitor posts a new message to the forum every few days. To archieve the live feeling we use some little tricks like the regular updates of the list of users that are currently online. Remember that a community is not made up by two or three moderators posting some news from time to time, and filling the Forum with Junk messages nobody cares about.

When

It is important to understand that we’re not delivering a half finished product, it is fully functional, we plan to add more features in a second (and maybe third) step
  1. Release
    1. Main engine: the basic framework which will allow us to load and execute single modules at a later time.
    2. Display some static content.
    3. News System
    4. User Registration and simple profiles.
  2. Release
    1. Advanced profiles
    2. Private messaging
    3. more to come…

This list is off course fairly superficial and does not cover the details of the implementation, it is an abstract planning step, that helps us get a general overview

Under the hood

As mentioned before we don’t want to reinvent the wheel for the millionth time again so we’ll use some libraries:

  1. Prototype: probably the most flexible and intuitive AJAX Library on the internet, really lightweight and easy to use.
  2. Behaviour: helps us to keep our pages downgradable for browser without JavaScript support (important also for indexing by search engine).
  3. script.aculo.us: what would a Web-2.0 application be without nice effects?

This is what we’ll use for the client side. For the server side we’ll use PHP which is nice enough for our purpose, and is a reasonable tradeoff between control, speed and abstractness. For client-server communication we’ll use JSON which allows us to directly encapsulate the data into a JavaScript object, thus making the interpretation of the data much easier, in this tutorial we’ll use the JSON-PHP Library.

The Behaviour Library allows us to bind functionality right to the DOM-Elements without having to add onclick, onload, on[whatever] handlers right in the HTML, it allows us to define some CSS Classes, associate them with certain actions and then the library will take care of the rest for us, fully automagically.

Another main problem with AJAX pages are the “one-page-application” (applications that run entirely on one page) that have some problems with Bookmarking and the famous back button, I decided that the community page will be implemented as a one page application because it makes the whole process much easier, everything runs in one context and we don’t have to care aabout the rest. But we also want to make our pages bookmarkable so we use the hash ending of the URL to identify which page we are currently on.

Basic setup

Ok, so it’s time for us to gather all our libraries and put them into our Project Folder. Our project directory structure looks like this:

The Javascript folder will contain our three libraries (prototype, behaviour and script.aculo.us), the lib directory will contain all our PHP libraries (just the JSON-PHP for now) and static will contain our non dynamic content, such as some welcome textes and help pages.

Implementation

Now we’ll finally getting to see the actual code, it is important that we go one step after another, starting from the basic page layout and then building the functionality on top of that, it will keep us motivated throughout the process to implement a little thing and then test it to see if it works as we’d expected. The base Page is just a Page with the basic content, possibly a welcome message that tells where the user landed and what this is all about. We’ll use a pretty standard Layout for our application, the only requirement for AJAX is that we assign some ID’s to the main parts so that we can easily reference them later. Now we can insert links as we would normally have done, browsers without JavaScript will see a classic page that is completely served by our PHP-Scripts on the server side. For example the first thing we’ll do is display some static, non generated content, like the Welcome page normal links would look like this:

<a href="BasePage.php?Page/About">About</a>

The PHP page would take the query string Page/About and would then serve the new page. Now we want to add the AJAX Magic, and this is magic indeed: we’ll use behaviour to catch the request before it is being sent and use AJAX methods from there on to download only the updated regions of the page. First we have to add a Class to all our internal links:

<a href="BasePage.php?Page/About" class="link">About</a>
this allows us to distinguish the links we want to catch from the links to outside resources, for which AJAX is not applicable anyway.
 
var rules = { 
  'a.link' : function(el){
    // We don't want to have garbled links...
    if(el.onclick)
      return;
    Engine.log("Applying rule to " + el.toString());
    var target = el.href.substring(el.href.indexOf('?') + 1);
    el.href = "#"+target;
    el.onclick = function(event){
      var targ;
      if (!event) var event = window.event;
      if (event.target) targ = event.target;
      else if (event.srcElement) targ = event.srcElement;
      if (targ.nodeType == 3) // defeat Safari bug
        targ = targ.parentNode;
      Engine.log("Clicked on internal Link " + targ.toString());
      Engine.loadPage(targ.href);
      return false;
    }
  }
};
Behaviour.register(rules);

By doing this we have defined how the user interacts with the AJAX Engine, every element that fires up a certain event will have a class, and behaviour will then take care of the hooking up. Off course the loadPage example isn’t a really complex one but it’s the most common interaction on our page, and it shows the important parts.

See what’s going on

To test and see if all is going as we designed it we will have some way to get feedback from what we do on our Page. Using the alert() function is trivial, too trivial for us and it is really annoying too. Better would be some sort of textfield where our debugging messages are displayed without disturbing the site interaction (alert() opens a popup and focuses it…). We’ll use a popup that can be closed without interupting the application and it won’t try to get the focus for every message.

debug: true,
console: null,
log: function(message){
  if(!Engine.debug)
    return;
  if(!Engine.console){
    // Log window does not exist, try to open it.
    consoleWnd = window.open('','Debug Information','width=350,height=250,menubar=0,toolbar=1,status=0,scrollbars=1,resizable=1');             consoleWnd.document.writeln('<html><head><title>Console</title></head><body bgcolor=white></body></html>);
    Engine.console = consoleWnd.document.body;
  }
  Engine.console.innerHTML += (message + "<br />n");
}
 

This makes it really easy to print debugging information on the running application: just use Engine.log(“Your message here…”); and Engine will take care of the rest. This is exactly what we need: the debugging information when needed but not always. This way of displaying debugging information is really simple, for more advanced features give Log4JS a try.

Modules design

Most AJAX applications in use right now load everything on startup, images, javascript and content. While this is surely an easy way to do things it’s not the best, because it takes a long time (sometimes too long) for the application to be ready, and much of the stuff that is loaded is never used, or used pretty late during execution. For exactly this reason we’ll divide the application into modules. Modules are independent parts of the application, like in our case these would be:

  • Page: a simple module which will be implemented in this part of the tutorial. It will load an HTML file from the server and display it in the content area.
  • Gallery: a gallery made of user pictures, the goal would also be to use Flickr as a backend for the pictures (e.g. take a group pool and display it, display pictures of a user, display pictures with a certain tag, …)
  • News: simply an extension to the Page module this will allow administrators and moderators to post news. News are passed to the Javascript engine using XML. Its main purpose is to demonstrate how to use XML-Files.
  • Messages: A module for sending and receiving private messages.
  • Forum: the most complex module we’ll see during this tutorial.

First of all we’ll see how to load modules and register them into a module handler (the Engine) and then we’ll move on to actually design the modules.

Module handling

Basically the Engine does specify an API that the modules then may use to access the various parts of the Page, thus we create an abstraction layer between the actual interface and the application. Besides providing an easy to use interface to the Page, the Engine also takes care of loading and managing modules. First of all we’ll have know which modules have already been loaded and if they are not yet loaded we’ll have to load them upon request:

 
  modules: array(),
   registerModule: function(moduleName){
     ...
   },
   loadModule: function(moduleName, callback){
     ...
   }
notice that we may provide a callback function so that will be executed as soon as the module is loaded. This is because we cannot use the following:
 
loadModule("myModule");
  modules["myModule"].execute();

because loadModule contains an asynchronous call which will immediately return. If we’re unlucky (and most likely we are) the second line is executed before the module has been downloaded and registered, thus generating some really nasty errors. The callback allows us to specify some actions that are to be executed when the module has been definitely loaded.

Modules can range from really easy ones, as the Page-Module is, to really complex things such as message boards, news system, live activity logs, to fully fledged Instant messaging application, there is absolutely no limit to the complexity. The only thing that we require is that the modules adhere to a certain format:

 
var moduleName = {
   init: function(){
     // Initialize the module
     // Register the module Engine:
     Engine.registerModule(moduleName);
   },
   // ...
};
moduleName.init();

The last line is used to initialize the module. Since the module code will be eval()’d it must take care of registering itself to the module handler, the Engine.

The Page Module

The Page module is our first module. On execute it will get the static page via XHRequest from the Web server and display it in the content area. Being the simplest module it consists only of one function and one anonymous function, which will take care of loading the content into the content area. Don’t forget to re-apply the Bahaviours to the loaded code otherwise you might get some unexpected results.

So here it goes, without further ado the code of the Page-Module, by now you should be able to understand what it does.

 
var Page = {
    init: function(){
        Engine.log("Page module loaded.");
        Engine.modules['Page'] = Page;
    },
    execute: function(url){
        Engine.setStatus("Loading content...");
        var myAjax = new Ajax.Request("static" + url + ".html",{
            method: 'get', 
//            parameters: "?nocache=" + new Date(), 
            onFailure: function(){
                Engine.showError("Could not load content.");
                Engine.log("Could not load page " + url);
            },
            onSuccess: function(req){
                Engine.log("Loaded page " + url);
                Engine.setContent(req.responseText);
                Engine.hideStatus();
            }
        });
    }
};
Page.init();

The rest of the Engine

The engine still isn’t done by now, some functions from above are still missing but it is easy to guess what they do, we simply need some helper functions such as setContent, setStatus, showError and alike, so here goes the rest of the code:

 
      setStatus: function(message){
        if($('status') == null){
            var body = document.getElementsByTagName("body")[0];
            var div = document.createElement("div");
            div.id = 'status';
            body.appendChild(div);
        }
        var node = $('status');
        node.innerHTML = message;
        new Effect.Appear(node);
    },
 
    hideStatus: function(){
        new Effect.Fade($('status'));
    },
 
    showError: function(message){
        Engine.setStatus(message);
        Engine.setTimeout("Engine.hideStatus();",15000);
    },
 
    setContent: function(content){
        $('content').innerHTML = content;
    },

What’s next?

In the next part of this tutorial we will finally add some really new code that hasn’t yet been covered by my first tutorial, namely we will implement a signup form and some simple user management. For now take a look at the working copy of the application here, or download a snapshot of the code here.

Other resources

Tools

  • FireFox Venkman: the javascript debugger of FireFox (does not work with 1.5.0.1, but the guys over at GetAhead got a fix).
  • FireBug Extension: FireBug is a new tool that aids with debugging Javascript, DHTML, and Ajax. It is like a combination of the Javascript Console, DOM Inspector, and a command line Javascript interpreter.
  • Eclipse AJAX Toolkit Framework:AJAX Toolkit Framework (ATF) provides extensible tools for building IDEs for the many different AJAX (asynchronous JavaScript and XML) run-time environments (such as Dojo, Zimbra, etc.). This technology also contains features for developing, debugging, and testing AJAX applications. The framework provides enhanced JavaScript editing features such as edit-time syntax checking; an embedded Mozilla Web browser; an embedded DOM browser; and an embedded JavaScript debugger.

Update: 27.03.2006

Some people, with popup blockers have been complaining that the code isn’t working. This is because the popup blocker, blocks the debugging window from being opened. To get it working in the downloaded source, just turn Debugging in the engine off (by setting debug: false) or disable the popup blocker. Sorry for that, in the next part I’ll explain a better way to do it :-)

  • Share/Bookmark

This website uses IntenseDebate comments, but they are not currently loaded because either your browser doesn't support JavaScript, or they didn't load fast enough.

40 Responses to “Site design using Prototype”

  1. Kdevi  on March 26th, 2006

    Here you go…. Snyke strikes again… Its must read tutorial for Prototype lovers….

    Keep it up…

    Reply

  2. SurveyValley.com  on March 26th, 2006

    I love you brother. Now, I got to know what tools to use for my new web portal. Thanks. man. Keep your good work.

    Reply

  3. Cole Mickens  on March 26th, 2006

    “When tears of joy stream down your face” – Coldplay…

    I want you to know… that I love you… in a completly plutonic way.

    I have been DYING trying to figure out how to make my webpages work with ajax and nonajax themes and with js and nonjs users… and this is the solution… capturing the event and deferring it to an ajax call… bloody brilliant.

    You would think that would occur to me after writing a JS script to circumvent Firefox’s right-click override…

    THANK YOU SO MUCH!!! +DIGG!!!

    Reply

  4. RickPub  on March 26th, 2006

    This is a fantastic resource. Thanks a lot!!

    ——————–
    http://www.wirah.com

    Reply

  5. Peter Schaefer  on March 26th, 2006

    Whoa, I didn’t even know you could write javascript like this, with object constructors and anonymous functions, thanks, this is like a parallel universe

    Reply

  6. wow  on March 26th, 2006

    1. Fancy quotes confuses code
    2. When images off, can’t read text

    Reply

  7. Life @ 15 Frames / Second » Blog Archive » Fading Roses & Raging Viruses » Site design using Prototype  on March 26th, 2006

    [...] Fading Roses & Raging Viruses » Site design using Prototype [...]

    Reply

  8. Myhedspace Blog » Blog Archive » Site design using Prototype  on March 26th, 2006

    [...] Site design using Prototype [...]

    Reply

  9. AlbanyWiFi.com » Blog Archive » Site design using Prototype  on March 27th, 2006

    [...] snyke.net/blog/2006/03/25/site-design-using-prototype/ [...]

    Reply

  10. James  on March 27th, 2006

    Anyone else worry about the 160kb of js that has to be loaded per page load?

    Reply

  11. just in ram » links for 2006-03-27  on March 27th, 2006

    [...] Fading Roses & Raging Viruses » Site design using Prototype (tags: ajax) [...]

    Reply

  12. Bloggitation » AJAX Site Design using Prototype  on March 27th, 2006

    [...] read more | digg story [...]

    Reply

  13. CRiSPyToWN -=- Blog of useless info»Blog Archive » AJAX Site Design using Prototype  on March 27th, 2006

    [...] read more | digg story • • • [...]

    Reply

  14. Matt  on March 27th, 2006

    In response to comment #10.. I’m more worried that you’re worried about 160k. I mean on a dial-up modem, doesn’t that take like.. 30 seconds to download? Assuming you’re downloading at an average speed of 6k per second it actually requires less than 30 seconds.

    Reply

  15. se.nsuo.us  on March 27th, 2006

    Consider http://encytemedia.com/event-selectors/ instead of behaviour

    Reply

  16. The.RSS.Reporter  on March 27th, 2006

    =?utf-8?B?ZGVsLmljaW8udXMvcG9wdWxhcg==?=…

    アメリカの大学生より日本の高校生の方が実はかなり衝撃的:できる!CSSを使いこなす
    http://blog.y-iweb.com/archives/000295.htmlDate: 3/26/2006 6:27 PM  

    Amanda: The Open Sour…

    Reply

  17. Mike  on March 27th, 2006

    Excellent. Keep up the good work.

    Reply

  18. Daniel  on March 27th, 2006

    Your tutorial is really very nice, but I have run into a problem: when you download the code for stage 2 and unpack it, it will run perfectly in Firefox but not in IE (6.0.2800.1106, Error message: Invalid Argument, line 22, char 4; it looks like the engine doesn’t initialize correctly). Stage 1 works, as do the demos if viewed on your webpage; has there been a change from the code online to the one that’s downloadable? Sorry if this has an obvious solution, but I’m a n00b at AJAX/javascript.

    Reply

  19. Michiel van der Blonk  on March 27th, 2006

    He, James,
    The browser will cache the js after the first time it’s loaded. So it’s not 160 KB per page, but 160KB total for the site. Still it’s good to optimize this, and one way is to compress all js files. The other is to dynamically load, or use prototype light (only 3KB) when you only need some effects and simple Ajax.

    Reply

  20. Information Technology » AJAX Site Design using Prototype  on March 27th, 2006

    [...] The internet is full of tutorials explaining those little tricks about AJAX, all that low level stuff, but nobody gives you the overview on how all these things should work together. We have libraries to abstract from the Browser dependant things, and we should concentrate on higher level design for good and usefull applications. read more | digg story Get small business ideas and earn extra income for your online business! Bookmark on del.icio.us [...]

    Reply

  21. Keir  on March 27th, 2006

    Actually, I think you’ll find (at least in the UK) that the average download speed on a 56k modem is 4-5 (32 Seconds > 40 Seconds).
    And if thats per page, then I don’t see many people sitting around waiting for that.

    Reply

  22. Daniel  on March 27th, 2006

    Just a quick update: I think it has to do with the “debug”-flag in Engine.js (the only thing different in the online version and the snapshot) – when it is set to true IE barfs, when set to false it works without a hitch. Just FYI, and thanks again for a great resource!

    Reply

  23. Snyke  on March 27th, 2006

    Oh yeah sorry for that, the debugging code complains about not being able to open a popup window. Next time I’ll use a different Window Type, I think Prototype offers some sort of Div-Window if I’m not mistaken.

    For now just turn debugging off, or diable your popup blocker, that should work ^^

    Reply

  24. links for 2006-03-28 at Brandon’s Blog  on March 28th, 2006

    [...] Fading Roses & Raging Viruses » Site design using Prototype Designings sites with Prototype (tags: ajax design prototype javascript) No Tags [...]

    Reply

  25. Cody  on March 28th, 2006

    Not bad — but it doesn’t take full advantage of prototype.

    I refactored it a bit — ignore the core code — to utilize the framework better as well as lossening the coupling in module/engine registration.

    http://espn.go.com/js/module/Page.js

    http://espn.go.com/js/com/espn/widget/Engine.js

    Reply

  26. Snyke  on March 28th, 2006

    Hey Cody,

    great work, absolutely got to give it a try.

    Reply

  27. Ted the Penguin  on March 29th, 2006

    [...] Tomorrow I’ll post on something else interesting in the world.  Maybe web design fo-pas.  Or 250,000 year-old heads. [...]

    Reply

  28. Tim  on April 1st, 2006

    Good job Snyke, I really like this approach.

    One thing does bother me, search engines do have a problem with indexing the page.
    For example: when you go to index.php, it should redirect to index.php#Page/welcome (with use of js). That’s where it breaks down.

    Is there a solution for this? Maybe you can add this in part #3 :)

    Reply

  29. Stefan Bartmann » Blog Archive » Prototype einsetzen  on May 3rd, 2006

    [...] Denn natürlich hilft auch hier das Netz weiter: Eine erste Anlaufstelle ist das Wiki von script.aculo.us, wo sich einige Infos zur Bibliothek finden. Sergio Pereira beschreibt in seinen Developer Notes for prototype.js die zur Verfügung stehenden Eigenschaften und Methoden kurz und knackig. Das Tutorial “Site design using Prototype” beschreibt darüber hinaus auch ein sinnvolles Vorgehen bei der Entwicklung von Webseiten mit Prototype. Einen Quick Guide to Prototype hat Ryan Campbell verfasst. Und wer den ganz schnellen Überblick (z.B. als Desktop-Hintergrund) benötigt, dem sei Prototype Dissected ans Herz gelegt. [...]

    Reply

  30. AJAX Site Design using Prototype - The Digg Effect - Search for Diggs or get Dugg  on June 8th, 2006

    [...] The internet is full of tutorials explaining those little tricks about AJAX, all that low level stuff, but nobody gives you the overview on how all these things should work together. We have libraries to abstract from the Browser dependant things, and we should concentrate on higher level design for good and usefull applications.read more | digg story [...]

    Reply

  31. COLD CASE » Blog Archive » AJAX Site Design using Prototype  on June 14th, 2006

    [...] read more | digg story Explore posts in the same categories: coldcase [...]

    Reply

  32. D.J. de Groot  on June 29th, 2006

    great resource, well done snyke.

    love the part about the none- alert error messages. i hate them too.

    check out: http://www.ajaxtalk.com

    ciao dj

    Reply

  33. senthil  on July 26th, 2006

    how to create php file using with Ajax prototype
    Give some example.
    i had read so many pages and examples,but i cant understand

    Reply

  34. Programming » AJAX Site Design using Prototype  on August 23rd, 2006

    [...] The internet is full of tutorials explaining those little tricks about AJAX, all that low level stuff, but nobody gives you the overview on how all these things should work together. We have libraries to abstract from the Browser dependant things, and we should concentrate on higher level design for good and usefull applications.read more | digg story [...]

    Reply

  35. Scot  on October 16th, 2006

    How would you submit form data from one of the generated pages say from #Page/Register.. using will go to the page put not pass any post variables fromt he form.

    Reply

  36. Snyke  on October 17th, 2006

    I would consider that a variable passed through a Form is not worth saving the state.

    Reply

  37. Fading Roses & Raging Viruses » Bookmarkability  on October 17th, 2006

    [...] Bookmarkability [::[ Tue 17 Oct 2006 ]::[ General ]::[ ]::] Back in february when I wrote my first two tutorials on how to build an Ajax Portal, I explained how to make the pages bookmarkable. Bookmarks in Ajax Applications are very different from bookmarks in Web 1.0. In classic Web applications bookmarks pointed to a Resource (always remember that URI stands for Uniform Resource Identifier) and that was really why bookmarks were (and still are) so usefull, you could save the URL, come back later and find the same content, send it to a friend and he would see the same thing as you did, this sharing is really popular today (see Digg.com and del.icio.us). Bookmarks in Ajax Applications go far beyond this resource identification, they conceptually save the state of the Ajax Application itself, so that the state can be resumed at a later time. Going back to the example I used in the tutorials, the currently displayed content will certainly be important to save, so saving the currently displayed document in the URL is important, on the other hand the automagically updating list of online users is not context relevant and is not important to save to the URL. in fact the user list is volatile, updates independently from the page itself and would be a complete overkill to put into your URL.To sum it up:KISS: Keep it simple stupid, don’t overload the URLs with useless state information that isn’t necessary. Creating too many differing URLs is not good coding style, keep them short and easy to remember, even better if it is something that makes sense.The reason why I write this article is that just today I received a comment on my old tutorial asking how to save Form data in the URL. First of all: it wasn’t possible in Web 1.0, why should you want to in Web 2.0? Forms submitted with the POST-Method isn’t saved in classic URLs either, and we never complained about this. In Web 2.0 we now have to first answer a conceptual question: is the data sent from the Form worth saving to the URL? If I have a Registration Form I wouldn’t want a user to be able to save the Page after the Form, because it would mean that every time he opens this page it would issue a new Registration request. Where saving Form data may be relevant is a Search feature where the searchtherm is saved, but again this is no big deal because you can simply add a parameter to the URL.Although the difference may be subtle it is still important to keep this in mind, there are things that are nice to be bookmarkable, others aren’t, the question of what is best is to be answered by you when developing [...]

    Reply

  38. Ajax  on November 26th, 2006

    Ajax Tutorial Links…

    Ajax Tutorial Links…

    Reply

  39. wawanesa  on January 25th, 2007

    nice site keep it on ;)

    Reply

  40. SOCIAL NETWORK - Social Network to promote peace - SOCIAL NETWORK - Social Network to promote peace - MyPacis.com » Ajax for Web 2.0: everything you wanted to know about Ajax…  on October 1st, 2007

    [...] Sote Design using prototype – The Web 2.0 style website designing using Prototype Javascript framework [...]

    Reply


Leave a Reply