BlogCFC ColdFish Code Block Print Function Update

Normally I pay quite a bit of attention to my site after I do any major updates or changes but I've been quite a slacker lately and I seemed to have missed a few issues that happened after I updated my blog core ... Bad me.

Something interesting that I truly thought would be an isolated incident - and my problem - actually wasn't. It seems that there was a bug where Gecko based browsers - Firefox - don't seem to like the window.frames[] syntax ... So, for those that are using BlogCFC and want to be able to print code blocks from Firefox ... You may need to update the formatter.cfc with the new syntax ... Here's the link for the formatter.cfc ... on RIAForge ... or you can simply copy the update from here ...

view plain print about
1<!---
2    Copyright 2009 Jason Delmore
3    All rights reserved.
4    jason@cfinsider.com
5    
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU Lesser General Public License (LGPL) as published by
8    the Free Software Foundation, either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU Lesser General Public License for more details.
15
16    You should have received a copy of the GNU Lesser General Public License    
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18    --->

19<cfcomponent output="false">
20    <cffunction name="init" access="public" hint="This function initializes all of the variables needed for the component." output="false">
21        <cfscript>
22            //initialize a buffer
23
                
24            // If you're using JDK 1.5 or later and want some extra performance this can be a StringBuilder
25
            //variables.buffer=createObject("java","java.lang.StringBuilder").init();
26
            variables.buffer=createObject("java","java.lang.StringBuffer").init();
27            
28            // initialize private variables
29
            // TODO : Change the parser state to be a struct rather than individual variables.
30
            variables.isCommented=false;
31            variables.isTag=false;
32            variables.isValue=false;
33            variables.isCFSETTag=false;
34            variables.isCFScript=false;
35            variables.isCFQueryTag=false;
36            variables.isOneLineComment=false;
37            variables.isMXML=false;
38            variables.isActionscript=false;
39            variables.isSQL=false;
40            variables.isSQLValue=false;
41            variables.initialparser="";
42            variables.spansOpened = 0;
43            variables.spansClosed = 0;
44        
</cfscript>
45        <cfreturn this/>
46    </cffunction>
47    
48    <cffunction name="getHTMLOutput" access="public" hint="This function accepts a block of code and formats it into syntax highlighted HTML." output="false">
49        <cfargument name="code" type="string"/>
50        <cfargument name="parser" type="string"/>
51        <cfargument name="codesig" type="string"/>
52        <cfscript>
53            var BIstream = createObject("java","java.io.StringBufferInputStream").init(arguments.code);
54            var IStream = createObject("java","java.io.InputStreamReader").init(BIstream);
55            var reader = createObject("java","java.io.BufferedReader").init(IStream);
56            var line = reader.readLine();
57            var linenumber = 0;
58            
59            if (arguments.parser neq ""{
60                "variables.is#arguments.parser#" = true;
61            }
62            
63            if (getConfig().getShowToolbar()) {
64                variables.buffer.append(getToolbarHTML(arguments.code,arguments.codesig));            
65            }
66            
67            variables.buffer.append("<span id='formatted_code_" & arguments.codesig & "' style='" & getStyle("TEXT") & "'>");
68            while isdefinedd("line")) {
69                if (getConfig().getShowLineNumbers()) {
70                    linenumber = linenumber + 1;
71                    variables.buffer.append("<span style='" & getStyle("LINENUMBER") & "'>" & linenumber & "</span>");
72                }
73                formatLine(line);
74                line = reader.readLine();
75            }
76            // there appears to be more spans created than cleaned up... closing up any extras... will need to review to see what is keeping extra spans
77
            while (variables.spansOpened gt variables.spansClosed) {
78                variables.spansClosed = variables.spansClosed + 1;
79                variables.buffer.append("</span>");
80            }
81            variables.buffer.append("</span>");
82            reader.close();
83
84            return variables.buffer;
85        
</cfscript>
86    </cffunction>
87    
88    <cffunction name="getToolbarHTML" access="private" output="true">
89        <cfargument name="code" type="string"/>
90        <cfargument name="codesig" type="string"/>
91        <cfset var toolbarHTML = ""/>
92        <cfsavecontent variable="toolbarHTML">
93            <iframe id='print_frame_#arguments.codesig#' style='display:inline;height:0px;width:0px;' frameborder='0'></iframe>
94            <script type='text/javascript'>
95                function toggle_view_#arguments.codesig#() {
96                    var temp = document.getElementById('htmlencoded_plain_#arguments.codesig#').style.display;
97                    document.getElementById('htmlencoded_plain_#arguments.codesig#').style.display=document.getElementById('formatted_code_#arguments.codesig#').style.display;
98                    document.getElementById('formatted_code_#arguments.codesig#').style.display=temp;
99                    if (temp=='none') {
100                        document.getElementById('view_#arguments.codesig#').innerHTML='view formatted';
101                    } else {
102                        document.getElementById('view_#arguments.codesig#').innerHTML='view plain';
103                    }
104                }
105                function copy_to_clipboard_#arguments.codesig#() {
106                    var code=unescape(document.getElementById('htmlencoded_plain_#arguments.codesig#').innerHTML).replace(/&lt;/g, '\x3C').replace(/&gt;/g, '\x3E').replace(/&amp;/g, '\x26').replace(/\x3Cbr\x3E/gi, '\r\n').replace(new RegExp('&nbsp;&nbsp;&nbsp;&nbsp;', 'gi'), '\t');
107                    window.clipboardData.setData('text',code);
108                }
109                function print_#arguments.codesig#() {
110                    document.getElementById("print_frame_#arguments.codesig#").contentWindow.document.body.innerHTML = document.getElementById('formatted_code_#arguments.codesig#').innerHTML;
111                    document.getElementById("print_frame_#arguments.codesig#").contentWindow.focus();
112                    document.getElementById("print_frame_#arguments.codesig#").contentWindow.print();
113                }
114                function show_about_#arguments.codesig#() {
115                    document.getElementById('about_#arguments.codesig#').style.display='inline';
116                    window.setTimeout('hide_about_#arguments.codesig#();', 4000);
117                }
118                function hide_about_#arguments.codesig#() {
119                    document.getElementById('about_#arguments.codesig#').style.display='none';
120                }
121            </script>
122            <div style='#getStyle("TOOLBAR")#'>
123                <span style='margin:0 0 0 2em'/>
124                <!--- Toggle code view --->
125                <a href='javascript:toggle_view_#arguments.codesig#()' style='#getStyle("TOOLBARLINK")#' id='view_#arguments.codesig#'>view plain</a>
126                
127                <!--- Copy to clipboard --->
128                <a href='javascript:copy_to_clipboard_#arguments.codesig#()' style='display:none;#getStyle("TOOLBARLINK")#' id='view_copy_to_clipboard_link_#arguments.codesig#'>copy to clipboard</a>
129                <!--- The cross-browser copy to clipboard methods out there are hacky and only work on certain browsers... if the browser handles it, then the link show up... --->
130                <script type='text/javascript'>if(window.clipboardData) { document.getElementById('view_copy_to_clipboard_link_#arguments.codesig#').style.display='inline';}</script>
131                
132                <!--- Print --->
133                <a href='javascript:print_#arguments.codesig#()' style='#getStyle("TOOLBARLINK")#'>print</a>
134                
135                <!--- About --->
136                <a href='javascript:show_about_#arguments.codesig#()' style='#getStyle("TOOLBARLINK")#'>about</a>
137                
138            </div>
139            <span id='about_#arguments.codesig#' style='display:none;#getStyle("TEXT")#'><span style='#getStyle("LINENUMBER")#'>&nbsp;</span>&nbsp;ColdFISH is developed by Jason Delmore.  Source code and license information available at <a href='http://coldfish.riaforge.org/'>coldfish.riaforge.org</a><br/></span>
140            <span id='htmlencoded_plain_#arguments.codesig#' style='display:none;#getStyle("TEXT")#'>#REReplace(REReplace(htmleditformat(arguments.code), "\n", "<br />", "ALL"),"\t","&nbsp;&nbsp;&nbsp;&nbsp;","ALL")#</span>
141        </cfsavecontent>
142        <cfreturn toolbarHTML/>
143    </cffunction>
144    
145    <cffunction name="formatLine" access="private" hint="This function takes a single line of code and formats it into syntax highlighted HTML." output="false">
146        <cfargument name="line" type="any"/>
147        <cfscript>
148            var character = "";
149            var thisLine=arguments.line;
150            var i = 0;
151            var endtagPos = 0;
152            var startAttributePos = 0;
153            var keywordskip = 0;
154
155            
156            if (variables.isOneLineComment) endOneLineComment();
157            
158            for (i=0; i LT thisLine.length(); i=i+1)
159            {
160                character=thisLine.charAt(javacast('int',i));
161                if (character EQ '
<')
162                {
163                    if (variables.isCFScript AND NOT variables.isValue)
164                        endCFScript();
165                    if (regionMatches(thisLine, 1, i+1, "!--", 0, 3))
166                    {
167                        if (regionMatches(thisLine, 1, i+4, "-", 0, 1))
168                        {
169                            startComment("CF");
170                        } else {
171                            startComment("HTML");
172                        }
173                    } else {
174                        if (regionMatches(thisLine, 1, i+1, "CF", 0, 2) OR regionMatches(thisLine, 1, i+1, "/CF", 0, 3))
175                        {
176                            startTag("CF");
177                            if (regionMatches(thisLine, 1, i+3, "SET", 0, 3) AND NOT regionMatches(thisLine, 1, i+6, "T", 0, 1)) // CFSET Tag
178                            {
179                                variables.buffer.append(thisLine.substring(javacast('int',i+1), javacast('int',i+6)));
180                                i=i+5;
181                                startCFSET();
182                            }
183                            else if (regionMatches(thisLine, 1, i+3, "SCRIPT>", 0, 6)) // CFSCRIPT TAG
184                            {
185                                variables.buffer.append(thisLine.substring(javacast('int',i+1), javacast('int',i+9)) & "&gt;");
186                                i=i+9;
187                                startCFScript();
188                            }
189                            else if (regionMatches(thisLine, 1, i+3, "QUERY", 0, 5)) // START CFQUERY TAG
190                            {    // TODO: This sets the value color immediately to match SQL values including the CFQuery tag...
191                                variables.isCFQueryTag = true;
192                            } else if (regionMatches(thisLine, 1, i+4, "QUERY", 0, 5)) // END CFQUERY TAG
193                            {
194                                variables.isCFQueryTag = false;
195                                endSQL();
196                            }
197                        }
198                        else if    (
199                                     regionMatches(thisLine, 1, i+1, "TA", 0, 2) OR
200                                    regionMatches(thisLine, 1, i+1, "/TA", 0, 3) OR
201                                    regionMatches(thisLine, 1, i+1, "TB", 0, 2) OR
202                                    regionMatches(thisLine, 1, i+1, "/TB", 0, 3) OR
203                                    regionMatches(thisLine, 1, i+1, "TD", 0, 2) OR
204                                    regionMatches(thisLine, 1, i+1, "/TD", 0, 3) OR
205                                    regionMatches(thisLine, 1, i+1, "TF", 0, 2) OR
206                                    regionMatches(thisLine, 1, i+1, "/TF", 0, 3) OR
207                                    regionMatches(thisLine, 1, i+1, "TH", 0, 2) OR
208                                    regionMatches(thisLine, 1, i+1, "/TH", 0, 3) OR
209                                    regionMatches(thisLine, 1, i+1, "TR", 0, 2) OR
210                                    regionMatches(thisLine, 1, i+1, "/TR", 0, 3)
211                                ) // HTML TABLE
212                        {
213                            startTag("HTMLTABLES");
214                        }
215                        else if (regionMatches(thisLine, 1, i+1, "IMG", 0, 3) OR regionMatches(thisLine, 1, i+1, "STY", 0, 3) OR regionMatches(thisLine, 1, i+1, "/STY", 0, 4)) //IMG or STYLE Tag
216                        // TODO: Do separate syntax highlighting for stuff inside style
217                        {
218                            startTag("HTMLSTYLES");
219                        }
220                        else if (
221                                    regionMatches(thisLine, 1, i+1, "FORM", 0, 4) OR
222                                    regionMatches(thisLine, 1, i+1, "/FORM", 0, 5) OR
223                                    regionMatches(thisLine, 1, i+1, "INPUT", 0, 5) OR
224                                    regionMatches(thisLine, 1, i+1, "/INPUT", 0, 5) OR
225                                    regionMatches(thisLine, 1, i+1, "TEXT", 0, 4) OR
226                                    regionMatches(thisLine, 1, i+1, "/TEXT", 0, 5) OR
227                                    regionMatches(thisLine, 1, i+1, "SELECT", 0, 6) OR
228                                    regionMatches(thisLine, 1, i+1, "/SELECT", 0, 7) OR
229                                    regionMatches(thisLine, 1, i+1, "OPT", 0, 3) OR
230                                    regionMatches(thisLine, 1, i+1, "/OPT", 0, 3)
231                                )
232                        {
233                            startTag("HTMLFORMS");
234                        }
235                        else if (
236                                     regionMatches(thisLine, 1, i+1, "MX:", 0, 3) OR
237                                    regionMatches(thisLine, 1, i+1, "/MX:", 0, 4)
238                                )
239                        {
240                            if (regionMatches(thisLine, 1, i+4, "SCRIPT>", 0, 6)) // MX:SCRIPT TAG
241                            {
242                                startTag("ACTIONSCRIPTTAG");
243                                variables.buffer.append(thisLine.substring(javacast('int',i+1), javacast('int',i+10)) & "&gt;");
244                                i=i+10;
245                                startActionscript();
246                            } else if (regionMatches(thisLine, 1, i+5, "SCRIPT>", 0, 6)) // END MX:SCRIPT TAG
247                            {
248                                endActionscript();
249                                startTag("ACTIONSCRIPTTAG");
250                                variables.buffer.append(thisLine.substring(javacast('int',i+1), javacast('int',i+11)));
251                                i=i+12;
252                                endTag();
253                            } else {
254                                startTag("MXML");
255                                startAttributePos=find(' ',thisLine,i+1); //start finding the next space from current position
256                                endtagPos=find('>
',thisLine,i+1); //start finding the end tag from current position
257                                if (startAttributePos neq 0) {  // start attribute colors
258                                    variables.buffer.append(thisLine.substring(javacast('int',i+1), javacast('int',startAttributePos)));
259                                    i=startAttributePos-1;
260                                    startMXMLTag();
261                                } else {
262                                    if (endtagPos neq 0) { // found >

263                                        variables.buffer.append(thisLine.substring(javacast('int',i+1), javacast('int',endtagPos-1)));
264                                        i=i+endtagPos;
265                                        variables.buffer.append("&gt;");
266                                        endHighlight();
267                                    }
268                                }    
269                            }
270                        } else {
271                            if (variables.isActionscript or variables.isSQL) {
272                                variables.buffer.append("&lt;");
273                            } else {
274                                startTag("HTML");
275                            }
276                        }
277                    }
278                }
279                else if (character EQ '>')
280                {
281                    if (variables.isCommented AND regionMatches(thisLine, 1, i-2, "--", 0, 2))
282                    {
283                        if (regionMatches(thisLine, 1, i-3, "-", 0, 1))
284                        {
285                            endComment("CF");
286                        } else {
287                            endComment("HTML");
288                        }
289                    } else {
290                        if (variables.isCFSETTag) {
291                            endCFSET();
292                        } else if (variables.isActionscript) {
293                            //This is where a CDATA for AS ends
294                            variables.buffer.append("&gt;");
295                        } else if (variables.isSQL) {
296                            variables.buffer.append("&gt;");
297                        } else if (variables.isCFQueryTag) {
298                            endTag();
299                            startSQL();
300                        } else if (variables.isMXML) {
301                            endMXMLTag();
302                        } else {
303                            endTag();
304                        }
305                    }
306                }
307                else if (character EQ '"')
308                {
309                    if (variables.isTag OR variables.isCFScript OR variables.isActionscript)
310                    {
311                        if (NOT variables.isValue) {
312                            startValue();
313                            variables.buffer.append('"');
314                        } else {
315                            variables.buffer.append('"');
316                            endValue();
317                        }
318                    } else {
319                        variables.buffer.append('"');
320                    }
321                }
322                else if (character EQ '{')
323                {
324                    startBind();
325                    variables.buffer.append("{");
326                    endBind();
327                }
328                else if (character EQ '}')
329                {
330                    startBind();
331                    variables.buffer.append("}");
332                    endBind();
333                }
334                else if (character EQ '/')
335                {
336                    if ((variables.isCFScript OR variables.isActionscript) AND regionMatches(thisLine, 1, i+1, "/", 0, 1) AND NOT variables.isCommented)
337                    {
338                        if (variables.isActionscript) {
339                            startOneLineComment("MXMLCOMMENT");
340                            variables.buffer.append("/");
341                        } else {
342                            startOneLineComment("HTMLCOMMENT");
343                            variables.buffer.append("/");
344                        }
345                    }
346                    else if (variables.isCommented)
347                    {
348                        if (regionMatches(thisLine, 1, i-1, "*", 0, 1))
349                        {
350                            endComment("SCRIPT");
351                        } else {
352                            variables.buffer.append("/");
353                        }
354                    } else {
355                        if (regionMatches(thisLine, 1, i+1, "*", 0, 1))
356                        {
357                            startComment("SCRIPT");
358                        } else {
359                            variables.buffer.append("/");
360                        }
361                    }
362                }
363                else if (variables.isSQL AND character EQ '-')
364                {
365                    if (regionMatches(thisLine, 1, i+1, "-", 0, 1) AND NOT variables.isCommented)
366                    {
367                        startOneLineComment("SQLCOMMENT");
368                        variables.buffer.append("-");
369                    } else {
370                        variables.buffer.append("-");
371                    }
372                }
373                else if (variables.isSQL AND character EQ "'" AND NOT variables.isCommented)
374                {
375                    if (NOT variables.isValue) {
376                        startValue();
377                        variables.buffer.append("'");
378                    } else {
379                        variables.buffer.append("'");
380                        endValue();
381                    }
382                }
383
384                // straight up replacements
385                else if (character EQ '\t' OR character EQ '    ')
386                {
387                    variables.buffer.append("&nbsp;&nbsp;&nbsp;&nbsp;");
388                }
389                else if (character EQ ' ')
390                {
391                    variables.buffer.append("&nbsp;");
392                }
393                else if (character EQ '&')
394                {
395                    if (regionMatches(thisLine, 1, i+1, "##", 0, 1)) {
396                        variables.buffer.append("&");
397                    } else {
398                        variables.buffer.append("&amp;");
399                    }
400                } else {
401                    if (not variables.isCommented AND not variables.isValue and (i eq 0 OR NOT listcontainsnocase('a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,@', thisLine.substring(javacast('int',i-1),javacast('int',i))))) {
402                        keywordskip = 0;
403                        // would like this to be much more generic rather than checking "is"
404                        if (variables.isActionscript) {
405                            keywordskip = keywordsearch(thisLine,i,"Actionscript");
406                        } else if (variables.isCFscript or variables.isCFSetTag) {
407                            keywordskip = keywordsearch(thisLine,i,"CFscript");
408                        } else if (variables.isSQL) {
409                            keywordskip = keywordsearch(thisLine,i,"sql");
410                        }
411                        if (keywordskip) {
412                            i = i + keywordskip;
413                        } else {
414                            variables.buffer.append(character.toString());
415                        }
416                    } else {
417                        variables.buffer.append(character.toString());
418                    }
419                }
420            }
421            variables.buffer.append("<br />");
422        </cfscript>
423    </cffunction>
424    <cffunction name="keywordsearch" access="private" hint="This function searches for keywords." output="false">
425        <cfargument name="thisLine" type="any"/>
426        <cfargument name="i" type="numeric"/>
427        <cfargument name="parser" type="string"/>
428        <cfset var keywordmap = getConfig().getKeywordmap()/>
429        <cfset var keyword = listfirst(thisLine.substring(javacast('int',i)),' (')/> <!--- search starting from current position --->
430        <cfset var findkey = StructFindKey(keywordmap[parser], keyword)/>
431        <cfset var keywordtype = ""/>
432        
433        <cfif arraylen(findkey)>
434            <cfset keywordtype = findkey[1].value/>
435            <cfset variables.buffer.append("<span style='" & getStyle(keywordtype) & "'>" & keyword & "</span>")/>
436             <cfreturn keyword.length()-1/>
437        </cfif>
438        <cfreturn 0/>
439    </cffunction>
440    <cffunction name="regionMatches" access="private" hint="This function checks if a regionMatches." output="false">
441        <cfargument name="string1" type="any"/>
442        <cfargument name="caseInsensitive" type="boolean" default="true"/>
443        <cfargument name="startPosition1" type="numeric"/>
444        <cfargument name="string2" type="any"/>
445        <cfargument name="startPosition2" type="numeric"/>
446        <cfargument name="endPosition2" type="numeric"/>
447        <cfreturn arguments.string1.regionMatches(arguments.caseInsensitive, javacast('int',arguments.startPosition1), arguments.string2, javacast('int',arguments.startPosition2), javacast('int',arguments.endPosition2))/>
448    </cffunction>
449    <cffunction name="startHighlight" access="private" hint="" output="false">
450        <cfargument name="element" type="string"/>
451        <cfset variables.spansOpened = variables.spansOpened + 1/>
452        <cfset variables.buffer.append("<span style='" & getStyle(arguments.element) & "'>")/>
453    </cffunction>
454    <cffunction name="endHighlight" access="private" hint="" output="false">
455        <cfargument name="line" type="any"/>
456        <cfset variables.spansClosed = variables.spansClosed + 1/>
457        <cfset variables.buffer.append("</span>")/>
458    </cffunction>
459    <cffunction name="startOneLineComment" access="private" output="false">
460        <cfargument name="type" type="string"/>
461        <cfscript>
462        startHighlight(type);
463        variables.isOneLineComment=true;
464        variables.isCommented=true;
465        
</cfscript>    
466    </cffunction>
467    <cffunction name="endOneLineComment" access="private" output="false">
468        <cfscript>
469        endHighlight();
470        variables.isOneLineComment=false;
471        variables.isCommented=false;
472        
</cfscript>    
473    </cffunction>
474    <cffunction name="startComment" access="private" output="false">
475        <cfargument name="type" type="string"/>
476        <cfscript>
477        if (type  EQ  "CF"{
478            startHighlight("CFCOMMENT");
479            variables.buffer.append("&lt;");
480        } else if (type  EQ  "HTML"{
481            if (variables.isMXML) {
482                startHighlight("MXMLCOMMENT");
483            } else {
484                startHighlight("HTMLCOMMENT");
485            }
486            variables.buffer.append("&lt;");
487        } else {
488            if (variables.isActionscript) {
489                startHighlight("ACTIONSCRIPTCOMMENT");
490            } else if (variables.isSQL) {
491                startHighlight("SQLCOMMENT");
492            } else {
493                startHighlight("CFSCRIPTCOMMENT");
494            }
495            variables.buffer.append("/");
496        }
497        variables.isCommented=true;
498        
</cfscript>
499    </cffunction>
500    <cffunction name="endComment" access="private" output="false">
501        <cfargument name="type" type="string"/>
502        <cfscript>
503        if (type  EQ  "SCRIPT"{
504            variables.buffer.append("/");
505        } else {
506            variables.buffer.append("&gt;");
507        }
508        endHighlight();
509        variables.isCommented=false;
510        
</cfscript>
511    </cffunction>
512    <cffunction name="startTag" access="private" output="false">
513        <cfargument name="type" type="string"/>
514        <cfscript>
515        if NOTT variables.isCommented AND NOT variables.isValue) {
516            if (type  EQ  "CF"{
517                startHighlight("CFTAG");
518            } else if (type  EQ  "HTMLSTYLES"{
519                startHighlight("HTMLSTYLES");
520            } else if (type  EQ  "HTMLTABLES"{
521                startHighlight("HTMLTABLES");
522            } else if (type  EQ  "HTMLFORMS"{
523                startHighlight("HTMLFORMS");
524            } else if (type EQ "MXML"{
525                startHighlight("MXML");
526            } else if (type EQ "ACTIONSCRIPTTAG"{
527                startHighlight("ACTIONSCRIPTTAG");
528            } else { // type is HTML
529
                startHighlight("HTML");
530            }
531            variables.isTag=true;
532        }
533        variables.buffer.append("&lt;");
534        
</cfscript>
535    </cffunction>
536    <cffunction name="endTag" access="private" output="false">
537        <cfscript>
538        variables.buffer.append("&gt;");
539        if NOTT variables.isCommented AND NOT variables.isValue) {
540            endHighlight();
541            variables.isTag=false;
542        }
543        
</cfscript>
544    </cffunction>
545    <cffunction name="startValue" access="private" output="false">
546        <cfscript>
547        if NOTT variables.isCommented) {
548            if (variables.isCFSETTag OR variables.isCFScript) {
549                startHighlight("CFSCRIPTVALUE");
550            } else if (variables.isActionscript) {
551                startHighlight("ACTIONSCRIPTVALUE");
552            } else if (variables.isMXML) {
553                startHighlight("MXMLVALUE");
554            } else if (variables.isSQL) {
555                startHighlight("SQLVALUE");
556            } else {
557                startHighlight("VALUE");
558            }
559            variables.isValue=true;
560        }
561        
</cfscript>
562    </cffunction>
563    <cffunction name="endValue" access="private" output="false">
564        <cfscript>
565        if NOTT variables.isCommented) {
566            endHighlight();
567            variables.isValue=false;
568        }
569        
</cfscript>
570    </cffunction>
571    <cffunction name="startBind" access="private" output="false">
572        <cfscript>
573        if NOTT variables.isCommented) {
574            startHighlight("BIND");
575        }
576        
</cfscript>
577    </cffunction>
578    <cffunction name="endBind" access="private" output="false">
579        <cfscript>
580        if NOTT variables.isCommented) {
581            endHighlight();
582        }
583        
</cfscript>
584    </cffunction>
585    <cffunction name="startCFSET" access="private" output="false">
586        <cfscript>
587        if NOTT variables.isCommented) {
588            startHighlight("CFSET");
589            variables.isCFSETTag=true;
590        }
591        
</cfscript>
592    </cffunction>
593    <cffunction name="endCFSET" access="private" output="false">
594        <cfscript>
595        if NOTT variables.isCommented) {
596            endHighlight();
597            variables.buffer.append("&gt;");
598            endHighlight();
599            variables.isCFSETTag=false;
600        } else {
601            variables.buffer.append("&gt;");
602        }
603        
</cfscript>
604    </cffunction>
605    <cffunction name="startMXMLTag" access="private" output="false">
606        <cfscript>
607        if NOTT variables.isCommented) {
608            startHighlight("MXMLATTRIBUTES");
609            // TODO: Add in MXML Value colors.
610
            // setStyle("VALUE","color:##900");
611
            variables.isMXML=true;
612        }
613        
</cfscript>
614    </cffunction>
615    <cffunction name="endMXMLTag" access="private" output="false">
616        <cfscript>
617        if NOTT variables.isCommented) {
618            endHighlight();
619            variables.buffer.append("&gt;");
620            endHighlight();
621            // TODO: Add in MXML Value colors.
622
            // setStyle("VALUE","color:##0000CC");
623
            variables.isMXML=false;
624        } else {
625            variables.buffer.append("&gt;");
626        }
627        
</cfscript>
628    </cffunction>
629    <cffunction name="startCFScript" access="private" output="false">
630        <cfscript>
631        if NOTT variables.isCommented) {
632            endHighlight();
633            startHighlight("CFSCRIPT");
634            variables.isCFScript=true;
635        }
636        
</cfscript>
637    </cffunction>
638    <cffunction name="endCFScript" access="private" output="false">
639        <cfscript>
640        if NOTT variables.isCommented) {
641            endHighlight();
642            variables.isCFScript=false;
643        }
644        
</cfscript>
645    </cffunction>
646    <cffunction name="startActionscript" access="private" output="false">
647        <cfscript>
648        if NOTT variables.isCommented) {
649            endHighlight();
650            startHighlight("ACTIONSCRIPT");
651            variables.isActionscript=true;
652        }
653        
</cfscript>
654    </cffunction>
655    <cffunction name="endActionscript" access="private" output="false">
656        <cfscript>
657        if NOTT variables.isCommented) {
658            variables.isActionscript=false;
659        }
660        
</cfscript>
661    </cffunction>
662    <cffunction name="startSQL" access="private" output="false">
663        <cfscript>
664        if NOTT variables.isCommented) {
665            variables.isSQL=true;
666        }
667        
</cfscript>
668    </cffunction>
669    <cffunction name="endSQL" access="private" output="false">
670        <cfscript>
671        if NOTT variables.isCommented) {
672            variables.isSQL=false;
673        }
674        
</cfscript>
675    </cffunction>
676    
677    <!--- configuration methods --->
678    <cffunction name="setConfig" access="public" hint="This sets the coldfish configuration object for the parser to use." output="false">
679        <cfargument name="config" type="any"/>
680        <cfset variables.coldfishconfig = config/>
681    </cffunction>
682    <cffunction name="getConfig" access="public" hint="This sets the coldfish configuration object for the parser to use." output="false">
683        <cfargument name="config" type="any"/>
684        <cfreturn variables.coldfishconfig/>
685    </cffunction>
686    <cffunction name="getStyle" access="private" hint="This function can be used to get the style used in conjunction with a type of language element." output="false">
687        <cfargument name="element" type="string"/>
688        <cfreturn getConfig().getStyle(element)/>
689    </cffunction>
690    <cffunction name="getInitialParser" access="private" hint="You can set the initial parser state with this.  This is helpful for scripts that make initial parsing impossible (ie. Script, Actionscript)" output="false">
691        <cfreturn getConfig().getInitialParser()/>
692    </cffunction>
693    <cffunction name="getKeywordmap" access="private" hint="You can set the keywordmap with this." output="false">
694        <cfreturn getConfig().getKeywordMap()/>
695    </cffunction>
696</cfcomponent>

I'd like to extend a special thanks to Ray Camden & Jason Delmore for their prompt replies and expeditious changes ... Much Appreciated.

Managing ColdFusion and Database Services Through Batch Scripting

Managing ColdFusion Application Services » Does StartUp Need So Many Steps? No.

Some time last year I realized the need to manage the application development services running simultaneously on my dev box. On my main box I'm running ColdFusion 8 on IIS 7, MySQL, MSSQL, and a WAMP setup as well. So when the need arose to manage which services were running, I would previously had called up an MMC snap-in and manually cranked up the required ColdFusion services; the MySQL or MSSQL database services and finally the sites I needed running on IIS - ad hoc. A good portion of my daily work schedule was getting consumed going back and forth to my MMC console.

Managing StartUp Services with Batch Scripting

I created a simple batch script to either crank up or shut down ColdFusion as needed and then I put a shortcut in a directory under c:\ where the shortcut would be accessible to my computer's PATH variables. This way I can get even lazier by just running my batch script by typing Win Key + R - enter my batch script shortcut name which is uniquely named "CF" and hitting enter ... Having the shortcut has been really convenient. However, there were some issues with my batch script. Namely, if ColdFusion or a database service were already running and I tried to start them ... Windows would throw help message 1282 - The requested service has already been started. So, I needed to clean up my batch script by testing for the running services first. I also added a call to open a separate cmd window with the status the development services in question. Below is my new and improved batch script - Again, uniquely titled - Dev Cranker. Very Unique - Almost brandable.

TITLE --- Dev Cranker ---

@ECHO OFF

REM - THIS IS A FILE TO MANAGE DEVLOPMENT ... HI

SETLOCAL ENABLEDELAYEDEXPANSION

:BEGIN

IF EXIST CODE.TXT DEL CODE.TXT

SET MySQL=MySQL

SET CF1= "ColdFusion 8 .NET Service"

SET CF2= "ColdFusion 8 ODBC Agent"

SET CF3= "ColdFusion 8 ODBC Server"

SET CF4= "ColdFusion 8 Application Server"

SET SBR= "SQL Browser"

SET MSSQL= "MSSQL$SQLEXPRESS"

ECHO.

SC QUERY %CF1% |FINDSTR "SERVICE_NAME"> CODE.TXT

ECHO.

SC QUERY %CF1% |FINDSTR "DISPLAY_NAME STATE" >> CODE.TXT

ECHO.

SC QUERY %CF2% |FINDSTR "SERVICE_NAME" >> CODE.TXT

ECHO.

SC QUERY %CF2% |FINDSTR "DISPLAY_NAME STATE" >> CODE.TXT

ECHO.

SC QUERY %CF3% |FINDSTR "SERVICE_NAME" >> CODE.TXT

ECHO.

SC QUERY %CF3% |FINDSTR "DISPLAY_NAME STATE" >> CODE.TXT

ECHO.

SC QUERY %CF4% |FINDSTR "SERVICE_NAME" >> CODE.TXT

ECHO.

SC QUERY %CF4% |FINDSTR "DISPLAY_NAME STATE" >> CODE.TXT



SC QUERY %MySQL% |FINDSTR "SERVICE_NAME" >> CODE.TXT

ECHO.

SC QUERY %MySQL% |FINDSTR "DISPLAY_NAME STATE" >> CODE.TXT

ECHO.

SC QUERY %SBR% |FINDSTR "SERVICE_NAME" >> CODE.TXT

ECHO.

SC QUERY %SBR% |FINDSTR "DISPLAY_NAME STATE" >> CODE.TXT

ECHO.

SC QUERY %MSSQL% |FINDSTR "SERVICE_NAME" >> CODE.TXT

ECHO.

SC QUERY %MSSQL% |FINDSTR "DISPLAY_NAME STATE" >> CODE.TXT

ECHO.

START "DEV CRANKER II --- SERVICE STATE " TYPE CODE.TXT

:ZERO

CLS

ECHO.

ECHO ====================================================================================================

ECHO.

ECHO Dev Cranker ...

ECHO

ECHO ====================================================================================================

ECHO.

ECHO 1. Start ColdFusion

ECHO 2. Stop ColdFusion

ECHO 3. Start a Database

ECHO 4. Stop a Database

ECHO 5. Quit and Exit

ECHO.

ECHO What Do You Want To Do?

ECHO ====================================================================================================

CHOICE "/C:12345"

ECHO ====================================================================================================

IF ERRORLEVEL ==5 GOTO NINE

IF ERRORLEVEL ==4 GOTO SIX

IF ERRORLEVEL ==3 GOTO TWO

IF ERRORLEVEL ==2 GOTO FIVE

IF ERRORLEVEL ==1 GOTO ONE

:TEN

ECHO ====================================================================================================

ECHO Good Bye! ...

ECHO Dev Cranker ...

SLEEP 3

GOTO END

:NINE


ECHO ====================================================================================================

ECHO.

ECHO Finished?

ECHO.

ECHO ====================================================================================================

CHOICE "/C:YN"

ECHO ====================================================================================================

IF ERRORLEVEL ==2 GOTO ZERO

IF ERRORLEVEL ==1 GOTO TEN

:EIGHT

SC QUERY %Mysql% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 1 (

SC STOP %Mysql% >> code.txt

ECHO %Mysql% Is Winding Down ...

) ELSE (

ECHO %Mysql% Isn't Running ...

)GOTO NINE

:SEVEN

SC QUERY %SBR% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 1 (

SC STOP %SBR% >> code.txt

ECHO %SBR% Is Winding Down ...

) ELSE (

ECHO %SBR% Isn't Running ...

)SC QUERY %MSSQL% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 1 (

SC STOP %MSSQL% >> code.txt

ECHO %MSSQL% Is Winding Down ...

) ELSE (

ECHO %MSSQL% Isn't Running ...

)

GOTO NINE

:SIX

ECHO ====================================================================================================

ECHO.

ECHO 1. MSSQL (Exp)

ECHO 2. MySql

ECHO 3. Just Exit (N/A)

ECHO ====================================================================================================

ECHO Which Database Are You Stopping?


ECHO ====================================================================================================

CHOICE "/C:123"

IF ERRORLEVEL ==3 GOTO NINE

IF ERRORLEVEL ==2 GOTO EIGHT

IF ERRORLEVEL ==1 GOTO SEVEN

EXIT

:FIVE

SC QUERY %CF1% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 1 (

SC STOP %CF1% >> code.txt

ECHO %CF1% Is Winding Down ...

) ELSE (

ECHO %CF1% Isn't Running ...

)

SC QUERY %CF2% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 1 (

SC STOP %CF2% >> code.txt

ECHO %CF2% Is Winding Down ...

) ELSE (

ECHO %CF2% Isn't Running ...

)

SC QUERY %CF3% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 1 (

SC STOP %CF3% >> code.txt

ECHO %CF3% Is Winding Down ...

) ELSE (

ECHO %CF3% Isn't Running ...

)

SC QUERY %CF4% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 1 (

SC STOP %CF4% >> code.txt

ECHO %CF4% Is Winding Down ...

) ELSE (

ECHO %CF4% Isn't Running ...

)

GOTO NINE

ECHO ====================================================================================================

ECHO.

ECHO Need To Stop a DataBase?

ECHO.

ECHO ====================================================================================================

CHOICE "/C:YN"

IF ERRORLEVEL ==2 GOTO NINE

IF ERRORLEVEL ==1 GOTO SIX

:FOUR

SC QUERY %Mysql% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 0 (

SC START %Mysql% >> code.txt

ECHO %Mysql% Is Cranking Up ...

) ELSE (

ECHO %Mysql% Is Already Cranked Up ...

)

GOTO NINE

:THREE

SC QUERY %SBR% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 0 (

SC START %SBR% >> code.txt

ECHO %SBR% Is Cranking Up ...

) ELSE (

ECHO %SBR% Is Already Cranked Up ...

)

SC QUERY %MSSQL% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 0 (

SC START %MSSQL% >> code.txt

ECHO %MSSQL% Is Cranking Up ...

) ELSE (

ECHO %MSSQL% Is Already Cranked Up ...

)

GOTO NINE

:TWO


CHO ====================================================================================================

ECHO.

ECHO 1. MSSQL (Exp)

ECHO 2. MySql

ECHO 3. Just Exit (N/A)

ECHO.

ECHO Which Database Are You Cranking Up?

ECHO ====================================================================================================

CHOICE "/C:123"

IF ERRORLEVEL ==3 GOTO NINE

IF ERRORLEVEL ==2 GOTO FOUR

IF ERRORLEVEL ==1 GOTO THREE

:ONE

SC QUERY %CF1% |FIND /i "RUNNING" > code.txt

IF !ERRORLEVEL! NEQ 0 (

SC START %CF1% >> code.txt

ECHO %CF1% Is Firing Up ...

) ELSE (

ECHO %CF1% Is Already Cranked Up ...

)SC QUERY %CF2% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 0 (

SC START %CF2% >> code.txt

ECHO %CF2% Is Firing Up ...

) ELSE (

ECHO %CF2% Is Already Cranked Up ...

)

SC QUERY %CF3% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 0 (

SC START %CF3% >> code.txt

ECHO %CF3% Is Firing Up ...

) ELSE (

ECHO %CF3% Is Already Cranked Up ...

)

SC QUERY %CF4% |FIND /i "RUNNING" >> code.txt

IF !ERRORLEVEL! NEQ 0 (

SC START %CF4% >> code.txt

ECHO %CF4% Is Firing Up ...

) ELSE (

ECHO %CF4% Is Already Cranked Up ...

ECHO.

)

ECHO ====================================================================================================

ECHO.

ECHO Need Data?

ECHO.

ECHO ====================================================================================================

CHOICE "/C:YN"

IF ERRORLEVEL ==2 GOTO NINE

IF ERRORLEVEL ==1 GOTO TWO

:END

If any one has some suggestions here - Feel free to share your opinion - This is one of the first batch scripts I've created so I'm sure there could improvements ... That's it.

5 Great Examples of SEO Friendly JavaScript & CSS Menus

Florida SEO Says, "Dear JavaScript ... Will You Ever Forgive Me? "

In my last post ... I attempted to explain how pure JavaScript menus were not good for SEO. However, after reading how the message came across to some of my readers, I feel it's probably best if I add some clarification. Thanks to Dan Switzer at PengoWorks for pointing out that my post needed some explanation. What I mentioned regarded the usage of pure JavaScript menus -Not All- JavaScript menus. If my post confused anyone, I apologize. There are lot's of SEO friendly menus that incorporate both JavaScript and yet maintain good web design practices ... So, now let's take a look at a few that stuck out with me.

In my humble opinion, menus that use JavaScript to manipulate the DOM (Document Object Model) and CSS (Cascading Style Sheets) to handle styling, maintain a good balance between usability and provide a dynamically appealing experience.

Okay ... So Show Me Some Cool Stuff Now

Before I show the following examples I want to point out the main factor that make these menus search engine friendly is the accessibility of the links to the robots. These menus do not rely on JavaScript to display the content either.

The first example here is from one of the most respected names in design ... A List Apart. Here Dave Shea provides an excellent detail on creating a menu implementing CSS Sprites with JQuery.

1. CSS Sprites2 - It's JavaScript Time

A List Apart
by: Dave Shea

CSS Sprites - A List Apart

Next, one of my favorite designers, Collis Ta'eed provides in exhaustive detail, everything required to create a very attractive and effective tabbed content area using CSS & JQuery ...

2. Slick Tabbed Content Area using CSS & JQuery

NetTuts
by: Collis Ta'eed

Slick Tabbed Content Area using CSS & JQuery

Soh Tanaka offers a very clean and attractive JQuery & CSS menu. Aside from issues with IE6's non-support of the :hover psuedo-class for elements other than anchor tags, this is a very good menu and degrades gracefully with JavaScript disabled.

3. Sexy Drop Down Menu w JQuery & CSS

Noupe
by: Soh Tanaka

Sexy Drop Down Menu w JQuery & CSS

Clark gives a solid example of how to create an animated drop down menu that also degrades well with JavaScript disabled - The key here is that the animation on the list elements is acceptable regardless of whether the visitor has JavaScript enabled.

4. Animated Drop Down Menu with jQuery

ClarkLab

Animated Drop Down Menu with jQuery

Okay ... I'm not a great designer by any sense. But, when I created this site I decided I wanted a little DHTML action and of course promote good SEO techniques at the same time ... So, I used Adobe's Spry  Accordion Widget to build my right navigation ... It too degrades gracefully with JavaScript disabled - The menus tabs will simply open up if JavaScript is not activated.

5. SEO Compliant Spry Accordion Menu

SEO Compliant Spry Accordion Menu

by: Adobe Lab

There are many excellent examples out there ... I really just wanted to take a few minutes to clarify the difference between a pure JavaScript menu and a menu that uses JavaScript and CSS.

That's it.

Florida SEO - SEO Fundamentals JavaScript vs Text Based Menus

SEO 101: Search Engine Robots Can't Follow Links in Pure JavaScript Menus

I've recently been receiving some interesting quotes for SEO services with Florida Search Engine Optimization. As part of the standard procedure I employ when reviewing a prospective client's site, I inform them that site architecture is very important and therefore must be taken into consideration. On several occassions I've suggested the removal of JavaScript based menus, and was met with a challenge. One of the prospective clients asked me why it was neccessary. So, I explained the mechanics of a crawler to him in some detail. The other prospective client wasn't all that trusting at first, so he decided to get a second opionion from another SEO before giving me the go ahead. I didn't have any problem with that ... I've been doing SEO for a while now and I've yet to find a client that accepted my opinion of a site review on blind faith regardless of the successful campaigns I've demonstrated.

SEO Experience Required - A Case for SEO Standardization?

What happened next really suprised me. I expected the need to debate my case with the prospective client that was not all that warm to me. However, I was actually opposed by both client's SEO's. Yes, that's correct. Both of the prospective client's SEO's were baffled as to why I suggested having text based menus. Oh yeah ... we SEO's tend to think they know everything ...

After receiving the suggestion for the menu removal, one of the SEO's mentioned to my client, "It's not important ... just make an XML sitemap so the robots can find you ... " I found that rather amusing. However, I found the other SEO's comment to be just as interesting as the first's. He simply stated that "He's even made Flash based sites rank on the first page ... " Thing is, the client requested a quote for services with me primarily due to the SEO's lack of effectiveness ... said SEO didn't so much as provide one case-in-point example - Pwned. So an experienced SEO is essentially stating that by providing an XML sitemap to the navigation of a site, serious issues arising from JavaScript based menus can be resolved. Okay - I don't think so ... But, let's see how accurate this is ... or isn't.

To Crawl or Not to Crawl? That Is ... Important.

A web site's internal link structure is very important when considering SEO. Almost any Decent SEO will attest to this. So why is it important? Because a web site is really nothing more than a group of pages linked together. Granted the linking relationships can become quite complex but it's really just a  Web of Links. When robots can't determine what is and isn't a link, they can't move from link A to B ... Link A was never realized.

To demonstrate ... Let's take a look at a site that employs JavaScript navigation and may not neccessarily require having highly crawlable links. I'm going to use Deluxe-Menu.com for this example - It's a very nice site that uses a JavaScript based menu, and does so with impunity - The site's entire theme is provided on the home page.

From the Eyes of a Spider - Where's the Web?

JavaScript Based Menu

So what we see here is a screen shot of the site's navigation ... We can clearly see the links for Home, Product Info, Samples, Download, Purchase and Support in the menu. Simple enough. However, if we inspect the internal linking of the page through a text-only browser such as Lynx,  it also shows that there are no visible traces to these links. The screen shot below is from the SEO text browser over at domaintools.com which, I have quite a habit of using ...

Text Browser View

So what are we're looking at here? This basically provides a detail of the elements a search engine robot would recognize when they visit a site. Blue signifies an anchor or link element. Red signifies images - for this example it isn't relevant to us whether those images are also links. The first link we see at the top of the page is for the site's logo, deluxe-menu.com. The next link we see in the document structure is for the site's sitemap which comes directly after the JavaScript menu. Notice how there are no links elements recognized by the text browser for the menu. A crawler would not identify this as a link either.

Okay, so I wouldn't expect someone in the C-Suite to be able to gauge the performance or effectiveness of a web site. But for an SEO? This should be first year  ... I find an unusually high amount of people claiming to be a "professional SEO" that don't know the first thing about web design, web development, Server Side programming or even HTML for that matter ...

In short ... This is old hat.

Google RESTful Ajax » JSON Search in ColdFusion

While I was hacking and cursing my way through a routine to convert RSS feeds in to HTML ... I had an idea. I thought, "Wow, maybe there's another way to do this ..." Converting RSS can be messy so I opted for a much easier and cleaner solution ... JSON. For those Ajax pros out there ... have a heart ... I'm definitely a nOOb at JSON ... I just wanted to share my experience because the introduction and experience was enlightening ... at least to me it was.

Before I go in to this, I am offering this obligatory warning. What I'm about to do may not be in compliance with Google's T.O.S. So, if you get yourself in a pickle with the big G ... It's on you. You've been warned.

What I wanted to do is create dynamic and fresh content for say ... Oh the news. That's a no brainer. Typically you could just grab some RSS feeds and embed them in your content. So, What if you wanted the HTML from say ... Google's news ... Hmmm? You could use a SOAP request to Google for the info ... But SOAP won't be supported by Google for much longer ... So, that's no good. That's where Google's RESTful JSON interface comes in. According to Google documentation for their AJAX Search API, The interface was created for developers that need to have access to Google's Search API in non-JavaScript environments. The docs provide the base URL's to retrieve results for several of their searchers. Here' s a list of all the types of searchers you can access remotely.

  • Web Search: http://ajax.googleapis.com/ajax/services/search/web
  • Local Search: http://ajax.googleapis.com/ajax/services/search/local
  • Video Search: http://ajax.googleapis.com/ajax/services/search/video
  • Blog Search: http://ajax.googleapis.com/ajax/services/search/blogs
  • News Search: http://ajax.googleapis.com/ajax/services/search/news
  • Book Search: http://ajax.googleapis.com/ajax/services/search/books
  • Image Search: http://ajax.googleapis.com/ajax/services/search/images
  • Patent Search: http://ajax.googleapis.com/ajax/services/search/patent

So I decided to have my hand at grabbing some news ... Here we go ...

view plain print about
1<!--- Create a Couple Vars for the Search Params ... ---> 
2<cfset gQuery = #ReReplaceNoCase("ColdFusion","\s+","%20","ALL")#>
3<cfset qRegion = #ReReplaceNoCase("Fort Lauderdale","\s+","%20","ALL")#>
4
5<!--- Call Google's AJAX Service ---> 
6<cfset gData = "http://ajax.googleapis.com/ajax/services/search/news?v=1.0&rsz=large&#qRegion#&q=#gQuery#">
7
8<!--- Save the Result to a Var ---> 
9<cfhttp
10    url="#gData#"
11    method="get"
12    result="gDataResult">

13</cfhttp>
14
15<!--- Make the Data is JSON --->
16<cfset gData = #SerializeJSON(gDataResult,false)#>
17
18<!--- Clean up the Result Data with RegEx --->
19<cfset gData = #REReplace(gDataResult.FileContent, 
20        "^\s*[[:word:]]*\s*\(\s*","")#
>

21<cfset gData = #REReplace(gData, "\s*\)\s*$""")#>
22
23<!--- Make Sure We Have JSON ... --->
24<cfif !IsJSON(gData)>
25  <h3>Uh Oh ... Somthing Went Terribly Wrong ... <br />
26      But! Don't Fret ... Coders are Hard at Work to Get Things Up ... <br />
27      Right Away ...  ;)
28  </h3>
29
30<!--- If the JSON is Good, Deserialize It. --->
31<cfelse>
32    <cfset gData = #DeserializeJSON(gData)#>
33
34<!--- Create a Var for the Reponse Data    --->
35<cfset response = #gData.responseData.results#>
36  <cfloop from="1" to="#ArrayLen(response)#" index="ndx">
37    <cfoutput>
38      <p><strong>
39          <a href="#response[ndx].unescapedurl#" rel="nofollow" 
40          target="_blank">
#response[ndx].titleNoFormatting#</a>
41          </strong><br /><br/>
42          #response[ndx].content# <br /> <br />
43      </p>
44    </cfoutput>
45  </cfloop>
46</cfif>

The Google Search API's Class reference has a list of all the arguments you can include as URL parameters that will give you plenty of options to customize your results ...

Florida Search Engine Optimization » Apache MultiViews » Mod_Rewrite

Apache apparently has a server module call mod_negotiation which contains an option called MultiViews. MultiViews is a setting that gives the server the ability to decide the best representation for a requested file that doesn't exist. Furthermore, If MultiViews is enabled on the server, it will take operational precedence before any mod_rewrite directives that you provide in your .htaccess files. Had I been aware of this setting being turned on by default with my host, I would have requested for them to turn if off. However, I was completely oblivious that the setting even existed. The Apache documentation states ...

MultiViews » I should have known

My issue with MultiViews was discovered after a WordPress installation went south. I had installed WP 2.7 with a theme that I created and a group of plug-ins which I typically use. After creating a page for the HTML sitemap, I browsed the URL to make sure everything was working correctly. The destination URL was www.website.com/sitemap/, yet when I browsed to the page, the server was re-writing the URL as www.website.com/sitemap.xml/ which incidentally did not exist. Now, I also had Arne Brachold's Google Sitemap Generator for WordPress installed and I figured that there must be something wrong with either my .htaccess files or his sitemap plug-in on WP 2.7. After uninstalling his plug-in and several others in an attempt to solve the mysterious problem, checking and re-checking my .htaccess files, re-installing WordPress several times and arguing with my host that I have exhausted practically all efforts ... I became quite irritated. It was only when one of the tech support guys that had some experience with Apache realized the issue and sent me an e-mail informing me that the issue was due to MultiViews being enabled on the server. He suggested that I should have had requested to have it turned off ... I should have known.

Woe to you ... on shared hosting

If you are on shared hosting, you may not even know that some shared hosting accounts come equipped with the mod_negotiation module activated by default. If you are using the Apache2handler and want to find out yourself, just create a .php file with phpinfo(); in it and run that ... you should be able see if the mod_negotiation module is loaded ... If you are using the CGI/FastCGI such as I am, I don't know how you can tell ... phpinfo(); doesn't reveal those settings ... for the record contact your host.

SEO Side Effects » Duplicate Content with MutiViews

After doing some research and about MutiViews, I found a thread on WebMasterWorld stating that there may be duplicate content issues from using the MultiViews as well. Apparently MutiViews will allow for multiple URL's to point to the same content ... If you are having issues with duplicate content ... or if you can't seem to get your mod_rewrite directives to work as expected, you may want to take a look at the fantastic article in WebMasterWorld by: JDMorgan "A guide to fixing duplicate content & URL issues on Apache" ...

That's it.

Florida SEO » LinkDiagnosis » One Fantastic SEO Tool

While doing my morning crawl through my email I ran across an article from Eric Enge of Stone Temple Consulting regarding a recent interview he had with Angus Norton of Microsoft ... after reading the article, I was persuaded to go check out Eric's site to see if there is anything valuable to the SEO world that he was kind enough to share with the rest of the us ... and sure enough there was ...

Perusing through the linkbuilding category on his site, I found a reference to LinkDiagnosis.Com ... knowing Eric to be of a discriminating taste, I investigated a little more ... and I'm glad I did ... This is one gold mine of a tool ...

Once you go to LinkDiagnosis.Com ... you need to click on the download link and install the firefox extension (Sorry IE users) and then restart your browser ... then return to the page and enter your URL or your competitions ...

I'll let you be the judge of the rest ...

That's it ...

ColdFusion Ajax & JQuery ... Learn to Earn

Recently I decided that it's time to start paying a bit more attention to improving the visual presentation of one of my sites. The site's built in ColdFusion with lot's of nifty CF Ajax stuff thrown in. CFDiv's, CFWindows, and Pods here and there too. So, I figured since I'm not too bad at coding in CF - at least I can usually Google my way through most of the snags I run in to - I wanted to try challenging myself a bit by taking on something fairly foreign to me. Ajax.

I know the basics of Ajax. Asynchronous JavaScript with XML - Sending and receiving Http requests and responses to a server to return data to a site without refreshing the documents. However, I had little experience actually taking on an Ajax project. But if that wasn't difficult enough, I wanted to get really frustrated, so I decided to use a JavaScript library that I had little if any experience with - Entrance JQuery.

Now I've seen a lot of very groovy examples of the nifty UI tricks that can be done with JQuery. They are cool. But I really wanted to learn how to use JQuery for my specific needs. So, I did. I choose to launch my campaign with a real world example. So for my project, I mapped out what I wanted to do with ColdFusion, JQuery, and Some CFAjax stuff. Here's what I planned out ... First, I wanted to add a US database to my site, SEOMasterList.com, to build out some dynamic pages. The reasons for that should be quite apparent. I'm planning to obtain rankings and possibly monetize the site.

Next, I wanted to add some cool visual effects while providing improved functionality to the site, and possibly give visitors a better experience.

And finally, I just wanted to see if I could actually do it - two weeks in to it, I finished it. I have got to admit I spent a hell of a lot of time learning while trying to get the site done - someone with experience could have probably launched it in a matter of no time. But, I learned a lot.

Once I modified, installed, and built out the components to accommodate the addition of the database, I set up another component to dynamically bind the results from the database to two selects in a form. This was very challenging - I spent the better part of two days just trying to get the bindings to work. Luckily, I found a cool ColdFusion script and modified it to output my database queries to XML. That came in really handy. The first go at trying to output 80,000+ records to a single flat XML file didn't work out to well. I ended up rebuilding the data CFC's several times in order to get what I needed. I ended up with a 1.4 MB XML file that I'm now using to bind to the data in the selects.

After tackling that part of the project, I wanted to introduce some visually appealing effects to the site so I picked up the latest JQuery code and sent it up to my site. After some effort, I was successful in creating a pretty slick interface for my Ajax loaded form. Through the use of some nifty ('#div').show(),('#div').hide () functionality on the form page, I was able to add and remove elements of the page which weren't necessary at every stage of the form submission process. This proved to be tougher to implement than I had expected. Up to this point I have a form bound by CFC's to data, an asynchronous form handler to process a form submission, and some nifty JQuery going on to assist in dressing up my application modifications. All in all. I think it went really well - and I learned a whole hell of a lot doing it. If you want to see what I did ... have a heart ... I'm an Ajax nOOb. Check it out on SEOMasterList ...

Florida SEO » CFEclipse » Aptana Migration

I learned ColdFusion in DreamWeaver. For that matter, I've learned HTML, CSS, JavaScript and PHP in DreamWeaver too, so it's been fairly difficult for me to completely switch over to Eclipse even though I actually prefer working in Eclipse more than I do DreamWeaver. For some reason I just feel like I'm 'really' coding when I'm working in Eclipse. I don't feel like a 'serious' programmer when I'm working in DreamWeaver. So, I've decided to share some of the things I've done to make the migration a bit more tolerable.

First off I have Eclipse 3.3.1.1 Europa J2EE. I'm sticking with that platform for now because the latest Eclipse release, Ganymede, doesn't play to well with CFEclipse.

Then I installed CFEclipse, and added the ColdFusion extensions for Eclipse, the ColdFusion 8 help files for Eclipse, the ColdFusion 8 Syntax dictionaries, and finally plugged CFEclipse in to the CFLib and CFSnippets snippex servers.

After that, I installed Aptana. Except for the annoying 'upgrade to pro' messages they send, Aptana's really nice. What I really like about Aptana, is it's great HTML, CSS and JavaScript functionality. Coding web pages in Aptana is a snap, and since I'm a snippets freak, Aptana's snippets view helps me make the transition to Eclipse that much easier. The most important thing I needed from Aptana though, was remote FTP view. Being able to FTP from within the IDE was definitely keeping me tied to DreamWeaver, and by removing the key bindings in Eclipse for CTRL+Shift+U, and CTRL+Shift+D, I was then able to take advantage of the default GET and PUT FTP shortcuts that were so familiar to me from working in DreamWeaver. CTRL+Shift+U (PUT File via FTP ), CTRL+Shift+D (GET File via FTP). I live by these shortcuts.

Snippets are a way of life for me in DreamWeaver. Once I create something reusable, it goes in to a snippet folder, and I assign a keyboard shortcut to it. One thing that really sold me on CFEclipse, was the awesome job Mark Drew did on making snippet variable regions. That's too damned cool. For working with HTML, CSS and JavaScript snippets, I've just added the snippets view from Aptana to my CFEclipse perspective and the same with my Aptana view, I added the sniptree view to my Aptana perspective. That way, I can get the best of both worlds. For more customizations, I decided to install JSEclipse for my JavaScript work. JSEclipse is also really nice.

So, it may still take me a bit to get used to Eclipse, but I'm going to do my damndest to make it my primary IDE.

Google Chrome » Rocks

So my first impression about Google's new Chrome browser, is that it Rocks. It's quite apparent that the engineers at Google know exactly what the development community is looking for and they make no small feat in creating a web browser that is able to completely annihilate the competition. Moreover, I really like some of the features that are included by default like the Firebug style DOM inspector that you can invoke by a right click context menu (Nice).

Chrome Rocks

In my first shot at testing out the new browser's speed, I attempted to pull up a Google Map that I have on my site which makes a remote call to Google's GeoCoder object to retrieve coordinates for a city to update the map locations based on those coordinates. Though I should actually have the coordinates in a Google GeoCoder Cache to speed up the process, I don't » yet.

The results were amazing. while it took Firefox 2 approximately 4 seconds to fully display the map image, it took all of 500 milliseconds for Chrome to display. My results are just from personal experience though. Google claims that Chrome's new JavaScript engine, V8, shows remarkable performance levels and they provide a collection of benchmark test results to boast. [Chrome Test Results.]

According to the developers over Mozilla, the new browser is not as impressive as Google may claim and they also ran some tests on Chrome with a JavaScript testing engine named SunSpider. Mozilla claims that the new TraceMonkey JavaScript engine slated to be included in a future edition of Firefox, will perform even faster than V8. Things really start to get interesting though when JQuery's inventor, John Resig, decided to run some of his own performance tests on Chrome. In short » John agrees that Chrome's JavaScript engine is really fast.

Hey » I Want Your Regular Expressions » Now

I really like regular expressions (RegEx's) and I have this crazy fascination with learning and using them whenever and wherever I get a chance. If you don't know what a regular expressions is, the Sun Developer's Network's states:

"A regular expression is a pattern of characters that describes a set of strings." » Simple enough.

I picked up the bug for RegEx's after trying to "think my way through" a JavaScript form validation function without any experience in regular expressions or JavaScript for that matter.

That event led me to pick up Mastering Regular Expressions, III ed. By Jeffrey Friedl; OReilly's JavaScript, The Definitive Guide, By David Flanagan, and a fierce desire to learn more about the almost alien-looking esoteric characters that were here to take over my programming world.

The good news I found out later was not only that I am not the only RegEx fan that has been drawn in by their mysterious attraction. Many of the coders and developers that I've found to respect, have this crazy bug too.

So, what I want to do now, is put together a RegEx Library right here on this post for all of the coolest regular expressions you know. So, post me your regular expression(s), with a brief description.

Delicious Goes Dot Com » Changes Page Layout

News travels fast on Twitter, and while I was checking out my updates, I saw that Delicious has changed their domain name from del.icio.us to the dot com delicious.com. I don't typically like change too much, but I think it will definitely help the site attract more visitors transferring over to the simpler name. I can't even count the amount of people that I tried to get started with delicious bookmarks that were completely confused with del.icio.us.

Changing the domain name wasn't what really stood out the most for me though. When I browsed over to my delicious bookmarks page to see the new name in my browser, I noticed that they completely changed the layout too.

New Layout for Delicious

Now I do a lot of bookmarking in delicious, but I usually don't go to the actual page that much since I have delicious bookmarks for Firefox installed - I just do all my bookmarking from the browser and don't actually go see the site.

The first thing I like about the new page design is the much simpler and probably more effective style of managing the bookmarks. With over 1800 bookmarks ... it gets really tough to handle the hundreds of different tags and bundles I've placed them all in.

That's It.

Florida SEO » CFC Problem » Solved

In my last post, I was ranting about a frustrating situation with CFC's on shared hosting over at HostMySite. Several months ago, I installed a rather cool CFC that only had a life span of three days - it worked fine - then all of a sudden - it stopped working.

A few weeks ago the same scenario took place - I installed a component on another one of my CF sites, SEO Master List - It ran fine for three weeks and the all of a sudden - it stopped working.

After many hours of confusion with the guys over at HostMySite, it turns out that occasionally during server resets the security permissions are changed. I really don't know whether there's someone to blame for it, but at least I know the why of it if not the how.

So, if you have a CF site on shared hosting over at HMS and find your CFC's stop working and your pretty certain that they should be, make sure to take a look at the security settings - That's it.

CFC Madness » Where's The Damned Component?

Okay, So I might be a bit frustrated here ... I have had ongoing issues with CFC's at HostMySite, and I can't for the life of me find the root cause to my dilemma. If someone could give me a clue ... I would really appreciate it. My problem follows ...

I have shared hosting accounts with HostMySite for my CF sites. A few months back I attempted to work with the CFlickr CFC on my blog ... However, after a few days of being in operation, I started to receive errors stating that ...

" The value of the Function Foo is not of type components.someComponent. "

After working with tech support for a few days, our first guess was that there was a mapping issue with the CFC, and that we just needed to update the mappings ... after checking the mappings several times, reinstalling the component several times, and having support finally tell me that "there must be something wrong with the code", I abandoned the project feeling a bit ... beaten.

Recently I created a feed aggregator based on the same code from ColdFusionBloggers ... For several weeks the site ran fine ... no problems. But recently, I tried to login to the admin area ... and I started to receive errors by email that were strikingly similar to the errors I had with the CFlikr CFC. This time I'm getting ...

" The value returned from the getComponent function is not of type components.entries "

So, I contacted tech support, only to hear the familiar lines ... "We'll create a ticket ... and call you when we know something ... " Which usually means I'll have to figure it out for myself. Now, I don't quite understand why a component would just stop working without any changes to it ... but I'm starting to think that this issue is not something that I caused. If there's a CFer out there that has had a similar experience and found a solution ... could you please throw me a rope?

That's it.

Florida Search Engine Optimization » SEO Master List Is Alive

Well, It's alive ... I've launched SEO Master List. I have high expectations for this application and hope that it is well received in the SEO community. Which, brings me to another subject. SEO's, can be quite pretentious and highly critical ... So, we'll just see how they like it. Then again ... if they don't ... I really dont' care all that much. I like it.

SEOMasterList is basically just a feed aggregator with a lot of cool Ajaxy stuff on it and some cool styling. Ray Camden actually created the app ... the original code is on coldfusionbloggers.org. A Big thanks goes out to him for his help in debugging the install too ... Thanks Ray.

For those that want their 'SEO' feeds aggregated on SEOMasterList.com ... send me an e-mail and I'll get right on it ... maybe. If your feed wasn't aggregated, and I tried to add it to the list ... there may have been a problem with the xml structure - there were many good SEO blogs that I was unable to parse due to xml rendering issues ...

Thats it.

Florida Search Engine Optimization » Has A New Bag

I've come up with a very cool new app that I put together ... The base code for the app comes from one of the baddest CF dudes around, Ray Camden, the ColdFusionJedi. I have really big expectations for this new app, and cannot wait to release it to see how the SEO community takes to it ... sorry, but I just can't say what it is ... yet.

SQL UDF to Convert Uppercase to Propercase

So, I figured that it would be a good idea to try and expand Florida Search Engine Optimization web coverage beyond the Miami, Fort Lauderdale areas. So, I started a project to build a quazi regional network in my site. At first, I tried to be creative and store all of the cities and counties of Florida in an array with the counties as an array of structs.

With some work, I was able to get this running. However, I realized that if and when I want to make changes or additions to the cities or counties, it was going to be quite a bit of work.

After some consideration I decided that it would be much easier to just use a database table with the cities and counties.

Now, everything was going quite well with the addition of the new database when I found that the city and county fields were all in uppercase ... I really didn't want to leave the data like that, so I decided to figure out how to change the 80,000 plus records to propercase, and I found just the thing ... a cool little T-SQL UDF ... that changes the data to propercase ... the code follows ...

view plain print about
1create FUNCTION PROPERCASE
2(
3--The string to be converted to proper case
4@input varchar(8000)
5)
6--This function returns the proper case string of varchar type
7RETURNS varchar(8000)
8AS
9BEGIN
10IF @input IS NULL
11BEGIN
12--Just return NULL if input string is NULL
13RETURN NULL
14END
15
16--Character variable declarations
17DECLARE @output varchar(8000)
18--Integer variable declarations
19DECLARE @ctr int, @len int, @found_at int
20--Constant declarations
21DECLARE @LOWER_CASE_a int, @LOWER_CASE_z int, @Delimiter char(3), @UPPER_CASE_A int, @UPPER_CASE_Z int
22
23--Variable/Constant initializations
24SET @ctr = 1
25SET @len = LEN(@input)
26SET @output = ''
27SET @LOWER_CASE_a = 97
28SET @LOWER_CASE_z = 122
29SET @Delimiter = ' ,-'
30SET @UPPER_CASE_A = 65
31SET @UPPER_CASE_Z = 90
32
33WHILE @ctr <= @len
34BEGIN
35--This loop will take care of reccuring white spaces
36WHILE CHARINDEX(SUBSTRING(@input,@ctr,1), @Delimiter) >
 0
37BEGIN
38SET @output = @output + SUBSTRING(@input,@ctr,1)
39SET @ctr = @ctr + 1
40END
41
42IF ASCII(SUBSTRING(@input,@ctr,1)) BETWEEN @LOWER_CASE_a AND @LOWER_CASE_z
43BEGIN
44--Converting the first character to upper case
45SET @output = @output + UPPER(SUBSTRING(@input,@ctr,1))
46END
47ELSE