Wednesday, November 11, 2009

Javascript Console - Missing in IE?

Ok so lets say your hacking away at your favorite web app using Firefox or Chrome and your dropping some "conole.log()" calls around to help you debug. Then you try the site in IE and it doesn't work. You look at the javascript error and you see that "console" is undefined.

You don't really want to have to remember to change your console.log() calls to alerts or something else when you test in IE so what do you? Well, it's easy to fix actually. Just plop this bit of Javascript in your file and worry no more.


if(console === undefined){
function _console(){
return {
log : function(v){
alert(v);
}

}
}
var console = new _console();
}


Admittedly that is a pretty weak implementation of the console.log() but it is a good starting place for you. If you want you can extend that do do things a little better (like append the log messages to the bottom of your webpage) but you get the basic idea. Enjoy!

Here is a slightly better version to give you an idea how you might extend it (requires the $ function such as that found in JQuery):

if(console === undefined){
function _console(){
return {
log : function(v){
this.prepLog();
var l = '<tr><td>'+v+'</td></tr>';
$('#divConsoleLog').append(l);
},
prepLog : function(){
if(!$('#divConsoleLog')[0]){
var d = '<table id="divConsoleLog"><caption>Console</caption></table>';
$('body').append(d);
}
}
}
}
var console = new _console();
}



Unsurprisingly, someone had this idea before me and they have a more feature rich implementation - check out "faux console" - but for whatever reason his didn't work for me and I don't feel like debugging it. However, his style sheet is pretty much just what I want:


#divConsoleLog{
position:absolute;
top:0;
right:0;
width:300px;
border:1px solid #999;
font-family:courier,monospace;
background:#eee;
font-size:10px;
padding:10px;
}
html>body #divConsoleLog{
position:fixed;
}
#divConsoleLog a{
float:right;
padding-left:1em;
padding-bottom:.5em;
text-align:right;
}

Thursday, November 05, 2009

Updated Blog Template

I just updated my blogs template to a much wider layout so that source code would be easier to read. Hopefully this drastic change doesn't throw off any of my "regular" visitors.

CF: QueryIntersect UDF

Today I needed a function that would return the intersection of two query objects (think Matrices and Sets). If you aren't that familiar with intersections basically what this function does is it returns a query object that contains each cell where the two queries were equal. Thus if you send in two queries with 10 different columns (excpet one of which in both is "firstname") and in both queries, in the 3rd row, the firstname was "richard" but nothing else about the two queries was equal then the return query would have a bunch of empty rows with one column (firstname) - and the firstname value of the third row would be richard.

One of these probably already exists out on the web somewhere but I wanted to write it myself and so I did.

A couple of caveats - 1. I know the variable naming sucks; I am lazy; so deal with it. I don't like having to "var" everything and I don't like typing "local" all the time; so I used very short and not very clear names. That's ok though becuase I also give you a unit test to show you that the function works the way I expect it to. Anyway, it's a pretty short function that you can easily update if you want it to be more readable.


<cffunction name="QueryIntersect" access="public" output="false" returntype="query">
<cfargument name="q1" type="query">
<cfargument name="q2" type="query">

<cfset var l = structNew() />
<!---
grab all the column names that are the same between the two incoming queries
--->
<cfset l.colNames = "" />
<cfloop list="#q2.columnList#" index="l.c">
<cfif ListFindNoCase(q1.columnList,l.c)>
<cfset l.colNames = listAppend(l.colNames, l.c) />
</cfif>
</cfloop>

<cfset l.q = QueryNew("#l.colNames#") />

<cfset l.max = q1.recordCount />
<cfif q2.recordCount LT q1.recordCount>
<cfset l.max = q2.recordCount>
</cfif>

<cfloop from="1" to="#l.max#" step="1" index="l.s">
<cfset QueryAddRow(l.q) />
<cfloop list="#l.colNames#" index="l.i">
<cfif q1["#l.i#"]["#l.s#"] EQ q2["#l.i#"]["#l.s#"]>
<cfset querySetCell(l.q,l.i,q1["#l.i#"]["#l.s#"]) />
</cfif>
</cfloop>
</cfloop>

<cfreturn l.q />

</cffunction>


Here is the unit test

<cffunction name="QueryIntersectTest" access="public" output="false">

<cfset var q1 = QueryNew("id,name,title") />
<cfset var q2 = QueryNew("name,email,id") />
<cfset var q3 = "" />

<cfscript>
QueryAddRow(q1);
QuerySetCell(q1,"id","1");
QuerySetCell(q1,"name","bill");
QuerySetCell(q1,"title","chief");

QueryAddRow(q1);
QuerySetCell(q1,"id","2");
QuerySetCell(q1,"name","carl");
QuerySetCell(q1,"title","bozo");


QueryAddRow(q2);
QuerySetCell(q2,"id","1");
QuerySetCell(q2,"name","tom");
QuerySetCell(q2,"email","tom@bob.com");

QueryAddRow(q2);
QuerySetCell(q2,"id","4");
QuerySetCell(q2,"name","carl");
QuerySetCell(q2,"email","carl@r.org");


QueryAddRow(q2);
QuerySetCell(q2,"id","3");
QuerySetCell(q2,"name","ted");
QuerySetCell(q2,"email","cop@pork.com");

q3 = QueryIntersect(q1,q2);

AssertTrue(q3.recordCount EQ q1.recordCount,"the wrong number of rows were in the intersection");
AssertTrue(ListFindNoCase(q3.columnList,"id"), "id should be a column");
AssertTrue(ListFindNoCase(q3.columnList,"name"), "name should be a column");
AssertTrue(q3.id EQ 1, "first row id should have intersected");
AssertTrue(q3.name EQ "", "first row name should not have intersected");
AssertTrue(q3["id"][2] EQ "", "second id should NOT have intersected");
AssertTrue(q3["name"][2] EQ "carl", "second name should have intersected");

</cfscript>

</cffunction>


I'll probably post another one later that does an intersection of all the rows in a single query as well. (I updated the unit test to better reflect what this function does).

Wednesday, October 28, 2009

ListToQuery UDF

Today I happened to find myself at CFlib.org - I don't go there often - and I was browsing the newer function submissions just for the heck of it and I came across the function ListToQuery by Russ Spivey. It's a straight forward enough function but I thought it could be a little cleaner, and potentially a little faster when dealing with large lists.

Here is my version:


<cffunction name="listToQuery" access="public" returntype="query" output="false"
hint="Converts a list to a single-column query.">

<cfargument name="list" type="string" required="yes" hint="List to convert.">
<cfargument name="delimiters" type="string" required="no" default="," hint="Things that separate list elements.">
<cfargument name="columnName" type="string" required="no" default="column" hint="Name to give query column.">
<cfargument name="includeBlanks" type="boolean" required="no" default="false" hint="include empty elements in the list as empty values in the query">

<cfset var query = queryNew("")>
<cfset QueryAddColumn(query,arguments. columnName,ListToArray(arguments.list,arguments.delimiters,arguments.includeBlanks))>

<cfreturn query>

</cffunction>


Sadly, I couldn't contact Russ directly to share my contribution so hopefully he doesn't mind me sticking this here. If it weren't for his initial cut at the function I wouldn't have thought to even try and write it.

If anyone wants to benchmark the two it would probably be interesting to see if my intuition is correct.
UPDATE:
Ok, well, I benchmarked it with 5000, 10000 and 30000 element lists (I didn't have the patience for a 100,000 element list sorry (i suppose I could optimize my list building). Here were the results:






5000 element list
FunctionTime in ms
Using QueryAddColumn0
Origional Version31






10000 element list
FunctionTime in ms
Using QueryAddColumn47
Origional Version235






30000 element list
FunctionTime in ms
Using QueryAddColumn110
Origional Version453