function HTMLWalker(/*HTMLWalker.DefaultHandler*/handler, /*Node*/node){
	/* Properties */
	this.handler = handler;
	this.__startNode = node;
	this.__currentNode = node;
	this.__lineage = [];
}

(function(/*HTMLWalker.prototype*/__proto__){
	var di = document.implementation;
	if (!di || !di.hasFeature("HTML", "1.0"))
		return;
	
	/* Commands for HTMLWalker (Constructors) */
	HTMLWalker.StopWalking = function(){};
	HTMLWalker.SkipChildren = function(){};
	HTMLWalker.SkipSibling = function(){};
	
	/* Constants */
	HTMLWalker.ELEMENT_NODE = 1;
	HTMLWalker.TEXT_NODE = 3;
	HTMLWalker.CDATA_SECTION_NODE = 4;
	HTMLWalker.PROCESSING_INSTRUCTION_NODE = 7;
	HTMLWalker.COMMENT_NODE = 8;
	HTMLWalker.DOCUMENT_NODE = 9;
	HTMLWalker.DOCUMENT_FRAGMENT_NODE = 11;
	
	/* Class method */
	HTMLWalker.normalizeHandler = function(yourHandler){
		// [caution] In JScript this method make no sense if yourHandler's
		// "prototype" property have been rewrited.
		if (yourHandler.prototype.__proto__)
			yourHandler.prototype.__proto__ = DefaultHandler.prototype;
		else
			yourHandler.prototype = new DefaultHandler;
	};
	
	/* Class property */
	HTMLWalker.DefaultHandler = DefaultHandler;
	function DefaultHandler(){};
	(function(__proto__){
		var func = function(node){};
		__proto__["*"] = func;
		__proto__["#document-fragment"] = func;
		__proto__["#text"] = func;
		__proto__["#comment"] = func;
		__proto__["#processing-instruction"] = func;
	})(DefaultHandler.prototype);
	
	/* Methods */
	__proto__.walk = function(){
		try {
			this.__next(this.__currentNode);
		} catch(err) {
			if (err instanceof HTMLWalker.StopWalking) {
				this.__lineage.push(this.__currentNode);
				return (this.__currentNode = this.__currentNode.firstChild);
			} else {
				throw err;
			}
		}
		var lineage = this.__lineage;
		while (lineage.length > 0)
			this.__next(lineage.shift().nextSibling);
	};
	__proto__.__next = function(node){
		try {
			this.handler[node.nodeName](node);
		} catch(err) {
			if (node === null) {
				return;
			} else if (err instanceof TypeError) { // handler undefined
		    	if (node.nodeType == HTMLWalker.ELEMENT_NODE)
					this.handler["*"](node);
				else if (node.nodeType == HTMLWalker.PROCESSING_INSTRUCTION_NODE)
					this.handler["#processing-instruction"](node);
			} else if (err instanceof HTMLWalker.SkipSibling) {
				return;
			} else if (err instanceof HTMLWalker.SkipChildren) {
				arguments.callee.call(this, node.nextSibling);
				return;
			} else {
				throw err;
			}
		} finally {
			if (node !== null)
				this.__currentNode = node;
		}
		try {
			arguments.callee.call(this, node.firstChild);
		} catch (err) {
			if (err instanceof HTMLWalker.StopWalking)
				this.__lineage.push(node);
			throw err;
		}
		arguments.callee.call(this, node.nextSibling);
	};
	__proto__.init = function(handler, node){
		handler = handler? handler : this.handler;
		node = node? node : this.__startNode;
		HTMLWalker.call(this, handler, node);
	};
})(HTMLWalker.prototype);

