<?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>Death of a Gremmie &#187; jquery</title>
	<atom:link href="http://deathofagremmie.com/tag/jquery/feed/" rel="self" type="application/rss+xml" />
	<link>http://deathofagremmie.com</link>
	<description>by Brian Neal</description>
	<lastBuildDate>Wed, 14 Jul 2010 02:31:19 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>jQuery UI Autocomplete Widget &amp; Caching</title>
		<link>http://deathofagremmie.com/2010/04/03/jquery-ui-autocomplete-widget-caching/</link>
		<comments>http://deathofagremmie.com/2010/04/03/jquery-ui-autocomplete-widget-caching/#comments</comments>
		<pubDate>Sat, 03 Apr 2010 21:30:54 +0000</pubDate>
		<dc:creator>gremmie</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[autocomplete]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[jquery-ui]]></category>
		<category><![CDATA[plugins]]></category>

		<guid isPermaLink="false">http://deathofagremmie.com/?p=349</guid>
		<description><![CDATA[I upgraded to the awesome new 1.4 version of jQuery recently in preparation for deploying another beta version of my site. Unfortunately the jquery-autocomplete plugin by Jörn Zaefferer doesn&#8217;t work with this new version. However, the equally awesome jQuery user interface library, jQuery UI, now has a new autocomplete widget that is based upon Jörn&#8217;s [...]]]></description>
			<content:encoded><![CDATA[<p>I upgraded to the awesome new 1.4 version of <a href="http://jquery.com/">jQuery</a> recently in preparation for deploying another beta version of my site. Unfortunately the <a href="http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/">jquery-autocomplete plugin by Jörn Zaefferer</a> doesn&#8217;t work with this new version. However, the equally awesome jQuery user interface library, <a href="http://jqueryui.com/">jQuery UI</a>, now has a new <a href="http://jqueryui.com/demos/autocomplete/">autocomplete widget</a> that is based upon Jörn&#8217;s design. The API isn&#8217;t quite the same, but after some reading you&#8217;ll find that most of the functionality is still there. However the new UI widget does not support caching requests, leaving that to the user to implement. This kind of surprised me, as I would think that would be a commonly requested feature. But perhaps the UI folks felt that everyone would have different caching requirements. There is <a href="http://jqueryui.com/demos/autocomplete/#remote-with-cache">one example on the UI site of how to cache</a>, but curiously it only caches the last request, which doesn&#8217;t seem all that useful to me.</p>
<p>In any event, after reading the documentation, it wasn&#8217;t hard to implement caching for my purposes. The following code simply caches the last 16 requests. Once the cache is full, it is flushed completely and ready to be filled again. Simple, crude, but hopefully somewhat effective.  I&#8217;m not a Javascript expert, but this will work for me. I hope someone else finds it useful and can use it as a starting point for their use-case.</p>
<pre>
<pre class="brush: javascript">
$(function() {
    var cache = {};
    var cacheSize = 0;
    $(&quot;#id_of_your_text_box&quot;).autocomplete({
        delay: 400,
        minLength: 2,
        source: function(request, response) {
            if (cache[request.term]) {
               response(cache[request.term]);
               return;
            }
            $.ajax({
                url: &quot;/your/url/goes/here/&quot;,
                type: &quot;GET&quot;,
                data: {
                    q: request.term,
                    limit: 10
                },
                dataType: &quot;json&quot;,
                success: function(data, textStatus) {
                    if (cacheSize &gt;= 16) {
                       cache = {};
                       cacheSize = 0;
                    }
                    cache[request.term] = data;
                    ++cacheSize;
                    response(data);
                },
                error: function(xhr, textStatus, ex) {
                    alert(&#039;Oops, an error occurred. &#039; + xhr.statusText + &#039; - &#039; +
                      xhr.responseText);
                }
            });
        }
    });
});
</pre>
</pre>
<p>The key to making this to work is to provide your own source function to the autocomplete widget. The widget will call this function when it needs data. Inside the function we simply check to see if request.term is already in our cache. If it is, then we simply return the cached result via the response callback function. If not, we have to make an ajax call to the server to retrieve the data.  When the ajax call completes, we store the result in our cache and then return it to the widget.</p>
<p>Again, this code simply stores the last 16 requests. When we run out of room we simply empty the cache and start over. A more sophisticated algorithm could use a different strategy like removing only the first item put in the cache or even the &#8220;least recently used&#8221; cache item. Break out your college computer science textbook on caching strategies and go nuts here.</p>
<p>The other feature this code doesn&#8217;t do is provide matching capabilities. For example, if the user has typed &#8220;foo&#8221; in the input box, this code will not return results for &#8220;bar foo&#8221;. If you need this, I suggest looking at the caching example on the UI site, or even at Jörn Zaefferer&#8217;s source code.</p>
]]></content:encoded>
			<wfw:commentRss>http://deathofagremmie.com/2010/04/03/jquery-ui-autocomplete-widget-caching/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Event Calendar: Submitting Events</title>
		<link>http://deathofagremmie.com/2009/03/08/event-calendar-submitting-events/</link>
		<comments>http://deathofagremmie.com/2009/03/08/event-calendar-submitting-events/#comments</comments>
		<pubDate>Sun, 08 Mar 2009 23:43:13 +0000</pubDate>
		<dc:creator>gremmie</dc:creator>
				<category><![CDATA[SG101 2.0]]></category>
		<category><![CDATA[calendar]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[gdata]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[jquery]]></category>

		<guid isPermaLink="false">http://deathofagremmie.com/?p=191</guid>
		<description><![CDATA[I&#8217;m continuing on in my experiment to leverage Google calendar for my site&#8217;s event calendar. I have built a form that allows users to submit new events subject to admin approval. I modeled the form after the GUI used by Google calendar. I also decided to used jQuery UI&#8217;s datepicker. jQuery UI just released version [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m continuing on in my experiment to leverage Google calendar for my site&#8217;s event calendar. I have built a form that allows users to submit new events subject to admin approval. I modeled the form after the GUI used by Google calendar. I also decided to used <a href="http://jqueryui.com/demos/datepicker/">jQuery UI&#8217;s datepicker</a>. jQuery UI just released <a href="http://blog.jqueryui.com/2009/03/jquery-ui-17/">version 1.7</a>, which is compatible with jQuery 1.3. After reading this <a href="http://encosia.com/2008/12/10/3-reasons-why-you-should-let-google-host-jquery-for-you/">blog entry</a>, I&#8217;ve decided to try letting Google host jQuery for me. Google is also hosting the standard jQuery UI themes CSS files, so this seems awfully convenient.</p>
<p>Check out the screenshot of the event submit form:</p>
<p><a href="http://deathofagremmie.com/wp-content/uploads/2009/03/date_picker.png"><img class="alignnone size-medium wp-image-192" title="Event Form with jQuery UI Datepicker" src="http://deathofagremmie.com/wp-content/uploads/2009/03/date_picker-300x187.png" alt="Event Form with jQuery UI Datepicker" width="300" height="187" /></a></p>
<p>The datepicker is very useful, and will add a lot to the site, I think. My existing PHP application is using a much less elegant form. So far this form only handles non-repeating events. I will add support for those later.</p>
<p>I then turned my attention to the backend: how to synchronize my event models with Google calendar? It turns out there is already a Django application designed for this purpose: <a href="http://code.google.com/p/django-gcal/">django-cal</a>. It is designed in a very generic way and seems to be very well done. I decided I didn&#8217;t need its full functionality, but I did reference the source code when designing my back end. So a big thank-you to the creators of django-cal!</p>
<p>At this point I have enough functionality to add events and get them on the Google calendar. I have coded, but not tested, update and delete functionality. I am using the <a href="http://code.google.com/apis/calendar/docs/1.0/developers_guide_python.html#batch">batch facility of the gdata API</a>. In a typical work cycle, I need to approve anywhere from 1 to 7 or so events at once. This isn&#8217;t a lot of events, but I think doing it in a batch mode will speed a synchronization cycle up noticeably.</p>
<p>I store a &#8220;status&#8221; field in each event that keeps track of the event status: new, new and approved, edited, edited and approved, deleted, deleted and approved, and &#8220;on calendar&#8221;. When a user adds an event it becomes &#8220;new&#8221;. An admin reviews the event, and marks it &#8220;new and approved&#8221;. After synchronization, the status changes to &#8220;on calendar&#8221;. Likewise, my plan is to let users edit and request deletes for any events that they submit. My custom admin synchronization view will the perform the operations on all events with the &#8220;&#8230; and approved&#8221; status in a batch mode.</p>
<p>The custom admin view I wrote about in the last entry is also working well. I overrode the base django.contrib.admin template, so it looks well-integrated with the rest of the admin part of the site. I also figured out how to use the breadcrumbs for admin navigation, and noticed the base template will display a variable called &#8220;messages&#8221; with the nice green checkmark, if present. Likewise, the admin CSS has a class called &#8220;errorlist&#8221; for displaying errors. So it is quite easy to make a pretty professional looking custom admin screen by studying the base template and admin CSS files. I will post a screenshot of my custom admin view after I get some more events through the work cycle in a future blog post.</p>
<p>The Python gdata API isn&#8217;t as clumsy as I first thought it was. I was able to wrap it in a Calendar class to make it easier to use. If you need to update or delete an event in batch mode, it seems like you have to retrieve the event from Google first. I struggled against this initially, since I was storing away the &#8220;edit link&#8221; for each event I create. I was hoping to use the edit link to avoid another trip to Google for an update. But after searching the <a href="http://groups.google.com/group/google-calendar-help-dataapi">Google Calendar Data API group</a> archives, it seems that is indeed recommended to retrieve any event prior to an update or delete, at least in batch mode.</p>
<p>This seems to be working out well so far. After I code and test the other operations I will blog with more details.</p>
]]></content:encoded>
			<wfw:commentRss>http://deathofagremmie.com/2009/03/08/event-calendar-submitting-events/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Member Map Part 2</title>
		<link>http://deathofagremmie.com/2009/02/14/member-map-part-2/</link>
		<comments>http://deathofagremmie.com/2009/02/14/member-map-part-2/#comments</comments>
		<pubDate>Sat, 14 Feb 2009 22:10:30 +0000</pubDate>
		<dc:creator>gremmie</dc:creator>
				<category><![CDATA[SG101 2.0]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[markitup]]></category>
		<category><![CDATA[membermap]]></category>

		<guid isPermaLink="false">http://deathofagremmie.com/?p=157</guid>
		<description><![CDATA[I&#8217;ve gotten the Member Map application ported to my Django powered site now. This was pretty straightforward and a lot of fun because of the Javascript aspect. Let me address the points I made in part 1 of this post, below. Leveraging jQuery. I did make use of jQuery for the client side Javascript. This [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve gotten the Member Map application ported to my Django powered site now. This was pretty straightforward and a lot of fun because of the Javascript aspect. Let me address the points I made in <a href="http://deathofagremmie.com/2009/02/06/member-map-part-1/">part 1</a> of this post, below.</p>
<ol>
<li>Leveraging <a href="http://jquery.com/">jQuery</a>. I did make use of jQuery for the client side Javascript. This really did simplify things. In the old version of the code, I had a 282 line Javascript file. The new version weighs in at 161 lines, which is 57% the size of the earlier version.</li>
<li>Use of JSON encoder library. It turns out that Django includes a version of the <a href="http://www.undefined.org/python/">simplejson</a> library already. This is used in Django&#8217;s serialization feature. I probably could have used this feature, but I didn&#8217;t need to serialize an entire model, and wanted to rename some of the fields. So I simply did a &#8220;import django.utils.simplejson as json&#8221; and went to town. My server side code was now much simpler than the previous version where I encoded by hand.</li>
<li>I did continue to use the <a href="http://markitup.jaysalvat.com/home/">markItUp</a>! editor to let users edit their message to go along with their location. I learned that the size of the editor is easily adjusted through the use of CSS. So I was able to override the default size of the editor by including my own CSS style sheet below the default sheet.</li>
<li>Reducing complexity. Here is where it got interesting. I decided to add a &#8220;json&#8221; field to the MapEntry model. This field is populated whenever a model object is saved by providing my own save() method. Inside my save() I use Django&#8217;s template render_to_string() to render the HTML  message that appears in the pop-up balloon on the map. This consists  of the user&#8217;s avatar followed by a message that is marked up in <a href="http://daringfireball.net/projects/markdown/">Markdown</a>. I then build a JSON representation of the MapEntry using Django&#8217;s simplejson. This representation consists of the following fields: name, location, latitude, longitude, and the HTML message. By pre-saving all of this information, it makes it much more efficient to retrieve 200+ users&#8217; information when the map is first loaded. In the view function, I make use of the values_list() queryset method to retrieve all the json fields. I don&#8217;t need the ORM to convert each MapEntry to an actual Python object.</li>
</ol>
<p>Another thing I did differently for the port was when a user updates or adds their position on the map, I don&#8217;t reload the entire map. I just adjust their information to reduce bandwidth with an AJAX post. I&#8217;m not sure why I didn&#8217;t do this before, perhaps because it was to tedious to do without jQuery. It was just easier to reload the whole thing.</p>
<p>This was all well and cool, doing all this pre-computation, but what happens when a user changes her avatar? Her entry on the map will likely have a broken graphic. No problem. Here I took advantage of <a href="http://docs.djangoproject.com/en/dev/topics/signals/">Django&#8217;s signals</a>. I attached a signal handler to listen for changes to the UserProfile model. Whenever the UserProfile is saved, my signal handler runs and re-saves the corresponding MapEntry to regenerate the JSON. Very slick.</p>
<p>And finally, another thing that I learned about Django was that simple_tags can have defaulted arguments. I added an argument to my avatar tag so that I could apply CSS to the generated HTML img tag if needed. In the Member Map pop-up balloon, it looked nicer if the avatar was floated to the left.</p>
<p>So all and all, again, I&#8217;m very happy with how easy Django makes writing an application like this. Using jQuery was also a big productivity booster.</p>
<p>Here are some screenshots. You can also see the new <a href="http://www.blueprintcss.org/">Blueprints CSS</a> in action as well.</p>
<div id="attachment_159" class="wp-caption alignnone" style="width: 310px"><a href="http://deathofagremmie.com/wp-content/uploads/2009/02/member_map1.png"><img class="size-medium wp-image-159" title="member_map1" src="http://deathofagremmie.com/wp-content/uploads/2009/02/member_map1-300x187.png" alt="Member Map" width="300" height="187" /></a><p class="wp-caption-text">Member Map 1</p></div>
<div id="attachment_160" class="wp-caption alignnone" style="width: 310px"><a href="http://deathofagremmie.com/wp-content/uploads/2009/02/member_map2.png"><img class="size-medium wp-image-160" title="member_map2" src="http://deathofagremmie.com/wp-content/uploads/2009/02/member_map2-300x187.png" alt="Member Map" width="300" height="187" /></a><p class="wp-caption-text">Member Map 2</p></div>
]]></content:encoded>
			<wfw:commentRss>http://deathofagremmie.com/2009/02/14/member-map-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Member Map Part 1</title>
		<link>http://deathofagremmie.com/2009/02/06/member-map-part-1/</link>
		<comments>http://deathofagremmie.com/2009/02/06/member-map-part-1/#comments</comments>
		<pubDate>Sat, 07 Feb 2009 00:39:44 +0000</pubDate>
		<dc:creator>gremmie</dc:creator>
				<category><![CDATA[SG101 2.0]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[markitup]]></category>
		<category><![CDATA[membermap]]></category>

		<guid isPermaLink="false">http://deathofagremmie.com/?p=154</guid>
		<description><![CDATA[I started working on the Member Map application last weekend. This will be a port of the PHP-Nuke module I wrote, which you can find here. In a nutshell, this application displays a Google Map on your site, and allows site members to place markers representing their location on the map. If you click on [...]]]></description>
			<content:encoded><![CDATA[<p>I started working on the Member Map application last weekend. This will be a port of the PHP-Nuke module I wrote, which you can find <a title="Member Map for PHP-Nuke" href="https://sourceforge.net/projects/membermapnuke/">here</a>. In a nutshell, this application displays a <a title="Google Maps API" href="http://code.google.com/apis/maps/">Google Map</a> on your site, and allows site members to place markers representing their location on the map. If you click on a marker, a balloon pops up which displays the user&#8217;s avatar, a link to their profile, and a short message that they have typed previously. The Nuke version of this application is a big hit on the current site, and last time I checked, we had over 220 members on the map.</p>
<p>The existing application consists of the PHP-Nuke code, written in PHP, on the server side, and a fair amount of Javascript on the client side. All interaction with Google Maps is done through the Javascript code.</p>
<p>For the port to Django, I want to do a couple of things different.</p>
<ol>
<li>Leverage <a title="jQuery" href="http://jquery.com/">jQuery</a> in the client-side Javascript. The existing version was written before I was aware of Javascript libraries, and it is all &#8220;vanilla&#8221; Javascript. By using jQuery, the amount of code I have to write will be much smaller. And by taking advantage of jQuery, it should be more portable and browser robust.</li>
<li>On the server side, the existing application did not use any <a title="JSON" href="http://json.org">JSON</a> encoder library. I simply constructed the JSON by hand. I can&#8217;t remember why I did this. I think it was partly because I didn&#8217;t know any better, and partly because the PHP version I had on the original hosting server may not have had it. I will explore what Python and/or Django options exist.</li>
<li>I&#8217;ll leverage the <a href="http://markitup.jaysalvat.com/home/">markItUp</a>! editor that I&#8217;ve been using for comments to let the users enter <a href="http://daringfireball.net/projects/markdown/">Markdown</a> comments. I&#8217;ve used this in the last couple of apps I&#8217;ve written and it will be easy to implement. The only concern with doing this is the user may be tempted to write a lot more than in the older app, and it may make for large &#8220;balloons&#8221;. We&#8217;ll see.</li>
<li>I want to make the new application less database and computationally intense than the older version. I don&#8217;t have any problems running the current version with 200 users, but I think I could do it a bit smarter this time around. This means I am going to try to save marked up and JSON encoded strings in the database rather than compute them on every page load. I am also wary of instantiating 200+ map entry model objects with the Django ORM (albeit in a loop) just to generate the JSON. Luckily Django lets you use raw SQL when needed. But in this case I think a simple use of the values() method will really be useful here.</li>
</ol>
<p>So, those are my going in goals. I&#8217;ve already started working on this, and I&#8217;ve learned a lot so far. In the next post or two I will detail the progress and what problems I ran into.</p>
]]></content:encoded>
			<wfw:commentRss>http://deathofagremmie.com/2009/02/06/member-map-part-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Anatomy of a Shoutbox; Part 6 &#8211; Conclusion: Delete In Place</title>
		<link>http://deathofagremmie.com/2008/12/24/anatomy-of-a-shoutbox-part-6-conclusion-delete-in-place/</link>
		<comments>http://deathofagremmie.com/2008/12/24/anatomy-of-a-shoutbox-part-6-conclusion-delete-in-place/#comments</comments>
		<pubDate>Wed, 24 Dec 2008 06:55:35 +0000</pubDate>
		<dc:creator>gremmie</dc:creator>
				<category><![CDATA[SG101 2.0]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[sg101]]></category>
		<category><![CDATA[shoutbox]]></category>

		<guid isPermaLink="false">http://deathofagremmie.com/?p=106</guid>
		<description><![CDATA[Welcome to part 6, the final post on the development of the new shoutbox. In case you missed the earlier parts, here they are: part 1, part 2, part 3, part 4, part 5. Here we are going to discuss the final feature: deleting a shout in place off the shout history page. The idea [...]]]></description>
			<content:encoded><![CDATA[<p>Welcome to part 6, the final post on the development of the new shoutbox. In case you missed the earlier parts, here they are:  <a href="../2008/12/02/anatomy-of-a-shoutbox-part-1-requirements/">part 1</a>, <a href="../2008/12/07/anatomy-of-a-shoutbox-part-2-model-and-admin/">part 2</a>, <a href="../2008/12/08/anatomy-of-a-shoutbox-part-3-view-template/">part 3</a>, <a href="../2008/12/14/anatomy-of-a-shoutbox-part-4-smilies/">part 4</a>, <a href="http://deathofagremmie.com/2008/12/20/anatomy-of-a-shoutbox-part-5-edit-in-place/">part 5</a>. Here we are going to discuss the final feature: deleting a shout in place off the shout history page.</p>
<p>The idea here is to add a delete link on the shouts that the user created as she is viewing the shout history. We will use the<a href="http://jquery.com/"> jQuery library</a> to dynamically add click handlers to these links. The click handler functions will make an AJAX post to the server and request that a shout be deleted. The server view function will verify that the user sending the request actually created the shout in question. If so, the shout will be deleted from the database and then a response sent back to the client. This response will be then be used by our Javascript to hide the deleted shout. Thus we will have deleted a shout without refreshing the entire page.</p>
<p>The first thing I did was modify the shout history template to output a link underneath a shout if the current user authored that shout. This template (shoutbox/view.html) was presented in <a href="http://deathofagremmie.com/2008/12/20/anatomy-of-a-shoutbox-part-5-edit-in-place/">part 5</a>. I&#8217;ll just show the new code inside the for loop that adds these links:</p>
<pre class="brush: python">    {% ifequal user shout.user %}
        | &lt;a href=&quot;#&quot; class=&quot;shout-del&quot; id=&quot;shout-del-{{ shout.id }}&quot;&gt;Delete&lt;/a&gt;
    {% endifequal %}
</pre>
<p>The important thing to note is our links don&#8217;t go anywhere, they have the CSS class &#8220;shout-del&#8221;, and we give them an id of &#8220;shout-del-xx&#8221; where xx is the id of the shout from the database.</p>
<p>I then modified the shoutbox_app.js document ready function to look for all links that have the class &#8220;shout-del&#8221; and I attach a click function to them. The click function asks the user via the Javascript confirm() function if they really want to delete the shout. If they confirm, I then use javascript regular expression magic to pull out the shout&#8217;s  primary key from the &lt;a&gt; tag&#8217;s id, and send a POST request to the server to delete that shout. The relevant snippet is shown below.</p>
<pre class="brush: javascript">     $(&#039;.shout-del&#039;).click(function () {
         if (confirm(&#039;Really delete this shout?&#039;)) {
             id = this.id;
             if (id.match(/shout-del-(\d+)/)) {
                $.post(&#039;/shout/delete/&#039;, { id : RegExp.$1 }, function(id) {
                    id = &#039;#shout-del-&#039; + id
                    $(id).parents(&#039;tr&#039;).hide();
                    $(&#039;div.shoutbox-history table tr:visible:even&#039;).removeClass(&#039;odd&#039;);
                    $(&#039;div.shoutbox-history table tr:visible:odd&#039;).addClass(&#039;odd&#039;);
                    }, &#039;text&#039;);
             }
         }
         return false;
     });
</pre>
<p>We are posting to the URL /shout/delete, and passing along the shout ID in the id parameter. We also supply a callback function that gets called when the delete happens successfully. More on that later. Finally the click function returns false so that the default action of clicking on a link is not executed.</p>
<p>We now turn to the server, and examine the view function that handles the URL /shout/delete. You&#8217;ll notice similar code as presented in part 5&#8242;s edit view function. This time I have discovered Django&#8217;s classes that derive from HttpResponse, and I use them in the error cases.</p>
<pre class="brush: python">def delete(request):
    &quot;&quot;&quot;This view deletes a shout. It is called by AJAX from the shoutbox history view.&quot;&quot;&quot;
    if request.user.is_authenticated():
        id = request.POST.get(&#039;id&#039;, None)
        if id is None:
            return HttpResponseBadRequest()
        try:
            shout = Shout.objects.get(pk=id)
        except Shout.DoesNotExist:
            return HttpResponseBadRequest()
        if request.user != shout.user:
            return HttpResponseForbidden()
        shout.delete()
        return HttpResponse(id)

    return HttpResponseForbidden()
</pre>
<p>We check to see if the user is authenticated before proceeding. We then retrieve the shout ID from the POST QueryDict. From the ID we retrieve the actual shout from the database using the Django ORM. And finally, if and only if the user making the request is the same user that created the shout, we delete it. If all of that went well, we send back a positive HTTP response that includes the ID of the shout that was deleted.</p>
<p>Now refer back to our Javascript, posted above.  In the callback function we receive the ID as text from the server. We then convert the numeric ID to the CSS ID and use jQuery to select the parent &lt;tr&gt; of our &lt;a&gt; tag, and hide it. Wow! That was a very compact way to do all of that work.</p>
<p>The remaining logic takes care of the table presentation. I used CSS to style every other row in the shout history table with a class called &#8220;odd&#8221;. Since we just hid one row, that scheme got messed up. Luckily, the compact notation of jQuery again comes to the rescue and we can quickly restyle all odd visible rows with our class &#8220;odd&#8221;, taking care to remove the style from the new even rows first.</p>
<p>And there we have a shoutbox written in Django and Javascript for the new version of my site. Not bad for my first attempt, and it certainly was much easier than I imagined. All in all, there is a lot less Python code than the existing PHP code, and we even get neat-o AJAX&#8217;y features that the old one didn&#8217;t have. So I think we will go with this for the first pass of the site. Once I get more comfortable with jQuery I may decide to redo the scrolling itself with it. We could also look into posting shouts to the database without a complete page refresh. There is always something to go back and refactor as you learn more about your tools and best practices. I hope someone found this quick series of posts useful.</p>
]]></content:encoded>
			<wfw:commentRss>http://deathofagremmie.com/2008/12/24/anatomy-of-a-shoutbox-part-6-conclusion-delete-in-place/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Anatomy of a Shoutbox; Part 5 &#8211; Edit In Place</title>
		<link>http://deathofagremmie.com/2008/12/20/anatomy-of-a-shoutbox-part-5-edit-in-place/</link>
		<comments>http://deathofagremmie.com/2008/12/20/anatomy-of-a-shoutbox-part-5-edit-in-place/#comments</comments>
		<pubDate>Sun, 21 Dec 2008 05:25:42 +0000</pubDate>
		<dc:creator>gremmie</dc:creator>
				<category><![CDATA[SG101 2.0]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[jeditable]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[sg101]]></category>
		<category><![CDATA[shoutbox]]></category>

		<guid isPermaLink="false">http://deathofagremmie.com/?p=93</guid>
		<description><![CDATA[In this post I&#8217;ll show how I got an edit-in-place feature working with the shoutbox. In case you missed the earlier parts, here they are: part 1, part 2, part 3, part 4. The first thing I did was create a &#8220;shout history&#8221; view. This allows you to view the shouts in a full page [...]]]></description>
			<content:encoded><![CDATA[<p>In this post I&#8217;ll show how I got an edit-in-place feature working with the shoutbox. In case you missed the earlier parts, here they are:  <a href="../2008/12/02/anatomy-of-a-shoutbox-part-1-requirements/">part 1</a>, <a href="../2008/12/07/anatomy-of-a-shoutbox-part-2-model-and-admin/">part 2</a>, <a href="../2008/12/08/anatomy-of-a-shoutbox-part-3-view-template/">part 3</a>, <a href="http://deathofagremmie.com/2008/12/14/anatomy-of-a-shoutbox-part-4-smilies/">part 4</a>.</p>
<p>The first thing I did was create a &#8220;shout history&#8221; view. This allows you to view the shouts in a full page context. In my views.py file I created this:</p>
<pre class="brush: python">def view(request, page=1):
    &quot;&quot;&quot;This view allows one to view the shoutbox history.&quot;&quot;&quot;
    paginator = DiggPaginator(Shout.objects.all(), SHOUTS_PER_PAGE, body=5, tail=3, margin=3, padding=2)
    try:
        the_page = paginator.page(int(page))
    except InvalidPage:
        raise Http404

    return render_to_response(&#039;shoutbox/view.html&#039;, {
        &#039;page&#039;: the_page,
        },
        context_instance = RequestContext(request))</pre>
<p>Here I am using the <a href="http://www.djangosnippets.org/snippets/773/">DiggPaginator</a>, which is a very nice paginator that gives you &#8220;<a title="Digg.com" href="http://digg.com/">Digg</a>-style&#8221; pagination. I then pass the page object to the template which does most of the interesting work.</p>
<p>In order to get the edit-in-place funcitonality to work, I am leveraging the <a title="Jeditable" href="http://www.appelsiini.net/projects/jeditable">Jeditable</a> <a title="jQuery" href="http://jquery.com/">jQuery</a> plugin. In order to use this plugin, in my shoutbox/view.html template I include &lt;script&gt; tags to bring in both the jQuery javascript library and the Jeditable plugin code. I then loop over the shouts for the page and display them along with the avatar and links to the user that made the shout. You&#8217;ll also see me using the <strong>smilify</strong> fiilter I discussed in part 4.</p>
<p><span id="more-93"></span>
<pre class="brush: html">{% extends &#039;base.html&#039; %}
&lt;pre&gt;{% load avatar_tags %}
{% load smiley_tags %}
{% block custom_css %}
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{{ MEDIA_URL }}css/shoutbox_app.css&quot; /&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;{{ MEDIA_URL }}css/pagination.css&quot; /&gt;
{% endblock %}
{% block custom_js %}
&lt;script type=&quot;text/javascript&quot; src=&quot;{{ MEDIA_URL }}js/jquery-1.2.6.min.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;{{ MEDIA_URL }}js/jquery.jeditable.mini.js&quot;&gt;&lt;/script&gt;
&lt;script type=&quot;text/javascript&quot; src=&quot;{{ MEDIA_URL }}js/shoutbox_app.js&quot;&gt;&lt;/script&gt;
{% endblock %}
{% block title %}Shout History{% endblock %}
{% block content %}
&lt;h2&gt;Shout History&lt;/h2&gt;
{% if page.object_list %}
{% include &#039;core/pagination.html&#039; %}

&lt;div class=&quot;shoutbox-history&quot;&gt;
&lt;table&gt;
{% for shout in page.object_list %}
&lt;tr class=&quot;{% cycle &#039;even&#039; &#039;odd&#039; %}&quot;&gt;
&lt;th&gt;
&lt;a href=&quot;{% url bio-view_profile username=shout.user.username %}&quot;&gt;{% avatar shout.user %}&lt;/a&gt;
&lt;a href=&quot;{% url bio-view_profile username=shout.user.username %}&quot;&gt;{{ shout.user.username }}&lt;/a&gt;
&lt;/th&gt;
&lt;td&gt;
&lt;div {% ifequal user shout.user %}class=&quot;edit&quot; id=&quot;shout-{{ shout.id }}&quot;{% endifequal %}&gt;{{ shout.shout|smilify|urlize }}&lt;/div&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;span class=&quot;date&quot;&gt;{{ shout.shout_date|date:&quot;D M d Y H:i:s&quot; }}&lt;/span&gt;
&lt;/td&gt;
&lt;/tr&gt;
{% endfor %}
&lt;/table&gt;
&lt;/div&gt;

{% include &#039;core/pagination.html&#039; %}
{% else %}
&lt;p&gt;No shouts at this time.&lt;/p&gt;
{% endif %}
{% endblock %}</pre>
<p>In order to get the edit-in-place working, I need to enclose the shouts the user can edit in a special div with the class &#8220;edit&#8221;. I do this inside the loop, adding the class attribute if the current user matches the shout user. I also give each of these divs an id of &#8220;shout-xx&#8221; where xx is the id of the shout being displayed. I thought about just using the shout id as the id, but thought this might clash with other page elements, so I prefixed it with &#8220;shout-&#8221;. This adds a tiny bit of work back in the view code, but not much.</p>
<p>Now that I have &#8220;tagged&#8221; the divs the user can edit, I configure the plugin with the following bit of jQuery magic in the shoutbox_app.js file:</p>
<pre class="brush: javascript">$(document).ready(function() {
     $(&#039;.edit&#039;).editable(&#039;/shout/edit/&#039;, {
         loadurl : &#039;/shout/text/&#039;,
         indicator : &#039;Saving...&#039;,
         tooltip   : &#039;Click to edit your shout...&#039;,
         submit : &#039;OK&#039;,
         cancel : &#039;Cancel&#039;
     });
 });
</pre>
<p>This code instructs jQuery to find all elements with the class &#8220;edit&#8221; and call the editable() function on them. The first parameter to editable() is the URL to send the edited text to when the user hits enter or clicks OK. In our case, this is /shout/edit. I&#8217;ll show this view in a minute. The next parameter to the editable() function is a javascript options object with several interesting options:</p>
<ul>
<li>loadurl &#8211; this is the URL that Jeditable will call to retrieve the text to edit. Recall that our text is <em>smilified</em>, so we don&#8217;t want the user edited the XHTML markup. We want the user to edit the original shout text.</li>
<li>indicator &#8211; after saving the edit, the Jeditable plugin will display this text to give the user feedback something is happening while it posts the edited text to the server.</li>
<li>tooltip &#8211; Jeditable will display this text as a tooltip when the user hovers their mouse over the div.</li>
<li>submit &#8211; Provides the text for a submit button.</li>
<li>cancel &#8211; Provides the text for a cancel button.</li>
</ul>
<p>Holy crap that was easy. Now all we need to do is provide those 2 view functions. One to receive and save the edited text, and one that is called to retrieve the original shout. First, here is the /shout/view view function that retrieves the original text.</p>
<pre class="brush: python">
shout_id_re = re.compile(r&#039;shout-(\d+)&#039;)
def text(request):
    &quot;&quot;&quot;This view function retrieves the text of a shout; it is used in the in-place
    editing of shouts on the shoutbox history view.&quot;&quot;&quot;
    if request.user.is_authenticated():
        m = shout_id_re.match(request.GET.get(&#039;id&#039;, &#039;&#039;))
        if m is None:
            return HttpResponse(u&#039;&#039;)
        try:
            shout = Shout.objects.get(pk=m.group(1))
        except Shout.DoesNotExist:
            return HttpResponse(u&#039;&#039;)
        return HttpResponse(shout.shout)

    return HttpResponse(u&#039;&#039;)</pre>
<p>Jeditable is expecting that this function return the raw shout, not XHTML markup. It will pass the id of the div the user wants to edit as a GET parameter. First we check to see if the user is authenticated as a minimal check. Note that we don&#8217;t use the login_required decorator, because that would redirect the Jeditable request to the login page if the user wasn&#8217;t logged in. We then use a regular expression to pull the numeric part out of the div&#8217;s id (recall that it comes in the form <em>shout-xx</em>). With the id in hand we simply query the shout object using the Django ORM. We then return the shout text as an HttpResponse.</p>
<p>Finally here is the view function that Jeditable posts the edited data to.</p>
<pre class="brush: python">def edit(request):
    &quot;&quot;&quot;This view accepts a shoutbox edit from the shoutbox history view.&quot;&quot;&quot;
    if request.user.is_authenticated():
        m = shout_id_re.match(request.POST.get(&#039;id&#039;, &#039;&#039;))
        if m is None:
            return HttpResponse(u&#039;&#039;)
        try:
            shout = Shout.objects.get(pk=m.group(1))
        except Shout.DoesNotExist:
            return HttpResponse(u&#039;&#039;)
        if request.user != shout.user:
            return HttpResponse(u&#039;&#039;)
        new_shout = request.POST.get(&#039;value&#039;, &#039;&#039;).strip()
        if new_shout == &#039;&#039;:
            return HttpResponse(u&#039;&#039;)
        shout.shout = new_shout
        shout.save()
        return render_to_response(&#039;shoutbox/render_shout.html&#039;, {
            &#039;shout&#039;: shout,
            },
            context_instance = RequestContext(request))

    return HttpResponse(u&#039;&#039;)</pre>
<p>This function does do a lot of error checking, so it looks more complicated than it really is. This time Jeditable is expecting us to return XHTML markup, and it passes 2 things to us as POST parameters: the div id appears as &#8220;id&#8221; and the new text appears as &#8220;value&#8221;.</p>
<p>In short, this function checks to make sure the user posting the new shout text is the same user that created the shout in the first place. If this is the case, it simply updates the shout text and saves it back to the database. We then render a template to return the new XHTML markup for the div. Remember that we have to smilify the text, so our template looks like this:</p>
<pre class="brush: html">{% load smiley_tags %}
{{ shout.shout|smilify|urlize }}
</pre>
<p>We load our smiley tags discussed in part 4, and filter the shout through our smilify filter and the Django urlize filter.</p>
<p>One thing that is very useful to add at this point is some CSS for our editable div tags. I added a :hover rule that changed the background color and mouse cursor. This provides additional visual cues to the user that the text can edited when the mouse is hovered over it.</p>
<p>And that is basically it! Here is a screen shot of a user editing a shout in-place on the shout history page.</p>
<div id="attachment_100" class="wp-caption alignnone" style="width: 598px"><img class="size-full wp-image-100" title="Edit-in-place in action" src="http://deathofagremmie.com/wp-content/uploads/2008/12/edit-in-place.png" alt="Edit-in-place in action" width="588" height="410" /><p class="wp-caption-text">Edit-in-place in action</p></div>
<p>I plan on adding one more blog post about the shoutbox. All we have left to do is an in-place delete of a shout on the shout history page. Stay tuned for part 6.</p>
]]></content:encoded>
			<wfw:commentRss>http://deathofagremmie.com/2008/12/20/anatomy-of-a-shoutbox-part-5-edit-in-place/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
