1
/* Unobtrustive Code Highlighter By Dan Webb 11/2005
5
Add a script tag for this script and any stylesets you need to use
6
to the page in question, add correct class names to CODE elements,
7
define CSS styles for elements. That's it!
11
Firefox/Mozilla PC/Mac
15
Known to degrade gracefully on:
18
Note: IE5.0 fails due to the use of lookahead in some stylesets. To avoid script errors
19
in older browsers use expressions that use lookahead in string format when defining stylesets.
21
This script is inspired by star-light by entirely cunning Dean Edwards
22
http://dean.edwards.name/star-light/.
25
// replace callback support for safari.
26
if ("a".replace(/a/, function() {return "b"}) != "b") (function(){
27
var default_replace = String.prototype.replace;
28
String.prototype.replace = function(search,replace){
29
// replace is not function
30
if(typeof replace != "function"){
31
return default_replace.apply(this,arguments)
34
var callback = replace;
35
// search string is not RegExp
36
if(!(search instanceof RegExp)){
37
var idx = str.indexOf(search);
40
default_replace.apply(str,[search,callback(search, idx, str)])
45
var lastidx = reg.lastIndex;
47
while((re = reg.exec(str)) != null){
49
var args = re.concat(idx, str);
51
str.slice(lastidx,idx),
52
callback.apply(null,args).toString()
55
lastidx += RegExp.lastMatch.length;
58
lastidx = reg.lastIndex;
61
result.push(str.slice(lastidx));
62
return result.join("")
66
var CodeHighlighter = { styleSets : new Array };
68
CodeHighlighter.addStyle = function(name, rules) {
69
// using push test to disallow older browsers from adding styleSets
70
if ([].push) this.styleSets.push({
73
ignoreCase : arguments[2] || false
77
// set highlighter to run on load (use LowPro if present)
78
if (typeof Event != 'undefined' && typeof Event.onReady == 'function')
79
return Event.onReady(CodeHighlighter.init.bind(CodeHighlighter));
81
var old = window.onload;
83
if (typeof window.onload != 'function') {
84
window.onload = function() { CodeHighlighter.init() };
86
window.onload = function() {
88
CodeHighlighter.init();
93
// only set the event when the first style is added
94
if (this.styleSets.length==1) setEvent();
97
CodeHighlighter.init = function() {
98
if (!document.getElementsByTagName) return;
99
if ("a".replace(/a/, function() {return "b"}) != "b") return; // throw out Safari versions that don't support replace function
100
// throw out older browsers
102
var codeEls = document.getElementsByTagName("CODE");
103
// collect array of all pre elements
104
codeEls.filter = function(f) {
106
for (var i = 0; i < this.length; i++) if (f(this[i])) a[a.length] = this[i];
110
var rules = new Array;
111
rules.toString = function() {
112
// joins regexes into one big parallel regex
113
var exps = new Array;
114
for (var i = 0; i < this.length; i++) exps.push(this[i].exp);
115
return exps.join("|");
118
function addRule(className, rule) {
119
// add a replace rule
120
var exp = (typeof rule.exp != "string")?String(rule.exp).substr(1, String(rule.exp).length-2):rule.exp;
121
// converts regex rules to strings and chops of the slashes
123
className : className,
124
exp : "(" + exp + ")",
125
length : (exp.match(/(^|[^\\])\([^?]/g) || "").length + 1, // number of subexps in rule
126
replacement : rule.replacement || null
130
function parse(text, ignoreCase) {
131
// main text parsing and replacement
132
return text.replace(new RegExp(rules, (ignoreCase)?"gi":"g"), function() {
133
var i = 0, j = 1, rule;
134
while (rule = rules[i++]) {
136
// if no custom replacement defined do the simple replacement
137
if (!rule.replacement) return "<span class=\"" + rule.className + "\">" + arguments[0] + "</span>";
139
// replace $0 with the className then do normal replaces
140
var str = rule.replacement.replace("$0", rule.className);
141
for (var k = 1; k <= rule.length - 1; k++) str = str.replace("$" + k, arguments[j + k]);
144
} else j+= rule.length;
149
function highlightCode(styleSet) {
151
var parsed, clsRx = new RegExp("(\\s|^)" + styleSet.name + "(\\s|$)");
154
// get stylable elements by filtering out all code elements without the correct className
155
var stylableEls = codeEls.filter(function(item) { return clsRx.test(item.className) });
157
// add style rules to parser
158
for (var className in styleSet.rules) addRule(className, styleSet.rules[className]);
161
// replace for all elements
162
for (var i = 0; i < stylableEls.length; i++) {
163
// EVIL hack to fix IE whitespace badness if it's inside a <pre>
164
if (/MSIE/.test(navigator.appVersion) && stylableEls[i].parentNode.nodeName == 'PRE') {
165
stylableEls[i] = stylableEls[i].parentNode;
167
parsed = stylableEls[i].innerHTML.replace(/(<code[^>]*>)([^<]*)<\/code>/i, function() {
168
return arguments[1] + parse(arguments[2], styleSet.ignoreCase) + "</code>"
170
parsed = parsed.replace(/\n( *)/g, function() {
172
for (var i = 0; i < arguments[1].length; i++) spaces+= " ";
173
return "\n" + spaces;
175
parsed = parsed.replace(/\t/g, " ");
176
parsed = parsed.replace(/\n(<\/\w+>)?/g, "<br />$1").replace(/<br \/>[\n\r\s]*<br \/>/g, "<p><br></p>");
178
} else parsed = parse(stylableEls[i].innerHTML, styleSet.ignoreCase);
180
stylableEls[i].innerHTML = parsed;
184
// run highlighter on all stylesets
185
for (var i=0; i < this.styleSets.length; i++) {
186
highlightCode(this.styleSets[i]);