var Highlighter = new Class({
	options: {
		autoUnhighlight: true,
		caseSensitive: false,
		startElement: false,
		endElement: false,
		elements: new Array(),
		className: 'highlight',
		onlyWords: true,
		tag: 'span'
	},
	initialize: function (options) {
		this.setOptions(options);
		this.getElements(this.options.startElement, this.options.endElement);
		this.words = [];
	},
	highlight: function (words) {
		if (words.constructor === String) {
			words = [words];
		}
		if (this.options.autoUnhighlight) {
			this.unhighlight(words);
		}
		var pattern = this.options.onlyWords ? '\b' + pattern + '\b' : '(' + words.join('\\b|\\b') + ')';
		var regex = new RegExp(pattern, this.options.caseSensitive ? '' : 'i');
		this.options.elements.each(function (el) {
			this.recurse(el, regex, this.options.className);
		}, this);
		return this;
	},
	unhighlight: function (words) {
		if (words.constructor === String) {
			words = [words];
		}
		words.each(function (word) {
			word = (this.options.caseSensitive ? word : word.toUpperCase());
			if (this.words[word]) {
				var elements = $$(this.words[word]);
				elements.setProperty('class', '');
				elements.each(function (el) {
					var tn = document.createTextNode(el.getText());
					el.getParent().replaceChild(tn, el);
				});
			}
		}, this);
		return this;
	},
	recurse: function (node, regex, klass) {
		if (node.nodeType === 3) {
			var match = node.data.match(regex);
			if (match) {
				var highlight = new Element(this.options.tag);
				highlight.addClass(klass);
				var wordNode = node.splitText(match.index);
				wordNode.splitText(match[0].length);
				var wordClone = wordNode.cloneNode(true);
				highlight.appendChild(wordClone);
				wordNode.parentNode.replaceChild(highlight, wordNode);
				highlight.setProperty('rel', highlight.get('text'));
				var comparer = highlight.get('text');
				if (!this.options.caseSensitive) {
					comparer = highlight.get('text').toUpperCase();
				}
				if (!this.words[comparer]) {
					this.words[comparer] = [];
				}
				this.words[comparer].push(highlight);
				return 1;
			}
		} else if ((node.nodeType === 1 && node.childNodes) && !/(script|style|textarea|iframe)/i.test(node.tagName) && !(node.tagName === this.options.tag.toUpperCase() && node.className === klass)) {
			for (var i = 0; i < node.childNodes.length; i++) {
				i += this.recurse(node.childNodes[i], regex, klass);
			}
		}
		return 0;
	},
	getElements: function (start, end) {
		var next = start.getNext();
		if (next.id != end.id) {
			this.options.elements.include(next);
			this.getElements(next, end);
		}
	}
});
Highlighter.implement(new Options);
window.addEvent('domready', function () {
	var start = document.id('highlighter-start');
	var end = document.id('highlighter-end');
	if (!start || !end || !window.highlight) {
		return true;
	}
	highlighter = new Highlighter({
		startElement: start,
		endElement: end,
		autoUnhighlight: true,
		onlyWords: false
	}).highlight(window.highlight);
	start.dispose();
	end.dispose();
});