Monthly Archives: March 2010

Easy jQuery multifile uploader


I needed a quick and dirty multi-file uploader for a site, but I did not want to install another plugin or a flash uploader.  I am using the jQuery form plugin already to handle AJAX form submissions so I will hook to that to upload my files to the site.

My method basically uses jQuery to inject a form with a filefield into the DOM.  I bind to the onchange event of the filefield so when a file is added the current file field is hidden, insert a new blank file field, and update a list of queued files waiting to be uploaded.  When the user hits the “Upload Files” button, I iterate through all the “input:file” elements using jQuery and submit the form using the jQuery form plugin with ajaxForm().  Of course, you can remove a file from queue as well by clicking on the X in the queue list.

It is an inelegant solution, but it does work.  Here is the code if you want to give it a try:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Multi-file upload</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="/js/jquery.form.js"></script>
</head>

<body>
 <div id="uploadContainer"></div>
 <input id="btnSubmit" type="button" value="Submit Files" onclick="submitLoop();" />
 <div id="fileInfo"/>

 <script language="javascript">
  $(document).ready(function(){
   $container = $('#uploadContainer');
   $info = $('#fileInfo');
   count=0;                            
   addUpload();
  });
 function addUpload(){
  count++;
  $('form').hide();
  $container.append('<form id="uploadForm-' + count +'" action="ajaxProxy.cfc?method=upload" enctype="multipart/form-data"><input id="file-' + count + '" name="filename" type="file" onchange="addUpload();" /></form>');
  showFileInfo();
 }
 function removeItem(id){
  if( $('#' + id).length){
   $('#' + id).remove();  
   showFileInfo();
  }
 }
 function showFileInfo(){
  $info.html('');
  $('input:file').each(function(){
   if( $(this).val().length ){
    $info.append('<div id="display-' + $(this).attr('id') + '"><a href="#" onclick="removeItem(\'' + $(this).parent().attr('id') +'\')">X</a> '  + $(this).val() + '</div>');
   }
  });
 }
 function submitLoop(){
  $('input:file').each(function(){
   var currItem = $(this).attr('id');
   if($(this).val()){
    $(this).parent().ajaxForm({
     async: false,
     success: function(data){
      var incoming = $.parseJSON(data);
      $('#display-' + currItem).append(' <strong>' + incoming.svrMessage + '</strong>');
     }
    }).submit();
   }
  });
 }
 </script>
</body>
</html>

Using jQuery sortable() to set display sort order


I am currently working on wrapping up a web site for a client that has quite a few image and video galleries each with 100+ images.  One of the biggest problems I have had with the galleries is being able to sort how they were displayed for the end users.  Initially I added in buttons to move the items up and down in the sort, but when you are dealing with a lot of items it can be tedious and aggravating work.

This weekend things came to a head because the client was getting annoyed.   They wanted a way to quickly update the item sort so I went about finding the best way to go about it because I love happy clients.  My main concern was having items in the database with the same sort order and possibly throwing off the sort.  As it turns out, the answer was surprisingly simple.

The web site in question is already using the jQueryUI so modifying the image administrator to use the UI sortable() option was really no hassle what-so-ever.  I am using AJAX pretty heavily on the site I just needed to do some editing to my file list page that is included on the page with an AJAX load call.

<cfscript>
 catObj = application.transfer.get("categories",url.id);
 base = '/path/to/web/root';
 filePath = base & catObj.getBaseDirectory();

 cq = application.transfer.createQuery("FROM images WHERE images.cat=:id ORDER BY images.sortorder");
 cq.setParam("id",url.id,"numeric");

 files = application.transfer.listByQuery(cq);
</cfscript>

<cfif files.recordcount>
 <div id="sortableContainer" style="margin-top: 48px;">
  <cfoutput query="files">
  <div id="#ID#" style="width: 600px; margin-bottom: 12px;">
   <div style="height: 20px; text-indent: 12px; padding-top: 4px;">Image Header</div>
   <div class="ui-widget-content">
    <table style="width: 600px;">
     <tr>
      <td width="75">
      <a href="/image_viewer.cfm?id=#ID#" target="_blank" style="color: blue;">
       <img src="/#catObj.getBaseDirectory()#/thumbs/#filename#" width="75" height="75"/>
      </a>        
     </td>
     <td>
      <form id="img#ID#" action="ajaxProxy.cfc" method="post">
       <input type="hidden" name="postType" value="updateImage" />
       <input type="hidden" name="id" value="#ID#" />
       <input type="text" name="Title" value="#Title#" style="width: 200px;" /><br />
       <input type="submit" name="Method" value="Update" />
       <input type="button" value="Delete" onClick="void(0);" />
       <input type="button" value="Set" onClick="void(0);" />
      </form>        
     </td>
    </tr>
   </table>
  </div>    
 </div>
 </cfoutput>
 </div>

 <div style="padding-bottom: 128px;"/>

<script type="text/javascript">
 $('#sortableContainer').sortable({
 stop    : function(event, ui){
 var result = $('#sortableContainer').sortable('toArray');
 var sortOrder = '';

 for(var i=0; i<result.length; i++){
 sortOrder += result[i];
 if(i < result.length - 1) sortOrder += ',';
 }
 setImageSortOrder(sortOrder);
 }
 });

 function setImageSortOrder(order){
 $.ajax({
 url            : 'ajaxProxy.cfc',
 data            : {
 method        : 'setImageSortOrder',
 sortOrder     : order
 },
 async        : false,
 success        : function(data){
 var incoming = $.parseJSON(data);
 if(incoming.svrStatus == "-1"){
 alert(incoming.svrMessage);
 }
 }
 });
 }
</script>
</cfif>

Basically this is just looping through the database and outputting all the images in divs in the div named sortableContainer which has the sortable() interaction applied to it.   I also bind to the stop event which will fire when a sortable child is released which will build a string containing the image id’s in the proper sort order.  I pass this value to the setImageSortOrder() function which makes an AJAX call to my remote proxy which will update the values in the database.

<cffunction name="setImageSortOrder" access="remote" returntype="struct" returnFormat="JSON">
 <cfargument name="sortOrder" type="string" required="yes"/>
  <cftry>
  <cfscript>
   var response = structNew();
   var imageObj = "";
   var sortCnt = listLen(arguments.sortOrder);
   var i = 0;

   for(i=1; i lte sortCnt; i++){
    imageObj = application.transfer.get("images", listGetAt(arguments.sortOrder, i));
    imageObj.setSortOrder(i);
    application.transfer.save(imageObj);
   }
   response['svrStatus'] = "0";
   response['svrMessage'] = "Video sort order updated successfully.";
  </cfscript>
  <cfcatch type="any">
   <cfscript>
    response['svrStatus'] = "-1";
    response['svrMessage'] = cfcatch.Message;
   </cfscript>
  </cfcatch>
 </cftry>
 <cfreturn response/>
</cffunction>

The function setting the sort order in the database is pretty straight forward.  I am using Transfer ORM for my database interactions, but it would be easy enough to covert the example into a query.  Since the jQuery function is passing a list of the image id’s in the correct order I loop through the list and set the sort order of each image to its index in the list.  No need to worry about items having the same sort order and displaying incorrectly.  It is pretty much fool proof.

Also of note, this code makes use of the $.parseJSON() function added in jQuery 1.4.1.  No more having to worry about whether or not a browser can support JSON.parse().  This in itself is a good reason to switch to a newer version of jQuery if you have not already.

Simple chat app using jQuery and ColdFusion


Way back in the day I was working on a jQuery game I called CFMud. As the name alludes to, it was to be a multi-user dungeon that would allow you to roam around “the world” and gain experience. One of the key components of the UI was the chat interface. I was just getting in to jQuery and ajax at the time so I never really worked all the kinks out of the system then life intervened and I have never picked the project back up (sorry Josh 😛 ).

Yesterday a co-worker mentioned that she was looking for a simple little chat application so I figured I would give it a shot. I threw together an extremely quick, extremely basic little demo. The application itself is comprised primarily of three pieces: the index.cfm file for the user interface, an application-scoped CFC that acts as the “chat server”. and finally an ajaxProxy.cfc that acts as a facade for remote calls.

To start, just call up the index.cfm you will need to enter a user name to use in the chat window. Enter a name and click on the “Set Name” button. This will then bring up the main chat window and currently updates every five seconds. I have tested it in the main browsers; FireFox, Chrome, and with much swearing, Internet Explorer. For some reason, IE likes to cache remote calls to CFC’s as well. Go figure.

The “chat server” is actually just an array stored in server memory.  Currently it is configured to store 100 messages, it will purge out the oldest message if the current chat log goes above this.  Also, to keep down the amount of data being transferred from the server, each client remembers the message id it received.  When the next call to get messages occurs, it only gets new messages.

If you would like to check it out, I have it available for download here: http://dl.dropbox.com/u/868773/chat.zip.  It is hardly worthy of an RIAForge project, but maybe I will play around with it some more later on.

To see it in action: http://demos.kisdigital.com

Debugging ajax calls with ColdFusion part deux


A few days ago I wrote a post about debugging ajax calls with ColdFusion.  My approach was to basically write every remote method within a cftry/cfcatch block.  Ray suggested I use the new onCFCRequest() method built in to CF9, unfortunately I am not running CF9.

However it did get me to thinking about ways I could create a wrapper for handling errors in my remote methods.  I tried several different ways based on onRequestStart and onRequest, but nothing really came of it.  Next I tried setting up my error handling strangely enough in the onError method.  This method works, but I am still not quite sure how I feel about it.  Here is what I worked out:

<cffunction name="onError" access="public" returntype="void" output="true">
 <cfargument name="Exception" type="any" required="true"/>
 <cfargument name="EventName" type="string" required="false" default="" />
 <cfset var result = structNew()/>
 <cfif arrayLen(arguments.exception.tagContext) and lcase(listLast(listLast(arguments.exception.tagContext[1].template, '/'), '.')) EQ "cfc">
  <cfset result['svrStatus'] = "-1"/>
  <cfset result['svrMessage'] = arguments.exception.message/>
  <cfoutput>#serializeJSON(result)#</cfoutput>
 <cfelse>
  <cfdump var="#arguments#"/>
 </cfif>
</cffunction>

This actually works out fairly well. When fired, the onError method checks to see if it was called from a CFC or a CFM page.  If it was called from a CFC it sets up the default return values for an error and then returns the data as a JSON object.  What I do not like about this method is, every CFC returns the dump as a JSON object, whether it is a remote remote method or not.  I would rather have a hard error if an application-scoped CFC failed, but I guess we cannot have it all.  I would have to say it makes a lot more sense to handle things this way instead of writing every CFC method in a try/catch block.

Debugging ajax calls with ColdFusion


I was debugging a video conversion application this afternoon that relies heavily on ajax to move the user along in the conversion process.  After each step in the process another ajax call is fired off to continue to the next step.  When things are working as expected it is a beautiful thing, when it does not things tend to get interesting.  I had some code inside my CFC and the server was throwing a hard error, needless to say my response codes were never returned back to my ajax call from the remote method.  The user would end up staring at a processing screen that would never go away.

To get around this I standardized all my ajax calls.  No matter what information I am expecting back from my CFC, I always pass back a svrStatus variable and a svrMessage variable.  Server status is the variable I check to make sure everything went as planned, if it is not 0 I know there was a problem.  The svrMessage variable is what I want the users to see so they are not scratching their heads wondering what is going on.

I whipped up a quick demonstration.  It is definitely nothing new, but I thought maybe it would help someone out.

Our basic page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Ajax debug</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
</head>

<body>

<div id="results" />

<script type="text/javascript">
 $(document).ready(function(){

  $res = $('#results');

  for(var i = 0; i < 3; i++){
   $.ajax({
    url    : 'remoteProxy.cfc',
    data   : {
    method : 'remoteFunction',
    pass   : i
   },
   async   : false,
   success : function(data) {
    incoming = JSON.parse(data);
    $res.append( '<strong>Pass ' + i + ':</strong> ' + incoming.svrMessage + '<br/>');
   }
  });
 }

 });
</script>
</body>
</html>

remoteProxy.cfc:

<cfcomponent output="no">

 <cffunction name="remoteFunction" access="remote" returntype="struct" returnFormat="JSON">
  <cfargument name="pass" type="numeric" required="yes"/>
  <cfset var respsone = structNew()/>
  <cftry>
   <cfset response['svrStatus'] = "0"/>
   <cfset response['svrMessage'] = "Method completed successfully"/>

   <cfif arguments.pass eq 1>
    <cfthrow message="Doh!  Something broke!"/>
   </cfif>
   <cfcatch type="any">
    <cfset response['svrStatus'] = "-1"/>
    <cfset response['svrMessage'] = cfcatch.Message/>    
   </cfcatch>
  </cftry>
  <cfreturn response>
 </cffunction>

</cfcomponent>

There is nothing too complicated going on here.  The web page makes a call to our remote CFC and in this case it is hard-coded to throw an error on the second pass, simulating an error condition.  The whole method is wrapped in a CFTRY block so if something did occur while the method was executing it would fire the CFCATCH block.  The CFCATCH block sets the svrStatus to -1 letting me know that there was an error and it also sets the svrMessage variable to CFCATCH.Message so we will know the root cause of the problem.

The web page itself is not that complicated either.  After making the ajax call we parse the data returned into JSON.  At this time we could simply look at the svrStatus variable see if we have an error and display a message if so.

Any other tips and tricks for debugging ajax calls?

WhosOnCFC 2.2.5


I have just uploaded WhosOnCFC 2.2.5 to RIAForge.  I am not on my main computer so I cannot update the source on GitHub.

It has been quite some time since my last update the the core component that handles user tracking; most of the recent changes have involved updated viewers and the like.  In this update I have basically rewritten the WhosOnPageTracker() which is the main method that ties everything together.  Yesterday, Kevin M., a user pinged me to let me know he was interested in using WhosOnCFC to track users on his web site, but when he installed it he noticed a substantial decrease in speed.   He asked if I could make any suggestions to make things run a bit faster.

Looking through the code I saw quite a few spots where things could definitely be optimized.  Here is a quick run-down of what was done:

  • ignoreDomains, ignoreIPs, and botList check is now run on the first hit on the site.  Version prior to 2.2.5 checked these on every request which slowed down execution time, especially if you have a long botList.  WhosOnCFC still checks for ignorePages on every request, but generally this list is very small.
  • When looping over lists I set a variable to the list length instead of evaluating it in the for loop.  The speed increase is probably marginal but every little bit counts.
  • Reorganized the function to streamline execution.
  • Added execution time (execTime) to the user data so you can see how long WhosOnPageTracker() took for each user.
  • Added the WhosOn Dump page back in.
  • Added a link to jView (jQuery viewer) on the main screen.  Looks like I forgot that in the last release.

Check it out and please let me know if you encounter any errors.

Using jQuery to scroll to the bottom of a div, revised


I am a firm believer that every day you should learn something new.  In this particular case, I learned something new quite some time ago and just never applied it to my code samples.  This is something I really want to pay more attention to because I do not want be a purveyor of misinformation.

Nick commented on this original post that once the animation began he could not scroll back up to the top of the post.  Looking into the problem I immediately noticed what was happening, the scrollHeight attribute was not taking into account the height of the scrolling div.  If the scrolling div has a height of 100px initially and enough text has been entered to scroll it, it may have a scrollHeight of 120px.  Moving down to the bottom of the div, the scrollTop would be 20px which is the scrollHeight – $(‘#div’).height().  Adjusting for that in the jQuery code will make it function as expected.

The corrected code is below:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>jQuery Scroller</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
</head>
<body>
<div id="myDiv" style="width: 500px; height: 100px; overflow: auto; border: 1px solid black;"></div>
<input type="text" id="myTextInput" />
<input type="button" value="Send" id="postButton" />
<div id="info"/>
<script type="text/javascript">
 $(document).ready(function(){
  $('#postButton').click(function(){
   $('#myDiv').append($('#myTextInput').val() + '<br/>');
   $('#myTextInput').val('');
   $("#myDiv").animate({ scrollTop: $("#myDiv").attr("scrollHeight") - $('#myDiv').height() }, 3000);
  });
 });
</script>
</body>
</html>

Update 9/5/2011:
As JP points out in the comments below, this may need to be modified to work with jQuery 1.6.1
http://stackoverflow.com/questions/6194837/somediv-attrscrollheight-not-working-in-jquery-1-6-1

Day two with FW/1


Playing around with FW/1 today I decided to take a look at the differences at how services differ from working with domain objects.  Skimming through the FW/1 documentation I did notice the framework has a built in function to populate a bean, namely the fw.populate() function.  Calling a bean (an object with getter/setter methods) is not much more involved in calling a service.  First, you create an instance of your bean.  Then call populate() method passing rc keys that you would like to set and the populate function will find the setX method in the bean and set the appropriate value.

I have an extremely simple application I have setup that has two views.  The default view is just a form that gets posted to the main.submit action which is just cfdumping the rc scope.

The sample “bean” setup for testing purposes:

<cfcomponent>
 <cffunction name="init">
  <cfscript>
   variables.instance = structNew();
   instance['fw'] = arguments.fw;
  </cfscript>
 </cffunction>
 <cffunction name="setFirstname">
  <cfscript>
   instance['firstName'] = arguments.firstName;
  </cfscript>
 </cffunction>
 <cffunction name="setLastname">
  <cfscript>
   instance['lastName'] = arguments.lastName;
  </cfscript>
 </cffunction>
 <cffunction name="saveUser" returntype="struct">
  <cfscript>
   var response = structNew();
   response['message'] = "Saving user: " & instance.firstName & " " & instance.lastName;
  </cfscript>
  <cfreturn response/>
 </cffunction>
</cfcomponent>

Nothing too complicated going on here, just a basic init method to setup the instance variables, two setter methods and finally a method for “saving” our user information.  The controller method for handing the submit is as follows:

<cffunction name="submit">
 <cfargument name="rc" type="struct"/>
 <cfscript>
  var userObj = createObject("component", "model.user");
  variables.fw.populate(userObj, "firstName,lastName");
  structAppend(rc, userObj.saveUser());
 </cfscript>
</cffunction>

This creates the user object, populates the values and then saves the user and appending the results into the rc scope.  Services are a little easier to implement as they have a one line invocation.  I am still trying to figure out when it would be best to use a service to move data in and out of the application as opposed to creating a bean in the model.  I would imagine the ultimate answer would be whatever works best for you, but I can see where services could be advantageous as well.  I welcome any comments.

Day one with FW/1


Aside from fun jQuery projects, I have really been meaning to pickup Framework One and really check it out.  After using XML controllers for a while not having to worry about defining all the behaviors is nice.  Best of all, it is really intuitive to work with.  After installing the FW/1 package I immediately deleted all the example applications and the introduction folder, that is just how I roll.

Then I started with a minimum framework: a default layout, a default view and a default controller.  I had everything up and running quickly.  Next I decided to write a sample application.  Nothing too fancy, enter a first and last name in a form and a submit button.  I could have went with the typical “Hello World” application, but I am kinda finicky about form processing.  The framework I use does not automatically append the form scope into the request context. Generally I default all my form values for my view in the controller and then roll the form scope in,  overwriting my defaults with any values that might be in the form scope.  If validation fails, I can always fall back to my submission page and have form values defaulted.  It did take me a little while to adjust to FW/1’s way of doing things, but it was not a large adjustment.

I also like how FW/1 allows for a startItem, endItem for controller methods.  It allows you to easily handle validation before the main method gets called.  I suppose the validation could be handled in the main method but I find it a little clearer when I break the logic into distinct pieces.

Finally I setup a simple “user” service.  In this case, it was just to return a string letting me know the user’s first and last name that was entered on the form.  The whole thing came together in less than an hour from start to finish, including time spent looking over the documentation when I did not fully understand something.

Although there are several aspects of the framework that will require me to change the way I handle certain things, I can see the framework is flexible and extremely easy to use.  I will definitely be using FW/1 in some future projects.

jQuery source code up


For those of you who were interested in getting the source for my demo app, it can be found here:

http://dl.dropbox.com/u/868773/030510.rar