<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Carsonified &#187; AJAX</title>
	<atom:link href="http://carsonified.com/blog/category/dev/ajax/feed/" rel="self" type="application/rss+xml" />
	<link>http://carsonified.com</link>
	<description></description>
	<lastBuildDate>Tue, 16 Mar 2010 13:01:45 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>5 Ways to Optimize AJAX in Ruby on Rails</title>
		<link>http://carsonified.com/blog/carsonified/features/5-ways-to-optimize-ajax-in-ruby-on-rails/</link>
		<comments>http://carsonified.com/blog/carsonified/features/5-ways-to-optimize-ajax-in-ruby-on-rails/#comments</comments>
		<pubDate>Mon, 02 Jul 2007 08:42:15 +0000</pubDate>
		<dc:creator>Shanti Braford</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Features]]></category>
		<category><![CDATA[Ruby on Rails]]></category>

		<guid isPermaLink="false">http://www.thinkvitamin.com/features/ajax/5-ways-to-optimize-ajax-in-ruby-on-rails</guid>
		<description><![CDATA[By <strong>Shanti Braford</strong><br />Any sufficiently advanced technology is indistinguishable from magic.- Arthur C. Clarke
When Google first unveiled GMail, then GMaps, a firestorm of interest and activity was generated &#8212; not just in those Google applications, but the technologies that powered them.
These web programming techniques (now, of course, known collectively as AJAX) have become incredibly popular, especially among Web [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style=""><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fcarsonified%2Ffeatures%2F5-ways-to-optimize-ajax-in-ruby-on-rails%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fcarsonified%2Ffeatures%2F5-ways-to-optimize-ajax-in-ruby-on-rails%2F" height="61" width="51" /></a></div><p><strong>Any sufficiently advanced technology is indistinguishable from magic.<br/></strong>- Arthur C. Clarke</p>
<p>When Google first unveiled <a href="http://gmail.google.com/">GMail</a>, then <a href="http://maps.google.com/">GMaps</a>, a firestorm of interest and activity was generated &#8212; not just in those Google applications, but the technologies that powered them.</p>
<p>These web programming techniques (now, of course, known collectively as <span class="caps">AJAX</span>) have become incredibly popular, especially among Web 2.0 startups and their early adopters.</p>
<p>The developers of <a href="http://rubyonrails.org">Ruby on Rails</a> recognized early on that allowing RoR developers to easily <span class="caps">AJAX</span>-ify their webapps would be a great addition to the framework.</p>
<p><img src="/images/articles/features/optimize-ajax/rails_logo_remix.gif" width="110" height="140" alt="" title="Ruby on Rails" style="float:right; margin:30px" />With the addition of <a href="http://www.oreilly.com/catalog/rjsrails/">RJS Templates</a> to Rails core, the <span class="caps">AJAX</span> bar was lowered even further.</p>
<p>Simple <span class="caps">AJAX</span> requests like incrementing the number of  <a href="http://digg.com/">diggs</a> a story has received, or splicing a comment into a blog, are remarkably fast, not to mention user friendly.</p>
<p>What we&#8217;ll be addressing today, though, is optimizing ever more complex <span class="caps">AJAX</span> requests that might involve a multitude of <span class="caps">SQL</span> calls and JavaScript rendering techniques.</p>
<p>Not all of the techniques are strictly JavaScript/browser-oriented.  The list was actually derived from the steps that we took to optimize <a href="http://sproutit.com/mailroom">Mailroom</a>, the first app in the  <a href="http://www.sproutit.com">Sproutit</a> suite for small businesses.</p>
<p>Mailroom is built for use with small teams; it can be used to manage shared email accounts such as support@ and sales@.  It combines an ajax-rich front end with features like tagging, saved replies (for later reuse) and shared internal conversation notes.</p>
<p><img src="/images/articles/features/optimize-ajax/sproutit_logo.gif" width="150" height="50" alt="" title="" style="float:right; margin:30px" />When we first launched Mailroom, the service was reasonably fast.  As we grew and our server got busier, however, some customers started to complain about how long it took for some of our <span class="caps">AJAX</span> functions to complete.</p>
<p>The following optimization techniques, though not complete by any means, were just a few of the methods we used over the course of several weeks to bring Mailroom, literally, up to speed.</p>
<p>Following the performance boost, Mailroom&#8217;s response times for a Load Conversation call (via <span class="caps">AJAX</span>), went from 2-8 seconds to averaging less than half a second.  Once we added pre-caching (technique #5) on the client side via JavaScript, clicking on a message resulted in a near-instantaneous load; sometimes faster than Outlook or Apple Mail.</p>
<h3>Possible Sources of Latency</h3>
<p>When building out your <span class="caps">AJAX</span> application, it&#8217;s a good idea to keep in mind the different sources of latency that can slow down the user experience.  These include:</p>
<ol>
<li>The Database (both in memory and filesystem swap)</li>
<li>App Server Execution &#038; Rendering</li>
<li>The Webserver</li>
<li>The Network</li>
<li>The Browser (only in really complex <span class="caps">AJAX </span> apps)</li>
</ol>
<p>There are others, but we hope this broadly covers them all.  We&#8217;ll only be taking a look at a few of these sources, but it&#8217;s useful to keep them all in mind as you build out your app.</p>
<h3>5 Ways You Can Optimize Your Ruby on Rails/<span class="caps">AJAX </span>Application</h3>
<ol>
<li>Optimal Database Indexing</li>
<li>Eliminate Redundant <span class="caps">SQL </span>Queries</li>
<li>Fragment Caching</li>
<li>Response Text Compression/Minimization</li>
<li>Pre-rendering and Client-side JavaScript Caching</li>
</ol>
<h3>1. Optimal Database Indexing</h3>
<p>For many applications, database indexing will be the biggest performance booster of them all.</p>
<p>Sample output from a Rails log file:</p>
<pre><code>
  Completed in 8.29523 (0 reqs/sec) | Rendering: 2.68176 (32%) | DB: 5.38202 (64%) | 200 OK [http://railsapp.org/action]
</code></pre>
<p>This action would be an ideal candidate for database optimization.  If, however, you see something like <code>DB: 0.05 (5%)</code>, the action is already spending most of it&#8217;s time rendering and there&#8217;s probably not a lot you can do in the way of database indexing.</p>
<p>It used to be somewhat difficult to pinpoint the exact function call in your Rails app that was generating an <span class="caps">SQL</span> query. But with last year&#8217;s release of Nathaniel Talbot&#8217;s <a href="http://blog.talbott.ws/articles/2006/05/17/querytrace-my-first-official-rails-plugin">QueryTrace plugin</a> for Rails, this query back tracing process got a whole lot simpler.</p>
<p>After you install the QueryTrace plugin your Rails app (in the &#8216;development&#8217; environment) will give you output like this in your log/development.log:</p>
<pre><code>
 Conversation Load (0.001538)   SELECT * FROM conversations WHERE (conversations.id = 23453) LIMIT 1
    app/models/feed_observer.rb:14:in `after_create'
    app/controllers/conversation_controller.rb:162:in `send_to'
    script/server:3
</code></pre>
<p>As you can see, it&#8217;s now trivial to find the exact call that&#8217;s generating the <span class="caps">SQL</span> database hit.</p>
<p><strong>Adding Indexes via Migrations</strong></p>
<p>The following is a migration&#8217;s <em>self.up</em> method to both create an <em>articles</em> table and add a few indices:</p>
<pre><code>
def self.up     # brings db schema to the next version
  create_table :articles do |t|
    t.column :title,      :string
    t.column :author_id,  :integer
    t.column :body,       :text
    t.column :is_live,    :boolean, :default => false
  end
  add_index :articles, :author_id
  add_index :articles, [ :author_id, :is_live ], :name => 'author_live_idx'
end
</code></pre>
<p>This would create both a single-column index on the author_id, as well as a multi-column index on author_id and is_live.</p>
<p>When in doubt, use a tool like <a href="http://railsbench.rubyforge.org/">railsbench</a> or the <a href="http://www.softwareverify.com/rubyPerformanceValidator/index.html">Ruby Performance Validator</a> to see where the bottlenecks are in your Rails app.</p>
<h3>2. Eliminate Redundant <span class="caps">SQL </span>Queries</h3>
<p><em>Note: this one is more of a &#8220;gotcha&#8221; applicable to those of us who are new to Ruby on Rails.  (Since I&#8217;ve fallen victim to this mistake several times, I thought it was worth mentioning here.)</em></p>
<p>It&#8217;s always a good idea to keep a &#8216;tail -f log/development.log&#8217; open when you are developing your Rails apps. (Win32 users can get tail, etc. <a href="http://unxutils.sourceforge.net/">here</a> or use <a href="http://www.cygwin.com/">Cygwin</a> )</p>
<p>You might be surprised at what you find if you are not in the habit of checking your query logs.</p>
<p>Ever written a quick hack like this?</p>
<pre><code>
  class ApplicationController < ActionController::Base
    def current_member
      return Member.find(@session[:member])
    end
    helper_method :current_member
  end
</code></pre>
<p>Only to discover that you&#8217;re actually calling <em>current_member</em> more than once in a particular controller/view combination? (thus generating multiple <span class="caps">SQL</span> calls for the same unchanged ActiveRecord object)</p>
<p>Here&#8217;s how to cache the variable in a per-request instance variable so that the <i>Member.find(session[:member_id])</i> database query only gets called once per request:</p>
<pre><code>
  class ApplicationController < ActionController::Base
    protected
      def current_member

        @current_member ||= Member.find(session[:member_id])
      end

      helper_method :current_member
    end
  end
</code></pre>
<h3>3. Response Text Compression/Minimization</h3>
<p>Before attempting the below technique, the simplest way to reduce the number of bits sent across the wire is to enable gzip compression in your web server.</p>
<p>Some links on gzip compress: <a href="http://httpd.apache.org/docs/2.2/mod/mod_deflate.html">mod_deflate (apache 2.2)</a>, <a href="http://forum.lighttpd.net/topic/7729">thread on configâ€™ing lighty with gzip</a>, <a href="http://losingmyjob.wordpress.com/2007/05/04/apache-22-mongrel-cluster/">apache 2.2   mod_deflate tutorial</a>.</p>
<p>In the initial version of Mailroom, we were loading the <em>tag conversation</em> <span class="caps">HTML</span> module via <span class="caps">AJAX</span> for every load conversation request.<img src="/images/articles/features/optimize-ajax/mailroom_tags.png" width="399" height="169" alt="" title="" style="float:right; margin:30px" /></p>
<p>There was quite a bit of superfluous <span class="caps">HTML</span> that was being returned, when all we really needed was the specific tags that this particular conversation had been tagged with.</p>
<p>By the way, this kind of optimization is helpful when you have one master page load (where it&#8217;s OK to take 1-2 seconds longer), followed by many successive <span class="caps">AJAX</span> requests that you want to be rendered as quickly as possible.</p>
<p>We&#8217;re also targeting the size of the <span class="caps">AJAX</span> response text sent back by the server.  A 4k response text is of course more preferable to a 16k one.</p>
<p>Initially, this was the <span class="caps">HTML</span> snippet being returned for every load conversation <span class="caps">AJAX</span> call:</p>
<pre><code>
<div id="tag-editor" class="editor tags" >
<h1>Choose from the tags below:</h1>
<form method="post" onsubmit="ch.updateTags(460857, this); return false;">
<div id="tag-cloud">
         &#8230; lots of tags  &#8230;
    </div>

or enter some new tags:
<input id="tag-input" name="tags" size="30" type="text">

 &#8230;
    </form>
</div>

</code></pre>
<p>Instead of returning this whole chunk of <span class="caps">HTML</span> each call, we are going to load a static, hidden <span class="caps">HTML</span> div once on the initial page load, and return (via <span class="caps">AJAX</span>, each call) just the bare essential tag data needed to render this static <span class="caps">HTML</span> into a per-conversation tag editor.</p>
<p>First we create a simple Tags object in javascript (with a little help from <a href="http://www.sergiopereira.com/articles/prototype.js.html">Prototype</a>):</p>
<pre><code>
  Tags = Class.create();
  Tags.prototype =  {
    initialize: function(conversation_id, tag_str, tag_ids) {
      this.conversation_id  = conversation_id;
      this.tag_str          = tag_str;
      this.tag_ids          = tag_ids;
    }
  }
</code></pre>
<p>In your main page load, we render a <i>static</i> version of the tag editor template that stays in a hidden div.</p>
<p>The static <span class="caps">HTML</span> in a hidden div:</p>
<pre><code>
<div id="tag-editor-static" style="display:none;">
<p id="tag-list-static">
        <span id="tag-listing-static">Tags: #TAGS#</span>&nbsp;
      &#8230; a lot more static HTML.  this olds a static list of tags   checkboxes for this organization  &#8230;
</div>

</code></pre>
<p>When a conversation is loaded, the contents of the hidden div will be read in via JavaScript.  Regular expressions are performed against it on the fly to replace elements like <em>#TAGS#</em> with actual per-conversation data returned via the <span class="caps">AJAX</span> request.</p>
<p>Here we create a simple JavaScript function that pulls in the contents of the hidden div, performs regex replacements on it, and completes with a Prototype Insertion call of the newly rendered <span class="caps">HTML</span>.</p>
<pre><code>
  loadTagEditor: function(tags) {
    var tags_static = $('tag-editor-static').innerHTML;
    var regex = /-static/g;
    tags_static = tags_static.replace(regex, '');  # Strip out the "-static" portion of the ids

    var tags_regex = /#TAGS#/;
    tags_static = tags_static.replace(tags_regex, tags.tag_str);
    ...
    new Insertion.After('the-widget-before-tag-editor', tags_static);
  }
</code></pre>
<p>Now whenever a conversation is selected, we just need to make sure the &#8220;loadTagEditor(tags)&#8221; method is called.</p>
<p>There are a few gotchas you&#8217;ll want to watch out for with this technique, such as not ending up with duplicate ids in your <span class="caps">HTML</span>.</p>
<p>In the hidden, static divs, one way to avoid duplicate ids is to append all your id names with &#8221;-static&#8221; and then strip out the &#8221;-static&#8221; when you load in your hidden div <span class="caps">HTML</span>. (that&#8217;s what we do above with the Regular Expression)</p>
<p>Now, our original chunk of <span class="caps">HTML </span>(which can really add up if you have 20 &#8211; 50 tags) that was previously being returned back with each Load Conversation <span class="caps">AJAX</span> request&#8212;is now replaced with a few simple lines of JavaScript:</p>
<pre><code>
  tags = new Tags(460857, 'home Marketing Development', new Array(1251, 1252, 1460));
  loadTagEditor(tags);
</code></pre>
<p>This is the Rails <span class="caps">RJS </span>(inside show_conversation.rjs, perhaps) that would be used to generate the above JavaScript:</p>
<pre><code>
  page << "tags = new Tags(#{@conversation.id}, '#{@conversation_tags * ' '}', new
Array(#{@conversation_tag_ids.inspect_raw}));"
  page << "ch.loadTagEditor(tags);"
</pre>
<p></code></p>
<h3>4. Fragment Caching</h3>
<p>Here we&#8217;ll use the ever so Web 2.0 example here of the tag cloud.  These little beauties can be expensive to calculate in <span class="caps">SQL</span>.  Once you&#8217;re site has been <a href="http://techcrunch.com/">TechCrunched</a>, you&#8217;ll want to make sure that your tag cloud (especially miniature versions loaded on heavily-trafficked pages, or via <span class="caps">AJAX</span>) gets cached as a Rails HTML fragment.</p>
<pre><code>
  class TagController < ApplicationController

    def tag_cloud
      fragment = read_fragment("myrailsapp.com/tagcloud")
      if not fragment
        setup_tag_cloud
        fragment = render :template => &#8220;tag/tag_cloud&#8221;
        write_fragment(&#8221;myrailsapp.com/tagcloud&#8221;, fragment)
      else
        #logger.info &#8220;Fragment cache read: #{fragment}&#8221;
        render :text => fragment and return
      end
    end

    private
      def setup_tag_cloud
        # Expensive SQL queries here &#8230;
      end
  end
</code></pre>
<p><i>Uncomment the logger.info call to verify that your fragment really is being read from the cache!</i></p>
<p>Admittedly, fragment caching in Rails can make your code less maintainable.  It&#8217;s a technique that should really only be used for expensive-to-compute, fairly static portions of your site.</p>
<p>There are several helpful rails caching plugins on the <a href="http://wiki.rubyonrails.org/rails/pages/Plugins">Plugins page</a> on the RoR wiki.</p>
<p>One simple but effective plugin is the <a href="http://happygiraffe.net/blog/archives/2006/05/08/timedfilestore-plugin-0-1">Timed File Store</a> which allows you to set an expiration (e.g. 15 minutes) on your cached fragments.</p>
<h3>5. Client-side JavaScript Caching and Pre-rendering </h3>
<p>If you <em>really</em> want to wow your users, pre-cache commonly called <span class="caps">AJAX</span> components into hidden divs so that the only time necessary to load them is the time it takes their browser to execute (eval) the pre-rendered JavaScript.</p>
<p>In the following example, we&#8217;ll cache conversations into hidden divs so that whenever a user clicks on a conversation, it&#8217;ll load almost instantaneously.</p>
<p>The pre-caching functions will all access a single global JavaScript variable that holds an array.  That array will be populated on the first page load with the conversation IDs that should be cached.</p>
<p>Here&#8217;s the inline JavaScript (placed at the bottom of the main rendered <span class="caps">HTML</span> page):</p>
<pre><code>
  <script type="text/javascript">
  //
   // BTW - Watch out for this JavaScript gotcha:  When you instantiate an Array in JavaScript using
   //   a single integer paramater (e.g. new Array(37)), JavaScript assumes that you want to create an array of that size (e.g. 37 empty elements!).
   // Pad JS arrays with a leading zero to avoid this.
   // Reverse the array first so that we can simply pop off each element as we cache it (later on)
   var load_ids = new Array(0, <%= @widget_ids.reverse.inspect_raw %>);
   // Start the preLoader, with a delay of .5 seconds to give the main page time to load still.
     setTimeout(&#8221;preLoader()&#8221;, 500);
  //]]&gt;
  </script>
</pre>
<p></code></p>
<p>Here are the JavaScript functions that do the preloading/caching:</p>
<pre><code>
  // Uses JavaScript's "setTimeout" function to call the "loadFirst" method (once per id).
  //    This method handles the logic of delaying the first load (to give the parent HTML page
  //    time to load) and putting a delay between each AJAX load so as to not overwhelm
  //    the browser &#038; server.
  function preLoader() {
    var cnt = 0;
    var offset = 250;              # Milliseconds to delay the initial load
    var delay_per_load = 500;      # Milliseconds between each load
    ids.each(
      function (id) {
        setTimeout("loadFirst()", (offset   (cnt * delay_per_load)));
        if (id > 0) {
          cnt  = 1;
        }
      }
    );
  }
  // Pops off the first element in the 'load_ids' array and loads it
  function loadFirst() {
    var id = load_ids.pop();
    if (id > 0) {
      preloadConversation(id);
    }
  }
  // Preloads the conversation specified by creating a new AJAX request -- Note the use of Prototype's
  //   "onComplete" method to specify that "cacheConversation" should be called when the AJAX request
  //   has been completed and its data returned.
  preloadConversation = function(conversation_id) {
    new Ajax.Request('/mailroom/conversation/'   conversation_id   '?preload=true', {asynchronous:true, evalScripts:false, onComplete: cacheConversation});
  }
  cacheConversation = function(originalRequest) {
    // ... omitting some "clever" hacks used to get the conversation id ('conv_id' variable) ...
    var hidden_div_id = 'pre-'   conv_id;
    // Set the Inner HTML of a hidden div to the JavaScript passed back that was generated by Rails RJS templates:
    $(hidden_div_id).innerHTML = encode(originalRequest.responseText);
  }
</pre>
<p></code></p>
<p>The following is the JavaScript function used to either A) load the cached conversation, or B) if the cache is not present, load the conversation directly off the server:</p>
<pre><code>
  loadConversation: function(id) {
    if ($('pre-'   id).innerHTML != '') {
      var decoded = decode($('pre-'   id).innerHTML);
      eval(decoded);
    } else {
      // Load the conversation via a new Ajax call (eval'ing the result by setting "evalScripts:true",
      //    then caching it via the onComplete parameter).
      new Ajax.Request('/mailroom/conversation/'   id, {asynchronous:true, evalScripts:true, onComplete: cacheConversation});
    }
  }
</pre>
<p></code></p>
<p>Phew.  Well, there you have it.  JavaScript, long the domain of cut n&#8217; paste <span class="caps">HTML</span> jockeys, has proven in recent years to be a formidable challenger to Flash (and Applets, remember those), once hailed as the future of interactive web applications.  Of course, each has their place and own unique advantages and disadvantages.</p>
<p>While most applications wouldn&#8217;t need the kind of firepower described in this technique, hopefully you&#8217;ve learned a little bit about JavaScript, Rails and <span class="caps">RJS</span> along the way.</p>
<h3>Footnote</h3>
<p>The inspect_raw method used in technique #5 is a custom method we added to the Array class. <a href="http://onwebapps.com/projects/ajax-rails-footnotes/">Go here</a> to learn more about this method and how we implemented it.</p>
<p><script src="http://digg.com/tools/diggthis.js" type="text/javascript"></script></p>
<img src="http://carsonified.com/?ak_action=api_record_view&id=132&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://carsonified.com/blog/carsonified/features/5-ways-to-optimize-ajax-in-ruby-on-rails/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Book: &#039;Bulletproof Ajax&#039; by Jeremy Keith</title>
		<link>http://carsonified.com/blog/carsonified/reviews/bulletproof-ajax-by-jeremy-keith/</link>
		<comments>http://carsonified.com/blog/carsonified/reviews/bulletproof-ajax-by-jeremy-keith/#comments</comments>
		<pubDate>Tue, 24 Apr 2007 10:35:46 +0000</pubDate>
		<dc:creator>Gareth Rushgrove</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Reviews]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[Jeremy Keith]]></category>

		<guid isPermaLink="false">http://www.thinkvitamin.com/reviews/dev/bulletproof-ajax-by-jeremy-keith/</guid>
		<description><![CDATA[By <strong>Gareth Rushgrove</strong><br />Gareth Rushgrove reports on Jeremy Keith's latest book, Bulletproof Ajax, finding out who the book is aimed at and what they'll learn from it.]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style=""><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fcarsonified%2Freviews%2Fbulletproof-ajax-by-jeremy-keith%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fcarsonified%2Freviews%2Fbulletproof-ajax-by-jeremy-keith%2F" height="61" width="51" /></a></div><p>Ajax has been a hot topic for quite a while now, and there seems to be a new book with the eponymous Greek warrior in its title pretty much every week. Coming in at just 207 pages, <a href="http://adactio.com">Jeremy Keith&#8217;s</a> new book, <a href="http://bulletproofajax.com">Bulletproof Ajax</a>, is the latest to join the fray. With only 200 odd pages how does he plan to cover such a complex subject you may be asking? Well, here&#8217;s the secret &#8211; Ajax is really quite simple when you get down to it.</p>
<p>Jeremy&#8217;s previous book, <a href="http://domscripting.com">DOM Scripting</a>, stands as a fantastic introduction to JavaScript in general and DOM Scripting in particular and <em>Bulletproof Ajax</em> follows on from that title to some degree. Although you get a short introduction to the JavaScript language it&#8217;s just enough to understand the examples (I&#8217;d recommend you have at least a passing knowledge of JavaScript before reading this book). Also if you are coming to Ajax as a server side programmer then this book is probably not what you&#8217;re looking for, but worth reading at a later date. Given the client-server nature of Ajax there are server-side code examples (in PHP) but these are generally brief and serve only to support the examples rather than look at real world usage. Again, it would be useful to have at least a passing familiarity with a server side language and to be able to know if you already have a web server handy to experiment with.</p>
<p>The book features plenty of sample projects &#8211; for instance a simple address book is built up, showing off how to use XML, JSON and HTML as data sources and introducing the central XMLHTTPRequest object. There&#8217;s lots of focus, as you would expect, on making these examples bulletproof, in this case making sure they work even if JavaScript is unavailable using a method called Hijax.</p>
<p>To go from simple inline event handlers through to completely unobtrusive, object-based code and discussions of closures in JavaScript would be pretty good going for a book twice the size. It&#8217;s testament to the clear, no-nonsense and eminently readable writing style that this never bogs down the examples. Some people are bound to complain about the use of the proprietary innerHTML property and the minimal coverage of XML and JSON in the larger examples. This seems to be a facet of the scope of the book and a pragmatic approach to the problem rather than an unintentional oversight. If you&#8217;re looking for an A-Z of building an enterprise Ajax application then you&#8217;re probably looking for another book (but you should read this one first anyway!).</p>
<p>An entire chapter is dedicated to Accessibility and Ajax, a hugely important subject and one I&#8217;ve not seen mentioned anywhere else to date. Although the chapter lacks equivocal conclusions (mainly because no one seems to have formed any yet) it raises all the important issues for discussion and debate and provides a solid set of references for further reading. Throughout the book everything is anchored on the importance of user experience, rather than simply using technology for technology&#8217;s sake; this makes Bulletproof Ajax stand out from the crowd of more technology-focused tomes on the subject.</p>
<p>As a standards savvy developer if you want to get up to speed quickly with the hows and whys of modern Ajax but don&#8217;t have the time to wade through an awful lot of blog posts then <em>Bulletproof Ajax</em> is worthwhile reading. If you already know what you&#8217;re up to then it&#8217;s a perfect book to recommend to your unenlightened colleagues. A perfectly digestible read for one of those long train journeys!</p>
<p>Book Name: Bulletproof Ajax<br />
Publisher: New Riders<br />
Author: <a href="http://adactio.com">Jeremy Keith</a><br />
URL: <a href="http://bulletproofajax.com">http://bulletproofajax.com</a><br />
Price: $34.99 <acronym title="United States Dollars">USD</acronym> <a href="http://www.amazon.com/gp/product/0321472667?ie=UTF8&#038;tag=vitamin06-20&#038;linkCode=as2&#038;camp=1789&#038;creative=9325&#038;creativeASIN=0321472667">Buy <em>Bulletproof Ajax</em> at Amazon and save 34% off the cover price!</a><img src="http://www.assoc-amazon.com/e/ir?t=vitamin06-20&#038;l=as2&#038;o=1&#038;a=0321472667" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /><br />
Rating out of 5: 3.5</p>
<img src="http://carsonified.com/?ak_action=api_record_view&id=1743&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://carsonified.com/blog/carsonified/reviews/bulletproof-ajax-by-jeremy-keith/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>Create Cross Browser Vector Graphics</title>
		<link>http://carsonified.com/blog/carsonified/features/create-cross-browser-vector-graphics/</link>
		<comments>http://carsonified.com/blog/carsonified/features/create-cross-browser-vector-graphics/#comments</comments>
		<pubDate>Tue, 19 Dec 2006 08:00:12 +0000</pubDate>
		<dc:creator>Dylan Schiemann</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Features]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[dojo]]></category>

		<guid isPermaLink="false">http://www.thinkvitamin.com/features/design/create-cross-browser-vector-graphics</guid>
		<description><![CDATA[By <strong>Dylan Schiemann</strong><br />For years, web developers have asked the question, &#8220;How can I draw in the browser?&#8221;.  This simple question has anything but a straightforward answer. While the image tag is great, CSS is amazing, and Flash gets the job done, it still is not enough. What I&#8217;ve wanted for almost a decade is the ability [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style=""><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fcarsonified%2Ffeatures%2Fcreate-cross-browser-vector-graphics%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fcarsonified%2Ffeatures%2Fcreate-cross-browser-vector-graphics%2F" height="61" width="51" /></a></div><p>For years, web developers have asked the question, &#8220;How can I draw in the browser?&#8221;.  This simple question has anything but a straightforward answer. While the image tag is great, CSS is amazing, and Flash gets the job done, it still is not enough. What I&#8217;ve wanted for almost a decade is the ability to draw and modify non-rectangular, browser-native shapes in my web sites. And I want to do this without having to use a graphics editing tool and reloading images from the server. I simply want the ability to manipulate, draw, and connect events to arbitrary shapes and styles, to create rich, sophisticated web user interfaces.</p>
<p>Landscape changes to web applications have been drastic with the widespread adoption of the XMLHttpRequest and other AJAX and Comet techniques. In my opinion, asynchronous, low-latency data transit (AJAX and Comet) paired with vector graphics opens up a world of new opportunities for web application development. I&#8217;m obviously not alone in this desire for browser-based drawing solutions, given the wide variety of image techniques and CSS hacks that have evolved over time.</p>
<p>Despite a history of <abbr title="World Wide Web Consortium">W3C</abbr> effort towards the <abbr title="Scalable Vector Graphics">SVG</abbr> specification, there was little support among browser vendors until now. Apple, the WhatWG, Mozilla, and Opera have been working in parallel on Canvas, a simpler but less feature-rich procedural vector drawing <abbr title="Application Programming Interface">API</abbr>.</p>
<p>After years of frustration, we finally have enough support to create a simple, unified, cross-browser drawing API: dojo.gfx is the newest piece of the Dojo Toolkit and is an amazingly powerful yet simple way to accomplish these goals. Browser support currently includes Firefox 1.5 , Internet Explorer 6 , and Opera 9. Safari fans fear not &#8211; support is underway with the latest WebKit nightlies, and is expected to be functional with the next major release of Safari in 2007.</p>
<p>If you want to skip straight to the examples and source code, there is a collection of resources at the end of this article. Otherwise, sit back and enjoy the code!</p>
<h3>What Does dojo.gfx Do?</h3>
<p>In a nutshell, dojo.gfx lets you draw natively in the browser with an easy to follow JavaScript API. Let&#8217;s start by simply drawing a blue rectangle and a green circle:</p>
<p><img src="http://www.thinkvitamin.com/images/articles/features/dojogfx/dojoGfxShapes.gif" alt="Drawing of simple blue rectangle and green circle" height="217"> </p>
<pre><code>
var node = document.createElement("div");
document.body.appendChild(node);
var surfaceWidth = 120;
var surfaceHeight = 220;
var surface = dojo.gfx.createSurface(node,surfaceWidth, surfaceHeight);
var rect = { x: 100, y: 0, width: 100, height: 100 };
var circle = { cx: 150, cy: 160, r: 50 };
var group = surface.createGroup();
var blueRect = group.createRect(rect)
	.setFill([0, 0, 255, 0.5])
	.applyTransform(dojo.gfx.matrix.identity);
var greenCircle = group.createCircle(circle)
	.setFill([0, 255, 0, 1.0])
	.setStroke({color: "black", width: 4, cap: "butt", join: 4})
	.applyTransform(dojo.gfx.matrix.identity);
</code></pre>
<p>As you can see from this basic example, the first step is to create a gfx surface on which to add nodes that you will be drawing.  It&#8217;s then possible to add shapes to the surface, and apply styles and transformations in a chainable manner. They are applied in the order defined and provide a concise yet flexible opportunity to style, fill, and transform your nodes. People familiar with vector graphics will immediately notice that we are using the same general concepts found in SVG. You will also notice that we have gone to great lengths to keep our syntax concise, using JavaScript literals for our property definitions.</p>
<h3>What Time Is It?</h3>
<p>A more complex and engaging example is the Dojo clock widget, developed by Dojo contributors Eugene Lazutkin and Tom Trenka:</p>
<p><img src="http://www.thinkvitamin.com/images/articles/features/dojogfx/dojoClockWidget.gif" alt="Example of Dojo clock widget" height="136"></p>
<p>The clock widget shows the flexibility of the Dojo widget system in non-HTML namespaces. The following code fragment demonstrates the straightforward method by which hour and minute hands are placed, including realistic shadows:</p>
<pre><code>
this.shadows.hour.shadow = this._initPoly(this.surface, hP)
	.setFill([0, 0, 0, 0.1]);
this.hands.hour = this._initPoly(this.surface, hP)
	.setStroke({color: this.handStroke, width:1 })
	.setFill({
		type:"linear",
		x1:0, y1:0, x2:0, y2:-27,
		colors:[{offset:0, color:"#fff"}, {offset:0.33, color:this.handColor}]
	});
this.shadows.minute.shadow = this._initPoly(this.surface, mP)
	.setFill([0, 0, 0, 0.1]);
this.hands.minute = this._initPoly(this.surface, mP)
	.setStroke({color: this.handStroke, width:1 })
	.setFill({
		type:"linear",
		x1:0, y1:0, x2:0, y2:-38,
		colors:[{offset:0, color:"#fff"}, {offset:0.33, color:this.handColor}]
	});
		</code></pre>
<h3>Circle Frenzy</h3>
<p>An example showing the powerful use of the Dojo event model with a complex dojo.gfx layout is the draggable circles demo developed by Dojo contributor Gavin Doughtie:</p>
<p><img src="http://www.thinkvitamin.com/images/articles/features/dojogfx/circles.gif" alt="Example of draggable circles demo" height="349"></p>
<p>Two <code>dojo.event.connect</code> calls are used to handle mouse events, which then call some simple functions to detect which shape was selected, handle mousemove events for dragging a circle, and then canceling a mousemove event when the mouse button is released. This is in fact the single biggest advantage of dojo.gfx over Canvas&#8230; items drawn with SVG/<abbr title="Vector Markup Language">VML</abbr> are normal DOM nodes in your document, to which you can easily connect event handlers.</p>
<pre><code>
dojo.event.connect(this.domNode, 'onmousedown', this, "handleMouseDown");
dojo.event.connect(this.domNode, 'onmouseup', this, "handleMouseUp");
getShape: function(evt){
	var id = evt.target.getAttribute('shapeid');
	var s = null;
	if (id) {
		s = this.gShapes[id];
	}
	// dojo.debug('target: '   evt.target   ' id: '   id   ' shape: '   s);
	return s;
},
handleMouseDown: function(evt){
	dojo.debug("got an event");
	var shape = this.getShape(evt);
	dojo.debug(shape)
	if (shape) {
		this.gCurrentShape = shape;
		dojo.event.connect(this.domNode, 'onmousemove', this, "handleMouseMove");
		dojo.event.browser.stopEvent(evt);
	}
},
handleMouseMove: function(evt){
	dojo.debug("mouse move");
	dojo.debug(this.gCurrentShape);
	if (this.gCurrentShape) {
		var pos = dojo.html.getAbsolutePosition(this.domNode);
		var x = evt.pageX - pos.x;
		var y = evt.pageY - pos.y;
		dojo.debug(x   "/"   y);
		this.gCurrentShape.setShape({cx: x, cy: y});
		dojo.event.browser.stopEvent(evt);
	}
},
handleMouseUp: function(evt){
	this.gCurrentShape = null;
	dojo.event.disconnect(this.domNode, 'onmousemove', this, "handleMouseMove");
}
	</code></pre>
<h3>Which Features Are Supported?</h3>
<h4>Strokes</h4>
<p>In HTML, we generally call this a border, but in vector graphics, it&#8217;s a stroke, because it can be a border of an element, or also the edge of anything, including a custom font. Strokes can be hidden, or set to various colors and widths. We also provide the ability to set join parameters, which define how lines come together at edges. You&#8217;ve probably noticed the latter, but had little control over it when using thick borders in HTML elements.</p>
<h4>Fills</h4>
<p>A fill is both background and foreground rolled into one. Transparent fills are supported though not particularly interesting in this discussion. Solid colors are nice and even more interesting are linear and radial gradients with various starting and stopping points.  While you cannot create every imaginable gradient today (because SVG and VML do not yet support gradient fills beyond linear and radial), there are a number of clever techniques, using layers and opacity, to achieve most 2-dimensional gradient effects.</p>
<h4>Shapes and Paths</h4>
<p>dojo.gfx provides a number of commonly used shapes including rectangle, ellipse, line, and circle, as well as the ability to draw along any path. Shapes are simply a special case of a path, which supports simple point to point options, as well as complex cubic and bezier curves.</p>
<h4>Opacity</h4>
<p>Most browser implementations of this feature in HTML are limited to an opacity that is the same for background and foreground.  With dojo.gfx, we can define full alpha opacity on the stroke and the fill, together or independently, providing much greater design flexibility.</p>
<h4>Linear Transformations</h4>
<p>Rotation, skew, move, resize, and more, are all possible with linear transformations and are based on standard matrix algebra.  dojo.gfx exposes this API to make just about any transformation possible. And while dojo.gfx is a two-dimensional API, I have no doubt that people will become inspired to develop three-dimensional transformations using the two-dimensional transformation API.
</p>
<h4>Rounded Corners</h4>
<p>What drawing API would be complete without the ability to draw rounded corners? dojo.gfx provides a simple API for doing just that. Say goodbye to background image hacks!</p>
<h3>Charts</h3>
<p>We recently announced that <a href="http://blog.dojotoolkit.org/2006/11/08/the-dojo-charting-engine-courtesy-of-greenplum-and-sitepen">Dojo has APIs for live charting</a>. These APIs are highly specialized and are not currently based on dojo.gfx. Charts were developed before dojo.gfx was ready for production. Nevertheless, they show how much is possible in today&#8217;s browsers with vector graphics. An example:</p>
<p><img src="http://www.thinkvitamin.com/images/articles/features/dojogfx/charts.gif" alt="Example of Dojo chart API" height="343"> </p>
<p>You may have noticed that I mentioned live charts. We will very soon release examples that show animated, dynamic, real-time charting. <a href="http://www.greenplum.com/">Greenplum</a> will soon be releasing the Greenplum Monitor, a database monitoring application built on top of this code that provides browser-based server performance information, much like that which you would find in the Apple Activity Monitor or Windows Task Manager.</p>
<h3>So, Why Not Canvas?</h3>
<p>The number one reason, in my perspective, is that because Canvas does not have a true DOM, you cannot easily attach DOM events to a specific node or layer in a drawing. There are other minor reasons, but this reason alone makes it much easier for JavaScript developers to work with HTML and vector graphical elements seamlessly and in the same web application interface.</p>
<h3>Are We Reinventing The Wheel?</h3>
<p>I hope not! dojo.gfx is not yet another vector markup language, but instead it is a solid subset of SVG features and nomenclature. Like Canvas, it is a procedural drawing API. dojo.gfx is not intended to solve every drawing need ever conceived &#8211; rather, it covers the baseline technology that is widely available across today&#8217;s browsers using SVG or VML within Internet Explorer. A major benefit of dojo.gfx is that it abstracts away the numerous inconsistencies across the browser vector graphics implementations. We all know that even the best, most standards-compliant implementations of HTML, CSS, and JavaScript are inconsistent. Vector graphics suffer in a similar manner.</p>
<p>One of the very first design decisions of which I was a proponent was that the Dojo Toolkit not be HTML-centric. After working on several SVG-based intranet applications, I was highly motivated to see Dojo work well with SVG and other namespaces that could become relevant in the future. This decision has helped significantly with the implementation of dojo.gfx, as very little of the Dojo codebase assumes an HTML DOM.</p>
<h3>The Future</h3>
<p>As you can see from the demos, we are just scratching the surface of what is possible with dojo.gfx. With this simple yet powerful set of options, we have the ability to create sophisticated native user interfaces in the browser. Based on our recent work at SitePen and the expressed interest of our clients, we&#8217;re well underway to building a new field of collaborative visualization web applications. I&#8217;m really excited about the possible application interfaces that we can build with native vector graphics support in today&#8217;s browsers.</p>
<h3>Credits</h3>
<p>Special thanks to Eugene Lazutkin, Gavin Doughtie, Kun Xi, Tom Trenka, Mozilla Foundation, Greenplum, SitePen, and the entire Dojo developer community for making this advancement possible. Without the strong community behind Dojo, ambitious projects like this would not be ready today.</p>
<h3>Resources</h3>
<ul>
<li>Dojo: <a href="http://dojotoolkit.org/api/">documentation</a>, <a href="http://manual.dojotoolkit.org/WikiHome/DojoDotBook/BookIntroduction">book</a>, and <a href="http://download.dojotoolkit.org/">download</a></li>
<li>Clock: <a href="http://archive.dojotoolkit.org/nightly/tests/widget/test_Clock.html">demo</a> and <a href="http://archive.dojotoolkit.org/nightly/src/widget/Clock.js">source</a></li>
<li>Circles: <a href="http://archive.dojotoolkit.org/nightly/demos/gfx/circles.html">demo and source</a></li>
<li>Charts: <a href="http://archive.dojotoolkit.org/nightly/tests/charting/test_engine.html">demo</a></li>
<li>Canvas: <a href="http://developer.mozilla.org/en/docs/Drawing_Graphics_with_Canvas">mozilla tutorial</a></li>
<li>Comet: <a href="http://cometd.com/">Cometd</a>, <a href="http://ligthstreamer.com/">Lightstreamer</a>, <a href="http://knownow.com/">KnowNow</a>, and <a href="http://getahead.ltd.uk/blog/joe/2006/04/11/1144770216929.html">DWR</a></li>
</ul>
<img src="http://carsonified.com/?ak_action=api_record_view&id=1730&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://carsonified.com/blog/carsonified/features/create-cross-browser-vector-graphics/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Create a Searchable Google map</title>
		<link>http://carsonified.com/blog/carsonified/features/create-a-searchable-google-map/</link>
		<comments>http://carsonified.com/blog/carsonified/features/create-a-searchable-google-map/#comments</comments>
		<pubDate>Mon, 30 Oct 2006 17:36:59 +0000</pubDate>
		<dc:creator>Mark Lukovsky</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Features]]></category>
		<category><![CDATA[Google Maps API]]></category>

		<guid isPermaLink="false">http://www.thinkvitamin.com/features/ajax/create-a-searchable-google-map</guid>
		<description><![CDATA[By <strong>Mark Lukovsky</strong><br />The GSmapSearchControl Solution is a simple to use application of the Google AJAX Search API that is designed to let you easily add a searchable map to your pages, sites and blogs. The rest of this page discusses this topic in detail &#8211; but if you just want to cut and paste some code, the [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style=""><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fcarsonified%2Ffeatures%2Fcreate-a-searchable-google-map%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fcarsonified%2Ffeatures%2Fcreate-a-searchable-google-map%2F" height="61" width="51" /></a></div><p>The <em>GSmapSearchControl Solution</em> is a simple to use application of the <a href="http://code.google.com/apis/ajaxsearch/index.html">Google AJAX Search API</a> that is designed to let you easily add a searchable map to your pages, sites and blogs. The rest of this page discusses this topic in detail &#8211; but if you just want to cut and paste some code, the &#8220;hello world&#8221; of the Map Search Control solution is available at the <a href="#hello-world">end of this article</a>.</p>
<p>This <a href="http://www.thinkvitamin.com/basicgmapsearch.html">basic sample</a> demonstrates the following features:</p>
<ul>
<li>A search control that allows you to look up addresses and business listings, plotting the results on a map.</li>
<li>The search control has a programmable center point, and at this center point you can specify a custom title and url.</li>
<li>The search control takes a small amount of verticle space while inactive, the inactive view is a small map with a white center point.</li>
<li>Once a search completes, the map expands and the search result cursors allow you to scroll through the set of search results.</li>
<li>Each search result contains a link to a details page for the result as well as a link to get driving directions from the center point of the map to the current search result.</li>
<li>The clear button in the center of the cursor controls clears the current set of results and sets the control into the idle state.</li>
</ul>
<p>The solution is designed for extreme ease of use. As a site designer you are able to control the center point location as well as a custom title and url. In addition, you can easily program a collection of &#8220;search links&#8221; that you can place on your page that when clicked, will drive the search control.</p>
<h4>New Functionality Update</h4>
<p>If you are already familiar with this solution, you might find the following new functionality helpful and useful.</p>
<ul>
<li><a href="#_setcentericon">Custom Icons</a></li>
<li><a href="#_setzoomlevel">Zoom Level Control</a></li>
<li><a href="#_setmaptype">Enabling GMapTypeControl</a></li>
</ul>
<h4>Instructions for adding this to your site</h4>
<p>Adding the solution is a simple three step process.</p>
<h5>Step 1 &#8211; Load AJAX Search API, the Google Maps API, and the Map Search Solution</h5>
<p>First you need to load the Google AJAX Search API, the Google Maps API, and the Map Search solution into your application. If you don&#8217;t already have a Google AJAX Search API key, your first step is to <a href="http://code.google.com/apis/ajaxsearch/signup.html">sign up for a key</a>. With your key in hand, add the following five lines of code to your page or blog template.</p>
<pre>
&lt;!-- maps api, ajax search api, map search solution code --&gt;
&lt;script src="http://maps.google.com/maps?file=api&#038;v=2&#038;key=YOUR-KEY"
    type="text/javascript"&gt;&lt;/script&gt;

&lt;script src="http://www.google.com/uds/api?file=uds.js&#038;v=1.0&#038;key=YOUR-KEY"
    type="text/javascript"&gt;&lt;/script&gt;
&lt;script src="http://www.google.com/uds/solutions/mapsearch/gsmapsearch.js"
    type="text/javascript"&gt;&lt;/script&gt;

&lt;!-- ajax search stylesheet, map search stylesheet --&gt;
&lt;link href="http://www.google.com/uds/css/gsearch.css" rel="stylesheet"
    type="text/css"/&gt;
&lt;link href="http://www.google.com/uds/solutions/mapsearch/gsmapsearch.css"
    rel="stylesheet" type="text/css"/&gt;
      </pre>
</p>
<h5>Step 2 &#8211; Define a location on your page for the Map Search Control</h5>
<p>The next step is to define a place on your page for the search control (note: you can have more than one). This is typically done by defining a named &lt;div&gt; element as we have shown below:</p>
<pre>
&lt;div id="mapsearch"&gt;Loading...&lt;/div&gt;
    </pre>
<p>You might want to set some styling attributes on this element to constrain the width of the control, set a border or margin, etc. For example, a style rule like this might be useful.</p>
<pre>
#mapsearch {
  width : 400px;
  margin : 10px;
  padding : 4px;
  border : 1px solid #f9f9f9;
}
    </pre>
<p>In addition to this base style, the height of the idle state map and the active state map is easily modified.</p>
<pre>
/* set height of idle state map */
#mapsearch .gsmsc-idleMapDiv { height : 200px; }

/* set height of active state map */
#mapsearch .gsmsc-mapDiv { height : 300px; }
    </pre>
<h5>Step 3 &#8211; Create a GSmapSearchControl and bind it to your page</h5>
<p>The final step involves creating and configuring the control. The control is very powerful and offers several options. In this section, we will describe the simplest case of adding a control to your page or blog followed several examples that demonstrate more advanced concepts.</p>
<p>The first step is to actually get your code to run. The easiest way to do this is to either extend an existing body onload handler, or create a new one. Find the &lt;body&gt; element on your page and add an <em>onload</em> handler.</p>
<pre>
&lt;body onload="OnLoad()"&gt;
    </pre>
<p>Now write your onload handler by adding some code to an existing &lt;script&gt; block, or by creating a new one. Find the &lt;body&gt; element on your page and add an <em>onload</em> handler. The code snippet below demonstrates how to create a new map search control that will appear on your page inside the element named &#8220;mapsearch&#8221;. The map will center itself on Google corporate headquarters located at &#8220;1600 Amphitheatre Parkway, Mountain View, CA.&#8221;</p>
<pre>
&lt;script type="text/javascript"&gt;
function OnLoad() {
  new GSmapSearchControl(
        document.getElementById("mapsearch"),             // container
        "1600 Amphitheatre Parkway, Mountain View, CA",   // center point
        null                                              // options
        );
}
&lt;/script&gt;
    </pre>
<h4>Options</h4>
<p>Notice in the above example, the value <code>null</code> is passed as the <code>options</code> argument, a Javascript object which is typically specified in object literal form. The following sections describe the possible values for <code>options</code>. These values support the following features:</p>
<ul>
<li>Setting a special title and url for the map center point.</li>
<li>Specifying a set of hot spots on your page that will trigger searches</li>
</ul>
<h5>Setting a special title and url for the map center point</h5>
<p>A special title and url can be programmed into the control and bound to the white center point marker of an active map. A typical use case for this feature might be to specify a conference name and conference url, or a company name and url. In order to request this functionality, the <code>title</code> and <code>url</code> properties of the <code>options</code> argument must be set. The following code snippet demonstrates this.</p>
<pre>
&lt;script type="text/javascript"&gt;
function OnLoad() {

  // set title to the Googleplex and the link to
  // the Google corporate information page
  var options =
    {
      title : "Googleplex",
      url : "http://www.google.com/corporate/index.html"
    }

  // create the map search control
  new GSmapSearchControl(
        document.getElementById("mapsearch"),
        "1600 Amphitheatre Parkway, Mountain View, CA",
        options
        );
}
&lt;/script&gt;
    </pre>
<h5>Setting up hot spots</h5>
<p>A common use case for this control is program hot spots on to your page thatwhen clicked will drive the search control. These hot spots might take the form of a list of recommendations, hotels, restaurants, etc. The search control supports this by allowing you to program a list of HTML elements and associated queries. When your users click on the element, the search control is activated with the specified query.</p>
<p>The <code>options</code> argument contains an optional <code>hotspot</code> property. This property is an array of objects, where each object contains an <code>element</code> property which is a reference to an HTML element on your page, as well as a <code>query</code> property which specifies a query to execute when the element is clicked.</p>
<p>The following code fragment demonstrates the use of this feature including the CSS declarations,HTML fragments, and options programming. If you prefer programming this on your own, perhaps by using &lt;a&gt; tags with <code>javascript:</code> urls, that&#8217;s a perfectly reasonable way to proceed. We include this capability in the solution because for some of our users, this is a simpler mechanism that requires less of their time to develop. In the snippet that follows we are going to show sample CSS, HTML, and search control initialization logic.</p>
<pre>
/* define the CSS used to style the hotspots */
h4.hotspot {
  font-size : 100%;
  font-weight : normal;
  color : rgb(9, 122, 182);
  margin-left : 8px;
  margin-top : 0px;
  margin-bottom : 2px;
  font-style : normal;
  cursor : pointer;
}

h4.hotspot:hover {
  color : rgb(237, 92, 11);
  text-decoration : underline;
}

&lt;!--
Define the HTML that places the hotspots on your page.
Note, you can use any HTML element you like including li, div, etc.
--&gt;

&lt;div id="mapsearch"&gt;Loading...&lt;/div&gt;
&lt;h3 class="hotspotheader"&gt;Recommendations&lt;/h3&gt;
&lt;h4 id="hs01" class="hotspot"&gt;Caffeine&lt;/h4&gt;
&lt;h4 id="hs02" class="hotspot"&gt;Thai Food&lt;/h4&gt;
&lt;h4 id="hs03" class="hotspot"&gt;Pizza&lt;/h4&gt;

&lt;h4 id="hs04" class="hotspot"&gt;Gym&lt;/h4&gt;
&lt;h4 id="hs05" class="hotspot"&gt;Hotel Avante&lt;/h4&gt;
&lt;h4 id="hs06" class="hotspot"&gt;Residence Inn&lt;/h4&gt;
&lt;h4 id="hs07" class="hotspot"&gt;The Four Seasons&lt;/h4&gt;
&lt;h4 id="hs08" class="hotspot"&gt;The Westin, Palo Alto&lt;/h4&gt;

&lt;script type="text/javascript"&gt;
function OnLoad() {

  // Create an array of hotspots. Each entry contains and html element
  // from your page, and the query to execute when that element is clicked
  var hotspotsList = [
      { element : document.getElementById("hs01"), query : "Starbucks" },
      { element : document.getElementById("hs02"), query : "Amarin Thai" },
      { element : document.getElementById("hs03"), query : "Frankie Johnnie &#038; Luigi" },
      { element : document.getElementById("hs04"), query : "Hotel Avante" },
      { element : document.getElementById("hs05"), query : "Residence Inn" },
      { element : document.getElementById("hs06"), query : "Four Seasons Palo Alto" },
      { element : document.getElementById("hs07"), query : "Westin Palo Alto" }
  ];

  // set title to the Googleplex and the link to
  // the Google corporate information page
  // set the hotspot list to the list above
  var options =
    { title : "Googleplex",
      url : "http://www.google.com/corporate/index.html",
      hotspots : hotspotsList
    }

  // create the map search control
  new GSmapSearchControl(
        document.getElementById("mapsearch"),
        "1600 Amphitheatre Parkway, Mountain View, CA",
        options
        );
}
&lt;/script&gt;
    </pre>
<h5>New! Setting Custom Center Icon</h5>
<p>Applications can either accept the default center location icon, or they can supply their own <code>GIcon</code> and the system will use that Icon to mark the center point of the map.</p>
<p>The <code>options</code> argument contains an optional <code>centerIcon</code> property. When supplied, property must specify a valid <a href="http://www.google.com/apis/maps/documentation/reference.html#GIcon">GIcon</a>. When specified, this icon is used as the center point marker for both the idle and active map.</p>
<p>The following code fragment demonstrates the use of this feature. It demonstrates the use of both a hand built icon, as well as the <code>G_DEFAULT_ICON</code> two calls:</p>
<pre>
// use the ridefinder, small yellow icon as the center point marker
var ci = new GIcon();
ci.image = "http://labs.google.com/ridefinder/images/mm_20_yellow.png";
... (rest of initialization omitted)

new GSmapSearchControl(container,
                       "1600 Amphitheatre Parkway, Mountain View, CA",
                       { title : "Googleplex",
                         url : "http://www.google.com/press/factorytour.html",
                         centerIcon : ci
                       }
                       );

// use the G_DEFAULT_ICON as the center point marker
new GSmapSearchControl(container,
                       "1600 Amphitheatre Parkway, Mountain View, CA",
                       { title : "Googleplex",
                         url : "http://www.google.com/press/factorytour.html",
                         centerIcon : G_DEFAULT_ICON
                       }
                       );
    </pre>
<h5>New! Setting Custom Selected and Unselected Icons</h5>
<p>Applications are able to specify the icons for the currently selected search result as well as for all unselected search results using a similar model.</p>
<p>The <code>options</code> argument contains an optional <code>selectedIcon</code> property. When supplied, property must specify a valid <a href="http://www.google.com/apis/maps/documentation/reference.html#GIcon">GIcon</a>. When specified, this icon is used as the marker for the selected search result.</p>
<p>The <code>options</code> argument contains an optional <code>unselectedIcon</code> property. When supplied, property must specify a valid <a href="http://www.google.com/apis/maps/documentation/reference.html#GIcon">GIcon</a>. When specified, this icon is used as the marker for all other search results.</p>
<p>The following code fragment demonstrates the use of this feature:</p>
<pre>
// use the G_DEFAULT_ICON as the for selected and unselected search results
new GSmapSearchControl(container,
                       "1600 Amphitheatre Parkway, Mountain View, CA",
                       { title : "Googleplex",
                         url : "http://www.google.com/press/factorytour.html",
                         selectedIcon : G_DEFAULT_ICON,
                         unselectedIcon : G_DEFAULT_ICON
                       }
                       );
    </pre>
<h5>Controlling the Zoom Level</h5>
<p>Applications can control the Zoom Level for the idle map (displayed when no searches are active) as well as for the active map (displayed when a map is showing results). The system defines two constants that are helpful when configuring this aspect of the control:</p>
<ul>
<li>GSmapSearchControl.IDLE_MAP_ZOOM &#8211; the default zoom level for the idle map</li>
<li>GSmapSearchControl.ACTIVE_MAP_ZOOM &#8211; the default zoom level for the active map</li>
</ul>
<p>The <code>options</code> argument contains an optional <code>idleMapZoom</code> property. When supplied, this property specifies the zoom level for the idle map. A common use of this property is to have the idle map&#8217;s zoom level match the default zoom level for the active map. In order to do this, use the constants described above to set <code>idleMapZoom</code> to <code>GSmapSearchControl.ACTIVE_MAP_ZOOM</code>.</p>
<p>The zoom level for the active map is controlled in a similar manner. The <code>options</code> argument contains an optional <code>activeMapZoom</code> property. When supplied, this property specifies the zoom level for the active map. Note: Applications can use these properties independently of each other.</p>
<p>The following code fragment demonstrates the use of this feature.</p>
<pre>
// set the idle map zoom so that it matches the
// default active map zoom level
new GSmapSearchControl(container,
                       "1600 Amphitheatre Parkway, Mountain View, CA",
                       { title : "Googleplex",
                         url : "http://www.google.com/press/factorytour.html",
                         idleMapZoom : GSmapSearchControl.ACTIVE_MAP_ZOOM
                       }
                       );

// set the active map zoom level up by 1
new GSmapSearchControl(container,
                       "1600 Amphitheatre Parkway, Mountain View, CA",
                       { title : "Googleplex",
                         url : "http://www.google.com/press/factorytour.html",
                         activeMapZoom : GSmapSearchControl.ACTIVE_MAP_ZOOM+1
                       }
                       );
    </pre>
<h5>New! Enabling the Map Type Control</h5>
<p>The default behavior of the maps created by this control is for them to not contain the <code>GMapTypeControl</code>, the control that allows switching between map, hybrid and satellite mode. This behavior is programmable allowing applications to cause the control to build maps with the <code>GMapTypeControl</code> available on all maps, or just on the active map.</p>
<p>The <code>options</code> argument contains an optional <code>mapTypeControl</code> property. When supplied, this property specifies which maps should be programmed to include the <code>GMapTypeControl</code>. </p>
<ul>
<li>GSmapSearchControl.MAP_TYPE_ENABLE_ACTIVE &#8211; When this value is specified for the <code>mapTypeControl</code>property, the active map will contain a <code>GMapTypeControl</code></li>
<li>GSmapSearchControl.MAP_TYPE_ENABLE_ALL &#8211; When this value is specified for the <code>mapTypeControl</code> property, both the active map and the idle map will contain a <code>GMapTypeControl</code></li>
</ul>
<p>The following code fragment demonstrates the use of this feature.</p>
<pre>
// enable a GMapTypeControl on the active map
new GSmapSearchControl(container,
                       "1600 Amphitheatre Parkway, Mountain View, CA",
                       { title : "Googleplex",
                         url : "http://www.google.com/press/factorytour.html",
                         mapTypeControl : GSmapSearchControl.MAP_TYPE_ENABLE_ACTIVE
                       }
                       );

// enable a GMapTypeControl on the all maps
new GSmapSearchControl(container,
                       "1600 Amphitheatre Parkway, Mountain View, CA",
                       { title : "Googleplex",
                         url : "http://www.google.com/press/factorytour.html",
                         mapTypeControl : GSmapSearchControl.MAP_TYPE_ENABLE_ALL
                       }
                       );
</pre>
<h4 id="hello-world">The &#8220;Hello World&#8221; of GSmapSearchControl</h4>
<p>The following page demonstrates a complete page which uses the <code>GSmapSearchControl</code> solution. You can start with this simple page, change <em>internal</em> to the value of your key and be up and running in seconds.</p>
<pre>
&lt;html&gt;
&lt;head&gt;

  &lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/&gt;
  &lt;title&gt;GSmapSearchControl Sample&lt;/title&gt;

  &lt;!-- Note:
    Make sure to replace the &amp;key=internal with &amp;key=YOUR-KEY
    in both the maps API script load and in the Ajax Search API script load statements
  --&gt;
  &lt;!-- maps api, ajax search api, map search solution code --&gt;

  &lt;script src="http://maps.google.com/maps?file=api&#038;v=2&#038;key=internal"
    type="text/javascript"&gt;&lt;/script&gt;
  &lt;script src="http://www.google.com/uds/api?file=uds.js&#038;v=1.0&#038;key=internal"
    type="text/javascript"&gt;&lt;/script&gt;
  &lt;script src="http://www.google.com/uds/solutions/mapsearch/gsmapsearch.js"
    type="text/javascript"&gt;&lt;/script&gt;

  &lt;!-- ajax search stylesheet, map search stylesheet --&gt;
  &lt;link href="http://www.google.com/uds/css/gsearch.css" rel="stylesheet"
    type="text/css"/&gt;
  &lt;link href="http://www.google.com/uds/solutions/mapsearch/gsmapsearch.css"
    rel="stylesheet" type="text/css"/&gt;
  &lt;style type="text/css"&gt;
    body, table, p{
      background-color: white;
      font-family: Arial, sans-serif;
      font-size: 13px;
    }

    #mapsearch {
      width : 400px;
      margin-left: 10px;
      padding: 4px;
      border : 1px solid #f9f9f9;
    }
  &lt;/style&gt;
  &lt;script type="text/javascript"&gt;

    function OnLoad() {

      var options = {
            title : "Googleplex",
            url : "http://www.google.com/corporate/index.html"
            }

      new GSmapSearchControl(
            document.getElementById("mapsearch"),
            "1600 Amphitheatre Parkway, Mountain View, CA",
            options
            );
    }
  &lt;/script&gt;
&lt;/head&gt;
&lt;body onload="OnLoad()"&gt;
  &lt;div id="mapsearch"&gt;Loading...&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
<h4>Examples</h4>
<ul>
<li><a href="http://www.thinkvitamin.com/basicgmapsearch.html">Basic Google Map Search</a></li>
<li><a href="http://www.thinkvitamin.com/gmapsearchhotspots.html">&#8216;Hotspots&#8217; Google Map Search</a></li>
</ul>
<p class="diggit"><img src="http://www.digg.com/img/digg-guy-small.gif" alt="digg.com logo" /> Like this article? <a href="http://digg.com/programming/Create_a_searchable_Google_map">Digg it</a>!</p>
<img src="http://carsonified.com/?ak_action=api_record_view&id=1718&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://carsonified.com/blog/carsonified/features/create-a-searchable-google-map/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>Responsible Asynchronous Scripting</title>
		<link>http://carsonified.com/blog/dev/responsible-asynchronous-scripting/</link>
		<comments>http://carsonified.com/blog/dev/responsible-asynchronous-scripting/#comments</comments>
		<pubDate>Sun, 28 May 2006 08:00:28 +0000</pubDate>
		<dc:creator>Shaun Inman</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[Features]]></category>
		<category><![CDATA[Asynchronous Scripting]]></category>
		<category><![CDATA[Business]]></category>

		<guid isPermaLink="false">http://www.thinkvitamin.com/features/ajax/responsible-asynchronous-scripting</guid>
		<description><![CDATA[By <strong>Shaun Inman</strong><br />Asynchronous or remote scripting has been lurking in the background of web app development for quite some time now. Originally dependent on proprietary technology like Java applets, ActiveX and Flash or clever combinations of disparate technologies like images and cookies, native support for the XMLHttpRequest (XHR) object in modern browsers has made it easier than [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style=""><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fdev%2Fresponsible-asynchronous-scripting%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fdev%2Fresponsible-asynchronous-scripting%2F" height="61" width="51" /></a></div><p>Asynchronous or remote scripting has been lurking in the background of web app development for quite some time now. Originally dependent on proprietary technology like Java applets, ActiveX and Flash or clever combinations of disparate technologies like <a href="http://www.depressedpress.com/Content/Development/JavaScript/Articles/GIFAsPipe/Index.cfm">images and cookies</a>, native support for the XMLHttpRequest (XHR) object in modern browsers has made it easier than ever to make web apps more responsive and more like their desktop counterparts. This lower barrier to entry also makes it easier to make poor decisions and inappropriate use of a powerful technology. </p>
<p>So what&#8217;s a well-intentioned web developer to do? Stop and think about the following before dropping that sudsy, lemon-scented bomb on unsuspecting users.</p>
<h3>Content or Action?</h3>
<p>Using XHR circumvents the browser&#8217;s history. As a result asynchronous scripting is most appropriate for actions and transient or conditional data&#8212;not navigating content. XHR doesn&#8217;t break the back button, misunderstanding user expectation does. </p>
<p><a href="http://www.google.com/webhp?complete=1&#038;hl=en">Search results</a>, <a href="http://flickr.com/photos/anizzle/136789375/in/set-72057594119892012/">tags</a>, <a href="http://ajaxian.com/archives/wordpress-ajax-comments">comments</a>, and <a href="http://www.rememberthemilk.com/signup/">data validation</a> are all good examples of transient data. All are given to flux and make perfect candidates for manipulation by AJAX or similar technologies.</p>
<p>On the other hand, avoid replacing traditional navigation with XHR. Doing so breaks an inherent functionality responsible for the success of the web: Universal Resource Locators. Unless we <a href="http://ajaxpatterns.org/Unique_URLs">reinvent the wheel</a> and restore this functionality ourselves (hidden iframes and fragment identifiers anyone?), it is impossible to link directly to content included with XHR. </p>
<p>Both HiDefDVD.com and Microsoft&#8217;s recently launched Windows Live Shopping make this laughable mistake. By relying on XHR for navigation and providing no alternative they effectively cede any search relevance to the market leader Amazon who appropriately uses the traditional navigation model (remember at the time of this writing search engines don&#8217;t parse JavaScript).</p>
<p>To avoid repeating a similar mistake, try this litmus test: If refreshing a page immediately after that page has been updated by remote scripting results in an <em>essentially</em> identical page then you&#8217;re doing okay. If not, head back to the drawing board.</p>
<h3>Communicating Change</h3>
<p>While not directly tied to asynchronous scripting, page content is often replaced unceremoniously in an application that uses XHR. Without the intermediary blank page followed by a browser&#8217;s built-in progress indicators informing users that something is happening, the responsibility now falls on the developer to inform the user of pending changes.</p>
<p>Spinners, hourglasses and progress bars build upon users&#8217; experience with desktop applications and indicate that an operation is pending completion. When inserting or replacing content, animation and movement go a long way towards capturing attention and alerting the user that the page has been updated.</p>
<p>These purely visual techniques fall short when the modified content has been scrolled off screen (imagine using an AJAX-ified mobile app) or with a screen reader. Potential solutions to this shortcoming are still being explored but parallels already exist on the desktop. When a background application requires attention in Mac OS X, it may chirp and bounce the dock icon to alert the user. Keyboard shortcuts can be used to navigate to the needy application. Using access keys and audio triggered by an XHR callback function it is possible to mirror this behavior.</p>
<h3>Progressive Enhancement</h3>
<p>In DOM Scripting, Jeremy Keith coined the term <a href="http://domscripting.com/blog/display/41">&#8220;Hijax.&#8221;</a> He suggests that we plan and develop using the traditional &#8220;links to GET/forms to POST&#8221; model without relying on JavaScript. Then we hijack those links and forms with asynchronous scripting calls using the onclick and onsubmit event handlers, allowing us to bypass intermediate screens and refine existing interaction.</p>
<p>This type of development favors using <a href="http://microformats.org/wiki/rest/ahah">AHAH</a> or <a href="http://codylindley.com/Javascript/237/axah-asynchronous-xhtml-and-http-crawl-before-you-ajax">AXAH</a> over <a href="http://www.json.org/">JSON</a> or XML-based flavors of AJAX. By including the same HTML generating file that we plan to request using XHR, we keep our business logic out of the JavaScript and on the server where it belongs.</p>
<p>There are a number of JavaScript libraries out there that encapsulate XHR. When choosing a library or framework for our applications we need to be asking ourselves hard questions. A framework might make our lives as developers easier but will it also facilitate making bad decisions? When taking the Hijax approach we may notice that while XHR allows you to POST data to the server, when using an HTML link we don&#8217;t actually make use of this feature. Why? There is no analogous to POSTing with a link in the traditional model, that behavior requires an HTML form. We need to keep in mind that a library that allows us to do so allows us to make a mistake that could disable our application when XHR is not supported or JavaScript is disabled.</p>
<p>Once these ideas about AJAX soak in we&#8217;ll be producing clean web apps that play nice with search engines, desktop and mobile browsers, and web-enabled dish-washers alike.</p>
<p class="diggit"><img src="http://www.digg.com/img/digg-guy-small.gif" alt="digg.com logo" /> Like this article? <a href="http://www.digg.com/programming/Responsible_Asynchronous_Scripting">Digg it</a>!</p>
<img src="http://carsonified.com/?ak_action=api_record_view&id=1688&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://carsonified.com/blog/dev/responsible-asynchronous-scripting/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Create Your Own Ajax effects</title>
		<link>http://carsonified.com/blog/dev/create-your-own-ajax-effects/</link>
		<comments>http://carsonified.com/blog/dev/create-your-own-ajax-effects/#comments</comments>
		<pubDate>Mon, 10 Apr 2006 12:22:21 +0000</pubDate>
		<dc:creator>Thomas Fuchs</dc:creator>
				<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Dev]]></category>
		<category><![CDATA[Features]]></category>

		<guid isPermaLink="false">http://www.thinkvitamin.com/features/ajax/create-your-own-ajax-effects</guid>
		<description><![CDATA[By <strong>Thomas Fuchs</strong><br />The basic and prebuilt effects in script.aculo.us are nice, but if you really want to build something great why not investigate doing your own, homegrown, do-it-yourself effects. We&#8217;re going to show you how to take basic effects and build on them to create your own. So let&#8217;s get going.
First, download and include Prototype and script.aculo.us [...]]]></description>
			<content:encoded><![CDATA[<div class="tweetmeme_button" style=""><a href="http://api.tweetmeme.com/share?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fdev%2Fcreate-your-own-ajax-effects%2F"><img src="http://api.tweetmeme.com/imagebutton.gif?url=http%3A%2F%2Fcarsonified.com%2Fblog%2Fdev%2Fcreate-your-own-ajax-effects%2F" height="61" width="51" /></a></div><p>The basic and prebuilt effects in script.aculo.us are nice, but if you really want to build something great why not investigate doing your own, homegrown, do-it-yourself effects. We&#8217;re going to show you how to take basic effects and build on them to create your own. So let&#8217;s get going.</p>
<p>First, download and include Prototype and script.aculo.us on your page as described in the <a href="http://wiki.script.aculo.us/scriptaculous/show/Usage">installation instructions</a>.</p>
<p>You&#8217;re ready to use the visual effects engine now! Give this short line a try:</p>
<pre><code>&lt;div onclick=&quot;Effect.Fade(this)&quot;&gt;Fade me already!&lt;/div&gt;</code></pre>
<p>To tweak the effect, try something like this:</p>
<pre><code>&lt;div onclick=&quot;Effect.Fade(this,{duration:3})&quot;&gt;Fade me slower!&lt;/div&gt;</code></pre>
<h4>The cash register effect</h4>
<p>You&#8217;re now ready to start building your own effects. All the pre-built effects in script.aculo.us really are about changing the style of elements, but there&#8217;s nothing to stop you from actually manipulating the contents of elements too. For our homegrown effect, we&#8217;ll do exactly that.</p>
<p>Say you have an online shop that uses AJAX for adding and removing products from a shopping basket. Of course, you may display a total for all the products in the basket, that gets automatically updated each time something changes. We want to give the user a clue that this total is correctly updated and reflects the current contents of the basket. You could use the <a href="http://www.37signals.com/svn/archives/000558.php">Yellow Fade Technique</a>, but we want to bring our own style in here and use something more snazzy.</p>
<p>So, we want to emulate the look and feel of an old cash register, and display the change in the total with a completely new homegrown effect, that dynamically adjusts the value from the old total to the new.</p>
<p>Without further ado, <a href="http://www.thinkvitamin.com/downloads/cashregister/cashregister.html">here&#8217;s a demo</a> so you know what I&#8217;m talking about.<br />
Plus you can download a <a href="http://www.thinkvitamin.com/downloads/cashregister/CashRegister.zip">zipped example</a> (20KB) to follow along (it includes all the files you need).</p>
<h4>Have a plan</h4>
<p>To make use of the facilities the effects engine provides, you should sit down and think about what your effect really does. The important thing is to come up with a method that renders exactly one specific frame as the effect engine will ask your method to do that.</p>
<p>For our cash register effect that means that for the duration of the effect, on each rendering of a frame, it should display the result of the expression <code>start_price + (price_delta * position)</code>, where <code>price_delta = end_price - start_price</code> and <code>position</code> refers to how far the effect is completed in a 0 to 1 range.</p>
<p>Example: Assume that the total currently displayed is $100.00 and we want to change that to $150.00. The value to display on position <code>0.5</code> (halfway through the effect) would be <code>$125.00</code> or <code>$100.00 + ($50.00 * 0.5)</code> with <code>price_delta = $150.00 - $100.00 = $50.00</code>.</p>
<pre>
<code>First frame (position 0):       &lt;span id=&quot;total&quot;&gt;$100.00&lt;/span&gt;
Halfway-through (position 0.5): &lt;span id=&quot;total&quot;&gt;$125.00&lt;/span&gt;
Last frame (position 1):        &lt;span id=&quot;total&quot;&gt;$150.00&lt;/span&gt;</code></pre>
<h4>Effect skeleton</h4>
<p>The visual effects engine provides a base class to build your own effects.<br />
<code>Effect.Base</code> is used like this:</p>
<pre><code>Effect.CashRegister = Class.create();
Object.extend(Object.extend(Effect.CashRegister.prototype,
  Effect.Base.prototype), {
    // we&#8217;ll fill this up later
});</code></pre>
<p>Next, we need to tell our new effect what to do.</p>
<h4>The initialize() method</h4>
<p>As dicussed earlier we want to have a transition from one price to another, and<br />
need to find out the delta before we start the effect. For convinience, we also just<br />
want to take the value that&#8217;s displayed in the element as a starting point, so we<br />
won&#8217;t have to remember the &#8220;old&#8221; price:</p>
<pre><code>initialize: function(element, price) {
  // optional third argument &#8220;options&#8221;
  var options = arguments[2] || {};

  // $ is a shortcut for document.getElementById in Prototype
  this.element = $(element);

  // find current price and parse it without the dollar sign
  this.startPrice = parseFloat(this.element.innerHTML.substring(1));

  // set finishPrice and precalculate delta
  this.finishPrice = price;
  this.delta = (this.finishPrice-this.startPrice);

  // the start method is provided by the effects engine
  // it should be the last line in your initialize() method
  this.start(options);
},</code></pre>
<p>A short note on using the <code>innerHTML</code> property of a DOM element here: It&#8217;s neither good nor evil, it&#8217;s just there, in all browsers. So, do use it. It&#8217;s much faster than any XML sit-ups with the DOM anyway.</p>
<h4>The update() method</h4>
<p>For the heavy lifting in our effect, we need to define the <code>update()</code> method. This method is called repeatedly by the visual effects engine for each frame to render. It get&#8217;s the <code>position</code> parameter which is in the range of 0 to 1.</p>
<pre><code>update: function(position) {
  // calculate value and convert to dollar/cent array
  var value = (this.startPrice + (this.delta*position)).toString().split(&#8217;.');

  // ensure two digits after the comma
  var cent  = value.length==1 ? &#8216;00&#8242; : (
    value[1].length == 1 ? value[1]+&#8221;0&#8243; : value[1].substring(0,2));

  // Element.update from Prototype sets the innerHTML of an element
  Element.update(this.element, &#8216;$&#8217; + value[0] + &#8216;.&#8217; + cent);
}</code></pre>
<p>And that&#8217;s it. Your effect is ready. To actually use it, call it by using (for example):</p>
<pre><code>new Effect.CashRegister('total',150);
</code></pre>
<p>If you want to have a little bit of that &#8220;slow motion awe&#8221; (where you&#8217;ll notice that easing in and out is automatically applied), try:</p>
<pre><code>new Effect.CashRegister('total',150,{duration:10}); </code></pre>
<p>Here&#8217;s the complete code with the comments removed.</p>
<pre><code>Effect.CashRegister = Class.create();
Object.extend(Object.extend(Effect.CashRegister.prototype,
Effect.Base.prototype), {
  initialize: function(element, price) {
    var options = arguments[2] || {};
    this.element = $(element);
    this.startPrice = parseFloat(this.element.innerHTML.substring(1));
    this.finishPrice = price;
    this.delta = (this.finishPrice-this.startPrice);
    this.start(options);
  },
  update: function(position) {
    var value = (this.startPrice + (this.delta*position)).toString().split(&#8217;.');
    var cent  = value.length==1 ? &#8216;00&#8242; : (
      value[1].length == 1 ? value[1]+&#8221;0&#8243; : value[1].substring(0,2));
    Element.update(this.element, &#8216;$&#8217; + value[0] + &#8216;.&#8217; + cent);
  }
});</code></pre>
<h3>More things to explore</h3>
<p>Of course, we&#8217;ve only scratched the surface here, so here&#8217;s a quick look at various other things waiting for discovery inside the visual effects engine:</p>
<h4>Callbacks</h4>
<p>At all stages while your effect runs, <a href="http://wiki.script.aculo.us/scriptaculous/show/CoreEffects">various callbacks</a> can be used to do further processing. For example, if you want to issue an other effect after your effect has completed, you can use the <code>afterFinish</code> callback:</p>
<pre><code>new Effect.CashRegister('total', 150, {
 afterFinish:function(effect){ new Effect.Highlight(effect.element) }
});</code></pre>
<h4>Transitions</h4>
<p>Transitions control the easing-in and out of effects, plus can be used for some more advanced control over how an effect should render itself. See the <a href="http://wiki.script.aculo.us/scriptaculous/show/CoreEffects">script.aculo.us wiki</a> for more information</p>
<h4>Queues</h4>
<p>Effect queues are a tool to build powerful, time-line based animation. <a href="http://www.railsdevelopment.com/2006/01/15/effectqueue/">Here&#8217;s a great tutorial</a>.</p>
<p>So what are you waiting for? Get out there and start creating your own wonderful world of visual effects.</p>
<h4>About Scriptaculous</h4>
<p>Among other things, <a href="http://script.aculo.us/">script.aculo.us</a> includes a fully-featured JavaScript effects engine that provides easy-to-use visual effects on the web. The primary goal is to enable AJAX-powered sites that give visual feedback to users, who otherwise might have no clue that page updates happen behind the scenes. The second use is to make in-page interactivity smoother (for example when a hidden panel opens, it can slide out smoothly instead of just popping up).</p>
<p>The visual effects engine first came to life as a part of <a href="http://www.rubyonrails.org/">Ruby on Rails</a> and particularily the <a href="http://prototype.conio.net/">Prototype JavaScript framework</a>, at a time when AJAX was very fresh and the <a href="http://www.37signals.com/svn/archives/000558.php">Yellow Fade Technique</a> had a revival. For the launch of script.aculo.us in June 2005, the engine was completely rewritten to provide a solid animation framework that&#8217;s easily expandable.</p>
<p>If you never heard about it, have a look at the <a href="http://wiki.script.aculo.us/scriptaculous/show/CombinationEffectsDemo">demo page</a> in the script.aculo.us documentation wiki. For a nice use of rich in-page interactivity visit <a href="http://www.apple.com/aperture/">Apple&#8217;s Aperture product web site</a>, and try the &#8220;Key Features of Aperture&#8221; box at the right of the screen (scroll down a bit to see it), and the <a href="http://www.wollzelle.com/">wollzelle homepage</a> (try the references in the portfolio section).</p>
<p>For usage in an web application, you might want to check out the <a href="http://www.fluxiom.com/">fluxiom teaser video</a> (most stuff in the effects engine came out of the development of fluxiom).</p>
<h4>What you don&#8217;t have to worry about</h4>
<p>script.aculo.us&#8217; time-based animation framework takes<br />
care of all the tedious background processing and advanced functionality,<br />
so you never have to worry about things like the rendering speed of the client/browser,<br />
easying in and out and queueing up stuff in timelines.</p>
<p>You&#8217;ll automatically get all sorts of tweaking options and callback functionality<br />
to really tune the effects to your liking (of course the provided effects come with<br />
sane defaults, so you can just drop a line of JavaScript code in and you&#8217;re all set).</p>
<p>Also, anything is meant to work (and does!) on Internet Explorer 6, Firefox 1.0 and later, plus Safari.</p>
<img src="http://carsonified.com/?ak_action=api_record_view&id=1677&type=feed" alt="" />]]></content:encoded>
			<wfw:commentRss>http://carsonified.com/blog/dev/create-your-own-ajax-effects/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
	</channel>
</rss>
