<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-21222625</id><updated>2011-11-27T20:41:42.781-05:00</updated><category term='PHP'/><category term='javascript'/><category term='XSL'/><category term='Linux'/><category term='SVG'/><category term='web development'/><category term='XML'/><category term='mobile phone'/><category term='podcasting'/><category term='Ruby on Rails'/><category term='Java'/><category term='Ajax'/><category term='prototype'/><title type='text'>The Pothoven Post</title><subtitle type='html'>Information, experience and learning of Steven Pothoven -- usually technology related.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.pothoven.net/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>83</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-21222625.post-1242110260056646022</id><published>2009-08-25T08:38:00.007-04:00</published><updated>2009-08-27T10:48:06.899-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'>PeepNotes - my Rails Rumble 2009 entry</title><content type='html'>&lt;a href="http://www.peepnote.com"&gt;&lt;img style="float:left; margin:0px 20px 10px 0px;cursor:pointer; cursor:hand;width: 143px; height: 53px;" src="http://3.bp.blogspot.com/_l-kZv4tdloI/SpPkEf_gb7I/AAAAAAAAAIc/lj4v5DwAxTw/s400/peepnote-logo.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5373889546341609394" /&gt;&lt;/a&gt;&lt;p style="padding-top: 8px"&gt;This past weekend I took part in &lt;a href="http://r09.railsrumble.com/"&gt;Rails Rumble 2009&lt;/a&gt; and help create a site called &lt;a href="http://www.peepnote.com"&gt;PeepNote&lt;/a&gt;.&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;If you're not familiar with Rails Rumble, I'll borrow the introduction from their web site:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The Rails Rumble is a 48 hour web application development competition. As a contestant, your team gets one weekend to design, develop, and deploy the best web property that you can, using the awesome power of &lt;a href="http://ruby-lang.org/"&gt;Ruby&lt;/a&gt; and &lt;a href="http://rubyonrails.com/"&gt;Rails&lt;/a&gt;.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Two of my teammates have written up nice articles on our experience, so I encourage you to read them:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.brianburridge.com/2009/08/24/rails-rumble-09-is-over/"&gt;Rails Rumble 09 is over&lt;/a&gt; by Brian Burridge&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://thevisualclick.com/blog/2009/08/48-hours-the-creation-of-peepnote/"&gt;48 Hours &amp; The Creation of PeepNote&lt;/a&gt; by Josh Hemsley&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Additionally, you can find out more information on PeepNote and recent developments on the &lt;a href="http://blog.peepnote.com/"&gt;PeepNote blog&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In the meantime, here is the promotional video:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center"&gt;&lt;br /&gt;&lt;object width="480" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/iefKbymUM2k&amp;hl=en&amp;fs=1&amp;rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/iefKbymUM2k&amp;hl=en&amp;fs=1&amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="480" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Other articles on PeepNote:&lt;br /&gt;&lt;a href="http://designerscouch.org/show_news/93/rails-rumble-competition-peepnote.html/"&gt;Rails Rumble Competition: Peepnote &lt;/a&gt; (Designer's Couch)&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;If you like PeepNote, &lt;a href="http://thevisualclick.com/peepnote/"&gt;PLEASE VOTE FOR US!&lt;/a&gt;&lt;/h3&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-1242110260056646022?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://thevisualclick.com/peepnote/' title='PeepNotes - my Rails Rumble 2009 entry'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/1242110260056646022/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=1242110260056646022' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1242110260056646022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1242110260056646022'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2009/08/peepnotes-my-rails-rumble-2009-entry.html' title='PeepNotes - my Rails Rumble 2009 entry'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_l-kZv4tdloI/SpPkEf_gb7I/AAAAAAAAAIc/lj4v5DwAxTw/s72-c/peepnote-logo.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-8423460613133180319</id><published>2009-08-13T10:44:00.007-04:00</published><updated>2009-08-13T11:40:58.543-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><title type='text'>Managing Tags in Rails</title><content type='html'>In this article I'm going to document a small piece of the administration component of the &lt;a href="http://www.rubyrailsreview.com/"&gt;Ruby Rails Review&lt;/a&gt; that I wrote.  On &lt;a href="http://www.rubyrailsreview.com/"&gt;Ruby Rails Review&lt;/a&gt; we kept a collection of interesting articles regarding Ruby on Rails, and these articles were tagged with various tags so that if you went to &lt;a href="http://www.rubyrailsreview.com/articles/by_date"&gt;browse the articles&lt;/a&gt; you could filter them by the various tags.  &lt;br /&gt;&lt;br /&gt;Of course, there's no reason to implement that whole tagging system ourselves since there are plug-ins for that.  To make the articles taggable we used the &lt;a href="http://www.intridea.com/2007/12/4/announcing-acts_as_taggable_on"&gt;acts_as_taggable_on&lt;/a&gt; plug-in.  This article just gives a method for easily editing the tags for a given article which works much like the Blogger labeling I used when writing this article.&lt;br /&gt;&lt;br /&gt;For Ruby Rails Review, the article editor form is shown below, though I've dimmed out everything but the tagging sections:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_l-kZv4tdloI/SoQsgjUn54I/AAAAAAAAAIM/2iMGHgSU5Og/s1600-h/ArticleEditForm.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 204px;" src="http://4.bp.blogspot.com/_l-kZv4tdloI/SoQsgjUn54I/AAAAAAAAAIM/2iMGHgSU5Og/s400/ArticleEditForm.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5369465593481652098" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As you can see, there is a text field where you can type in tags in a comma separated list, as well a list of all currently defined tags.  Selected tags are shown in blue and underlined to give a visual indication that they are selected, but all tags are clickable to either select or deselect them.  As tags from the list at the bottom are selected or deselected, they are added or removed from the text field.  If you enter tags in the text field they are either selected or added (or deselected if you remove a tag) from the tag list below.&lt;br /&gt;&lt;br /&gt;So, how do we do this?&lt;br /&gt;&lt;br /&gt;First, the form code looks like this:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;&amp;lt;% form_for @article_detail, :url =&amp;gt; { :action =&amp;gt; "update", :id =&amp;gt; @article_detail.article_id } do |f| %&amp;gt;&lt;br /&gt;   ...&lt;br /&gt;   &amp;lt;%= f.text_field :tag_list, :onchange =&amp;gt; "updateTagList(this.value);" %&amp;gt;&lt;br /&gt;   ...&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;br /&gt;&amp;lt;div id="tags" style="margin-top:30px;"&amp;gt;&lt;br /&gt;&amp;lt;% @tags.each do |tag|%&amp;gt;&lt;br /&gt;&amp;lt;div class="tag&amp;lt;%= ' selectedTag' if @article_detail.tag_list.index(tag.name) %&amp;gt;" onclick="selectTag(this);"&amp;gt;&amp;lt;%= tag.name %&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;% end %&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You'll also need a little bit of CSS added to make the selected tags stand out (added to your main.css).  I happened to make them blue and underlined, but you can make them highlighted or whatever you please:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;.selectedTag {&lt;br /&gt;      color: #247CD4;&lt;br /&gt;      text-decoration: underline;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Finally, add the necessary JavaScript in either your application.js or a separate JavaScript file of you choosing:&lt;br /&gt;&lt;pre class="code"&gt;&lt;br /&gt;function selectTag(tagElem) {&lt;br /&gt;    tagElem.toggleClassName('selectedTag');&lt;br /&gt;    var selected_tags = buildTagList();&lt;br /&gt;    $('article_detail_tag_list').value = selected_tags;&lt;br /&gt;    $('article_detail_tag_list').focus();&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;function updateTagList(selected_tags) {&lt;br /&gt;    selected_tags = selected_tags.split(',');&lt;br /&gt;    var tags = $H();&lt;br /&gt;    $$('.tag').each(function(tag) { &lt;br /&gt;        tags.set(tag.innerHTML, tag);&lt;br /&gt;        tag.removeClassName("selectedTag");&lt;br /&gt;    });&lt;br /&gt;    selected_tags.each(function(tagName) {&lt;br /&gt;        tagName = tagName.strip();&lt;br /&gt;        var tag = tags.get(tagName);&lt;br /&gt;        if (tag) {&lt;br /&gt;            tag.addClassName("selectedTag");&lt;br /&gt;        } else {&lt;br /&gt;            $('tags').insert('&amp;lt;div class="tag selectedTag" onclick="selectTag(this);"&amp;gt;'+tagName+'&amp;lt;/div&amp;gt;');&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;function buildTagList(tag) {&lt;br /&gt;    var selected_tags = [];&lt;br /&gt;    $$('.selectedTag').each(function(tagElem) {&lt;br /&gt;        selected_tags.push(tagElem.innerHTML);&lt;br /&gt;    });&lt;br /&gt;    if (tag) {&lt;br /&gt;        if (selected_tags.indexOf(tag) === -1) {&lt;br /&gt;            selected_tags.push(tag);&lt;br /&gt;        } else {&lt;br /&gt;            selected_tags = selected_tags.without(tag);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    selected_tags = selected_tags.join(',');&lt;br /&gt;    return selected_tags;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's it!  Now you can easily maintain the tags on your taggable items.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-8423460613133180319?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/8423460613133180319/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=8423460613133180319' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/8423460613133180319'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/8423460613133180319'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2009/08/managing-tags-in-rails.html' title='Managing Tags in Rails'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_l-kZv4tdloI/SoQsgjUn54I/AAAAAAAAAIM/2iMGHgSU5Og/s72-c/ArticleEditForm.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-5842522143528823750</id><published>2009-08-10T13:28:00.002-04:00</published><updated>2009-08-10T13:31:35.333-04:00</updated><title type='text'>RailsRumble '09</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://blog.railsrumble.com/assets/2009/7/3/rr09_badge_250.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 250px; height: 250px;" src="http://blog.railsrumble.com/assets/2009/7/3/rr09_badge_250.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;I plan to participate in this year's &lt;a href="http://r09.railsrumble.com/"&gt;Rails Rumble&lt;/a&gt; with team &lt;a href="http://r09.railsrumble.com/teams/agile-nomads"&gt;Agile Nomads&lt;/a&gt; on August 22nd.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-5842522143528823750?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/5842522143528823750/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=5842522143528823750' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/5842522143528823750'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/5842522143528823750'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2009/08/railsrumble-09.html' title='RailsRumble &apos;09'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-7976090324356467157</id><published>2009-02-06T11:42:00.000-05:00</published><updated>2009-02-06T11:46:45.819-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'>Acts as Conference coverage on Ruby Rails Review</title><content type='html'>&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-7976090324356467157?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.brianburridge.com/2009/02/06/acts-as-conference-coverage-on-ruby-rails-review/' title='Acts as Conference coverage on Ruby Rails Review'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/7976090324356467157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=7976090324356467157' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7976090324356467157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7976090324356467157'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2009/02/acts-as-conference-coverage-on-ruby.html' title='Acts as Conference coverage on Ruby Rails Review'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-234179045655900131</id><published>2009-01-26T12:15:00.005-05:00</published><updated>2009-08-13T11:45:13.423-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'></title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.actsasconference.com"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 300px; height: 100px;" src="http://www.actsasconference.com/images/badge_med_attendee.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br/&gt;&lt;br /&gt;&lt;a href="http://www.eventvue.com/events/aac09"&gt;Connect with other attendees&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-234179045655900131?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.actsasconference.com/' title=''/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/234179045655900131/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=234179045655900131' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/234179045655900131'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/234179045655900131'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2009/01/attending-actsasconference-next-week.html' title=''/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-3852097415447306365</id><published>2009-01-08T11:47:00.004-05:00</published><updated>2009-01-08T11:55:35.906-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'>Ruby tip to handle missing records</title><content type='html'>Here's quick and simple tip to cleanly handle non-existent records in Ruby.&lt;br /&gt;&lt;br /&gt;By default if you try to use &lt;code&gt;find(id)&lt;/code&gt; to fetch a record from your database and the id doesn't exist you'll get a &lt;code&gt;RecordNotFound&lt;/code&gt; error like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&gt;&gt; r = Record.find(9999)&lt;br /&gt;ActiveRecord::RecordNotFound: Couldn't find Record with ID=9999&lt;br /&gt;        from /usr/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/base.rb:1383:in `find_one'&lt;br /&gt;        from /usr/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/base.rb:1366:in `find_from_ids'&lt;br /&gt;        from /usr/lib/ruby/gems/1.8/gems/activerecord-2.1.1/lib/active_record/base.rb:541:in `find'&lt;br /&gt;        from (irb):3&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You could wrap it with a &lt;code&gt;begin&lt;/code&gt; and &lt;code&gt;rescue&lt;/code&gt; and have the rescue clause create a new record such as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;begin&lt;br /&gt;  r = Record.find(id)&lt;br /&gt;rescue&lt;br /&gt;  r = Record.new&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Or you could simply use find_by_id which will return &lt;code&gt;nil&lt;/code&gt; if the record is not found and re-write it as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;r = Record.find_by_id(id) || Record.new&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-3852097415447306365?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/3852097415447306365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=3852097415447306365' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3852097415447306365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3852097415447306365'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2009/01/ruby-tip-to-handle-missing-records.html' title='Ruby tip to handle missing records'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-5706756735485974578</id><published>2008-12-23T21:26:00.002-05:00</published><updated>2008-12-23T21:32:28.839-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'>Merb and Rails Merge</title><content type='html'>The competing Ruby frameworks, Rails and Merb, have decided to join forces!&lt;br /&gt;&lt;br /&gt;Here is the &lt;a href="http://weblog.rubyonrails.org/2008/12/23/merb-gets-merged-into-rails-3"&gt;announcement&lt;/a&gt; from Rails creator David Heinemeier Hansson and the similar &lt;a href="http://yehudakatz.com/2008/12/23/rails-and-merb-merge/"&gt;announcement&lt;/a&gt; from the Merb perspective from Yehuda Katz.&lt;br /&gt;&lt;br /&gt;Keep up with all the latest Ruby and Rails news at &lt;a href="http://www.rubyrailsreview.com"&gt;Ruby Rails Review&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-5706756735485974578?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://rubyonrails.org/merb' title='Merb and Rails Merge'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/5706756735485974578/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=5706756735485974578' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/5706756735485974578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/5706756735485974578'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/12/merb-and-rails-merge.html' title='Merb and Rails Merge'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-6598218966370280372</id><published>2008-12-16T09:01:00.005-05:00</published><updated>2008-12-16T09:36:45.015-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'>Ruby on Rails News Site</title><content type='html'>I haven't been very active on this blog recently, but I've been keeping plenty active on other endeavors.  One of which is the &lt;a href="http://www.rubyrailsreview.com"&gt;Ruby Rails Review&lt;/a&gt; which my friend, &lt;a href="http://www.brianburridge.com"&gt;Brian Burridge&lt;/a&gt;, &lt;a href="http://www.brianburridge.com/2008/12/10/announcing-ruby-rails-review-a-ruby-and-rails-news-site/"&gt;announced&lt;/a&gt; last week.&lt;br /&gt;&lt;br /&gt;As he mentioned in the &lt;a href="http://www.brianburridge.com/2008/12/10/announcing-ruby-rails-review-a-ruby-and-rails-news-site/"&gt;announcement&lt;/a&gt; I created a very easy to use CMS tool which allows us to drag-and-drop the news stories all over the page for placement (or addition and removal), as well as a &lt;a href="http://en.wikipedia.org/wiki/Bookmarklet"&gt;bookmarklet&lt;/a&gt; to simplify the process of adding new stories.  I've very happy with how it turned out, unfortunately no one gets to see it but he and I.&lt;br /&gt;&lt;br /&gt;&lt;div style="width:95%;height:80px;margin-right: 0.75in; background-color: #F5ECE2; -moz-border-radius: 8px; -webkit-border-radius: 8px; -o-border-radius: 8px; -khtml-border-radius: 8px; border-radius: 8px;"&gt;&lt;br /&gt;&lt;a href="http://www.twitter.com/agilenomads"&gt;&lt;img src="http://www.rubyrailsreview.com/images/agile_blue2.png" align="right" style="margin-top: -10px;"/&gt;&lt;/a&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-6598218966370280372?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.rubyrailsreview.com' title='Ruby on Rails News Site'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/6598218966370280372/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=6598218966370280372' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/6598218966370280372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/6598218966370280372'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/12/ruby-on-rails-news-site.html' title='Ruby on Rails News Site'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-1153589517558586251</id><published>2008-09-15T15:32:00.000-04:00</published><updated>2008-09-15T15:35:26.750-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'></title><content type='html'>Find me at:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.rubyconf.org/"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 800px;" src="http://www.rubyconf.org/images/banner.jpg" border="0" alt="" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-1153589517558586251?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/1153589517558586251/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=1153589517558586251' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1153589517558586251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1153589517558586251'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/09/find-me-at.html' title=''/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-2410004024181618661</id><published>2008-09-09T11:39:00.006-04:00</published><updated>2008-09-22T09:10:04.394-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Campfire activity notifier for Gnome, KDE, or console</title><content type='html'>The &lt;a href="http://37signals.blogs.com/products/2007/09/get-a-visual-al.html"&gt;37signals blog&lt;/a&gt; points out a simply Ruby &lt;a href="http://www.snailbyte.com/2007/09/13/campfire-activity-notifier-for-kde/"&gt;script for KDE&lt;/a&gt; that will give you a visual notification when a new message is posted to a &lt;a href="http://www.campfirenow.com/"&gt;Campfire&lt;/a&gt; chat room.&lt;br /&gt;&lt;br /&gt;I have modified that script to work for Gnome as well as KDE, and additionally a text console.  I also made a few other additions such as:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Allow you to specify a specific chat room(s) as a command line argument&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Multiple chats rooms can be monitored concurrently using threading&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Added the Campfire logo to the notifications&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Added some initial status messages to display login status, room topic, and current people in the room&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Display of the last 3 messages of the current day's transcript so you know what's going on without need to login&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Filter out ads&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Here is my version of the script:&lt;br /&gt;&lt;pre name="code"&gt;&lt;br /&gt;#!/usr/bin/env ruby&lt;br /&gt;&lt;br /&gt;# == Synopsis&lt;br /&gt;#      Program to monitor a campfire chat room&lt;br /&gt;#      Modified from code provided at: http://www.snailbyte.com/2007/09/13/campfire-activity-notifier-for-kde/&lt;br /&gt;#&lt;br /&gt;# == Usage&lt;br /&gt;#      campfireMonitor [roomName] [roomName] ...&lt;br /&gt;#&lt;br /&gt;# == Author&lt;br /&gt;#      Steven Pothoven and Snailbyte Ltd.&lt;br /&gt;&lt;br /&gt;require 'rubygems'&lt;br /&gt;require 'tinder'&lt;br /&gt;require "cgi"&lt;br /&gt;&lt;br /&gt;class App&lt;br /&gt;  VERSION = '0.1.1'&lt;br /&gt;&lt;br /&gt;  def initialize arguments, stdin&lt;br /&gt;    # default settings&lt;br /&gt;    campfireSubdomain = 'mySubdomain'&lt;br /&gt;    campfireUsername = 'user@email.com'&lt;br /&gt;    campfirePassword = 'password'&lt;br /&gt;    roomNames = ['Room1', 'Room2']&lt;br /&gt;    if arguments.length &gt; 0&lt;br /&gt;      roomNames = arguments&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    @ui = 'gnome'&lt;br /&gt;    @campfireIconPath = '/path/to/campfire-logo.png'&lt;br /&gt;    # define you favorite audio player and audio file here for audio notifications&lt;br /&gt;    @soundCommand = 'mplayer /usr/share/sounds/pop.wav'&lt;br /&gt;  &lt;br /&gt;&lt;br /&gt;    @campfire = Tinder::Campfire.new campfireSubdomain&lt;br /&gt;    if @campfire.login campfireUsername, campfirePassword&lt;br /&gt;      alert nil, "CampfireMonitor", "Successfully logged in #{campfireUsername}"&lt;br /&gt;      @rooms = roomNames.collect { |roomName| @campfire.find_room_by_name roomName }&lt;br /&gt;      # remove any invalid rooms&lt;br /&gt;      @rooms.delete(nil);&lt;br /&gt;      @rooms.each do |room|&lt;br /&gt;        notify room, "CampfireMonitor", "Entered room."&lt;br /&gt;        notify room, "CampfireMonitor", "Topic is: #{room.topic}.".gsub("'","")&lt;br /&gt;        notify room, "CampfireMonitor", "Current users are:  #{room.users}.".gsub("'","")&lt;br /&gt;      end&lt;br /&gt;    else&lt;br /&gt;      alert nil, "CampfireMonitor", "Failed to log in #{campfireUsername}"&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  # display notifcation message&lt;br /&gt;  def notify room, user, msg&lt;br /&gt;    if msg and msg.size &gt; 0&lt;br /&gt;      msg = CGI.unescapeHTML(msg);&lt;br /&gt;      if @ui == 'kde'&lt;br /&gt;        system "dcop knotify default notify eventname \'#{user}\' \'#{'&lt;a href="'+@campfire.uri.to_s+'/room/'+room.id+'"&gt;'+room.name+'&lt;/a&gt;: ' unless room.nil?} #{msg}\' '' '' 16 2"&lt;br /&gt;      elsif @ui == 'gnome'&lt;br /&gt;        system "notify-send -i #{@campfireIconPath} '#{user}' '#{'&lt;a href="'+@campfire.uri.to_s+'/room/'+room.id+'"&gt;'+room.name+'&lt;/a&gt;: ' unless room.nil?} #{msg}'"&lt;br /&gt;      else&lt;br /&gt;        puts "#{room.name+':' unless room.nil?}#{user} - #{msg}"&lt;br /&gt;      end&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;  &lt;br /&gt;  # notify with sound&lt;br /&gt;  def alert room, user, msg&lt;br /&gt;    notify room, user, msg&lt;br /&gt;    if @soundCommand and @soundCommand.length &gt; 0&lt;br /&gt;      system @soundCommand&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def run&lt;br /&gt;    # first get any missed messages for today&lt;br /&gt;    threads = []&lt;br /&gt;    @rooms.each do |room|&lt;br /&gt;      thread = Thread.new do&lt;br /&gt;        room.transcript(Date.today).last(3).each do |m|&lt;br /&gt;          if !m.nil? and m[:message] and m[:message].size &gt; 1&lt;br /&gt;            notify room, m[:person], m[:message].gsub("'","")&lt;br /&gt;          end&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;      threads &lt;&lt; thread&lt;br /&gt;    end&lt;br /&gt;    threads.each { |thread| thread.join}&lt;br /&gt;&lt;br /&gt;    # listen for more messages&lt;br /&gt;    threads = []&lt;br /&gt;    @rooms.each do |room|&lt;br /&gt;      thread = Thread.new do&lt;br /&gt;        notify room, "CampfireMonitor", "Waiting for messages..."&lt;br /&gt;        room.listen do |m|&lt;br /&gt;          if !m.nil? and m[:message].size &gt; 1&lt;br /&gt;            unless m[:person] == "Ad"&lt;br /&gt;              alert room, m[:person], m[:message].gsub("'","")&lt;br /&gt;            end&lt;br /&gt;          end&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;      threads &lt;&lt; thread&lt;br /&gt;    end&lt;br /&gt;    threads.each { |thread| thread.join}&lt;br /&gt;&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;app = App.new(ARGV, STDIN)&lt;br /&gt;app.run&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I'm sure you can think of plenty more customizations such as command-line options for all the default settings, etc.  But I wanted to keep it fairly simple.&lt;br /&gt;&lt;br /&gt;One last thing, here is the cropped version of the Campfire logo I use for my notifications (campfire-logo.png): &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_l-kZv4tdloI/SMaqEuJFGAI/AAAAAAAAAGU/J816z_EM33Q/s1600-h/campfire-logo.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_l-kZv4tdloI/SMaqEuJFGAI/AAAAAAAAAGU/J816z_EM33Q/s200/campfire-logo.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5244065814201833474" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-2410004024181618661?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/2410004024181618661/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=2410004024181618661' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2410004024181618661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2410004024181618661'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/09/campfire-activity-notifier-for-gnome.html' title='Campfire activity notifier for Gnome, KDE, or console'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_l-kZv4tdloI/SMaqEuJFGAI/AAAAAAAAAGU/J816z_EM33Q/s72-c/campfire-logo.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-7467261260224401120</id><published>2008-09-02T19:33:00.001-04:00</published><updated>2008-09-02T20:14:52.707-04:00</updated><title type='text'>Google Chrome (Chromium) JavaScript speed</title><content type='html'>Today Google &lt;a href="http://googleblog.blogspot.com/2008/09/fresh-take-on-browser.html"&gt;announced&lt;/a&gt; and released their own web browser named &lt;a href="http://code.google.com/chromium/"&gt;Chrome&lt;/a&gt; (aka &lt;a href="http://www.chromium.org/"&gt;Chromium&lt;/a&gt;).  They even made a nice &lt;a href="http://www.google.com/googlebooks/chrome/index.html"&gt;comic book&lt;/a&gt; to describe the advantages of it.&lt;br /&gt;&lt;br /&gt;One of the key features they advertise is faster JavaScript execution due to the V8 JavaScript VM.  So, I decided to test it using my &lt;a href="http://blog.pothoven.net/2007/12/performance-based-web-app-functionality.html"&gt;JS-BogoMips test&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Here are the results of my tests (Windows Vista on Intel® Core™2 Quad Q6600):&lt;br /&gt;Firefox 3:&lt;br /&gt;JS-BogoMIPS (3x) = (0.436779 + 0.446775 + 0.450888) / 3 = 0.444814&lt;br /&gt;&lt;br /&gt;Chrome:&lt;br /&gt;JS-BogoMIPS (3x) = (2.758552 + 2.884791 + 2.881541) / 3 = 2.841628&lt;br /&gt;&lt;br /&gt;which is a 6.4x speed increase!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-7467261260224401120?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/7467261260224401120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=7467261260224401120' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7467261260224401120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7467261260224401120'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/09/google-chrome-chromium-javascript-speed.html' title='Google Chrome (Chromium) JavaScript speed'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-1636242809939931948</id><published>2008-07-16T15:46:00.000-04:00</published><updated>2008-07-16T15:47:11.176-04:00</updated><title type='text'>Steven Pothoven for President</title><content type='html'>&lt;OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" WIDTH="384" HEIGHT="304"&gt;&lt;PARAM NAME=movie VALUE="http://www.paltalk.com/marketing/media/vanksen/main.swf"&gt;&lt;PARAM NAME=quality VALUE=high&gt;&lt;PARAM NAME=flashvars VALUE="firstname=Steven&amp;lastname=Pothoven&amp;urlfin=http%3A%2F%2Fwww.news3online.com%2Fspread.php"&gt;&lt;PARAM NAME="BGCOLOR" VALUE="#000000" /&gt;&lt;PARAM NAME="allowScriptAccess" VALUE="always" /&gt;&lt;EMBED src="http://www.paltalk.com/marketing/media/vanksen/main.swf" quality=high WIDTH="384" HEIGHT="304"  ALIGN="" TYPE="application/x-shockwave-flash" FLASHVARS="firstname=Steven&amp;lastname=Pothoven&amp;urlfin=http%3A%2F%2Fwww.news3online.com%2Fspread.php" PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer" BGCOLOR="#000000" ALLOWSCRIPTACCESS="ALWAYS"&gt;&lt;/EMBED&gt;&lt;/OBJECT&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-1636242809939931948?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/1636242809939931948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=1636242809939931948' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1636242809939931948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1636242809939931948'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/07/steven-pothoven-for-president.html' title='Steven Pothoven for President'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-3448367255613725530</id><published>2008-05-06T08:52:00.006-04:00</published><updated>2008-09-11T16:03:40.042-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><title type='text'>Keystroke and Field Validation with JavaScript</title><content type='html'>Yesterday, I provided &lt;a href="http://blog.pothoven.net/2008/05/keydown-vs-keypress-in-javascript.html"&gt;a simple tool&lt;/a&gt; to demonstrate the differences between &lt;code&gt;keydown&lt;/code&gt; and &lt;code&gt;keypress&lt;/code&gt; events.  Today, I'm going to apply that knowledge toward making a lightweight form validation library.  This library will prevent invalid characters from being entered in form input elements which is useful toward preventing malicious data from being entered (think &lt;a href="http://en.wikipedia.org/wiki/Cross-site_scripting"&gt;XSS&lt;/a&gt;), as well as ensuring data integrity (data is in the valid format, all required fields present, etc.).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Sample Form:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;form &gt;&lt;table id="validationTable"&gt; &lt;tr&gt;  &lt;th&gt;Alpha&lt;/th&gt;  &lt;td&gt;&lt;input type="text" name="alpha" id="alpha" /&gt;&lt;/td&gt;  &lt;td&gt;&lt;span id="alpha-error" style="display:none"&gt;Invalid&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;   &lt;th&gt;&lt;span style="font-weight:bold;"&gt;Alphanumeric*&lt;/span&gt;&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="alphanumeric" id="alphanumeric" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="alphanumeric-error" style="display:none"&gt;Invalid&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;   &lt;th&gt;Date&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="date" id="date" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="date-error" style="display:none"&gt;Date must be yyyy-mm-dd&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;   &lt;th&gt;Decimal&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="decimal" id="decimal" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="decimal-error" style="display:none"&gt;Invalid decimal&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;   &lt;th&gt;Digit&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="digit" id="digit" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="digit-error" style="display:none"&gt;Invalid digit&lt;/span&gt;&lt;/td&gt;&lt;br /&gt; &lt;/tr&gt;  &lt;tr&gt;   &lt;th&gt;Email&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="email" id="email" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="email-error" style="display:none"&gt;Invalid email address&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;   &lt;th&gt;Hostname&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="hostname" id="hostname" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="hostname-error" style="display:none"&gt;Invalid hostname&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;   &lt;tr&gt;   &lt;th&gt;IP address&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="ipAddress" id="ipAddress" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span  id="ipAddress-error" style="display:none"&gt;Invalid IP address&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;   &lt;tr&gt;   &lt;th&gt;Integer&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="integer" id="integer" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="integer-error" style="display:none"&gt;Invalid Integer&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;   &lt;tr&gt;   &lt;th&gt;Punctuation&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="punctuation" id="punctuation" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="punctuation-error" style="display:none"&gt;Invalid punctuation&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;&lt;br /&gt;  &lt;tr&gt;   &lt;th&gt;Real number&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="real" id="real" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="real-error" style="display:none"&gt;Invalid real number&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;   &lt;tr&gt;   &lt;th&gt;Time&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="time" id="time" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="time-error" style="display:none"&gt;Invalid time&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;   &lt;tr&gt;   &lt;th&gt;Whitespace&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="whitespace" id="whitespace" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="whitespace-error" style="display:none"&gt;Invalid whitespace&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt;  &lt;tr&gt;   &lt;th&gt;custom&lt;br /&gt;   type&lt;input type="text" name="customDatatype" id="customDatatype" /&gt;&lt;br /&gt;   format&lt;input type="text" name="customFormat" id="customFormat" /&gt;&lt;/th&gt;   &lt;td&gt;&lt;input type="text" name="custom" id="custom" /&gt;&lt;/td&gt;   &lt;td&gt;&lt;span id="custom-error" style="display:none"&gt;Invalid&lt;/span&gt;&lt;/td&gt;  &lt;/tr&gt; &lt;/table&gt;&lt;br /&gt;&lt;input type="button" onclick="fv.validateAllFields(this.form);" value="Validate Form" /&gt;&lt;/form&gt;&lt;br /&gt;* denotes required field.&lt;br /&gt;&lt;br /&gt;&lt;script src="http://www.pothoven.net/javascripts/formValidation-packed.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;script src="http://www.pothoven.net/javascripts/src/validationBlog.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;The form above should only allow valid characters in each input field - this is your keystroke validation.  Furthermore, for fields that require a specific format, when you leave a field with invalid formatted data it should provide an immediate visual indication of the error by setting a &lt;code&gt;fieldWithErrors&lt;/code&gt; CSS class on invalid fields.  Additionally, a &lt;code&gt;fv:onInvalid&lt;/code&gt; event is fired on the invalid element to allow custom events handlers to  provide additional actions.  For example, enter a number in the date field and tab out.  The field will be outlined in red from the &lt;code&gt;fieldWithErrors&lt;/code&gt; CSS class and the message, "Date must be yyyy-mm-dd" will be shown by the handling of the &lt;code&gt;fv:onInvalid&lt;/code&gt; event.  Note: the CSS class will be removed automatically when the field is corrected, and a corresponding &lt;code&gt;fv:onValid&lt;/code&gt; event will be fired to allow the custom code to clean up after itself.&lt;br /&gt;&lt;br /&gt;Validation rules are defined as Regular Expressions.  If you are not familiar with Regular Expressions here is a good &lt;a href="http://www.regular-expressions.info/reference.html"&gt;reference&lt;/a&gt;. Additionally, many predefined regex rules for various validations can be found at the &lt;a href="http://regexlib.com/DisplayPatterns.aspx"&gt;regex library&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Several common data types and formats are defined by default in the library, but it also provide the mechanism to add you own data types. In the &lt;span style="font-style:italic;"&gt;custom&lt;/span&gt; entry, the &lt;code&gt;type&lt;/code&gt; entry allows you to specify an on-the-fly keystroke validation regular expression, and the &lt;code&gt;format&lt;/code&gt; entry allows you to specify an on-the-fly field format validation regular expression.&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-style:italic;"&gt;Validate Form&lt;/span&gt; button demonstrates the action you would want to perform before a form submission.  It invokes a &lt;code&gt;validateAllFields&lt;/code&gt; function which just runs all the field validations (and invokes corresponding CSS rules and issues events).  It also returns an array of the invalid field names in case you want to build a list of errors (like the Rails flash messages).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Usage Instructions&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sample Usage:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;script src="javascript/formValidation.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;script&amp;gt;&lt;br /&gt;   var vf = new FormValidation();&lt;br /&gt;   vf.addValidationForField("date", "date", "date");&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;code&gt;addValidationForField&lt;/code&gt; requires 3 parameters:&lt;ol&gt;&lt;br /&gt;&lt;li&gt;The identifer of the field to add validation for&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The name of the data type to use for keystroke validation&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The name of the data format to use for field validation&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;In this sample all the names are the same, but they usually wouldn't be.&lt;br /&gt;&lt;br /&gt;Just doing the above will prevent invalid characters from being entered in the data field and can indicate when the field is invalid.&lt;br /&gt;&lt;br /&gt;When a field is changed, the format validation is run and will do two things.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;It will mark invalid fields are the &lt;code&gt;fieldWithErrors&lt;/code&gt; CSS class. This can be used to appropriately highlight the error to the user.  For example, the following CSS rule will outline the field in red:&lt;pre name="code"&gt;&lt;br /&gt;.fieldWithErrors {&lt;br /&gt;    border: 3px solid red;&lt;br /&gt;}&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;An event will also be issued in the field to indicate whether or not the field is valid.  The events to watch for are &lt;code&gt;fv:onInvalid&lt;/code&gt; and &lt;code&gt;fv:onValid&lt;/code&gt;.  These events can be used to hide/show error messages such the following which will display a pre-defined error message:&lt;pre&gt;&lt;br /&gt;$$('input').each(function(inputElem) {&lt;br /&gt;    inputElem.observe("fv:onInvalid", function() {&lt;br /&gt;        $(this.identify() + "-error").show();}.bind(inputElem));&lt;br /&gt;    inputElem.observe("fv:onValid", function() {&lt;br /&gt;        $(this.identify() + "-error").hide();}.bind(inputElem));&lt;br /&gt;});&lt;/pre&gt;&lt;br /&gt;Or any number of other actions.  For example, you may wish to display a popup error message such as those provided by &lt;a href="http://prototype-window.xilinus.com/"&gt;Prototype Window&lt;/a&gt; or add messages to an error flash area as commonly done in Rails apps or toggle the submit button as enabled/disabled.  Using these events allows you to add whatever actions you desire when the&lt;br /&gt;validation fails (or passes).&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Download&lt;/span&gt;&lt;br /&gt;&lt;a href="http://www.pothoven.net/javascripts/src/formValidation.js" onclick="javascript:urchinTracker('files/formValidation.js');"&gt;formValidation.js&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-3448367255613725530?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/3448367255613725530/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=3448367255613725530' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3448367255613725530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3448367255613725530'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/05/keystroke-and-field-validation-with.html' title='Keystroke and Field Validation with JavaScript'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-7618833198062103550</id><published>2008-05-05T16:26:00.009-04:00</published><updated>2008-05-05T17:26:56.747-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><title type='text'>keydown vs. keypress (in JavaScript)</title><content type='html'>In my next post, I intend to discuss a validation library which performs keystroke and field level validation for HTML forms using JavaScript and regular expressions.  However, before you can correctly understand how the validation works, as well as when you can test for various validation conditions, you have to recognize the different behaviors between the &lt;code&gt;keydown&lt;/code&gt; and &lt;code&gt;keypress&lt;/code&gt; events. &lt;br /&gt;&lt;br /&gt;In the following text fields, type whatever you like and notice the different behaviors in the table below it.&lt;form&gt;&lt;br /&gt;&lt;label for="keystrokes" style="font-weight: bold; font-size: 18px;"&gt;Type here → &lt;input id="keystrokes" name="keystrokes" type="text"&gt;&lt;/label&gt;&lt;/form&gt;&lt;table id="keystrokeTable" style="border: 3px ridge black; margin: 0px auto; text-align: center; border-collapse: collapse;"&gt; &lt;thead&gt;&lt;tr&gt;&lt;th&gt;Event&lt;/th&gt;&lt;th&gt;charCode&lt;/th&gt;&lt;th&gt;keyCode&lt;/th&gt;&lt;th&gt;Display&lt;/th&gt;&lt;th&gt;Shifted?&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Keydown&lt;/th&gt;&lt;td id="keydownCharCode"&gt;&lt;/td&gt;&lt;td id="keydownKeyCode"&gt;&lt;/td&gt;&lt;td id="keydownDisplay"&gt;&lt;/td&gt;&lt;td id="keydownShift"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;       &lt;tr&gt;&lt;th&gt;Keypress&lt;/th&gt;&lt;td id="keypressCharCode"&gt;&lt;/td&gt;&lt;td id="keypressKeyCode"&gt;&lt;/td&gt;&lt;td id="keypressDisplay"&gt;&lt;/td&gt;&lt;td id="keypressShift"&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Some notable problem keys when dealing with JavaScript keystroke validation are the arrow keys and other editing keys (delete, home, end, insert) which should be allowed in order to edit a field value, however they can appear to be punctuation symbols which may not be desired in the field. Function keys and number pad keys are also problematic.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;Here are some key observations regarding keypress events:&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Firefox sends regular keys as charCodes and special keys as keyCodes&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Safari sends both charCode and keyCode for regular keys but does not issue keypress events for special keys&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Internet Explorer does not send charCodes, only keyCodes, and does not issue keypress events for special keys&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Opera does not send charCodes, only keyCodes, but also issues keypress events for special keys making it impossible to distinguish between some keys such as '-' vs. &lt;code&gt;Insert&lt;/code&gt; and '.' vs. &lt;code&gt;Delete&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Konqueror will send both charCode and keyCode for regular keys, but only keyCode for special keys&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;script type="text/javascript" src="http://www.pothoven.net/javascripts/src/keystrokes.js"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-7618833198062103550?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/7618833198062103550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=7618833198062103550' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7618833198062103550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7618833198062103550'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/05/keydown-vs-keypress-in-javascript.html' title='keydown vs. keypress (in JavaScript)'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-7022299976272547807</id><published>2008-05-02T14:37:00.002-04:00</published><updated>2008-05-02T16:17:58.647-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mobile phone'/><title type='text'>Bluetooth Proximity Monitor (improved)</title><content type='html'>About a month ago, a friend of mine pointed out a bluetooth proximity program for windows which would lock your desktop when you walked away by polling your mobile phone's proximity to your computer.  I thought that sounded fun to try as I have a bluetooth enabled phone and laptop, however, I'm running Linux, not Windows.  After a quick search on Google I found a &lt;a href="http://gentoo-wiki.com/TIP_Bluetooth_Proximity_Monitor"&gt;Bluetooth Proximity Monitor&lt;/a&gt; script for Linux.  It worked pretty well as it was, but I've made a few adjustments to it to improve it for my purposes.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;The original script is written for KDE or other window managers that use xscreensaver.  Since I'm currently running in Gnome, I had to switch the commands to use gnome-screensaver instead.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;While changing the screensaver commands, I also added the ability to toggle my instant messenger (Pidgin) from 'available' to 'away'&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The original script has a single THRESHOLD value to toggle between being near and far.  I decided I needed separate NEAR and FAR thresholds.  This is due to the wide variance of proximity I can have while I'm sitting at my desk.  Just turning in my chair so that my body was between the phone and the laptop could change my RSSI (proximity value) from -1 to -18, so I need a fairly high threshold to prevent that.  On the other hand, setting the threshold high could allow my system to unlock when I'm still 30 feet away.  So making separate thresholds allows the proximity monitor not to trigger just because I turned in my chair, but also not unlock until I've actually returned to my desk.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;With the higher away threshold, it's possible (though uncommon) to totally leave the bluetooth range before it triggers that you've gone away.  So, I also added a little logic to trigger the change in proximity if you were previously in near proximity, but now your bluetooth can no longer be pinged (out of range, turned off, etc).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Finally, I alter the proximity check interval depending on if you're near or far.  The motivation for this was an attempt to reduce power demands a little in order to prolong the battery life.  I haven't done any actual tests to determine if it helped or not.  Basically, if you're at your desk, it only checks every 5 seconds to make sure you're still there.  If you've walked away it switches to check every 2 seconds in order to be more responsive to when you return.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;In the end, my version of the bluetooth proximity monitor script looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#!/bin/sh&lt;br /&gt;&lt;br /&gt;DEVICE="00:1A:8A:61:6C:FE"&lt;br /&gt;CHECK_INTERVAL=5&lt;br /&gt;NEAR_THRESHOLD=-1&lt;br /&gt;FAR_THRESHOLD=-22&lt;br /&gt;PID=0&lt;br /&gt;START_CMD='/usr/bin/gnome-screensaver'&lt;br /&gt;FAR_CMD='/usr/bin/gnome-screensaver-command -l &amp;&amp; purple-remote setstatus?status=away '&lt;br /&gt;NEAR_CMD='/usr/bin/gnome-screensaver-command -d &amp;&amp;  purple-remote setstatus?status=available'&lt;br /&gt;HCITOOL="/usr/bin/hcitool"&lt;br /&gt;DEBUG="/var/log/btproximity.log"&lt;br /&gt;&lt;br /&gt;connected=0&lt;br /&gt;&lt;br /&gt;function msg {&lt;br /&gt;    echo "$1" &amp;gt;&amp;gt; $DEBUG&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function msgn {&lt;br /&gt;    echo -n "$1" &amp;gt;&amp;gt; $DEBUG&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function check_connection {&lt;br /&gt;    connected=0;&lt;br /&gt;    found=0&lt;br /&gt;    for s in `$HCITOOL con`; do&lt;br /&gt;        if [[ "$s" == "$DEVICE" ]]; then&lt;br /&gt;            found=1;&lt;br /&gt;        fi&lt;br /&gt;    done&lt;br /&gt;    if [[ $found == 1 ]]; then&lt;br /&gt;        connected=1;&lt;br /&gt;    else&lt;br /&gt;#       msgn 'Attempting connection...'&lt;br /&gt;        if [ -z "`$HCITOOL cc $DEVICE 2&amp;gt;&amp;1`" ]; then&lt;br /&gt;#            msg 'Connected.'&lt;br /&gt;            connected=1;&lt;br /&gt;        else&lt;br /&gt;            msg "ERROR: Could not connect to device $DEVICE."&lt;br /&gt;        fi&lt;br /&gt;    fi&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function check_xscreensaver {&lt;br /&gt;    PID=`ps -C gnome-screensaver --no-heading | awk '{ print $1 }'`&lt;br /&gt;    if [ "$PID" == "" ];  then&lt;br /&gt;        $START_CMD &amp;&lt;br /&gt;    fi&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;check_connection&lt;br /&gt;&lt;br /&gt;while [[ $connected -eq 0 ]]; do&lt;br /&gt;    check_connection&lt;br /&gt;    sleep 1&lt;br /&gt;done&lt;br /&gt;&lt;br /&gt;name=`$HCITOOL name $DEVICE`&lt;br /&gt;msg "Monitoring proximity of \"$name\" [$DEVICE]";&lt;br /&gt;&lt;br /&gt;state="near"&lt;br /&gt;while /bin/true; do&lt;br /&gt;&lt;br /&gt;    check_xscreensaver&lt;br /&gt;    check_connection&lt;br /&gt;&lt;br /&gt;    if [[ $connected -eq 1 ]]; then&lt;br /&gt;        rssi=`$HCITOOL rssi $DEVICE | sed -e 's/RSSI return value: //g'`&lt;br /&gt;&lt;br /&gt;        if (( $rssi &amp;lt;= $FAR_THRESHOLD )); then&lt;br /&gt;            if [[ "$state" == "near" ]]; then&lt;br /&gt;                msg "*** Device \"$name\" [$DEVICE] has left proximity (signal: $rssi)"&lt;br /&gt;                state="far"&lt;br /&gt;                $FAR_CMD &amp;gt; /dev/null 2&amp;gt;&amp;1&lt;br /&gt;   CHECK_INTERVAL=2&lt;br /&gt;            fi&lt;br /&gt;        elif (( $rssi &amp;gt;= $NEAR_THRESHOLD )); then&lt;br /&gt;            if [[ "$state" == "far" ]]; then&lt;br /&gt;                msg "*** Device \"$name\" [$DEVICE] is within proximity (signal: $rssi)"&lt;br /&gt;                state="near"&lt;br /&gt;                $NEAR_CMD &amp;gt; /dev/null 2&amp;gt;&amp;1&lt;br /&gt;                $START_CMD &amp;&lt;br /&gt;   CHECK_INTERVAL=5&lt;br /&gt;            fi&lt;br /&gt;        fi&lt;br /&gt;#        msgn "RSSI = $rssi, "&lt;br /&gt;    elif [[ "$state" == "near" ]]; then     &lt;br /&gt;            msg "*** Device \"$name\" [$DEVICE] is no longer detectable"&lt;br /&gt;            state="far"&lt;br /&gt;            $FAR_CMD &amp;gt; /dev/null 2&amp;gt;&amp;1&lt;br /&gt;            CHECK_INTERVAL=2&lt;br /&gt;    fi&lt;br /&gt;#    msg "state = $state, PID = $PID, sleep = $CHECK_INTERVAL"&lt;br /&gt;&lt;br /&gt;    sleep $CHECK_INTERVAL&lt;br /&gt;done&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-7022299976272547807?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/7022299976272547807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=7022299976272547807' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7022299976272547807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7022299976272547807'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/05/bluetooth-proximity-monitor-improved.html' title='Bluetooth Proximity Monitor (improved)'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-5879389610585508318</id><published>2008-04-30T11:00:00.006-04:00</published><updated>2008-05-01T15:12:42.943-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><title type='text'>Using JavaScript in blogs without &lt;script&gt; support</title><content type='html'>At one point, &lt;a href="www.blogger.com"&gt;Blogger&lt;/a&gt; (which is used for this blog) did not allow the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag in blog entries so some people came up with interesting work-a-rounds.  This post will showcase two techniques which work well together to provide JavaScript capabilities to your blogs.  The first technique supports inline JavaScript with a make-shift &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag, while the second technique supports loading external JavaScript files specific to each blog entry which can still be useful even with &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; support.  &lt;br /&gt;&lt;br /&gt;These techniques work well together and almost need to be used together.  The inline technique is only handy for adding a line or two of code because Blogger will try to be smart regarding the formatting of your blog entry and add &lt;code&gt;&amp;lt;br/&amp;gt;&lt;/code&gt; tags between all your lines of code making the JavaScript interpreter choke.  Additionally, Blogger also tries to format any &amp;lt; or &amp;gt; symbols as &amp;amp;lt; and &amp;amp;gt; which don't work too well in your code.  The second technique allows you to load as much JavaScript code as you want, however you'll probably want to use the first technique to invoke the included JavaScript functions.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold"&gt;Testing inline JavaScript: &lt;span id="jsTestResult" style="background-color:yellow"&gt;failed&lt;/span&gt;.&lt;/span&gt; (wait for page to complete loading)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold"&gt;Technique 1 - Allowing Inline JavaScript&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;That previous test line tested my ability to add inline JavaScript to my blog following &lt;a href="http://ecmanaut.blogspot.com/2006/01/adding-javascript-to-blogger-posts.html"&gt;these instructions&lt;/a&gt; from ecmanaut.&lt;br /&gt;&lt;br /&gt;To use JavaScript in this blog without any &lt;code&gt;&amp;lt;br/&amp;gt;&lt;/code&gt; tags, I added the following code to my blogger template:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;script type='text/javascript'&amp;gt;&lt;br /&gt;   Event.observe(window, &amp;#39;load&amp;#39;, function() {&lt;br /&gt;      var c = document.getElementsByTagName(&amp;#39;code&amp;#39;), s, i;&lt;br /&gt;      var junk = /^\s*\46lt;\133\133CDATA\133|]]\46gt;\s*$/g;&lt;br /&gt;      for( i=0; i &amp;amp;lt; c.length; i++ ) {&lt;br /&gt;         s = c[i].getAttribute(&amp;#39;style&amp;#39;) || &amp;#39;&amp;#39;;&lt;br /&gt;         if( s.match( /display[\s:]+none/i ) ) {&lt;br /&gt;            eval( c[i].innerHTML.replace( junk, &amp;#39;&amp;#39; ) );&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;   });&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I made a couple of slight variations to the original code snippet on the instruction page. First of all, before I added that code, I also included prototype.js in my template to have access to it's handy extensions.  I was then able to utilize protype's &lt;code&gt;Event.observe&lt;/code&gt; function to execute this code when the page is loaded without messing up and other &lt;code&gt;onload&lt;/code&gt; actions.  Executing this code during the &lt;code&gt;onload&lt;/code&gt; is recommended in the instructions, but not explained how to do it.  Finally, since the template requires valid XML, the &lt;code&gt;i &amp;lt; c.length&lt;/code&gt; line isn't valid until you change the &amp;lt; to &amp;amp;lt;&lt;br /&gt;&lt;br /&gt;Then, in order to run this JavaScript test, I added this line to the bottom of this blog entry:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;code style="display:none"&amp;gt; &amp;lt;[[CDATA[ $('jsTestResult').innerHTML = 'passed'; ]]&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;which changes the word &lt;span style="font-weight:bold"&gt;failed&lt;/span&gt; to &lt;span style="font-weight:bold"&gt;passed&lt;/span&gt; in my test line above.&lt;br /&gt;&lt;code style="display:none"&gt; &lt;[[CDATA[ $('jsTestResult').innerHTML = 'passed'; ]]&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Technique 2 - Loading External JavaScript Files&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In order to load JavaScript files, you could edit your Blogger template to include all the files you want as I did for &lt;a href="http://www.prototypejs.org"&gt;prototype.js&lt;/a&gt; and &lt;a href="http:///script.aculo.us"&gt;script.aculo.us&lt;/a&gt;.  However, then every view of your blog will load ALL your JavaScript files.  That's fine if you have some JavaScript library that you want to be able to utilize in many/all your blog entries, but if you're adding entries like mine which are just showcasing various JavaScript techniques in separate blog entries, you only want to load the specific blog entry JavaScript if it's being viewed. Enter &lt;a href="http://www.codehouse.com/javascript/articles/external/"&gt;Dynamically Loading External JavaScript Files&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Right after the previously mentioned code, I added the &lt;code&gt;dhtmlLoadScript&lt;/code&gt; function.  However, since I have prototype.js loaded, I modified it a little bit.  Here's my version:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function dhtmlLoadScript(url) {&lt;br /&gt;   var e = new Element(&amp;quot;script&amp;quot;, {src: url, type: &amp;quot;text/javascript&amp;quot;});&lt;br /&gt;   document.getElementsByTagName(&amp;quot;head&amp;quot;)[0].appendChild(e);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, in any blog entry which I want to use an external JavaScript file I simply add the line:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;code style="display:none"&amp;gt; &amp;lt;[[CDATA[ dhtmlLoadScript('http://some.domain.com/jsfile.js'); ]]&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and I can use any of the included function.  Furthermore, since the inline script won't be evaluated until the &lt;code&gt;onLoad&lt;/code&gt; event it triggered, I don't have to worry about registering it for an &lt;code&gt;onLoad&lt;/code&gt; event itself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-5879389610585508318?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/5879389610585508318/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=5879389610585508318' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/5879389610585508318'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/5879389610585508318'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/04/inline-js-test.html' title='Using JavaScript in blogs without &amp;lt;script&amp;gt; support'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-2141452777152167792</id><published>2008-02-22T15:16:00.008-05:00</published><updated>2008-05-01T15:15:44.131-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><title type='text'>busyProcess: a visual indicator for long JavaScript tasks</title><content type='html'>Some time ago I wrote an article to demonstrate &lt;a href="http://blog.pothoven.net/2007/02/adding-processing-indicator-with.html"&gt;a simple method of displaying a processing/loading indicator for Ajax requests using prototype&lt;/a&gt;.  That works great for Ajax requests, but let's assume you have some CPU intensive task that will be processed on the client side in JavaScript.  This task will take several seconds to complete and you want to add some sort of visual indicator to the page while it's working so the user knows something is happening.  enter the &lt;code&gt;busyProcess&lt;/code&gt; function.&lt;br /&gt;&lt;br /&gt;You could code your particular JavaScript task to display a processing indicator itself and remove it when it's done, however there are two problems you'll encounter.  First, you'll probably never see the processing indicator since your task will immediately do its thing and not give control back to the browser in order to display the indicator before the task finishes and your code removed the indicator.  Second, you'll end up adding the same code over and over around each function that may take a while to complete (DRY - Do not Repeat Yourself!).&lt;br /&gt;&lt;br /&gt;&lt;code&gt;busyProcess&lt;/code&gt; handles both of these situations.  It is a flexible wrapper for any function passed in to it so that it can be used for any task, and it defers the execution of the function in order to allow the browser to render the visual indicator.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold"&gt;Example&lt;/span&gt;&lt;br /&gt;&lt;script type="text/javascript" src="http://www.pothoven.net/javascripts/src/busyProcess.js"&gt;&lt;/script&gt;&lt;br /&gt;Click this &lt;input type="button" value="button" id="busyButton" onclick="busyLoop(this, 3);" /&gt; to be busy for 3 seconds.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold"&gt;Get to the code already!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/**&lt;br /&gt; * busyProcess&lt;br /&gt; * &lt;br /&gt; * Add a busy indicator over an element while running the function it invokes&lt;br /&gt; * @param {Object} element clicked on to invoke the task&lt;br /&gt; * @param {Function} function to invoke after adding visual indicator&lt;br /&gt; */       &lt;br /&gt;function busyProcess(element, func) {&lt;br /&gt;   var busyIndicator = new Element("div", {id: "busy"});&lt;br /&gt;   busyIndicator.setStyle({zIndex: "100",&lt;br /&gt;                           position: "absolute",&lt;br /&gt;                           fontWeight:"bold",&lt;br /&gt;                           height: "16px"});&lt;br /&gt;   Position.clone($(element), busyIndicator, {setWidth: false, setHeight: false});&lt;br /&gt;   busyIndicator.innerHTML = '&lt;img src="images/wait.gif" style="width:16px;height:16px;" /&gt; Processing...';&lt;br /&gt;   document.body.appendChild(busyIndicator);           &lt;br /&gt;&lt;br /&gt;   // function needs to be deferred in order for the browser to render the&lt;br /&gt;   // busy indicator, but we need to wrap it in order to remove the busy indicator&lt;br /&gt;   // when it's done&lt;br /&gt;   func = func.wrap(&lt;br /&gt;      function(proceed) {&lt;br /&gt;         proceed();&lt;br /&gt;         busyIndicator.remove();&lt;br /&gt;      });&lt;br /&gt;   func.defer();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold"&gt;Ok, so how does it work?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The function takes in two arguments. The first is the element clicked on. This is used as the location of the popup processing indicator as the item clicked on is what invoked the action for the user.  Since the prototype $() function is used, this can be an element id as well.  The second argument is the function to invoke.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;busyProcess&lt;/code&gt; starts by building the processing indicator.  This can be customized to your preferences.  Just be sure the indicator has a z-index greater than anything other layers on the screen, and that is has &lt;code&gt;absolute&lt;/code&gt; positioning.  I then utilize the prototype &lt;code&gt;Position.clone&lt;/code&gt; to position the indicator over the element clicked on.&lt;br /&gt;&lt;br /&gt;Now we get to the more interesting part.  The indicator needs to go away when the task is done.  But how do we know when it's done?  You could make the task issue a custom event and register an event listener here to catch it in order to remove the processing indicator.  However, then any function you use &lt;code&gt;busyProcess&lt;/code&gt; with will need to issue that event when it's done.  That could get messy, and easy to forget.  So, instead we &lt;code&gt;wrap&lt;/code&gt; the original function to have it remove the processing indicator when it's done.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold"&gt;So, how do you use this?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For a simple example, let's assume we want to sort some large table by different columns when the user clicks on little triangle icons indicating ascending or descending.&lt;br /&gt;For simplicity I'm just going to add the code inline on an &lt;code&gt;onclick&lt;/code&gt; attribute in the HTML.  As a general practice I usually try to register event handlers in my JavaScript code so that the HTML has no JavaScript in it.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;td id="zipcode"&amp;gt;Zip Code&lt;br /&gt;&amp;lt;img src="images/sort-asc.gif" alt="ascending sort icon" &lt;br /&gt;     onclick="busyProcess(this, function() {sortTableBy(this.up('td').identify());}.bind(this));" /&amp;gt;&lt;br /&gt;&amp;lt;/td&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I intentionally made that a little more complicated than it had to be just to demonstrate that you can use inline anonymous functions as well.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold"&gt;Optional (but useful) addition&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In order to give a more obvious indication that work is being done and to prevent the user from clicking on anything else until it's done you may want to either darken or lighten the rest of the page.  To do this, add another layer at the beginning of the &lt;code&gt;busyProcess&lt;/code&gt; function like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   var lightenScreen = new Element("div", {id: "lightenScreen",&lt;br /&gt;                                          'class': "lightenBackground"});&lt;br /&gt;   document.body.appendChild(lightenScreen);           &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;where the &lt;code&gt;lightenBackground&lt;/code&gt; class is defined in CSS as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;.lightenBackground {&lt;br /&gt;    background-color: white;&lt;br /&gt;    opacity: 0.5; /* Safari, Opera */&lt;br /&gt;    -moz-opacity: 0.50; /* FireFox */&lt;br /&gt;    filter: alpha(opacity = 50); /* IE */&lt;br /&gt;    z-index: 20;&lt;br /&gt;    height: 100%;&lt;br /&gt;    width: 100%;&lt;br /&gt;    background-repeat: repeat;&lt;br /&gt;    position: fixed;&lt;br /&gt;    top: 0px;&lt;br /&gt;    left: 0px;&lt;br /&gt;    cursor: wait;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or the corresponding darkening version:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;.darkenBackground {&lt;br /&gt; background-color: black;&lt;br /&gt; opacity: 0.2; /* Safari, Opera */&lt;br /&gt; -moz-opacity: 0.20; /* FireFox */&lt;br /&gt; filter: alpha(opacity = 20); /* IE */&lt;br /&gt; z-index: 20;&lt;br /&gt; height: 100%;&lt;br /&gt; width: 100%;&lt;br /&gt; background-repeat: repeat;&lt;br /&gt; position: fixed;&lt;br /&gt; top: 0px;&lt;br /&gt; left: 0px;&lt;br /&gt; cursor: wait;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Just be sure to remove this layer as part of the &lt;code&gt;wrap&lt;/code&gt; by adding the line: &lt;code&gt;lightenScreen.remove();&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-2141452777152167792?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/2141452777152167792/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=2141452777152167792' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2141452777152167792'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2141452777152167792'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/02/busyprocess-visual-indicator-for-long.html' title='busyProcess: a visual indicator for long JavaScript tasks'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-2916058375188455116</id><published>2008-02-08T18:00:00.000-05:00</published><updated>2008-02-13T16:00:40.768-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'>acts_as_conference - day 1</title><content type='html'>I attended the &lt;a href="http://www.actsasconference.com/"&gt;acts_as_conference&lt;/a&gt; conference on Ruby on Rails.  It was put on by &lt;a href="http://www.railsforall.org/"&gt;Rails for All&lt;/a&gt; whose founder, Robert Dempsey had previously given a free class on Ruby on Rails in Tampa, where I live, for free which was my first real exposure to RoR, and how I learned about this conference.&lt;br /&gt;&lt;br /&gt;The conference was attended by around 150 people.  There were some technical glitches, particularly with WiFi access for everyone, but overall it ran smoothly.&lt;br /&gt;&lt;br /&gt;Here are the topics covered with a brief summary of each:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;&lt;a href="http://www.nealford.com/downloads/conferences/canonical/Neal_Ford-Advanced_DSLs_in_Ruby-slides.pdf"&gt;Advanced DSLs in Ruby&lt;/a&gt; - Neal Ford&lt;/h3&gt;A &lt;span style="font-weight: bold;"&gt;DSL&lt;/span&gt; is a &lt;span style="font-style: italic;"&gt;domain specific languages.&lt;/span&gt;&lt;span&gt;  Basically this is about &lt;/span&gt;building fluent (readable) interfaces and  improve abstraction by eliminating noise.&lt;br /&gt;&lt;br /&gt;So you can type:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recipe = Recipe.new "Spicy Bread"&lt;br /&gt;recipe.add 200.grams.of Flour&lt;br /&gt;recipe.add 1.lb.of Nutmeg&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;instead of something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recipe = Recipe.new("Spicy Bread")&lt;br /&gt;flour = new Ingredient("Flour")&lt;br /&gt;flour.setMeasurement("grams")&lt;br /&gt;flour.setAmount(200)&lt;br /&gt;recipe.add(flour)&lt;br /&gt;nutmeg = new Ingredient("Nutmeg")&lt;br /&gt;nutmeg.setMeasurement("lb")&lt;br /&gt;nutmeg.setAmount(1)&lt;br /&gt;recipe.add(nutmeg)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This works by using a few techniques.  First extending the &lt;code&gt;Numeric&lt;/code&gt; class to allow the numbers to converted based on the measurement type to a common measurement in grams, and add an &lt;code&gt;of&lt;/code&gt; function to build a new Ingredient object&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class Numeric&lt;br /&gt;def gram&lt;br /&gt; self&lt;br /&gt;end&lt;br /&gt;alias_method :grams, :gram&lt;br /&gt;&lt;br /&gt;def pound&lt;br /&gt; self * 453.59237&lt;br /&gt;end&lt;br /&gt;alias_method :lb, :pound&lt;br /&gt;alias_method :lbs, :pound&lt;br /&gt;alias_method :pounds, :pound&lt;br /&gt;&lt;br /&gt;def of ingredient&lt;br /&gt; if ingredient.kind_of String?&lt;br /&gt;   ingredient = new Ingredient(ingredient)&lt;br /&gt; end&lt;br /&gt; ingredient.quantity = self&lt;br /&gt; ingredient&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The reason you can create the Ingredient without surrounding the Ingredient name with quotes is by type transmogrification which uses of the &lt;code&gt;const_missing&lt;/code&gt; construct:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class Object&lt;br /&gt;def self.const_missing(sym)&lt;br /&gt; eval "Ingredient.new(sym.to_s)"&lt;br /&gt;end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Neal added some additional techniques like using bubble method in order to build a nutrition profile for the recipe as you build the recipe.&lt;br /&gt;Overall, the presentation was very interesting to see how you can manipulate Ruby to make ever more readable code.  However, in general I don't think it's worth the effort or the confusion for new developers trying to figure out how the code even works.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Working with others: Best Practices for Rails Teams - Luke Francl&lt;/h3&gt;Luke discussed some of the issues that can arise in a team environment, specifically for Rails.  He had a nice handout which unfortunately isn't available in softcopy form.  Here are the bullet points.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Migrations - Migrations have a tendency to stop working (what he calls &lt;span style="font-style: italic;"&gt;migration decay&lt;/span&gt;).  So, use the schema.rb as the authoritative source for your database schema.  When creating the database on another system run &lt;code&gt;rake db:schema:load&lt;/code&gt; instead of running the migrations.&lt;/li&gt;&lt;li&gt;Seed Data - Loading seed data in your migrations can break as the models change and fixtures are for test data.  Luke recommends using &lt;a href="http://code.google.com/p/db-populate"&gt;db-populate&lt;/a&gt; to populate seed data along with &lt;code&gt;ActiveRecord::Base.create_or_update&lt;/code&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def self.create_or_update(options = {})&lt;br /&gt;id = options.delete(:id)&lt;br /&gt;record = find_by_id(id) || new&lt;br /&gt;record.id = id&lt;br /&gt;record.attributes = options&lt;br /&gt;record.save!&lt;br /&gt;record&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;3rd party code - &lt;a href="http://errtheblog.com/posts/50-vendor-everything"&gt;vendor everything&lt;/a&gt; using &lt;a href="http://gemsonrails.rubyforge.org/"&gt;gemsonrails&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Security - HTML escape everything in your views with &lt;code&gt;h&lt;/code&gt;.  There are several XSS plugins that can be used as well.  Mass assignments (ex. &lt;code&gt;LineItem.new(params[:line_item])&lt;/code&gt;) can be dangerous, be sure to protect your attributes with:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;:attr_protected - attributes you can't write to (via mass parameter assignment)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;:attr_accessible - attributes you can write to&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Source control - The source code is the life of your project. Be sure to use source control management (SCM).  Commit atomic changes, not batches of changes with informative messages.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Bug tracking - Use a bug tracker that has some workflow for various states of completion as well as email integration to inform interested parties and SCM integration to link code fixes to bugs.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Continuous integration - Tis ties everything together.  Verifies that new code doesn't break anything else right away, ensures all libraries and code packages are available, etc.&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Rails on AIR - Peter Armstrong&lt;/h3&gt;&lt;br /&gt;This was mostly about using Flex and using Flex Builder (Eclipse). Flex is way to build Flash applications without knowing Flash and AIR allows your Flash application to run as a stand alone desktop applications. Frankly, I wasn't too interested in this.  It seems like a lot of work for not much gain.  Yes, Flash is installed in like 99% of browsers, however there are drawbacks to using it in web apps.  For example, you site with not be indexable be search engines which can reduce the traffic to your site, and I've been able to accomplish most everything I would need to use Flash for with HTML/CSS/JavaScript.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Keynote - Dan Benjamin&lt;/h3&gt;&lt;br /&gt;Dan provided some tips for developing good software development and interface design:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;As Simple As Possible, But No Simpler&lt;/li&gt;&lt;li&gt;Focus on Creating a Great User Experience&lt;/li&gt;&lt;li&gt;Anticipate User Actions&lt;/li&gt;&lt;li&gt;Think Like your Users&lt;/li&gt;&lt;li&gt;You're Probably Not as Good of a Designer as you Think&lt;/li&gt;&lt;li&gt;Develop for One Scenario, Not for Ten&lt;/li&gt;&lt;li&gt;Good Code Does Not Impress Users&lt;/li&gt;&lt;li&gt;Don't Release a Beta&lt;/li&gt;&lt;li&gt;Apologize (for any problem)&lt;/li&gt;&lt;li&gt;Just Ship It&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="text-align: center;"&gt;"simplicity is the key to happiness in the modern world"&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;That's all for day 1.  Watch for day two which has these topics:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;JRuby - Charles Nutter&lt;/li&gt;&lt;li&gt;Shining a Light on the Dark Magic of ActiveRecord - Anthony Eden&lt;/li&gt;&lt;li&gt;Smarticus University: BDD With RSpec - Bryan Liles&lt;/li&gt;&lt;li&gt;Adding Media to Your Rails Application - Dave Naffis and Josh Owens&lt;/li&gt;&lt;li&gt;Lessons from the Trenches – Learning from the Rails Bootcamp - Charles Brian Quinn&lt;/li&gt;&lt;li&gt;Keynote - Obie Fernandez&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-2916058375188455116?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.actsasconference.com/' title='acts_as_conference - day 1'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/2916058375188455116/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=2916058375188455116' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2916058375188455116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2916058375188455116'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/02/actsasconference-day-1.html' title='acts_as_conference - day 1'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-2384884264445431227</id><published>2008-01-22T22:28:00.000-05:00</published><updated>2008-01-23T15:01:21.884-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>My ImageFlow with Lightbox packaged sample</title><content type='html'>Per a request from Clemens to my &lt;a href="http://blog.pothoven.net/2008/01/imageflow-with-lightbox-lite.html"&gt;last post&lt;/a&gt;, I've packaged my revised version of &lt;a href="http://imageflow.finnrudolph.de/"&gt;ImageFlow&lt;/a&gt; and &lt;a href="http://www.huddletogether.com/projects/lightbox2/"&gt;Lightbox2&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;In addition, I've made my &lt;a href="http://www.pothoven.net/files/ImageFlow_LightBox.tgz" onclick="javascript:urchinTracker('files/ImageFlow_LightBox.tgz');"&gt;sample application&lt;/a&gt; issue an Ajax request to get the list of images to demonstrate how you can dynamically build the photo album.  Here is the code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// sample application&lt;br /&gt;var MyApp = function() {&lt;br /&gt; var imgTemplate = new Template('&lt;img src="reflect.php?img=#{filename}" longdesc="javascript:LightBoxLite.displayImage(\'#{filename}\')" alt="#{caption}" /&gt;');&lt;br /&gt;&lt;br /&gt;    function initializeAlbum(response) {&lt;br /&gt;        var imageList;&lt;br /&gt;        var imgHTML = '';&lt;br /&gt;&lt;br /&gt;        try {&lt;br /&gt;            imageList = response.responseJSON; &lt;br /&gt;        } catch (e) {&lt;br /&gt;            console.error(e, "\n", response.responseText);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // IE bug : for all other browsers an imageList.each() works great here,&lt;br /&gt;        // however IE doesn't like it so we revert to a for loop&lt;br /&gt;        for (var i = 0; i &lt; imageList.length; i++) {&lt;br /&gt;            var filename = imageList[i];&lt;br /&gt;            // IE bug : IE computes an incorrect value for imageList.length resulting &lt;br /&gt;            // in undefined filenames in the loop, check for that&lt;br /&gt;            if (filename) {&lt;br /&gt;                // for simplicity, extract the base filename for the caption.  &lt;br /&gt;                // Caption could also be defined for each image in the JSON response data.&lt;br /&gt;                var caption = filename.split('/').pop().split('.')[0].gsub("%20", " ");&lt;br /&gt;                imgHTML += imgTemplate.evaluate({filename : filename, caption : caption});&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        $('images').innerHTML = imgHTML;&lt;br /&gt;        // IE bug : IE needs a moment to let the innerHTML change register before proceeding&lt;br /&gt;        // so we add a small timeout to let it catch its breath&lt;br /&gt;        setTimeout(ImageFlow.initialize, 10);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return {&lt;br /&gt;        loadAlbum : function(albumName) {&lt;br /&gt;            $('startButton').hide();&lt;br /&gt;            $('photoAlbum').show();&lt;br /&gt;            var myAjax = new Ajax.Request('imageList.php?albumName=' + albumName, {&lt;br /&gt;                method: 'get',&lt;br /&gt;                onSuccess: initializeAlbum&lt;br /&gt;                });&lt;br /&gt;        }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;}();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The initial button you see in the sample application, invokes the &lt;code&gt;loadAlbum()&lt;/code&gt; function when clicked as shown:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;input type="button" value="Click here to load photo album" onclick="MyApp.loadAlbum('FlickrEyeCandy');"&amp;gt;&amp;lt;/input&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Download the packaged &lt;a href="http://www.pothoven.net/files/ImageFlow_LightBox.tgz" onclick="javascript:urchinTracker('files/ImageFlow_LightBox.tgz');"&gt;sample application&lt;/a&gt; shown below.&lt;br /&gt;&lt;br /&gt;&lt;iframe style="width: 700px; height: 600px; border: 1px solid #aaa" src="http://www.pothoven.net/ImageFlow"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update January 23, 3pm EST: &lt;/b&gt;It was reported that the sample application didn't work in IE.  This was due to some deficiencies in IE which I have accounted for now.  I have pointed them out in the code with the comments beginning with &lt;code&gt;IE bug&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-2384884264445431227?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/2384884264445431227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=2384884264445431227' title='18 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2384884264445431227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2384884264445431227'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/01/my-imageflow-with-lightbox-packaged.html' title='My ImageFlow with Lightbox packaged sample'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>18</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-7542908699707818896</id><published>2008-01-18T16:42:00.000-05:00</published><updated>2008-01-23T09:32:21.352-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>ImageFlow with Lightbox Lite</title><content type='html'>I like the idea of &lt;a href="http://194.95.111.244/~countzero/scripts/_myImageFlowLightbox2/"&gt;using ImageFlow with Lightbox2&lt;/a&gt;, however, I don't like that it required a tweaked version of ImageFlow, nor that I then had to modify my image list to wrap the &lt;a href="http://www.huddletogether.com/projects/lightbox2/"&gt;Lightbox2&lt;/a&gt; elements around them.  Additionally, Lightbox2 adds a lot of functionality that's not being using in this case (all the next/previous image controls, image caching, etc), and it also wants to initialize itself during the window.onload event which I don't want in my Ajax apps.&lt;br /&gt;&lt;br /&gt;So, wrote my own Lightbox Lite.  Well, technically, I extracted my own Lightbox Lite from the Lightbox2 code with a few modifications.&lt;br /&gt;&lt;br /&gt;To begin with, I added this block of HTML to my web page.  The Lightbox2 code constructs this dynamically in its &lt;code&gt;initialize&lt;/code&gt; function, but I don't see the advantage of that vs just hard coding it into the page to begin with.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;div id="photo" style="display:none"&amp;gt;&lt;br /&gt;    &amp;lt;div id="lightbox"&amp;gt;&lt;br /&gt;    &amp;lt;div id="outerImageContainer"&amp;gt;&lt;br /&gt;        &amp;lt;div id="imageContainer"&amp;gt;&lt;br /&gt;            &amp;lt;img id="lightboxImage" style="display:none"&amp;gt;&amp;lt;/img&amp;gt;&lt;br /&gt;            &amp;lt;div id="imageloading"&amp;gt;&lt;br /&gt;                &amp;lt;img src="images/loadingImage.gif"&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div id="imageDataContainer" style="display:none"&amp;gt;&lt;br /&gt;        &amp;lt;div id="imageData"&amp;gt;&lt;br /&gt;             &amp;lt;div id="imageDetails"&amp;gt;&lt;br /&gt;                 &amp;lt;span id="imagecaption"&amp;gt;&amp;lt;/span&amp;gt;&lt;br /&gt;             &amp;lt;/div&amp;gt;&lt;br /&gt;             &amp;lt;div id="imageBottomNav" onclick="LightBoxLite.reset();" style="float:right" &amp;gt;&lt;br /&gt;                 &amp;lt;img src="images/close-button.png" alt="Close" title="Close"&amp;gt;&lt;br /&gt;             &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then I added the relevant CSS rules to my stylesheet:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#photo {&lt;br /&gt; z-index: 51;&lt;br /&gt; position: absolute;&lt;br /&gt; top: 5px;&lt;br /&gt; left: 1px;&lt;br /&gt; width: 100%;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#outerImageContainer {&lt;br /&gt; background-color: white;&lt;br /&gt; width: 250px;&lt;br /&gt; height: 250px;&lt;br /&gt; margin: 0 auto;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#imageContainer {&lt;br /&gt; padding-top: 10px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#imageloading {&lt;br /&gt; position: absolute;&lt;br /&gt; top: 40%;&lt;br /&gt; left: 47.5%;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#imageDataContainer {&lt;br /&gt; font: 10px Verdana, Helvetica, sans-serif;&lt;br /&gt; background-color: white;&lt;br /&gt; margin: 0 auto;&lt;br /&gt; line-height: 1.4em;&lt;br /&gt; overflow: auto;&lt;br /&gt; width: 100%;&lt;br /&gt; padding-bottom: 5px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#imageData {&lt;br /&gt; padding: 0 10px;&lt;br /&gt; color: #666;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#imageData #imageDetails {&lt;br /&gt; width: 80%;&lt;br /&gt; float: left;&lt;br /&gt; text-align: left;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#imageData #imageCaption {&lt;br /&gt; font-weight: bold;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next I added the Lightbox Lite code to my JavaScript (note, you still need prototype.js and script.aculo.us):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;LightBoxLite = function() {&lt;br /&gt;    &lt;br /&gt;    var borderSize = 10;&lt;br /&gt;    var resizeDuration = 0.6;&lt;br /&gt;    &lt;br /&gt;    /**&lt;br /&gt;     * resizeImageContainer&lt;br /&gt;     *&lt;br /&gt;     * @param {Number} desired width&lt;br /&gt;     * @param {Number} desired height&lt;br /&gt;     */&lt;br /&gt;    function resizeImageContainer( imgWidth, imgHeight) {&lt;br /&gt;&lt;br /&gt;        // get current width and height&lt;br /&gt;        var widthCurrent = $('outerImageContainer').getWidth();&lt;br /&gt;        var heightCurrent = $('outerImageContainer').getHeight();&lt;br /&gt;&lt;br /&gt;        // get new width and height&lt;br /&gt;        var widthNew = (imgWidth  + (borderSize * 2));&lt;br /&gt;        var heightNew = (imgHeight  + (borderSize * 2));&lt;br /&gt;&lt;br /&gt;        // scalars based on change from old to new&lt;br /&gt;        var xScale = ( widthNew / widthCurrent) * 100;&lt;br /&gt;        var yScale = ( heightNew / heightCurrent) * 100;&lt;br /&gt;&lt;br /&gt;        // calculate size difference between new and old image, and resize if necessary&lt;br /&gt;        var wDiff = widthCurrent - widthNew;&lt;br /&gt;        var hDiff = heightCurrent - heightNew;&lt;br /&gt;&lt;br /&gt;        if (!( hDiff === 0)) {&lt;br /&gt;            new Effect.Scale('outerImageContainer', yScale, {scaleX: false, duration: resizeDuration, queue: 'front'}); &lt;br /&gt;        }&lt;br /&gt;        if (!( wDiff === 0)) {&lt;br /&gt;            new Effect.Scale('outerImageContainer', xScale, {scaleY: false, delay: resizeDuration, duration: resizeDuration}); &lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        $('imageDataContainer').style.width = widthNew + "px";&lt;br /&gt;&lt;br /&gt;        showImage();&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    /**&lt;br /&gt;     * updateDetails&lt;br /&gt;     * &lt;br /&gt;     */&lt;br /&gt;    function updateDetails() {&lt;br /&gt;        var caption = $('lightboxImage').src.split('/').pop().split('.')[0].gsub("%20", " ");&lt;br /&gt;        $('imagecaption').innerHTML = caption; &lt;br /&gt;        &lt;br /&gt;        new Effect.Parallel(&lt;br /&gt;            [ new Effect.SlideDown( 'imageDataContainer', { sync: true, duration: resizeDuration, from: 0.0, to: 1.0 }), &lt;br /&gt;              new Effect.Appear('imageDataContainer', { sync: true, duration: resizeDuration }) ] &lt;br /&gt;        );&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    /**&lt;br /&gt;     * showImage&lt;br /&gt;     * Display image and begin preloading neighbors.&lt;br /&gt;     */&lt;br /&gt;    function showImage() {&lt;br /&gt;        $('imageloading').hide();&lt;br /&gt;        new Effect.Appear('lightboxImage', { duration: resizeDuration, queue: 'end', afterFinish: updateDetails });&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    function checkForPreloadComplete(imgPreloader) {&lt;br /&gt;        if (imgPreloader.complete) {&lt;br /&gt;            $('lightboxImage').src = imgPreloader.src;&lt;br /&gt;            resizeImageContainer(imgPreloader.width, imgPreloader.height);&lt;br /&gt;        } else {&lt;br /&gt;            setTimeout(checkForPreloadComplete.bind(this, imgPreloader), 100);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return {&lt;br /&gt;        /**&lt;br /&gt;         * displayImage&lt;br /&gt;         * display an image in a lightbox&lt;br /&gt;         *&lt;br /&gt;         * @param {String} URL of image&lt;br /&gt;         */&lt;br /&gt;        displayImage : function(imageUrl) {&lt;br /&gt;            $('imageloading').show();&lt;br /&gt;            $('lightboxImage').hide()&lt;br /&gt;            $('imageDataContainer').hide()&lt;br /&gt;            $('photo').show();&lt;br /&gt;&lt;br /&gt;            var imgPreloader = new Image();&lt;br /&gt;            imgPreloader.src = imageUrl;&lt;br /&gt;&lt;br /&gt;            // once image is preloaded, resize image container&lt;br /&gt;            setTimeout(checkForPreloadComplete.bind(this, imgPreloader), 100);&lt;br /&gt;        },&lt;br /&gt;        &lt;br /&gt;        reset : function() {&lt;br /&gt;            $('photo').hide();&lt;br /&gt;            $('imageloading').show();&lt;br /&gt;            $('lightboxImage').hide()&lt;br /&gt;            $('imageDataContainer').hide()&lt;br /&gt;            $('lightboxImage').src = "";&lt;br /&gt;            $('outerImageContainer').style.width = "250px";&lt;br /&gt;            $('outerImageContainer').style.height = "250px";&lt;br /&gt;            &lt;br /&gt;            &lt;br /&gt;        }&lt;br /&gt;    };&lt;br /&gt;}();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The only alteration you have to do to your ImageFlow images is to add a call to the &lt;code&gt;LightBoxLite.displayImage&lt;/code&gt; function in the &lt;code&gt;longdesc&lt;/code&gt; attribute of your images, which should currently have the address of the original image to display when you click on it in the image flow.  So, instead of:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;img src="reflect.php?img=myimage.jpg" longdesc="myimage.jpg" alt="myimage" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It becomes:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;img src="reflect.php?img=myimage.jpg" longdesc="javascript:LightBoxLite.displayImage('myimage.jpg')" alt="myimage" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here is an &lt;a href="http://www.pothoven.net/ImageFlow"&gt;example&lt;/a&gt; of it being used.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update January 20: &lt;/b&gt;I tweaked a couple CSS rules to correctly center the loading indicator for Internet Explorer.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update January 23: &lt;/b&gt;I received a request to package my modifications. See my &lt;a href="http://blog.pothoven.net/2008/01/my-imageflow-with-lightbox-packaged.html"&gt;new blog entry&lt;/a&gt; where I have done that in addition to demonstrating how to add Ajax to receive the image list.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-7542908699707818896?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/7542908699707818896/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=7542908699707818896' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7542908699707818896'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7542908699707818896'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/01/imageflow-with-lightbox-lite.html' title='ImageFlow with Lightbox Lite'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-1278964204109203411</id><published>2008-01-17T17:08:00.000-05:00</published><updated>2008-01-23T09:28:29.947-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>ImageFlow (improved)</title><content type='html'>&lt;a href="http://194.95.111.244/~countzero/myCMS/index.php?c_id=12&amp;s_id=13"&gt;Finn Rudolph&lt;/a&gt; has created a very nice "cover flow" type of control in JavaScript called &lt;a href="http://194.95.111.244/%7Ecountzero/scripts/_myImageFlow"&gt;ImageFlow&lt;/a&gt; (which is an improvement of &lt;a href="hthttp://www.adventuresinsoftware.com/blog/?p=104"&gt;Michael L. Perry's Cover flow&lt;/a&gt;).  &lt;br /&gt;&lt;br /&gt;It's a very nice package and quite easy to use!  However, it currently has a few code shortcomings.  My primary issues with it was that it defined everything in a global scope (with common names that easily clash with other functions) and that it assumes the photos are in a static page and thus initializes itself when the web page loads.  The later issue was the biggest thing I needed to fix since I wanted to use it in an Ajax application where I'm getting the images as a result of an XMLHttpRequest.&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;I added an "ImageFlow" scoping around the whole package so that all the variables and particularly the functions aren't global to my whole application (and potentially clashing).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I added "var" declarations for many of the variables which didn't specify "var" so that they would not become fully global variables, but only global to the ImageFlow scope.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I changed the "onload" function to be an "initialize()" function so that I could execute it when my page was ready&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I made all the variables and functions totally private to ImageFlow with the only publicly visible function being "intialize()"&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Since the images in the image flow div are now loaded sometime after the page is loaded, I added a check to the initialize function determine when all images are really loaded (otherwise some would be sized wrong).&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Now, whenever you're ready for the ImageFlow to be displayed, just call "ImageFlow.initialize()"&lt;br /&gt;&lt;br /&gt;If you're interested in these updates, you can download my altered &lt;a href="http://www.pothoven.net/files/imageflow.js" onclick="javascript:urchinTracker('files/imageflow.js');"&gt;imageflow.js&lt;/a&gt;.  I have also submitted them to Finn, so hopefully they will be adopted into the official version.&lt;br /&gt;&lt;br /&gt;NOTE: The file was updated on January 18, 10:00am EST after running it through &lt;a href="http://www.jslint.com/"&gt;JSLint&lt;/a&gt; -- which I should have done before posting it originally.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update January 18: &lt;/b&gt;I've added a new &lt;a href="http://blog.pothoven.net/2008/01/imageflow-with-lightbox-lite.html"&gt;article&lt;/a&gt; describing the addition of a scaled down version of &lt;a href="http://www.huddletogether.com/projects/lightbox2/"&gt;Lightbox2&lt;/a&gt; without modifying ImageFlow.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-1278964204109203411?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/1278964204109203411/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=1278964204109203411' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1278964204109203411'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1278964204109203411'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/01/imageflow-improved.html' title='ImageFlow (improved)'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-7071660986672722745</id><published>2008-01-11T08:53:00.001-05:00</published><updated>2008-02-13T16:02:05.368-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ruby on Rails'/><title type='text'></title><content type='html'>&lt;a href="http://www.actsasconference.com/" title="Rails For All presents acts_as_conference"&gt;&lt;img src="http://www.actsasconference.com/images/badge_med_attendee.png" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-7071660986672722745?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/7071660986672722745/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=7071660986672722745' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7071660986672722745'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/7071660986672722745'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2008/01/rails-for-all-presents-actsasconference.html' title=''/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-5356723914484065742</id><published>2007-12-19T15:35:00.000-05:00</published><updated>2007-12-19T16:00:30.906-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><title type='text'>Accordion select list</title><content type='html'>Kevin Miller has created an &lt;a href="http://stickmanlabs.com/accordion/"&gt;accordion&lt;/a&gt; widget using &lt;a href="http://www.prototypejs.org"&gt;prototype&lt;/a&gt; and &lt;a href="http://script.aculo.us"&gt;script.aculo.us&lt;/a&gt;.  In this article I'll demonstrate how to build a selection list that has sub-sections utilizing the accordion control.&lt;br /&gt;&lt;br /&gt;Here is a demonstation comparing a basic select list control and the accordion select list:&lt;br /&gt;&lt;iframe style="width:430px; height: 250px; border: 1px solid #aaa" src="http://www.pothoven.net/accordionSelect.html"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;To make the control work, you first add a read-only text box with the drop down button image floating to the right of it, for example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;span style="float: right"&amp;gt;&lt;br /&gt; &amp;lt;img id="accSelectToggle" src="images/btn_dropdown.png" width="21" height="16" alt="Expand list" title="Expand list" /&amp;gt;&lt;br /&gt;&amp;lt;/span&amp;gt;&lt;br /&gt;&amp;lt;input type="text" id="accSelect" title="List" readonly="readonly" value="Option 1.1" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You then define a &lt;code&gt;div&lt;/code&gt; which contains the accordion control, such as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;div id="accSelectOptions" class="accordion_container" style="display: none"&amp;gt;&lt;br /&gt;&amp;lt;h1 class="accordion_toggle"&amp;gt;Category 1&amp;lt;/h1&amp;gt;&lt;br /&gt;&amp;lt;div class="accordion_content"&amp;gt;&lt;br /&gt;&amp;lt;ul&amp;gt;&lt;br /&gt; &amp;lt;li value="option11"&amp;gt;Option 1.1&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li value="option12"&amp;gt;Option 1.2&amp;lt;/li&amp;gt;&lt;br /&gt; &amp;lt;li value="option13"&amp;gt;Option 1.3&amp;lt;/li&amp;gt;&lt;br /&gt;&amp;lt;/ul&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Add your preferred CSS styling.  In my example the CSS looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#accSelect {&lt;br /&gt; width: 169px;&lt;br /&gt; height: 14px;&lt;br /&gt; border-style: none;&lt;br /&gt; padding-left: 4px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.accordion_container {&lt;br /&gt; z-index: 10;&lt;br /&gt; position: absolute;&lt;br /&gt; width: 198px;&lt;br /&gt; border: solid 1px black;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.accordion_toggle {&lt;br /&gt; display: block;&lt;br /&gt; padding: 0.3em 0.5em;&lt;br /&gt; font-weight: normal;&lt;br /&gt; font-size: 1em;&lt;br /&gt; text-decoration: none;&lt;br /&gt; outline: none;&lt;br /&gt; border-bottom: 1px solid #def;&lt;br /&gt; color: #000000;&lt;br /&gt; cursor: pointer;&lt;br /&gt; margin: 0px;&lt;br /&gt; background-image: url('images/expand.gif');&lt;br /&gt; background-position: 99% 50%;&lt;br /&gt; background-repeat: no-repeat;&lt;br /&gt; padding-right: 5px;&lt;br /&gt; background-color: #9BE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.accordion_toggle_active {&lt;br /&gt; color: #ffffff;&lt;br /&gt; font-weight: bolder;&lt;br /&gt; background-image: url('images/ns-expand.gif');&lt;br /&gt; background-color: #47B;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.accordion_content {&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; overflow: auto;&lt;br /&gt; display: block;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.accordion_content ul {&lt;br /&gt; padding-left: 1em;&lt;br /&gt; margin: 0;&lt;br /&gt; padding-right: 0;&lt;br /&gt; padding-bottom: 0px;&lt;br /&gt; list-style-type: none&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.accordion_content li {&lt;br /&gt; margin: auto;&lt;br /&gt; padding: 0px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.highlight {&lt;br /&gt; background-color: #DDD;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And finally add a little JavaScript to make the magic happen:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    function  toggleAccordionSelect(event) {&lt;br /&gt;        var image = event.target;&lt;br /&gt;        var collapseIcon = "images/btn_dropdown-selected.png";&lt;br /&gt;        var expandIcon = "images/btn_dropdown.png";&lt;br /&gt;&lt;br /&gt;        if ($('accSelectOptions').style.display === 'none') {&lt;br /&gt;            image.src = collapseIcon;&lt;br /&gt;            image.alt = "Collapse List";&lt;br /&gt;            image.title = "Collapse List";&lt;br /&gt;            $('accSelectOptions').show();&lt;br /&gt;        }&lt;br /&gt;        else {&lt;br /&gt;            image.src = expandIcon;&lt;br /&gt;            image.alt = "Expand List";&lt;br /&gt;            image.title = "Expand List";&lt;br /&gt;            $('accSelectOptions').hide();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    function toggleHighlight(event) {&lt;br /&gt;        var item = event.target;&lt;br /&gt;        item.toggleClassName('highlight');&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    function selectAccordionOption(event) {&lt;br /&gt;        var selectedOption = Event.element(event);&lt;br /&gt;&lt;br /&gt;        // update the selection input field value to the selected display value&lt;br /&gt;        $('accSelect').value = selectedOption.innerHTML;&lt;br /&gt;&lt;br /&gt;        // internal value is stored as an attribute, do whatever you need to with it&lt;br /&gt;        var realValue = selectedOption.getAttribute('value');&lt;br /&gt;        &lt;br /&gt;        // hide the accordion list&lt;br /&gt;        toggleAccordionSelect({target: $('accSelectToggle')});       &lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;    var accordionSelect = new accordion('accSelectOptions', {&lt;br /&gt;        classNames : {&lt;br /&gt;            toggle : 'accordion_toggle',&lt;br /&gt;            toggleActive : 'accordion_toggle_active',&lt;br /&gt;            content : 'accordion_content'&lt;br /&gt;            },&lt;br /&gt;        onEvent : 'mousedown'&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;    // By default, open first accordion in the drop down&lt;br /&gt;    var accordionToggles = $$('#accSelectOptions .accordion_toggle');&lt;br /&gt;    accordionSelect.activate(accordionToggles[0]);&lt;br /&gt;&lt;br /&gt;    // register event observers        &lt;br /&gt;    Event.observe('accSelectToggle', 'mousedown', toggleAccordionSelect);&lt;br /&gt;    $$('#accSelectOptions .accordion_content li').each(function(accSelectOption) {&lt;br /&gt;        Event.observe(accSelectOption, 'mousedown', selectAccordionOption);&lt;br /&gt;        Event.observe(accSelectOption, 'mouseover', toggleHighlight);&lt;br /&gt;        Event.observe(accSelectOption, 'mouseout', toggleHighlight);&lt;br /&gt;    });&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-5356723914484065742?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/5356723914484065742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=5356723914484065742' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/5356723914484065742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/5356723914484065742'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/12/accordion-select-list.html' title='Accordion select list'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-3744996709856706621</id><published>2007-12-19T14:24:00.001-05:00</published><updated>2008-02-22T16:38:47.194-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><title type='text'>Aborting Ajax requests (for prototype.js)</title><content type='html'>Sometimes your slick Ajax application may have submitted a request that you no longer care about and want to abort.  For example, perhaps you've implemented a type-ahead feature and the user has now typed another character prior to the first results returning. Or perhaps you fetch values for other parts of a form based on user selections and the user changed their choice prior to the first response returning.&lt;br /&gt;&lt;br /&gt;I was surprised to see that the &lt;a href="http://www.prototypejs.org"&gt;prototype.js&lt;/a&gt; library does not include an abort method for its Ajax.Request object.  So, here's my implementation of Ajax.Request.abort():&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/**&lt;br /&gt; * Ajax.Request.abort&lt;br /&gt; * extend the prototype.js Ajax.Request object so that it supports an abort method&lt;br /&gt; */&lt;br /&gt;Ajax.Request.prototype.abort = function() {&lt;br /&gt;    // prevent and state change callbacks from being issued&lt;br /&gt;    this.transport.onreadystatechange = Prototype.emptyFunction;&lt;br /&gt;    // abort the XHR&lt;br /&gt;    this.transport.abort();&lt;br /&gt;    // update the request counter&lt;br /&gt;    Ajax.activeRequestCount--;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To use this function, you need to keep a handle on the Ajax request you want to abort.  So, somewhere in you code you'll have something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    var myAjaxRequest = new Ajax.Request(requestUrl, {[request options]});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If you want to abort that request, simply call:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    myAjaxRequest.abort();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold"&gt;Update - Feb. 22, 2008:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;It was pointed out in the comments that the &lt;code&gt;Ajax.activeRequestCount&lt;/code&gt; can occasionally become negative.  I have been able to replicate that situation, while at the same time confirming that it does not consistently happen.  This leads me to believe that it's most likely a timing issue such as the &lt;code&gt;abort&lt;/code&gt; is issued after the response has already started to be received and/or processed so that both the response processing decrements the counter as well as the abort.&lt;br /&gt;&lt;br /&gt;My personal work-around for this is to add these lines to the end of my &lt;code&gt;onComplete&lt;/code&gt; handler:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if (Ajax.activeRequestCount &amp;lt; 0) {&lt;br /&gt;    Ajax.activeRequestCount = 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I don't want to remove the counter decrement from the &lt;code&gt;abort&lt;/code&gt; function or else cleanly aborted requests will leave the activeRequestCount &amp;gt; 0 when there are no real outstanding requests.&lt;br /&gt;&lt;br /&gt;If anyone has a better solution, I'd be interested to hear from you.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-3744996709856706621?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/3744996709856706621/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=3744996709856706621' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3744996709856706621'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3744996709856706621'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/12/aborting-ajax-requests-for-prototypejs.html' title='Aborting Ajax requests (for prototype.js)'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-3550472785897171151</id><published>2007-12-13T11:36:00.000-05:00</published><updated>2007-12-13T12:03:59.249-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Performance-based Web App Functionality</title><content type='html'>In the development of some CPU intensive web applications, I've come to realize that sometimes I need to throttle down some of the features of the application in order to maintain good response times for a better user experience.  So, I've added what I refer to as &lt;a href="http://www.pothoven.net/bogoMips.html"&gt;jsBogoMips&lt;/a&gt; to some of my applications.&lt;br /&gt;&lt;br /&gt;&lt;iframe style="width: 770px; height: 1650px; border: 1px solid #aaa" src="http://www.pothoven.net/bogoMips.html"&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;br /&gt;The above example shows throttling of visual effects, but it can be used to throttle other things as well.  For example, in the application I actually developed this for, while it does use script.aculo.us visual effects, what I really needed to be throttled was the generation of an SVG chart from an arbitrary amount data elements received in XML data.  So, based on the jsBogoMips value, I control how many data points are charted.&lt;br /&gt;&lt;br /&gt;If you're interested in some alterative JavaScript performance testing, you might also be interested in &lt;a href="http://celtickane.com/webdesign/jsspeed2007.php"&gt;Celtic Kane's JavaScript Speed Test&lt;/a&gt; and &lt;a href="http://nontroppo.org/timer/kestrel_tests/"&gt;Performance Tests for Opera 9.5&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-3550472785897171151?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.pothoven.net/bogoMips.html' title='Performance-based Web App Functionality'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/3550472785897171151/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=3550472785897171151' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3550472785897171151'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3550472785897171151'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/12/performance-based-web-app-functionality.html' title='Performance-based Web App Functionality'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-1248601462629319504</id><published>2007-10-02T12:31:00.000-04:00</published><updated>2007-12-19T16:38:48.053-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>Bishop Swap Puzzle</title><content type='html'>As one final tribute to the &lt;a href="http://en.wikipedia.org/wiki/The_7th_Guest"&gt;7th Guest&lt;/a&gt; chess puzzles, I've added the &lt;a href="http://www.pothoven.net/bishopPuzzle.html" onclick="javascript:urchinTracker('/bishopPuzzle.html');"&gt;Bishop Swap Puzzle&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.pothoven.net/bishopPuzzle.html" onclick="javascript:urchinTracker('/bishopPuzzle.html');"&gt;GIVE IT A TRY!&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_l-kZv4tdloI/R2mPTlrad8I/AAAAAAAAAFA/TRyyczMnT6c/s1600-h/Screenshot-Bishop+Swap+Puzzle.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp3.blogger.com/_l-kZv4tdloI/R2mPTlrad8I/AAAAAAAAAFA/TRyyczMnT6c/s320/Screenshot-Bishop+Swap+Puzzle.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5145801615941990338" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;p style="font-weight: bold; font-size: 1.2em;"&gt;Summary of Puzzle Experience&lt;/p&gt;&lt;br /&gt;Each of these puzzles had unique problems which were fun to work out and gained in complexity (though each whole puzzle only took a couple hours to code, so they weren't terribly complex).&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;The &lt;a href="http://www.pothoven.net/queenPuzzle.html" onclick="javascript:urchinTracker('/queenPuzzle.html');"&gt;8 Queens Puzzle&lt;/a&gt; wasn't moving any pieces, so I could just put the queen wherever the user clicked.  The only complexity was to check for conflicting queens in all directions and remove them.  Of course, my initial goal wasn't to make an interactive game, but a solution generator so my wife's move selection strategy was key, but very simple to implement.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The &lt;a href="http://www.pothoven.net/knightPuzzle.html" onclick="javascript:urchinTracker('/knightPuzzle.html');"&gt;Knight Puzzle&lt;/a&gt; added the animation for user moves which was more visually interesting.  However, since there's only one open square, you don't have to worry about multiple moves for any given piece.  The piece can either be moved to the open square or it can't.  There were two interesting algorithms necessary for the knight puzzle.  The first is pretty simple and is just the computation of valid "L" shaped moves which is done taking the absolute value of the differences of the coordinates of the piece to move and the blank square.  If the absolute value of the differences is 1 &amp;amp; 2 (or 2 &amp;amp; 1), then it's valid.&lt;br /&gt;The second algorithm is a little more interesting.  It is how to determine if all the pieces are in the correct location.  For this, I added a little weighting of the row and column indicies as follows:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;let the computedValue = (row * 2) + (column * 3)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;not done if:&lt;ul&gt;&lt;li&gt;any white knights have a computedValue &amp;gt; 10&lt;/li&gt;&lt;br /&gt;&lt;li&gt;any black knights have a computedValue &amp;lt; 10&lt;/li&gt;&lt;br /&gt;&lt;li&gt;any piece has a computedValue == 10&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;The following matrix demonstrates this:&lt;br /&gt;&lt;table style="margin: 0px auto; padding: 2px; width: 200px;"&gt;&lt;br /&gt;&lt;colgroup&gt;&lt;col width="33"&gt;&lt;col width="33"&gt;&lt;col width="33"&gt;&lt;col width="33"&gt;&lt;/colgroup&gt;&lt;colwidth="33px"&gt;&lt;/colwidth="33px"&gt;&lt;col width="33"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;y\x&lt;/th&gt;&lt;th&gt;0&lt;/th&gt;&lt;th&gt;1&lt;/th&gt;&lt;th&gt;2&lt;/th&gt;&lt;th&gt;3&lt;/th&gt;&lt;th&gt;4&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;0&lt;/th&gt;&lt;td&gt;0&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;1&lt;/th&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;14&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;2&lt;/th&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;3&lt;/th&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;18&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;th&gt;4&lt;/th&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;14&lt;/td&gt;&lt;td&gt;17&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The &lt;a href="http://www.pothoven.net/bishopPuzzle.html" onclick="javascript:urchinTracker('/bishopPuzzle.html');"&gt;Bishop Puzzle&lt;/a&gt; added the potential of multiple moves for any given bishop selection.  So, I had to keep track of what piece was selected, determine which possible moves were available, and when multiple moves are available, highlight them and allow the user to select one.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-1248601462629319504?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.pothoven.net/bishopPuzzle.html' title='Bishop Swap Puzzle'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/1248601462629319504/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=1248601462629319504' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1248601462629319504'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1248601462629319504'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/10/bishop-swap-puzzle.html' title='Bishop Swap Puzzle'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_l-kZv4tdloI/R2mPTlrad8I/AAAAAAAAAFA/TRyyczMnT6c/s72-c/Screenshot-Bishop+Swap+Puzzle.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-3902400227308559641</id><published>2007-09-27T10:08:00.007-04:00</published><updated>2008-05-01T16:06:38.412-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>Knight Swap Puzzle</title><content type='html'>I decided to write another &lt;a href="http://en.wikipedia.org/wiki/The_7th_Guest"&gt;7th Guest&lt;/a&gt; inspired game, the &lt;a href="http://www.pothoven.net/knightPuzzle.html" onClick="javascript:urchinTracker('/knightPuzzle.html');"&gt;Knight Swap&lt;/a&gt; game.&lt;br /&gt;&lt;br /&gt;This time I added some animations from the &lt;a href="http://script.aculo.us/"&gt;Script.aculo.us&lt;/a&gt; library to make it a little more visually interesting.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.pothoven.net/knightPuzzle.html" onClick="javascript:urchinTracker('/knightPuzzle.html');"&gt;GIVE IT A TRY!&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_l-kZv4tdloI/R2mOU1rad6I/AAAAAAAAAEU/LS1CkDfwoZ0/s1600-h/Screenshot-Knight+Swap+Puzzle.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp0.blogger.com/_l-kZv4tdloI/R2mOU1rad6I/AAAAAAAAAEU/LS1CkDfwoZ0/s320/Screenshot-Knight+Swap+Puzzle.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5145800537905199010" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-3902400227308559641?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.pothoven.net/knightPuzzle.html' title='Knight Swap Puzzle'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/3902400227308559641/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=3902400227308559641' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3902400227308559641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3902400227308559641'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/09/knight-swap-puzzle.html' title='Knight Swap Puzzle'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_l-kZv4tdloI/R2mOU1rad6I/AAAAAAAAAEU/LS1CkDfwoZ0/s72-c/Screenshot-Knight+Swap+Puzzle.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-8219933152637502652</id><published>2007-09-25T11:00:00.001-04:00</published><updated>2007-12-19T16:36:13.888-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><title type='text'>Eight Queens Puzzle</title><content type='html'>&lt;p&gt;Recently, my wife decided to re-visit some of our old games and was playing &lt;a href="http://en.wikipedia.org/wiki/The_7th_Guest"&gt;The 7th Guest&lt;/a&gt;.&amp;nbsp; Several of the puzzles in that games are chess based, and one is the &lt;a href="http://www.pothoven.net/queenPuzzle.html" onClick="javascript:urchinTracker('/queenPuzzle.html');"&gt;Eight Queens Puzzle&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;I decided it would be a fun and fairly simple task to build a JavaScript implementation of that game which would also attempt to solve it.&lt;/p&gt; &lt;p&gt;My first pass merely randomly placed queens on the board (and subsequently removed the conflicting pieces) as a brut force method to stumble upon the solution.&amp;nbsp; I let that version run all day and it never found a solution -- which according to the &lt;a href="http://en.wikipedia.org/wiki/Eight_queens_puzzle"&gt;Wikipedia entry&lt;/a&gt;, there are 92 of.&lt;/p&gt; &lt;p&gt;Then I added the logic she used to solve the puzzle which was to only add a new queen if it either didn't remove any others, or only removed one other queen.&amp;nbsp; That technique worked surprisingly well usually only requires the program to take from the 10s to the low 100s of attempts before it has a solution and it much less complex that the solution described in the Wikipedia entry.&lt;/p&gt; &lt;p&gt;In only took a couple hours to build, and in the end, I have a very simple program which will allow you to try your hand at solving the puzzle yourself, or can quickly figure out a solution from whatever you have on the board when you give up.&lt;/p&gt; &lt;p&gt;&lt;a href="http://www.pothoven.net/queenPuzzle.html" onClick="javascript:urchinTracker('/queenPuzzle.html');"&gt;GIVE IT A TRY!&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_l-kZv4tdloI/R2mOnVrad7I/AAAAAAAAAEc/mo4WTy67z4c/s1600-h/Screenshot-Eight+Queens+Puzzle.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp2.blogger.com/_l-kZv4tdloI/R2mOnVrad7I/AAAAAAAAAEc/mo4WTy67z4c/s320/Screenshot-Eight+Queens+Puzzle.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5145800855732778930" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-8219933152637502652?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.pothoven.net/queenPuzzle.html' title='Eight Queens Puzzle'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/8219933152637502652/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=8219933152637502652' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/8219933152637502652'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/8219933152637502652'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/09/eight-queens-puzzle.html' title='Eight Queens Puzzle'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp2.blogger.com/_l-kZv4tdloI/R2mOnVrad7I/AAAAAAAAAEc/mo4WTy67z4c/s72-c/Screenshot-Eight+Queens+Puzzle.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-6648073211835801181</id><published>2007-08-22T16:07:00.002-04:00</published><updated>2008-05-02T16:18:47.745-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mobile phone'/><title type='text'>Tips for new Samsung Sync Users</title><content type='html'>&lt;p&gt;As I mentioned &lt;a href="http://blog.pothoven.net/2007/08/enjoying-high-speed-wireless.html"&gt;earlier&lt;/a&gt;, I recently acquired a &lt;a href="http://www.samsung.com/us/consumer/detail/detail.do?group=mobilephones&amp;type=mobilephones&amp;amp;subtype=att&amp;model_cd=SGH-A707DAACIN"&gt;Samsung A707&lt;/a&gt; which is provided by at&amp;amp;t/Cingular.  In this article I'm going to recommend some software and other customizations to make it more useful.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;&lt;strong&gt;Access high-speed Internet everywhere&lt;br /&gt;&lt;/strong&gt;I described this in my earlier article, &lt;a href="http://blog.pothoven.net/2007/08/enjoying-high-speed-wireless.html"&gt;Enjoying High Speed Wireless Connectivity Anywhere&lt;/a&gt;, so I won't do so again, but the important tip from that article is to download &lt;a href="http://www.wireless.att.com/businesscenter/solutions/wireless-laptop/software.jsp"&gt;at&amp;t Communications Manager&lt;/a&gt; to connect your laptop to the Internet via Bluetooth or USB.  &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Easily manage address book, calendar, email, multimedia, and more&lt;br /&gt;&lt;/strong&gt;You may think you need to buy a USB cable in order to connect with your phone and synchronize your address book, appointments, music library, etc., but the good news is that you don't!  If you're laptop is equipped with Bluetooth (like mine is), simply download &lt;a href="http://www.samsung.com/us/support/download/supportDown.do?group=mobilephones&amp;amp;type=mobilephones&amp;subtype=att&amp;amp;model_nm=SGH-A707&amp;amp;amp;language=&amp;dType=D&amp;amp;mType=SW&amp;vType=R"&gt;Samsung PC Studio&lt;/a&gt; (it's free!).  It will run through a simple wizard to connect to your phone either via Bluetooth or USB (it also supports Infrared and Serial connections, but since the phone doesn't, that doesn't help).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_l-kZv4tdloI/RsyX5SkEDZI/AAAAAAAAADk/pARxZmqD6wo/s1600-h/sstudio.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; cursor: pointer;" src="http://bp3.blogger.com/_l-kZv4tdloI/RsyX5SkEDZI/AAAAAAAAADk/pARxZmqD6wo/s320/sstudio.png" alt="" id="BLOGGER_PHOTO_ID_5101619488395365778" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Once you're connected you can manually manage your address book, email, etc. or you can synchronize them with Outlook/Outlook Express.  You can use this application to configure an Internet connection as well, but it's easier with the at&amp;t application (see tip 1).&lt;br /&gt;The "Manage Multimedia Files" option allows you to copy music, photos, and videos to/from the phone -- including Podcasts.  &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Add useful apps to your phone&lt;br /&gt;&lt;/strong&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="http://www.google.com/gmm/index.html"&gt;Google Maps&lt;/a&gt;&lt;br /&gt;Other than making phone calls, one of the most useful thing a cell phone can do for you is help you find businesses while on the road.  Google Maps is a "must have" application for this.  Not only can if find local businesses by name or keywords, with a click of a button you can call them  You can get directions to them including step-by-step instructions to get you there.  It also adds real-time traffic information, can display satellite imagery, and provide navigation to move around the map and zoom in/out.  &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.google.com/mobile/mail/index.html"&gt;GMail&lt;/a&gt;&lt;br /&gt;Yes, the Sync comes with a mobile email program which connects to Yahoo!, AOL, Windows Live Mail (aka Hotmail), Juno, NetZero, and more.  However, the one service it lacks it my preference, GMail.  Fortunately, Google has provided their own application which can be installed on your phone to access GMail accounts.  Unfortunately, I haven't found anyway to make it the default email program so that your phone will tell you when you get new mail like it will for the other email types.  &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.operamini.com/"&gt;Opera Mini&lt;/a&gt;&lt;br /&gt;The built in browser works fine for WAP/WML content, but when you need a more powerful browser, Opera Mini is the way to go.  If you have iPhone envy and want a browser like it demonstrates showing the whole page and zooming in on pertinent sections,  try the beta of version 4 which can do that too.  &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.sitex.com.ua/en/products/mWebPlayer/index.htm"&gt;mWebPlayer&lt;/a&gt;&lt;br /&gt;The Sync includes "XM Radio Mobile" which allows you to stream XM Radio content to your phone.  The only problem is, you have to pay for the service.  As an alternative, mWebPlayer will stream radio stations off the Internet (for free!).  The free version of mWebPlayer doesn't save changes you make to the station list, however, since you can load your &lt;a href="http://www.live365.com/"&gt;Live365.com&lt;/a&gt; favorites, it's quick and easy to just load that list each time you start the application.   &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.getjar.com/software/Samsung/SGH_A707/Applications/Messengers"&gt;Instant Messaging&lt;/a&gt;&lt;br /&gt;The Sync includes an instant messaging application which can connect to either AIM, MSN, or Yahoo!  However, it has two drawbacks.  You can only connect to one at a time, and it uses your text messaging count for each message (which when you have unlimited data but limited text messages is rather stupid).  Fortunately, there are a large number of IM clients out there which can connect to multiple IM communities at once and don't waste your messaging total.   The biggest drawback is that, like the email, when you're using one of these apps you won't get notified when you're not using the phone and you get a message.  You have to be running the application at the time.  Since there are lots to choose from, I'm not specifying a particular one since I haven't evaluated them all, just follow the link and pick the one that sounds best to you.  &lt;/li&gt;&lt;li&gt;Games&lt;br /&gt;There's going to be plenty of times when you're waiting in some lobby and need something to do to pass the time. Here are a few staple games that are both free and well done.  &lt;ul&gt; &lt;li&gt;&lt;a href="http://www.odesys.com/wireless/games/backgammon/index.htm"&gt;Backgammon&lt;/a&gt;&lt;br /&gt;This is a nice implementation of an old favorite.  The LITE version is free and fully functional except that you can't play against other people online.   &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.cellufun.com/Games/games.asp?game=Chess"&gt;Chess&lt;/a&gt;&lt;br /&gt;Another nice (and free) rendition of a timeless classic.   &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.getjar.com/products/2088/TobiTrisTetrisGame"&gt;Tetris&lt;/a&gt;&lt;br /&gt;A good way to waste time.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt; &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Add useful URLs to add to your browser favorites&lt;br /&gt;&lt;/strong&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="http://mobile.google.com/"&gt;Google&lt;/a&gt;&lt;br /&gt;Who can surf the net without Google?  &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.google.com/mobile/youtube/index.html"&gt;YouTube&lt;/a&gt;&lt;br /&gt;You too can watch everyone's homevideos on your cell phone (who needs an iPhone? -- the Sync can toggle between portrait and landscape too!)  &lt;/li&gt;&lt;li&gt;&lt;a href="http://wireless.tvguide.com/"&gt;TV Guide&lt;/a&gt;&lt;br /&gt;Who wants to flip through a paper guide and translate it to the correct station number? Get a customized list of what's on right now for your TV provider.  &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.biblegateway.com/mobile/"&gt;Bible&lt;/a&gt;&lt;br /&gt;Don't want to carry the Bible with you to church, no problem, get it online.  &lt;/li&gt;&lt;li&gt;&lt;a href="http://www.srh.noaa.gov/wml/"&gt;Weather (NOAA)&lt;/a&gt;&lt;br /&gt;I happen to live in a hurricane zone, so when a hurricane it coming, I want to be able to get up-to-date weather reports.  &lt;/li&gt;&lt;li&gt;&lt;a href="http://wireless.getjar.com/mobile/software/"&gt;Java Applications&lt;/a&gt;&lt;br /&gt;Many of those aforementioned Java application, and many more can be downloaded from GetJar.com  &lt;/li&gt;&lt;li&gt;&lt;a href="http://mobile.palmone.com/us/"&gt;Palm Mobile Portal&lt;/a&gt;&lt;br /&gt;True, this isn't a Palm, but Palm does have a nice portal site for other mobile sites.&lt;/li&gt;&lt;/ul&gt; &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Free 411 Information&lt;br /&gt;&lt;/strong&gt;Add a contact to your address book for &lt;a href="http://www.google.com/mobile/goog411/index.html"&gt;1-800-GOOG-411&lt;/a&gt; (1-800-466-4411) to get local business information.  &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Customize appearance&lt;/strong&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_l-kZv4tdloI/RsyX5ikEDaI/AAAAAAAAADs/QZ7ul9aZ8mI/s1600-h/Clearwater+Beach.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://bp0.blogger.com/_l-kZv4tdloI/RsyX5ikEDaI/AAAAAAAAADs/QZ7ul9aZ8mI/s320/Clearwater+Beach.jpg" alt="" id="BLOGGER_PHOTO_ID_5101619492690333090" border="0" /&gt;&lt;/a&gt; Using pictures you take or upload with PC Studio (tip 2), be sure to set the "Caller ID" field for your address book contacts to pictures of them which will show up when they call.  Until you get a picture of your friend, the &lt;a href="http://www.dilbert.com/comics/dilbert/the_characters/index.html"&gt;Dilbert characters&lt;/a&gt; work nicely.  Also, customize your ring tone and wallpaper.  To the right is a picture I took at Clearwater Beach (Florida) which has been resized to fit the screen (240x320) in case you don't have a picture of your own. &lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-6648073211835801181?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/6648073211835801181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=6648073211835801181' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/6648073211835801181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/6648073211835801181'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/08/tips-for-new-samsung-sync-users.html' title='Tips for new Samsung Sync Users'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_l-kZv4tdloI/RsyX5SkEDZI/AAAAAAAAADk/pARxZmqD6wo/s72-c/sstudio.png' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-1710708635846682585</id><published>2007-08-17T15:39:00.000-04:00</published><updated>2007-09-01T18:22:06.660-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><title type='text'>Automatically Minimize (Minify) JavaScript</title><content type='html'>One of the &lt;a href="http://developer.yahoo.com/performance/rules.html"&gt;13 Simple Rules for Speeding Up Your Website&lt;/a&gt; is to &lt;a href="http://developer.yahoo.com/performance/rules.html#minify"&gt;Minify your JavaScript&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Sidenote: These rules can be easily checked using the &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; add-on to &lt;a href="http://www.getfirebug.com/"&gt;Firebug.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The recommended tool to minify your JavaScript is &lt;a href="http://crockford.com/javascript/jsmin"&gt;JSMin&lt;/a&gt;.  However, the default use of JSMin is to minimize your JavaScript in a separate step before you deploy your code and then have two JavaScript files - the original JavaScript you use to develop the code and the production "minified" version.    For me the problem with that is the fact you have two versions to keep track of and keep in sync.  Plus before you deploy your code, you need to make sure all your HTML pages use the minified filename not the original version.&lt;br /&gt;&lt;br /&gt;Fortunately, there's a &lt;a href="http://code.google.com/p/jsmin-php/"&gt;port of JSMin to PHP&lt;/a&gt; which allows you minify your JavaScript on the fly, but the usage instructions make it appear that you need to change your script source to point to separate PHP file for each JavaScript, or have a single PHP with all your JavaScript files hard-coded in it -- which is a nice option to get multiple JavaScript files with a single request.&lt;br /&gt;&lt;br /&gt;What I'm going to show in this post is how to use &lt;code&gt;jsmin-php&lt;/code&gt; to create a filter on your server so that all your JavaScript files are transparently minified as your retrieve them.&lt;br /&gt;&lt;br /&gt;First off, download the current version of &lt;a href="http://code.google.com/p/jsmin-php/"&gt;jsmin-php&lt;/a&gt; and save it in your directory.  As the current version at the time of this writing is &lt;code&gt;jsmin-1.1.0.php&lt;/code&gt;, I'll assume that is the file name.&lt;br /&gt;&lt;br /&gt;Next, save the script below as &lt;code&gt;jsmin.php&lt;/code&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?php&lt;br /&gt;// PHP filter to invoke JSMin on all JavaScript files&lt;br /&gt;// see http://code.google.com/p/jsmin-php/&lt;br /&gt;require_once('jsmin-1.1.0.php');&lt;br /&gt;&lt;br /&gt;$js_file = pathinfo($_SERVER['ORIG_PATH_INFO']);&lt;br /&gt;$js_path = substr($js_file['dirname'], 11);&lt;br /&gt;$js_file = $js_file['basename'];&lt;br /&gt;&lt;br /&gt;$charset = "utf-8";&lt;br /&gt;$mime = "text/javascript";&lt;br /&gt;&lt;br /&gt;header("Content-Type: $mime;charset=$charset");&lt;br /&gt;&lt;br /&gt;// Output a minified version of JavaScript file&lt;br /&gt;if (file_exists($js_file)) {&lt;br /&gt;echo JSMin::minify(file_get_contents($js_file));&lt;br /&gt;} else {&lt;br /&gt;echo JSMin::minify(file_get_contents($js_path . '/' . $js_file));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Note: If you change the name of this script, you'll probably need to change the &lt;code&gt;11&lt;/code&gt; on the &lt;code&gt;$js_path&lt;/code&gt; definition.  That rips the script name (&lt;code&gt;/jsmin.php/&lt;/code&gt;) off the beginning of the incoming path on the request so I can use a relative path to find scripts in a subdirectory.  It should be the length of the script name plus 2 (for the leading and trailing '/'s).&lt;br /&gt;&lt;br /&gt;Now, update your &lt;code&gt;.htaccess&lt;/code&gt; file and add the lines:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# make all JavaScript files be minimized via JSMin&lt;br /&gt;AddHandler jsmin .js&lt;br /&gt;Action jsmin /jsmin.php&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That should be it!  If you fetch a JavaScript file from your server, it should now be minified.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Here are a few  quick additions you could add to make &lt;a href="http://developer.yahoo.com/yslow/"&gt;YSlow&lt;/a&gt; happy.&lt;br /&gt;&lt;br /&gt;At the beginning of jsmin.php, adding the line:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ob_start("ob_gzhandler");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;should GZIP the output for you.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Also, where the &lt;code&gt;Content-Type&lt;/code&gt; header is set, adding the following lines will add an expiration of 49 hours (needs to be &gt; 48 hours to by sufficiently "far future" to make YSlow happy.  If you really want to cut it close you could use (60 * 60 * 48) + 1):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$offset = 60 * 60 * 49;&lt;br /&gt;$ExpStr = "Expires: " . gmdate("D, d M Y H:i:s", time() + $offset) . " GMT";&lt;br /&gt;header($ExpStr);&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-1710708635846682585?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/1710708635846682585/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=1710708635846682585' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1710708635846682585'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/1710708635846682585'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/08/automatically-minimize-javascript.html' title='Automatically Minimize (Minify) JavaScript'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-5681425080254284023</id><published>2007-08-15T15:55:00.001-04:00</published><updated>2008-05-02T16:19:29.224-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='mobile phone'/><title type='text'>Enjoying High-Speed Wireless Connectivity Anywhere</title><content type='html'>In the interest of providing other technology posts other than just JavaScript code snippets, I thought I'd summarize my experience using at&amp;t's 3G+ network.  More than basic &lt;a href="http://en.wikipedia.org/wiki/3G"&gt;3G&lt;/a&gt;, I actually get to utilize their &lt;a href="http://en.wikipedia.org/wiki/HSDPA"&gt;HSDPA&lt;/a&gt; network here in Tampa where I live.&lt;br /&gt;&lt;br /&gt;What's the difference?  Well, according to the Wikipedia sources I linked to above, the standard 3G &lt;span style="font-style: italic;"&gt;allows the transmission of 384 kbit/s for mobile systems&lt;/span&gt; whereas &lt;span style="font-style: italic;"&gt;current HSDPA deployments support down-link speeds of 1.8, 3.6, 7.2 and 14.4 Mbit/s&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;I recently acquired a &lt;a href="http://www.samsung.com/us/consumer/detail/detail.do?group=mobilephones&amp;type=mobilephones&amp;amp;subtype=att&amp;model_cd=SGH-A707DAACIN"&gt;Samsung   A707&lt;/a&gt; (aka Samsung Sync) which is a considerable improvement over my prior &lt;a href="http://www.motorola.com/motoinfo/product/details.jsp?globalObjectId=55"&gt;Motorola V180&lt;/a&gt; -- though the V180 served me well as a basic phone, and even survived a fall into a river.   I don't intend to review the A707 as there are plenty of other reviews out there (&lt;a href="http://www.mobilewhack.com/reviews/cingular_sync_review.html"&gt;1&lt;/a&gt;, &lt;a href="http://reviews.cnet.com/Samsung_Sync_SGH_A707/4505-6454_7-32143300.html"&gt;2&lt;/a&gt;, &lt;a href="http://www.1800mobiles.com/cingular-sync-a707-cingular-music-phone.html"&gt;3&lt;/a&gt;, &lt;a href="http://www.time.com/time/business/article/0,8599,1564108,00.html"&gt;4&lt;/a&gt;, &lt;a href="http://www.infosyncworld.com/reviews/n/7216.html"&gt;5&lt;/a&gt;), nor do I care to discuss the merits of one carrier's wireless technology over another's (ex HSDPA vs. &lt;a href="http://en.wikipedia.org/wiki/Evolution-Data_Optimized"&gt;EV-DO&lt;/a&gt;).  All I want to talk about is my experience utilizing my phone to access the internet, particularly on my laptop and PocketPC through my phone, and what kind of performance I've seen.&lt;br /&gt;&lt;br /&gt;Once I got the phone, I signed up for a &lt;span style="font-style: italic;"&gt;MEdia Max&lt;/span&gt; bundle which gave me unlimited data on the phone.  I plan on doing LOTS of data with the phone, so I can't have some sort of puny 1MB or 5MB limit.&lt;br /&gt;&lt;br /&gt;To see what kind of bandwidth I could get on the cell phone, I pointed my &lt;a href="http://www.operamini.com/"&gt;Opera Mini&lt;/a&gt; browser to &lt;a href="http://www.thespeedtest.com/speedtest"&gt;The Speed Test&lt;/a&gt;* and got results such as:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_l-kZv4tdloI/RsNkjPibRAI/AAAAAAAAAB8/Am-Zbxwg-6A/s1600-h/cellbw1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_l-kZv4tdloI/RsNkjPibRAI/AAAAAAAAAB8/Am-Zbxwg-6A/s400/cellbw1.png" alt="" id="BLOGGER_PHOTO_ID_5099029759742657538" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;In the interest of full disclosure, that was one of the higher scores, however several other tests were in the same vicinity (ex. 2801.8kbps) so it could not be dismissed as an anomaly.  The lower end of the scores were around 1913.5kbps.  These are all respectable scores and inline with the statement that the initial HSDPA networks deployments support 3.6 Mbit/s peak with phase one deployments to eventually reach achieve peak data rates of 14.4 Mbit/s.    Who says internet access on cell phones is slow!?&lt;br /&gt;&lt;br /&gt;Now, it's all well and good that the phone can attain that high bandwidth and it certainly makes streaming videos off of YouTube or at&amp;t's Cellular Video service to your cell phone work smoothly, but at some point, you just want a full size screen and keyboard for your internet access.&lt;br /&gt;&lt;br /&gt;The A707 doesn't come with a USB cable, so I utilized its built-in Bluetooth.  To me, the Bluetooth is actually nicer since I can leave myphone in my pocket or in my belt click and still connect your laptop to it.  Plus, I can connect my laptop and PocketPC to it simultaneously which is nice too.  However, it does drain the battery faster.&lt;br /&gt;&lt;br /&gt;To use the phone for internet access via Bluetooth, you need to pair it to your laptop (or other Bluetooth enabled device).  So, enable Bluetooth in the phone settings.  I won't provide my own instructions for this as at&amp;amp;t has a nice &lt;a href="http://supportcingular.atgnow.com/cng/tutorials/KB73246.html"&gt;tutorial online&lt;/a&gt;.    Then you need to pair your phone and laptop together.  Again, at&amp;t provides &lt;a href="https://cingular.atgnow.com/cng/tutorials/KB64519.html"&gt;vendor specific instructions&lt;/a&gt; for this.  at&amp;amp;t has made a simple client called &lt;a href="http://www.wireless.att.com/businesscenter/solutions/wireless-laptop/software.jsp"&gt;at&amp;t Communication Manager&lt;/a&gt; which allows you to easily connect with the phone.   Setup instructions are provided on the download page.  The only thing I had to do special was manually select the GSM device (see Tools -&gt; Settings... -&gt; GSM -&gt; Device Selection and select the Bluetooth Modem that was setup for me when I did the Bluetooth pairing).  Once the Communication Manager finds your device, you simply click on the "Connect" button and you're in business.&lt;br /&gt;&lt;br /&gt;My laptop is a few years old, so I believe it's built in &lt;a href="http://en.wikipedia.org/wiki/Bluetooth"&gt;Bluetooth&lt;/a&gt; is version 1, not version 2 which significantly limits it's bandwidth potential (see Wikipedia entry on &lt;a href="http://en.wikipedia.org/wiki/Bluetooth"&gt;Bluetooth&lt;/a&gt;).   By accessing the internet via my cell phone via Bluetooth, I got speeds varying from around 274kbps to 347kbps.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_l-kZv4tdloI/RsRFCikEDVI/AAAAAAAAACs/9T2HFF2k30M/s1600-h/cellbw2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_l-kZv4tdloI/RsRFCikEDVI/AAAAAAAAACs/9T2HFF2k30M/s400/cellbw2.png" alt="" id="BLOGGER_PHOTO_ID_5099276588030365010" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;These results are significantly reduced from the cell phone alone, though still tolerable for common internet tasks (reading email, viewing typical web pages, etc).&lt;br /&gt;&lt;br /&gt;The results for my laptop are comparable to the results I saw on my Dell Axim x51v as well.  I had to do a little more &lt;a href="http://www.aximsite.com/boards/tips-tricks/67234-once-all-how-connect-through-bluetooth-under-cingular-6820-a.html"&gt;manual configuration&lt;/a&gt; to get the Axim connected to the internet through the phone, but once I got it working, I got results such as:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_l-kZv4tdloI/RsRF3ykEDXI/AAAAAAAAAC8/xGvU5PLTcrY/s1600-h/cellbw3.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp1.blogger.com/_l-kZv4tdloI/RsRF3ykEDXI/AAAAAAAAAC8/xGvU5PLTcrY/s400/cellbw3.png" alt="" id="BLOGGER_PHOTO_ID_5099277502858399090" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Just for comparison, the bandwidth I got at a local WiFi hotspot was around 1Mbps:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp2.blogger.com/_l-kZv4tdloI/RsRFeCkEDWI/AAAAAAAAAC0/bwK35RqRbq0/s1600-h/cellbw4.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp2.blogger.com/_l-kZv4tdloI/RsRFeCkEDWI/AAAAAAAAAC0/bwK35RqRbq0/s400/cellbw4.png" alt="" id="BLOGGER_PHOTO_ID_5099277060476767586" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;So the conclusion is that if WiFi is available, it's still the faster option (at least for me without a USB connection nor Bluetooth 2.0 speeds), but having the ability to have broadband speed internet access ANYWHERE I go (in the city) is certainly a convenience!  And possibly it's more secure if you're in a public hotpost with packet sniffers lurking around.&lt;br /&gt;&lt;br /&gt;I'd be interested to here if anyone has similar experience but using Bluetooth 2.0 instead.  Is it faster?  Is a USB connection faster?&lt;br /&gt;&lt;br /&gt;*Note: I also verified my laptop speed results with &lt;a href="http://www.speedtest.net/"&gt;Speedtest.net&lt;/a&gt; which has a really nice interface.  However, since it's Flash based, it doesn't run in the cell phone browser.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Update - December 19, 2007:&lt;/h3&gt;&lt;br /&gt;I have replaced my laptop and am now running &lt;a href="http://www.redhat.com"&gt;Red Hat Enterprise Linux&lt;/a&gt; on my new one.  Therefore, I could no longer use the at&amp;t Communications Manager to make a connection.  Fortunately, I found these instructions for &lt;a href="http://www.teleost.org/docs/bluetooth-phone-linux.html"&gt;How to use a Bluetooth phone as [a] modem in Linux&lt;/a&gt; which were very helpful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-5681425080254284023?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/5681425080254284023/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=5681425080254284023' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/5681425080254284023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/5681425080254284023'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/08/enjoying-high-speed-wireless.html' title='Enjoying High-Speed Wireless Connectivity Anywhere'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp0.blogger.com/_l-kZv4tdloI/RsNkjPibRAI/AAAAAAAAAB8/Am-Zbxwg-6A/s72-c/cellbw1.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-4599469597751046363</id><published>2007-08-14T09:50:00.000-04:00</published><updated>2007-08-14T10:18:34.661-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><title type='text'>Synthesizing Events in JavaScript</title><content type='html'>According to &lt;i&gt;JavaScript: The Definitive Guide&lt;/i&gt; (4th ed), section 19.2.9 on Synthesizing Events, it says,&lt;br /&gt;&lt;br /&gt;&lt;p class="quote"&gt;Unfortunately, at the time of this writing, the synthetic event API is not supported by Netscape 6, nor by the current version of Mozilla&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;But, it does work in the current version of Firefox (and IE).  I had a situation where I needed to simulate events, so I wrote the following function to make it simple:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// simulateEvent&lt;br /&gt;//&lt;br /&gt;// simulate a user action&lt;br /&gt;//&lt;br /&gt;function simulateEvent(eventType, targetElement) {&lt;br /&gt;    var event;&lt;br /&gt;    targetElement = $(targetElement);&lt;br /&gt;    &lt;br /&gt;    if (targetElement) {&lt;br /&gt;     // check for IE&lt;br /&gt;     if (window.ActiveXObject) {&lt;br /&gt;         event = document.createEventObject();&lt;br /&gt;         targetElement.fireEvent("on"+eventType,event);&lt;br /&gt;     } else {&lt;br /&gt;         switch (eventType) {&lt;br /&gt;             case "abort":&lt;br /&gt;             case "blur":&lt;br /&gt;             case "change":&lt;br /&gt;             case "error":&lt;br /&gt;             case "focus":&lt;br /&gt;             case "load":&lt;br /&gt;             case "reset":&lt;br /&gt;             case "resize":&lt;br /&gt;             case "scroll":&lt;br /&gt;             case "select":&lt;br /&gt;             case "submit":&lt;br /&gt;             case "unload":&lt;br /&gt;                 event = document.createEvent("HTMLEvents");&lt;br /&gt;                 event.initEvent(eventType, "true", "true");&lt;br /&gt;                 break;&lt;br /&gt;             case "click":&lt;br /&gt;             case "mousedown":&lt;br /&gt;             case "mousemove":&lt;br /&gt;             case "mouseout":&lt;br /&gt;             case "mouseover":&lt;br /&gt;             case "mouseup":&lt;br /&gt;                 event = document.createEvent("MouseEvents");&lt;br /&gt;                 event.initMouseEvent(eventType, true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);&lt;br /&gt;                 break;&lt;br /&gt;         }&lt;br /&gt;         targetElement.dispatchEvent(event);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-4599469597751046363?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/4599469597751046363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=4599469597751046363' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/4599469597751046363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/4599469597751046363'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/08/synthesizing-events-in-javascript.html' title='Synthesizing Events in JavaScript'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-3280351452474971696</id><published>2007-03-08T13:17:00.000-05:00</published><updated>2007-03-08T15:04:57.643-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><title type='text'>Top Web Technologies</title><content type='html'>&lt;a href="http://www.eweek.com/author_bio/0,1908,a=2164,00.asp"&gt;Jim Rapoza&lt;/a&gt; of eWeek compiled his list of the &lt;a href="http://www.eweek.com/slideshow_viewer/0,1205,l=&amp;s=26705&amp;amp;a=202626&amp;amp;po=1,00.asp?p=y?kc=EWEWEMNL030807EP35A"&gt;Top Web Technologies of All Time&lt;/a&gt; as a collection of slides.&lt;br /&gt;&lt;br /&gt;Here is his list:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;XML&lt;br /&gt;&lt;/li&gt;&lt;li&gt;HTML&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Netscape Navigator&lt;br /&gt;&lt;/li&gt;&lt;li&gt;HTTP&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Apache&lt;br /&gt;&lt;/li&gt;&lt;li&gt;NCSA Mosaic&lt;br /&gt;&lt;/li&gt;&lt;li&gt;CERN httpd&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Internet Explorer 3&lt;br /&gt;&lt;/li&gt;&lt;li&gt;NCSA httpd&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Firefox&lt;br /&gt;&lt;/li&gt;&lt;li&gt;SSL&lt;br /&gt;&lt;/li&gt;&lt;li&gt;ViolaWWW&lt;br /&gt;&lt;/li&gt;&lt;li&gt;WAIS&lt;br /&gt;&lt;/li&gt;&lt;li&gt;CGI&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Internet Information Server&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Squid&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Java&lt;br /&gt;&lt;/li&gt;&lt;li&gt;HotMetal&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Flash&lt;br /&gt;&lt;/li&gt;&lt;li&gt;PHP&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Dreamweaver&lt;br /&gt;&lt;/li&gt;&lt;li&gt;RSS&lt;br /&gt;&lt;/li&gt;&lt;li&gt;WebTrends&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Blogger&lt;br /&gt;&lt;/li&gt;&lt;li&gt;PlaceWare&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Lynx&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Perl&lt;/li&gt;&lt;/ul&gt;While I would definitely concur on many items on the list, there are others that I would disagree with or substitute.&lt;br /&gt;&lt;br /&gt;Regarding the standardized protocols and languages: XML, HTML, HTTP, RSS, SSL, Java, PHP, Perl, RSS, and even Flash, I have no objections.  They are certainly the foundations of the web. However, I do think he's missing a biggie of JavaScript which, while it had a dubious start and I avoided it for quite a while, is shaping out to be &lt;b&gt;the&lt;/b&gt; scripting language of &lt;i&gt;Web 2.0&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Regarding his list of browsers: NCSA Mosaic, Netscape Navigator, and Internet Explorer, Firefox? Absolutely.  ViolaWWW? I don't think so.  Yes, it came out before NCSA Mosaic, but Mosaic was really more responsible for bringing the WWW to the masses, and if you really want to show the start of the web browsers, why not point to the actual original browser named &lt;a href="http://www.w3.org/History/1994/WWW/Journals/CACM/screensnap2_24c.gif"&gt;WorldWideWeb&lt;/a&gt;  by &lt;a href="http://www.w3.org/People/Berners-Lee/"&gt;Tim Berners-Lee&lt;/a&gt;?  Spyglass? Come on, it's just a commercial re-branding of NCSA Moasic. Yes, it became Internet Explorer, but you could just as well say NCSA Moasic became Internet Explorer and not bother to mention this interim step. Similarly, Netscape was first called &lt;a href="http://1997.webhistory.org/www.lists/www-talk.1994q4/0187.html"&gt;Mosaic Netscape&lt;/a&gt;, so do we need to list that too? Is wasn't really significant in itself.  Lynx?  Sure, it comes in handy from time to time, but I don't think it's a top web technology.  Opera? It's a nice browser and all, but I don't think it's ever made that much of an impact overall.  Yes, it introduced some new ideas like mouse gestures, but I just don't think it was that significant.  Why not Safari for the Mac, or Galleon or Konqueor on Linux?  Same thing.  They're nice, but more of an &lt;i&gt;also ran&lt;/i&gt; than a top web technology.&lt;br /&gt;&lt;br /&gt;Then we have our web servers: CERN httpd and NCSA httpd which begot Apache, no question. IIS? I guess in some circles, but Apache is still significantly more important.  If it's on his list because it provides the .Net platform, what about Tomcat, JBoss, or other commercial products like WebSphere and BEA for J2EE?  I don't think I'd put IIS on the list, but if I did, I think I'd be remiss not to include Tomcat or some of the other Java application servers.&lt;br /&gt;&lt;br /&gt;Next are HTML editors HotMetal and DreamWeaver.  Personally, I think the Netscape/Mozilla Composer and/or FrontPage had a wider impact toward bringing HTML editing and composition to the masses originally.  Jim makes the comment that, &lt;i&gt;nearly all serious Web developers now use Dreamweaver&lt;/i&gt;, but I know plenty of serious web developers who don't use Dreamweaver so I think that was a personal bias on his part.&lt;br /&gt;&lt;br /&gt;Finally he have various web services like WAIS and Blogger.  WAIS had it's place as a search technology (though more familiarly recognized as &lt;a href="http://en.wikipedia.org/wiki/Gopher_%28protocol%29"&gt;Gopher&lt;/a&gt;), but I think Yahoo and subsequently Google had a more significant impact in organizing the Web to make it useful to the masses.  It's hard to classify it as a Web technology when Gopher/WAIS was essentially replaced by the web.  Blogger is certainly important and can stay on the list, but if it is, I think some other services should also be on the list.  For example, Yahoo's provision for &lt;a href="http://docs.yahoo.com/docs/pr/release124.html"&gt;free email&lt;/a&gt; (as well as Hotmail, GMail, and others) was more important toward getting people to use the web for online services.  Also, services like MySpace are more significant toward advancing the collaboration ideas of Web 2.0.  And is PlaceWare really more significant than all the other similar solutions?  Why not CUSeeMe or NetMeeting?&lt;br /&gt;&lt;br /&gt;Also, what about other key technologies that make the web work like databases (MySQL anyone?).&lt;br /&gt;&lt;br /&gt;Just my thoughts.  Any other suggestions?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-3280351452474971696?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.eweek.com/slideshow_viewer/0,1205,l=&amp;s=26705&amp;a=202626&amp;po=1,00.asp?p=y?kc=EWEWEMNL030807EP35A' title='Top Web Technologies'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/3280351452474971696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=3280351452474971696' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3280351452474971696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3280351452474971696'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/03/top-web-technologies.html' title='Top Web Technologies'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-2200556442625153401</id><published>2007-03-06T17:47:00.000-05:00</published><updated>2007-03-06T17:55:25.221-05:00</updated><title type='text'>The Truth about Global Warming?</title><content type='html'>A local talk show personality, &lt;a href="http://en.wikipedia.org/wiki/Todd_Schnitt"&gt;Todd Schnitt&lt;/a&gt;, has compiled a &lt;a href="http://www.schnittshow.com/globalwarming.html"&gt;list of articles&lt;/a&gt; by respected scientists who dispute global warming.&lt;br /&gt;&lt;br /&gt;&lt;p class="quote"&gt;It is virtually shoved down our throats that scientists are in complete agreement about global warming.  Al Gore and the media assure us that there is an absolute consensus in the scientific community that humans are heating the planet and irreversible damage is looming as a result of man's carbon output.  How about some intellectual honesty?  Reasonable debate?  &lt;br /&gt;-- SchnittShow.com&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;It's nice to hear the other side of the story.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-2200556442625153401?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.schnittshow.com/globalwarming.html' title='The Truth about Global Warming?'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/2200556442625153401/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=2200556442625153401' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2200556442625153401'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2200556442625153401'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/03/truth-about-global-warming.html' title='The Truth about Global Warming?'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-2250522244128870048</id><published>2007-03-01T14:19:00.000-05:00</published><updated>2007-12-19T14:33:33.090-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><title type='text'>Simple draggable HTML using prototype.js</title><content type='html'>I know there are plenty of JavaScript packages out there to allow you to create draggable elements on your HTML page, but to me, most of them appear to be overly complicated for a simple function and are often part of a larger framework that you may not need.&lt;br /&gt;&lt;br /&gt;The solution I'm providing today does depends on the &lt;a href="http://prototypejs.org/"&gt;prototype.js&lt;/a&gt; package (I'm using version 1.5 currently) which allows the code size to stay smaller.&lt;br /&gt;&lt;br /&gt;Here's how to make any HTML element draggable in 5 easy steps.&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Add the class of &lt;code&gt;draggable&lt;/code&gt; to whatever you want to move.  Actually, you can use any class identifier you want, but &lt;code&gt;draggable&lt;/code&gt; is what I'm looking for in my code.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Give your &lt;code&gt;draggable&lt;/code&gt; element a &lt;code&gt;position&lt;/code&gt; style (&lt;code&gt;position: absolute;&lt;/code&gt; or &lt;code&gt;position: relative;&lt;/code&gt;) and an initial &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt; value.  Generally, you'd want to put this in your styles in an external CSS with the &lt;code&gt;draggable&lt;/code&gt; class, but the &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt; values need to be set as an attribute in the HTML.  Hint: setting the &lt;code&gt;position&lt;/code&gt; to &lt;code&gt;relative&lt;/code&gt; with the &lt;code&gt;top&lt;/code&gt; and &lt;code&gt;left&lt;/code&gt; values set to &lt;code&gt;0px&lt;/code&gt; will allow it to start located where it normally would be in the browser.  You'll probably want to give it a higher &lt;code&gt;z-index&lt;/code&gt; and solid &lt;code&gt;background-color&lt;/code&gt; as well, but that's up to you.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Include the 3 drag event handling functions I'll describe below in your code somewhere&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Register all your &lt;code&gt;draggable&lt;/code&gt; elements to the event listeners.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Have fun dragging window elements around.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h4&gt;The event handlers&lt;/h4&gt;&lt;br /&gt;Before you can use the following functions, you need to define some variables to hold some information during the drag. If you make the subsequent functions part of another containing object, you can simply prepend all occurrences of these variables in the functions with &lt;code&gt;this.&lt;/code&gt; and they will be stored in the containing object instead.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var dragObj;&lt;br /&gt;var cursorStartX;&lt;br /&gt;var cursorStartY;&lt;br /&gt;var elementStartX;&lt;br /&gt;var elementStartY;&lt;br /&gt;var dragGofn;&lt;br /&gt;var dragStopfn;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h5&gt;startDrag&lt;/h5&gt;&lt;br /&gt;&lt;code&gt;startDrag&lt;/code&gt; will initialize a mouse drag event.  It captures the initial location of the mouse when beginning the drag in order to calculate movement later.  It also captures the current coordinates of the dragged element for correct positioning as the location of the mouse are most likely not the origin of the element being dragged.  Finally, it associates the additional event observers to the element to capture the mouse movement (&lt;code&gt;onmousemove&lt;/code&gt;) and release of the mouse button (&lt;code&gt;onmouseup&lt;/code&gt;).&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function startDrag(event) { &lt;br /&gt;    dragObj = Event.element(event);&lt;br /&gt;    // if the target of the event is not a "draggable" element, then search&lt;br /&gt;    // through it's parents for an element that is draggable.&lt;br /&gt;    // if we can't find a draggable element before reaching the root document&lt;br /&gt;    // element, then bail out&lt;br /&gt;    while (!Element.hasClassName(dragObj,"draggable")) {&lt;br /&gt;         dragObj = $(dragObj).up('.draggable');&lt;br /&gt;    }&lt;br /&gt;    cursorStartX = Event.pointerX(event);&lt;br /&gt;    cursorStartY = Event.pointerY(event);&lt;br /&gt;    elementStartX = parseInt(dragObj.style.left, 10);&lt;br /&gt;    elementStartY = parseInt(dragObj.style.top, 10);&lt;br /&gt;    &lt;br /&gt;    Event.observe(dragObj, 'mousemove', dragGofn);&lt;br /&gt;    Event.observe(dragObj, 'mouseup', dragStopfn);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h5&gt;dragGo&lt;/h5&gt;&lt;br /&gt;Once the drag event has been initiated by the &lt;code&gt;startDrag&lt;/code&gt; function, the &lt;code&gt;dragGo&lt;/code&gt; function will update the location of the element being dragged so that it moves around the screen with the mouse.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function dragGo(event) { &lt;br /&gt;&lt;br /&gt;    var x, y;&lt;br /&gt;    x = Event.pointerX(event);&lt;br /&gt;    y = Event.pointerY(event);&lt;br /&gt;&lt;br /&gt;    // move the target object&lt;br /&gt;    dragObj.style.left = (x - cursorStartX + elementStartX) + "px";&lt;br /&gt;    dragObj.style.top = (y - cursorStartY + elementStartY) + "px";&lt;br /&gt;    &lt;br /&gt;    Event.stop(event);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h5&gt;dragStop&lt;/h5&gt;&lt;br /&gt;Once the user has release the mouse button, we need to stop observing the mouse movements, so the &lt;code&gt;dragStop&lt;/code&gt; function will un-register the event handlers.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function dragStop(event) { &lt;br /&gt;    // Stop capturing mousemove and mouseup events.&lt;br /&gt;    Event.stopObserving(dragObj, 'mousemove', dragGofn);&lt;br /&gt;    Event.stopObserving(dragObj, 'mouseup', dragStopfn);&lt;br /&gt;    dragObj = null;&lt;br /&gt;&lt;br /&gt;    Event.stop(event);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;Registering the &lt;code&gt;draggable&lt;/code&gt; elements&lt;/h4&gt;&lt;br /&gt;You're almost ready to start moving items on the screen, but first you have to register all your &lt;code&gt;draggable&lt;/code&gt; elements to the &lt;code&gt;startDrag&lt;/code&gt; function so that they will be notified of the drag events. So, we utilize prototype's element-by-classname selector to find all the &lt;code&gt;draggable&lt;/code&gt; elements and associate the event listener to them.&lt;br /&gt;&lt;br /&gt;Note: in order to un-register the event listeners, you have to have the handle to the event listener you used to register it originally.  Thus, we used the variables &lt;code&gt;dragGofn&lt;/code&gt; and &lt;code&gt;dragStopfn&lt;/code&gt; to register and un-register the event listeners.  These variables are defined here during the original event registration.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function registerEvents() {&lt;br /&gt;    dragGofn = dragGo.bindAsEventListener();&lt;br /&gt;    dragStopfn = dragStop.bindAsEventListener();&lt;br /&gt;    &lt;br /&gt;    $$(".draggable").each(function(draggableElem) {&lt;br /&gt;        Event.observe(draggableElem, 'mousedown', startDrag.bindAsEventListener(), true);&lt;br /&gt;    });&lt;br /&gt;    &lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; the final &lt;code&gt;true&lt;/code&gt; on the &lt;code&gt;Event.observe&lt;/code&gt; tells prototype to request &lt;i&gt;capturing&lt;/i&gt; instead of &lt;i&gt;bubbling&lt;/i&gt;.  This is necessary  for this function to work in Safari (see &lt;a href="http://kirblog.idetalk.com/2006/06/safari-javascript-problems.html"&gt;Kir's blog&lt;/a&gt;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-2250522244128870048?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/2250522244128870048/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=2250522244128870048' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2250522244128870048'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2250522244128870048'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/03/simple-draggable-html-using-prototypejs.html' title='Simple draggable HTML using prototype.js'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-6490624569101924762</id><published>2007-02-26T13:49:00.000-05:00</published><updated>2007-12-19T14:34:10.797-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><title type='text'>Adding a Processing indicator with Prototype.js</title><content type='html'>Here's a simple method to add a Processing/Loading/Working indicator to indicate when an Ajax request is being processed with &lt;a href="http://www.prototypejs.org"&gt;prototype.js&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;First, define a hidden &lt;code&gt;div&lt;/code&gt; (or other preferred HTML element) which will be displayed to indicate that some background processing is going on.  Mine happens to look like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;div id="loading" style="display:none"&amp;gt;Loading...&lt;br /&gt;&amp;lt;div style="background-color: white; position: absolute; top: 0px; right: 0px; width: 5px; height: 5px; cursor:pointer;"&lt;br /&gt; onclick="$('loading').hide()"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/div&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In my code, I added another &lt;code&gt;div&lt;/code&gt; on the upper right corner which I can click to close this indicator in case something goes wrong with the Ajax event handling which I'll provide in a moment.&lt;br /&gt;&lt;br /&gt;I then added a little CSS magic to make it look the way I wanted.  In my case, I want the indicator to be on top of everything, centered on the page, with a processing image (&lt;img src="http://bp1.blogger.com/_l-kZv4tdloI/ReMtOAAMs2I/AAAAAAAAAAM/pQohs3geY3g/s200/progress-running.gif" border="0" alt=""id="BLOGGER_PHOTO_ID_5035918526872597346" /&gt;) on the right side of the text.  The background color is white with a double gray border and large, bold font.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#loading {&lt;br /&gt; z-index: 100;&lt;br /&gt; position: absolute;&lt;br /&gt; top: 40%;&lt;br /&gt; left: 40%;&lt;br /&gt; background-image: url("../images/progress-running.gif");&lt;br /&gt; background-repeat: no-repeat;&lt;br /&gt; background-position: 5px;&lt;br /&gt; background-color: white;&lt;br /&gt; padding-left: 25px;&lt;br /&gt; padding-top: 8px;&lt;br /&gt; border-style: double;&lt;br /&gt; border-color: #c0c0c0;&lt;br /&gt; width: 120px;&lt;br /&gt; height: 30px;&lt;br /&gt; font-size: 1.5em;&lt;br /&gt; font-weight: bolder;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok, so now I have the indicator I want to popup.  This can be tailored to your preferences.  Now, you simply tell prototype to display it whenever it is processing an Ajax request.  This is done by registering some event listeners to the prototype Ajax class.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  // register event listeners on the Ajax requests to show/hide the processing indicator&lt;br /&gt; Ajax.Responders.register({&lt;br /&gt;    onCreate: function() {&lt;br /&gt;  if (Ajax.activeRequestCount === 1) {&lt;br /&gt;    $('loading').show();&lt;br /&gt;   }&lt;br /&gt;    },&lt;br /&gt;    onComplete: function() {&lt;br /&gt;   if (Ajax.activeRequestCount === 0) {&lt;br /&gt;    $('loading').hide();&lt;br /&gt;   }&lt;br /&gt;    }&lt;br /&gt; });&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's it!  Now you should get an indicator whenever you issue an Ajax request that goes away whenever the processing is done.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-6490624569101924762?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/6490624569101924762/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=6490624569101924762' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/6490624569101924762'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/6490624569101924762'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/02/adding-processing-indicator-with.html' title='Adding a Processing indicator with Prototype.js'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_l-kZv4tdloI/ReMtOAAMs2I/AAAAAAAAAAM/pQohs3geY3g/s72-c/progress-running.gif' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-8934197472981000670</id><published>2007-02-23T11:07:00.000-05:00</published><updated>2007-08-21T11:21:10.856-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Cookies and Flash for local storage</title><content type='html'>In &lt;a href="http://blog.pothoven.net/2006/10/using-cookies-in-javascript.html"&gt;previous posts&lt;/a&gt; I provided code for using cookies in JavaScript.  I've now enhanced that code to utilize the flash storage mechanism which was described by &lt;a href="http://www.christophkhouri.com/blog/2006/12/12/100k-of-client-storage-with-javascript-and-flash/"&gt;Christoph Khouri&lt;/a&gt;, however his page seems to be down currently, so I'll provide the files you need.  Note: this is the same technique used by the &lt;a href="http://manual.dojotoolkit.org/WikiHome/DojoDotBook/Book50"&gt;Dojo Storage&lt;/a&gt; module, without the need for Dojo which adds extra overhead costs.&lt;br /&gt;&lt;br /&gt;Using flash storage alleviates the 4k size limit of cookies and initially provides 100k of storage seamlessly and can be increased to any size by the user.  It also eliminates unnecessary network traffic as all the cookies are transmitted to the server all the time, and makes it harder for prying eyes to see what's being stored as vs browsing the cookies in the browser.&lt;br /&gt;&lt;br /&gt;In order to use the flash storage, first, download &lt;a href="http://www.pothoven.net/files/flashStorage.zip" onclick="javascript:urchinTracker('/download/flashStorage.zip');"&gt;flashStorage.zip&lt;/a&gt; which came from Christoph Khouri's site.  Next, download my current &lt;a href="http://www.pothoven.net/files/cookies.js" onclick="javascript:urchinTracker('/download/cookies.js');"&gt;cookies.js&lt;/a&gt; file.&lt;br /&gt;&lt;br /&gt;Now, include the flashStorage JavaScript and my cookies.js JavaScript in the header of your HTML page:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" src="jscript/flashStorage/swfobject.js"&amp;gt;lt;script&amp;gt;&lt;br /&gt;&amp;lt;script type="text/javascript" src="jscript/flashStorage/flashStorage.js"&amp;gt;&amp;lt;script&amp;gt;&lt;br /&gt;&amp;lt;script type="text/javascript" src="jscript/cookies.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Next, somewhere in the body your page add the following code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;div style="float: right;" id="storageHolder"&amp;gt;&lt;br /&gt;&amp;lt;embed type="application/x-shockwave-flash"&lt;br /&gt;src="jscript/flashStorage/storage.swf" id="storageMovie" name="storageMovie" bgcolor="#ffffff" quality="high"&lt;br /&gt;swliveconnect="true" wmode="transparent" height="137" width="214" /&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt; var so = new SWFObject("jscript/flashStorage/storage.swf", "storageMovie", "50", "10", "8", "#ffffff");&lt;br /&gt; so.addParam("swLiveConnect", "true");&lt;br /&gt; so.addParam("wmode", "transparent");&lt;br /&gt; so.write("storageHolder");&lt;br /&gt;&lt;br /&gt;//Initialize the flash storage by passing the id of the flash movie&lt;br /&gt;Storage.init("storageMovie");&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's it!  Now whenever you create a &lt;i&gt;cookie&lt;/i&gt; like &lt;code&gt;Cookies.create("myCookie", "myValue")&lt;/code&gt; it will actually store that in the flash storage instead.  However, if you don't wish to include the Flash storage component, it will default to store the value in a browser cookie instead.&lt;br /&gt;&lt;br /&gt;&lt;strike&gt;Maybe sometime in the future I'll add detection to see if the Flash plugin is installed in addition to the Storage object and default to standard cookies if Flash isn't installed.&lt;/strike&gt;&lt;br /&gt;&lt;bold&gt;Update:&lt;/bold&gt; I've added a check for the Flash plugin being installed as a pre-requisite to using the Flash storage object in addition to the check for the JavaScript code/object being included.  FYI, my check for Flash for those needing to do the same thing is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var hasFlash = (navigator.plugins &amp;&amp;amp;&lt;br /&gt;              (navigator.plugins["Shockwave Flash"] || navigator.plugins["Shockwave Flash 2.0"])) ||&lt;br /&gt;             (navigator.mimeTypes &amp;&amp;amp;&lt;br /&gt;              navigator.mimeTypes['application/x-shockwave-flash']);&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-8934197472981000670?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/8934197472981000670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=8934197472981000670' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/8934197472981000670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/8934197472981000670'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/02/cookies-and-flash-for-local-storage.html' title='Cookies and Flash for local storage'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-2794249796030840719</id><published>2007-02-23T09:15:00.001-05:00</published><updated>2009-04-23T08:30:28.010-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Ajax Cross-Domain Issue</title><content type='html'>A common solution to the XMLHttpRequest cross-domain issue (aka cross site scripting or XSS), where you can only communicate with the originating domain, is to configure Apache to use either &lt;code&gt;mod_proxy&lt;/code&gt; or &lt;code&gt;mod_rewrite&lt;/code&gt; for URL rewriting and a pass-through proxy. &lt;br /&gt;&lt;br /&gt;With Apache you need to enable the proxy functions (&lt;code&gt;configure --enable-proxy&lt;/code&gt;).  If you want to handle it all with &lt;code&gt;mod_proxy&lt;/code&gt;, then define you proxy settings in &lt;code&gt;httpd.conf&lt;/code&gt; such as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ProxyPass /doExt http://external.com/doAction&lt;br /&gt;ProxyPassReverse /doExt http://external.com/doAction&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you want to use &lt;code&gt;mod_rewrite&lt;/code&gt; then turn on &lt;code&gt;mod_rewrite&lt;/code&gt; in &lt;code&gt;httpd.conf&lt;/code&gt; or &lt;code&gt;.htaccess&lt;/code&gt; with &lt;code&gt;RewriteEngine on&lt;/code&gt;  and define your rewriting rules such as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;RewriteRule ^/doExt$ http://external.com/doAction [P]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then when you post to your server via &lt;/code&gt;/doExt&lt;/code&gt;, Apache will pass the request on to the external site.&lt;br /&gt;&lt;br /&gt;That's all well and good if you have access to the Apache configuration or the server is already configured with the proper modules.  But, when your on a hosted environment, that's not always feasible.  Another solution would be to provide a proxy servlet to make the remote request for you and return the result.  What I am providing is in Java and PHP, but it could be written you language of choice.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Java proxy&lt;/h3&gt;&lt;br /&gt;The following servlet class will invoke some external URL on behalf of the original request and return the result from the external request as the result of the original local request.  Note: In the following code I hard-coded the real destination.  However, it could be determined from a parameter in the original request just as easily.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import java.io.BufferedReader;&lt;br /&gt;import java.io.IOException;&lt;br /&gt;import java.io.InputStreamReader;&lt;br /&gt;import java.io.PrintWriter;&lt;br /&gt;import java.net.URL;&lt;br /&gt;import java.util.Enumeration;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * This is a simply proxy class in order to bypass AJAX security contraints.&lt;br /&gt; *&lt;br /&gt; */&lt;br /&gt;&lt;br /&gt;public class ServerProxy extends javax.servlet.http.HttpServlet {&lt;br /&gt;&lt;br /&gt;        private static final long serialVersionUID = 1L;&lt;br /&gt;&lt;br /&gt;        public void doGet(javax.servlet.http.HttpServletRequest request,&lt;br /&gt;                        javax.servlet.http.HttpServletResponse response)&lt;br /&gt;                        throws javax.servlet.ServletException, java.io.IOException {&lt;br /&gt;                performTask(request, response);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void doPost(javax.servlet.http.HttpServletRequest request,&lt;br /&gt;                        javax.servlet.http.HttpServletResponse response)&lt;br /&gt;                        throws javax.servlet.ServletException, java.io.IOException {&lt;br /&gt;                performTask(request, response);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void performTask(javax.servlet.http.HttpServletRequest request,&lt;br /&gt;                        javax.servlet.http.HttpServletResponse response)&lt;br /&gt;                        throws javax.servlet.ServletException, java.io.IOException {&lt;br /&gt;&lt;br /&gt;                // Get the external URL to invoke&lt;br /&gt;                String realUrl = "http://www.external.com";&lt;br /&gt;&lt;br /&gt;                // Copy the request parameters from the original request&lt;br /&gt;                Enumeration paramNames = request.getParameterNames();&lt;br /&gt;                String paramName;&lt;br /&gt;                while (paramNames.hasMoreElements()) {&lt;br /&gt;                        paramName = (String) paramNames.nextElement();&lt;br /&gt;                        realUrl = realUrl.concat(paramName + "="&lt;br /&gt;                                        + request.getParameter(paramName));&lt;br /&gt;                        if (paramNames.hasMoreElements()) {&lt;br /&gt;                                realUrl = realUrl.concat("&amp;");&lt;br /&gt;                        }&lt;br /&gt;                }&lt;br /&gt;&lt;br /&gt;                // real URL will be returning XML data&lt;br /&gt;                response.setContentType("text/xml");&lt;br /&gt;                PrintWriter out = response.getWriter();&lt;br /&gt;&lt;br /&gt;                // invoke the real URL and copy the result into the response&lt;br /&gt;                // for the original request&lt;br /&gt;                URL real = new URL(realUrl);&lt;br /&gt;                BufferedReader in = new BufferedReader(&lt;br /&gt;                   new InputStreamReader(real.openStream()));&lt;br /&gt;                String inputLine;&lt;br /&gt;                while ((inputLine = in.readLine()) != null)&lt;br /&gt;                        out.println(inputLine);&lt;br /&gt;                in.close();&lt;br /&gt;&lt;br /&gt;                return;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;PHP proxy&lt;/h3&gt;&lt;br /&gt;Here is a similar solution using PHP:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$remoteServer = "http://www.external.com";&lt;br /&gt;$path = $_GET[”path”];&lt;br /&gt;$proxyTarget = $remoteServer.$path;&lt;br /&gt;$connection = curl_init($proxyTarget);&lt;br /&gt;curl_setopt($connection, CURLOPT_HEADER, false);&lt;br /&gt;curl_setopt($connection, CURLOPT_RETURNTRANSFER, true);&lt;br /&gt;$data = curl_exec($connection);&lt;br /&gt;curl_close($connection);&lt;br /&gt;header(”Content-Type: text/xml”);&lt;br /&gt;echo $data;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-2794249796030840719?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/2794249796030840719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=2794249796030840719' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2794249796030840719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/2794249796030840719'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/02/ajax-cross-domain-issue.html' title='Ajax Cross-Domain Issue'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-3038495914220163446</id><published>2007-02-23T08:50:00.000-05:00</published><updated>2007-02-23T09:10:41.198-05:00</updated><title type='text'>Google Apps Premier Edition, a good thing?</title><content type='html'>I see on &lt;a href="http://googleblog.blogspot.com/2007/02/google-apps-grows-up.html"&gt;Google's Blog&lt;/a&gt; that they are announcing &lt;a href="http://www.google.com/a/enterprise/"&gt;Google Apps Premier Edition&lt;/a&gt; which is a collection of the existing Google Apps (Gmail, Google Talk, Google Calendar, Page Creator, and Google Docs &amp; Spreedsheets) which have additional collaboration features, APIs and support for which they will charge $50/yr per user.&lt;br /&gt;&lt;br /&gt;I certainly think Google's apps are very useful, of high quality, and worthy of compensation and they certainly offer good service with a 99.9 percent uptime service-level agreement in which customers will receive credits for downtime.  However, the reason I and so many others use Google apps is because they are free so I hope this is not a sign of things to come.  In the blog entry for this announcement they say,&lt;br /&gt;&lt;br /&gt;&lt;p class="quote"&gt;Google Apps also won't forget its roots anytime soon. The Standard and Education Editions will continue to be offered for free...&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;It's the &lt;i&gt;"anytime soon"&lt;/i&gt; clause that causes me apprehension.  Does that infer that there is a future plan to charge for their services?  I've been migrating to Google more and more, I don't want to go through the trouble of migrating somewhere else.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-3038495914220163446?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.google.com/a/enterprise/' title='Google Apps Premier Edition, a good thing?'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/3038495914220163446/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=3038495914220163446' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3038495914220163446'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/3038495914220163446'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/02/google-apps-premier-edition-good-thing.html' title='Google Apps Premier Edition, a good thing?'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-116889109234627693</id><published>2007-01-15T14:49:00.000-05:00</published><updated>2007-08-21T11:17:26.758-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Erasing all your cookies</title><content type='html'>In a &lt;a href="http://blog.pothoven.net/2006/10/using-cookies-in-javascript.html"&gt;previous post&lt;/a&gt; I provided some [non-original] code to do basic cookie manipulation in JavaScript.  Well, if you're now using cookies to save a whole bunch of user settings and preferences for your web application, you may want to provide an option to let users clear all their saved settings (or perhaps just provide a hidden hot-spot so you can clear your settings for testing purposes which is why I needed this function).&lt;br /&gt;&lt;br /&gt;You could keep track of all your cookie names and call the &lt;code&gt;eraseCookie&lt;/code&gt; function repeatedly yourself for each cookie.  Or, you could name all your cookies with a common prefix for your whole application and use the following function to clear them all out for you.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function eraseCookiesStartingWith(prefix) {&lt;br /&gt; var allCookies = document.cookie.split(';');&lt;br /&gt; for(var i=0;i &lt; allCookies.length;i++) {&lt;br /&gt;  var aCookie = allCookies[i];&lt;br /&gt;  // remove any whitespace from start of cookie name&lt;br /&gt;  while (aCookie.charAt(0)==' ') aCookie = aCookie.substring(1,aCookie.length);&lt;br /&gt;  var aCookieName = (aCookie.split('='))[0];&lt;br /&gt;  if (aCookie.indexOf(prefix) == 0) {&lt;br /&gt;   createCookie(aCookieName,"",-1);&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-116889109234627693?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://blog.pothoven.net/2006/10/using-cookies-in-javascript.html' title='Erasing all your cookies'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/116889109234627693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=116889109234627693' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116889109234627693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116889109234627693'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2007/01/erasing-all-your-cookies.html' title='Erasing all your cookies'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-116464326245566287</id><published>2006-11-27T11:00:00.000-05:00</published><updated>2006-12-28T10:58:36.226-05:00</updated><title type='text'>Why is sliced bread such a great thing?</title><content type='html'>I happened to be pondering why everything is compared to sliced bread (ie. "That's the greatest thing since sliced bread"), so I looked around a little and found this article to justify why everything is the greatest thing since sliced bread:&lt;br /&gt;&lt;br /&gt;&lt;p class="quote"&gt;"Sliced bread was the culmination of a century of technological innovation."&lt;br /&gt;-- &lt;a href="http://www.engineerguy.com/comm/4263.htm"&gt;http://www.engineerguy.com/comm/4263.htm&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-116464326245566287?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.engineerguy.com/comm/4263.htm' title='Why is sliced bread such a great thing?'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/116464326245566287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=116464326245566287' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116464326245566287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116464326245566287'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/11/why-is-sliced-bread-such-great-thing.html' title='Why is sliced bread such a great thing?'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-116006150736538695</id><published>2006-11-08T16:00:00.002-05:00</published><updated>2008-05-19T15:45:37.650-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='SVG'/><category scheme='http://www.blogger.com/atom/ns#' term='XSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Using XML, XPath, and XSLT with JavaScript</title><content type='html'>There are several JavaScript XML libraries available to try to provide a cross-browser XML library.  I've previously cited &lt;a href="http://www.nczonline.net/downloads/"&gt;zXML&lt;/a&gt; in my &lt;a href="http://blog.pothoven.net/2006/05/inline-dynamic-svg-from-xml-with-ajax.html"&gt;Inline SVG&lt;/a&gt; article, there's also &lt;a href="http://sarissa.sourceforge.net/"&gt;Sarissa&lt;/a&gt; and &lt;a href="http://xmljs.sourceforge.net/"&gt;XML for &amp;lt;Script&amp;gt;&lt;/a&gt; and I'm sure others.&lt;br /&gt;&lt;br /&gt;However, when I started using them I discovered various problems and limitations with them for what I needed to do.  So, I decided to write my own cross-browser XML routines.  I wanted an XML library that was as lightweight as possible, doing just what I needed and utilizing as much of the built in browser capabilities as possible.&lt;br /&gt;&lt;br /&gt;In this article, I will first present a simple XML class which just holds the XML DOM object returned from an AJAX request and provides functions to get and change node values based on XPaths.  Secondly, I will give a simple XSLT class to preform XSL Transformations.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The complete code for the XML and XSL library is available &lt;a href="http://www.pothoven.net/javascripts/src/xml.js" onClick="javascript:urchinTracker('/download/xml.js');"&gt;here&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;XML&lt;/h3&gt;&lt;br /&gt;First off is the constructor which determined if we will be using IE (ActiveX) functions or W3C standard functions for XML manipulation and either stores the XML DOM returned as the &lt;code&gt;responseXML&lt;/code&gt; from an &lt;code&gt;XMLHttpRequest&lt;/code&gt;(XHR) or creates an empty DOM object for later manipulation.&lt;br /&gt;&lt;pre&gt;//&lt;br /&gt;// XML&lt;br /&gt;//&lt;br /&gt;function XML(xmlDom) {&lt;br /&gt;this.isIE = window.ActiveXObject;&lt;br /&gt;if (xmlDom != null) {&lt;br /&gt; this.xmlDom = xmlDom;&lt;br /&gt;} else {&lt;br /&gt; // create an empty document&lt;br /&gt; if (this.isIE) {&lt;br /&gt;    Try.these (&lt;br /&gt;       function() { axDom = new ActiveXObject("MSXML2.DOMDocument.5.0"); },&lt;br /&gt;       function() { axDom = new ActiveXObject("MSXML2.DOMDocument.4.0"); },&lt;br /&gt;       function() { axDom = new ActiveXObject("MSXML2.DOMDocument.3.0"); },&lt;br /&gt;       function() { axDom = new ActiveXObject("MSXML2.DOMDocument"); },&lt;br /&gt;       function() { axDom = new ActiveXObject("Microsoft.XmlDom"); }&lt;br /&gt;    );&lt;br /&gt;    this.xmlDom = axDom;&lt;br /&gt; } else {&lt;br /&gt;    this.xmlDom = document.implementation.createDocument("", "", null);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In case you're not getting the XML data from an XHR response, I also provided a &lt;code&gt;load&lt;/code&gt; function to load an XML file from an URL.&lt;br /&gt;&lt;pre&gt;// load&lt;br /&gt;//&lt;br /&gt;// Loads an XML file from an URL&lt;br /&gt;XML.prototype.load = function(url) {&lt;br /&gt; this.xmlDom.async = false;&lt;br /&gt; this.xmlDom.load(url);&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, I want to be able to find a single node in the XML DOM based on an XPath.  The &lt;code&gt;getNode&lt;/code&gt; function will do that for me.&lt;br /&gt;&lt;pre&gt;// getNode&lt;br /&gt;//&lt;br /&gt;// get a single node from the XML DOM using the XPath&lt;br /&gt;XML.prototype.getNode = function(xpath) {&lt;br /&gt; if (this.isIE) {&lt;br /&gt;    var result = this.xmlDom.selectSingleNode(xpath);&lt;br /&gt; } else {&lt;br /&gt;    var evaluator = new XPathEvaluator();&lt;br /&gt;    var result = evaluator.evaluate(xpath, this.xmlDom, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);&lt;br /&gt; }&lt;br /&gt; return result;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;More often, I don't need the actual DOM node object, but the value of that node, so I provide the &lt;code&gt;getNodeValue&lt;/code&gt; a function to get the value of a node.&lt;br /&gt;&lt;pre&gt;// getNodeValue&lt;br /&gt;//&lt;br /&gt;// get the value of a the element specified by the XPath in the XML DOM&lt;br /&gt;XML.prototype.getNodeValue = function(xpath) {&lt;br /&gt; var value = null;&lt;br /&gt; try {&lt;br /&gt;    var node = this.getNode(xpath);&lt;br /&gt;&lt;br /&gt;    if (this.isIE &amp;&amp;amp;amp; node) {&lt;br /&gt;       value = node.text;&lt;br /&gt;    } else if (!this.isIE &amp;&amp;amp; node.singleNodeValue) {&lt;br /&gt;       value = node.singleNodeValue.textContent;&lt;br /&gt;    }&lt;br /&gt; } catch (e) {}&lt;br /&gt;&lt;br /&gt; return value;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you're just looking for a particular node/value, the previous functions are fine, but more often you need a group of node or node values (ex. all the book titles).  So, the full &lt;a href="http://www.pothoven.net/javascripts/src/xml.js" onClick="javascript:urchinTracker('/download/xml.js');"&gt;JavaScript file&lt;/a&gt; also contains corresponding functions to get multiple nodes (&lt;code&gt;getNodes&lt;/code&gt;) and node values as an array (&lt;code&gt;getNodeValues&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;At some point, you'll probably want to access this XML DOM as XML data (string), so we need a function to serialize the XML from the DOM to a String. The &lt;code&gt;getNodeAsXml&lt;/code&gt; function will do that for you.  If you pass in the root XPath ("/") it will serialize the complete XML file. Also, the included &lt;code&gt;prettyPrintXml&lt;/code&gt; function can be used to escape the HTML characters.&lt;br /&gt;&lt;pre&gt;// getNodeAsXml&lt;br /&gt;//&lt;br /&gt;// get the XML contents of a node specified by the XPath&lt;br /&gt;XML.prototype.getNodeAsXml = function(xpath) {&lt;br /&gt; var str = null;&lt;br /&gt; var aNode = this.getNode(xpath);&lt;br /&gt; try {&lt;br /&gt;    if (this.isIE) {&lt;br /&gt;       str = aNode.xml;&lt;br /&gt;    } else {&lt;br /&gt;       var serializer = new XMLSerializer();&lt;br /&gt;       str = serializer.serializeToString(aNode.singleNodeValue);&lt;br /&gt;    }&lt;br /&gt; } catch (e) {&lt;br /&gt;    str = "ERROR: No such node in XML";&lt;br /&gt; }&lt;br /&gt; return str;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So far, we've only read the XML data we received.  But we may also want to change the contents of the XML. So, here are the functions necessary to change existing node values (&lt;code&gt;updateNodeValue&lt;/code&gt;), add new nodes/values (&lt;code&gt;insertNode&lt;/code&gt;), and delete existing nodes (&lt;code&gt;removeNode&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;Note: the XPaths used for node insertions cannot be overly complex as I wanted to keep this library simple.&lt;br /&gt;&lt;pre&gt;// updateNodeValue&lt;br /&gt;//&lt;br /&gt;// update a specific element value in the XML DOM&lt;br /&gt;XML.prototype.updateNodeValue = function(xpath, newvalue) {&lt;br /&gt; var node = this.getNode(xpath);&lt;br /&gt; var changeMade = false;&lt;br /&gt; newvalue = newvalue.trim();&lt;br /&gt;&lt;br /&gt; if (this.isIE &amp;&amp;amp;amp; node) {&lt;br /&gt;    if (node.text != newvalue) {&lt;br /&gt;       node.text = newvalue;&lt;br /&gt;       changeMade = true;&lt;br /&gt;    }&lt;br /&gt; } else if (!this.isIE &amp;&amp;amp; node.singleNodeValue) {&lt;br /&gt;    if (node.singleNodeValue.textContent != newvalue) {&lt;br /&gt;       node.singleNodeValue.textContent = newvalue;&lt;br /&gt;       changeMade = true;&lt;br /&gt;    }&lt;br /&gt; } else {&lt;br /&gt;    if (newvalue.length &amp;gt; 0) {&lt;br /&gt;       this.insertNode(xpath);&lt;br /&gt;       changeMade = this.updateNodeValue(xpath, newvalue);&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; return changeMade;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// insertNode&lt;br /&gt;//&lt;br /&gt;// insert a new element (node) into the XML document based on the XPath&lt;br /&gt;XML.prototype.insertNode = function(xpath) {&lt;br /&gt; var xpathComponents = xpath.split("/");&lt;br /&gt; var newChildName = xpathComponents.last();&lt;br /&gt; var parentPath = xpath.substr(0, xpath.length - newChildName.length - 1);&lt;br /&gt; var qualifierLoc = newChildName.indexOf("[");&lt;br /&gt; // remove qualifier for node being added&lt;br /&gt; if (qualifierLoc != -1) {&lt;br /&gt;    newChildName = newChildName.substr(0, qualifierLoc);&lt;br /&gt; }&lt;br /&gt; var node = this.getNode(parentPath);&lt;br /&gt; var newChild = null;&lt;br /&gt; if (this.isIE &amp;&amp;amp; node) {&lt;br /&gt;    newChild = this.xmlDom.createElement(newChildName);&lt;br /&gt;    node.appendChild(newChild);&lt;br /&gt; } else if ((!this.isIE) &amp;&amp;amp; node.singleNodeValue) {&lt;br /&gt;    newChild = this.xmlDom.createElement(newChildName);&lt;br /&gt;    node.singleNodeValue.appendChild(newChild);&lt;br /&gt; } else {&lt;br /&gt;    // add the parent, then re-try to add this child&lt;br /&gt;    var parentNode = this.insertNode(parentPath);&lt;br /&gt;    newChild = this.xmlDom.createElement(newChildName);&lt;br /&gt;    parentNode.appendChild(newChild);&lt;br /&gt; }&lt;br /&gt; return newChild;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;// removeNode&lt;br /&gt;//&lt;br /&gt;// remove an element (node) from the XML document based on the xpath&lt;br /&gt;XML.prototype.removeNode = function(xpath) {&lt;br /&gt; var node = this.getNode(xpath);&lt;br /&gt; var changed = false;&lt;br /&gt; if (this.isIE &amp;&amp;amp;amp; node) {&lt;br /&gt;    node.parentNode.removeChild(node);&lt;br /&gt;    changed = true;&lt;br /&gt; } else if ((!this.isIE) &amp;&amp;amp; node.singleNodeValue) {&lt;br /&gt;    node.singleNodeValue.parentNode.removeChild(node.singleNodeValue);&lt;br /&gt;    changed = true;&lt;br /&gt; }&lt;br /&gt; return changed;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You should now be able to fully read and manipulate XML using XPaths.&lt;br /&gt;&lt;br /&gt;For any significant reformatting or processing of the XML, you'll probably want to utilize XSLT.  So, next I'll provide a simple XSLT class.  It will preload (into a DOM object) an XSL file in its constructor.  This provides rapid transformations of new XML data passed in to it's &lt;code&gt;transform&lt;/code&gt; function (including parameters).&lt;br /&gt;&lt;br /&gt;First, the constructor which requires an URL pointing to the XSL file to be used for transformations.  It does not accept a DOM object like the XML class did as it is assumed that the XSL (layout information) is more-or-less static and stored in a file whereas the XML data would most likely be dynamic and received via an XHR request.&lt;br /&gt;&lt;pre&gt;//&lt;br /&gt;// XSLT Processor&lt;br /&gt;//&lt;br /&gt;function XSLT(xslUrl) {&lt;br /&gt; this.isIE = window.ActiveXObject;&lt;br /&gt; if (this.isIE) {&lt;br /&gt;    var xslDom = new ActiveXObject("MSXML2.FreeThreadedDOMDocument");&lt;br /&gt;    xslDom.async = false;&lt;br /&gt;    xslDom.load(xslUrl);&lt;br /&gt;    if (xslDom.parseError.errorCode != 0) {&lt;br /&gt;       var strErrMsg = "Problem Parsing Style Sheet:\n" +&lt;br /&gt;          " Error #: " + xslDom.parseError.errorCode + "\n" +&lt;br /&gt;          " Description: " + xslDom.parseError.reason + "\n" +&lt;br /&gt;          " In file: " + xslDom.parseError.url + "\n" +&lt;br /&gt;          " Line #: " + xslDom.parseError.line + "\n" +&lt;br /&gt;          " Character # in line: " + xslDom.parseError.linepos + "\n" +&lt;br /&gt;          " Character # in file: " + xslDom.parseError.filepos + "\n" +&lt;br /&gt;          " Source line: " + xslDom.parseError.srcText;&lt;br /&gt;         alert(strErrMsg);&lt;br /&gt;       return false;&lt;br /&gt;    }&lt;br /&gt;    var xslTemplate = new ActiveXObject("MSXML2.XSLTemplate");&lt;br /&gt;    xslTemplate.stylesheet = xslDom;&lt;br /&gt;    this.xslProcessor = xslTemplate.createProcessor();&lt;br /&gt; } else {&lt;br /&gt;    var xslDom = document.implementation.createDocument("", "", null);&lt;br /&gt;    xslDom.async = false;&lt;br /&gt;    xslDom.load(xslUrl);&lt;br /&gt;    this.xslProcessor = new XSLTProcessor();&lt;br /&gt;    this.xslProcessor.importStylesheet(xslDom);&lt;br /&gt;}&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, the &lt;code&gt;transform&lt;/code&gt; function will preform the XSL transformation and return the result.  It accepts parameters to be passed to the XSL as an associative array of parameter name and value pairs.&lt;br /&gt;&lt;pre&gt;// transform&lt;br /&gt;//&lt;br /&gt;// Transform an XML document&lt;br /&gt;XSLT.prototype.transform = function(xml, params) {&lt;br /&gt; // set stylesheet parameters&lt;br /&gt; for (var param in params) {&lt;br /&gt;    if (typeof params[param] != 'function') {&lt;br /&gt;       if (this.isIE) {&lt;br /&gt;          this.xslProcessor.addParameter(param, params[param]);&lt;br /&gt;       } else {&lt;br /&gt;          this.xslProcessor.setParameter(null, param, params[param]);&lt;br /&gt;       }&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; if (this.isIE) {&lt;br /&gt;    this.xslProcessor.input = xml.xmlDom;&lt;br /&gt;    this.xslProcessor.transform();&lt;br /&gt;    var output = this.xslProcessor.output;&lt;br /&gt; } else {&lt;br /&gt;    var resultDOM = this.xslProcessor.transformToDocument(xml.xmlDom);&lt;br /&gt;    var serializer = new XMLSerializer();&lt;br /&gt;    var output = serializer.serializeToString(resultDOM);&lt;br /&gt; }&lt;br /&gt; return output;&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You should now be able to easily utilize XML and XSL on your web pages.  An example usage of this XML/XSLT library would be to first build an XSLT object during the page initialization (onload).&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xslProcessor = new XSLT(xslUrl);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then, when XML responses are received from an AJAX request, process them.  For example,&lt;br /&gt;&lt;pre&gt;// get the XML data&lt;br /&gt;try {&lt;br /&gt; var xmlData = new XML(request.responseXML);&lt;br /&gt;} catch (e) {&lt;br /&gt; alert(request.responseText);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// get the username out of the XML&lt;br /&gt;var userName = xmlData.getNodeValue('//userName');&lt;br /&gt;&lt;br /&gt;// transform the XML to some HTML content&lt;br /&gt;var newData = xslProcessor.transform(xmlData, {'param':'value'});&lt;br /&gt;document.getElementById('someDiv').innerHTML = newData;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update (February 27, 2007): &lt;/b&gt; I previously had a function in my XML class to generate valid HTML for the XML, but I've now modified it to use Oliver Becker's &lt;a href="http://www2.informatik.hu-berlin.de/%7Eobecker/XSLT/#xmlverbatim"&gt;XML to HTML Verbatim Formatter with Syntax Highlighting&lt;/a&gt; stylesheet to format the XML.  If that is not available, it will default to simply serialize the XML and convert the special characters.  The code for this looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/ Define a class variable which can be used to apply a style sheet to&lt;br /&gt;// the XML for format it to HTML&lt;br /&gt;XML.htmlFormatter = new XSLT("xsl/xmlverbatim.xsl");&lt;br /&gt;&lt;br /&gt;// toHTML&lt;br /&gt;//&lt;br /&gt;// Transform the XML into formatted HTML&lt;br /&gt;//&lt;br /&gt;XML.prototype.toHTML = function() {&lt;br /&gt;var html = null;&lt;br /&gt;if (XML.htmlFormatter) {&lt;br /&gt; html = XML.htmlFormatter.transform(this);&lt;br /&gt;} else {&lt;br /&gt; html = this.getNodeAsXml('/');&lt;br /&gt; if (html != null) {&lt;br /&gt;  html = html.replace(/&amp;amp;/g, "&amp;amp;");&lt;br /&gt;  html = html.replace(/&amp;lt;/g, "&amp;amp;lt;");&lt;br /&gt;  html = html.replace(/&amp;gt;/g, "&amp;amp;gt;&amp;lt;br/&amp;gt;");&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;   return html;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As before, the complete package is available to &lt;a href="http://www.pothoven.net/javascripts/src/xml.js" onClick="javascript:urchinTracker('/download/xml.js');"&gt;download&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Update (March 7, 2007):&lt;/b&gt; I have updated this package to utilize Google's &lt;a href="http://goog-ajaxslt.sourceforge.net/"&gt;AJAXSLT&lt;/a&gt; XSL-T implementation when a native JavaScript XSLT function is not available.  I need to test it more before releasing it however.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-116006150736538695?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/116006150736538695/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=116006150736538695' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116006150736538695'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116006150736538695'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/11/using-xml-xpath-and-xslt-with.html' title='Using XML, XPath, and XSLT with JavaScript'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-116189111670509379</id><published>2006-10-26T14:16:00.000-04:00</published><updated>2007-08-21T11:12:09.423-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='SVG'/><category scheme='http://www.blogger.com/atom/ns#' term='XSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Cross-browser inline SVG solved</title><content type='html'>In three previous posts (&lt;a href="http://blog.pothoven.net/2006/05/inline-dynamic-svg-from-xml-with-ajax.html"&gt;1&lt;/a&gt;, &lt;a href="http://blog.pothoven.net/2006/06/help-with-dynamic-inline-svg.html"&gt;2&lt;/a&gt;, &lt;a href="http://blog.pothoven.net/2006/07/follow-up-on-inline-svg.html"&gt;3&lt;/a&gt;) I've discussed getting browser-side dynamically generated SVGs to render properly across browsers. I now have a solution which is working for me.&lt;br /&gt;&lt;br /&gt;Below is a screenshot my test application now.  The XML used as sample data and the XSLT to product the SVG data was provided &lt;a href="http://blog.pothoven.net/2006/06/help-with-dynamic-inline-svg.html"&gt;earlier&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6623/2144/1600/Snap20061026.4.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger/6623/2144/400/Snap20061026.5.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;(Note: I was also able to add some linking between the SVG and HTML so that as you mouse over each bar it will be highlighted in the SVG and simultaneously highlighted the corresponding row in the HTML table.)&lt;br /&gt;&lt;br /&gt;In order to make this work I used the &lt;code&gt;embed&lt;/code&gt; tag for IE and the &lt;code&gt;object&lt;/code&gt; tag for all other browsers (tested with Firefox and Opera).  My HTML has a &lt;code&gt;div&lt;/code&gt; named &lt;i&gt;svgDiv&lt;/i&gt; which will dynamically be given the SVG to render.  For Firefox I can create my &lt;code&gt;object&lt;/code&gt; with the dynamic SVG in one step, but for Internet Explorer, I first have to embed a static, blank SVG (as mentioned &lt;a href="http://blog.pothoven.net/2006/07/follow-up-on-inline-svg.html"&gt;earlier&lt;/a&gt;) and then replace the blank SVG with the dynamic SVG.  However, I determined that this could not be done all in one JavaScript function.  I found that if I programatically add the embedded blank SVG, and them immediately replace the blank SVG with the dynamic one, it would fail, but having something like an &lt;code&gt;alert&lt;/code&gt; message between adding the blank SVG and replacing it with the dynamic SVG would work.  The only explanation I could reason for this is that the actual addition of the &lt;code&gt;embed&lt;/code&gt; element is not completed by the browser until the JavaScript reaches a pause in processing (ie. not flushed/committed), and that the need to create the &lt;code&gt;alert&lt;/code&gt; popup causes the changes to be committed. (&lt;b&gt;does anyone know how to make the browser commit the changes?&lt;/b&gt;).  My solution to this problem was to add the blank SVG when the page is loaded (if IE) so that it will be available for me to replace when I'm ready.&lt;br /&gt;&lt;br /&gt;When the page is loaded, if the browser is IE I call this function to add the blank SVG:&lt;br /&gt;&lt;pre&gt;// embedBlankSVG&lt;br /&gt;//&lt;br /&gt;// add an &amp;lt;embed&amp;gt; tag to display a blank static SVG&lt;br /&gt;// this is needed for IE to replace with dynamic content later&lt;br /&gt;SVG.prototype.embedBlankSVG = function(width, height) {&lt;br /&gt; var svgEmbed = document.createElement('embed');&lt;br /&gt; svgEmbed.setAttribute('codebase', 'http://www.adobe.com/svg/viewer/install/');&lt;br /&gt; svgEmbed.setAttribute('classid', 'clsid:78156a80-c6a1-4bbf-8e6a-3cd390eeb4e2');&lt;br /&gt; svgEmbed.setAttribute('pluginspage', 'http://www.adobe.com/svg/viewer/install/');&lt;br /&gt; svgEmbed.setAttribute('src', 'blank.svg');&lt;br /&gt; svgEmbed.setAttribute('width', width);&lt;br /&gt; svgEmbed.setAttribute('height', height);&lt;br /&gt; svgEmbed.setAttribute('type', 'image/svg+xml');&lt;br /&gt; svgEmbed.setAttribute('id', 'embeddedSVG');&lt;br /&gt; this.svgDiv.appendChild(svgEmbed);&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then, whenever I want to render a new SVG, I invoke this function:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;// render&lt;br /&gt;//&lt;br /&gt;// render the dynamic SVG in the page&lt;br /&gt;SVG.prototype.render = function() {&lt;br /&gt; var str = this.toString();&lt;br /&gt; &lt;br /&gt; if (this.isIE) {&lt;br /&gt;    // For IE, we use an &amp;lt;embed&amp;gt; tab&lt;br /&gt;  &lt;br /&gt;    // build replacement SVG document&lt;br /&gt;    var newSvgElement = document.createElement("svg");&lt;br /&gt;    newSvgElement.setAttribute("xmlns", "http://www.w3.org/2000/svg");&lt;br /&gt;    newSvgElement.setAttribute('xmlns:xlink', "http://www.w3.org/1999/xlink");&lt;br /&gt;    newSvgElement.setAttribute("width", this.width);&lt;br /&gt;    newSvgElement.setAttribute("height", this.height);&lt;br /&gt;    newSvgElement.innerHTML = str;&lt;br /&gt;  &lt;br /&gt;    // replace blank SVG with new SVG&lt;br /&gt;    domtodom(newSvgElement, 'embeddedSVG');&lt;br /&gt; } else {&lt;br /&gt;    this.clearPage();&lt;br /&gt;   &lt;br /&gt;    // For all others (mainly Mozilla based), we use an &amp;lt;object&amp;gt; tag&lt;br /&gt;    // with the SVG data added inline&lt;br /&gt;  &lt;br /&gt;    var svgObject = document.createElement('object');&lt;br /&gt;    svgObject.setAttribute('name', 'svgObj');&lt;br /&gt;    svgObject.setAttribute('codebase', 'http://www.adobe.com/svg/viewer/install/');&lt;br /&gt;    svgObject.setAttribute('classid', 'clsid:78156a80-c6a1-4bbf-8e6a-3cd390eeb4e2');&lt;br /&gt;    svgObject.setAttribute('data', 'data:image/svg+xml,'+ str);&lt;br /&gt;    svgObject.setAttribute('width', '850');&lt;br /&gt;    svgObject.setAttribute('height', '350');&lt;br /&gt;    svgObject.setAttribute('type', 'image/svg+xml');&lt;br /&gt;    this.svgDiv.appendChild(svgObject);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;where the &lt;code&gt;toString&lt;/code&gt; function produces SVG data from XML data (whether read from a file or returned in an &lt;code&gt;XMLHttpRequest&lt;/code&gt; response) and the &lt;code&gt;domtodom&lt;/code&gt; function comes from &lt;a href="http://www.bayes.co.uk/xml/portal.aspx?page=/xml/index.xml&amp;subpage=/xml/utils/domtodom.xml"&gt;Chris Bayes' domtodom.js&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This was tested in Firefox 2.0, IE 7.0, and Opera 9.02 (though the table highlighting isn't currently working in Opera.  It says I have a &lt;i&gt;Security Error: attempted to read protected variable&lt;/i&gt;).&lt;br /&gt;&lt;br /&gt;&lt;a name="help"&gt;&lt;/a&gt;&lt;br /&gt;&lt;h3&gt;Help requested&lt;/h3&gt;&lt;br /&gt;I would like to be able to correspondingly highlight bars in the graph when I mouse over the table rows, but every attempt to add the:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;script type="text/ecmascript"&amp;gt;&lt;br /&gt;&amp;lt;![CDATA[&lt;br /&gt;...&lt;br /&gt;]]&amp;gt;&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;to the SVG data (as described &lt;a href="http://wiki.svg.org/Inter-Document_Communication"&gt;here&lt;/a&gt;) immediately causes Firefox to crash.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Updated 12/07/2006 per comment below&lt;/h3&gt;&lt;br /&gt;The comment below asked what the &lt;code&gt;clearPage&lt;/code&gt; function does.  Since I can't format it nicely in the comments section, I decided to update the post with it.  It doesn't really do anything too profound which is why I didn't post it originally.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// clear out any old displayed data from the page&lt;br /&gt;SVG.prototype.clearPage = function() {&lt;br /&gt; this.svgDiv.innerHTML = '';&lt;br /&gt; this.tableDiv.innerHTML = '';&lt;br /&gt; &lt;br /&gt; if (this.isIE) {&lt;br /&gt;  this.embedBlankSVG(this.svgWidth, this.svgHeight);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Additionally, while I'm updating this posting anyway, I was able to add JavaScript to my SVG through the XSLT (see &lt;a href="#help"&gt;Help Requested&lt;/a&gt; section) by adding it like this in the XSL:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;xsl:element name="script"&amp;gt;&lt;br /&gt;   &amp;lt;xsl:attribute name="type"&amp;gt;&lt;br /&gt;      &amp;lt;xsl:value-of select="'text/ecmascript'" /&amp;gt;&lt;br /&gt;   &amp;lt;/xsl:attribute&amp;gt;&lt;br /&gt;   &amp;lt;xsl:text&amp;gt;&lt;br /&gt;   &amp;amp;lt;![CDATA[&lt;br /&gt; ...  &lt;br /&gt;   ]]&amp;amp;gt;&lt;br /&gt;   &amp;lt;/xsl:text&amp;gt;&lt;br /&gt;&amp;lt;/xsl:element&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I haven't gotten it to do everything that I want yet, but at least the browser no longer immediately crashes.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-116189111670509379?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://blog.pothoven.net/2006/07/follow-up-on-inline-svg.html' title='Cross-browser inline SVG solved'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/116189111670509379/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=116189111670509379' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116189111670509379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116189111670509379'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/10/cross-browser-inline-svg-solved.html' title='Cross-browser inline SVG solved'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-116171899214162442</id><published>2006-10-24T15:35:00.000-04:00</published><updated>2006-10-25T05:06:36.500-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='SVG'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Scalable Vector Graphics (SVG) Intro</title><content type='html'>I ran across an introduction to SVG today entitled, &lt;a href="http://luxor-xul.sourceforge.net/talk/jug-nov-2002/slides.html"&gt;Scalable Vector Graphics (SVG), Creating High-End 2D Graphics Using XML&lt;/a&gt; while I was looking for something else, but it gives a nice summary of SVG so I thought I'd share it.   It covers the main questions:&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;What&lt;/b&gt; is SVG (and what is is not)?&lt;/li&gt;&lt;li&gt;&lt;b&gt;How&lt;/b&gt; do you use SVG?&lt;/li&gt;&lt;li&gt;&lt;b&gt;Why&lt;/b&gt; should you use SVG?&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;It's a few years old (2002), but still a nice article.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-116171899214162442?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://luxor-xul.sourceforge.net/talk/jug-nov-2002/slides.html' title='Scalable Vector Graphics (SVG) Intro'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/116171899214162442/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=116171899214162442' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116171899214162442'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116171899214162442'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/10/scalable-vector-graphics-svg-intro.html' title='Scalable Vector Graphics (SVG) Intro'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-116126526774800816</id><published>2006-10-19T09:13:00.000-04:00</published><updated>2006-10-27T10:28:10.213-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>IE7, any real improvement?</title><content type='html'>Now that IE7 is out, I will be 'upgrading' to it to ensure my web sites support it, but I wonder have much of an upgrade it really is -- especially since they've had 5 years to improve it.&lt;br /&gt;&lt;br /&gt;I got an email this morning from &lt;a href="http://www.eweek.com"&gt;eWEEK&lt;/a&gt; highlighting Jim Rapoza's &lt;a href="http://www.eweek.com/article2/0,1895,2033704,00.asp?kc=EWEWEMNL101906EPTA"&gt;glowing review&lt;/a&gt; of IE7.  One one hand, he acknowledges that IE7 hasn't made any revolutionary step forward in the browser space, but has merely &lt;i&gt;caught up&lt;/i&gt; as he says,&lt;br /&gt;&lt;br /&gt;&lt;p class="quote"&gt;While we wouldn't yet call IE 7 one of the best browsers available today, Microsoft has greatly closed the distance between its browser and those of its competitors. Version 7 catches IE up with now-common browser features...&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;but after summarizing the key user interface and security enhancements, he makes this statement,&lt;br /&gt;&lt;br /&gt;&lt;p class="quote"&gt;On the standards side, IE 7 does a much better job than previous versions of the product at supporting key standards, such as Cascading Style Sheets support and JavaScript. IE 7 still doesn't have perfect standards support, but it is much better than previous versions of the browser. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;That statement puzzles me since according to &lt;a href="http://www.webdevout.net/browser_support_summary.php?uas=IE6-IE7-FX1_5-OP8-OP9"&gt;this analysis&lt;/a&gt; which summarizes web browser standards support, I see very little improvement in standards support except in CSS 2.1 &amp; 3 Basic Selectors.&lt;br /&gt;&lt;br /&gt;To me, it sounds like Microsoft has merely put a new glossy finish on a rotting infrastructure (a whitewashed tomb so to speak) which is really disappointing to me, since after listening to &lt;a href="http://ajaxian.com/archives/audible-ajax-episode-18-the-ie7-team"&gt;this interview&lt;/a&gt; of the IE7 team by the &lt;a href="http://ajaxian.com/"&gt;Ajaxians&lt;/a&gt; last month, I was more optimistic about standards support.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-116126526774800816?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.webdevout.net/browser_support_summary.php?uas=IE6-IE7-FX1_5-OP8-OP9' title='IE7, any real improvement?'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/116126526774800816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=116126526774800816' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116126526774800816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116126526774800816'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/10/ie7-any-real-improvement.html' title='IE7, any real improvement?'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-116005849321642946</id><published>2006-10-05T10:19:00.000-04:00</published><updated>2007-09-29T19:59:33.749-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Using Cookies in JavaScript</title><content type='html'>&lt;b&gt;Update (February 23, 2007):&lt;/b&gt; I have updated the cookies functionality below to utilize flash storage if available and fall back to cookies if not available. See &lt;a href="http://blog.pothoven.net/2007/02/cookies-and-flash-for-local-storage.html"&gt;Cookies and Flash for local storage&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Whether you like them or not, cookies are an easy way store simple user specific information across web pages and sessions.  Here are the basic JavaScript methods to create (and update), read, or delete (CRUD) cookies with JavaScript.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function createCookie(name, value, days) {&lt;br /&gt;if (days) {&lt;br /&gt; var date = new Date();&lt;br /&gt; date.setTime(date.getTime()+(days*24*60*60*1000));&lt;br /&gt; var expires = "; expires="+date.toGMTString();&lt;br /&gt;}&lt;br /&gt;else {&lt;br /&gt; var expires = ";";&lt;br /&gt;}&lt;br /&gt;document.cookie = name + "=" + value + expires + "; path=/";&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function readCookie(name) {&lt;br /&gt;var nameEQ = name + "=";&lt;br /&gt;var cookies = document.cookie.split(';');&lt;br /&gt;for(var i = 0; i &amp;lt; cookies.length;i++) {&lt;br /&gt; var cookie = cookies[i];&lt;br /&gt; while (cookie.charAt(0) == ' ') {&lt;br /&gt;  cookie = cookie.substring(1, cookie.length);&lt;br /&gt; }&lt;br /&gt; if (cookie.indexOf(nameEQ) == 0) {&lt;br /&gt;  return cookie.substring(nameEQ.length, cookie.length);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;return null;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;function eraseCookie(name) {&lt;br /&gt;createCookie(name, "", -1);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Detailed explanation of code how this code works is available &lt;a href="http://www.quirksmode.org/js/cookies.html"&gt;here&lt;/a&gt;.  This script was originally written by &lt;a href="http://www.scottandrew.com/"&gt;Scott Andrew&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-116005849321642946?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.quirksmode.org/js/cookies.html' title='Using Cookies in JavaScript'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/116005849321642946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=116005849321642946' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116005849321642946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116005849321642946'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/10/using-cookies-in-javascript.html' title='Using Cookies in JavaScript'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-116005780543993192</id><published>2006-10-05T10:09:00.000-04:00</published><updated>2007-08-21T11:12:57.861-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><title type='text'>Add/remove options to/from a select list</title><content type='html'>In my past two posts I've show how to &lt;a href="http://blog.pothoven.net/2006/10/move-options-across-select-lists.html"&gt;move options between select lists&lt;/a&gt; and &lt;a href="http://blog.pothoven.net/2006/10/move-options-up-and-down-select-lists.html"&gt;reorder options in a list&lt;/a&gt;.  Now, to round out my little series on selection list manipulation with JavaScript, I'll give simple functions to add a value or remove a value from a list.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// addSelectOption&lt;br /&gt;//&lt;br /&gt;// Add the single select option to the selection list with the id specified&lt;br /&gt;//&lt;br /&gt;function addSelectOption(selectId, value, display) {&lt;br /&gt; if (display == null) {&lt;br /&gt;  display = value;&lt;br /&gt; }&lt;br /&gt;    var anOption = document.createElement('option');&lt;br /&gt;    anOption.value = value;&lt;br /&gt;    anOption.innerHTML = display;&lt;br /&gt;    document.getElementById(selectId).appendChild(anOption);&lt;br /&gt;    return anOption;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// removeSelectOption&lt;br /&gt;//&lt;br /&gt;// Remove the option with the specified value from the list of options&lt;br /&gt;// in the selection list with the id specified&lt;br /&gt;//&lt;br /&gt;function removeSelectOption(selectId, value) {&lt;br /&gt; var select = document.getElementById(selectId);&lt;br /&gt; var kids = select.childNodes; &lt;br /&gt; var numkids = kids.length; &lt;br /&gt; for (var i = 0; i &lt; numkids; i++) {&lt;br /&gt;      if (kids[i].value == value) {&lt;br /&gt;   select.removeChild(kids[i]);&lt;br /&gt;   break;&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-116005780543993192?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/116005780543993192/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=116005780543993192' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116005780543993192'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/116005780543993192'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/10/addremove-options-tofrom-select-list.html' title='Add/remove options to/from a select list'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115998684514262913</id><published>2006-10-04T14:33:00.000-04:00</published><updated>2007-08-21T11:13:19.499-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><title type='text'>Move options up and down select lists</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6623/2144/1600/moveupdown.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://photos1.blogger.com/blogger/6623/2144/320/moveupdown.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;In my &lt;a href="http://blog.pothoven.net/2006/10/move-options-across-select-lists.html"&gt;previous post&lt;/a&gt; I showed how to move options in a select list from one list to another with JavaScript.  Once you've moved them, you may want to reorder them if order is important.  So, now I'll show how to do that.&lt;br /&gt;&lt;br /&gt;First, define your control in HTML to look something like what is shown to the right where you have up and down buttons which will be used to move selected elements up and down in the list.  &lt;br /&gt;&lt;br /&gt;Your HTML will look something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;select id="orderedList" multiple="multiple"&gt;&amp;lt;/select&gt;&lt;br /&gt;&amp;lt;img src="moveup.gif" alt="Move Up" onclick="moveOptionsUp('orderedList')" /&gt;&lt;br /&gt;&amp;lt;img src="movedown.gif" alt="Move Down" onclick="moveOptionsDown('orderedList')" /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see, I've added two image which when you click on them invoke the &lt;code&gt;moveOptionsUp&lt;/code&gt; and &lt;code&gt;moveOptionsDown&lt;/code&gt; function.  The JavaScript for these functions looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// moveOptionsUp&lt;br /&gt;//&lt;br /&gt;// move the selected options up one location in the select list&lt;br /&gt;//&lt;br /&gt;function moveOptionsUp(selectId) {&lt;br /&gt; var selectList = document.getElementById(selectId);&lt;br /&gt; var selectOptions = selectList.getElementsByTagName('option');&lt;br /&gt; for (var i = 1; i &amp;lt; selectOptions.length; i++) {&lt;br /&gt;  var opt = selectOptions[i];&lt;br /&gt;  if (opt.selected) {&lt;br /&gt;   selectList.removeChild(opt);&lt;br /&gt;   selectList.insertBefore(opt, selectOptions[i - 1]);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// moveOptionsDown&lt;br /&gt;//&lt;br /&gt;// move the selected options down one location in the select list&lt;br /&gt;//&lt;br /&gt;function moveOptionsDown(selectId) {&lt;br /&gt; var selectList = document.getElementById(selectId);&lt;br /&gt; var selectOptions = selectList.getElementsByTagName('option');&lt;br /&gt; for (var i = selectOptions.length - 2; i &gt;= 0; i--) {&lt;br /&gt;  var opt = selectOptions[i];&lt;br /&gt;  if (opt.selected) {&lt;br /&gt;   var nextOpt = selectOptions[i + 1];&lt;br /&gt;   opt = selectList.removeChild(opt);&lt;br /&gt;   nextOpt = selectList.replaceChild(opt, nextOpt);&lt;br /&gt;   selectList.insertBefore(nextOpt, opt);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Unfortunately there's no &lt;code&gt;insertAfter&lt;/code&gt; function for a node, so the &lt;code&gt;moveOptionsDown&lt;/code&gt; function is a little funky.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115998684514262913?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115998684514262913/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115998684514262913' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115998684514262913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115998684514262913'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/10/move-options-up-and-down-select-lists.html' title='Move options up and down select lists'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115998680345260868</id><published>2006-10-04T14:32:00.000-04:00</published><updated>2006-12-22T01:34:54.906-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Move options across select lists</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6623/2144/1600/moveacross.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://photos1.blogger.com/blogger/6623/2144/320/moveacross.png" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Perhaps you've seen controls like what is shown to the right where you have some list of items in one selection list and you can move them in and out of another by pressing the '&lt;' and '&gt;' buttons and you want to know how to do it.&lt;br /&gt;&lt;br /&gt;Here's one way.  First, define your select lists and buttons similarly to that shown below.  In my case, I fill in the select options with AJAX, so I have no options defined in the HTML, but if you generated the HTML on the server, then you'll have options in the HTML as well.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   &amp;lt;select id="available" size="10" multiple="multiple"&gt;&amp;lt;/select&gt;&lt;br /&gt;   &amp;lt;input type="button" value="&amp;gt;" &lt;br /&gt;          onclick="moveOptionsAcross($('available'), $('selected'))" /&gt;&amp;gt;&amp;lt;/input&gt;&lt;br /&gt;   &amp;lt;input type="button" value="&amp;lt;" &lt;br /&gt;          onclick="moveOptionsAcross($('selected'), $('available'))" /&gt;&amp;lt;&amp;lt;/input&gt;&lt;br /&gt;   &amp;lt;select id="selected" size="10" multiple="multiple"&gt;&amp;lt;/select&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now include the following JavaScript code:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// moveOptionsAcross&lt;br /&gt;//&lt;br /&gt;// Move selected options from one select list to another&lt;br /&gt;//&lt;br /&gt;function moveOptionsAcross(fromSelectList, toSelectList) {&lt;br /&gt;  var selectOptions = fromSelectList.getElementsByTagName('option');&lt;br /&gt;  for (var i = 0; i &amp;lt; selectOptions.length; i++) {&lt;br /&gt;     var opt = selectOptions[i];&lt;br /&gt;     if (opt.selected) {&lt;br /&gt;      fromSelectList.removeChild(opt);&lt;br /&gt;      toSelectList.appendChild(opt);&lt;br /&gt;&lt;br /&gt; // originally, this loop decremented from length to 0 so that you&lt;br /&gt; // wouldn't have to worry about adjusting the index.  However, then&lt;br /&gt; // moving multiple options resulted in the order being reversed from when&lt;br /&gt; // was in the original selection list which can be confusing to the user.&lt;br /&gt; // So now, the index is adjusted to make sure we don't skip an option.&lt;br /&gt;      i--;&lt;br /&gt;     }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You should now be able to select options in one list and press the appropriate button to move them to the other list.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115998680345260868?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115998680345260868/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115998680345260868' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115998680345260868'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115998680345260868'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/10/move-options-across-select-lists.html' title='Move options across select lists'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115997171704752528</id><published>2006-10-04T10:21:00.000-04:00</published><updated>2006-10-10T14:42:28.666-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><title type='text'>Checkbox as Radio Buttons</title><content type='html'>Sometimes you want the appearance of a checkbox on your form, but you want it to function like a radio button where only a single option can be selected at a time.  The following JavaScript function will make a group of checkboxes function like a radiogroup.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function singleSelectCheckbox(checkbox) {&lt;br /&gt; var aGroup = checkbox.parentNode;&lt;br /&gt; var allInputs = aGroup.getElementsByTagName("input");&lt;br /&gt; for (var i = 0; i &lt; allInputs.length; i++) {&lt;br /&gt;  if (allInputs[i] == checkbox) {&lt;br /&gt;   continue;&lt;br /&gt;  } else {&lt;br /&gt;   allInputs[i].checked = false;&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This assumes you've placed the checkboxes to work as a radio button group in some sort of containing HTML element (a &lt;code&gt;td&lt;/code&gt; or &lt;code&gt;span&lt;/code&gt; or something like that).&lt;br /&gt;Your HTML code would look something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;span&gt;&lt;br /&gt;&amp;lt;input onclick="singleSelectCheckbox(this)" type="checkbox"&gt;Option 1&amp;lt;/input&gt;&lt;br /&gt;&amp;lt;input onclick="singleSelectCheckbox(this)" type="checkbox"&gt;Option 2&amp;lt;/input&gt;&lt;br /&gt;&amp;lt;input onclick="singleSelectCheckbox(this)" type="checkbox"&gt;Option 3&amp;lt/input&gt;&lt;br /&gt;&amp;lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115997171704752528?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115997171704752528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115997171704752528' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115997171704752528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115997171704752528'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/10/checkbox-as-radio-buttons.html' title='Checkbox as Radio Buttons'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115936745394003551</id><published>2006-09-27T10:30:00.000-04:00</published><updated>2006-09-27T10:30:54.446-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>The move from Netgear MP101 to Xbox Media Center</title><content type='html'>For some time now, I've been using a Netgear MP101 with the free &lt;a href="href=" com=""&gt;TwonkyVision&lt;/a&gt; MusicServer.  Unfortunately, the free version of MusicServer is no longer available from TwonkyVision as it is now part of the TwonkyMedia package, but you can still get it from places like the "Files" section of the &lt;a href="http://tech.groups.yahoo.com/group/NetgearMP101Group/"&gt;NetgearMP101 Yahoo! Group&lt;/a&gt;.  Even though the MP101 includes wireless B support, in order to have optimum playback performance, I used two &lt;a href="http://catalog.belkin.com/IWCatProductPage.process?Product_Id=136493"&gt;Belkin Wireless G&lt;/a&gt; routers bridged together and connected the MP101 to the bridged router via ethernet.  Using the matched routers to do the wireless communication gives significant communications improvements over using the wireless B.&lt;br /&gt;&lt;br /&gt;Well, that was well and good for music, but I wanted to do more (view pictures, videos, etc).  I first tried out &lt;a href="http://www.mythtv.org/"&gt;MythTV&lt;/a&gt; and &lt;a href="http://www.team-mediaportal.com/"&gt;MediaPortal&lt;/a&gt; on a spare computer.  They were ok, though a bit slow (particularly in startup time) and MythTV proved a little difficult to get working with the hardware I had.  Though the biggest factor in not going that route was that my wife was not happy with the computer case sitting next to the entertainment center.  Yes, I could buy a small form-factor case, motherboard, etc. and build a small PC to look like another audio component to fit in the entertainment canter, but I wanted to do this cheaply.&lt;br /&gt;&lt;br /&gt;Then I ran into this &lt;a href="http://www.popsci.com/popsci/how20/c635c6f39986c010vgnvcm1000004eecbccdrcrd.html"&gt;article&lt;/a&gt; in Popular Science (printed edition) to build an "Xbox Media Monster".  I knew you could hack Xboxes to run Linux or make them into media centers, but I thought you needed a modchip or good soldering skills (which I don't have), but apparently now you no longer need to do any hardware modifications!  The instructions in the printed Popular Science article were a little too general, so I found more complete instructions &lt;a href="http://www.productwiki.com/microsoft_xbox/article/how_to_go_from_xbox_to_xbox_media_center_in_30_minutes.html"&gt;here&lt;/a&gt; which include the specific part numbers you need for the original MechAssault.&lt;br /&gt;&lt;br /&gt;So, I made a quick stop to &lt;a href="http://www.gamestop.com/Default.asp"&gt;GameStop&lt;/a&gt; to buy a used Xbox, an Action Replay memory device, and an original copy of the game MechAssault.  Finding the original MechAssault was the only tricky part and required me to go to a second store.  Then I simply followed the &lt;a href="http://www.productwiki.com/microsoft_xbox/article/how_to_go_from_xbox_to_xbox_media_center_in_30_minutes.html"&gt;instructions&lt;/a&gt; and I was up and running &lt;a href="http://www.xboxmediacenter.de/info_project.htm"&gt;XBMC&lt;/a&gt; (XBox Media Center) in no time.  I used the &lt;a href="http://xbmc.nightspirit.nl/"&gt;XBMC BLINGBLING edition&lt;/a&gt; as it was the most up-to-date edition I could find.&lt;br /&gt;&lt;br /&gt;I'm loving my Xbox Media Center now. Besides being able to view and stream my pictures, music, and videos from my Linux server, XBMC also contains a scripting ability for which many scripts are &lt;a href="http://www.xbmcscripts.com/"&gt;available&lt;/a&gt; to do things like view the latest videos on &lt;a href="http://www.youtube.com/"&gt;YouTube&lt;/a&gt;, &lt;a href="http://video.google.com/"&gt;Google Videos&lt;/a&gt;, or your favorite community video source, stream audio from &lt;a href="http://shoutcast.com/"&gt;Shoutcast&lt;/a&gt;, &lt;a href="http://www.live365.com/index.live"&gt;Live365&lt;/a&gt;, or other audio sources, view current weather radar, TV listings, etc.  If there's a media source online, someone probably has a script to view it via XBMC.&lt;br /&gt;&lt;br /&gt;XBMC can play video DVD and audio CDs (and rip them for you). You can use &lt;a href="http://www.xboxmediacenter.com/wiki/index.php?title=Xlink_Kai"&gt;Xlink Kai&lt;/a&gt; to play games across the internet instead of a normal LAN (Local Area Network). Additionally, you can install Xbox apps like &lt;a href="http://dvd2xbox.xbox-scene.com/"&gt;dvd2xbox&lt;/a&gt; to rip your Xbox games to the Xbox hard drive.  Extracting your favorite game to the hard drive can help game performance.  Another Xbox application you may want to try is &lt;a href="http://mameox.sourceforge.net/"&gt;MAMEoX&lt;/a&gt; to play your favorite classic arcade game on your Xbox.&lt;br /&gt;&lt;br /&gt;XBMC includes a web server so you can remotely control your Xbox, and an FTP server to move files to/from your Xbox.&lt;br /&gt;&lt;br /&gt;All in all, it's an impressive system for around $140 (for Xbox and all necessary accessories).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115936745394003551?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.xboxmediacenter.de/' title='The move from Netgear MP101 to Xbox Media Center'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115936745394003551/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115936745394003551' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115936745394003551'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115936745394003551'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/09/move-from-netgear-mp101-to-xbox-media.html' title='The move from Netgear MP101 to Xbox Media Center'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115886149560089311</id><published>2006-09-21T13:48:00.000-04:00</published><updated>2006-09-21T14:01:13.863-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Creating Mashups</title><content type='html'>For those of you interested in creating &lt;i&gt;mashups&lt;/i&gt; (a website or web application that combines content from more than one source - wikipedia), then &lt;a href="http://www.programmableweb.com/"&gt;ProgrammableWeb&lt;/a&gt; is the place to go!&lt;br /&gt;&lt;br /&gt;ProgrammableWeb showcases the latest mashup sites.  For each site showcased, they provide a list of the mashup APIs used.  They also have an API dashboard which provides a comprehensive database of API resources available to you as a developer and they have lots of tutorials and articles.&lt;br /&gt;&lt;br /&gt;At the time of this writting, there are 1001 mashup sites showcased.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115886149560089311?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.programmableweb.com/' title='Creating Mashups'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115886149560089311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115886149560089311' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115886149560089311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115886149560089311'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/09/creating-mashups.html' title='Creating Mashups'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115886002090502731</id><published>2006-09-21T13:33:00.000-04:00</published><updated>2006-09-21T13:33:42.393-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Using IE in Linux</title><content type='html'>If you're a web developer, you mostly likely have multiple browsers installed, or readily available to you, to test your web pages with.  This is even more essential if you're building AJAX applications as there are many norotious variances between JavaScript and CSS implementations - particularly between Internet Explorer and more W3C compliant browsers like Mozilla, Firefox, and Opera.&lt;br /&gt;&lt;br /&gt;To use IE in Linux, you have a few options.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Use WINE (Windows Emulator)&lt;/li&gt;&lt;li&gt;Use a virtual machine like VMWARE&lt;/li&gt;&lt;li&gt;Connect remotely to a Windows machine with something like VNC, NX, or Citrix.&lt;/li&gt;&lt;/ol&gt;I've used all three of these methods, and they all have their pros and cons, which it not really the point of this article, but I'll provide a few.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;WINE&lt;br /&gt;&lt;dl&gt;&lt;br /&gt;&lt;dt&gt;pros&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;free, good performance and minimal memory requirements&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;cons&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;can be hard to setup, may not always work due to emulation problems (ex. APIs not implmented in WINE)&lt;/dd&gt;&lt;/dl&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;VMWARE&lt;br /&gt;&lt;dl&gt;&lt;br /&gt;&lt;dt&gt;pros&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;fully supported, descent performance with sufficient hardware&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;cons&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;not free, resource intensive, takes longer to startup&lt;/dd&gt;&lt;/dl&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Remote connection&lt;br /&gt;&lt;dl&gt;&lt;br /&gt;&lt;dt&gt;pros&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;real windows machine&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;cons&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;requires a second machine, may have network latency&lt;/dd&gt;&lt;/dl&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;To address the "hard to setup" aspect of getting IE to run in WINE, I've used &lt;a href="http://www.von-thadden.de/Joachim/WineTools/"&gt;winetools&lt;/a&gt; before which works well, but it's really meant to set up a whole lot more than just IE.  I've recently run across &lt;a href="http://www.tatanka.com.br/ies4linux/index-en.html"&gt;IEs4Linux&lt;/a&gt; which is a more straightforward method to simply set up IE in Linux under Wine.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115886002090502731?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.tatanka.com.br/ies4linux/index-en.html' title='Using IE in Linux'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115886002090502731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115886002090502731' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115886002090502731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115886002090502731'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/09/using-ie-in-linux.html' title='Using IE in Linux'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115885624051293208</id><published>2006-09-21T12:30:00.000-04:00</published><updated>2006-09-21T12:30:40.770-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Speeding up Linux</title><content type='html'>I'm a happy Linux user and like to tweak my operating systems to get as much performance out of them as possible, though not at the expense of sacrificing too much user experience niceties.&lt;br /&gt;This article posses &lt;a href="http://www.noenemies.com/7-ways-to-speed-up-your-linux-desktop/"&gt;7 Ways to Speed Up Your Linux Desktop&lt;/a&gt; which in a nutshell are:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Use a Minimalistic Window Manager/Desktop Environment&lt;/li&gt;&lt;li&gt;Customizing GNOME, KDE, or Xfce&lt;/li&gt;&lt;li&gt;Get More RAM&lt;/li&gt;&lt;li&gt;Kill Unneeded Processes&lt;/li&gt;&lt;li&gt;Remove Virtual Terminals&lt;/li&gt;&lt;li&gt;Use “Small” Applications&lt;/li&gt;&lt;li&gt;Remove Start-Up Services&lt;/li&gt;&lt;/ol&gt;Like I said, I still want a nice user experience, so I'm sticking with KDE and Firefox for my browser (for many reasons, not the least of which is that I can synchronize bookmarks and plugins across multiple computers and OSes for a common Internet experience).  However, the other tips can be useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115885624051293208?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.noenemies.com/7-ways-to-speed-up-your-linux-desktop/' title='Speeding up Linux'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115885624051293208/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115885624051293208' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115885624051293208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115885624051293208'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/09/speeding-up-linux.html' title='Speeding up Linux'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115885549237335081</id><published>2006-09-21T12:17:00.000-04:00</published><updated>2007-08-21T11:08:43.623-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Web Design Trends</title><content type='html'>This &lt;a href="http://www.ontoinfo.com/2006/09/13/current-trends-in-web-design/"&gt;article&lt;/a&gt; analyzes some of the current trends in web design.  It breaks them down into the following 8 categories:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Web Desktop Style&lt;/li&gt;&lt;li&gt;Silver Orange Style&lt;/li&gt;&lt;li&gt;Apple Style&lt;/li&gt;&lt;li&gt;Microsoft Style&lt;/li&gt;&lt;li&gt;Magazine Style&lt;/li&gt;&lt;li&gt;Rounded Plain Style&lt;/li&gt;&lt;li&gt;Web 2.0 Design Style&lt;/li&gt;&lt;li&gt;Adobe / Macromedia Style&lt;/li&gt;&lt;/ul&gt;For each style, the distinctive elements for each style are described, a sample screenshot snippet is provided as well a links to sites illustrating these styles, then pros and cons for using each style is given.  Perhaps this will be useful for deciding how to design your next webpage.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115885549237335081?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.ontoinfo.com/2006/09/13/current-trends-in-web-design/' title='Web Design Trends'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115885549237335081/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115885549237335081' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115885549237335081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115885549237335081'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/09/web-design-trends.html' title='Web Design Trends'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115885471014214377</id><published>2006-09-21T12:05:00.000-04:00</published><updated>2006-09-21T12:05:11.970-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Web Metrics</title><content type='html'>Good metrics for web sites are nearly as ellusive as good metrics in software development.  The most frequently used metric in software is probably the lines of code (LOC) count which totally rewards inefficient, bloated, cut-n-paste style coding over well designed and reusable coding styles.  Similarly, web metrics like hit counts and page views can similarly reward inefficient web coding styles (such as lots of extra page elements (graphics) to be downloaded, or excessive numbers of pages to be navigated to get the content).&lt;br /&gt;&lt;br /&gt;This &lt;a href="http://ajax.sys-con.com/read/267061.htm"&gt;article&lt;/a&gt; has a nice sumary of some metrics like pageviews and reach and how redesigning some populate sites to use newer techniques like AJAX and RSS would significantly change their current metrics.  It doesn't offer a solution for a good metric, but demonstrates the need for one.&lt;br /&gt;&lt;br /&gt;In the aforementioned article, it has a link for specifically &lt;a href="http://www.google.com/support/analytics/bin/answer.py?answer=33985&amp;amp;topic=7292"&gt;measuring AJAX applications with Analytics&lt;/a&gt; which is useful information.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115885471014214377?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://ajax.sys-con.com/read/267061.htm' title='Web Metrics'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115885471014214377/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115885471014214377' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115885471014214377'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115885471014214377'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/09/web-metrics.html' title='Web Metrics'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115878323425471659</id><published>2006-09-20T16:13:00.000-04:00</published><updated>2006-09-20T18:05:38.413-04:00</updated><title type='text'>No phishing allowed</title><content type='html'>I ran across the OpenDNS.org service the other day from some technology video podcast where David Ulevitch (founder of OpenDNS) was being interviewed about it.  You simply set the DNS in your router (or on your PC) to the following two addresses:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;208.67.222.222&lt;/li&gt;&lt;li&gt;208.67.220.220&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Then, whenever you attempt to access a phishing (scam) site, it will instead give you a message that the site you attempted to access was a phishing site.&lt;br /&gt;&lt;br /&gt;This service will also correct obvious typos in your URL which can be handy too.&lt;br /&gt;&lt;br /&gt;They also claim to be a faster DNS  because the have "a really big, smart cache" and are "geographically distributed", however I don't see how accessing their regional server can be faster than the local DNS on your ISP network, but I'm currently using this service and it's plenty fast.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115878323425471659?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.opendns.com/' title='No phishing allowed'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115878323425471659/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115878323425471659' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115878323425471659'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115878323425471659'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/09/no-phishing-allowed.html' title='No phishing allowed'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115513687058557605</id><published>2006-08-09T11:04:00.000-04:00</published><updated>2007-08-21T11:07:26.888-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Tracking satellites</title><content type='html'>I've always had a passing interest in tracking satellites.  For over 10 years I've had &lt;a href="http://www.dransom.com/stsplus.html"&gt;STSORBIT PLUS&lt;/a&gt; Space Shuttle and Satellite Tracking Software by David H. Ransom, Jr. installed on my computer to bring up once in a while, particularly during Space Shuttle missions.  However, it's kind of antiquated and requires manual fetching of the current orbital data ("TLEs" or "2-Line Elements") to use it.&lt;br /&gt;&lt;br /&gt;Similarly, in my earlier post on using XPlanet to generate &lt;a href="http://blog.pothoven.net/2006/01/current-satellite-wallpaper.html"&gt;current satellite wallpaper&lt;/a&gt; I discuss how to build a dynamic wallpaper which shows satellite tracking over the current earth image (with current cloud cover) but it also requires fetching the TLEs.&lt;br /&gt;&lt;br /&gt;However, now it appears I can drop STSPLUS in favor of an Ajax enabled &lt;a href="http://www.n2yo.com/"&gt;online version&lt;/a&gt; which has lots of nice features and utilizes Google maps for the mapping.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115513687058557605?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.n2yo.com/' title='Tracking satellites'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115513687058557605/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115513687058557605' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115513687058557605'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115513687058557605'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/08/tracking-satellites.html' title='Tracking satellites'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115273146465312051</id><published>2006-07-12T15:10:00.000-04:00</published><updated>2007-08-21T10:42:47.688-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Get request parameters through JavaScript</title><content type='html'>This is not original content, I'm just a reciting information I found elsewhere (see &lt;a href="http://www.javaworld.com/javaforums/showthreaded.php?Cat=&amp;Board=Enterprisejava&amp;Number=18880&amp;page=&amp;view=&amp;sb=5&amp;o=&amp;vc=1"&gt;original&lt;/a&gt;), but with more processing done on the client side in JavaScript for Ajax, it's sometimes necessary to be able to process parameters passed on the URL with JavaScript as you would with PHP, JSP, etc. on the server.  So since I found a method to do it, I'm posting it here for my own benefit so I don't have to find it again.  You can also download it &lt;a href="http://www.pothoven.net/files/parameters.js" onClick="javascript:urchinTracker('/download/parameters.js');"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function getParameter ( queryString, parameterName ) {&lt;br /&gt;   // Add "=" to the parameter name (i.e. parameterName=value)&lt;br /&gt;   var parameterName = parameterName + "=";&lt;br /&gt;   if ( queryString.length &amp;gt; 0 ) {&lt;br /&gt;      // Find the beginning of the string&lt;br /&gt;      begin = queryString.indexOf ( parameterName );&lt;br /&gt;      // If the parameter name is not found, skip it, otherwise return the value&lt;br /&gt;      if ( begin != -1 ) {&lt;br /&gt;         // Add the length (integer) to the beginning&lt;br /&gt;         begin += parameterName.length;&lt;br /&gt;         // Multiple parameters are separated by the "&amp;amp;" sign&lt;br /&gt;         end = queryString.indexOf ( "&amp;amp;" , begin );&lt;br /&gt;      if ( end == -1 ) {&lt;br /&gt;         end = queryString.length&lt;br /&gt;      }&lt;br /&gt;      // Return the string&lt;br /&gt;      return unescape ( queryString.substring ( begin, end ) );&lt;br /&gt;   }&lt;br /&gt;   // Return "null" if no parameter has been found&lt;br /&gt;   return "null";&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;queryString&lt;/code&gt; is obtained via &lt;code&gt;var queryString = window.top.location.search.substring(1);&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115273146465312051?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115273146465312051/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115273146465312051' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115273146465312051'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115273146465312051'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/07/get-request-parameters-through.html' title='Get request parameters through JavaScript'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115220257885769508</id><published>2006-07-06T11:54:00.000-04:00</published><updated>2007-08-21T11:06:05.870-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='SVG'/><category scheme='http://www.blogger.com/atom/ns#' term='XSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Follow up on inline SVG</title><content type='html'>This is just a brief follow-up to my previous &lt;a href="http://blog.pothoven.net/2006/06/help-with-dynamic-inline-svg.html"&gt;request for help&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I had built my XSLT to render the SVG as described in the article, &lt;a href="http://www-128.ibm.com/developerworks/xml/library/x-svggrph/"&gt;Render dynamic graphs in SVG&lt;/a&gt; and my mouseover coloring events was modeled after the article, &lt;a href="http://www-128.ibm.com/developerworks/xml/library/x-svgint/"&gt;Add interactivity to your SVG&lt;/a&gt;, which were both articles from &lt;a href="http://www.ibm.com/developerworks"&gt;developerWorks&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So, my generated code for a bar in my graph looked like:&lt;br /&gt;&lt;pre&gt;&amp;lt;svg:rect x="35" y="272.88" height="57.12" width="109" style="fill:rgb(49,0,0)"&amp;gt;&lt;br /&gt;&amp;lt;set attributeName="fill" from="rgb(49,0,0)" to="red" begin="mouseover" end="mouseout" /&amp;gt;&lt;br /&gt;&amp;lt;/svg:rect&amp;gt;&lt;br /&gt;&lt;/pre&gt;However, after taking the advice from Martin Honnen (&lt;a href="http://groups.yahoo.com/group/svg-developers/"&gt;SVG Developers Yahoo! Group&lt;/a&gt;) my generated code for the same bar in the graph looks like:&lt;br /&gt;&lt;pre&gt;&amp;lt;svg:rect x="35" y="272.88" height="57.12" width="109" fill="rgb(49,0,0)" onmouseover="evt.target.setAttribute('fill', 'red');" onmouseout="evt.target.setAttribute('fill','rgb(49,0,0)');"/&amp;gt;&lt;br /&gt;&lt;/pre&gt;The result is that the correct colorings are now displayed when rendered inline in IE, and the mouseover events work correctly in Firefox.&lt;br /&gt;&lt;br /&gt;The only thing still not working is the mouseover events when rendered inline in IE.&lt;br /&gt;&lt;br /&gt;Martin Honnen further suggested to start with a blank SVG placed in the page with the &lt;code&gt;embed&lt;/code&gt; tag, and then access it to manipulate it with the &lt;code&gt;getSVGDocument&lt;/code&gt; method (which should work in IE, Firefox 1.5+, and Opera 9) to add the content.  However, my attempts at this method have not been successful to date.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115220257885769508?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115220257885769508/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115220257885769508' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115220257885769508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115220257885769508'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/07/follow-up-on-inline-svg.html' title='Follow up on inline SVG'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115220086190862550</id><published>2006-07-06T11:24:00.000-04:00</published><updated>2007-01-02T18:12:38.096-05:00</updated><title type='text'>IBM's Death Spiral</title><content type='html'>Robert X. Cringley has a very interesting article on &lt;a href="http://www.pbs.org/cringely/pulpit/pulpit20060518.html"&gt;Why IBM is in Trouble&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;p class="quote"&gt;"[IBM] has entered a death spiral of under-bidding and then under-delivering."&lt;br/&gt;&lt;br /&gt;"decades no longer matter to publicly traded American companies.  All that really matter are fiscal quarters."&lt;/br&gt;&lt;br /&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;I'm tempted to add my own personal editorial on the subject, but I don't want to put my job in jeopardy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115220086190862550?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.pbs.org/cringely/pulpit/pulpit20060518.html' title='IBM&apos;s Death Spiral'/><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115220086190862550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115220086190862550' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115220086190862550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115220086190862550'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/07/ibms-death-spiral.html' title='IBM&apos;s Death Spiral'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115152495517525083</id><published>2006-06-28T16:02:00.000-04:00</published><updated>2007-08-21T11:04:28.070-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='SVG'/><category scheme='http://www.blogger.com/atom/ns#' term='XSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Help with dynamic inline SVG</title><content type='html'>Normally, I like to post solutions to problems on this blog, but today I'm hoping someone can help me.&lt;br /&gt;&lt;br /&gt;In a previous &lt;a href="http://blog.pothoven.net/2006/05/inline-dynamic-svg-from-xml-with-ajax.html"&gt;post&lt;/a&gt; I presented some methods to embed SVG inline after generating it from XML data.  However, there's still a problem, particularly in Internet Explorer, which I discovered when I tried to add some event handling to the SVG.  Let me walk through the problem.&lt;br /&gt;&lt;br /&gt;First, we start out with some sample XML data like:&lt;br /&gt;&lt;pre&gt;&amp;lt;?xml version="1.0" ?&amp;gt;&lt;br /&gt;&amp;lt;SrmData&amp;gt;&lt;br /&gt;&amp;lt;data&amp;gt;&lt;br /&gt;&amp;lt;display_name&amp;gt;srmdb2&amp;lt;/display_name&amp;gt;&lt;br /&gt;&amp;lt;date&amp;gt;2006-04-30&amp;lt;/date&amp;gt;&lt;br /&gt;&amp;lt;processor_busy&amp;gt;19.04&amp;lt;/processor_busy&amp;gt;&lt;br /&gt;&amp;lt;/data&amp;gt;&lt;br /&gt;&amp;lt;data&amp;gt;&lt;br /&gt;&amp;lt;display_name&amp;gt;srmxml05&amp;lt;/display_name&amp;gt;&lt;br /&gt;&amp;lt;date&amp;gt;2006-04-30&amp;lt;/date&amp;gt;&lt;br /&gt;&amp;lt;processor_busy&amp;gt;37.68&amp;lt;/processor_busy&amp;gt;&lt;br /&gt;&amp;lt;/data&amp;gt;&lt;br /&gt;&amp;lt;data&amp;gt;&lt;br /&gt;&amp;lt;display_name&amp;gt;srmdev1&amp;lt;/display_name&amp;gt;&lt;br /&gt;&amp;lt;date&amp;gt;2006-04-30&amp;lt;/date&amp;gt;&lt;br /&gt;&amp;lt;processor_busy&amp;gt;26.6&amp;lt;/processor_busy&amp;gt;&lt;br /&gt;&amp;lt;/data&amp;gt;&lt;br /&gt;&amp;lt;data&amp;gt;&lt;br /&gt;&amp;lt;display_name&amp;gt;srmxml01&amp;lt;/display_name&amp;gt;&lt;br /&gt;&amp;lt;date&amp;gt;2006-04-30&amp;lt;/date&amp;gt;&lt;br /&gt;&amp;lt;processor_busy&amp;gt;42.74&amp;lt;/processor_busy&amp;gt;&lt;br /&gt;&amp;lt;/data&amp;gt;&lt;br /&gt;&amp;lt;data&amp;gt;&lt;br /&gt;&amp;lt;display_name&amp;gt;srmweb2&amp;lt;/display_name&amp;gt;&lt;br /&gt;&amp;lt;date&amp;gt;2006-04-30&amp;lt;/date&amp;gt;&lt;br /&gt;&amp;lt;processor_busy&amp;gt;1.66&amp;lt;/processor_busy&amp;gt;&lt;br /&gt;&amp;lt;/data&amp;gt;&lt;br /&gt;&amp;lt;data&amp;gt;&lt;br /&gt;&amp;lt;display_name&amp;gt;srmweb1&amp;lt;/display_name&amp;gt;&lt;br /&gt;&amp;lt;date&amp;gt;2006-04-30&amp;lt;/date&amp;gt;&lt;br /&gt;&amp;lt;processor_busy&amp;gt;5.61&amp;lt;/processor_busy&amp;gt;&lt;br /&gt;&amp;lt;/data&amp;gt;&lt;br /&gt;&amp;lt;data&amp;gt;&lt;br /&gt;&amp;lt;display_name&amp;gt;srmxml02&amp;lt;/display_name&amp;gt;&lt;br /&gt;&amp;lt;date&amp;gt;2006-04-30&amp;lt;/date&amp;gt;&lt;br /&gt;&amp;lt;processor_busy&amp;gt;22.74&amp;lt;/processor_busy&amp;gt;&lt;br /&gt;&amp;lt;/data&amp;gt;&lt;br /&gt;&amp;lt;/SrmData&amp;gt;&lt;br /&gt;&lt;/pre&gt;Which I run through my XSLT and get the following SVG (notice the mouseover events):&lt;br /&gt;&lt;pre&gt;&amp;lt;svg:svg baseProfile="full" width="850" height="350" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"&amp;gt;&lt;br /&gt;&amp;lt;svg:g&amp;gt;&lt;br /&gt;&amp;lt;!--Now Draw the main X and Y axis--&amp;gt;&lt;br /&gt;&amp;lt;svg:g style="stroke-width:3; stroke:black"&amp;gt;&lt;br /&gt;&amp;lt;!--X Axis--&amp;gt;&lt;br /&gt;&amp;lt;svg:path d="M 30 330 L 830 330 Z" /&amp;gt;&lt;br /&gt;&amp;lt;!--Y Axis--&amp;gt;&lt;br /&gt;&amp;lt;svg:path d="M 30 30 L 30 330 Z" /&amp;gt;&lt;br /&gt;&amp;lt;/svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:g style="fill:none; stroke:#B0B0B0; stroke-width:2; stroke-dasharray:2 4;text-anchor:end; font-size:12px; stroke-width:5; stroke:black "&amp;gt;&lt;br /&gt;&amp;lt;!--Add dotted line at 50--&amp;gt;&lt;br /&gt;&amp;lt;svg:path d="M 30 30 L 830 30 Z" /&amp;gt;&lt;br /&gt;&amp;lt;svg:path d="M 30 165 L 830165 Z" /&amp;gt;&lt;br /&gt;&amp;lt;!--Add Y-axis labels--&amp;gt;&lt;br /&gt;&amp;lt;svg:text style="fill:black; stroke:none" x="20" y="30"&amp;gt;&lt;br /&gt;100 &amp;lt;/svg:text&amp;gt;&lt;br /&gt;&amp;lt;svg:text style="fill:black; stroke:none" x="20" y="165"&amp;gt;&lt;br /&gt;50 &amp;lt;/svg:text&amp;gt;&lt;br /&gt;&amp;lt;svg:text style="fill:black; stroke:none" x="20" y="330"&amp;gt;&lt;br /&gt;0 &amp;lt;/svg:text&amp;gt;&lt;br /&gt;&amp;lt;/svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:rect x="35" y="272.88" height="57.12" width="109" style="fill:rgb(49,0,0)"&amp;gt;&lt;br /&gt;&amp;lt;set attributeName="fill" from="rgb(49,0,0)" to="red" begin="mouseover" end="mouseout" /&amp;gt;&lt;br /&gt;&amp;lt;/svg:rect&amp;gt;&lt;br /&gt;&amp;lt;svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:text style="fill:black; stroke:none" x="35" y="347" rotate="45" font-size="8"&amp;gt;&lt;br /&gt;srmdb2&amp;lt;/svg:text&amp;gt;&lt;br /&gt;&amp;lt;/svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:rect x="149" y="216.96" height="113.03999999999999" width="109" style="fill:rgb(96,0,0)"&amp;gt;&lt;br /&gt;&amp;lt;set attributeName="fill" from="rgb(96,0,0)" to="red" begin="mouseover" end="mouseout" /&amp;gt;&lt;br /&gt;&amp;lt;/svg:rect&amp;gt;&lt;br /&gt;&amp;lt;svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:text style="fill:black; stroke:none" x="149" y="347" rotate="45" font-size="8"&amp;gt;&lt;br /&gt;srmxml05&amp;lt;/svg:text&amp;gt;&lt;br /&gt;&amp;lt;/svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:rect x="263" y="250.2" height="79.80000000000001" width="109" style="fill:rgb(68,0,0)"&amp;gt;&lt;br /&gt;&amp;lt;set attributeName="fill" from="rgb(68,0,0)" to="red" begin="mouseover" end="mouseout" /&amp;gt;&lt;br /&gt;&amp;lt;/svg:rect&amp;gt;&lt;br /&gt;&amp;lt;svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:text style="fill:black; stroke:none" x="263" y="347" rotate="45" font-size="8"&amp;gt;&lt;br /&gt;srmdev1&amp;lt;/svg:text&amp;gt;&lt;br /&gt;&amp;lt;/svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:rect x="377" y="201.78" height="128.22" width="109" style="fill:rgb(109,0,0)"&amp;gt;&lt;br /&gt;&amp;lt;set attributeName="fill" from="rgb(109,0,0)" to="red" begin="mouseover" end="mouseout" /&amp;gt;&lt;br /&gt;&amp;lt;/svg:rect&amp;gt;&lt;br /&gt;&amp;lt;svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:text style="fill:black; stroke:none" x="377" y="347" rotate="45" font-size="8"&amp;gt;&lt;br /&gt;srmxml01&amp;lt;/svg:text&amp;gt;&lt;br /&gt;&amp;lt;/svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:rect x="491" y="325.02" height="4.9799999999999995" width="109" style="fill:rgb(4,0,0)"&amp;gt;&lt;br /&gt;&amp;lt;set attributeName="fill" from="rgb(4,0,0)" to="red" begin="mouseover" end="mouseout" /&amp;gt;&lt;br /&gt;&amp;lt;/svg:rect&amp;gt;&lt;br /&gt;&amp;lt;svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:text style="fill:black; stroke:none" x="491" y="347" rotate="45" font-size="8"&amp;gt;&lt;br /&gt;srmweb2&amp;lt;/svg:text&amp;gt;&lt;br /&gt;&amp;lt;/svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:rect x="605" y="313.17" height="16.830000000000002" width="109" style="fill:rgb(14,0,0)"&amp;gt;&lt;br /&gt;&amp;lt;set attributeName="fill" from="rgb(14,0,0)" to="red" begin="mouseover" end="mouseout" /&amp;gt;&lt;br /&gt;&amp;lt;/svg:rect&amp;gt;&lt;br /&gt;&amp;lt;svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:text style="fill:black; stroke:none" x="605" y="347" rotate="45" font-size="8"&amp;gt;&lt;br /&gt;srmweb1&amp;lt;/svg:text&amp;gt;&lt;br /&gt;&amp;lt;/svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:rect x="719" y="261.78" height="68.22" width="109" style="fill:rgb(58,0,0)"&amp;gt;&lt;br /&gt;&amp;lt;set attributeName="fill" from="rgb(58,0,0)" to="red" begin="mouseover" end="mouseout" /&amp;gt;&lt;br /&gt;&amp;lt;/svg:rect&amp;gt;&lt;br /&gt;&amp;lt;svg:g&amp;gt;&lt;br /&gt;&amp;lt;svg:text style="fill:black; stroke:none" x="719" y="347" rotate="45" font-size="8"&amp;gt;&lt;br /&gt;srmxml02&amp;lt;/svg:text&amp;gt;&lt;br /&gt;&amp;lt;/svg:g&amp;gt;&lt;br /&gt;&amp;lt;/svg:g&amp;gt;&lt;br /&gt;&amp;lt;/svg:svg&amp;gt;&lt;br /&gt;&lt;/pre&gt;If I embed the above SVG from a static file (via &lt;code&gt;&amp;lt;embed src="myfile.svg"&amp;gt;&lt;/code&gt;), it is rendered correctly in IE as:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6623/2144/1600/Snap1.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger/6623/2144/320/Snap1.png" alt="" border="0" /&gt;&lt;/a&gt;where in the above snapshot, the mouse is over the first bar so the mouseover event has changed it to bright red to highlight it.&lt;br /&gt;&lt;br /&gt;The problem is that when it is rendered dynmically inline in Internet Explorer, as described in my previous &lt;a href="http://blog.pothoven.net/2006/05/inline-dynamic-svg-from-xml-with-ajax.html"&gt;post&lt;/a&gt;, it looks like:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6623/2144/1600/Snap2.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger/6623/2144/320/Snap2.png" alt="" border="0" /&gt;&lt;/a&gt;where there is no coloring and no mouseover events.  Both cases require the Adobe SVG Viewer to be installed to render it.  So why is it rendered differently?&lt;br /&gt;&lt;br /&gt;There is one interesting difference in how IE handles this.  When using the &lt;code&gt;embed&lt;/code&gt; tag, if you right click on the image, the pop-up context menu will look like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6623/2144/1600/Snap3.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger/6623/2144/400/Snap3.png" alt="" border="0" /&gt;&lt;/a&gt;where it is obviously using the Adobe SVG Viewer as evidenced by the "About Adobe SVG Viewer..." menu option and other options such as copying, viewing, and saving the SVG.   On the other hand, when using the inlined SVG method, the context menu will look like:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6623/2144/1600/Snap4.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger/6623/2144/400/Snap4.png" alt="" border="0" /&gt;&lt;/a&gt;as it would on any other part of the web page.  So even though the Adobe plugin rendered the image, it is not apparent.&lt;br /&gt;&lt;br /&gt;Meanwhile, on the Firefox side of things, regardless of whether you &lt;code&gt;embed&lt;/code&gt; a static SVG file, put the SVG directly inline (if you're using XHTML), or put it in an &lt;code&gt;object&lt;/code&gt; tag, it always looks the same:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6623/2144/1600/Snap5.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger/6623/2144/320/Snap5.png" alt="" border="0" /&gt;&lt;/a&gt;which is good except for the fact that the Firefox (Gecko) SVG rendering engine doesn't seem to support the mouseover events either.&lt;br /&gt;&lt;br /&gt;As an alternative method, I tried &lt;a href="http://www.liquidx.net/plotkit/"&gt;PlotKit&lt;/a&gt; which can generate dynamic SVG graphs and charts.  So, I built an HTML table from my XML data and told PlotKit to use that and in both IE and Firefox it worked correctly to give me a graph such as:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6623/2144/1600/Snap6.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger/6623/2144/320/Snap6.png" alt="" border="0" /&gt;&lt;/a&gt;The context menu of this graph is like my inline approach and doesn't acknowledge the Adobe SVG Viewer, however, PlotKit was able to get coloring to work.  The current version of PlotKit does not support events which made it both not acceptable for my overall solution as well as provided me no way to test if the mouseover would have worked with the approach used by PlotKit.  However, since at least colors worked, I started investigating how he (Alastair Tse) was able to render the dynamic inline SVG in IE.&lt;br /&gt;&lt;br /&gt;I found this note in one of his samples:&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Note: With SVG, the most compatible way is NOT to put in SVG tags, but create the SVG element with Javascript. Therefore, instead of adding &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; tag, we just have a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; container and create the SVG element using SVGRenderer.SVG().&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;And looking at the PlotKit code I can see that his library is dynamically building the SVG using JavaScript DOM APIs to add each element.&lt;br /&gt;&lt;br /&gt;Well, since I want to use XSLT to generate my SVG from XML and not write my own version of the PlotKit library to build my SVGs, I tried a hybrid approach.  I used the DOM APIs to create a temporary DOM element.  I assigned my generated SVG to that element's innerHTML in order to have the browser build a DOM tree out of my SVG.  I then created an &lt;code&gt;svg&lt;/code&gt; DOM element, and appended the first child DOM element of my SVG DOM (the first &lt;code&gt;g&lt;/code&gt; element) to the &lt;code&gt;svg&lt;/code&gt; DOM element and appened the &lt;code&gt;svg&lt;/code&gt; DOM element to the document.  Which I would think would be theoretically the same thing, but to no avail.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Does anyone out there know how to fix this?  How can I make Internet Explorer render my dynamic inline SVG correctly in order to get proper coloring and event handling?&lt;br /&gt;&lt;br /&gt;Update: See &lt;a href="http://blog.pothoven.net/2006/07/follow-up-on-inline-svg.html"&gt;follow-up&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115152495517525083?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115152495517525083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115152495517525083' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115152495517525083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115152495517525083'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/06/help-with-dynamic-inline-svg.html' title='Help with dynamic inline SVG'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-115133796271940735</id><published>2006-06-26T12:05:00.000-04:00</published><updated>2006-06-26T12:06:03.953-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>OpenAjax Alliance</title><content type='html'>I'm now a memeber of the OpenAjax Alliance (http://www.openajaxalliance.org/)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-115133796271940735?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/115133796271940735/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=115133796271940735' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115133796271940735'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/115133796271940735'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/06/openajax-alliance.html' title='OpenAjax Alliance'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-114938978330964177</id><published>2006-06-03T22:56:00.000-04:00</published><updated>2006-06-03T22:56:23.650-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Compressing HTML, CSS, and JavaScript files</title><content type='html'>Adding gzip compression to files received from your web server can enhance the network latency of your web application.  As simple way to do this is to define a handler in your &lt;code&gt;.htaccess&lt;/code&gt; file to compress your HTML, CSS, and JavaScript files.  To do this, add these lines to your &lt;code&gt;.htaccess&lt;/code&gt; file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;AddHandler application/x-httpd-php .css .html .js&lt;br /&gt;php_value auto_prepend_file /path/to/gzip-page.php&lt;br /&gt;php_flag zlib.output_compression On&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;where the path is specific to your installation.&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;gzip-page.php&lt;/code&gt; is needed to define the content-type for the compressed data so that they are not assumed to be PHP files.  The code should look something like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?php&lt;br /&gt;$pathinfo = pathinfo($PHP_SELF);&lt;br /&gt;$extension = $pathinfo['extension'];&lt;br /&gt;switch ($extension) {&lt;br /&gt; case "css" :&lt;br /&gt;  header("Content-type: text/css");&lt;br /&gt;  break;&lt;br /&gt; case "html" :&lt;br /&gt;  header("Content-type: text/html");&lt;br /&gt;  break;&lt;br /&gt; case "js" :&lt;br /&gt;  header("Content-type: text/javascript");&lt;br /&gt;  break;&lt;br /&gt;&lt;br /&gt; default :&lt;br /&gt;  break;&lt;br /&gt;}&lt;br /&gt;?&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now all your HTML, CSS, and JavaScript content will be compressed if the browser supports compressed data (which most do).  For Ajax applications this is most useful since you'll probably have a decent amount of JavaScript code to transmit and potentially a fair amount of CSS as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-114938978330964177?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/114938978330964177/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=114938978330964177' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114938978330964177'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114938978330964177'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/06/compressing-html-css-and-javascript.html' title='Compressing HTML, CSS, and JavaScript files'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-114918624103252405</id><published>2006-06-01T13:50:00.000-04:00</published><updated>2006-06-01T14:24:02.896-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='SVG'/><category scheme='http://www.blogger.com/atom/ns#' term='XSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>SVG in IE and Firefox</title><content type='html'>As follow-on work to my previous post &lt;a href="http://pothoven.blogspot.com/2006/05/inline-dynamic-svg-from-xml-with-ajax.html"&gt;&lt;span style="font-style: italic;"&gt;Inline dynamic SVG from XML with Ajax&lt;/span&gt;&lt;/a&gt; where I discussed how to incorporate dynamically generated SVG in your web page, I've been trying to figure out how to make IE correctly prompt you to install the SVG plugin.  That is usually accomplished with the &lt;code&gt;PLUGINSPAGE&lt;/code&gt; attribute of the &lt;code&gt;embed&lt;/code&gt; tag (see http://developer.apple.com/quicktime/compatibility.html for a nice explanation of the technique), but I'm using the &lt;code&gt;object&lt;/code&gt; tag (which is recommended since it complies with the W3C HTML specifications while embed does not and it allows the data to be added inline).  Unfortunately, adding the &lt;code&gt;codebase&lt;/code&gt; attribute of &lt;code&gt;http://www.adobe.com/svg/viewer/install/&lt;/code&gt; (the Adobe SVG Viewer install site) and the &lt;code&gt;classid&lt;/code&gt; attribute of &lt;code&gt;clsid:78156a80-c6a1-4bbf-8e6a-3cd390eeb4e2&lt;/code&gt; (the Adobe SVG Viewer ActiveX control id) didn't do what I had hoped for.&lt;br /&gt;&lt;br /&gt;I haven't figured out how to make IE prompt for the plugin yet, but I did find &lt;a href="http://www.spartanicus.utvinternet.ie/embed.htm#svg"&gt;this page&lt;/a&gt;  which provides an alternative method to check for IE and alternate using the &lt;code&gt;object&lt;/code&gt; or the &lt;code&gt;embed&lt;/code&gt; (in his case or inline SVG in my example) rather than using the &lt;code&gt;isASVInstalled()&lt;/code&gt; method I used.&lt;br /&gt;&lt;br /&gt;In his example, Spartanicus uses the &lt;a href="http://msdn.microsoft.com/workshop/author/dhtml/overview/ccomment_ovw.asp"&gt;MS "uplevel revealed" conditional comment&lt;/a&gt; to define a CSS type as:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;.svg{display:none}&lt;br /&gt;*&gt;.svg{display:inline}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then his HTML looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;!--[if IE]&gt;&amp;lt;embed src="img/butterfly_vector.svg" height="120" width="170"&gt;&amp;lt;![endif]--&gt;&lt;br /&gt;&amp;lt;object data="img/butterfly_vector.svg" type="image/svg+xml" class="svg" height="120" width="170"/&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Where the class of &lt;code&gt;svg&lt;/code&gt; in the &lt;code&gt;object&lt;/code&gt; version will causes it not to display if not in Internet Explorer.&lt;br /&gt;&lt;br /&gt;It's an interesting technique, but in my case of inline generated SVG where I can't use the &lt;code&gt;embed&lt;/code&gt; tag since I have no &lt;code&gt;src&lt;/code&gt; so I'd have to insert the SVG inline for IE and then also have the wrapped object version.  Unfortunately, the non-object version would still display the data in the SVG XML even though it would not be an SVG graphic which would look terrible.  So, it doesn't really help me in this case, but I thought it was interesting.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-114918624103252405?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/114918624103252405/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=114918624103252405' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114918624103252405'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114918624103252405'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/06/svg-in-ie-and-firefox.html' title='SVG in IE and Firefox'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-114875881680191008</id><published>2006-05-27T15:39:00.000-04:00</published><updated>2007-08-21T10:41:03.857-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='SVG'/><category scheme='http://www.blogger.com/atom/ns#' term='XSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Inline dynamic SVG from XML with Ajax</title><content type='html'>I had this problem I was trying to solve for work, and surprisingly, I couldn't find the solution anywhere else online, so I solved it myself and now offer the fruits of my labor.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Problem&lt;/span&gt;: retrieve report data in  XML format from a SOA application and generate an HTML textual representation of the report data (ex. a table) &lt;span style="font-style: italic;"&gt;as well as a graphical representation (ex. bar graph, pie chart, etc) on the client side&lt;/span&gt; using Ajax.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Solution:&lt;/span&gt; ok, there's plenty of  information online regarding generating the XMLHTTPRequest to fetch the XML data and render HTML, so I won't go into that here other than to say I utilized the &lt;a href="http://prototypejs.org/"&gt;prototype.js&lt;/a&gt; package to make my life a little simpler.  Also, to make my XML manipulation easier cross-browser, I utilized the &lt;a href="http://www.nczonline.net/downloads/"&gt;zXML&lt;/a&gt; package from Nickolas C. Zakas (co-author of &lt;span style="font-style: italic;"&gt;Professional Ajax&lt;/span&gt; which is where I read about it).  The one caveat regarding using the zXML package, is that I had to use it to parse the XML from the XMLHTTPRequest.responseText instead of using the pre-parsed XMLHTTPRequest.responseXML like so:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;currentReportDOM = zXmlDom.createDocument();&lt;br /&gt;currentReportDOM.async = false;&lt;br /&gt;currentReportDOM.loadXML(request.responseText);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok, so I have the report XML data in a DOM object in the variable named currentReportDOM.  For this example I chose to format the XML into SVG using XSLT.  You could of course do direct manipulation of the DOM object in Javascript if you'd prefer.  Nothing spectacular in the XSLT file so I won't show it's contents here, but I apply the XSLT as shown:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// first convert the XML to SVG using XSLT&lt;br /&gt;var xslDom = zXmlDom.createDocument();&lt;br /&gt;xslDom.async = false;&lt;br /&gt;xslDom.load("chart.xsl");&lt;br /&gt;var str = zXslt.transformToText(currentReportDOM.documentElement, xslDom);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, the str variable contains the SVG XML as a string, and here's where the difficulties start.  If this was XHTML in Firefox, I could just paste in the SVG data in some div like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$("reportSVGDiv").innerHTML = str;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;However, since this needs to work cross-browser, and Internet Explorer doesn't handle &lt;code&gt;.xhtml&lt;/code&gt; files, this is a &lt;code&gt;.html&lt;/code&gt; file and when you do that in HTML Firefox no longer renders it as an SVG, but just as plain XML, and Internet Explorer doesn't know what to do with it either since it doesn't support SVG natively.&lt;br /&gt;&lt;br /&gt;Most instructions for embedding inline SVG in an HTML page for Internet Explorer instruct you to use either an &lt;code&gt;embed&lt;/code&gt;, an &lt;code&gt;object&lt;/code&gt;, or an &lt;code&gt;iframe&lt;/code&gt; and set the source as the SVG file.  This works great as along as your SVG data is actually in a file and the file ends with either a &lt;code&gt;.svg&lt;/code&gt; or a &lt;code&gt;.svgz&lt;/code&gt;(compressed) extension.  If it was in a file, I could add an object to my page dynamically with the SVG file like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var svgObject = document.createElement('object');&lt;br /&gt;svgObject.setAttribute('type', 'image/svg+xml');&lt;br /&gt;svgObject.setAttribute('data', 'svgdata.svg');&lt;br /&gt;$("reportSVGDiv").appendChild(svgObject);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And it would be rendered.  However, in this case, the data was received as raw XML from a server and rendered to SVG by the browser.  Theoretically, you could add the data to the object inline like this:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;var svgObject = document.createElement('object');&lt;br /&gt;svgObject.setAttribute('type', 'image/svg+xml');&lt;br /&gt;svgObject.setAttribute('data', 'data:image/svg+xml,'+ str);&lt;br /&gt;$("reportSVGDiv").appendChild(svgObject);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;However, since you're no longer referencing a file with a &lt;code&gt;.svg&lt;/code&gt; or &lt;code&gt;.svgz&lt;/code&gt;extension, the IE MIME type handling -- which depends on file extensions, not the specified MIME type (&lt;code&gt;image/svg+xml&lt;/code&gt;) -- won't recognize it as an SVG.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;How to get it working in Internet Explorer:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To get Internet Explorer working you first have to install the &lt;a href="http://www.adobe.com/svg/viewer/install/"&gt;Adobe SVG Viewer plugin&lt;/a&gt; so that IE can render SVGs.  Then you have to define the &lt;code&gt;svg&lt;/code&gt;namespace in your HTML header like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;br /&gt;&amp;lt;!DOCTYPE html&lt;br /&gt;    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"&lt;br /&gt;    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;&lt;br /&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml"&lt;br /&gt;xmlns:svg="http://www.w3.org/2000/svg"&lt;br /&gt;xmlns:xlink="http://www.w3.org/1999/xlink"&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Then in the &lt;code&gt;head&lt;/code&gt; section add the following lines to associate the Adobe SVG viewer with the &lt;code&gt;svg&lt;/code&gt; namespace (since Internet Explorer bases MIME types off of file extensions and we have no file in this case).&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;object id="AdobeSVG" classid="clsid:78156a80-c6a1-4bbf-8e6a-3cd390eeb4e2"&gt;&amp;lt;/object&gt;&lt;br /&gt;&amp;lt;?import namespace="svg" implementation="#AdobeSVG"?&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, as long as the SVG data you generate is tagged with the svg namespace (ex. &amp;lt;svg:svg&gt;...&amp;lt;/svg:svg&gt;) then placing the SVG XML data in the innerHTML of some div will actually work!  Additionally, you don't want the SVG embedded in an object tag in IE since Adobe SVG Viewer always disables scripting when it determines that the SVG file is embedded using the OBJECT tag.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;How to get it working in Firefox:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Ironically, even though Firefox supports SVG natively (in XHTML) you can't just stick it in the HTML without embedding it in an &lt;code&gt;object&lt;/code&gt;(or &lt;code&gt;embed&lt;/code&gt;, or &lt;code&gt;iframe&lt;/code&gt;).  So, we have to build the &lt;code&gt;object &lt;/code&gt;and add the SVG data inline as I demonstrated earlier, but I'll give the complete example now:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// remove any old charts&lt;br /&gt;while ($("reportSVGDiv").hasChildNodes()) {&lt;br /&gt; $("reportSVGDiv").removeChild($("reportSVGDiv").firstChild);&lt;br /&gt;}&lt;br /&gt;var svgObject = document.createElement('object');&lt;br /&gt;svgObject.setAttribute('name', 'svg');&lt;br /&gt;svgObject.setAttribute('codebase', 'http://www.adobe.com/svg/viewer/install/');&lt;br /&gt;svgObject.setAttribute('classid', 'clsid:78156a80-c6a1-4bbf-8e6a-3cd390eeb4e2');&lt;br /&gt;svgObject.setAttribute('data', 'data:image/svg+xml,'+ str);&lt;br /&gt;svgObject.setAttribute('width', '1050');&lt;br /&gt;svgObject.setAttribute('height', '550');&lt;br /&gt;svgObject.setAttribute('type', 'image/svg+xml');&lt;br /&gt;$("reportSVGDiv").appendChild(svgObject);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;How to get it working cross-browser:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The last piece in the puzzle is how to determine when we're in IE with the Adobe SVG viewer installed, and when we're not.  To do that, I use a method to check for the Adobe SVG viewer:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// isASVInstalled&lt;br /&gt;//&lt;br /&gt;// utililty function to check for Adobe SVG viewer&lt;br /&gt;//&lt;br /&gt;function isASVInstalled() {&lt;br /&gt;try {&lt;br /&gt; var asv = new ActiveXObject("Adobe.SVGCtl");&lt;br /&gt; return true;&lt;br /&gt;}&lt;br /&gt;catch(e){ }&lt;br /&gt;return false;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Putting it all together:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;With all the pieces in place, my final Javascript function to render the SVG inline with dynamic XML data received via Ajax from an SOA looks like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// toSVG&lt;br /&gt;//&lt;br /&gt;// convert the report XML data to a SVG (scalable vector graphic) image&lt;br /&gt;//&lt;br /&gt;function toSVG() {&lt;br /&gt;// first convert the XML to SVG using XSLT&lt;br /&gt;var xslDom = zXmlDom.createDocument();&lt;br /&gt;xslDom.async = false;&lt;br /&gt;xslDom.load("chart.xsl");&lt;br /&gt;var str = zXslt.transformToText(currentReportDOM.documentElement, xslDom);&lt;br /&gt;&lt;br /&gt;// check if we're using the AdobeSVG viewer (Internet Explorer)&lt;br /&gt;if (isASVInstalled() ) {&lt;br /&gt; $("reportSVGDiv").innerHTML = straw;&lt;br /&gt;} else {&lt;br /&gt;// otherwise, we're assuming firebug/Mozilla which can render the SVG directly&lt;br /&gt;// if this was true XHTML instead of HTML, then firebug would also render it directly&lt;br /&gt;// using the inheriting above, however, since it has to be HTML to make IA happy,&lt;br /&gt;// we then have to wrap the SVG in an &lt;object&gt; tag&lt;br /&gt;// You can wrap the SVG in an &lt;object&gt; tag for IA as well if you include the SVG from&lt;br /&gt;// a file with a .svg extension, but since we're generating it dynamically, we don't&lt;br /&gt;// have a file.  The HTML spec supports loading the data inline, however, since IE's&lt;br /&gt;// MIME types depend on file extensions, this doesn't work in IA.&lt;br /&gt;&lt;br /&gt;// remove any old charts&lt;br /&gt;while ($("reportSVGDiv").hasChildNodes()) {&lt;br /&gt; $("reportSVGDiv").removeChild($("reportSVGDiv").firstChild);&lt;br /&gt;}&lt;br /&gt;var svgObject = document.createElement('object');&lt;br /&gt;svgObject.setAttribute('name', 'svg');&lt;br /&gt;svgObject.setAttribute('codebase', 'http://www.adobe.com/svg/viewer/install/');&lt;br /&gt;svgObject.setAttribute('classid', 'clsid:78156a80-c6a1-4bbf-8e6a-3cd390eeb4e2');&lt;br /&gt;svgObject.setAttribute('data', 'data:image/svg+xml,'+ straw);&lt;br /&gt;svgObject.setAttribute('width', '1050');&lt;br /&gt;svgObject.setAttribute('height', '550');&lt;br /&gt;svgObject.setAttribute('type', 'image/svg+xml');&lt;br /&gt;$("reportSVGDiv").appendChild(svgObject);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/object&gt;&lt;/object&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I hope this helps someone and saves them from the aggravation I encounted trying to get this to work!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-114875881680191008?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/114875881680191008/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=114875881680191008' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114875881680191008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114875881680191008'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/05/inline-dynamic-svg-from-xml-with-ajax.html' title='Inline dynamic SVG from XML with Ajax'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-114875368633831279</id><published>2006-05-27T14:08:00.000-04:00</published><updated>2006-05-27T14:14:52.216-04:00</updated><title type='text'>Blog renamed</title><content type='html'>I decided to rename my blog.  Until now, it had been entitled &lt;span style="font-style: italic;"&gt;Pothoven's Pansophy&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;When I originally named it, I picked &lt;span style="font-style: italic;"&gt;pansophy&lt;/span&gt; because I wanted a word for knowledge or wisdom that started with 'P' for some alliteration with my name. So, looking up knowledge or wisdom in a thesaurus I found:&lt;br /&gt;&lt;br /&gt;pansophy&lt;br /&gt;\Pan"so*phy\, n. [Pan- + Gr. ? wisdom, ? wise: cf. F. pansophie.] Universal wisdom; esp., a system of universal knowledge proposed by Comenius (1592 -- 1671), a Moravian educator.&lt;br /&gt;&lt;br /&gt;However, every time I look at it, it looks too much like &lt;span style="font-style: italic;"&gt;pansy&lt;/span&gt;.  So, I'm renaming the blog to &lt;span style="font-style: italic;"&gt;Pothoven Post&lt;/span&gt; to sound more like a newspaper, but still with the alliteration.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-114875368633831279?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/114875368633831279/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=114875368633831279' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114875368633831279'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114875368633831279'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/05/blog-renamed.html' title='Blog renamed'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-114554361194705142</id><published>2006-04-20T10:06:00.000-04:00</published><updated>2006-05-27T15:50:01.063-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='XSL'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Ajax: HTML vs XML responses</title><content type='html'>&lt;span style="font-family:arial;"&gt;&lt;/span&gt;A friend of mine has a discussion on his blog regarding returning straight HTML or XML in your AJAX responses (see &lt;a href="http://www.brianburridge.com/2006/01/30/what-type-of-data-should-an-ajax-call-return/"&gt;What type of data should an Ajax call return?&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;I voiced a comment regarding my appreciation for the direction used by &lt;a href="http://openrico.org"&gt;Rico&lt;/a&gt; where XML is always returned, though it may contain an HTML snippet such as:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;ajax-response&gt;&lt;br /&gt;&amp;lt;response type="element" id="personInfo"&gt;&lt;br /&gt;[valid XHTML here]&lt;br /&gt;&amp;lt;/response&gt;&lt;br /&gt;&amp;lt;/ajax-response&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;where the type="element" id="personInfo" indicates to Rico to replace the innerHTML of the element with id personInfo with the contents of the response.&lt;br /&gt;&lt;br /&gt;Or alternatively:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;ajax-response&gt;&lt;br /&gt;&amp;lt;response type="object" id="formLetterUpdater"&gt;&lt;br /&gt;[valid XML data here]&lt;br /&gt;&amp;lt;/response&gt;&lt;br /&gt;&amp;lt;/ajax-response&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The type="object" id="formLetterUpdater" calls a JavaScript object registered under the name matching the id of formLetterUpdater. That Javascript would then process the contents of the response as XML to extract whatever data is necessary.&lt;br /&gt;&lt;br /&gt;For either mechanism, multiple "response" clauses can be returned in a single "ajax-response" allowing multiple sections of a web page to be updated from a single AJAX action if necessary.&lt;br /&gt;&lt;br /&gt;My apprehension about this method is that the server side processing has too much knowledge about how the data will be used (by dictating the ids).  Of course, that might be able to be handled by having the AJAX response send which ids it wants data for.&lt;br /&gt;&lt;br /&gt;Of these two mechanisms from Rico, I think the XML data mechanism has the most flexibility, as it describes the data in XML and the UI can extract what it needs and format it correctly whether by coding manual JavaScript DOM processing as indicated with the formLetterUpdater above, or by utilizing XSLT on the browser to format it as desired.  Using XSLT provides multiple advantages for formatting and re-formatting the data for the user without the need to communicate with the server over and over to format the HTML.  For example, switching themes, languages, or switching from a table to a graph.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-114554361194705142?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/114554361194705142/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=114554361194705142' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114554361194705142'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114554361194705142'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/04/ajax-html-vs-xml-responses.html' title='Ajax: HTML vs XML responses'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-114538220755482532</id><published>2006-04-18T13:43:00.000-04:00</published><updated>2006-04-19T10:40:14.636-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='XSL'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Wrapping long strings with XSL 1.0</title><content type='html'>For my recent work with XML and XSL, I needed to wrap strings when generating a text report.  I found examples online to do it, but they depended on XSL 2.0 functions such as the string tokenizer.  I needed a way to do it with plain old XSL 1.0, so the solution I came up with is below.  This solution is a bit more complex than necessary since I needed the ability to add markup around the wrapped lines (the prefix and suffix).  Additionally, I needed the ability to add different markup before and after wrapped lines as well (the hangingPrefix and hangingSuffix).  I could have taken it out to simplify this example, however, I thought some people might find it useful.  Additionally, it's designed for all the prefixes and suffixes to be optional anyway.  Here's my solution (the second template called splitString is the entry point):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; &amp;lt;!-- recursive section of splitString... don't call directly --&gt;&lt;br /&gt; &amp;lt;xsl:template name="firstString"&gt;&lt;br /&gt;  &amp;lt;xsl:param name="string" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="length" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="currentpos" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="prefix" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="hangingPrefix" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="suffix" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="hangingSuffix" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="padding" /&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;xsl:choose&gt;&lt;br /&gt;   &amp;lt;xsl:when test="substring($string, $currentpos, 1) = ' '"&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="substring($string, 1, $currentpos - 1)" /&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="$suffix" /&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;xsl:call-template name="splitString"&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="string" select="substring($string, $currentpos + 1)" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="length" select="$length" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="prefix" select="$hangingPrefix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="hangingPrefix" select="$hangingPrefix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="suffix" select="$hangingSuffix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="hangingSuffix" select="$hangingSuffix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="padding" select="$padding" /&gt;&lt;br /&gt;    &amp;lt;/xsl:call-template&gt;&lt;br /&gt;   &amp;lt;/xsl:when&gt;&lt;br /&gt;   &amp;lt;xsl:when test="substring($string, $currentpos, 1) = '/'"&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="substring($string, 1, $currentpos)" /&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="$suffix" /&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;xsl:call-template name="splitString"&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="string" select="substring($string, $currentpos + 1)" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="length" select="$length" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="prefix" select="$hangingPrefix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="hangingPrefix" select="$hangingPrefix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="suffix" select="$hangingSuffix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="hangingSuffix" select="$hangingSuffix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="padding" select="$padding" /&gt;&lt;br /&gt;    &amp;lt;/xsl:call-template&gt;&lt;br /&gt;   &amp;lt;/xsl:when&gt;&lt;br /&gt;   &amp;lt;xsl:otherwise&gt;&lt;br /&gt;    &amp;lt;xsl:call-template name="firstString"&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="string" select="$string" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="length" select="$length" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="currentpos" select="$currentpos - 1" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="prefix" select="$prefix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="hangingPrefix" select="$hangingPrefix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="suffix" select="$suffix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="hangingSuffix" select="$hangingSuffix" /&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="padding" select="$padding" /&gt;&lt;br /&gt;    &amp;lt;/xsl:call-template&gt;&lt;br /&gt;   &amp;lt;/xsl:otherwise&gt;&lt;br /&gt;  &amp;lt;/xsl:choose&gt;&lt;br /&gt; &amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;!-- split a string to a specified size.&lt;br /&gt;  Note: there is no newline at the end of the last line to allow additions to the end of the line&lt;br /&gt;  &lt;br /&gt;  string: string to split&lt;br /&gt;  length: max length string can be per line&lt;br /&gt;  prefix: optional string to prepend to the first line&lt;br /&gt;  hangingPrefix:  optional string to prepend subsequent lines with&lt;br /&gt;  suffix: optional string to append to the end of the first line&lt;br /&gt;  hangingSuffix:  optional string to append to subsequent lines&lt;br /&gt;  padding: optional string to pad the last line of a splt string with to allow extra text to be added after it&lt;br /&gt; --&gt;&lt;br /&gt; &amp;lt;xsl:template name="splitString"&gt;&lt;br /&gt;  &amp;lt;xsl:param name="string" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="length" select="$maxLineLength" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="prefix" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="hangingPrefix" select="$prefix" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="suffix" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="hangingSuffix" select="$suffix" /&gt;&lt;br /&gt;  &amp;lt;xsl:param name="padding" /&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;xsl:choose&gt;&lt;br /&gt;   &amp;lt;xsl:when test="(string-length($prefix) +&lt;br /&gt;           string-length($string) + &lt;br /&gt;           string-length($suffix)) &amp;gt; $length"&gt;&lt;br /&gt;    &amp;lt;xsl:variable name="currentpos" select="$length - string-length($suffix)" /&gt;&lt;br /&gt;    &amp;lt;xsl:choose&gt;&lt;br /&gt;     &amp;lt;xsl:when test="contains(substring($string, 1, $currentpos), ' ') or&lt;br /&gt;         contains(substring($string, 1, $currentpos), '/')"&gt;&lt;br /&gt;      &amp;lt;xsl:call-template name="firstString"&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="string" select="concat($prefix, $string)" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="length" select="$length" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="currentpos" select="$currentpos" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="prefix" select="$prefix" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="hangingPrefix" select="$hangingPrefix" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="suffix" select="$suffix" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="hangingSuffix" select="$hangingSuffix" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="padding" select="$padding" /&gt;&lt;br /&gt;      &amp;lt;/xsl:call-template&gt;&lt;br /&gt;     &amp;lt;/xsl:when&gt;&lt;br /&gt;     &amp;lt;xsl:otherwise&gt;&lt;br /&gt;      &amp;lt;!-- no line split character within maximum line length --&gt;&lt;br /&gt;      &amp;lt;xsl:value-of select="$prefix" /&gt;&lt;br /&gt;      &amp;lt;xsl:value-of select="substring($string, 1, $currentpos - string-length($prefix))" /&gt;&lt;br /&gt;      &amp;lt;xsl:value-of select="$suffix" /&gt;&lt;br /&gt;      &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;xsl:call-template name="splitString"&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="string" select="substring($string, $currentpos + 1 - string-length($prefix))" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="length" select="$length" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="prefix" select="$hangingPrefix" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="hangingPrefix" select="$hangingPrefix" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="suffix" select="$hangingSuffix" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="hangingSuffix" select="$hangingSuffix" /&gt;&lt;br /&gt;       &amp;lt;xsl:with-param name="padding" select="$padding" /&gt;&lt;br /&gt;      &amp;lt;/xsl:call-template&gt;&lt;br /&gt;     &amp;lt;/xsl:otherwise&gt;&lt;br /&gt;    &amp;lt;/xsl:choose&gt;&lt;br /&gt;   &amp;lt;/xsl:when&gt;&lt;br /&gt;&lt;br /&gt;   &amp;lt;!--  less than $length chars left, just write them --&gt;&lt;br /&gt;   &amp;lt;xsl:otherwise&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="$prefix" /&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="$string" /&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="$suffix" /&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="substring($padding, 1, ($length - string-length($prefix) - string-length($string) - string-length($suffix))) " /&gt;&lt;br /&gt;   &amp;lt;/xsl:otherwise&gt;&lt;br /&gt;  &amp;lt;/xsl:choose&gt;&lt;br /&gt; &amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-114538220755482532?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/114538220755482532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=114538220755482532' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114538220755482532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114538220755482532'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/04/wrapping-long-strings-with-xsl-10.html' title='Wrapping long strings with XSL 1.0'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-114538136472806670</id><published>2006-04-18T13:08:00.000-04:00</published><updated>2006-04-19T10:30:38.240-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='XSL'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Sorting groups in XSL</title><content type='html'>For some of my recent work I had to generate reports from XML using XSL.  One of the techniques I had to use frequently was &lt;a href="http://www.jenitennison.com/xslt/grouping/muenchian.html"&gt;grouping data using the Muenchian Method.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The referenced web page shows an example of getting a group of contacts and sorting it by surname.  That's all well and good if your only computing the group once and processing it straight-forward as shown.  But what if the XML data is large so calculating the group is costly (in terms of memory and CPU) and you need to use it multiple times and the order is important because you're using it within the context of processing some larger processing loop (ex. 10 at a time associated with another value)? &lt;br /&gt;&lt;br /&gt;For an example, let's just say you need to generate a report that has a cover sheet listing all the unique surnames in your file, then separate statistics for each surname such as how many people share that surname.  This is a very contrived example, but at least it will demonstrate what I mean.&lt;br /&gt;&lt;br /&gt;Using the example in the referenced web page, we could build a list of unique sorted surnames to use in various places in our XSL via:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;xsl:key name="contacts-by-surname" match="contact" use="surname"/&gt;&lt;br /&gt;&amp;lt;xsl:variable name="surnames" select="contact[count(. | key('contacts-by-surname', surname)[1]) = 1]/surname"/&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;xsl:template match="/"&gt;&lt;br /&gt;  &amp;lt;xsl:call-template name="sortList"&gt;&lt;br /&gt;     &amp;lt;xsl:with-param name="list" select="$surnames"/&gt;&lt;br /&gt;  &amp;lt;/xsl:call-template&gt;&lt;br /&gt; &lt;br /&gt;  &amp;lt;!-- list is now sorted to use whenever necessary --&gt;&lt;br /&gt;  &amp;lt;xsl:for-each select="$surnames"&gt;&lt;br /&gt;    [process sorted surname]&lt;br /&gt;  &amp;lt;/xsl:for-each&gt;&lt;br /&gt;&amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!-- sort list by the value of its current node --&gt;&lt;br /&gt;&amp;lt;xsl:template name="sortList"&gt;&lt;br /&gt; &amp;lt;xsl:param name="list"/&gt;&lt;br /&gt; &amp;lt;xsl:for-each select="$list"&gt;&lt;br /&gt;      &amp;lt;xsl:sort select="." /&gt;&lt;br /&gt; &amp;lt;/xsl:for-each&gt;&lt;br /&gt;&amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The call to sortList will sort it for all subsequent uses.&lt;br /&gt;&lt;br /&gt;So, to see it in action, here is our sample data:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;records&gt;&lt;br /&gt; &amp;lt;contact id="0001"&gt;&lt;br /&gt;  &amp;lt;title&gt;Mr&amp;lt;/title&gt;&lt;br /&gt;  &amp;lt;forename&gt;John&amp;lt;/forename&gt;&lt;br /&gt;  &amp;lt;surname&gt;Smith&amp;lt;/surname&gt;&lt;br /&gt; &amp;lt;/contact&gt;&lt;br /&gt; &amp;lt;contact id="0002"&gt;&lt;br /&gt;  &amp;lt;title&gt;Dr&amp;lt;/title&gt;&lt;br /&gt;  &amp;lt;forename&gt;Amy&amp;lt;/forename&gt;&lt;br /&gt;  &amp;lt;surname&gt;Jones&amp;lt;/surname&gt;&lt;br /&gt; &amp;lt;/contact&gt;&lt;br /&gt; &amp;lt;contact id="0003"&gt;&lt;br /&gt;  &amp;lt;title&gt;Mr&amp;lt;/title&gt;&lt;br /&gt;  &amp;lt;forename&gt;John&amp;lt;/forename&gt;&lt;br /&gt;  &amp;lt;surname&gt;Doe&amp;lt;/surname&gt;&lt;br /&gt; &amp;lt;/contact&gt;&lt;br /&gt; &amp;lt;contact id="0004"&gt;&lt;br /&gt;  &amp;lt;title&gt;Mr&amp;lt;/title&gt;&lt;br /&gt;  &amp;lt;forename&gt;Jack&amp;lt;/forename&gt;&lt;br /&gt;  &amp;lt;surname&gt;Smith&amp;lt;/surname&gt;&lt;br /&gt; &amp;lt;/contact&gt;&lt;br /&gt; &amp;lt;contact id="0005"&gt;&lt;br /&gt;  &amp;lt;title&gt;Mr&amp;lt;/title&gt;&lt;br /&gt;  &amp;lt;forename&gt;Mike&amp;lt;/forename&gt;&lt;br /&gt;  &amp;lt;surname&gt;Jordan&amp;lt;/surname&gt;&lt;br /&gt; &amp;lt;/contact&gt;&lt;br /&gt; &amp;lt;contact id="0006"&gt;&lt;br /&gt;  &amp;lt;title&gt;Mr&amp;lt;/title&gt;&lt;br /&gt;  &amp;lt;forename&gt;Jim&amp;lt;/forename&gt;&lt;br /&gt;  &amp;lt;surname&gt;Johnson&amp;lt;/surname&gt;&lt;br /&gt; &amp;lt;/contact&gt;&lt;br /&gt; &amp;lt;contact id="0007"&gt;&lt;br /&gt;  &amp;lt;title&gt;Mr&amp;lt;/title&gt;&lt;br /&gt;  &amp;lt;forename&gt;Randy&amp;lt;/forename&gt;&lt;br /&gt;  &amp;lt;surname&gt;Jones&amp;lt;/surname&gt;&lt;br /&gt; &amp;lt;/contact&gt;&lt;br /&gt; &amp;lt;contact id="0008"&gt;&lt;br /&gt;  &amp;lt;title&gt;Mr&amp;lt;/title&gt;&lt;br /&gt;  &amp;lt;forename&gt;Bobby&amp;lt;/forename&gt;&lt;br /&gt;  &amp;lt;surname&gt;Jones&amp;lt;/surname&gt;&lt;br /&gt; &amp;lt;/contact&gt;&lt;br /&gt; &amp;lt;contact id="0009"&gt;&lt;br /&gt;  &amp;lt;title&gt;Mr&amp;lt;/title&gt;&lt;br /&gt;  &amp;lt;forename&gt;Glenn&amp;lt;/forename&gt;&lt;br /&gt;  &amp;lt;surname&gt;Smith&amp;lt;/surname&gt;&lt;br /&gt; &amp;lt;/contact&gt;&lt;br /&gt; &amp;lt;contact id="0010"&gt;&lt;br /&gt;  &amp;lt;title&gt;Mr&amp;lt;/title&gt;&lt;br /&gt;  &amp;lt;forename&gt;Bill&amp;lt;/forename&gt;&lt;br /&gt;  &amp;lt;surname&gt;McDonnald&amp;lt;/surname&gt;&lt;br /&gt; &amp;lt;/contact&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/records&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here is the complete style sheet:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;&lt;br /&gt; &amp;lt;xsl:output method="text" indent="no" omit-xml-declaration="yes" /&gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;xsl:variable name="newline"&gt;&lt;br /&gt;  &amp;lt;!-- Hex 10 is a line feed, and hex 13 is a carriage return --&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="'&amp;amp;#13;'" /&gt;&lt;br /&gt; &amp;lt;/xsl:variable&gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;!-- Remove all whitespace from the source XML so that it is not echoed into the new report --&gt;&lt;br /&gt; &amp;lt;xsl:strip-space elements="//*" /&gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;xsl:key name="contacts-by-surname" match="contact" use="surname" /&gt;&lt;br /&gt; &amp;lt;xsl:variable name="surnames" select="//contact[count(. | key('contacts-by-surname', surname)[1]) = 1]/surname" /&gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;xsl:template match="/"&gt;&lt;br /&gt;  &amp;lt;xsl:text&gt;Full list of surnames:&amp;lt;/xsl:text&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;  &amp;lt;xsl:for-each select="//surname"&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="." /&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="' '" /&gt;&lt;br /&gt;  &amp;lt;/xsl:for-each&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;xsl:text&gt;List of unique surnames:&amp;lt;/xsl:text&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;  &amp;lt;xsl:for-each select="$surnames"&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="." /&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="' '" /&gt;&lt;br /&gt;  &amp;lt;/xsl:for-each&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;xsl:call-template name="sortList"&gt;&lt;br /&gt;   &amp;lt;xsl:with-param name="list" select="$surnames" /&gt;&lt;br /&gt;  &amp;lt;/xsl:call-template&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;!-- list is now sorted to use whenever necessary --&gt;&lt;br /&gt;  &amp;lt;xsl:text&gt;List of unique surnames (sorted):&amp;lt;/xsl:text&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;  &amp;lt;xsl:for-each select="$surnames"&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="." /&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="' '" /&gt;&lt;br /&gt;  &amp;lt;/xsl:for-each&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;xsl:text&gt;People per surname:&amp;lt;/xsl:text&gt;&lt;br /&gt;  &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;  &amp;lt;xsl:for-each select="$surnames"&gt;&lt;br /&gt;   &amp;lt;xsl:variable name="surname" select="."/&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="$surname" /&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="' ('" /&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="count(//contact[surname = $surname])" /&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="'):'" /&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;   &amp;lt;xsl:for-each select="//contact[surname = $surname]"&gt;&lt;br /&gt;    &amp;lt;xsl:sort select="forname"/&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="'   '" /&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="forename" /&gt;&lt;br /&gt;    &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;   &amp;lt;/xsl:for-each&gt;&lt;br /&gt;   &amp;lt;xsl:value-of select="$newline" /&gt;&lt;br /&gt;  &amp;lt;/xsl:for-each&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;!-- sort list by the value of its current node --&gt;&lt;br /&gt; &amp;lt;xsl:template name="sortList"&gt;&lt;br /&gt;  &amp;lt;xsl:param name="list" /&gt;&lt;br /&gt;  &amp;lt;xsl:for-each select="$list"&gt;&lt;br /&gt;   &amp;lt;xsl:sort select="." /&gt;&lt;br /&gt;  &amp;lt;/xsl:for-each&gt;&lt;br /&gt; &amp;lt;/xsl:template&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/xsl:stylesheet&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And here is the report:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Full list of surnames:&lt;br /&gt;Smith Jones Doe Smith Jordan Johnson Jones Jones Smith McDonnald &lt;br /&gt;&lt;br /&gt;List of unique surnames:&lt;br /&gt;Smith Jones Doe Jordan Johnson McDonnald &lt;br /&gt;&lt;br /&gt;List of unique surnames (sorted):&lt;br /&gt;Doe Johnson Jones Jordan McDonnald Smith &lt;br /&gt;&lt;br /&gt;People per surname:&lt;br /&gt;Doe (1):&lt;br /&gt;   John&lt;br /&gt;&lt;br /&gt;Johnson (1):&lt;br /&gt;   Jim&lt;br /&gt;&lt;br /&gt;Jones (3):&lt;br /&gt;   Amy&lt;br /&gt;   Randy&lt;br /&gt;   Bobby&lt;br /&gt;&lt;br /&gt;Jordan (1):&lt;br /&gt;   Mike&lt;br /&gt;&lt;br /&gt;McDonnald (1):&lt;br /&gt;   Bill&lt;br /&gt;&lt;br /&gt;Smith (3):&lt;br /&gt;   John&lt;br /&gt;   Jack&lt;br /&gt;   Glenn&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-114538136472806670?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/114538136472806670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=114538136472806670' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114538136472806670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114538136472806670'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/04/sorting-groups-in-xsl.html' title='Sorting groups in XSL'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-114079174469595752</id><published>2006-02-24T09:34:00.000-05:00</published><updated>2006-09-12T19:43:55.280-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Mastering Ajax</title><content type='html'>These were posted to &lt;a href="http://ajaxian.com"&gt;Ajaxian&lt;/a&gt; and had some useful tidbits:&lt;br /&gt;&lt;br /&gt;3 part series on Mastering AJAX:&lt;br /&gt;&lt;a href="http://www-128.ibm.com/developerworks/web/library/wa-ajaxintro1.html"&gt;http://www-128.ibm.com/developerworks/web/library/wa-ajaxintro1.html&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www-128.ibm.com/developerworks/web/library/wa-ajaxintro2/"&gt;http://www-128.ibm.com/developerworks/web/library/wa-ajaxintro2/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www-128.ibm.com/developerworks/java/library/wa-ajaxintro3/index.html"&gt;http://www-128.ibm.com/developerworks/java/library/wa-ajaxintro3/index.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;2 part series on using SOAP with AJAX&lt;br /&gt;&lt;a href="http://www-128.ibm.com/developerworks/webservices/library/ws-wsajax/"&gt;http://www-128.ibm.com/developerworks/webservices/library/ws-wsajax/&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www-128.ibm.com/developerworks/webservices/library/ws-wsajax2/"&gt;http://www-128.ibm.com/developerworks/webservices/library/ws-wsajax2/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;See the &lt;a href="http://www.maxkiesler.com/index.php/weblog/comments/round_up_of_30_ajax_tutorials/"&gt;round-up of 30 AJAX tutorials&lt;/a&gt; for more useful information.&lt;br /&gt;&lt;br /&gt;I'm also in the process of reading &lt;a href="http://books.slashdot.org/article.pl?sid=05/11/23/1426249"&gt;Ajax in Action&lt;/a&gt; which is very well written.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-114079174469595752?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/114079174469595752/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=114079174469595752' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114079174469595752'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/114079174469595752'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/02/mastering-ajax.html' title='Mastering Ajax'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113866091860613594</id><published>2006-01-30T17:29:00.000-05:00</published><updated>2006-01-30T18:16:07.436-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Enabling JSP in Tomcat</title><content type='html'>Sometimes you just want to enable a directory on your tomcat server to be able to run JSP pages without actually installing a WAR or EAR file.  Here are the essential steps to enable a directory to execute JSP files.&lt;br /&gt;&lt;br /&gt;For this example, I'm setting it up on a SuSE Linux system so your directory structure may vary.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Create a JSP directory in your tomcat webapps directory. In this case, the default webapps directory for Tomcat is &lt;code&gt;/srv/www/tomcat5/base/webapps/&lt;/code&gt; so I make a  &lt;code&gt;jsp&lt;/code&gt; directory there.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Under the &lt;code&gt;jsp&lt;/code&gt; directory I create the basic webapp directory structure and create a directory named &lt;code&gt;/srv/www/tomcat5/base/webapps/jsp/WEB-INF&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;In the &lt;code&gt;WEB-INF&lt;/code&gt; directory, I create a basic deployment descriptor named &lt;code&gt;web.xml&lt;/code&gt; which looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;&lt;br /&gt;&lt;!DOCTYPE web-app&lt;br /&gt;PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"&lt;br /&gt;"http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"&gt;&lt;br /&gt;&amp;lt;web-app&gt;&lt;br /&gt;   &amp;lt;display-name&gt;My JSPs&amp;lt;display-name&gt;&lt;br /&gt;&amp;lt;/web-app&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;In the tomcat configuration directory &lt;code&gt;/etc/tomcat5/base/Catalina/localhost&lt;/code&gt; add the context path for the JSPs in a file named &lt;code&gt;jsp.xml&lt;/code&gt; that looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;Context path="/jsp" docBase="jsp"&lt;br /&gt;        debug="0" reloadable="true" /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Restart Tomcat (&lt;code&gt;rctomcat5 restart&lt;/code&gt;)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Create some sample JSP file in the jsp directory (&lt;code&gt;/srv/www/tomcat5/base/webapps/jsp&lt;/code&gt; such as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;html&gt;&lt;br /&gt;&amp;lt;head&gt;&lt;br /&gt;&amp;lt;title&gt;Hello World&amp;lt;/title&gt;&lt;br /&gt;&amp;lt;/head&gt;&lt;br /&gt;&amp;lt;body&gt;&lt;br /&gt;&amp;lt;h1&gt;&amp;lt;%= "Hello World" %&gt;&amp;lt;h1&gt;       &lt;br /&gt;&amp;lt;/body&gt;&lt;br /&gt;&amp;lt;/html&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Make sure it's working via &lt;code&gt;http://localhost:8080/jsp/index.jsp&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I run Tomcat under Apache via mod_jk so that I can utilize the SSL layer of Apache.  So, to make the JSP transparently accessible from Apache, I update the &lt;code&gt;/etc/apache2/conf.d/jk.conf&lt;/code&gt; and add the following lines:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    # Add the jsp directory&lt;br /&gt;    Alias /jsp "/srv/www/tomcat5/base/webapps/jsp"&lt;br /&gt;    &amp;lt;Directory "/srv/www/tomcat5/base/webapps/jsp"&gt;&lt;br /&gt;        Options Indexes FollowSymLinks&lt;br /&gt;        allow from all&lt;br /&gt;    &amp;lt;/Directory&gt;&lt;br /&gt;    JkMount /jsp ajp13&lt;br /&gt;    JkMount /jsp/* ajp13&lt;br /&gt;    &amp;lt;Location "/jsp/WEB-INF/"&gt;&lt;br /&gt;        AllowOverride None&lt;br /&gt;        deny from all &lt;br /&gt;    &amp;lt;/Location&gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;After restarting Apache (&lt;code&gt;rcapache2 restart&lt;/code&gt;) I can then reference the JSPs directly from Apache, such as &lt;code&gt;http://localhost/jsp/index.jsp&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113866091860613594?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113866091860613594/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113866091860613594' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113866091860613594'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113866091860613594'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/enabling-jsp-in-tomcat.html' title='Enabling JSP in Tomcat'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113864388868980300</id><published>2006-01-30T12:51:00.000-05:00</published><updated>2006-01-30T13:00:03.000-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Multimedia setup for SuSE 10</title><content type='html'>This was a very useful resource for setting up multimedia capabilities for SuSE 10:&lt;br /&gt;&lt;a href="http://www.thejemreport.com/mambo/content/view/178/42/"&gt;http://www.thejemreport.com/mambo/content/view/178/42/ &lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Additionally, adding some of the extra Yast software sources from &lt;a href="http://www.suseforums.net/index.php?showtopic=18575"&gt;http://www.suseforums.net/index.php?showtopic=18575&lt;/a&gt; or &lt;a href="http://www.opensuse.org/Additional_YaST_Package_Repositories#External_YaST_Repositories"&gt;http://www.opensuse.org/Additional_YaST_Package_Repositories#External_YaST_Repositories&lt;/a&gt; is useful.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113864388868980300?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113864388868980300/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113864388868980300' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113864388868980300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113864388868980300'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/multimedia-setup-for-suse-10.html' title='Multimedia setup for SuSE 10'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113863863121310359</id><published>2006-01-30T11:16:00.000-05:00</published><updated>2007-12-19T17:04:19.506-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><title type='text'>Current Earth image wallpaper</title><content type='html'>When I'm using Windows, I use &lt;a href="http://www.tropicdesigns.net/"&gt;WeatherPulse&lt;/a&gt; to display the current satelite picture on my desktop (the "GOES E on Color" option).  I'm very happy with it and wanted something similar on my SuSE Linux KDE desktop.  To do that, I used XPlanet.  Below are the setup steps I followed:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Install xplanet RPM if not already installed.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I made a &lt;code&gt;.xplanet&lt;/code&gt; directory in my home directory in which I  copied the config file from &lt;code&gt;/usr/share/xplanet/config&lt;/code&gt; &lt;/li&gt;&lt;br /&gt;&lt;li&gt;In the &lt;code&gt;[earth]&lt;/code&gt; section of the config file, I added the lines:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;map=/home/username/.xplanet/earthmap4k.png&lt;br /&gt;bump_map=/home/username/.xplanet/earthbump4k.png&lt;br /&gt;bump_scale=5&lt;br /&gt;cloud_map=/home/pothoven/.xplanet/clouds.jpg&lt;br /&gt;&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;To get the dynamic cloud map, I downloaded the &lt;code&gt;download_clouds.pl&lt;/code&gt; Perl script from &lt;a href="http://xplanet.sourceforge.net/clouds.php"&gt;http://xplanet.sourceforge.net/clouds.php&lt;/a&gt; to my .xplanet directory.  Note: you may wish to change this line in the script to suite your preferences:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;my $MaxDownloadFrequencyHours = 1;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I then added this line to my crontab&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;0 * * * * cd /home/username/.xplanet; perl download_clouds.pl &gt; /dev/null&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;to update the cloud information once an hour&lt;/li&gt;&lt;br /&gt;&lt;li&gt;To get the topography and enahanced coloring maps in my config, they can be purchased from &lt;a href="http://gw.marketingden.com/planets/earth.html"&gt;J.H.T.'s Planetary Pixel Emporium&lt;/a&gt; (though a Google search for &lt;code&gt;earthmap4k&lt;/code&gt; may reveal other sources).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If you want to track a satelite, such as the Hubble telescope or International Space Station (ISS), download the lastest tracking data with either:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wget -O science.tle http://www.celestrak.com/NORAD/elements/science.txt&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;for scientific satelites (Hubble) or:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wget -O stations.tle http://celestrak.com/NORAD/elements/stations.txt&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;for space stations (ISS). In the resulting science.tle file, you will see a section with the header "HST" (Hubble space telescope), place those lines in a file named &lt;code&gt;hubble.tle&lt;/code&gt; which will then look like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;HST&lt;br /&gt;1 20580U 90037B   06008.34262361  .00000587  00000-0  33114-4  0       5098&lt;br /&gt;2 20580  28.4688  11.8178 0003636 285.4875  74.5312  14.99943058661070&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you also want to track the IST, also add the "ISS (ZARYA)" lines from the &lt;code&gt;stations.tle&lt;/code&gt; file. These lines will look something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ISS (ZARYA)&lt;br /&gt;1 25544U 98067A   06009.46434028  .00013978  00000-0  10043-3 0  1177&lt;br /&gt;2 25544  51.6445 283.5661 0010748 133.7206 217.3373 15.73896465408147&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then, create a file named &lt;code&gt;hubble&lt;/code&gt; in your &lt;code&gt;.xplanet&lt;/code&gt; directory that contains:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;20580 "" image=hubble.png transparent={0,0,0} trail={orbit,-10,0,5} color=green&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;for tracking the Hubble and/or:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;25544 "" image=iss.png transparent={0,0,0} trail={orbit,-10,0,5} color=green&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;for tracking the space station. Finally, add this line to the &lt;code&gt;[earth]&lt;/code&gt; clause of the config:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;satellite_file=/home/username/.xplanet/hubble&lt;br /&gt;&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Finally, set it up in KDE.  right click on the background.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Select &lt;code&gt;Configure Desktop&lt;/code&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Click on &lt;code&gt;Background&lt;/code&gt; in the left hand panel.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;In the Background group box, I have &lt;code&gt;No picture&lt;code&gt; selected.&lt;/code&gt;&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Click on &lt;code&gt;Advanced Options&lt;/code&gt;.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I have &lt;code&gt;Use the following program for drawing the background&lt;/code&gt; checked.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Select XPlanet in the list.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Click on Modify.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;My &lt;code&gt;Command&lt;/code&gt; entry is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xplanet  --config /home/username/.xplanet/config&lt;br /&gt;      --geometry %xx%y --num_times 1 --output %f.jpg&lt;br /&gt;      --latitude 27.96 --longitude -82.48 --radius 60 &amp;&amp;amp;&lt;br /&gt;      mv %f.jpg %f&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(the latitude and longitude is for where I live -- adjust to suit)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;My &lt;code&gt;Preview cmd&lt;/code&gt; entry is the same as above.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I have my &lt;code&gt;Refresh time&lt;/code&gt; set to 5 minutes if I'm tracking satellites, and 30 minutes if not.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Enjoy a dynamic updating picture of the earth such as:&lt;br/&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://photos1.blogger.com/blogger/6623/2144/1600/xplanet.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://photos1.blogger.com/blogger/6623/2144/320/xplanet.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Update - December 19, 2007&lt;/h3&gt;&lt;br /&gt;First off, I want to point out that XPlanet is an option for Windows as well. See &lt;a href="http://xplanet.sourceforge.net/windows/"&gt;XPlanet on Microsoft Windows&lt;/a&gt;.  You can use the  same Perl script to download the latest cloud cover image using &lt;a href="http://www.activestate.com/Products/activeperl/"&gt;ActivePerl&lt;/a&gt;.  When you run XPlanet use the &lt;code&gt;-fork&lt;/code&gt; option to have it periodically update the background.&lt;br /&gt;&lt;br /&gt;Secondly, the instructions above were for KDE, but if you use Gnome, here are the instructions to  &lt;a href="http://xplanet.sourceforge.net/FAQ.php#gnome2"&gt;get XPlanet to work with Gnome2&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113863863121310359?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113863863121310359/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113863863121310359' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113863863121310359'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113863863121310359'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/current-satellite-wallpaper.html' title='Current Earth image wallpaper'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113821521695609775</id><published>2006-01-25T13:53:00.000-05:00</published><updated>2006-01-25T14:00:57.293-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><title type='text'>Encoding email addresses</title><content type='html'>Here is a handy online tool I use to encode email addresses on my web &lt;br /&gt;pages so that spammers won't find it via automated searches.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://automaticlabs.com/enkoderform/"&gt;http://automaticlabs.com/enkoderform/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113821521695609775?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113821521695609775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113821521695609775' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113821521695609775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113821521695609775'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/encoding-email-addresses.html' title='Encoding email addresses'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113820435125182995</id><published>2006-01-25T10:38:00.000-05:00</published><updated>2006-05-27T15:45:36.806-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Using XML in Ajax (with PHP)</title><content type='html'>In support of my earlier post about using the XMLHttpRequest object for XML, here is  &lt;a href="http://www.xml.com/pub/a/2005/02/09/xml-http-request.html"&gt;a nice article&lt;/a&gt; demonstrating the use of the responseXML attribute of the XMLHttpRequest with PHP.  Here is &lt;a href="http://www.phpbuilder.com/columns/kassemi20050606.php3?print_mode=1"&gt;another&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Also, just for reference here are a few nice sites discussing developments in AJAX:&lt;br /&gt;&lt;a href="http://ajaxian.com/"&gt;Ajaxian&lt;/a&gt;&lt;br /&gt;&lt;a href="http://ajaxblog.com/"&gt;AJAX blog&lt;/a&gt;&lt;br /&gt;&lt;a href="http://ajaxmatters.com/"&gt;AJAX Matters&lt;/a&gt;&lt;br /&gt;&lt;a href="http://ajaxinfo.com/"&gt;ajax info&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As well as some good usage guideline articles:&lt;br /&gt;&lt;a href="http://sourcelabs.com/ajb/archive/2005/05/ajax_mistakes.html"&gt;Ajax Mistakes&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.baekdal.com/articles/Usability/XMLHttpRequest-guidelines/"&gt;XMLHttpRequest Usability Guidelines&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113820435125182995?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113820435125182995/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113820435125182995' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113820435125182995'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113820435125182995'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/using-xml-in-ajax-with-php.html' title='Using XML in Ajax (with PHP)'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113819936949614259</id><published>2006-01-25T09:29:00.000-05:00</published><updated>2007-08-21T10:58:44.622-04:00</updated><title type='text'>Detecting email problems</title><content type='html'>This is just a little tip for detecting email issues with your web&lt;br /&gt;hosting provider.  Myself and some friends of mine were experiencing&lt;br /&gt;email loss with our hosting provider.  So, in order to provide some&lt;br /&gt;imperial data we set up a couple of automated processes to send&lt;br /&gt;ourselves email every 5 minutes.  We set up one process on the&lt;br /&gt;provider's server which sent to an email account in question on the&lt;br /&gt;provider's server, as well as an external control account.  Then we set&lt;br /&gt;up another process on another server outside of the hosting provider's&lt;br /&gt;realm to send email to the same email account in question on the hosting&lt;br /&gt;provider and the control account.  The external email account was our&lt;br /&gt;control set to monitor that all the email messages were actually&lt;br /&gt;transmitted.&lt;br /&gt;&lt;br /&gt;The setup (done on both the hosted server and the secondary server):&lt;br /&gt;&lt;br /&gt;Make a test message such as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Subject: 5min Test from [servername]&lt;br /&gt;From: Automated Test &amp;lt;test@mydomain.com&amp;gt;&lt;br /&gt;&lt;br /&gt;Test email. &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The header information in the test message is useful for adding filters&lt;br /&gt;to your email client.  The blank line is necessary after the header&lt;br /&gt;lines in order for sendmail to recognize it as a header.&lt;br /&gt;&lt;br /&gt;We then set up a simple cron process&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;*/5 * * * * /usr/sbin/sendmail -i email@mydomain.com,control@gmail.com &amp;lt; testemail.txt&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will send out the message every 5 minutes (every minute divisible&lt;br /&gt;by 5).  Then it is just a matter of scanning the email received for&lt;br /&gt;missing emails.&lt;br /&gt;In the end, we were able to detect email loss as well as delayed email.&lt;br /&gt;The hosting provider was then able to make some adjustments to stop the&lt;br /&gt;loss.  During peek load times, some email still gets lost for a hour or&lt;br /&gt;two, but does eventually show up.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113819936949614259?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113819936949614259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113819936949614259' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113819936949614259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113819936949614259'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/detecting-email-problems.html' title='Detecting email problems'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113803228762817678</id><published>2006-01-23T10:49:00.000-05:00</published><updated>2007-08-21T10:56:52.332-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Ajax in practice</title><content type='html'>In my research of using AJAX, I've run across plenty of nice examples. Most of the examples consist of the basics of building and using an XMLHttpRequest such as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function createRequestObject() {&lt;br /&gt;var ro;&lt;br /&gt;var browser = navigator.appName;&lt;br /&gt;if(browser == "Microsoft Internet Explorer"){&lt;br /&gt;  ro = new ActiveXObject("Microsoft.XMLHTTP");&lt;br /&gt;}else{&lt;br /&gt;  ro = new XMLHttpRequest();&lt;br /&gt;}&lt;br /&gt;return ro;&lt;br /&gt;}&lt;br /&gt;var http = createRequestObject();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The use that XMLHttpRequest object to submit the request to the back-end server such as:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function sendRequest(action) {&lt;br /&gt; http.open('get', 'ajaxHandler.php?action='+action);&lt;br /&gt; http.onreadystatechange = handleResponse;&lt;br /&gt; http.send(null);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And finally some function to update the browser page based on the result:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;function handleResponse() {&lt;br /&gt;/* The XMLHttpRequest object has a property called readyState with several states:&lt;br /&gt;    0: Uninitialized&lt;br /&gt;    1: Loading&lt;br /&gt;    2: Loaded&lt;br /&gt;    3: Interactive&lt;br /&gt;    4: Finished&lt;br /&gt;*/&lt;br /&gt;if(http.readyState == 4){&lt;br /&gt;var response = http.responseText;&lt;br /&gt;document.getElementById('divName').innerHTML = response;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's all well and good, but is seems like a misuse of an &lt;span style="font-weight: bold;"&gt;XML&lt;/span&gt;HttpRequest to send a plain text (or HTML) response. We have a perfect opportunity to separate presentation from data and everyone seems to mash them together on the back-end instead of letting the browser handle the rendering. At least the example &lt;a href="http://www.phpbuilder.com/columns/kassemi20050606.php3"&gt;here&lt;/a&gt; demonstrates using the responseXML property of the XMLHttpRequest object instead of only the text response which is nice.&lt;br /&gt;&lt;br /&gt;One of the worst abuses that I've encountered so far seems to b e the Google Suggest implementation. In my past research of AJAX I ran across &lt;a href="http://serversideguy.blogspot.com/2004/12/google-suggest-dissected.html"&gt;this excellent piece&lt;/a&gt; by Chris Justus about how Google used AJAX to implement Google Suggest. I must say, I'm disappointed with how the folks at Google did that. As Chris demonstrates, they actually return a JavaScript snippet which it then invoked by the browser to add the search suggestions.&lt;br /&gt;&lt;br /&gt;Sample returned value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sendRPCDone(frameElement, "fast bug", &lt;br /&gt;new Array("fast bug track", "fast bugs", "fast bug", "fast bugtrack"),&lt;br /&gt;new Array("793,000 results", "2,040,000 results", "6,000,000 results", "7,910 results"),&lt;br /&gt;new Array(""));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The actual sendRPCDone method is previously loaded in the web page, but they instruct the browser to invoke it was it gets the response. Obviously it works, and is very fast, but the data just seems too bound to the implementation. Why not just return the data in a format that can be used in this page, and possibly other functions? Something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;suggestions&gt;&lt;br /&gt; &amp;lt;search&gt;fast bug&amp;lt;/search&gt;&lt;br /&gt; &amp;lt;suggestion&gt;&lt;br /&gt;     &amp;lt;value&gt;fast bug track&amp;lt;/value&gt;&lt;br /&gt;     &amp;lt;occurs&gt;793,000&amp;lt;/occurs&gt;&lt;br /&gt; &amp;lt;/suggestion&gt;&lt;br /&gt; &amp;lt;suggestion&gt;&lt;br /&gt;    &amp;lt;value&gt;fast bugs&amp;lt;/value&gt;&lt;br /&gt;    &amp;lt;occurs&gt;2,040,000&amp;lt;/occurs&gt;&lt;br /&gt; &amp;lt;/suggestion&gt;&lt;br /&gt; &amp;lt;suggestion&gt;&lt;br /&gt;    &amp;lt;value&gt;fast bug&amp;lt;/value&gt;&lt;br /&gt;    &amp;lt;occurs&gt;6,000,000&amp;lt;/occurs&gt;&lt;br /&gt; &amp;lt;/suggestion&gt;&lt;br /&gt; &amp;lt;suggestion&gt;&lt;br /&gt;    &amp;lt;value&gt;fast bugtrack&amp;lt;/value&gt;&lt;br /&gt;    &amp;lt;occurs&gt;7,910&amp;lt;/occurs&gt;&lt;br /&gt; &amp;lt;/suggestion&gt;&lt;br /&gt;&amp;lt;/suggestions&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113803228762817678?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113803228762817678/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113803228762817678' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113803228762817678'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113803228762817678'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/ajax-in-practice.html' title='Ajax in practice'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113778471556944195</id><published>2006-01-20T14:07:00.000-05:00</published><updated>2006-01-20T14:45:49.260-05:00</updated><title type='text'>Publication</title><content type='html'>I ran across this &lt;a href="http://springerlink.metapress.com/link.asp?id=t4p9tj0w77bc9ayx"&gt;link&lt;/a&gt; to a paper based on my Master's thesis. My graduate advisor, &lt;a href="http://www.cs.ucf.edu/~workman/"&gt;Dr. David Workman&lt;/a&gt;, edited my paper and presented it at conference a couple years ago. &lt;br /&gt;&lt;br /&gt;He has apparently turned it into the topic of a whole course which I became aware of when some of his students contacted me about the source code that was used in the program this paper discusses.  Here is a &lt;a href="http://longwood.cs.ucf.edu/~workman/cen5016/Projects/TRED/TRED.html"&gt;link&lt;/a&gt; to the course information.  The full text of the above mentioned paper is available from this course page.&lt;br /&gt;&lt;br /&gt;It's nice to know that your work still has life after you graduate!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113778471556944195?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113778471556944195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113778471556944195' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113778471556944195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113778471556944195'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/publication.html' title='Publication'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113777191081222956</id><published>2006-01-20T10:25:00.000-05:00</published><updated>2007-08-21T11:00:15.046-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='podcasting'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><title type='text'>Changing FeedCreator.class.php for Podcasting</title><content type='html'>As I mentioned in my Podcasting HowTo, I used Kai Blankenhorn's &lt;a href="http://www.bitfolge.de/rsscreator-en.html"&gt;FeedCreator.class.php&lt;/a&gt; as the basis of my podcasting feed. In this posting I will detail my additions to his code to support podcasting and iTunes.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;You may download the final file &lt;a href="http://www.pothoven.net/files/feedcreator.zip" onClick="javascript:urchinTracker('/download/feedcreator.zip');"&gt;here&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;First, at line 31, I updated the change log:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;v1.7.2-iTunes&lt;br /&gt;  added support for iTunes tags&lt;br /&gt;v1.7.2-podcast&lt;br /&gt;  added support for podcast (added enclosure)&lt;br /&gt;  made the Item category be an Array as you are allowed to specify&lt;br /&gt;     multiple category elements in the RSS spec&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then, at what becomes line 173 (after the changelog changes), I added&lt;br /&gt;these classes:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/**&lt;br /&gt;* Version string.&lt;br /&gt;**/&lt;br /&gt;define("FEEDCREATOR_VERSION", "FeedCreator 1.7.2-iTunes");&lt;br /&gt;/**&lt;br /&gt;* An Enclosure is a part of an Item&lt;br /&gt;*&lt;br /&gt;* @author Steven Pothoven &amp;lt;steven@pothoven.net&gt;&lt;br /&gt;* @since 1.7.2-podcast&lt;br /&gt;*/&lt;br /&gt;class Enclosure {&lt;br /&gt; /**&lt;br /&gt;  * Attributes of an enclosure&lt;br /&gt;  */&lt;br /&gt; var $url, $length, $type = "audio/mpeg";&lt;br /&gt;}&lt;br /&gt;/**&lt;br /&gt;* iTunes extensions to RSS 2.0&lt;br /&gt;*&lt;br /&gt;* @author Steven Pothoven &amp;lt;steven@pothoven.net&gt;&lt;br /&gt;* @since 1.7.2-iTunes&lt;br /&gt;*/&lt;br /&gt;class iTunes {&lt;br /&gt; /**&lt;br /&gt;  * This tag can only be populated using iTunes specific categories.&lt;br /&gt;  */&lt;br /&gt; var $category, $subcategory;&lt;br /&gt; /**&lt;br /&gt;  * This tag should be used to note whether or not your Podcast contains explicit material.&lt;br /&gt;  * There are 2 possible values for this tag: Yes or No&lt;br /&gt;  */&lt;br /&gt; var $explicit;&lt;br /&gt; /*&lt;br /&gt;  * At the Channel level, this tag is a short description that provides general information about the Podcast. It will appear next to your Podcast as users browse through listings of Podcasts&lt;br /&gt;  * At the Item level, this tag is a short description that provides specific information for each episode.&lt;br /&gt;  * Limited to 255 characters or less, plain text, no HTML&lt;br /&gt;  */&lt;br /&gt; var $subtitle;&lt;br /&gt; /*&lt;br /&gt;  * At the Channel level, this tag is a long description that will appear next to your Podcast cover art when a user selects your Podcast.&lt;br /&gt;  * At the Item level, this tag is a long description that will be displayed in an expanded window when users click on an episode.&lt;br /&gt;  * Limited to 4000 characters or less, plain text, no HTML&lt;br /&gt;  */&lt;br /&gt; var $summary;&lt;br /&gt; /*&lt;br /&gt;  * At the Channel level this tag contains the name of the person or company that is most widely attributed to publishing the Podcast and will be displayed immediately underneath the title of the Podcast.&lt;br /&gt;  * If applicable, at the item level, this tag can contain information about the person(s) featured on a specific episode.&lt;br /&gt;  */&lt;br /&gt; var $author;&lt;br /&gt; /*&lt;br /&gt;  * This tag is for informational purposes only and will allow users to know the duration prior to download&lt;br /&gt;  * The tag is formatted: HH:MM:SS&lt;br /&gt;  */&lt;br /&gt; var $duration;&lt;br /&gt; /*&lt;br /&gt;  * This tag allows users to search on text keywords&lt;br /&gt;  * Limited to 255 characters or less, plain text, no HTML, words must be separated by spaces&lt;br /&gt;  */&lt;br /&gt; var $keywords;&lt;br /&gt; /*&lt;br /&gt;  * This tag contains the e-mail address that will be used to contact the owner of the Podcast for communication specifically about their Podcast on iTunes.&lt;br /&gt;  * Required element specifying the email address of the owner.&lt;br /&gt;  */&lt;br /&gt; var $owner_email;&lt;br /&gt; /*&lt;br /&gt;  * Optional element specifying the name of the owner.&lt;br /&gt;  */&lt;br /&gt; var $owner_name;&lt;br /&gt; /*&lt;br /&gt;  * This tag specifies the artwork for the Channel and Item(s). This artwork can be larger than the maximum allowed by RSS.&lt;br /&gt;  * Preferred size: 300 x 300 at 72 dpi&lt;br /&gt;  * Minimum size: 170 pixels x 170 pixels square at 72 dpi&lt;br /&gt;  * Format: JPG, PNG, uncompressed&lt;br /&gt;  */&lt;br /&gt; var $image;&lt;br /&gt; /*&lt;br /&gt;  * This tag is used to block a podcast or an episode within a podcast from being posted to iTunes. Only use this tag when you want a podcast or an episode to appear within the iTunes podcast directory.&lt;br /&gt;  */&lt;br /&gt; var $block;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the FeedItem class I added these lines at the new line 283:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;      /**&lt;br /&gt;       * Support for attachments&lt;br /&gt;       */&lt;br /&gt;      var $enclosure;&lt;br /&gt;      /**&lt;br /&gt;       * Support for iTunes&lt;br /&gt;       */&lt;br /&gt;      var $itunes;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the _setFormat function, I added a new case at the new line 491:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;          case "PODCAST":&lt;br /&gt;              $this-&gt;_feed = new PodcastCreator();&lt;br /&gt;              break; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In the createFeed function, I added the iTunes extentions at line&lt;br /&gt;1102:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;              /* iTunes add iTunes specific tags */&lt;br /&gt;              if ($this-&gt;itunes!="") {&lt;br /&gt;          if ($this-&gt;itunes-&gt;category!="") {&lt;br /&gt;             $feed.= " &amp;lt;itunes:category text=\"".htmlspecialchars($this-&gt;itunes-&gt;category)."\"&gt;\n";&lt;br /&gt;             if ($this-&gt;itunes-&gt;subcategory!="") {&lt;br /&gt;                $feed.= " &amp;lt;itunes:category text=\"".htmlspecialchars($this-&gt;itunes-&gt;subcategory)."\"/&gt;\n";&lt;br /&gt;             }&lt;br /&gt;             $feed.= "        &amp;lt;/itunes:category&gt;\n";&lt;br /&gt;          }&lt;br /&gt;          if ($this-&gt;itunes-&gt;explicit!="") {&lt;br /&gt;             $feed.= " &amp;lt;itunes:explicit&gt;".$this-&gt;itunes-&gt;explicit."&amp;lt;/itunes:explicit&gt;\n";&lt;br /&gt;          }&lt;br /&gt;          if ($this-&gt;itunes-&gt;subtitle!="") {&lt;br /&gt;             $feed.= " &amp;lt;itunes:subtitle&gt;".htmlspecialchars($this-&gt;itunes-&gt;subtitle)."&amp;lt;/itunes:subtitle&gt;\n";&lt;br /&gt;          }&lt;br /&gt;          if ($this-&gt;itunes-&gt;summary!="") {&lt;br /&gt;             $feed.= " &amp;lt;itunes:summary&gt;".htmlspecialchars($this-&gt;itunes-&gt;summary)."&amp;lt;/itunes:summary&gt;\n";&lt;br /&gt;          }&lt;br /&gt;          if ($this-&gt;itunes-&gt;author!="") {&lt;br /&gt;             $feed.= " &amp;lt;itunes:author&gt;".htmlspecialchars($this-&gt;itunes-&gt;author)."&amp;lt;/itunes:author&gt;\n";&lt;br /&gt;          }&lt;br /&gt;          if ($this-&gt;itunes-&gt;keywords!="") {&lt;br /&gt;             $feed.= " &amp;lt;itunes:keywords&gt;".htmlspecialchars($this-&gt;itunes-&gt;keywords)."&amp;lt;/itunes:keywords&gt;\n";&lt;br /&gt;          }&lt;br /&gt;          if ($this-&gt;itunes-&gt;owner_email!="") {&lt;br /&gt;             $feed.= "        &amp;lt;itunes:owner&gt;\n";&lt;br /&gt;             $feed.= " &amp;lt;itunes:email&gt;".$this-&gt;itunes-&gt;owner_email."&amp;lt;/itunes:email&gt;\n";&lt;br /&gt;             if ($this-&gt;itunes-&gt;owner_name!="") {&lt;br /&gt;                $feed.= " &amp;lt;itunes:name&gt;".$this-&gt;itunes-&gt;owner_name."&amp;lt;/itunes:name&gt;\n";&lt;br /&gt;             }&lt;br /&gt;             $feed.= "        &amp;lt;/itunes:owner&gt;\n";&lt;br /&gt;          }&lt;br /&gt;          if ($this-&gt;itunes-&gt;image!="") {&lt;br /&gt;             $feed.= " &amp;lt;itunes:link rel=\"image\" type=\"image/jpeg\" href=\"".$this-&gt;itunes-&gt;image."\"&gt;[image]&amp;lt;/itunes:link&gt;\n";&lt;br /&gt;          }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and additional iTunes and podcast extensions at line 1154:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;                /* podcasts add the enclosure element */&lt;br /&gt;          if ($this-&gt;items[$i]-&gt;enclosure!="") {&lt;br /&gt;             $feed.= " &amp;lt;enclosure url=\"".str_replace(" ", "%20", htmlspecialchars($this-&gt;items[$i]-&gt;enclosure-&gt;url)).                                "\" length=\"".htmlspecialchars($this-&gt;items[$i]-&gt;enclosure-&gt;length).                               "\" type=\"".htmlspecialchars($this-&gt;items[$i]-&gt;enclosure-&gt;type).                               "\"/&gt;\n";&lt;br /&gt;          }&lt;br /&gt;                /* iTunes add iTunes specific tags */&lt;br /&gt;          if ($this-&gt;items[$i]-&gt;itunes!="") {&lt;br /&gt;              if ($this-&gt;items[$i]-&gt;itunes-&gt;category!="") {&lt;br /&gt;                 $feed.= " &amp;lt;itunes:category text=\"".htmlspecialchars($this-&gt;items[$i]-&gt;itunes-&gt;category)."\"&gt;\n";&lt;br /&gt;                 if ($this-&gt;items[$i]-&gt;itunes-&gt;subcategory!="") {&lt;br /&gt;                    $feed.= " &amp;lt;itunes:category text=\"".htmlspecialchars($this-&gt;items[$i]-&gt;itunes-&gt;subcategory)."\"/&gt;\n";&lt;br /&gt;                 }&lt;br /&gt;                 $feed.= "            &amp;lt;/itunes:category&gt;\n";&lt;br /&gt;              }&lt;br /&gt;              if ($this-&gt;items[$i]-&gt;itunes-&gt;explicit!="") {&lt;br /&gt;                 $feed.= " &amp;lt;itunes:explicit&gt;".$this-&gt;items[$i]-&gt;itunes-&gt;explicit."&amp;lt;/itunes:explicit&gt;\n";&lt;br /&gt;              }&lt;br /&gt;              if ($this-&gt;items[$i]-&gt;itunes-&gt;subtitle!="") {&lt;br /&gt;                 $feed.= " &amp;lt;itunes:subtitle&gt;".htmlspecialchars($this-&gt;items[$i]-&gt;itunes-&gt;subtitle)."&amp;lt;/itunes:subtitle&gt;\n";&lt;br /&gt;              }&lt;br /&gt;              if ($this-&gt;items[$i]-&gt;itunes-&gt;summary!="") {&lt;br /&gt;                 $feed.= " &amp;lt;itunes:summary&gt;".htmlspecialchars($this-&gt;items[$i]-&gt;itunes-&gt;summary)."&amp;lt;/itunes:summary&gt;\n";&lt;br /&gt;              }&lt;br /&gt;              if ($this-&gt;items[$i]-&gt;itunes-&gt;author!="") {&lt;br /&gt;                 $feed.= " &amp;lt;itunes:author&gt;".htmlspecialchars($this-&gt;items[$i]-&gt;itunes-&gt;author)."&amp;lt;/itunes:author&gt;\n";&lt;br /&gt;              }&lt;br /&gt;              if ($this-&gt;items[$i]-&gt;itunes-&gt;keywords!="") {&lt;br /&gt;                 $feed.= " &amp;lt;itunes:keywords&gt;".htmlspecialchars($this-&gt;items[$i]-&gt;itunes-&gt;keywords)."&amp;lt;/itunes:keywords&gt;\n";&lt;br /&gt;              }&lt;br /&gt;              if ($this-&gt;items[$i]-&gt;itunes-&gt;duration!="") {&lt;br /&gt;                 $feed.= " &amp;lt;itunes:duration&gt;".$this-&gt;items[$i]-&gt;itunes-&gt;duration."&amp;lt;/itunes:duration&gt;\n";&lt;br /&gt;              }&lt;br /&gt;              if ($this-&gt;items[$i]-&gt;itunes-&gt;image!="") {&lt;br /&gt;                 $feed.= " &amp;lt;itunes:link rel=\"image\" type=\"image/jpeg\" href=\"".$this-&gt;items[$i]-&gt;itunes-&gt;image."\"&gt;[image]&amp;lt;/itunes:link&gt;\n";&lt;br /&gt;              }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Finally, I added the PodcastCreator class at line 1235:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;/**&lt;br /&gt;* PodcastCreator is a FeedCreator that implements Podcast&lt;br /&gt;*&lt;br /&gt;* @see http://backend.userland.com/rss&lt;br /&gt;* @since 1.7.2-podcast&lt;br /&gt;* @author Steven Pothoven &amp;lt;steven@pothoven.net&gt;&lt;br /&gt;*/&lt;br /&gt;class PodcastCreator extends RSSCreator20 {&lt;br /&gt;function PodcastCreator() {&lt;br /&gt;   parent::_setRSSVersion("2.0");&lt;br /&gt;   parent::_setXMLNS("itunes=\"http://www.itunes.com/DTDs/Podcast-1.0.dtd\"");&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113777191081222956?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113777191081222956/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113777191081222956' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113777191081222956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113777191081222956'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/changing-feedcreatorclassphp-for.html' title='Changing FeedCreator.class.php for Podcasting'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113776903125252756</id><published>2006-01-20T09:42:00.000-05:00</published><updated>2007-08-21T10:54:28.459-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='podcasting'/><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='XML'/><title type='text'>Podcasting HowTo</title><content type='html'>As I mentioned in my &lt;a href="http://blog.pothoven.net/2006/01/podcasting.html"&gt;last post&lt;/a&gt;, I added podcasting to our church web site. Today I thought I'd share how I did that for those interested in doing something similar.&lt;br /&gt;&lt;br /&gt;To start with, I place all the sermon MP3 files into a separate directory. The MP3 files themselves adhere to the naming standard of:&lt;br /&gt;&lt;br /&gt;SermonDate_Speaker_Passage.mp3&lt;br /&gt;&lt;br /&gt;Where the date is in YYMMDD format.  So, this past weeks sermons file is named:&lt;br /&gt;&lt;br /&gt;060115_John Keen_Mark 1;1-8.mp3&lt;br /&gt;&lt;br /&gt;This makes it very easy add sermons to the list by just uploading the file. Note: the passage uses a ';' to delineate chapter and verse instead of a ':', this is because a ':' is not allowed in a filename on some systems (Windows). The code will correct that when displayed.&lt;br /&gt;&lt;br /&gt;I started with a simple table to be shown on the &lt;a href="http://www.seminolepca.org/sermons"&gt;web page.&lt;/a&gt; Shortly after that I thought an RSS feed of the contents would be handy so people could be motified of new sermons without visiting the web page. The RSS feed lead to the podcast feed, as they are nearly the same thing.&lt;br /&gt;&lt;br /&gt;They all essentially do the same thing, so I'll skip right to the podcast version. However, I'll share one little tidbit from the web page version. From the table on the web page, you can either stream the MP3, or download the file. Streaming an MP3 file from your server doesn't take any special software. I wrote a simple PHP script which I name m3u.php which allows me to stream any MP3 file. The contents of the m3u.php looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?php&lt;br /&gt;header("Content-Type: audio/x-mpegurl");&lt;br /&gt;print 'http://www.hostname.com/path/to/mp3/' . $_GET['filename'];&lt;br /&gt;?&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, in my table code, invoke it as (in PHP):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;a href="sermons/m3u.php?filename=' . $filename . '"&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To make the podcast feed, I started with Kai Blankenhorn's &lt;a href="http://www.bitfolge.de/rsscreator-en.html"&gt;FeedCreator.class.php&lt;/a&gt;. It worked very well for the RSS feed, but it doesn't support the podcast extensions. So, I updated it to add the podcast and iTunes extensions to RSS. If you're interested, I'll be happy to provide my updated version. I'll summarize the changes in &lt;a href="http://blog.pothoven.net/2006/01/changing-feedcreatorclassphp-for.html"&gt;a separate post&lt;/a&gt; so I don't make this post too long.&lt;br /&gt;&lt;br /&gt;Now, I'll walk through my podcast generating PHP class.  First, I need a function to list all the files in my mp3 directory:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// Function list_dir will list all the files in a given directory&lt;br /&gt;//&lt;br /&gt;function list_dir($dirname)&lt;br /&gt;{&lt;br /&gt;   static $result_array = array();&lt;br /&gt;   $handle=opendir($dirname);&lt;br /&gt;   while ($file=readdir($handle)) {&lt;br /&gt;      // ignore the current and parent directory references ('.' and '..')&lt;br /&gt;      if (0 == strpos($file, '.')) {&lt;br /&gt;         continue;&lt;br /&gt;      }&lt;br /&gt;      $result_array[]=$file;&lt;br /&gt;   }&lt;br /&gt;   closedir ($handle);&lt;br /&gt;   return $result_array;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I then set up the common header portion of the podcast feed:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$rss = new UniversalFeedCreator();&lt;br /&gt;// if cache is less than 12 hours (43200 seconds) old, use it&lt;br /&gt;// this is because the feed only changes once a week to begin with, and this&lt;br /&gt;// script does a lot of ID3 tag manipulation which can take a while&lt;br /&gt;$rss-&gt;useCached("PODCAST", "sermons/podcast.xml", 43200);&lt;br /&gt;$rss-&gt;title = "Seminole PCA Sermons";&lt;br /&gt;$rss-&gt;description = "Recent sermons from the pulpit of Seminole Presbyterian Church";&lt;br /&gt;$rss-&gt;language = "en-us";&lt;br /&gt;$rss-&gt;link = "http://www.seminolepca.org";&lt;br /&gt;$rss-&gt;webmaster = "webadmin@seminolepca.org";&lt;br /&gt;$rss-&gt;docs = "http://blogs.law.harvard.edu/tech/rss";&lt;br /&gt;$rss-&gt;syndicationURL = "http://www.seminolepca.org/".$PHP_SELF;&lt;br /&gt;$rss-&gt;category = "Religion";&lt;br /&gt;&lt;br /&gt;$image = new FeedImage();&lt;br /&gt;$image-&gt;title = "Seminole PCA, Tampa, FL";&lt;br /&gt;$image-&gt;url = "http://www.seminolepca.org/images/rss-SPC.jpg";&lt;br /&gt;$image-&gt;link = "http://www.seminolepca.org";&lt;br /&gt;$image-&gt;description = "Feed provided by seminolepca.org. Click to visit.";&lt;br /&gt;$image-&gt;width = 144;&lt;br /&gt;$image-&gt;height = 204;&lt;br /&gt;$rss-&gt;image = $image;&lt;br /&gt;&lt;br /&gt;$itunes = new iTunes();&lt;br /&gt;$itunes-&gt;category = "Religion &amp; Spirituality";&lt;br /&gt;$itunes-&gt;subcategory = "Christianity";&lt;br /&gt;$itunes-&gt;subtitle = $rss-&gt;description;&lt;br /&gt;$itunes-&gt;owner_email = "church@seminolepca.org";&lt;br /&gt;$itunes-&gt;owner_name = "Seminole Presbyterian Church";&lt;br /&gt;$itunes-&gt;author = "Seminole Presbyterian Church";&lt;br /&gt;$itunes-&gt;image = "http://www.seminolepca.org/images/podcast-SPC.jpg";&lt;br /&gt;$rss-&gt;itunes = $itunes;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, I loop through my list of mp3 files to build the contents of the podcast feed:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;// PHP code to dynamically generate RSS feed&lt;br /&gt;//&lt;br /&gt;// Start of code -------&lt;br /&gt;$mp3dir="sermons/mp3";&lt;br /&gt;// get a list of the "mp3" directory&lt;br /&gt;$array=list_dir($mp3dir);&lt;br /&gt;// reverse sort the array (puts newest sermon first)&lt;br /&gt;rsort ($array);&lt;br /&gt;reset ($array);&lt;br /&gt;&lt;br /&gt;// for each file in the filelist, generate the appropriate HTML table code&lt;br /&gt;foreach ($array as $filename) {&lt;br /&gt;   // break apart the filename into the 3 data fields; date, speaker,&lt;br /&gt;   // and subject using file naming convention:&lt;br /&gt;   // YYMMDD_Speaker_Passage.mp3&lt;br /&gt;   //&lt;br /&gt;   $tok = strtok($filename, "_");&lt;br /&gt;   $unixdate = mktime(0, 0, 0, substr($tok, 2, 2), substr($tok, 4, 2), substr($tok, 0, 2));&lt;br /&gt;   $sermondate=date("F d, Y", $unixdate);&lt;br /&gt;            &lt;br /&gt;   $tok=strtok("_");&lt;br /&gt;   $speaker=$tok;&lt;br /&gt;   $tok=strtok(".");&lt;br /&gt;   $passage=$tok;&lt;br /&gt;   $chapter = strtok($passage, ";");&lt;br /&gt;   $verse = strtok(".");&lt;br /&gt;   if ($verse !== false) {&lt;br /&gt;      $passage = $chapter . ':' . $verse;&lt;br /&gt;   } else {&lt;br /&gt;      $passage = $chapter;&lt;br /&gt;   }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;at this point in my PHP code, I also use the &lt;a href="http://getid3.sourceforge.net/"&gt;getid3&lt;/a&gt; PHP library to ensure that all the ID3 tags are set correctly in the mp3 file. This is not important for actually generating the podcast feed, so I'll omit it here.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;   $item = new FeedItem();&lt;br /&gt;   $item-&gt;title = $speaker . ' on ' . $passage;&lt;br /&gt;   $item-&gt;link = "http://www.seminolepca.org/" . $mp3dir . "/" . $filename;&lt;br /&gt;   $item-&gt;description = $sermondate . ' - ' . $speaker . ' expounds on ' . $passage;&lt;br /&gt;   $item-&gt;date = $unixdate;&lt;br /&gt;   $item-&gt;source = "http://www.seminolepca.org";&lt;br /&gt;   $item-&gt;author = $speaker;&lt;br /&gt;   $item-&gt;enclosure = new Enclosure();&lt;br /&gt;   $item-&gt;enclosure-&gt;url = "http://www.seminolepca.org/" . $mp3dir . "/" . $filename;&lt;br /&gt;   $item-&gt;enclosure-&gt;length= $id3['filesize'];&lt;br /&gt;   $item-&gt;category[] = $itunes-&gt;category;&lt;br /&gt;&lt;br /&gt;   $item-&gt;itunes = new iTunes();&lt;br /&gt;   $item-&gt;itunes-&gt;category = $itunes-&gt;category;&lt;br /&gt;   $item-&gt;itunes-&gt;subcategory = $itunes-&gt;subcategory;&lt;br /&gt;   $item-&gt;itunes-&gt;keywords = "Seminole Presbyterian Church PCA Reformed Christian Sermon Tampa Florida FL";&lt;br /&gt;   $item-&gt;itunes-&gt;summary = $item-&gt;description;&lt;br /&gt;   $item-&gt;itunes-&gt;author = $item-&gt;author;&lt;br /&gt;   $item-&gt;itunes-&gt;duration = $id3['playtime_string'];&lt;br /&gt;   $item-&gt;itunes-&gt;image = "http://www.seminolepca.org/images/podcast-SPC.jpg";&lt;br /&gt;   $rss-&gt;addItem( $item);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, I save feed for caching purposes and return it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$rss-&gt;saveFeed("PODCAST", "sermons/podcast.xml");&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The resulting podcast XML can be seen &lt;a href="http://www.seminolepca.org/podcast-sermons.php"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Once that's working, go register you feed at the various podcast directories such as:&lt;br /&gt;&lt;a href="http://ipassages.com/index.php"&gt;iPassage&lt;/a&gt; (if you podcasting sermons)&lt;br /&gt;&lt;a href="http://www.ipodder.org/"&gt;iPodder&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.podcast.net/"&gt;Podcast.net&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.podcastalley.com/"&gt;Podcast Alley&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.apple.com/itunes/podcasts/"&gt;iTunes&lt;/a&gt; (&lt;a href="http://www.feedforall.com/itunes-tutorial-mac.htm"&gt;HowTo&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;I published my feed in iPodder, Podcast.net, and Podcast Alley and was automatically propagated to other directories including the iTunes Store.&lt;br /&gt;&lt;br /&gt;For reference, here is the official &lt;a href="http://phobos.apple.com/static/iTunesRSS.html"&gt;Apple iTunes/Podcasting specification&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113776903125252756?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113776903125252756/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113776903125252756' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113776903125252756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113776903125252756'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/podcasting-howto.html' title='Podcasting HowTo'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113770919094959052</id><published>2006-01-19T17:13:00.000-05:00</published><updated>2006-01-20T14:21:05.630-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web development'/><category scheme='http://www.blogger.com/atom/ns#' term='Ajax'/><title type='text'>Podcasting</title><content type='html'>One of the things I do outside of my job is maintain the website for my church.  I attend Seminole Presbyterian Church (&lt;a href="http://www.seminolepca.org"&gt;http://www.seminolepca.org&lt;/a&gt;). On that site I added podcasting for our sermons and was recently interviewed by a reporter at the St. Petersburg Times regarding the podcasting. You can read the article &lt;a href="http://www.sptimes.com/2006/01/09/Tampabay/Podcasts_picking_up__.shtml"&gt;here&lt;/a&gt; or from the archive &lt;a href="http://pqasb.pqarchiver.com/sptimes/968224811.html?MAC=f2e2a3357a99dcc42371466fda46c304&amp;did=968224811&amp;amp;FMT=FT&amp;FMTS=FT&amp;amp;date=Jan+12%2C+2006&amp;author=CURTIS+KRUEGER&amp;amp;pub=St.+Petersburg+Times&amp;printformat=&amp;amp;desc=Podcasts+picking+up%2C+but+who%27s+listening%3F"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I will be redesigning that page soon and will probably be incorporating some AJAX techniques, so I will be adding posts regarding AJAX soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113770919094959052?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113770919094959052/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113770919094959052' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113770919094959052'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113770919094959052'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/podcasting.html' title='Podcasting'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-21222625.post-113770872163514216</id><published>2006-01-19T16:53:00.000-05:00</published><updated>2006-01-19T17:12:01.643-05:00</updated><title type='text'>About my blog</title><content type='html'>Hello, my name is Steven Pothoven, welcome to my blog!&lt;br /&gt;&lt;br /&gt;I am a software architect / software engineer for IBM.  I have a Master's degree in Computer Science and have been working at IBM since 1995.  My specialty is in object-oriented development using Java (J2EE) and I am also a IBM Certified Solution Developer for XML and Related Technologies. The natural progression of my career has moved me toward SOA (Service Oriented Architecture) as both Java and XML are key technologies.&lt;br /&gt;&lt;br /&gt;This blog will primarily focus on various computer technologies as I happened to be researching various topics, or reading interesting articles.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/21222625-113770872163514216?l=blog.pothoven.net' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.pothoven.net/feeds/113770872163514216/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=21222625&amp;postID=113770872163514216' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113770872163514216'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/21222625/posts/default/113770872163514216'/><link rel='alternate' type='text/html' href='http://blog.pothoven.net/2006/01/about-my-blog.html' title='About my blog'/><author><name>Steven Pothoven</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='http://1.bp.blogspot.com/_l-kZv4tdloI/TUrkCD9kHRI/AAAAAAAAAJo/toMx4geX5VA/s220/Photo%2Bon%2B2009-11-22%2Bat%2B21.41.jpg'/></author><thr:total>0</thr:total></entry></feed>
