(function() {
	var t = document.createElement('div');
	var domNodeInsertedIntoDocument = false;
	var domNodeRemovedFromDocument  = false;
	
	t.addEventListener('DOMNodeInsertedIntoDocument', function() { domNodeInsertedIntoDocument = true; }, false);
	t.addEventListener('DOMNodeRemovedFromDocument', function() { domNodeRemovedFromDocument = true; }, false);
	
	document.documentElement.appendChild(t);
	document.documentElement.removeChild(t);
	
	var addEventListener = Element.prototype.addEventListener;
	var removeEventListener = Element.prototype.removeEventListener;

	if (! 'WeakMap'	in window) {
		window.WeakMap = function() { this._map = { }; this._keys = [] }
		window.WeakMap.prototype = {
			_key: function(key) { 
				if (this._keys.indexOf(key) == -1)
					this._keys.push(key);
				return this._keys.indexOf(key);
			},
			get: function(key, defaultValue) { return this._map[this._key(key)] === void 0 ? defaultValue : this._map[this._key(key)]; },
			set: function(key, value) { this._map[this._key(key)] = value; },
			has: function(key) { return this._map[this._key(key)] !== void 0; },
			delete: function(key) {
				delete this._map[this._key(key)];
				this._keys[this._key(key)] = void 0;
			}
		};
	}
	
	if (!domNodeInsertedIntoDocument || !domNodeRemovedFromDocument) {
		function isInSubtree(node, parent) {
			do {
				if (node == parent) return true;
				node = node.parentNode;
			} while (node);
			return false;
		}
		function isInDocument(node) {
			return isInSubtree(node, document.documentElement);
		}
		
		var nodesWithInsertEvent = [ ];
		var nodesWithRemoveEvent = [ ];
		window._nodeList = { insert: nodesWithInsertEvent, remove: nodesWithRemoveEvent }
		var addedDocumentNodeInsertedEvent = false;
		var addedDocumentNodeRemovedEvent  = false;
		function documentNodeInsertedEvent(event) {
			nodesWithInsertEvent.forEach(function(aNode) {
				var element = aNode.element.get(aNode);
				if (!element) return;
				if (element._isInDocument) return;
				if (isInDocument(element)) {
					var event = document.createEvent('Event');
					event.initEvent('DOMNodeInsertedIntoDocument', false, false);
					element.dispatchEvent(event);
					element._isInDocument = true;
				}
			});
		}
		
		function documentNodeRemovedEvent(outerEvent) {
			nodesWithRemoveEvent.forEach(function(aNode) {
				var element = aNode.element.get(aNode);
				if (!element) return;
				if (!element._isInDocument) return;
				if (isInSubtree(element, outerEvent.target)) {
					var event = document.createEvent('Event');
					event.initEvent('DOMNodeRemovedFromDocument', false, false);
					element.dispatchEvent(event);
					element._isInDocument = false;
				}
			});
		}
		
		Element.prototype.addEventListener = function(eventName, handler, capture) {
			if (eventName == 'DOMNodeInsertedIntoDocument' && !domNodeInsertedIntoDocument) {
				console.log('Adding element to insert listeners: ', this);
				if (!Object.getOwnPropertyDescriptor(this, '_isInDocument')) {
					Object.defineProperty(this, '_isInDocument', {
						enumerable: false,
						writable: true,
						configurable: true,
						value: isInDocument(this)
					});
				}
				
				var obj = { element: new WeakMap(), handler: handler, isCapture: capture };
				obj.element.set(obj, this);
				nodesWithInsertEvent.push(obj);
				
				if (!addedDocumentNodeInsertedEvent) {
					document.addEventListener('DOMNodeInserted', documentNodeInsertedEvent, false);
					addedDocumentNodeInsertedEvent = true;
				}
			}
			
			if (eventName == 'DOMNodeRemovedFromDocument' && !domNodeRemovedFromDocument) {
				console.log('Adding element to remove listeners: ', this);
				if (!Object.getOwnPropertyDescriptor(this, '_isInDocument')) {
					Object.defineProperty(this, '_isInDocument', {
						enumerable: false,
						writable: true,
						configurable: true,
						value: isInDocument(this)
					});
				}
				
				var obj = { element: new WeakMap(), handler: handler, isCapture: capture };
				obj.element.set(obj, this);
				nodesWithRemoveEvent.push(obj);
				
				if (!addedDocumentNodeRemovedEvent) {
					document.addEventListener('DOMNodeRemoved', documentNodeRemovedEvent, false);
					addedDocumentNodeRemovedEvent = true;
				}
			}
			
			return addEventListener.apply(this, arguments);
		}

		Element.prototype.removeEventListener = function(eventName, handler, capture) {
			if (eventName == 'DOMNodeInsertedIntoDocument' && !domNodeInsertedIntoDocument) {
				console.log('Removing node from insert listeners: ', this);
				nodesWithInsertEvent = nodesWithInsertEvent.filter(function(item) {
					if (!item.element.get(item)) return false;
					if (item.element.get(item) == this && item.handler == handler && item.isCapture == capture)
						return false;
					return true;
				}, this);
			}
			
			if (eventName == 'DOMNodeRemovedFromDocument' && !domNodeRemovedFromDocument) {
				console.log('Removing node from remove listeners: ', this);
				nodesWithRemoveEvent = nodesWithRemoveEvent.filter(function(item) {
					if (!item.element.get(item)) return false;
					if (item.handler == handler && item.isCapture == capture && item.element.get(item) == this)
						return false;
					return true;
				}, this);
			}
			
			return removeEventListener.apply(this, arguments);
		}
		
		document.createElement = function() {
			var e = Object.getPrototypeOf(this).createElement.apply(this, arguments);
			if (Object.getPrototypeOf(e).addEventListener != Element.prototype.addEventListener)
				Object.getPrototypeOf(e).addEventListener = Element.prototype.addEventListener;
			if (Object.getPrototypeOf(e).removeEventListener != Element.prototype.removeEventListener)
				Object.getPrototypeOf(e).removeEventListener = Element.prototype.removeEventListener;
			return e;
		}
	}
})();


