Adobe recently put out a small poll to find out how interested the ColdFusion development community would be in an officially supported IDE. Someone posted it to the CF-Talk list and it generated the predictable, well-worn debate that happens every time someone mentions the acronym. In my experiences over the years, I have come to the following opinion.
People don’t switch development tools for two reasons.
The majority of people that I have worked with over the years don’t switch because their current tool, “gets the job done”. They have structured their workflow in such a way to, in their minds, maximize their usage of the tool. If they can do their job right now with no impediments, and get everything done on time, then what’s the point in trying something new?
Perfectly valid point. If you have no interest in learning a new tool for development, why the heck should you?
Unfortunately, there is the small, but vociferous group of developers that DO try out something new, find one or two roadblocks and then rant and rave about how bad the tool is. For example, I worked with a fellow that was still using CF Studio in 2006. It worked for him. He saw my Eclipse installation and thought it looked cool because of all the plugins I was using. He decided to give it a try, so he installed Eclipse and started downloading the list of plugins that I had provided to him.
After only 24 (3 days) hours of experience with Eclipse, he declared that it was the worst tool he had ever used. He was frustrated with the unfamiliar UI, the endless options that could be configured, the new key mappings that he was forced to learn, and lack of features. I spent a day trying to show him how easily these perceived hurdles could be overcome, but he had already made up his mind.
Many of his complaints are mirrored in some of the comments I hear people say about any tool that they “hate”.
I’m one of a, apparently, small group of people who actually take the time to work with a tool for weeks, or even months before I make up my mind about it. I’ve tried a plethora of tools over the years – starting with CF Studio, I’ve tried HomeSite, Dreamweaver, Eclipse, Code Chameleon, and a few others that I’ve forgotten about. I’d pore over every feature of these tools, trying to squeeze every ounce of productivity that I could out of them. I’d install plugins, extensions, helper tools, wizards, you name it. I’d never say die in the hopes that I have finally found a tool that fits how I want to work.
At this point in time, Eclipse is that tool. I’m able to get to it work the way I want, instead of the other way around much more so than anything I’ve used in the past. Admittedly, I do a lot more than ColdFusion development, so my opinion probably isn’t worth as much as someone who does only that.
I have the following features installed.
I use all of the plugins every day and having them all contained within one IDE is perfect for how I work. There are people within my company right now that would absolutely hate Eclipse. It doesn’t fit their work style or their (simplistic?) needs.
The point I’m clumsily trying to make is that before you degrade into saying what you don’t like about Tool X, try stating humbly what you do like about Tool Y and why it’s a great idea for a new IDE. Brush that chip off of your shoulder and remember that there is always someone smarter/wittier/more productive/more inventive/more creative than you.
This is a riot. I don’t find myself laughing out loud very often at something I read on the Web, but this idiot still has me chuckling.
If you build web applications, at some point you have built a form that had dynamically named form elements. You loop through a CFQUERY and build some input fields that’s a combination of a string ID and a ColdFusion variable (usually currentRow).
<cfloop query="aSimpleQuery"><cfoutput>
<input type="hidden" id="columnOne_#currentRow#" value="#columnOneValue#">
<input type="hidden" id="columnTwo_#currentRow#" value="#columnTwoValue#">
</cfoutput></cfloop>
I’ve seen a lot of developers struggle with how to then easily identify these fields via Javascript in order to modify/read their values, change the styles, or build a complex data structure from the values.
The power of jQuery’s DOM traverser makes it a snap. It’s just a RegEx expression to find the elements you need. You can also use the built-in function each() for looping over the elements instead of making a for loop.
$("input[id^='columnOne_']").each(function(i) {
if( this.val() != 0 ) {
// Do something really cool here
}
}
At this point, you may be thinking, “Yeah, but what about checkboxes, eh?!? And what if they have name attributes, but no ID??” No problem, just use an even more advanced jQuery selector and the code remains the same.
$("input[name^='columnOne_']:checkbox").each(function(i) {
if( this.val() != 0 ) {
// Do something really cool here
}
}
Take a look-see at all of the jQuery selectors and you’ll see how easy they make it to find DOM elements.
In part one of this two-part article, I talked about some of the considerations you needed to take into account when embarking on your VoIP path: how to pick a vendor, what type of service, determining your needs, etc.
In this article, I’m going to cover the meat of what had to be done to get it up and running. The process itself was not hard, however, there were several kinks that had to be ironed out before it was running smoothly.
We, being the happy-go-lucky geeks that we are, decided to buy our own PBX hardware, with embedded Asterisk software. We found the Ipeya SIPS listed on eBay where they sell their IP PBX as a package with 5, 10, or 20 phones included. The price couldn’t be beat when we compared with buying the phone and PBX seperate from another vendor.
Unfortunately, the software on the Ipeya is fairly weak and unintuitive. It’s perfectly suited for personal or home office usage, but trying to squeeze the functionality we need for a small business is a lesson in patience. It’s a custom UI built on top of the Astersk system for VoIP, and provides an adequate interface to the major sections you need to get yourself up and running.
One of the big problems right now is that out-of-the-box solutions are few and far between and none I have seen to date are mature enough to be a sleek, sophisticated solution for an Asterisk UI. VoIP is a new technology and the software support has a long way to go to catch up.
Before buying your software, be sure to insist on a thorough test drive with the vendor to see how easy it is to navigate and make changes to your system.
These three Javascript libraries are at the core of how I build fun, well-organized, and easy to maintain web applications. Each one serves a different purpose:
I’ll start off with a nice example of how these three libraries play well together and can make an otherwise complicated action very simple and easy to write.
This code has three pieces of functionality. I’m using the jQuery Datepicker on my page, and when the user selects a date from the calendar, I want the database to immediately be updated with the date chosen. If the method fails, throw up a nice message box with a failure message and ask the user to try again.
// Use jQuery to bind the dataSelected event of the jQuery DatePicker
$('#requestDueDate').bind('dateSelected',function(e, selectedDate, $td){
// Use ajaxCFC to call the updateRequestDates() function of the Request component
$.AjaxCFC({
url: "model/ajax/request/Request.cfc",
useDefaultErrorHandler: false,
method: "updateRequestDates",
data: { 'request_hdr_seq_no': request_hdr_seq_no,
'request_due_date': selectedDate.asString(),
'test_completion_date':$("#testCompletionDate").val() },
timeout:30000,
success: submitRequestCheck,
error: function(results) {
// If the call fails, throw up an Ext message box to alert the user
Ext.MessageBox.alert('Update Failed', "Failed to update the request due date. Please try again.");
$('input#testCompletionDate').val("");
}
});
});
All I was required to do was write 14 lines of code. Now, I can’t even imagine having to write this code without the use of the Big 3.
Now for one of my favorite features of this site (it’s actually a boring feature, but how I implemented it is cool).

This is just a simple note feature where users can record any pertinent information about the work they are performing. I’m using the Ext Accordion Widget to display all past notes. You can see how the user can expand and contract each note item – not really needed, but never hurts to wow the users a little.
This particular page was written to operate as a true application. Any changes to the UI are immediately written to the database via AJAX calls. The UI is then updated when the operation completes, and the user can continue working while several tasks are being executed. No page reloads at all.
So when they add a note, it must be inserted into the database, and the Accordian object must have a new panel added to it, with the new text inserted inside.
I’ll admit, it stretched all my capabilities and imagination for Javascript programming. All in all, it took over 3 days of trying different things, and research, to get it to work.
First, I had to include the required features of the libraries.
<script type="text/javascript" src="js/jquery.AjaxCFC.js"></script>
<script type="text/javascript" src="js/json.js"></script>
<script type="text/javascript" src="js/ext-buttons.js"></script>
<script type="text/javascript" src="js/ext-layout.js"></script>
<script type="text/javascript" src="js/Ext.ux.InfoPanel.js"></script>
<script type="text/javascript" src="js/Ext.ux.Accordion.js"></script>
*This adds up to 300k of jQuery/Ext Javascript libraries.
Then, I build the <div> element that will be used to display the dialog and the accordion panels
<div id="NoteModalDialog">
<div class="x-dlg-hd">Request Notes</div>
<div class="x-dlg-bd">
<div class="x-dlg-tab" title="Notes">
<div id="RequestNoteForm"></div>
<div style="font-size:1.1em; font-weight:800;">Previous Notes</div>
<div id="acc-ct" style="width:100%; height:200px">
<cfloop query="requestNotesQuery">
<cfoutput>
<div id="panel-#currentRow#">
<div>
#first_name# #last_name# - #setup_date#:
</div>
<div>
<div class="text-content">#note_txt#</div>
<div style="border-bottom:5px solid white;"></div>
</div>
</div>
</cfoutput>
</cfloop>
</div>
</div>
</div>
</div>
Then I instantiate the accordion object and build all the panels with previous notes
$(document).ready(function(){
// Create accordion to hold previous notes in note popup
var accordion = new Ext.ux.Accordion('acc-ct', { height:400,independent:true});
// Create a panel for each previous note
<cfloop query="requestNotesQuery"><cfoutput>
var panel#currentRow# = accordion.add(new Ext.ux.InfoPanel('panel-#currentRow#', {collapsed:false}));
</cfoutput></cfloop>
}
Then, of course, I need my wonderful Javascript object that handles building the dialog, saving the note, and loading of previous notes back into the UI/DOM.
It first builds a dynamic Ext form (which includes validation).
var noteForm = new Ext.form.Form({ labelWidth:25 });
var noteText = new Ext.form.TextArea({
fieldLabel: 'Note',
name: 'noteText',
width:375,
allowBlank:false,
grow:true,
growMax:200,
emptyText:"Enter your request notes here..."
});
noteForm.add(noteText);
Then using ajaxCFC, I insert the data into database
$.AjaxCFC({
url: "model/ajax/note/Note.cfc",
useDefaultErrorHandler: false,
method: "add",
async:false,
data: { 'seq_no': request_hdr_seq_no, 'note_type':'REQUESTHDR', 'note_txt': noteText.getValue() },
timeout:30000,
success: function(results) {
// Reset the note field to blank
noteText.setRawValue("");
{{ This nested ajaxCFC call code shown below }}
},
error: function(results) {
Ext.MessageBox.alert('Save Failed', results.responseText);
}
});
Upon success of the insertion of the data, update the UI by adding a panel to the Accordion object with the new note inside.
// Return all notes from the database for this request
$.AjaxCFC({
url: "model/ajax/request/Request.cfc",
useDefaultErrorHandler: false,
method: "getAllRequestNotes",
async:false,
data: { 'request_hdr_seq_no': request_hdr_seq_no },
timeout:30000,
success: function(results) {
// Empty out the current accordion object and re-populate it with a panel for each note
var panel = new Object();
var accordion = new Ext.ux.Accordion('acc-ct', { height:'400', independent:true });
$("#acc-ct").empty();
for(i=0; i<results.NOTECOUNT;i++) {
$("#acc-ct").append('<div id="panel-' + i + '"><div>' + results.PANELS[i].FIRST_NAME + ' ' + results.PANELS[i].LAST_NAME + ' - ' + results.PANELS[i].SETUP_DATE + '</div><div><div class="text-content">' + results.PANELS[i].NOTE_TXT + '</div></div></div>');
panel[i] = accordion.add(new Ext.ux.InfoPanel('panel-'+i, {collapsed:false}));
}
},
error: function(results) {
Ext.MessageBox.alert('UI Update Failed', "Failed to update Notes popup with latest note.");
}
});
I put it all together, and I get a self-documenting, elegant Javascript object that handles user notes.
// Create the object that will be used for the Request Notes popup
var requestNotesEdit = function() {
var dialog;
var noteForm = new Ext.form.Form({ labelWidth:25 });
var noteText = new Ext.form.TextArea({
fieldLabel: 'Note',
name: 'noteText',
width:375,
allowBlank:false,
grow:true,
growMax:200,
emptyText:"Enter your request notes here..."
});
noteForm.add(noteText);
return {
init : function() { this.buildDialog(); },
saveData : function() {
if (noteForm.isValid()) {
$.AjaxCFC({
url: "model/ajax/note/Note.cfc",
useDefaultErrorHandler: false,
method: "add",
async:false,
data: { 'seq_no': request_hdr_seq_no, 'note_type':'REQUESTHDR', 'note_txt': noteText.getValue() },
timeout:30000,
success: function(results) {
// Reset the note field to blank
noteText.setRawValue("");
// Return all notes from the database for this request
$.AjaxCFC({
url: "model/ajax/request/Request.cfc",
useDefaultErrorHandler: false,
method: "getAllRequestNotes",
async:false,
data: { 'request_hdr_seq_no': request_hdr_seq_no },
timeout:30000,
success: function(results) {
// Empty out the current accordion object and re-populate it with a panel for each note
var panel = new Object();
var accordion = new Ext.ux.Accordion('acc-ct', { height:'400', independent:true });
$("#acc-ct").empty();
for(i=0; i<results.NOTECOUNT;i++) {
$("#acc-ct").append('<div id="panel-' + i + '"><div>' + results.PANELS[i].FIRST_NAME + ' ' + results.PANELS[i].LAST_NAME + ' - ' + results.PANELS[i].SETUP_DATE + '</div><div><div class="text-content">' + results.PANELS[i].NOTE_TXT + '</div></div></div>');
panel[i] = accordion.add(new Ext.ux.InfoPanel('panel-'+i, {collapsed:false}));
}
},
error: function(results) {
Ext.MessageBox.alert('UI Update Failed', "Failed to update Notes popup with latest note.");
}
});
},
error: function(results) {
Ext.MessageBox.alert('Save Failed', results.responseText);
}
});
dialog.hide();
}else{
Ext.Msg.alert('Field Required', 'Please enter in some note text before submitting.');
}
},
buildDialog : function() {
noteForm.render('RequestNoteForm');
dialog = new Ext.BasicDialog("NoteModalDialog", {
modal:true,
width:450,
height:350,
shadow:true,
minWidth:400,
minHeight:300,
animateTarget:'requestNotes'
});
dialog.addKeyListener(27, this.hideWindow, this);
dialog.addButton('Save', this.saveData, this);
dialog.addButton('Cancel', this.hideWindow, this);
},
showWindow : function() { dialog.show(); },
hideWindow : function() { dialog.hide(); }
};
};