// ==UserScript==
// @name          Middle Click Override
// @version       1.08
// @description   Allows javascript links, buttons, etc. to work with the middle mouse button
//								Original concept by xErath (http://files.myopera.com/xErath/files/middleclick.js)
//								Works best when opera:config#UserPrefs|TargetDestination is 2
// @include       *
// ==/UserScript==

(function(opera){
	var preventJSLocationModification = true,	// window.location modification opens a background window
			onlyLinksOrButton = false;	// Only run script when clicking links or buttons

	var origLocObject = location, wFocus = opera.focus = focus, wBlur = opera.blur = blur, wOpen = opera.open = open,
			clickContext;

	focus = blur = function(){};
	open = function() {
		clickContext&&(arguments[1] = '_blank');
		var win = wOpen.apply(self,arguments);
		clickContext&&wFocus.call(self);
		return win;
	}

	if( preventJSLocationModification ) {
		opera.defineMagicVariable('location',
		function(curVal) {
			if( !clickContext ) { return origLocObject; }
			this.hash = origLocObject.hash;
			this.host = origLocObject.host;
			this.hostname = origLocObject.hostname;
			this.href = origLocObject.href;
			this.pathname = origLocObject.pathname;
			this.port = origLocObject.port;
			this.protocol = origLocObject.protocol;
			this.search = origLocObject.search;
			this.reload = function() { return window.open(origLocObject); };
			this.toString = function() { return origLocObject.toString(); };
			this.valueOf = function() { return origLocObject.valueOf(); };
			this.replace = function(url) { window.open(url); };
			this.assign = this.replace;
			setTimeout(function(locObj) {
				clickContext = clickContext||true;
				if( origLocObject.hash != locObj.hash ) {
					origLocObject.hash = locObj.hash;
				}
				else if( origLocObject.href != locObj.href ) {
					window.open(locObj.href)
				}
				else {
					for( var i = 0, props = ['protocol','host','port','pathname','search'], prop; prop = props[i]; i++ ) {
						if( origLocObject[prop] != locObj[prop] ) {
							var win = window.open();
							win.location[prop] = locObj[prop];
							break;
						}
					}
				}
				clickContext = (clickContext===true)?false:clickContext;
			},0,this);
			return this;
		},
		function(newVal) { clickContext?open(loc):origLocObject.assign(loc); return loc; });
	}

	opera.addEventListener('BeforeEvent.mousedown',function(ujsEv) {
		var ev = ujsEv.event;
		if( ev.button != 1 || clickContext ) { return; }

		var target = ev.target,
		cNode = target.selectSingleNode('ancestor-or-self::*[ ((local-name()="a" or local-name()="area") and @href) or (local-name()="input" and (@type="submit" or @type="image")) or (local-name()="button" and (not(@type) or @type="submit" or @type="button")) ]');
		if( !cNode && onlyLinksOrButton ) { return; }

		clickContext = {};
		ev.triggered = true;
		opera.addEventListener('BeforeEventListener',beforeEvL,false);
		opera.addEventListener('AfterEvent',afterEv,false);

		if( !cNode ) { return; }

		if( clickContext.isLink = !(cNode.forms instanceof NodeList) ) {
			if( cNode.protocol && cNode.protocol.toLowerCase() == 'javascript:' ) {
				clickContext.jsCode = cNode.href.substr(11);
			}
			clickContext.href = cNode.getAttribute('href');
			cNode.removeAttribute('href');
		}
		else { cNode = (cNode.hasAttribute('action')||!cNode.form)?cNode:cNode.form; }

		cNode.hasAttribute('target')&&(clickContext.target = cNode.getAttribute('target'));
		cNode.setAttribute('target','_blank');
		clickContext.cNode = cNode;
	},false);

	function beforeEvL(ujsEv) {
		if( !ujsEv.event.triggered ) { return ujsEv.preventDefault(); }
		if( clickContext.tmpFix = clickContext.isLink ) {
			clickContext.cNode.setAttribute('href',clickContext.href);
		}
	}

	function afterEv(ujsEv) {
		if( !ujsEv.event.triggered ) { return ujsEv.event.preventDefault();  }
		if( clickContext.tmpFix ) {
			clickContext.href = clickContext.cNode.getAttribute('href');
			clickContext.tmpFix = clickContext.cNode.removeAttribute('href');
		}

		switch( ujsEv.event.type ) {
		case 'mousedown':
			triggerEvent('mouseup',ujsEv.event); break;
		case 'mouseup':
			triggerEvent('click',ujsEv.event); break;
		case 'click':
			opera.removeEventListener('BeforeEventListener',beforeEvL,false);
			opera.removeEventListener('AfterEvent',afterEv,false);
			setTimeout(fixAll,0);
			if( clickContext.isLink && !ujsEv.eventCancelled ) {
				clickContext.jsCode?(new Function(clickContext.jsCode))():clickContext.cNode.setAttribute('href',clickContext.href);
				wFocus.call(self);
			}
		}
	}

	function fixAll() {
		clickContext.cNode&&((clickContext.target===undefined)?clickContext.cNode.removeAttribute('target'):clickContext.cNode.setAttribute('target',clickContext.target));
		clickContext.isLink?clickContext.cNode.setAttribute('href',clickContext.href):wBlur.call(self);
		wFocus.call(self);
		clickContext = false;
	}

	function triggerEvent(type,oEvent) {
		var newEv = document.createEvent('MouseEvents');
		newEv.initMouseEvent(type,true,true,window,1,oEvent.screenX,oEvent.screenY,oEvent.clientX,oEvent.clientY,oEvent.ctrlKey,oEvent.altKey,oEvent.shiftKey,oEvent.metaKey,0,null);
		newEv.triggered = true;
		setTimeout(function(el,ev) { el.dispatchEvent(ev); },0,oEvent.target,newEv);
	}
})(window.opera);