// ==UserScript==
// @name          IE Emulator
// @namespace     http://dean.edwards.name/weblog/2005/03/ie-emulator
// @description	  Pretend to be Internet Explorer
// @include       http://www.example.com/
// ==/UserScript==


// IE Emulator version 1.0.1 (2005-08-18)
// Copyright 2005, Dean Edwards
// This software is licensed under the CC-GNU LGPL
// Web: http://creativecommons.org/licenses/LGPL/2.1/

(function() {
// ------------------------------------------------------------------
//  explorer emulation for firefox
// ------------------------------------------------------------------

// originally part of: http://dean.edwards.name/moz-behaviors/
// thanks to Erik Arvidsson (http://webfx.eae.net/dhtml/ieemu/)

/* note: in my comments where i say support/mimic a property:
   support = exactly the same as internet explorer
   mimic = close enough
*/

// return a node of type ELEMENT_NODE
function getElementNode($node) {
    while ($node && $node.nodeType != Node.ELEMENT_NODE) {
        $node = $node.parentNode;
    }
    return $node;
};

// For sniffers
// ------------
navigator.__defineGetter__("appName", function() {
	return "Microsoft Internet Explorer";
});
var $appVersion = navigator.appVersion;
navigator.__defineGetter__("appVersion", function() {
	return $appVersion + " (compatible; MSIE 6.0;)";
});
var $userAgent = navigator.userAgent;
navigator.__defineGetter__("userAgent", function() {
	return $userAgent + " (compatible; MSIE 6.0;)";
});

// mimic ActiveXObject (XMLHTTP only)
window.ActiveXObject = XMLHttpRequest;

// CSSStyleDeclaration
// -------------------
// support microsoft's styleFloat
CSSStyleDeclaration.prototype.__defineGetter__("styleFloat", function() {
	return this.cssFloat;
});
CSSStyleDeclaration.prototype.__defineSetter__("styleFloat", function($value) {
	this.cssFloat = $value;
});
// mimic microsoft's pixel representations of left/top/width/height
// the getters only work for values that are already pixels
CSSStyleDeclaration.prototype.__defineGetter__("pixelLeft", function() {
	return parseInt(this.left) || 0;
});
CSSStyleDeclaration.prototype.__defineSetter__("pixelLeft", function($value) {
	this.left = $value + "px";
});
CSSStyleDeclaration.prototype.__defineGetter__("pixelHeight", function() {
	return parseInt(this.height) || 0;
});
CSSStyleDeclaration.prototype.__defineSetter__("pixelHeight", function($value) {
	this.height = $value + "px";
});
CSSStyleDeclaration.prototype.__defineGetter__("pixelTop", function() {
	return parseInt(this.top) || 0;
});
CSSStyleDeclaration.prototype.__defineSetter__("pixelTop", function($value) {
	this.top = $value + "px";
});
CSSStyleDeclaration.prototype.__defineGetter__("pixelWidth", function() {
	return parseInt(this.width) || 0;
});
CSSStyleDeclaration.prototype.__defineSetter__("pixelWidth", function($value) {
	this.width = $value + "px";
});

// for older versions of gecko we need to use getPropertyValue() to
// access css properties returned by getComputedStyle().
// we don't want this so we fix it.
try {
var $computedStyle = getComputedStyle(this, null);
// the next line will throw an error for some versions of mozilla
var $test = $computedStyle.display;
} catch ($ignore) {
// the previous line will throw an error for some versions of mozilla
} finally {
if (!$test) {
	// the above code didn't work so we need to fix CSSStyleDeclaration
	var $UPPER_CASE = /[A-Z]/g;
	function _dashLowerCase($match){return "-" + $match.toLowerCase()};
	function _cssName($propertyName) {return $propertyName.replace($UPPER_CASE, _dashLowerCase)};
	function _assignStyleGetter($propertyName) {
		var $cssName = _cssName($propertyName);
		CSSStyleDeclaration.prototype.__defineGetter__($propertyName, function() {
			return this.getPropertyValue($cssName);
		});
	};
	for (var $propertyName in this.style) {
		if (typeof this.style[$propertyName] == "string") {
			_assignStyleGetter($propertyName);
		}
	}
}}

// XML Support
// -----------
function selectNodes($document, $queryString, $contextNode) {
    var $result= $document.evaluate($queryString, $contextNode, $document.createNSResolver($document.documentElement),
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    var $nodeList = [];
    for (i = 0; i < $result.snapshotLength; i++) {
        $nodeList[i]= $result.snapshotItem(i);
    }
    return $nodeList;
};
function selectSingleNode($document, $queryString, $contextNode){
    return selectNodes($document, $queryString + "[1]", $contextNode)[0];
};
Document.prototype.__defineGetter__("xml", function() {
    return (new XMLSerializer).serializeToString(this);
});
XMLDocument.prototype.selectNodes = function($queryString) {
    return selectNodes(this, $queryString, this);
};
XMLDocument.prototype.selectSingleNode = function($queryString) {
	return selectSingleNode(this, $queryString, null);
};
Element.prototype.selectNodes = function($queryString) {
    return (this.ownerDocument.selectNodes) ?
        selectNodes(this.ownerDocument, $queryString, this) : null;
};
Element.prototype.selectSingleNode = function($queryString) {
    return (this.ownerDocument.selectSingleNode) ?
        selectSingleNode(this.ownerDocument, $queryString, this) : null;
};
Element.prototype.__defineGetter__("text", function() {
    return this.textContent;
});

// HTMLDocument
// ------------
// support microsoft's "all" property
HTMLDocument.prototype.__defineGetter__("all", function() {
	return this.getElementsByTagName("*");
});
// support microsoft's "scripts" property
HTMLDocument.prototype.__defineGetter__("scripts", function() {
	return this.getElementsByTagName("script");
});
// mimic the "createEventObject" method for the document object
HTMLDocument.prototype.createEventObject = function() {
	return document.createEvent("Events");
};

// HTMLElement
// -----------
// mimic microsoft's "all" property
HTMLElement.prototype.__defineGetter__("all", function() {
	return this.getElementsByTagName("*");
});
// support "parentElement"
HTMLElement.prototype.__defineGetter__("parentElement", function() {
	return (this.parentNode == this.ownerDocument) ? null : getElementNode(this.parentNode);
});
// support "uniqueID"
HTMLElement.prototype.__defineGetter__("uniqueID", function() {
	// a global counter is stored privately as a property of this getter function.
	// initialise the counter
	if (!arguments.callee.count) arguments.callee.count = 0;
	// create the id and increment the counter
	var $uniqueID = "moz_id" + arguments.callee.count++;
	// creating a unique id, creates a global reference
	window[$uniqueID] = this;
	// we don't want to increment next time, so redefine the getter
	this.__defineGetter__("uniqueID", function(){return $uniqueID});
	return $uniqueID;
});
// mimic microsoft's "currentStyle"
HTMLElement.prototype.__defineGetter__("currentStyle", function() {
	return getComputedStyle(this, null);
});
// mimic microsoft's "runtimeStyle"
HTMLElement.prototype.__defineGetter__("runtimeStyle", function() {
//# this doesn't work yet (https://bugzilla.mozilla.org/show_bug.cgi?id=45424)
//#	return this.ownerDocument.defaultView.getOverrideStyle(this, null);
	return this.style;
});
// support "innerText"
HTMLElement.prototype.__defineGetter__("innerText", function() {
	return this.textContent;
});
HTMLElement.prototype.__defineSetter__("innerText", function($value) {
    this.innerHTML = "";
    this.appendChild(document.createTextNode($value));
});
// mimic the "createEventObject" method
HTMLElement.prototype.createEventObject = function() {
	return this.ownerDocument.createEventObject();
};
// mimic the "fireEvent" method
HTMLElement.prototype.fireEvent = function($name, $event) {
	if (!$event) $event = this.ownerDocument.createEventObject();
	$event.initEvent($name.slice(2), false, false);
	this.dispatchEvent($event);
	// not sure that this should be here??
	if (typeof this[$name] == "function") this[$name]();
	else if (this.getAttribute($name)) eval(this.getAttribute($name));
};
// support the "contains" method
HTMLElement.prototype.contains = function($element) {
	return Boolean($element == this || ($element && this.contains($element.parentElement)));
};
// support the "removeNode" method
HTMLElement.prototype.removeNode = function() {
    return this.parentNode.removeChild(this);
};
// support the "insertAdjacentElement" method
HTMLElement.prototype.insertAdjacentElement = function($where, $element) {
    switch ($where.toLowerCase()) {
        case "beforebegin":
            this.parentNode.insertBefore($element, this);
            break;
        case "beforeend":
            this.appendChild($element);
            break;
        case "afterbegin":
            this.insertBefore($element, this.firstChild);
            break;
        case "afterend":
            if (this.nextSibling) this.parentNode.insertBefore($element, this.nextSibling);
            else this.parentNode.appendChild($element);
            break;
        default:
            throw "Invalid Argument";
    }
};
HTMLElement.prototype.setCapture =
HTMLElement.prototype.releaseCapture = new Function; // dummy

// Events
// ------
function globaliseEventObject($event) {
    window.event = $event;
};
// mimic the "attachEvent" method
window.attachEvent =
HTMLDocument.prototype.attachEvent =
HTMLElement.prototype.attachEvent = function($name, $handler) {
	this.addEventListener($name.slice(2), globaliseEventObject, true);
	this.addEventListener($name.slice(2), $handler, false);
};
// mimic the "removeEvent" method
window.removeEvent =
HTMLDocument.prototype.removeEvent =
HTMLElement.prototype.removeEvent = function($name, $handler) {
	this.removeEventListener($name.slice(2), $handler, false);
};
// support microsoft's proprietary event properties
Event.prototype.__defineGetter__("srcElement", function() {
	return getElementNode(this.target);
});
Event.prototype.__defineGetter__("fromElement",function() {
	return (this.type == "mouseover") ? this.relatedTarget : (this.type == "mouseout") ? this.srcElement : null;
});
Event.prototype.__defineGetter__("toElement", function() {
	return (this.type == "mouseout") ? this.relatedTarget : (this.type == "mouseover") ? this.srcElement : null;
});
// convert w3c button id's to microsoft's
Event.prototype.__defineGetter__("button", function() {
	return (this.which == 1) ? 1 : (this.which == 2) ? 4 : 2;
});
// mimic "returnValue" (default is "true")
Event.prototype.__defineGetter__("returnValue", function() {
	return true;
});
Event.prototype.__defineSetter__("returnValue", function($value) {
	if (this.cancelable && !$value) {
		// this can't be undone!
		this.preventDefault();
		this.__defineGetter__("returnValue", function() {
			return false;
		});
	}
});
// mozilla already supports the read-only "cancelBubble"
//  so we only need to define the setter
Event.prototype.__defineSetter__("cancelBubble", function($value) {
	// this can't be undone!
	if ($value) this.stopPropagation();
});
Event.prototype.__defineGetter__("offsetX", function() {
	return this.layerX;
});
Event.prototype.__defineGetter__("offsetY", function() {
	return this.layerY;
});
// and that's it!
// thanks mozilla for being such a developer's playground :D
})();