Une Bévue wrote:
Hi,
i'm in search of an online syntax highlighter able to highlight either
JavaScript, CSS or HTMl only or a whole page.
I am aware of no such tool, so cannot advise properly in this regard.
Still, the topic is quite interesting, so I thought I'd try and write
some pure javascript highlighter.
The following, being regexp-based, is rather short and simple. However,
regexp approaches unfortunately fail on certain lexical analysis
(especially statements mixing strings, regexps and comments, with tokens
like "/" and "*"), therefore the script will not be 100% accurate
(though probably enough for most uses).
Anyway, that was a fun thing to do to reorganize a mind blurred by the
regular Friday beers
Cheers,
Elegie.
The test case:
---
<script type="text/javascript" src="js-highlighter.js"></script>
<script type="text/javascript">
function foo(){
document.getElementById("result").innerHTML =
document.forms[0].elements["source"].value.highlightJS() ;
}
</script>
<form action="#">
<textarea name="source" rows="30" cols="40"></textarea>
<input type="button" value="Highlight!" onclick="foo()">
<pre id="result"></pre>
</form>
---
The javascript file (js-highlighter.js):
---
String.prototype.highlightJS = function() {
// ------------------------------------------------------------------
// Parameters
// ------------------------------------------------------------------
var
COLOR_STRINGS = "blue" ,
COLOR_COMMENTS = "green" ,
COLOR_NUMBERS = "red" ,
COLOR_KEYWORDS = "navy" ,
COLOR_REGEXPS = "gray" ;
var
SYMBOL_STRINGS = "s" ,
SYMBOL_COMMENTS = "c" ,
SYMBOL_NUMBERS = "n" ,
SYMBOL_KEYWORDS = "k" ,
SYMBOL_REGEXPS = "r" ;
// ------------------------------------------------------------------
// Main
// ------------------------------------------------------------------
var keywords = [
"in" ,
"break" ,
"else" ,
"new" ,
"var" ,
"case" ,
"finally" ,
"return" ,
"void" ,
"catch" ,
"for" ,
"switch" ,
"while" ,
"continue" ,
"function" ,
"this" ,
"with" ,
"default" ,
"if" ,
"throw" ,
"delete" ,
"try" ,
"do" ,
"instanceof" ,
"typeof"
];
var source=this;
var entities=[];
var esc=null;
var ii;
// define the 'escape char'
esc=new Date().getTime();
while(this.indexOf("x"+esc+"x")!=-1)
esc++;
esc="x"+esc+"x";
source=source.replace(new RegExp(esc,"g"), esc+esc);
// substitute text by pointer, for advanced expressions
source=substitute(source, SYMBOL_COMMENTS,
/\/\/[^\r\n]*/g);
source=substitute(source, SYMBOL_COMMENTS,
/\/\*([^*]|(\*+[^*/]))*\*+\//g);
source=substitute(source, SYMBOL_REGEXPS,
/\/(\\\/|\[(\\\]|[^\]\r\n])+\]|[^/\r\n])+\/[gim]*/g);
source=substitute(source, SYMBOL_STRINGS,
/"(\\"|[^"])*"/g);
source=substitute(source, SYMBOL_STRINGS,
/'(\\'|[^'])*'/g);
source=substitute(source, SYMBOL_NUMBERS,
/(\b\d+\.?\d*|(\b\d*)?\.\d+)(e[+-]?\d*)?/gi);
// same for keywords
for(ii=0; ii<keywords.length; ii++) {
source=substitute(
source,
SYMBOL_KEYWORDS,
new RegExp("\\b"+keywords[ii]+"\\b","g")
);
}
// clean overlapping in substituted values
// this does not solve all problems, though
for(ii=0; ii<entities.length; ii++) {
entities[ii] = reinject(
entities[ii],
"[" +
SYMBOL_STRINGS +
SYMBOL_COMMENTS +
SYMBOL_NUMBERS +
SYMBOL_KEYWORDS +
SYMBOL_REGEXPS +
"]"
);
}
// htmlize all parts
source=htmlize(source)
for(ii=0; ii<entities.length; ii++) {
entities[ii] = htmlize(entities[ii]);
}
// reinject 'colored' values
source=reinject(source, SYMBOL_STRINGS, COLOR_STRINGS);
source=reinject(source, SYMBOL_COMMENTS, COLOR_COMMENTS);
source=reinject(source, SYMBOL_REGEXPS, COLOR_REGEXPS);
source=reinject(source, SYMBOL_NUMBERS, COLOR_NUMBERS);
source=reinject(source, SYMBOL_KEYWORDS, COLOR_KEYWORDS);
// replace 'escape char'
source=source.replace(new RegExp(esc+esc,"g"),esc);
return source;
// ------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------
function substitute(source, symbol, match) {
source=source.replace(
match,
function(s){
entities[entities.length]=s;
return esc +
symbol +
(entities.length-1) +
esc ;
}
);
return source;
}
function reinject(source, symbol, color) {
source=source.replace(
new RegExp(esc+symbol+"(\\d+)"+esc,"gi"),
function(s, m){
return span(entities[m], color);
}
);
return source;
}
function span(s, color) {
return color ? "<span style=\"color:"+color+"\">"+s+"<\/span>" : s;
}
function htmlize(source) {
source=source.replace(/&/g, "&");
source=source.replace(/</g, "<");
source=source.replace(/>/g, ">");
source=source.replace(/\r\n|\r|\n/g,"<br>");
source=source.replace(/\s/g," ");
return source;
}
}
---