List Injection for PRE Tags

This doc is outdated as there is a new version of the script. Please see LIPT: Inyección de listas a gogó (Spanish) while I prepare the English translation.

As suggested by Ingo Chao in a problem with the impression of the tag pre, I developed a Javascript function to convert any <pre class="code"> element into an ordered list. Now, for something completely different, a new version.

Improvements

Here's a demo


function foo() { // Just a demo
	//doSomething();
	doNothing();
}

How to use it

Download the Javascript source, put it somewhere in your server and point to it from an script tag in your HTML doc. Now you can define the CSS rules. The ordered list has a class name of code, every line has a class name of tab# (tab0, tab1, tab2, etc.) where the actual code lives inside a code element. Comments are marked as spans with class name cmt.

License

Anyone that you like. It'd be nice if you send me a note telling you are using this script or pay a beer for me ;)

Look at Ingo Chao's List Injection PRE Tags to find my inspiration source and alternative implementations. And please, send any comment about this code to choan@alice.0z0ne.com.

Have a nice day,
Choan C. Gálvez

The script


function lipt() {
	
	/* change to FALSE if you don't want to proccess comments (boogie buggy) */
	var PROCCESS_COMMENTS = true; 

	if (!document.getElementsByTagName) {
		return;
	}

	// look for pre tags in the doc
	var pres = document.getElementsByTagName('pre');

	if (0 == pres.length) {
		return; // no pre tags, nothing to do
	}

	for (var i = 0; i < pres.length; i++) {
		var pre = pres[i];

		// search ONE code tab inside the pre
		var code = pre.getElementsByTagName('code')[0];
		
		if (null == code) {
			continue; // no one here, try with the next pre tag
		}

		// go for the job
		var inMultiLineComment = false;
		
		var content = getText(code);

		// normalize new lines
		if (!window.opera) { /* Opera seems to have a nice bug with global replacements */
			content = content.replace(/\n|\r|\r\n/g, '\n');
		} else {
			content = content.replace(/\n|\r|\r\n/, '\n');
		}
		content = content.replace(/^\n*/, ''); /* trim empty lines at start */
		content = content.replace(/\n*$/, ''); /* trim empty lines at the end */

		var lines = content.split('\n');

		var ol = document.createElement('ol');
		ol.className = 'code';
		
		for (var j = 0; j < lines.length; j++) {
			var line = lines[j];
			line = line.replace(/\t/g, '    '); // replace tab with four spaces
			
			var cname = 'tab' + (Math.floor(countSpaces(line) / 4) - 1); // className for this line
			var restSpaces = countSpaces(line) % 4;
			line = line.replace(/^ +/, '');
			if (restSpaces) {
				for (var k = 0; k < restSpaces; k++) {
					line = '\u00A0' + line;
				}
			}
			var parts = new Array();

			if (inMultiLineComment) {
				parts = ['', line];
			} else {
				parts = [line];
			}

			if (PROCCESS_COMMENTS) {
				var slashSlashPos = line.indexOf('//');
				var starSlashPos = line.indexOf('/*');
				var slashStarPos = line.indexOf('*/');
				if (slashSlashPos != -1) {
					parts = line.split('//');
					parts[1] = '//' + parts[1];
				} else if (starSlashPos != -1) {
					if (inMultiLineComment) {
						parts = ['', line];
					} else {
						parts = line.split('/*');
						parts[1] = '/*' + parts[1];
					}
					inMultiLineComment = true;
				}

				
				if (slashStarPos != -1) {
					inMultiLineComment = false;
				}
			}

			var li = document.createElement('li');
			li.className = cname; // tab0, tab1, etc.
			var span = document.createElement('code');
			var code = document.createTextNode(parts[0]); 
			span.appendChild(code);

			
			if (parts[1]) {
				var cmt = document.createElement('span');
				cmt.appendChild(document.createTextNode(parts[1]));
				cmt.className = 'cmt';
				span.appendChild(cmt);
			}


			span.appendChild(document.createTextNode('\u00A0')); /* Unicode non-breaking space fix for Firefox */
			
			li.appendChild(span);
			ol.appendChild(li);
		};

		pre.parentNode.replaceChild(ol, pre);
		i--; // if we replace the pre, now there are n - 1 pre elements in the collection we are traversing
	};

	function countSpaces(s) {
		var spaceCount = 0;
		for (var i = 0; i < s.length; i++) {
			if (' ' == s.substr(i,1)) {
				spaceCount++;
			} else {
				break;
			}
		};
		return spaceCount;
	}

	function getText(node) {
		/* from JavaScript The Definitive Guide */
		var s = '';
		var children = node.childNodes;
		for (var i=0; i < children.length; i++) {
			var child = children[i];
			if (child.nodeType == 3 /* Text node */) {
				s += child.data;
			} else {
				s += getText(child);
			}
		}
		return s;
	}
}



// add the handler to the onload event
var oldOnload = window.onload;
if (typeof oldOnload == 'function') {
	window.onload = function() {
		oldOnload();
		lipt();
	}
} else {
	window.onload = lipt;
}


Created 2005/06/25.