- IT Conversations: Ryan Freitas
Great rant on what collaboration is and isn’t and on what some of the cool tools are.
(categories: collaboration podcast email wikis IM sharing ) - Blog by Kveton > The PDX Mojo: Open Source Finding its Stride
Like Portland? Work at Jive! http://www.jivesoftware.com/company/jobs.jsp
(categories: portland jobs oregon ) - Scripting News for 12/20/2006 > Scripting News Annex
Douglas Crockford: “The good thing about reinventing the wheel is that you can get a round one.”
(categories: json javascript quotes ) - Daring Fireball: About the Footnotes
In case you’re wondering where I got the hankering for footnotes in my posts on the Clearspace blog. Wait, you haven’t seen my posts over there? Check it out: http://jivesoftware.com/blog/
(categories: footnotes design html usability blogging )
Monthly Archives: December 2006
Links: 12-21-2006
- Raible Designs | [TSE] Building Modern Web Applications with Mike Stenhouse
Lots of good CSS and JavaScript stuff…
(categories: css javascript design development standards toread )
Links: 12-19-2006
- Jive jumps into Web collaboration | CNET News.com
The cat is jumping out of the bag.
(categories: clearspace jive awesomeness )
Links: 12-16-2006
- InfoQ: How to Design a Good API & Why it Matters
Video of Josh Bloch at Javapolis.
(categories: api java presentations reference software videos design )
Links: 12-11-2006
- Craythur.com
None of the apps are useful and it’s slow, but still… wow.
(categories: javascript os prototype scriptaculous desktop )
Script.aculous, Autocompleter, Element.collectTextNodes and Element.cleanWhitespace
A couple days ago I was fighting with the Script.aculous autocompleter. I was using code that was almost exactly like the customized autocompletion demo, where the content returned from the AJAX call was a bulleted list of items with each item containing three div’s:
<ul id="mylist"> <li> <div><img src="/images/avatar/robot.png" /></div> <div>Aaron Johnson</div> </li> <li> ... </li> </ul>
After the content is returned, the user selects an option and the ‘updateElement()’ function of Autocompleter.Base is called, which itself uses a function ‘Element.collectTextNodes()’. Element.collectTextNodes traverses the all the nodes in the selected option and retrieves the text value of each one, effectively stripping the HTML from the ul element. Finally, the autocompleter takes the resulting text value and updates the value of the textbox that you started with in the first place.
At least that’s what’s supposed to happen. What actually happened was that the textbox wasn’t showing the selected value at all and I beat my head against my desk a couple times. Why? If you take the HTML example above and run it through Element.collectTextNodes(), you’ll get this:
Aaron Johnson
not this:
Aaron Johnson
See the line break? If you try to update the value of a textbox with a variable whose first line is a line break, the textbox sees only the line break and nothing else.
The solution turns out to be a widely used function in prototype: Element.cleanWhitespace(), which removes all empty text node children of an element. So I updated Element.collectTextNodes to look like this:
Element.collectTextNodes = function(element) { element = Element.cleanWhitespace(element); return $A($(element).childNodes).collect( function(node) { return (node.nodeType==3 ? node.nodeValue : (node.hasChildNodes() ? Element.collectTextNodes(node) : '')); }).flatten().join(''); }
I created a wiki page on the script.aculous wiki for the Element.collectTextNodes function, post a comment on the wiki if you agree that the function should include a call to Element.cleanWhitespace.
Links: 12-7-2006
- The Loss of James Kim | MetaFilter
Google ad at the bottom of the page: Unconditional Love
(categories: family ) - Into the Wild (2007)
Fascinating book, becoming a movie in 2007.
(categories: tosee adventure wilderness mccandless )
Ajax.Autocompleter is not a constructor
Using script.aculous to create some cool effects on your website? Ever get this error message?
Ajax.Autocompleter is not a constructor
All of the results I found on Google suggested that the solution to the problem was to make sure that you were including controls.js (which is where the autocompletion stuff lives in script.aculous). If you checked and double checked that you have controls.js (or that you’re including scriptaculous.js which itself includes controls.js), then your problem could be that you included prototype.js twice. Example:
<script type="text/javascript" src="prototype.js"></script> <script type="text/javascript" src="scriptaculous.js"></script> <script> function createAC() { new Ajax.Autocompleter('mytextbox', 'myautocomplete', 'autocomplete/url', {}); } </script> <input type="text" id="mytextbox" name="username" value="" /> <div id="myautocomplete" class="autocomplete"></div> <a href="#" onclick="createAC(); return false;">start autocomplete</a> <script type="text/javascript" src="prototype.js"></script>
Why? Prototype defines a class ‘Ajax’:
var Ajax = { getTransport: function() { ...
and then Script.aculous extends that class:
Ajax.Autocompleter = Class.create(); Object.extend(Object.extend(Ajax.Autocompleter.prototype, Autocompleter.Base.prototype), { ...
So including prototype.js a second time blows away the class defined by Script.aculous. Really easy to fix, but if you’re working on a large team, make sure that prototype.js is only included once on every page.
Links: 12-4-2006
- CopyAllUrls Mozilla-Firefox Extension ? Plasser.net
CopyAllUrls is a Firefox extension that copies and pastes all urls of open tabs inclusive history in structured and well defined form to and from the clipboard.
(categories: firefox extensions )
RSS/Atom feeds, Last Modified and Etags
Sometime last week I read this piece by Sam Ruby, which summarized says this:
…don’t send Etag and Last-Modified headers unless you really mean it. But if you can support it, please do. It will save you some bandwidth and your readers some processing.
The product I’ve been working on at work (which I should be able to start talking about soon which I can talk about now) for the last couple months uses feeds (either Atom, RSS 1.0 or RSS 2.0, your choice) extensively but didn’t have Etag or Last-Modified support so I spent a couple hours working on it this past weekend. We’re using ROME, so the code ended up looking something like this:
HttpServletRequest request = ... HttpServletResponse response = .... SyndFeed feed = ... if (!isModified(request, feed)) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); } else { long publishDate = feed.getPublishedDate().getTime(); response.setDateHeader("Last-Modified", publishDate); response.setHeader("Etag", getEtag(feed)); } ... private String getEtag(SyndFeed feed) { return "\"" + String.valueOf(feed.getPublishedDate().getTime()) + "\""; } ... private boolean isModified(HttpServletRequest request, SyndFeed feed) { if (request.getHeader("If-Modified-Since") != null && request.getHeader("If-None-Match") != null) { String feedTag = getEtag(feed); String eTag = request.getHeader("If-None-Match"); Calendar ifModifiedSince = Calendar.getInstance(); ifModifiedSince.setTimeInMillis(request.getDateHeader("If-Modified-Since")); Calendar publishDate = Calendar.getInstance(); publishDate.setTime(feed.getPublishedDate()); publishDate.set(Calendar.MILLISECOND, 0); int diff = ifModifiedSince.compareTo(publishDate); return diff != 0 || !eTag.equalsIgnoreCase(feedTag); } else { return true; } }
There are only a two gotchas in the code:
- The value of the Etag must be quoted, hence the
getEtag(...)
method above returning a string wrapped in quotes. Not hard to do, but easy to miss. - The first block of code above uses the
setDateHeader(String name, long date)
to set the ‘Last-Modified’ HTTP header, which conveniently takes care of formatting the given date according to the RFC 822 specification for dates and times. The published date comes from ROME. Here’s where it gets tricky: if the client returns the ‘If-Modified-Since’ header and you retrieve said date from the request usinggetDateHeader(String name)
, you’ll get a Date in the GMT timezone, which means if you want to compare the date you’ll have to get the date into your own timezone. That’s relatively easy to do by creating aCalendar
instance and setting the time of the instance to the value you retrieved from the header. The Calendar instance will transparently take care of the timezone change for you. But there’s still one thing left: the date specification for RFC 822 doesn’t specify a millisecond so if thelong
value you hand tosetDateHeader(long date)
method contains a millisecond value and you then try to use the same value to compare against the ‘If-Modified-Since’ header, you’ll never get a match. The easy way around that is to manually set the millisecond bits on the date you get back from the ‘If-Modified-Since’ header to zero.
If you’re interested, there are a number of other blogs / articles about Etags and Last-Modified headers: