/**
 * $Id$
 *
 * @author Moxiecode
 * @copyright Copyright � 2004-2007, Moxiecode Systems AB, All rights reserved.
 *
 * The contents of this file will be wrapped in a class later on.
 */

/**#@+
 * @member TinyMCE_Engine
 * @method
 */
tinyMCE.add(TinyMCE_Engine, {
	/**
	 * Sets the enabled/disabled state of build in events on the specific node.
	 * This function is needed since some events gets executed in WYSIWYG mode.
	 *
	 * @param {HTMLNode} node HTML node to enable/disable events on.
	 * @param {boolean} state true/false state if the events should be disabled or enabled.
	 * @private
	 */
	_setEventsEnabled : function(node, state) {
		var evs, x, y, elms, i, event;
		var events = ['onfocus','onblur','onclick','ondblclick',
					'onmousedown','onmouseup','onmouseover','onmousemove',
					'onmouseout','onkeypress','onkeydown','onkeydown','onkeyup'];

		evs = tinyMCE.settings.event_elements.split(',');
		for (y=0; y<evs.length; y++){
			elms = node.getElementsByTagName(evs[y]);
			for (i=0; i<elms.length; i++) {
				event = "";

				for (x=0; x<events.length; x++) {
					if ((event = tinyMCE.getAttrib(elms[i], events[x])) !== '') {
						event = tinyMCE.cleanupEventStr("" + event);

						if (!state)
							event = "return true;" + event;
						else
							event = event.replace(/^return true;/gi, '');

						elms[i].removeAttribute(events[x]);
						elms[i].setAttribute(events[x], event);
					}
				}
			}
		}
	},

	/**
	 * Patch function for MSIE specific events, this one simply grabs the window.event object and
	 * passes it as a argument to the handleEvent function of the TinyMCE_Engine class.
	 *
	 * @param {string} editor_id Editor id to patch.
	 * @private
	 */
	_eventPatch : function(editor_id) {
		var n, inst, win, e;

		// Remove odd, error
		if (typeof(tinyMCE) == "undefined")
			return true;

		try {
			// Try selected instance first
			if (tinyMCE.selectedInstance) {
				win = tinyMCE.selectedInstance.getWin();

				if (win && win.event) {
					e = win.event;

					if (!e.target)
						e.target = e.srcElement;

					TinyMCE_Engine.prototype.handleEvent(e);
					return;
				}
			}

			// Search for it
			for (n in tinyMCE.instances) {
				inst = tinyMCE.instances[n];

				if (!tinyMCE.isInstance(inst))
					continue;

				inst.select();
				win = inst.getWin();

				if (win && win.event) {
					e = win.event;

					if (!e.target)
						e.target = e.srcElement;

					TinyMCE_Engine.prototype.handleEvent(e);
					return;
				}
			}
		} catch (ex) {
			// Ignore error if iframe is pointing to external URL
		}
	},

	findEvent : function(e) {
		var n, inst;

		if (e)
			return e;

		for (n in tinyMCE.instances) {
			inst = tinyMCE.instances[n];

			if (tinyMCE.isInstance(inst) && inst.getWin().event)
				return inst.getWin().event;
		}

		return null;
	},

	/**
	 * Unload document event handler function. This function will be executed when the
	 * page is unloaded, this will automaticly move the current editor contents to the textarea element this enables
	 * the editor to restore it's state when the user presses the back button in the browser.
	 * This will execute the triggerSave function.
	 */
	unloadHandler : function() {
		tinyMCE.triggerSave(true, true);
	},

	/**
	 * Adds the handleEvent function to the specified editor instance.
	 *
	 * @param {inst} inst Editor control instance to add event handler to.
	 */
	addEventHandlers : function(inst) {
		this.setEventHandlers(inst, 1);
	},

	/**
	 * Sets or removes event handles form the specified instance.
	 *
	 * @param {bool} s True/false state if to add or remove event handlers.
	 */
	setEventHandlers : function(inst, s) {
		var doc = inst.getDoc(), ie, ot, i, f = s ? tinyMCE.addEvent : tinyMCE.removeEvent;

		ie = ['keypress', 'keyup', 'keydown', 'click', 'mouseup', 'mousedown', 'controlselect', 'dblclick'];
		ot = ['keypress', 'keyup', 'keydown', 'click', 'mouseup', 'mousedown', 'focus', 'blur', 'dragdrop'];

		inst.switchSettings();

		if (tinyMCE.isIE) {
			for (i=0; i<ie.length; i++)
				f(doc, ie[i], TinyMCE_Engine.prototype._eventPatch);
		} else {
			for (i=0; i<ot.length; i++)
				f(doc, ot[i], tinyMCE.handleEvent);

			// Force designmode
			try {
				doc.designMode = "On";
			} catch (e) {
				// Ignore
			}
		}
	},

	/**
	 * Mouse move handler function, this will be executed each time
	 * the mouse is moved within a editor instance. This function stores away the current selection in MSIE
	 * this will then be used when a undo/redo level is added.
	 */
	onMouseMove : function() {
		var inst, lh;

		// Fix for IE7 bug where it's not restoring hover on anchors correctly
		if (tinyMCE.lastHover) {
			lh = tinyMCE.lastHover;

			// Call out on menus and refresh class on normal buttons
			if (lh.className.indexOf('mceMenu') != -1)
				tinyMCE._menuButtonEvent('out', lh);
			else
				lh.className = lh.className;

			tinyMCE.lastHover = null;
		}

		if (!tinyMCE.hasMouseMoved) {
			inst = tinyMCE.selectedInstance;

			// Workaround for bug #1437457 (Odd MSIE bug)
			if (inst.isFocused) {
				inst.undoBookmark = inst.selection.getBookmark();
				tinyMCE.hasMouseMoved = true;
			}
		}

	//	tinyMCE.cancelEvent(inst.getWin().event);
	//	return false;
	},

	/**
	 * Cancels the specified event, this will disable the event from be passed to other listeners in event chain.
	 *
	 * @param {DOMEvent} e Event to cancel.
	 * @return Returns false.
	 * @type bool
	 */
	cancelEvent : function(e) {
		if (!e)
			return false;

		if (tinyMCE.isIE) {
			e.returnValue = false;
			e.cancelBubble = true;
		} else {
			e.preventDefault();
			e.stopPropagation && e.stopPropagation();
		}

		return false;
	},

	/**
	 * Adds a event handler function to the specified object.
	 *
	 * @param {HTMLElement} o Object to add event handler to.
	 * @param {string} n Event name to listen to for example "click".
	 * @param {function} h Function handler to execute when event occurs.
	 */
	addEvent : function(o, n, h) {
		// Add cleanup for all non unload events
		if (n != 'unload') {
			function clean() {
				var ex;

				try {
					tinyMCE.removeEvent(o, n, h);
					tinyMCE.removeEvent(window, 'unload', clean);
					o = n = h = null;
				} catch (ex) {
					// IE may produce access denied exception on unload
				}
			}

			// Add memory cleaner
			tinyMCE.addEvent(window, 'unload', clean);
		}

		if (o.attachEvent)
			o.attachEvent("on" + n, h);
		else
			o.addEventListener(n, h, false);
	},

	/**
	 * Removes a event handler function from the specified object.
	 *
	 * @param {HTMLElement} o Object to remove event handler from.
	 * @param {string} n Event name to stop listening for. Example "click".
	 * @param {function} h Function handler to detach from the event.
	 */
	removeEvent : function(o, n, h) {
		if (o.detachEvent)
			o.detachEvent("on" + n, h);
		else
			o.removeEventListener(n, h, false);
	},

	/**
	 * Adds accessibility keydown handler to the specified select element.
	 *
	 * @param {DOMEvent} e Event that gets passed when the element is focused.
	 * @param {HTMLElement} s Select element that the keydown handler gets added to.
	 * @param {DOMWindow} w DOM window reference to add.
	 */
	addSelectAccessibility : function(e, s, w) {
		// Add event handlers 
		if (!s._isAccessible) {
			s.onkeydown = tinyMCE.accessibleEventHandler;
			s.onblur = tinyMCE.accessibleEventHandler;
			s._isAccessible = true;
			s._win = w;
		}

		return false;
	},

	/**
	 * Accessibility handler that gets executed when the user hits a key in a select element.
	 * This handler trams the enter/return or space key and then executes the onchange event handler.
	 *
	 * @param {DOMEvent} e DOM event object instance.
	 */
	accessibleEventHandler : function(e) {
		var elm, win = this._win;

		e = tinyMCE.isIE ? win.event : e;
		elm = tinyMCE.isIE ? e.srcElement : e.target;

		// Unpiggyback onchange on blur
		if (e.type == "blur") {
			if (elm.oldonchange) {
				elm.onchange = elm.oldonchange;
				elm.oldonchange = null;
			}

			return true;
		}

		// Piggyback onchange
		if (elm.nodeName == "SELECT" && !elm.oldonchange) {
			elm.oldonchange = elm.onchange;
			elm.onchange = null;
		}

		// Execute onchange and remove piggyback
		if (e.keyCode == 13 || e.keyCode == 32) {
			elm.onchange = elm.oldonchange;
			elm.onchange();
			elm.oldonchange = null;

			tinyMCE.cancelEvent(e);
			return false;
		}

		return true;
	},

	/**
	 * Resets the iframe width and height to it's old values before a drag/drop operation occured.
	 * This function is used in a workaround for a MSIE bug where drag/drop fails in iframes with width/height in %. 
	 */ 
	_resetIframeHeight : function() {
		var ife;

		if (tinyMCE.isRealIE) {
			ife = tinyMCE.selectedInstance.iframeElement;

	/*		if (ife._oldWidth) {
				ife.style.width = ife._oldWidth;
				ife.width = ife._oldWidth;
			}*/

			if (ife._oldHeight) {
				ife.style.height = ife._oldHeight;
				ife.height = ife._oldHeight;
			}
		}
	}

	/**#@-*/
});