/**
 * Event.simulate(@element, eventName[, options]) -> Element
 *
 * - @element: element to fire event on
 * - eventName: name of event to fire (only MouseEvents and HTMLEvents interfaces are supported)
 * - options: optional object to fine-tune event properties - pointerX, pointerY, ctrlKey, etc.
 *
 *    $('foo').simulate('click'); // => fires "click" event on an element with id=foo
 *
 **/
(function(){

  var eventMatchers = {
	'HTMLEvents': /^(?:load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll)$/,
	'MouseEvents': /^(?:click|mouse(?:down|up|over|move|out))$/
  }
  var defaultOptions = {
	pointerX: 0,
	pointerY: 0,
	button: 0,
	ctrlKey: false,
	altKey: false,
	shiftKey: false,
	metaKey: false,
	bubbles: true,
	cancelable: true
  }

  Event.simulate = function(element, eventName) {
	var options = Object.extend(defaultOptions, arguments[2] || { });
	var oEvent, eventType = null;

	element = $(element);

	for (var name in eventMatchers) {
	  if (eventMatchers[name].test(eventName)) { eventType = name; break; }
	}

	if (!eventType)
	  throw new SyntaxError('Only HTMLEvents and MouseEvents interfaces are supported');

	if (document.createEvent) {
	  oEvent = document.createEvent(eventType);
	  if (eventType == 'HTMLEvents') {
		oEvent.initEvent(eventName, options.bubbles, options.cancelable);
	  }
	  else {
		oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, document.defaultView,
		  options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
		  options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element);
	  }
	  element.dispatchEvent(oEvent);
	}
	else {
	  options.clientX = options.pointerX;
	  options.clientY = options.pointerY;
	  oEvent = Object.extend(document.createEventObject(), options);
	  element.fireEvent('on' + eventName, oEvent);
	}
	return element;
  }

  Element.addMethods({ simulate: Event.simulate });
})()
