Archive for the Category 'ajax'

Parameters string and the Prototype Ajax.Request method

I just noticed something that's a little funny about Prototype 1.6>. I've successfully deployed a number of projects with v1.5 of the library, with all of the improvements I've been reading about with v1.6, I decided to keep up with the times and use the newest version for a project that is very DOM Scripting intensive.

While making a request with either new Ajax.Request or new Ajax.Updater something kept going wrong.

Though the Prototype API Docs were giving examples such as this:

JavaScript:
  1. new Ajax.Updater('products', '/some_url', { method: 'get' });

Firebug was throwing me an error. Hooray for Firebug for doing it's job. Well, that's what I thought at first, until it was time to see what this error was about. Long story short, Google returned nothing...

CODE:
  1. pair has no properties
  2. http://projectname.localhost/js/prototype.js
  3. Line 417
  4.  
  5. [Break on this error]  if ((pair = pair.split('='))[0]) {

It turns out that this error was being thrown by Prototype's toQueryParams method. toQueryParams was being called by Ajax.Base's initialize method...

JavaScript:
  1. if (Object.isString(this.options.parameters))
  2.     this.options.parameters = this.options.parameters.toQueryParams();
  3. else if (Object.isHash(this.options.parameters))
  4.     this.options.parameters = this.options.parameters.toObject();

I said to myself (in the third person) "Nater, you're not passing any params, the request URL is specified elsewhere, AND it's 'friendly' anyway... this seems silly". Looking up a couple of lines it turns out that the default Ajax.Base.options.parameters key is set to an empty string by default. Since toQueryParams() is not happy when it's passed an empty (albeit default) string, what else can we do? The simple answer is to pass an empty object instead!

JavaScript:
  1. new Ajax.Updater('products', '/some_url', { method: 'get' ,parameters:{}});

Voila! Since this seems like it's something that's way too easy to come across, I haven't yet figured out if it's a bug or I'm just missing something that's even more basic.

Switching to Zimbra

I've been thinking about switching to Zimbra... for quite a while now actually. Their new desktop client (with offline sweetness) seems like it's pretty damn cool, and being able to use the same UI regardless of platform... nice.

If any of you have had any direct experience using Zimbra, please get in touch, I have a few questions I'd like to run by someone who's actually used the opensource albeit unsupported version, if at all possible.

ajax boot camp

Earlier today Ajaxian mentioned Eric Pascarello's proposal for an Ajax Boot Camp. A two day adventure filled with lectures and hands-on coding. Some of questions Eric plans to answer with this workshop are:

  • what is the consequences of not using caching on the client?
  • what happens with session?
  • what causes that memory leak?

Current plans are to host the camp in the Baltimore/DC area (my old stomping grounds) sometime in september. You can read the original post and keep yourself updated here.

Now drop and give me twenty.

monster’s new instant job search beta

Since I'm currently job hunting, a few times a week I bounce around between the different job sites out there to see if there are any decent opportunities close to where I live, or if I do actually have to commute to NYC to find good work. Today I was checking out monster after persuing a lead that made it's way to my Inbox early this morning to discover Monster's new Instant Job Search.

The Instant Job Search is a simple ajax app that lets job seekers do basic keyword searches of available jobs on the fly, without the use of a submit button. You specify your zip code, and search radius, and get results sort of quickly (avg. search time for me was between 2-5 seconds, not blazingly fast by any means, but quicker than a page refresh of course. I quickly typed in "Ajax" to get two listings of the same job for a .NET Developer in Connecticut. Well I'm not a .NET guy, so I expanded the radius from 35 to 70 miles (which includes NYC of course) and got 12 returned results, most of which I can work with.

Some interesting findings

This new little app works like an auto-complete and begins searching once you type in two characters in the Keywords field. As I said before there isn't a submit button, so it seems like every time you add a letter, it sends a request. And when you enter another character, it cancels that request and replaces it with a new one. So this is my quesiton. What's the point of this app? By sending requests with each keystroke and trying to return any data any chance it gets, theoretically if a user types slowly and is a hunt and peck type, they could actually using more bandwidth than if they had a page refresh since the server is returning more than the typical auto-complete string array.

Json meets AHAH

Ok, I'm sure you're saying "what's the big deal?" It's just lightweight data and folks who hunt and peck will never notice. And I'll agree with you. The WTF is what's returned by the server when you run your query, a line of JSON then some AHAH style HTML. Huh? Here's my query.

HTML:
  1. http://my.monster.com/JobSearch/InstantFeeder.aspx?_location=12528&_keywords=ajax&_radius=70&_sort=rank&_pagenum=1&_pagesize=1

And here's the JSON / AHAH hybrid that was returned

HTML:
  1. {"jobs":{"returned":12,"found":22,"pagenum":1}}
  2. <table class="ijsResult" id="ijsresult_1" xmlns:fo="http://www.w3.org/1999/XSL/Format">
  3.   <tr>
  4.     <td style="vertical-align:top;">
  5.       <span style="display:none;" id="ijsmeta_1">{'jobid':46098442 , 'positionid':36388017, 'displayoptions':}</span>
  6.       <div class="colItemNumber">1.
  7.         <br /></div>
  8.     </td>
  9.     <td style="vertical-align:top;">
  10.       <div class="colDetailedDateActive">
  11.         <span style="display:none;" class="colApplied" id="ijsapplied_1">A</span>
  12.         <span style="display:none;" class="colFiled" id="ijsfiled_1" onclick="IjsEvent({etype:'delfromfile',value:1});return false;">F</span>7/25/2006</div>
  13.       <div class="colDetailedJobTitle">
  14.         <span style="display:none;" id="ijsstar_1">
  15.           <img src="http://media.monster.com/mm/usen/icons/10x10_arrow.gif" border="0" alt="" />
  16.         </span>
  17.         <a href="http://jobsearch.monster.com/getjob.asp?JobID=46098442&amp;WT.mns_pth=1001" target="_blank">Java Developer with XML and Web Services exp</a>
  18.       </div>
  19.       <div>
  20.         <span class="colDetailedCompanyName">CyberCoders</span>  <span class="colDetailedLocation">Harrison, New York</span></div>
  21.       <div class="colDetailedSummary">Java developer for exciting Software Company This position is open as of 7/26/2006. Not a fit for this position? Java Developer with XML and Web Services exp   Location    ...<a href="http://jobsearch.monster.com/getjob.asp?JobID=46098442&amp;WT.mns_pth=1001" target="_blank">[more]</a>  <span class="colSaveToFile" id="ijssavetofile_1"><a href="javascript:return false;" onclick="IjsEvent({etype:'savetofile',value:1});return false;">[save to file]</a></span></div>
  22.     </td>
  23.   </tr>
  24. </table>

Pretty weird eh? Every time results are returned there is a parsing error due to syntax issues with the JSON string. And then other errors related to various DOM issues ie. "uncaught exception: Permission denied to set property Window.status" and "Error in parsing value for property 'right'. Declaration dropped."

What I don't understand is why the use of a hybrid return data set in the first place? All of the XHTML is formatted in the same way so why bloat things up with the redundant code? That same data could be in XML or JSON and be totally trimmed down. Below is an example of a JSON version of the same data. The JSON string equates to 592 bytes as opposed to the HTML version that is a much heftier 1,749 bytes. You get the point.

JavaScript:
  1. {"jobs": {
  2.     "meta": {
  3.         "returned": 1,
  4.         "found": 22,
  5.         "pagenum": 1},
  6.     "result": [
  7.         {"jobid": 46098442,
  8.         "positionid": 36388017,
  9.         "displayoptions":,
  10.         "itemNumber":1,
  11.         "ijsapplied": "A",
  12.         "ijsfiled": "7/25/2006",
  13.         "detailedJobTitle": "Java Developer with XML and Web Services exp",
  14.         "detailedCompanyName": "CyberCoders",
  15.         "detailedLocation": "Harrison, New York",
  16.         "detailedSummary": "Java developer for exciting Software Company This position is open as of 7/26/2006. Not a fit for this position? Java Developer with XML and Web Services exp   Location    ..."}]}

How to make a beta annoying to debug

Three words, lousy code formatting. The javascript errors took me to this line

JavaScript:
  1. return"";},SafeEval:function(s){return eval("["+s+"]")[0];},CookieGet:function(ckey){var carr=document.cookie.split(/\s*;\s*/);for(var nvk in carr){var nvsplit=carr[nvk].split(/=/);if(nvsplit[0]==ckey){return nvsplit[1];}}

Coding grammar

I can't stress enough the importance of well formatted code. When you are writing in any language, english included, poor formatting equals improper grammar. Thissentence,isinenglishandcanberead!eventhoughitisnotnaturalYadig? If a highschool student wrote a paper that was difficult to read, they'd get a lousy grade even if it was the most most amazing piece of work in the entire history of literature. The same applies to coding. Yes, when I typed in my query the app returned the results I was expecting. But from such a large company as monster.com, I was hoping for more.

doing simple things without a code library

So I just was reading this article written by David Talbot on Devx.com about Yahoo's UI libraries aiming to make crossbrowser development a bit easier for some folks.

The only notable feature that I could seem to find, was the simplification of setting the opacity of a DOM element in an elegant way. An entire library for that? I got a little curious and took a further look.

Here's an exerpt from the artile:

The DOM and Event Libraries
YUI's capabilities in regard to core DOM and event handling are incredibly rock-solid, perform as expected, and cover a broad range of capabilities. Most surprisingly, this API gives you simple cross-browser transparency control. Most developers aren't even aware that magic tricks even exist (via DirectX calls) to make semi-transparency work in IE, much less the Mozilla-based equivalents, but YUI enables access to most, if not all, of the CSS properties you'd ever want. Here's an example:

YAHOO.util.Dom.setStyle('MyDivID', 'opacity', .5);

Suprizingly? Are you kidding me? Maybe I just don't get what the big deal is. I've been developing crossbrowser DHTML in one way or another since the beginnings of the last browser wars. And back in 99-2000 when everyone was bitching about the diversity in W3C support between the version 4 browsers, I was just doing what needed to be done to make everything work the way it needed to.

So despite Mr. Talbot's enthusiasm for what I considered to be not much of a big deal at all I headed over to Yahoo's Developer Center poked around their lousy documentation and downloaded the most recent build here on Sourceforge. I took a peek at the source, and it turns out that Yahoo and I have almost the same exact approach, thing is, running their entire library is pretty needless.

Here's what I came up with to put together a fade in effect for the items being loaded into my portfolio via xmlHttpRequest.

here's the very simple browser check:

JavaScript:
  1. var ua = navigator.userAgent.toLowerCase();
  2. var isOpera = (ua.indexOf('opera') != -1);
  3. var isIE = (ua.indexOf('msie') != -1 && !isOpera);

and here's an exerpt from my getPortfolioDetail function:

JavaScript:
  1. xhrPortfolioDetail.onreadystatechange = function() {
  2.     if (xhrPortfolioDetail.readyState == 4 && xhrPortfolioDetail.status == 200) {
  3.         portfolioDetail.innerHTML = xhrPortfolioDetail.responseText;
  4.         var fader;
  5.         for(count = 1;count <= 10; count++){
  6.             fader = setTimeout(function(){
  7.                 val = count*0.1;
  8.                 if (isIE && typeof portfolioDetail.style.filter == 'string'){
  9.                     portfolioDetail.style.filter = 'alpha(opacity=' + val * 100 + ')';
  10.                     if (!portfolioDetail.currentStyle || !portfolioDetail.currentStyle.hasLayout) {
  11.                     portfolioDetail.style.zoom = 'normal'; // if you use '1' it distorts things
  12.                 }
  13.             } else {
  14.                 portfolioDetail.style.opacity = val;
  15.                 portfolioDetail.style['-moz-opacity'] = val;
  16.                 portfolioDetail.style['-khtml-opacity'] = val;
  17.             }
  18.         },100*count);
  19.     }
  20.     xhrPortfolioDetail = null;
  21. }

Pretty simple eh? One thing that you can't see is where I use the same approach to lower the opacity to 0.1 while loading, so there isn't any flashing of the loaded content before it fades in.

I know that there are tons of AJAX Frameworks out there that provide fade in type effects, but I'm not quite there on implementing one. Noteables include Dojo, MochiKit, and of course the ever popular script.aculo.us.

I'm still working on making it work a little more smoothly and efficient as the delay in setting the opacity while i'm doing the xmlHttpRequest is inconsistant at best.

is Spry the way to go?

Framework this, framework that. RoR or CakePHP? bah, what about somthing else? Well Adobe Labs has introduced the Spry Framework for Ajax prerelease 1.1. And their demos look pretty good.

Photo Gallery
Photo Gallery
Product Table
Product Table
RSS Reader
RSS Reader
The release addresses issues reported by testers since Spry first came out. You can find the change log here, the overview here, and find the download here.

Even though Spry's UI effects make it quite attractive for my playful and easily distracted side. I wonder how it would hold up with my use of XHTML return data instead the clientside XML parsing that i talked about in this previous post.

true asynchronous requests and ‘anti-patterns’

I caught a post on ajaxian today talking about ajax 'anti-patterns', and the term caught my eye. Apparently Ed Burnette has coined the term referring to anti-patterns as things to watch out for while developing your ajax app. Chris Cornutt from ajaxian summed it up with this short list.

  • Chatty communication - lots of “chatterâ€? back and forth between the server
  • Too much XML parsing - libraries to parse XML in the browser are still relatively slow
  • Loading everything before displaying something - don’t make the user wait if things are slow. give them something.
  • Rendering on the server - make good decisions as to where to render various page content
  • Over thinking the design - don’t use the old mentality of just a few releases, update often and in bits

This got me thinking, as am currently in the redevelopment of my site, and most importantly my portfolio, I have deliberately avoided having my serverside pass XML back to the client, and have instead opted to use formatted XHTML. I had been thinking that this was a copout and that I should in my next build deploy a much more extensible Javascript based XML parser for my list of thumbnails and subsequent portfolio item data. I mean, using XML means that I can free my server up and pass less complex data back to the client right? Well yes and no.

Currently I'm hosted with mediatemple. The renound hosting company for design geeks everywhere. And if I remember correctly, they have pretty decent servers, with decent processing power that I actually give them money every month to be able to use. Additionally, my 'lowest common denominator' testing machine here is a 6-7 year old 700mhz P3. Not so much of a powerhouse, and since it's been beaten to hell over the years, it's not the processing powerhouse that it once was back at the end of the last decade. So if somthing is going to perform badly, my trusty P3 will catch it and let me know. This is where we get to the issue of clientside XML parsing, forthought, and extensibility.

If you're anywhere on my site other than in the portfolio itself, the sidebar nav's casestudies links are generated with a simple mySQL query, and a loop in PHP that builds a link that looks like this.

HTML:
  1. http://www.naterkane.com/portfolio.php?casestudy=1&id=231

It basically tells the page to say, hey, we want to show a particular project off the bat, so instead of just calling the function to load up the project thumbnails, lets also specify a particular project, and load it up too, with the related case study data instead of just the basic project description text.

In order to do that I had to make sure that my xmlHttpRequests didn't step on each other's toes. So I instead of specifying a global request/response object, I kept it inside of each method, and used a constructor that created a local object instead.

JavaScript:
  1. function xhrCreate(type){
  2.     var xhr;
  3.     if (!type) {
  4.         type = 'text';
  5.     }
  6.     if (window.ActiveXObject) {
  7.         try {
  8.             xhr = new ActiveXObject("Msxml2.XMLHTTP");
  9.         } catch (e) {
  10.             try {
  11.                 xhr = new ActiveXObject("Microsoft.XMLHTTP");
  12.             } catch (e) {}
  13.         }
  14.     } else if (window.XMLHttpRequest) {
  15.         xhr = new XMLHttpRequest();
  16.         if (xhr.overrideMimeType) {
  17.             xhr.overrideMimeType('text/' + type);
  18.         }
  19.     }
  20. return (xhr);
  21. }
  22.  
  23. function getPortfolio(role) {
  24.     var xhrPortfolio = xhrCreate('html');
  25.     var portfolio = document.getElementById("portfolio");
  26.     var url = "includes/getPortfolio.php?role=" + role;
  27.     url +='&t=' + new Date().getTime();
  28.     portfolio.innerHTML = '<img src="http://www.naterkane.com/blog/images/loading.gif" />';
  29.     xhrPortfolio.open("GET", url , true);
  30.     xhrPortfolio.onreadystatechange = function() {
  31.         if (xhrPortfolio.readyState == 4 && xhrPortfolio.status == 200) {
  32.             portfolio.innerHTML = xhrPortfolio.responseText;
  33.             xhrPortfolio = null;
  34.         } else if (xhrPortfolio.readyState == 4 && xhrPortfolio.status == 404) {
  35.             portfolio.innerHTML = xhrPortfolio.statusText;
  36.             xhrPortfolio = null;
  37.         }
  38.     }
  39.     xhrPortfolio.send(null);
  40. }
  41.  
  42. function getPortfolioDetail(id,casestudy) {
  43.     var xhrPortfolioDetail = xhrCreate('html');
  44.     var portfolioDetail = document.getElementById("portfolioDetail");
  45.     var url = "includes/getPortfolioDetail.php?id=" + id;
  46.     url +='&t=' + new Date().getTime();
  47.     if (casestudy){
  48.         url += "&casestudy=1";
  49.     }
  50.     portfolioDetail.innerHTML = '<img src="http://www.naterkane.com/blog/images/loading.gif" />';
  51.     xhrPortfolioDetail.open("GET", url , true);
  52.     xhrPortfolioDetail.onreadystatechange = function() {
  53.         if (xhrPortfolioDetail.readyState == 4 && xhrPortfolioDetail.status == 200) {
  54.             portfolioDetail.innerHTML = xhrPortfolioDetail.responseText;
  55.             xhrPortfolioDetail = null;
  56.         } else if (xhrPortfolioDetail.readyState == 4 && xhrPortfolioDetail.status == 404) {
  57.             portfolioDetail.innerHTML = xhrPortfolioDetail.statusText;
  58.             xhrPortfolioDetail = null;
  59.         }
  60.     }
  61.     xhrPortfolioDetail.send(null);
  62. }

Ok so now I'm sending two requests at the same time, they're not colliding and everything is peachy.

The

Obj.responseText

in both methods employ AHAH (preformatted XHTML), with calls to other javascript function, and other complex data that I don't care to distribute to anyone else. So why am I not parsing it on the client side? I'd rather have the PHP do the work for me once, instead of having my PHP first have to parse the data into XML, and then use Javascript to reparse it again into somthing that the browser can display as I intend. Which would further slow down the response time of the call.

So am I right or wrong in my approach to returning data this way? I'm still not sure, but at least I know that I'm not burdening my client with additional duties.

Portfolio Update

that's right, i finally got around to updating my portfolio. everything is semi organized, at least alot more so than it was before and a bit ajaxy as well. organized by year, and sortable by what role i played in the project. a few projects have case studies, and a few others have really crappy screenshots that are either 6 years old, or were pulled from the nice folks over at archive.org. as i'm looking for work right now, you should be able to see some new features popping up over the next few days/weeks.


Nater Kane naterkane personal http://www.naterkane.com LinkedIn Profile Web Technologist personal nater@naterkane.com 1978-09-12 voice 845.234.6698 | fax 707.922.0593
964 Flushing Ave. Brooklyn, NY 11206