Quality Reads

Sunday, August 26, 2007

Client-Side Error Logging

If you've done any amount of server-side scripting (CF, PHP, RoR, .NET, etc.), you already know the benefits of error handling. It allows you to capture unusual, sometimes unforeseen, errors that can occur in your script and handle them gracefully so your users get proper feedback on what just happened. No matter how great a programmer your are, something out of your control will break your code. Its your responsibility to build in a safety net so your users don't break their neck when they step off the correct path.

Since most developers would generally agree with my sentiment, why don't you see AJAX applications properly handling and logging client-side errors. The application that sticks out for me is Gmail. At least once a week I'll see a host of non-fatal errors pop up. Why? Can't errors that bubble up to the application scope be captured? Yes.

Be better than Google. Here's how:


var ErrorLogger = Class.create();
ErrorLogger.prototype = {
initialize: function(url, opts){
this.url = url;
this.active = true;
this.opts = opts;
window.onerror = this.onError.bind(this);
},
onError: function(msg, URI, line){
try{
if(this.active){
var body = 'URI=' + escape(URI) + '&line=' + line + '&msg=' + escape(msg) + '&brw=' + escape(Object.toJSON(Prototype.Browser)) + '&pv=' + escape(Prototype.Version);
if('onError' in this.opts)this.opts.onError.apply(this, arguments);
var opts = {
onSuccess : this.onSuccess.bind(this),
onFailure : this.onComplete,
postBody : body
};
new Ajax.Request(this.url, opts);
}
}catch(e){
this.onFailure();
}
return true;
},
onSuccess: function(){
if('onSuccess' in this.opts)this.opts.onSuccess.apply(this, arguments);
},
onFailure: function(){
this.active = false;
if('onFailure' in this.opts)this.opts.onFailure.apply(this, arguments);
}
};


What's it doing? Well, first off this is a Prototype Class so you need to include prototype.js in order to use it. Here's the breakdown:
  1. The window.onerror event is set to call ErrorLogger.onError. When an unhandled error bubbles up to the window scope, onError will be called to handle it.
  2. Three arguments are passed into the onError function ( msg, URI, line ) and an Ajax request with some additional browser information is constructed and posted to the URL specified in the constructor.
  3. Lastly, the onError function returns true so the browser knows to disregard the error.
Can you just show the syntax for use? No prob.



<html>
<head>
<title>Logger Test</title>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript" src="ErrorLogger.js"></script>
<script type="text/javascript">
var er;
var init = function(){
er = new ErrorLogger('TestResult.html',
{
onSuccess : function()
{
$('btn').setStyle({'backgroundColor' : 'red'});
},
onError : function()
{
alert('An error occurred on the page. Run for your life!');
}
});
};
</script>
</head>
<body onload="init()">
<button id="btn" onclick="nonExistentFunction()">Throw Error</button>
</body>
</html>

When you click on the button labeled "Throw Error", the ErrorLogger class will gracefully handle the error and post some useful debug information to your server. From there you can do whatever you want. Personally, I just toss it in a log file that I monitor. All that in less than 1KB, not bad.

I've tested it on IE6/7 and FF2. Its definitely not production ready quite yet but I'll throw an update up with my final version in a day or two.

Cheers,
Todd

Thursday, August 23, 2007

Add A Blog Search Feed to Google Reader

If you're like me your Google Reader RSS Feed Reader is filled with great blogs site ( Lifehacker, Mashable, Techmeme, etc.) While I'm not ditching any of my favorite web filters, sometimes I want to get my information direct from the source, or just another source. Here's a handy trick to keep you ahead of the curve.

Go to Google's Blog Search. Note the RSS/ATOM links on the side. Toss those in your Feed Reader and you're ready to go.

Oh, did I say this was difficult and time consuming? Definitely not.

Very useful? You know it.

Cheers,
Todd

PS - Depending on the search query, you may run into some spam. Play around w/ the advanced search options to get things working right.

Tuesday, August 21, 2007

Got Some Free Time?

Great list of top notch Flash sites: http://flashprayer.blogspot.com/

Steer clear during work hours...lest your lose your whole day (which may or may not be a good thing).

:)

Cheers,
Todd

PS - AIR Tour on Friday in Boston.

Monday, August 13, 2007

Designer//Slash//Model



Simply awesome.


Enjoy!


Check out the source.

Tuesday, August 07, 2007

Code Highlighting


If you're in the market for a code highlighter, I just ran into a good one that handles PHP, Java, Javascript, Perl, SQL, HTML, and CSS. To rock this on your blog/website, all you need to do is include the javascript source in you page header and add a textarea tag like so:


<textarea id="myCpWindow" class="codepress javascript linenumbers-off">
// your code here
</textarea>

Notice the language to be highlighted is included in the class declaration. There's a couple other useful features (such as copying code to the user's clipboard) that can be found here. To make life even easier, I've written up a quick ColdFusion custom tag to create the textarea declaration and include the appropriate content (sorry, non-CF users). Here's the code:


<CFSETTING enablecfoutputonly="true">
<CFIF ThisTag.ExecutionMode eq "Start">
<cfparam name="attributes.language" type="string"><!--- Language to Highlight --->
<cfparam name="attributes.file" type="string"><!--- relative file path --->
<cfparam name="attributes.readOnly" type="boolean" default="false">
<cfparam name="attributes.lineNumbers" type="boolean" default="true">
<cfparam name="attributes.autoComplete" type="boolean" default="false">

<cfset attriList = "language,file,readOnly,lineNumbers,autoComplete">
<cfset attriKey = StructKeyList(attributes)>

<CFTRY>
<CFFILE action="read"
file="#ExpandPath(attributes.file)#"
variable="fileResult">

<CFCATCH type="any">
<CFTHROW detail="Double check the file location. The error occurred trying reading the specified filed.">
</CFCATCH>
</CFTRY>

<CFOUTPUT><textarea class='codepress</CFOUTPUT>
<CFIF attributes.readOnly><CFOUTPUT> readonly-on </CFOUTPUT></CFIF>
<CFIF NOT attributes.lineNumbers><CFOUTPUT> linenumbers-off </CFOUTPUT></CFIF>
<CFIF NOT attributes.autoComplete><CFOUTPUT> autocomplete-off </CFOUTPUT></CFIF>
<CFOUTPUT>'</CFOUTPUT>
<CFLOOP from="1" to="#ListLen(attriKey)#" index="aIndex">
<CFIF NOT ListFindNoCase(attriList, ListGetAt(attriKey,aIndex))>
<CFOUTPUT> #ListGetAt(attriKey,aIndex)#='#attributes[ListGetAt(attriKey,aIndex)]#' </CFOUTPUT>
</CFIF>
</CFLOOP>
<CFOUTPUT>>#fileResult#</textarea></CFOUTPUT>

</CFIF>
<CFSETTING enablecfoutputonly="false">


You can use the tag like so:


<cf_syntaxify <-- whatever you name the tag -->
language="ColdFusion"
file="test.cfm"
id="testID" />


Any attribute you define in the tag that is not param'ed at the top of the custom tag will be passed onto the <textarea> tag. Note the id attribute in the above example.

There are a couple other code highlighters available, most notably the one released by Google, but the simplicity of CodePress is immediately apparent once you check out the docs. Let me know if you run into any problems w/ the custom tag (I haven't tested it extensively yet).

Cheers,
Todd

Friday, August 03, 2007

The Cross-Over Point AKA FU Money

Hackzine has a great post on money management. They discuss the often sought after (little talked about) situation known as FU Money. The point where you earn more in interest on your investments than you spend in living expenses. Its a really simple concept that most people don't think about until they're old and gray (Hackzine begs to differ).

I've come up with a new response to "What's fueling this whole Web 2.0 frenzy?" FU Money, obviously!

Have a great weekend.

Cheers,
Todd

CF8 Logos...


Rey Bango just posted a bunch of CF8 logos. If you're working on a ColdFusion 8 powered project (I know I am), then you may want to snag one of these. I'm hoping to have some info on my *hush* *hush* AIR derby project by Monday.

Cheers,
Todd

Saturday, July 28, 2007

Ning :: Full Source Access

API's are great from a security standpoint but creativity takes a backseat in the process. What happens when you come up with an idea/concept that the original developer never thought of? With a standard web service, nothing. With Ning, you can write your own API .

http://www.ning.com/help/faq-developers.html

Perhaps I'm blurring the truth a little bit. Ning doesn't really provide an "API" like other web applications, they've created an application layer that ANY community user can edit. Their API is exposed via PHP, a very untraditional approach to open development that begs the question: if Ning can do it, why can't you? or me? or Google?

If you could provide all your company's information/resources in a system like Ning, would you?

Thursday, July 26, 2007

Drag & Drop in AIR

I just ran into a great article by Alastair outlining the use of Native Drag & Drop functionality in AIR (w/ a little RoR mixin). The documentation for Native D&D is very spotty, there are more mistakes then anything else. So, if you haven't learned the hard way yet, let me make your life a little easier: http://blog.vixiom.com/2007/06/29/merb-on-air-drag-and-drop-multiple-file-upload/

Here's a simple test application you can use to get started:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init(event)">

<mx:Script>
<![CDATA[
import mx.events.DragEvent;
import flash.desktop.DragManager;
import flash.desktop.DragActions;
import flash.events.NativeDragEvent;
import flash.desktop.TransferableFormats;
import flash.filesystem.File;

private function init(e:Event):void
{
//add the event handlers
this.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onEnter);
dropPanel.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDrop);
}

public function onEnter(event:NativeDragEvent):void
{
//Check to see if the drag item is the right format
if(event.transferable.hasFormat(TransferableFormats.FILE_LIST_FORMAT))
{
DragManager.acceptDragDrop(dropPanel);
}
}

public function onDrop(event:NativeDragEvent):void{
trace("dropped");
// Cast the drag & drop data as an array
var files:Array = event.clipboard.dataForFormat(flash.desktop.ClipboardFormats.FILE_LIST_FORMAT) as Array;
for each (var f:File in files)
{
// check out the file URL
trace(f.url);
}
}
]]>
</mx:Script>

<mx:Panel id="dropPanel"
top="10"
left="10"
height="100"
width="100"
title="Drop Files Here"
backgroundColor="#FFF">


</mx:Panel>

</mx:WindowedApplication>



Also worth a read: http://coenraets.org/blog/2007/06/air-to-desktop-drag-and-drop-two-simple-utility-classes/

I'll let you know why I'm reading up on D&D in a couple days :)

Cheers,
Todd

***Updated for AIR Beta 2***

Sunday, July 22, 2007

CT-CFUG Presentation

Just a quick heads up. JB and I are giving a presentation on Adobe AIR at the Connecticut ColdFusion User Group on Tuesday night (July 24th, 2007). If you're in the area and up on technology, you might want to think about stopping in for our jam session. I hear there's going to be some free Adobe swag in it.

Cheers,
Todd

Thursday, July 19, 2007

Nokia Didn't See This Use Case Coming

Adam Prestin pointed me to this crazy picture earlier today and I had to re-post it.

No this phone wasn't modded out by some MAKE afficionado. Its actually a terrorist/insurgent/{insert political spin here} bomb timer from Iraqi, apparently a very common one too.

Its a good thing they missed the call.

(Thanks Adam)

Bubblemark Animation Test Confirms It

Alexey Gavrilov compiled a nice set of performance tests for all the major RIA development platforms (Flex, Silverlight, JavaFX, AJAX). Flex (aka Flash) performed like a champ in the browser arena, which isn't horribly surprising considering how long its been around. Both Flex & Actionscript are up to, or on the way to, version 3.0 and Flash is rocking numero nine. I do have to admit that I'm impressed by Microsoft's progress on Silverlight. After the Flex 2.0 release, they must have recognized the enterprise level development capabilities that Flex brought to the table and jumped right on the issue. If they had taken a look at the crazy flash apps Pier was doing back in 2001, they might have foreseen this RIA trend long in advance.

Here are a couple surprises:
  • JavaFX is 4.4 times slower than Flash.
  • Firefox + Silverlight (CLR) — 99 fps
  • Flex and AIR peform at the same speed (as I previous reported)
While it looks like Silverlight is going to be a strong contender from a performance standpoint, I'm going to hold my opinion until they release the first non-Windows version.

Check out the complete results here.

PS- I also enjoyed reading Alexey's initial experiences w/ Flex. I distinctly remember dealing with each one of the issues he mentioned. Good times.

Thursday, July 12, 2007

There's an Advanced DataGrid!?!

I guess in all the excitement surrounding Adobe AIR, I missed out on a huge feature available in Flex 3, the Advanced DataGrid.


If you're working w/ alot of data in your Flex/AIR applications like I am, you need to check this component out. It may just save your life/sanity.

Here are some of the main features:
  • Multi-column sorting
  • Tree View (basically a tree component mashed into a DataGrid)
  • Cell Formatting
  • Summary Collections (allows you to perform a calcuation on the DataGrid columns and output a result)
  • Column Grouping
Seriously, half the work I'm doing right now could be handled with this component. Check out some of the details here or the Flex 3 docs here. This is the type of great stuff I expect from the Flex team. Now if we could only get the CS3-to-Flex skinning integration to work properly everything would be brilliant.

Cheers,
Todd

Tuesday, July 10, 2007

Performance in AIR


If you're like me, you might have thought that Adobe AIR (formerly Apollo) would provide a more powerful computing platform for desktop RIA development. Well, you'd be wrong. After running a number of performance tests ranging from long-running financial calculations to CPU intensive UI effects, AIR and Flex executed at approximately the same speed (~2% in favor of Flex -- insignificant).

While I must profess a little disappointment, I don't think its a huge downside of the platform since Flash provides so many other core competencies. However, you need to take it into account whenever you are about to perform a lengthy IO operation. The user interface will freeze up when saving/generating multiple files. At Pier, we've been popping up a modal window just prior to all IO ops to keep the user from trying to interact w/ the UI during the freeze.

My suggestion is take the time to setup you IO framework so you don't have to code a custom modal on every operation.

Cheers,
Todd

Saturday, June 30, 2007

Execution Times in Apollo vs. Flash

I just wrapped up a 12hrs. coding session at work. We're creating an application that provides ROI (return on investment) calculations to prospective technology clients. Its standard practice these days to sell software based on value even though selling on features is a heck of a lot more fun, which is where the ROI calc. comes in handy.

Behind the calculator's UI, there are a plethora of events being generated to update the key benefits used by the buyer. As we've continued to work on larger calculators (more benefits), the processing time in AIR has lagged even when the Flex seems to run unhindered. So tonight I did a little testing to see how significant the difference was, I was shocked to learn that AIR executed the same functions as the Flex application at a speed ~ 54% slower. The "native" application is running slower?!? Even more bizarre, the execution time goes back to normal when I load a secondary SWF inside the AIR application to handle the calculations. Very weird and disheartening to say the least. I don't have the tests at home otherwise I'd post them but if anyone has a reason for this discrepancy. Do tell.

You can expect the source for my tests tomorrow. Until then, you might want to think about creating a wrapper for calling functions w/in a SWFLoader. Here's a useful article about how to make calls from the child app. to the parent.

Cheers,
Todd

Thursday, June 21, 2007

Living in Boston...

I guess I haven't taken the time to make mention of my new job (or write a post for that matter). I packed up my bags about a week ago and made the long haul up to Boston, actually it was three short hauls. Besides the psycho that yelled at me for "borrowing" her parking space while I moved my furniture into my apartment, everyone I've met has been great. A trend I'm sure can't last forever but I'm enjoying it for now.


My new company, Pier Inc., creates Rich Internet Applications using Flash (Flex, AIR). We've got an impressive list of Fortune five clients and skunkworks to boot! I've got to get back to work for now but you can expect some great Flex/AIR tutorials to follow.

Cheers,
Todd

Thursday, June 07, 2007

Managing Markers in Google Maps



I just spent the last four hours trying to coerce Firefox into properly displaying 600 markers on Google Maps (using GMarkerManager). In the end, I came up with the same response "too much recursion", FF's words not mine.

My original process was to create the map, so users have something to look at, then make an XHR call for all the markers I wanted to place on it. Logical, I thought at the time. For some reason, making the XHR call after the map is instantiated seems to freeze up my computer (dual-core Mac). A very bad sign considering the average user doesn't have the power setup that I do. I tweaked a couple functions to improve performance but nothing seemed to work.

Then I found this web page (and demo) which provides a VERY simple solution. Load the data up front then initialize the map. Presto, GMarkerManager kicks in and everything works perfectly. If you don't feel this answer is satisfying...I hear you. Frankly, it doesn't sit well with me either but it works and the load time isn't horrible.

Hopefully, someone will see this post and avoid getting brutalized like I did.

Cheers,
Todd

Wednesday, June 06, 2007

Is Google Expanding Too Quickly?

I want to say no. Really.
I want to believe that Google is the company of the future. Nimble, smart, and opportunistic. Shifting resources on a dime to push whatever technology is on the horizon.
Then I get a reality check:
You'll notice something strange in the top left corner of the page. Yep, thats some Javascript for their Google Analytics account. Upon further inspection, I discovered they forgot the closing script tag. Pretty sloppy. I'm not saying I haven't thrown pages up before they were ready or that I don't make mistakes. But come on. Someone should have QC'ed the page and noticed that.
I guess the future isn't today.

Friday, June 01, 2007

Updated: Custom Event System for Prototype

After using my original Event System for a little while I realize that I structured the original Classes all wrong. For some reason, I though it was a good idea to pass the event dispatching responsibilities off on the Event object itself. Bad idea. So I updated the code, all 2K of it.

Here's the update:


/**
* @author toddcullen
*/
CustomEvent = {};
CustomEvent.Events = {};
CustomEvent.Events.Base = Class.create();
CustomEvent.Events.Base.prototype = {
initialize : function(){
this.type = "CustomEvent.Events.Base";
}
}
CustomEvent.EventController = Class.create();
CustomEvent.EventController.prototype = {
initialize: function(){
this.listeners = $A([]);
},
addEventListener: function(n, f){
this.listeners.push({name: n, callback: f});
},
removeEventListener: function(n, f){
this.listeners = this.listeners.without({name: n, callback: f});
},
dispatchEvent: function(n, e){
for(var x=0; x<this.listeners.length; x++){
if(this.listeners[x].name == n){
this.listeners[x].callback(e);
}
}
}
}
var EventController = new CustomEvent.EventController();


You can create any type of Event you'd like. Its just an object you pass from the event target to the listener. Here's an example usage where I created a DataEvent Event Class:


/*
* Data Event
*/
CustomEvent.Events.DataEvent = Class.create();
CustomEvent.Events.DataEvent.prototype = {
initialize : function(data){
this.type = "CustomEvent.Events.DataEvent";
this.data = $H(data);
}
};

Event.observe(window, "load", function(){
EventController.addEventListener("BasicEvent", testListener);
EventController.addEventListener("DataEvent", testListener);

Event.observe('basic', 'click', dispatchBasic);
Event.observe('data', 'click', dispatchData);
});

function dispatchBasic(){
var event = new CustomEvent.Events.Base();
EventController.dispatchEvent("BasicEvent", event);
}
function dispatchData(){
var event = new CustomEvent.Events.DataEvent({info : 'HELLO WORLD!'});
EventController.dispatchEvent("DataEvent", event);
}
function testListener(event){
alert("event type:"+event.type);
if(event.type == "CustomEvent.Events.DataEvent"){
alert('event data: ' + event.data.inspect());
}
}


The HTML is simply an anchor with an id of "data" and an anchor with an idea of "basic". This system is really useful for an HTML UI that has to be flexible. Minimize/eliminates the need for objects to know about each other. They only need to "know" about the EventController.

Snag the full demo here.

Cheers,
Todd

Apollo/Gears Integration

Check out this press release on Apollo/Gears integration.

Gotta run!

Seriously, I have to leave this time.