text: (function(){
  var extend = function(d, s) { for (var p in s) d[p] = s[p]; return d;},
  	// only uses browser detection for key events
  	browser = {
  		msie:     !!(window.attachEvent && !window.opera),
  		opera:  !!window.opera,
  		webkit : navigator.userAgent.indexOf('AppleWebKit/') > -1,
  		safari: navigator.userAgent.indexOf('AppleWebKit/') > -1 && navigator.userAgent.indexOf('Chrome/') == -1,
  		gecko:  navigator.userAgent.indexOf('Gecko') > -1,
  		mobilesafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/),
  		rhino : navigator.userAgent.match(/Rhino/) && true
  	},
  	createEventObject = function(type, options, element){
  		var event = element.ownerDocument.createEventObject();
  		return extend(event, options);
  	},
  	data = {}, 
  	id = 1, 
  	expando = "_synthetic"+(new Date() - 0),
  	bind,
  	unbind,
  	key = /keypress|keyup|keydown/,
  	page = /load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll/,
  	//this is maintained so we can click on html and blur the active element
  	activeElement,
  
  /**
   * @class Syn
   * @download funcunit/dist/syn.js
   * @test funcunit/synthetic/qunit.html
   * Syn is used to simulate user actions.  It creates synthetic events and
   * performs their default behaviors.
   * 
   * <h2>Basic Use</h2>
   * The following clicks an input element with <code>id='description'</code>
   * and then types <code>'Hello World'</code>.
   * 
  @codestart
  Syn.click({},'description')
    .type("Hello World")
  @codeend
   * <h2>User Actions and Events</h2>
   * <p>Syn is typically used to simulate user actions as opposed to triggering events. Typing characters
   * is an example of a user action.  The keypress that represents an <code>'a'</code>
   * character being typed is an example of an event. 
   * </p>
   * <p>
   *   While triggering events is supported, it's much more useful to simulate actual user behavior.  The 
   *   following actions are supported by Syn:
   * </p>
   * <ul>
   *   <li><code>[Syn.prototype.click click]</code> - a mousedown, focus, mouseup, and click.</li>
   *   <li><code>[Syn.prototype.dblclick dblclick]</code> - two <code>click!</code> events followed by a <code>dblclick</code>.</li>
   *   <li><code>[Syn.prototype.key key]</code> - types a single character (keydown, keypress, keyup).</li>
   *   <li><code>[Syn.prototype.type type]</code> - types multiple characters into an element.</li>
   *   <li><code>[Syn.prototype.move move]</code> - moves the mouse from one position to another (triggering mouseover / mouseouts).</li>
   *   <li><code>[Syn.prototype.drag drag]</code> - a mousedown, followed by mousemoves, and a mouseup.</li>
   * </ul>
   * All actions run asynchronously.  
   * Click on the links above for more 
   * information on how to use the specific action.
   * <h2>Asynchronous Callbacks</h2>
   * Actions don't complete immediately. This is almost 
   * entirely because <code>focus()</code> 
   * doesn't run immediately in IE.
   * If you provide a callback function to Syn, it will 
   * be called after the action is completed.
   * <br/>The following checks that "Hello World" was entered correctly: 
  @codestart
  Syn.click({},'description')
    .type("Hello World", function(){
    
    ok("Hello World" == document.getElementById('description').value)  
  })
  @codeend
  <h2>Asynchronous Chaining</h2>
  <p>You might have noticed the [Syn.prototype.then then] method.  It provides chaining 
  so you can do a sequence of events with a single (final) callback.  
  </p><p>
  If an element isn't provided to then, it uses the previous Syn's element.
  </p>
  The following does a lot of stuff before checking the result:
  @codestart
  Syn.type('ice water','title')
    .type('ice and water','description')
    .click({},'create')
    .drag({to: 'favorites'},'newRecipe',
      function(){
        ok($('#newRecipe').parents('#favorites').length);
      })
  @codeend
  
  <h2>jQuery Helper</h2>
  If jQuery is present, Syn adds a triggerSyn helper you can use like:
  @codestart
  $("#description").triggerSyn("type","Hello World");
  @codeend
   * <h2>Key Event Recording</h2>
   * <p>Every browser has very different rules for dispatching key events.  
   * As there is no way to feature detect how a browser handles key events,
   * synthetic uses a description of how the browser behaves generated
   * by a recording application.  </p>
   * <p>
   * If you want to support a browser not currently supported, you can
   * record that browser's key event description and add it to
   * <code>Syn.key.browsers</code> by it's navigator agent.
   * </p>
  @codestart
  Syn.key.browsers["Envjs\ Resig/20070309 PilotFish/1.2.0.10\1.6"] = {
    'prevent':
      {"keyup":[],"keydown":["char","keypress"],"keypress":["char"]},
    'character':
      { ... }
  }
  @codeend
   * <h2>Limitations</h2>
   * Syn fully supports IE 6+, FF 3+, Chrome, Safari, Opera 10+.
   * With FF 1+, drag / move events are only partially supported. They will
   * not trigger mouseover / mouseout events.<br/>
   * Safari crashes when a mousedown is triggered on a select.  Syn will not 
   * create this event.
   * <h2>Contributing to Syn</h2>
   * Have we missed something? We happily accept patches.  The following are 
   * important objects and properties of Syn:
   * <ul>
   * 	<li><code>Syn.create</code> - contains methods to setup, convert options, and create an event of a specific type.</li>
   *  <li><code>Syn.defaults</code> - default behavior by event type (except for keys).</li>
   *  <li><code>Syn.key.defaults</code> - default behavior by key.</li>
   *  <li><code>Syn.keycodes</code> - supported keys you can type.</li>
   * </ul>
   * <h2>Roll Your Own Functional Test Framework</h2>
   * <p>Syn is really the foundation of JavaScriptMVC's functional testing framework - [FuncUnit].
   *   But, we've purposely made Syn work without any dependencies in the hopes that other frameworks or 
   *   testing solutions can use it as well.
   * </p>
   * @constructor 
   * Creates a synthetic event on the element.
   * @param {Object} type
   * @param {Object} options
   * @param {Object} element
   * @param {Object} callback
   * @return Syn
   */
  Syn = function(type, options, element, callback){		
  	return ( new Syn.init(type, options, element, callback) )
  }
  	
  if(window.addEventListener){ // Mozilla, Netscape, Firefox
  	bind = function(el, ev, f){
  		el.addEventListener(ev, f, false)
  	}
  	unbind = function(el, ev, f){
  		el.removeEventListener(ev, f, false)
  	}
  }else{
  	bind = function(el, ev, f){
  		el.attachEvent("on"+ev, f)
  	}
  	unbind = function(el, ev, f){
  		el.detachEvent("on"+ev, f)
  	}
  }	
  /**
   * @Static
   */	
  extend(Syn,{
  	/**
  	 * Creates a new synthetic event instance
  	 * @hide
  	 * @param {Object} type
  	 * @param {Object} options
  	 * @param {Object} element
  	 * @param {Object} callback
  	 */
  	init: function( type, options, element, callback ) {
  		var args = Syn.args(options,element, callback),
  			self = this;
  		this.queue = [];
  		this.element = args.element;
  		
  		//run event
  		if(typeof this[type] == "function") {
  			this[type](args.options, args.element, function(defaults,el ){
  				args.callback && args.callback.apply(self, arguments);
  				self.done.apply(self, arguments)		
  			})
  		}else{
  			this.result = Syn.trigger(type, args.options, args.element);
  			args.callback && args.callback.call(this, args.element, this.result);
  		}
  	},
  	jquery: function( el, fast ) {
  		if(window.FuncUnit && window.FuncUnit.jquery){
  			return window.FuncUnit.jquery
  		} if (el){
  			return Syn.helpers.getWindow(el).jQuery || window.jQuery	
  		}
  		else{
  			return window.jQuery
  		}
  	},
  	/**
  	 * Returns an object with the args for a Syn.
  	 * @hide
  	 * @return {Object}
  	 */
  	args: function() {
  		var res = {}
  		for(var i=0; i < arguments.length; i++){
  			if(typeof arguments[i] == 'function'){
  				res.callback = arguments[i]
  			}else if(arguments[i] && arguments[i].jquery){
  				res.element = arguments[i][0];
  			}else if(arguments[i] && arguments[i].nodeName){
  				res.element = arguments[i];
  			}else if(res.options && typeof arguments[i] == 'string'){ //we can get by id
  				res.element = document.getElementById(arguments[i])
  			}
  			else if(arguments[i]){
  				res.options = arguments[i];
  			}
  		}
  		return res;
  	},
  	click: function( options, element, callback ) {
  		Syn('click!',options,element, callback);
  	},
  	/**
  	 * @attribute defaults
  	 * Default actions for events.  Each default function is called with this as its 
  	 * element.  It should return true if a timeout 
  	 * should happen after it.  If it returns an element, a timeout will happen
  	 * and the next event will happen on that element.
  	 */
  	defaults : {
  		focus: function() {
  			if(!Syn.support.focusChanges){
  				var element = this,
  					nodeName = element.nodeName.toLowerCase();
  				Syn.data(element,"syntheticvalue", element.value)
  				
  				//TODO, this should be textarea too
  				//and this might be for only text style inputs ... hmmmmm ....
  				
  				if(nodeName == "input" || nodeName == "textarea"){ 
  					bind(element, "blur", function(){
  						if( Syn.data(element,"syntheticvalue") !=  element.value){
  							
  							Syn.trigger("change", {}, element);
  						}
  						unbind(element,"blur", arguments.callee)
  					})
  					
  				}
  			}
  		},
  		submit: function() {
  			Syn.onParents(this, function(el){
  				if( el.nodeName.toLowerCase() == 'form'){
  					el.submit()
  					return false;
  				}
  			});
  		}
  	},
  	changeOnBlur: function( element, prop, value ) {
  		
  		bind(element, "blur", function(){		
  			if( value !=  element[prop]){
  				Syn.trigger("change", {}, element);
  			}
  			unbind(element,"blur", arguments.callee)
  		})
  		
  	},
  	/**
  	 * Returns the closest element of a particular type.
  	 * @hide
  	 * @param {Object} el
  	 * @param {Object} type
  	 */
  	closest: function( el, type ) {
  		while(el && el.nodeName.toLowerCase() != type.toLowerCase()){
  			el = el.parentNode
  		}
  		return el;
  	},
  	/**
  	 * adds jQuery like data (adds an expando) and data exists FOREVER :)
  	 * @hide
  	 * @param {Object} el
  	 * @param {Object} key
  	 * @param {Object} value
  	 */
  	data: function( el, key, value ) {
  		var d;
  		if(!el[expando]){
  			el[expando] = id++;
  		}
  		if(!data[el[expando]]){
  			data[el[expando]] = {};
  		}
  		d = data[el[expando]]
  		if(value){
  			data[el[expando]][key] = value;
  		}else{
  			return data[el[expando]][key];
  		}
  	},
  	/**
  	 * Calls a function on the element and all parents of the element until the function returns
  	 * false.
  	 * @hide
  	 * @param {Object} el
  	 * @param {Object} func
  	 */
  	onParents: function( el, func ) {
  		var res;
  		while(el && res !== false){
  			res = func(el)
  			el = el.parentNode
  		}
  		return el;
  	},
  	//regex to match focusable elements
  	focusable : /^(a|area|frame|iframe|label|input|select|textarea|button|html|object)$/i,
  	/**
  	 * Returns if an element is focusable
  	 * @hide
  	 * @param {Object} elem
  	 */
  	isFocusable: function( elem ) {
  		var attributeNode;
  		return ( this.focusable.test(elem.nodeName) || (
  			(attributeNode = elem.getAttributeNode( "tabIndex" )) && attributeNode.specified ) )
  			&& Syn.isVisible(elem)
  	},
  	/**
  	 * Returns if an element is visible or not
  	 * @hide
  	 * @param {Object} elem
  	 */
  	isVisible: function( elem ) {
  		return (elem.offsetWidth && elem.offsetHeight) || (elem.clientWidth && elem.clientHeight)
  	},
  	/**
  	 * Gets the tabIndex as a number or null
  	 * @hide
  	 * @param {Object} elem
  	 */
  	tabIndex: function( elem ) {
  		var attributeNode = elem.getAttributeNode( "tabIndex" );
  		return attributeNode && attributeNode.specified && ( parseInt( elem.getAttribute('tabIndex') ) || 0 )
  	},
  	bind : bind,
  	unbind : unbind,
  	browser: browser,
  	//some generic helpers
  	helpers : {
  		createEventObject : createEventObject,
  		createBasicStandardEvent: function( type, defaults ) {
  			var event;
  			try {
  				event = document.createEvent("Events");
  			} catch(e2) {
  				event = document.createEvent("UIEvents");
  			} finally {
  				event.initEvent(type, true, true);
  				extend(event, defaults);
  			}
  			return event;
  		},
  		inArray: function( item, array ) {
  			for(var i =0; i < array.length; i++){
  				if(array[i] == item){
  					return i;
  				}
  			}
  			return -1;
  		},
  		getWindow: function( element ) {
  			return element.ownerDocument.defaultView || element.ownerDocument.parentWindow
  		},
  		extend:  extend,
  		scrollOffset: function( win ) {
  			var doc = win.document.documentElement,
  				body = win.document.body;
  			return {
  				left :  (doc && doc.scrollLeft || body && body.scrollLeft || 0) + (doc.clientLeft || 0),
  				top : (doc && doc.scrollTop || body && body.scrollTop || 0) + (doc.clientTop || 0)
  			}
  				
  		},
  		addOffset: function( options, el ) {
  			var jq = Syn.jquery(el)
  			if(typeof options == 'object' &&
  			   options.clientX === undefined &&
  			   options.clientY === undefined &&
  			   options.pageX   === undefined &&
  			   options.pageY   === undefined && jq){
  				var el = jq(el)
  					off = el.offset();
  				options.pageX = off.left + el.width() /2 ;
  				options.pageY = off.top + el.height() /2 ;
  			}
  		}
  	},
  	// place for key data
  	key : {
  		ctrlKey : null,
  		altKey : null,
  		shiftKey : null,
  		metaKey : null
  	},
  	//triggers an event on an element, returns true if default events should be run
  	/**
  	 * Dispatches an event and returns true if default events should be run.
  	 * @hide
  	 * @param {Object} event
  	 * @param {Object} element
  	 * @param {Object} type
  	 * @param {Object} autoPrevent
  	 */
  	dispatch : (document.documentElement.dispatchEvent ? 
  				function(event, element, type, autoPrevent){
  					var preventDefault = event.preventDefault, 
  						prevents = autoPrevent ? -1 : 0;
  					
  					//automatically prevents the default behavior for this event
  					//this is to protect agianst nasty browser freezing bug in safari
  					if(autoPrevent){
  						bind(element, type, function(ev){
  							ev.preventDefault()
  							unbind(this, type, arguments.callee)
  						})
  					}
  					
  					
  					event.preventDefault = function(){
  						prevents++;
  						if(++prevents > 0){
  							preventDefault.apply(this,[]);
  						}
  					}
  					element.dispatchEvent(event)
  					return prevents <= 0;
  				} : 
  				function(event, element, type){
  					try {window.event = event;}catch(e) {}
  					//source element makes sure element is still in the document
  					return element.sourceIndex <= 0 || element.fireEvent('on'+type, event)
  				}
  			),
  	/**
  	 * @attribute
  	 * @hide
  	 * An object of eventType -> function that create that event.
  	 */
  	create :  {
  		//-------- PAGE EVENTS ---------------------
  		page : {
  			event : document.createEvent ? function(type, options, element){
  					var event = element.ownerDocument.createEvent("Events");
  					event.initEvent(type, true, true ); 
  					return event;
  				} : createEventObject
  		},
  		// unique events
  		focus : {
  			event: function( type, options, element ) {
  				Syn.onParents(element, function(el){
  					if( Syn.isFocusable(el)){
  						if(el.nodeName.toLowerCase() != 'html'){
  							el.focus();
  							activeElement = el;
  						}
  						else if(activeElement){
  							// TODO: The HTML element isn't focasable in IE, but it is
  							// in FF.  We should detect this and do a true focus instead
  							// of just a blur
  							var doc = Syn.helpers.getWindow(element).document;
  							if(doc != window.document){
  								return false;
  							}else if(doc.activeElement){
  								doc.activeElement.blur();
  								activeElement = null;
  							}else{
  								activeElement.blur();
  								activeElement = null;
  							}
  							
  							
  						}
  						return false
  					}
  				});
  				return true;
  			}
  		}
  	},
  	/**
  	 * @attribute support
  	 * Feature detected properties of a browser's event system.
  	 * Support has the following properties:
  	 * <ul>
  	 * 	<li><code>clickChanges</code> - clicking on an option element creates a change event.</li>
  	 *  <li><code>clickSubmits</code> - clicking on a form button submits the form.</li>
  	 *  <li><code>mouseupSubmits</code> - a mouseup on a form button submits the form.</li>
  	 *  <li><code>radioClickChanges</code> - clicking a radio button changes the radio.</li>
  	 *  <li><code>focusChanges</code> - focus/blur creates a change event.</li>
  	 *  <li><code>linkHrefJS</code> - An achor's href JavaScript is run.</li>
  	 *  <li><code>mouseDownUpClicks</code> - A mousedown followed by mouseup creates a click event.</li>
  	 *  <li><code>tabKeyTabs</code> - A tab key changes tabs.</li>
  	 *  <li><code>keypressOnAnchorClicks</code> - Keying enter on an anchor triggers a click.</li>
  	 * </ul>
  	 */
  	support : {
  		clickChanges : false,
  		clickSubmits : false,
  		keypressSubmits : false,
  		mouseupSubmits: false,
  		radioClickChanges : false,
  		focusChanges : false,
  		linkHrefJS : false,
  		keyCharacters : false,
  		backspaceWorks : false,
  		mouseDownUpClicks : false,
  		tabKeyTabs : false,
  		keypressOnAnchorClicks : false,
  		optionClickBubbles : false,
  		ready : 0
  	},
  	/**
  	 * Creates a synthetic event and dispatches it on the element.  
  	 * This will run any default actions for the element.
  	 * Typically you want to use Syn, but if you want the return value, use this.
  	 * @param {String} type
  	 * @param {Object} options
  	 * @param {HTMLElement} element
  	 * @return {Boolean} true if default events were run, false if otherwise.
  	 */
  	trigger: function( type, options, element ) {
  		options || (options = {});
  		
  		var create = Syn.create,
  			setup = create[type] && create[type].setup,
  			kind = key.test(type) ? 
  				'key' : 
  				( page.test(type) ?
  					"page" : "mouse" ),
  				createType = create[type] || {},
  				createKind = create[kind],
  				event,
  				ret,
  				autoPrevent,
  				dispatchEl = element;
  		
  		//any setup code?
  		Syn.support.ready == 2 && setup && setup(type, options, element);
  		
  		autoPrevent = options._autoPrevent;
  		//get kind
  		
  		delete options._autoPrevent;
  			
  		if(createType.event){
  			ret = createType.event(type, options, element)
  		}else{
  			//convert options
  			options = createKind.options ? createKind.options(type,options,element) : options;
  			
  			if(!Syn.support.changeBubbles && /option/i.test(element.nodeName)){
  				dispatchEl = element.parentNode; //jQuery expects clicks on select
  			}
  			
  			//create the event
  			event = createKind.event(type,options,dispatchEl)
  			
  			//send the event
  			ret = Syn.dispatch(event, dispatchEl, type, autoPrevent)
  		}
  		
  		//run default behavior
  		ret && Syn.support.ready == 2
  			&& Syn.defaults[type] 
  			&& Syn.defaults[type].call(element, options, autoPrevent);
  		return ret;
  	},
  	eventSupported: function( eventName ) { 
  		var el = document.createElement("div"); 
  		eventName = "on" + eventName; 
  
  		var isSupported = (eventName in el); 
  		if ( !isSupported ) { 
  			el.setAttribute(eventName, "return;"); 
  			isSupported = typeof el[eventName] === "function"; 
  		} 
  		el = null; 
  
  		return isSupported; 
  	}
  	
  });
  	var h = Syn.helpers;
  /**
   * @Prototype
   */
  extend(Syn.init.prototype,{
  	/**
  	 * @function then
  	 * <p>
  	 * Then is used to chain a sequence of actions to be run one after the other.
  	 * This is useful when many asynchronous actions need to be performed before some
  	 * final check needs to be made.
  	 * </p>
  	 * <p>The following clicks and types into the <code>id='age'</code> element and then checks that only numeric characters can be entered.</p>
  	 * <h3>Example</h3>
  	 * @codestart
  	 * Syn('click',{},'age')
  	 *   .then('type','I am 12',function(){
  	 *   equals($('#age').val(),"12")  
  	 * })
  	 * @codeend
  	 * If the element argument is undefined, then the last element is used.
  	 * 
  	 * @param {String} type The type of event or action to create: "_click", "_dblclick", "_drag", "_type".
  	 * @param {Object} options Optiosn to pass to the event.
  	 * @param {String|HTMLElement} [element] A element's id or an element.  If undefined, defaults to the previous element.
  	 * @param {Function} [callback] A function to callback after the action has run, but before any future chained actions are run.
  	 */
  	then: function( type, options, element, callback ) {
  		if(Syn.autoDelay){
  			this.delay();
  		}
  		var args = Syn.args(options,element, callback),
  			self = this;
  
  		
  		//if stack is empty run right away
  		
  		//otherwise ... unshift it
  		this.queue.unshift(function(el, prevented){
  			
  			if(typeof this[type] == "function") {
  				this.element = args.element || el;
  				this[type](args.options, this.element, function(defaults, el){
  					args.callback && args.callback.apply(self, arguments);
  					self.done.apply(self, arguments)		
  				})
  			}else{
  				this.result = Syn.trigger(type, args.options, args.element);
  				args.callback && args.callback.call(this, args.element, this.result);
  				return this;
  			}
  		})
  		return this;
  	},
  	/**
  	 * Delays the next command a set timeout.
  	 * @param {Number} [timeout]
  	 * @param {Function} [callback]
  	 */
  	delay: function( timeout, callback ) {
  		if(typeof timeout == 'function'){
  			callback = timeout;
  			timeout = null;
  		}
  		timeout = timeout || 600
  		var self = this;
  		this.queue.unshift(function(){
  			setTimeout(function(){
  				callback && callback.apply(self,[])
  				self.done.apply(self, arguments)
  			},timeout)
  		})
  		return this;
  	},
  	done: function( defaults, el ) {
  		el && (this.element = el);;
  		if(this.queue.length){
  			this.queue.pop().call(this, this.element, defaults);
  		}
  		
  	},
  	/**
  	 * @function click
  	 * Clicks an element by triggering a mousedown, 
  	 * mouseup, 
  	 * and a click event.
  	 * <h3>Example</h3>
  	 * @codestart
  	 * Syn.click({},'create',function(){
  	 *   //check something
  	 * })
  	 * @codeend
  	 * You can also provide the coordinates of the click.  
  	 * If jQuery is present, it will set clientX and clientY
  	 * for you.  Here's how to set it yourself:
  	 * @codestart
  	 * Syn.click(
  	 *     {clientX: 20, clientY: 100},
  	 *     'create',
  	 *     function(){
  	 *       //check something
  	 *     })
  	 * @codeend
  	 * You can also provide pageX and pageY and Syn will convert it for you.
  	 * @param {Object} options
  	 * @param {HTMLElement} element
  	 * @param {Function} callback
  	 */
  	"_click" : function( options, element, callback, force ) {
  		Syn.helpers.addOffset(options, element);
  		Syn.trigger("mousedown", options, element);
  		
  		//timeout is b/c IE is stupid and won't call focus handlers
  		setTimeout(function(){
  			Syn.trigger("mouseup", options, element)
  			if(!Syn.support.mouseDownUpClicks || force){
  				Syn.trigger("click", options, element)
  				callback(true)
  			}else{
  				//we still have to run the default (presumably)
  				Syn.create.click.setup('click',options,element)
  				Syn.defaults.click.call(element)
  				//must give time for callback
  				setTimeout(function(){
  					callback(true)
  				},1)
  			}
  			
  		},1)
  	},
  	/**
  	 * Right clicks in browsers that support it (everyone but opera).
  	 * @param {Object} options
  	 * @param {Object} element
  	 * @param {Object} callback
  	 */
  	"_rightClick" : function( options, element, callback ) {
  		Syn.helpers.addOffset(options, element);
  		var mouseopts =  extend( extend({},Syn.mouse.browser.right.mouseup ), options)
  		
  		Syn.trigger("mousedown", mouseopts, element);
  		
  		//timeout is b/c IE is stupid and won't call focus handlers
  		setTimeout(function(){
  			Syn.trigger("mouseup", mouseopts, element)
  			if (Syn.mouse.browser.contextmenu) {
  				Syn.trigger("contextmenu", 
  					extend( extend({},Syn.mouse.browser.right.contextmenu ), options), 
  					element)
  			}
  			callback(true)
  		},1)
  	},
  	/**
  	 * @function dblclick
  	 * Dblclicks an element.  This runs two [Syn.prototype.click click] events followed by
  	 * a dblclick on the element.
  	 * <h3>Example</h3>
  	 * @codestart
  	 * Syn.dblclick({},'open')
  	 * @codeend
  	 * @param {Object} options
  	 * @param {HTMLElement} element
  	 * @param {Function} callback
  	 */
  	"_dblclick" : function( options, element, callback ) {
  		Syn.helpers.addOffset(options, element);
  		var self = this;
  		this._click(options, element, function(){
  			setTimeout(function(){
  				self._click(options, element, function(){
  					Syn.trigger("dblclick", options, element)
  					callback(true)
  				},true)
  			},2)
  			
  		})
  	}
  })
  
  var actions = ["click","dblclick","move","drag","key","type",'rightClick'],
  	makeAction = function(name){
  		Syn[name] = function(options, element, callback){
  			return Syn("_"+name, options, element, callback)
  		}
  		Syn.init.prototype[name] = function(options, element, callback){
  			return this.then("_"+name, options, element, callback)
  		}
  	}
  for(var i=0; i < actions.length; i++){
  	makeAction(actions[i]);
  }
  /**
   * Used for creating and dispatching synthetic events.
   * @codestart
   * new MVC.Syn('click').send(MVC.$E('id'))
   * @codeend
   * @constructor Sets up a synthetic event.
   * @param {String} type type of event, ex: 'click'
   * @param {optional:Object} options
   */
  if (window.jQuery || (window.FuncUnit && window.FuncUnit.jquery)) {
  	((window.FuncUnit && window.FuncUnit.jquery) || window.jQuery  ).fn.triggerSyn = function(type, options, callback){
  		Syn(type, options, this[0], callback)
  		return this;
  	};
  }
  
  window.Syn = Syn;
  }())