// ==UserScript==
// @name           word highlight
// @namespace      http://ss-o.net/
// @description    keywords highlight for Google Search and All
// @include        http://*
// @version        1.0.6
// ==/UserScript==

//console.time("highlight");
(function word_hightlight(loaded){
	if (!loaded && window.opera && document.readyState == 'interactive') {
		document.addEventListener('DOMContentLoaded', function(){word_hightlight(true);}, false);
		return;
	}
	if (document.contentType && !/html/i.test(document.contentType))
		return;

	// keybinds
	var KEY_NEXT = 'n';
	var KEY_PREV = 'N';
	var KEY_SEARCH = 'C-/';

	// open new tabs?
	var TARGET_BLANK = false;

	var isOpera = !!this.opera,
		isFirefox = !!this.Components,
		isChromium = !!this.chromium,
		isSafari = this.getMatchedCSSRules && !isChromium;
	var UP_IMAGE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAAQ0lEQVR42mNgGAjw+vXr/yBMSB1RAGYYVQxFN4wiQ3EZRpahhAwjyVBChpBkKCHNJBlKSBMhTFXDUAwlpIhUzEBtAABtjZyQ3YVdfgAAAABJRU5ErkJggg==';
	var DOWN_IMAGE = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAMCAYAAABiDJ37AAAAPElEQVR42q3PsQkAMAwDQe2/qoaIK3UhRuEfXPqwJTrbhxwURS+9ff+N6tW2XGFpQyosoVhCsYRiCcXaBhIFnJBtuvEqAAAAAElFTkSuQmCC';

	var PRE = 'wordhighlight', ID_PRE = PRE + '_id';
	var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
	var STYLE_CLASS = '0123456789'.split('').map(function(a,i){return PRE + '_word'+i;});
	var setuped = false;
	var keyword = '', words = [], word_lists = [], word_inputs_list, layers, positions = [];
	var xp_all = new $XE('descendant::font[starts-with(@name,"' + PRE + '_word")]', document.body);
	var keyCodeStr = {
		8:  'BAC',
		9:  'TAB',
		10: 'RET',
		13: 'RET',
		27: 'ESC',
		33: 'PageUp',
		34: 'PageDown',
		35: 'End',
		36: 'Home',
		37: 'Left',
		38: 'Up',
		39: 'Right',
		40: 'Down',
		45: 'Insert',
		46: 'Delete',
		112: 'F1',
		113: 'F2',
		114: 'F3',
		115: 'F4',
		116: 'F5',
		117: 'F6',
		118: 'F7',
		119: 'F8',
		120: 'F9',
		121: 'F10',
		122: 'F11',
		123: 'F12'
	};
	var whichStr = {
		32: 'SPC'
	};
	var htmlDoc = isChromium ? document.implementation.createHTMLDocument('hogehoge') : document;
	init_keyboard();
	if (init_keyword() !== false)
		setup();

	function highlight(doc, ext_word) {
		words = words.filter(function(w){return w;});
		if (words.length <= 0)
			return;
		var _index,_words;
		if (ext_word && ext_word.words) {
			_words = ext_word.words;
			_index = ext_word.index;
		} else {
			_words = words;
		}
		var exd_words = _words.map(function(w){return new RegExp('(' + w.replace(/\W/g,'\\$&') + ')(?!##)', 'ig');});
		var xw = ' and (' + _words.map(function(w){return ' contains(translate(self::text(),"abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ"),"'+w.toUpperCase().replace(/(")/g,'\\$1')+'") ';}).join(' or ') + ') ';
		$X('descendant::text()[string-length(normalize-space(self::text())) > 0 ' + xw +' and not(ancestor::textarea) and not(ancestor::script) and not(ancestor::style)]', doc).forEach(function(text_node) {
			var df, text = text_node.nodeValue, id_index = 0,
			parent = text_node.parentNode, range = document.createRange(), replace_strings = [],
			new_text = reduce(exd_words, function(text,ew,i) {
				var _i = _index || i;
				return text.replace(ew,function($0,$1) {
					replace_strings[id_index] = '<font id="' + ID_PRE + id_index + '" class="' + STYLE_CLASS[_i%10] + '" name="'+PRE+'_word'+_i+'">' + $1 + '</font>';
					return '##'+(id_index++)+'##';
				});
			}, text).
				replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').
				replace(/##(\d+)##/g, function($0,$1) {
				return replace_strings[$1] || '';
			});
			if (replace_strings.length) {
				try {
					if (isChromium) {
						range.selectNodeContents(htmlDoc.documentElement);
					} else {
						range.selectNode(text_node);
					}
					df = range.createContextualFragment(new_text);
					if (df.firstChild) parent.replaceChild(df, text_node);
					range.detach();
				} catch (e) {
					error(e);
				}
			}
		});
	}

	function setup(){
		setuped = true;
		var sheet = addCSS([
			'font.' + PRE + '_word0,.' + PRE + '_item0{display:inline;background-color: #FFFF80; color: black;}',
			'font.' + PRE + '_word1,.' + PRE + '_item1{display:inline;background-color: #99ccff; color: black;}',
			'font.' + PRE + '_word2,.' + PRE + '_item2{display:inline;background-color: #ff99cc; color: black;}',
			'font.' + PRE + '_word3,.' + PRE + '_item3{display:inline;background-color: #66cc66; color: black;}',
			'font.' + PRE + '_word4,.' + PRE + '_item4{display:inline;background-color: #cc99ff; color: black;}',
			'font.' + PRE + '_word5,.' + PRE + '_item5{display:inline;background-color: #ffcc66; color: black;}',
			'font.' + PRE + '_word6,.' + PRE + '_item6{display:inline;background-color: #669999; color: black;}',
			'font.' + PRE + '_word7,.' + PRE + '_item7{display:inline;background-color: #cc9966; color: black;}',
			'font.' + PRE + '_word8,.' + PRE + '_item8{display:inline;background-color: #999999; color: black;}',
			'font.' + PRE + '_word9,.' + PRE + '_item9{display:inline;background-color: #cc6699; color: black;}',
			'#' + PRE + '_words{cursor:move;position:fixed;z-index:1024;right:1em;opacity:0.8;list-style-type:none;margin:0;padding:0;border:1px solid #333;background:#fff;width:auto;bottom:1em;}',
			'#' + PRE + '_words.fixed{}',
			'#' + PRE + '_words:hover{opacity:1;}',
			'#' + PRE + '_words.edit{opacity:1;cursor:default;}',
			'#' + PRE + '_words.edit li.' + PRE + '_inputs{display:none;}',
			'#' + PRE + '_words li.' + PRE + '_editor{display:none;}',
			'#' + PRE + '_words.edit li.' + PRE + '_editor{display:inline-block;}',
			'#' + PRE + '_words.edit li.' + PRE + '_editor input{max-width:400px;}',
			'#' + PRE + '_words li{display:inline-block;margin:0.2em;line-height:1.5;padding:0;border:none;font-size:medium;}',
			'#' + PRE + '_words > li {vertical-align:middle;}',
			'#' + PRE + '_words > li.' + PRE + '_title{display:inline-block;background:#333;color:#fff;padding:0.1em 0.3em;}',
			'#' + PRE + '_words > li.' + PRE + '_ctrl > input{display:inline-block;margin:0.1em 0.3em;background:#99cc99;border:2px solid #996666;cursor:pointer;}',
			'#' + PRE + '_word_inputs_list {padding:0;margin:0;}',
			'#' + PRE + '_word_inputs_list > li{position:relative;}',
			'#' + PRE + '_word_inputs_list > li > label{cursor:pointer;}',
			'#' + PRE + '_word_inputs_list > li > input{cursor:pointer;}',
			'#' + PRE + '_word_inputs_list > li > label.'+PRE+'_movelabel{height:4px;line-height:4px;position:absolute;background:#555555;width:100%;text-align:center;margin:0;padding:0;left:0px;}',
			'#' + PRE + '_word_inputs_list > li > label.'+PRE+'_movelabel.prev{top:-4px;}',
			'#' + PRE + '_word_inputs_list > li > label.'+PRE+'_movelabel.next{bottom:-4px;}',
			'#' + PRE + '_word_inputs_list > li > label.'+PRE+'_movelabel input{display:none;}',
			'#' + PRE + '_word_inputs_list > li:hover > label.'+PRE+'_movelabel{line-height:15px;height:15px;}',
			'#' + PRE + '_word_inputs_list > li:hover > label.'+PRE+'_movelabel.prev{top:-15px;}',
			'#' + PRE + '_word_inputs_list > li:hover > label.'+PRE+'_movelabel.next{bottom:-15px;}',
			'#' + PRE + '_word_inputs_list > li:hover > label.'+PRE+'_movelabel input{display:inline;}',
			'#' + PRE + '_word_inputs_list > li > label.'+PRE+'_movelabel:hover{background:#999999;}',
			'#' + PRE + '_word_inputs_list > li > label > input[type=image]{vertical-align:top;padding:0;height:12px;}',
		].join('\n'));
		highlight(document.body);
		var ul = document.createElementNS(HTML_NAMESPACE, 'ul');
		ul.id = PRE + '_words';
		ul.className = 'fixed';
		var title = document.createElementNS(HTML_NAMESPACE, 'li');
		title.textContent = 'keywords';
		title.className = PRE + '_title';
		ul.appendChild(title);
		var editor = document.createElementNS(HTML_NAMESPACE, 'li');
		editor.className = PRE + '_editor';
		var text_input = document.createElementNS(HTML_NAMESPACE, 'input');
		text_input.type = 'text';
		text_input.addEventListener('keypress',function(e){
			if (e.keyCode === 13) {
				edit.value = 'edit';
				ul.className = '';
				keyword = text_input.value;
				init_words();
				resetup();
			}
		},false);
		editor.appendChild(text_input);
		ul.appendChild(editor);
		var ctrl = document.createElementNS(HTML_NAMESPACE, 'li');
		ctrl.className = PRE + '_ctrl';
		var edit = document.createElementNS(HTML_NAMESPACE, 'input');
		edit.type = 'button';
		edit.value = 'edit';
		ctrl.appendChild(edit);
		edit.addEventListener('click',function(){
			if (ul.className == 'edit') {
				edit.value = 'edit';
				ul.className = '';
				keyword = text_input.value;
				init_words();
				resetup();
			} else {
				text_input.style.width = Math.max(keyword.length,10) + 'em';
				ul.className = 'edit';
				edit.value = 'set';
				text_input.value = keyword;
				text_input.focus();
			}
		},false);
		var off = document.createElementNS(HTML_NAMESPACE, 'input');
		off.type = 'button';
		off.value = 'off';
		ctrl.appendChild(off);
		off.addEventListener('click',function(){
			document.body.removeChild(ul);
			xp_all.get().forEach(function(layer,i){
				layer.parentNode.replaceChild(document.createTextNode(layer.textContent),layer);
			});
			sheet.disable = true;
			if (addCSS.__style.parentNode) addCSS.__root.removeChild(addCSS.__style);
			window.name = '';
			word_lists = [];
			setuped = false;
		},false);
		var _li = document.createElementNS(HTML_NAMESPACE, 'li');
		word_inputs_list = document.createElementNS(HTML_NAMESPACE, 'ul');
		_li.appendChild(word_inputs_list);
		word_inputs_list.id = PRE + '_word_inputs_list';
		_li.className = PRE + '_inputs';
		ul.appendChild(_li);
		ul.appendChild(ctrl);
		word_lists = create_inputlist(words);
		document.body.appendChild(ul);
		var drag = endrag(ul,{x:'right',y:'bottom'});
		drag.hook('__drag_begin', function(e){
			if (this.element && this.element.className === 'edit')
				return false;
		});
		layers = xp_all.get();
		if (window.AutoPagerize) {
			init();
		} else {
			window.addEventListener('GM_AutoPagerizeLoaded',init,false);
			window.addEventListener('AutoPatchWork#load',init,false);
		}
	}

	function add_word(word) {
		window.name = PRE + '::' + encodeURIComponent(keyword);
		highlight(document.body,{words:[word],index:words.length});
		word_lists.push.apply(word_lists,create_inputlist([word], words.length));
		layers = xp_all.get();
	}

	function resetup() {
		xp_all.get().forEach(function(layer,i){
			layer.parentNode.replaceChild(document.createTextNode(layer.textContent),layer);
		});
		word_lists.forEach(function(item){item.item.parentNode.removeChild(item.item);});
		window.name = PRE + '::' + encodeURIComponent(keyword);
		highlight(document.body);
		layers = xp_all.get();
		word_lists = create_inputlist(words);
	}

	function move(node) {
		if (!node) return;
		node.style.outline = node.style.WebkitOutline = '4px solid #33ccff';
		if (node.getBoundingClientRect) {
			var pos = node.getBoundingClientRect();
			document.documentElement.scrollTop = document.body.scrollTop =
				pos.top + window.pageYOffset - window.innerHeight/2 + (pos.bottom - pos.top);
		} else {
			node.scrollIntoView();
		}
		setTimeout(function(){
			node.style.outline = node.style.WebkitOutline = 'none';
		},2000);
	}

	function create_inputlist(words, start) {
		return words.map(function(w, i){
			var _i = i + (start||0);
			var li = document.createElementNS(HTML_NAMESPACE, 'li');
			li.className = PRE + '_item' + _i%10;
			var _next = document.createElementNS(HTML_NAMESPACE, 'input');
			var _prev = document.createElementNS(HTML_NAMESPACE, 'input');
			var next_label = document.createElementNS(HTML_NAMESPACE, 'label');
			var prev_label = document.createElementNS(HTML_NAMESPACE, 'label');
			_next.type = _prev.type = 'image';
			_next.src = DOWN_IMAGE;
			_prev.src = UP_IMAGE;
			_next.id = next_label.htmlFor = PRE + '_next_button' + _i;
			_prev.id = prev_label.htmlFor = PRE + '_prev_button' + _i;
			prev_label.className = next_label.className = PRE + '_movelabel';
			prev_label.className += ' prev';
			next_label.className += ' next';
			positions[i+1] || (positions[i+1]=-1);
			var xp = new $XE('descendant::font[@name="' + PRE + '_word' + _i +'"]', document.body);
			var xp_count = new $XE('count(descendant::font[@name="' + PRE + '_word' + _i +'"])', document.body);
			_next.addEventListener('click',function(){
				var layers = xp.get();
				next(i,layers);
			},false);
			_prev.addEventListener('click',function(){
				var layers = xp.get();
				prev(i,layers);
			},false);
			prev_label.appendChild(_prev);
			li.appendChild(prev_label);
			var label = document.createElementNS(HTML_NAMESPACE, 'label');
			label.textContent = w + '(' + xp_count.get({result_type:XPathResult.NUMBER_TYPE}).numberValue + ')';
			var check = document.createElementNS(HTML_NAMESPACE, 'input');
			check.type = 'checkbox';
			check.checked = true;
			var _id = check.id = ID_PRE + '_check' + _i;
			label.htmlFor = _id;
			label.className = PRE + '_label' + _i % 10;
			li.appendChild(check);
			li.appendChild(label);
			next_label.appendChild(_next);
			li.appendChild(next_label);
			word_inputs_list.appendChild(li);
			check.addEventListener('change', function(){
				var layers = xp.get();
				if (check.checked) {
					highlight(document.body,{words:[w],index:_i});
				} else {
					layers.forEach(function(layer,i){
						layer.parentNode.replaceChild(document.createTextNode(layer.textContent),layer);
					});
				}
			},false);
			return {item:li,word:w,label:label,xpath:xp_count};
		});
	}

	function endrag(element,opt){
		endrag = function(element,opt){
			return new endrag.proto(element,opt||{});
		}
		endrag.proto = function(elem,opt){
			var self = this;
			this.element = elem;
			this.style = elem.style;
			var _x = opt.x !== 'right';
			var _y = opt.y !== 'bottom';
			this.x = _x ? 'left' : 'right';
			this.y = _y ? 'top' : 'bottom';
			this.xd = _x ? -1 : 1;
			this.yd = _y ? -1 : 1;
			this.computed_style = document.defaultView.getComputedStyle(elem, '');
			this.drag_begin = function(e){self.__drag_begin(e);};
			elem.addEventListener('mousedown', this.drag_begin, false);
			this.dragging = function(e){self.__dragging(e)};
			document.addEventListener('mousemove', this.dragging, false);
			this.drag_end = function(e){self.__drag_end(e)};
			document.addEventListener('mouseup', this.drag_end, false);
		};
		endrag.proto.prototype = {
			__drag_begin:function(e){
				var _c = this.computed_style;
				this.isDragging = true;
				this.position = {
					_x:parseFloat(_c[this.x]),
					_y:parseFloat(_c[this.y]),
					x:e.pageX,
					y:e.pageY
				};
				e.preventDefault();
			},
			__dragging:function(e){
				if (!this.isDragging) return;
				var x = e.pageX, y = e.pageY, p = this.position;
				p._x = p._x + (p.x - x) * this.xd;
				p._y = p._y + (p.y - y) * this.yd;
				this.style[this.x] = p._x + 'px';
				this.style[this.y] = p._y + 'px';
				p.x = x;
				p.y = y;
			},
			__drag_end:function(e){
				if (this.isDragging)
					this.isDragging = false;
			},
			hook:function(method,func){
				if (typeof this[method] === 'function') {
					var o = this[method];
					this[method] = function(){
						if (func.apply(this,arguments) === false)
							return;
						o.apply(this,arguments);
					};
				}
			}
		};
		return endrag(element,opt);
	}

	function target_google(doc,index){
		var as = $X('descendant::a[@href and not(starts-with(@href,"javascript:") or starts-with(@href,"#"))]',doc);
		as.forEach(function(a,i){
			if (a.target != 'self')
				a.target = PRE + (i + (index || 0) * as.length) + '::' + encodeURIComponent(keyword);
		});
	}

	function init_keyword(){
		var ref = document.referrer;
		var name = window.name;
		var host = location.host;
		if (/google\./.test(host) && /^\/search/.test(location.pathname)) {
			var _q = $X('descendant::input[@name="q"]',document.body)[0];
			keyword = clean(_q.value);
			window.name = PRE + '::' + encodeURIComponent(keyword);
			if (TARGET_BLANK) target_google(document.getElementById('res'));
		} else if (window.name.indexOf(PRE) == 0) {
			keyword = new RegExp(PRE + '\\d*::(.+)').exec(decodeURIComponent(window.name))[1];
		} else if (/google/.test(ref)) {
			var _a = document.createElementNS(HTML_NAMESPACE, 'a');
			_a.href = ref;
			if (!/google\./.test(_a.host) || !/[&?]q=([^&]+)/.test(_a.search)) return;
			keyword = clean(decodeURIComponent(/[&?]q=([^&]+)/i.exec(_a.search)[1]));
			window.name = PRE + '::' + encodeURIComponent(keyword);
		} else {
			return false;
		}
		init_words();
		return true;
	}

	function clean(str) {
		return str.replace(/(?:(?:\s?(?:site|(?:all)?in(?:url|title|anchor|text)):|(?:\s|^)-|[()])\S*|(\s)OR\s)/g,'$1');
	}

	function init_words(){
		if (keyword) {
			var ret=[],eword = keyword.replace(/"([^"]+)"/g,function($0,$1){ret.push($1);return '';});
			words = eword.split(/[\+\s\.:\|#]/).concat(ret);
		}
	}

	function init_minibuffer() {
		if (window.Minibuffer)
			document.removeEventListener('keypress', keyhandler, false);
		var mini = window.Minibuffer;
		mini.addCommand({
			name: 'keyword-search',
			command: function(stdin){
				keyword += ' ' + this.args.join(' ');
				init_words();
				if (setuped) resetup();
				else setup();
				return stdin;
			}
		});
		mini.addShortcutkey({
			key:KEY_NEXT,
			command:next,
			description: 'emphasis next keyword'
		});
		mini.addShortcutkey({
			key:KEY_PREV,
			command:prev,
			description: 'emphasis prev keyword'
		});
		mini.addShortcutkey({
			key:KEY_SEARCH,
			command:function(e){
				instant_search();
			},
			description: 'emphasis prev keyword'
		});
	}
	function next(index,_layers) {
		_layers || (_layers = (layers || (layers = xp_all.get()) ));
		index || (index = 0);
		move(_layers[positions[index]++] || (positions[index]=1,_layers[0]));
	}
	function prev(index,_layers) {
		_layers || (_layers = (layers || (layers = xp_all.get()) ));
		index || (index = 0);
		move(_layers[positions[index]--] || (positions[index]=_layers.length-1,_layers[positions[index]--]));
	}
	function init_keyboard() {
		if (isOpera) {
		} else if (window.Minibuffer) {
			init_minibuffer();
			return;
		} else {
			window.addEventListener('GM_MinibufferLoaded', init_minibuffer, false);
		}
		if (!window.chromium) {
			document.addEventListener('keypress', keyhandler, false);
		} else {
			document.addEventListener('keydown', keyhandler, false);
		}
	}
	function get_key(evt){
		var key = String.fromCharCode(evt.which),
		ctrl = evt.ctrlKey ? 'C-' : '',
		meta = (evt.metaKey || evt.altKey) ? 'M-' : '';
		if (evt.ctrlKey && evt.which >= 186 && evt.which < 192) {
			key = String.fromCharCode(evt.which - 144);
		}
		if (evt.keyIdentifier && evt.keyIdentifier !== 'Enter' && !/^U\+/.test(evt.keyIdentifier) ) {
			key = evt.keyIdentifier;
		} else if ( evt.which !== evt.keyCode ) {
			key = keyCodeStr[evt.keyCode] || whichStr[evt.which] || key;
		} else if (evt.which <= 32) {
			key = keyCodeStr[evt.keyCode] || whichStr[evt.which];
		}
		return ctrl+meta+key;
	}
	function keyhandler(evt){
		if (/^(?:input|textarea)$/i.test(evt.target.localName)) return;
		var fullkey = get_key(evt);
		if (keyword){
			switch (fullkey) {
			case KEY_NEXT:
				next();
				break;
			case KEY_PREV:
				prev();
				break;
			}
		}
		switch (fullkey) {
		case KEY_SEARCH:
			evt.preventDefault();
			evt.stopPropagation();
			instant_search();
			break;
		}
	}
	function instant_search(){
		if (instant_search.input) {
			instant_search.input.focus();
			return;
		}
		var input = instant_search.input = document.createElementNS(HTML_NAMESPACE, 'input');
		input.id = PRE + '_textinput';
		input.setAttribute('style','border:none;margin:0px;padding:0px;position:fixed;bottom:0px;left:0.5em;background:#000;color:#fff;font-weight:bold;width:40%;font-size:large;');
		input.addEventListener('keypress',function(evt) {
			var fullkey = get_key(evt);
			switch (fullkey) {
			case 'RET':
				evt.preventDefault();
				evt.stopPropagation();
				keyword += ' ' + input.value;
				init_words();
				if (setuped) add_word(input.value);
				else setup();
			case 'ESC':
				document.body.removeChild(input);
				instant_search.input = null;
			}
		},false);
		input.addEventListener('input',function(e) {
			var text = input.value;
			if (!/\S/.test(text)) return;
			var x = 'descendant::text()[contains(self::text(),"' + text + '") and not(ancestor::textarea) and not(ancestor::script) and not(ancestor::style)]/parent::*';
			var node = document.evaluate(x, document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
			if (node) {
				move(node);
			}
		},false);
		document.body.appendChild(input);
		input.focus();
	}

	function init(e){
		var page = 0;
		if (window.AutoPagerize && window.AutoPagerize.addFilter) {
			window.AutoPagerize.addFilter(function(docs) {
				docs.forEach(function(doc){
					highlight(doc);
					if (TARGET_BLANK) target_google(doc,++page);
				});
				word_lists.forEach(function(item){
					item.label.textContent = item.word + '(' + item.xpath.get({result_type:XPathResult.NUMBER_TYPE}).numberValue + ')';
				});
				layers = xp_all.get();
			});
		} else {
			window.addEventListener('AutoPatchWork#DOMNodeInserted',function(e){
				highlight(e.target);
				if (TARGET_BLANK) target_google(e.target, ++page);
			},false);
			window.addEventListener('AutoPatchWork#pageload',function(e){
				word_lists.forEach(function(item){
					item.label.textContent = item.word + '(' + item.xpath.get({result_type:XPathResult.NUMBER_TYPE}).numberValue + ')';
				});
				layers = xp_all.get();
			},false);
		}
	}

	function $XE(exp, context) {
		var xe = new XPathEvaluator();
		var resolver = xe.createNSResolver(document.documentElement);
		//var defaultNS = document.lookupNamespaceURI(window.opera ? '' : null);
		var defaultNS = (document.documentElement.nodeName !== 'HTML') ? context.namespaceURI : null;
		if (defaultNS) {
			var defaultPrefix = '__default__';
			if (!isChromium)
				exp = addDefaultPrefix(exp, defaultPrefix);
			var defaultResolver = resolver;
			resolver = function (prefix) {
				return (prefix == defaultPrefix) ? defaultNS : defaultResolver.lookupNamespaceURI(prefix);
			};
		}
		var ex = xe.createExpression(exp, resolver);
		this.get = function(param) {
			param || (param={});
			var result = this.result = 
				ex.evaluate(param.context||context, param.result_type||XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,this.result);
			if (param.result_type) return result;
			for (var i = 0, len = result.snapshotLength, res = new Array(len); i < len; i++) {
				res[i] = result.snapshotItem(i);
			}
			return res;
		};
	}

	// via AutoPagerize Thx! nanto_vi
	function addDefaultPrefix(xpath, prefix) {
		var tokenPattern = /([A-Za-z_\u00c0-\ufffd][\w\-.\u00b7-\ufffd]*|\*)\s*(::?|\()?|(".*?"|'.*?'|\d+(?:\.\d*)?|\.(?:\.|\d+)?|[\)\]])|(\/\/?|!=|[<>]=?|[\(\[|,=+-])|([@$])/g;
		var TERM = 1, OPERATOR = 2, MODIFIER = 3;
		var tokenType = OPERATOR;
		prefix += ':';
		function replacer(token, identifier, suffix, term, operator, modifier) {
			if (suffix) {
				tokenType =
					(suffix == ':' || (suffix == '::' && (identifier == 'attribute' || identifier == 'namespace')))
					? MODIFIER : OPERATOR;
			} else if (identifier) {
				if (tokenType == OPERATOR && identifier != '*') {
					token = prefix + token;
				}
				tokenType = (tokenType == TERM) ? OPERATOR : TERM;
			} else {
				tokenType = term ? TERM : operator ? OPERATOR : MODIFIER;
			}
			return token;
		}
		return xpath.replace(tokenPattern, replacer);
	}

	function $X(exp, context, resolver, result_type) {
		context || (context = document);
		var Doc = context.ownerDocument || context;
		var result = Doc.evaluate(exp, context, resolver, result_type || XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
		if (result_type) return result;
		for (var i = 0, len = result.snapshotLength, res = new Array(len); i < len; i++) {
			res[i] = result.snapshotItem(i);
		}
		return res;
	}

	// reduce https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/reduce#Compatibility
	function reduce(arr, fun){
		var len = arr.length, i = 0, rv;
		if (arguments.length >= 3) rv = arguments[2];
		else {do {
			if (i in arr) {
				rv = arr[i++];break;
			}
			if (++i >= len) throw new TypeError();
		} while (true)};
		for (; i < len; i++) if (i in arr) rv = fun.call(null, rv, arr[i], i, arr);
		return rv;
	}

	function error(e){
		if (isOpera) {
			opera.postError(e);
		} else if (window.console) {
			console.error(e);
		}
	}

	function addCSS(css){
		var sheet, self = arguments.callee;
		if (document.createStyleSheet) { // for IE
			sheet = document.createStyleSheet();
			sheet.cssText = css;
			return sheet;
		} else if (!self.__style || !self.__root) {
			sheet = document.createElementNS(HTML_NAMESPACE, 'style');
			sheet.type = 'text/css';
			self.__style = sheet;
			self.__root = document.getElementsByTagName('head')[0] || document.documentElement;
		}
		sheet = self.__style.cloneNode(false);
		sheet.textContent = css;
		return self.__root.appendChild(sheet).sheet;
	}
})();
//console.timeEnd("highlight");

// Bench: http://www.google.com/search?hl=en&q=HTML+5+Markup+Language
