(function($){
   /**
    * jQuery script to format all <pre> elements with class "syntax_hilite"
    * with keyword syntax hiliting CSS.  If you don't specify the language in the
    * <pre> class, it will assume Javascript.  Options: c, cpp, java, js
    *
    * @author Brett Fattori (bfattori@fry.com)
    * @version 1.0
    *
    * Requires jQuery 1.2.6 or higher
    */
   var keywordsJS = ["debugger","as","break","case","catch","class","const","continue","default","delete",
                   "do","else","export","extends","false","finally","for","function","if","import",
                   "in","instanceof","is","namespace","new","null","package","private","public","return",
                   "super","switch","this","throw","true","try","typeof","use","var","void","while","with"];

   // Not currently used, but reserved for future use 
   var futureJS = ["abstract","enum","goto","implements","interface","native","protected","synchronized",
                 "throws","transient","volatile","boolean","byte","char","double","final","float","int",
                 "long","short","static"];

   var keywordsC = ["auto","break","case","char","const","continue","default","do",
                    "double","else","enum","extern","float","for","goto","if",
                    "int","long","register","return","short","signed","sizeof","static",
                    "struct","switch","typedef","union","unsigned","void","volatile","while"];

   var keywordsCPP = keywordsC.concat(["bool","catch","class","delete","friend","inline",
                     "new","namespace","operator","private","protected","public",
                     "tempate","this","throw","try","template"]);

   var keywordsJava = ["abstract","continue","for","new","switch","assert","default","goto","package","synchronized",
                       "boolean","do","if","private","this","break","double","implements","protected","throw",
                       "byte","else","import","public","throws","case","enum","instanceof","return","transient",
                       "catch","extends","int","short","try","char","final","interface","static","void",
                       "class","finally","long","strictfp","volatile","const","float","native","super","while"];
   
   // Strings
//   var stRE = "(([\"'])[^\\2\\\\\\n\\r]*?\\2)";
   var stRE = "(([\"'])(\\\\2|[^\\n\\r])*\\2)";

   // Regular expressions
   var rxRE = "(\/[^\\n\\r]*\/[gim]*)([,\.;\)])";

   // Single line comments
   var slRE = "(\/\/[^\\n\\r]*?$)";

   // Multi line comments
   var mlRE = "(/\\*(\\n|.)*?\\*/)";                  

   // Hex
   var hxRE = "(\\b0[xX][0-9a-fA-F]+\\b)";

   // Numbers
   var nmRE = "(\\b(([0-9]+)?\\.)?[0-9]+\\b)";

   // Process marked code blocks when document is ready
   $(document).ready(function() {
      $("pre.syntax_hilite")
         .each(function(i, e) {
            var E = $(e);
            
            // Keywords as a regex string
            var lang;
            if (E.hasClass("c")) {
               lang = keywordsC;
            } else if (E.hasClass("cpp")) {
               lang = keywordsCPP;
            } else if (E.hasClass("java")) {
               lang = keywordsJava;
            } else {
               lang = keywordsJS;
            }
               
            var kwRE = (function(kSet){ 
                        var l = "(\\b("; 
                        for (var k in kSet) { 
                           l += (l.length > 4 ? "|" : "") + kSet[k];
                        } 
                        return l + ")\\b)";
                      })(lang);


            // Formatter regular expression
            var ff = stRE + "|" + rxRE + "|" + slRE + "|" + mlRE + "|" + hxRE + "|" + nmRE + "|" + kwRE;
            var fRE = new RegExp(ff, "gm");

            var t = E.html();
            t = t.replace(/</g,"&lt;").replace(/>/g,"&gt;");
            t = t.replace(fRE, function(m, strn, s1, s6, regex, s2, slcomm, mlcomm, s3, hexnum, num, s4, s5, keywd) {
               if (strn) { return "<span class='string'>" + strn + "</span>"; }
               if (regex) { return "<span class='regex'>" + regex + "</span>" + s2; }
               if (slcomm) { return "<span class='comment'>" + slcomm + "</span>"; }
               if (mlcomm) { return "<span class='comment'>" + mlcomm + "</span>"; }
               if (hexnum) { return "<span class='hex number'>" + hexnum + "</span>"; }
               if (num) { return "<span class='number'>" + num + "</span>"; }
               if (keywd) { return "<span class='keyword'>" + keywd + "</span>"; }
            });
            if (E.hasClass("cpp")) {
               // Remove the last line as it may contain tags
               // TODO: Can this be done better??
               var a = t.split("\n");
               t = a.splice(-a.length, a.length - 1).join("\n");
            }
            
            // Add line numbers
            if (E.hasClass("numbered")) {
               var ln = 1;
               var tln = t.split("\n").length;
               var pad = tln / 10;
               var lnRE = /^/gm;
               t = t.replace(lnRE, function(m) {
                  return "<span class='line-no'>" + (ln++) + ": </span>";
               });
            }
            
            E.html(t);
         });
      });
})(jQuery);  