// Chris Pyper 2006
// Core editor functions

// We are using IE if the document.all property exists
var ie = document.all;

// Turn of debugging window
var debugOn = false;

// Flag to track status of editor
var editorOn = false;

// Refernce to debugger window
var debugWindow = true;

// Some global vars
var isDrag = false;
var x,y,z = false;
var resizeMode = false;
var resizeWidth, resizeHeight, resizeType, resizeElementUpSkip = false;

// DOM reference to object currently being moved
var docObj = false;

// DOM reference of tabs for resize box
var docObjTab = false;

// DOM refernce to DIV that serves ar editor
var editor;

// Some vars for Mozilla specific functionality
var iframe = false;

// References of various DOM object, some functionality require swapping of DOM object so
// these vars hold the refernces to the original and cloned DOM nodes
var docSourceOrig  = false;
var docSourceClone = false;

// Refernece to resizing table
var editBoxRef = false;

// Reference to content of resize table
var resizeTableContentRef = false;

// Variable to hold highest known z-index
var maxZIndex = 2000;
var startZIndex = maxZIndex;
var maxArtZIndex = 0;

// Hold range and selection of highlighted object
var cachedRange		= null;
var cachedSelection = null;

// Hold text of highligted object
var cachedInnerText = null;

// Cached command array used to cache commands for comparison on each change, this way we minimize 
// signaling to outside editor only when there is change
var cachedCommandArray = null;

// Flag to hold whether or not document has been edited
var documentChanged = false;

// Grid layout element id
var GRIDLAYOUT_ELEMENTID = "layoutGrid";

// How long to delay before showing the grid on a mousedown
var GRIDLAYOUT_DELAY = 100;  // ms

// set current grid for art|text
var CURR_GRID_TYPE = "text";// text | art



// Switch design mode on the referenced DOM node
function designModeOnEditable(docSource)
{
	debug("designModeOnEditable(): Selected = " + docSource.tagName, 'info');

	// Turn off design mode to switch off and save any node being edited
	designModeOffEditable();

	// Gecko based browsers let you define a height for an element that is less then the actual displayed height
	// when you fetch this height in the switch to editing mode you get the height that is less then the content.
	// Since this height is the one used for the iframe it gives the appearance of cropping the content.  This issue
	// only effects height, as width is constrained and height is used for overflow.  The fix is to grab the display
	// height in gecko and set the actual height to that.  Added to work with IE as well to clean up any overly long
	// text areas.
	if(docSource.tagName != 'IMG')
	{
		docSource.style.height = 'auto';
		var tempHeight = docSource.offsetHeight + EDITMODEOFFSET;
		setStyle(docSource, 'height', tempHeight, true);
	}

	// Initalize global DOM references
	docSourceOrig  = docSource;
	docSourceClone = false;
	editBoxRef 	   = false;
	iframe         = false;
		
	// Make sure we can edit this field, check if class is correct
	if(docSource.className.search(/editable/gi) < 0)
	{
		debug('designModeOnEditable(): Element does not have editable style, cannot edit', 'warn'); 
		return false;
	}
	
	// Clone node
	var clone = docSource.cloneNode(true);
	setStyle(clone, 'left', 0, true);
	setStyle(clone, 'top', 0, true);
	setStyle(clone, 'position', 'relative');

	// With Mozilla things are a little diffrent.  There is no contentEditable property.  There is a concept known
	// as design mode where only an entire page, frame, or iframe can have it's content edited.  Once it is turned
	// on only content can be read/written to the frame and only some scattered DOM functionality is supported.
	// Javascript will not function properly with in the frame, and the frame itself will not support events, so
	// in order to mimic the functionality found in IE a new iframe is spawned and the content of the node editable
	// node it written, this iframe is then given the same size and moved to the location of the editable node, the
	// node is then made invisible so the iframe give the appearance of taking over the node

	// If using mozilla we have to edit text diffrently, as explained above
	if(!ie && docSourceOrig.tagName != 'IMG')
	{
		// Clear out the iframe
		iframe = false;
	
		// Build the new iframe and add to the document, set it invisilbe and turn on editing
		// A new iframe must be generated each time, building one into the html document and then
		// manipulating it casues issues with referencing for some reason
		
		var width  = getStyle(docSource, 'width', true);
		var height = getStyle(docSource, 'height', true);
		iframe              = document.createElement("iframe");
		iframe.id           = 'iframeEditor';
		iframe.scrolling    = 'no';
		iframe.marginheight = '0';
		iframe.marginwidth  = '0';
		setStyle(iframe, 'width', width, true);
		setStyle(iframe, 'height', height, true);
		setStyle(iframe, 'border', 0, true);
		// Highlight frame
		addClass(iframe, 'editBorder');

		var resizeBox = buildResizeBox(iframe);
		debug('designModeOnEditable(): Using iframe in resize box', 'info');
	}
	// Images can be manipluated the same way in all browsers
	else
	{
		var resizeBox = buildResizeBox(clone);
		debug('designModeOnEditable(): Using cloned element in resize box', 'info');	
	}

	// Position the resize box
	setStyle(resizeBox, 'position', 'absolute');

	// Maintain the zIndex
	setStyle(resizeBox, 'zIndex', getStyle(docSource, 'zIndex'));

	// Left, minus offset to prevent nudge of content
	var leftPos = getStyle(docSource, 'left', true);
	var leftOffset = 0;
	if(iframe)
		leftOffset = RESIZETABLEIFRAMEOFFSET;
	else if(ie)
		leftOffset = RESIZETABLEOFFSETIE;
	else
		leftOffset = RESIZETABLEOFFSETMOZ;
	leftPos = leftPos - leftOffset;
	debug('designModeOnEditable(): leftPos: ' + leftPos, 'info');
	setStyle(resizeBox, 'left', leftPos, true); 
	
	// Top, minus offset to prevent nudge of content
	var topPos = getStyle(docSource, 'top', true);
	var topOffset = 0;
	if(iframe)
		topOffset = RESIZETABLEIFRAMEOFFSET;
	else if(ie)
		topOffset = RESIZETABLEOFFSETIE;
	else
		topOffset = RESIZETABLEOFFSETMOZ;
	topPos = topPos - topOffset;
	debug('designModeOnEditable(): topPos: ' + topPos, 'info');
	setStyle(resizeBox, 'top', topPos, true);

	setStyle(resizeBox, 'visibility', 'hidden');
	resizeBox.id = 'nodeEditEditable';

	// The rezise box is positioned and ready, now add content
	if(!ie && docSourceOrig.tagName != 'IMG')
	{
		// Add to docuemnt and get reference back, the one that is 'returned' by the DOM functions
		// is buggy, I found it is best to use getElementById to go out and get it
		document.getElementById(MIDDLEEDITORID).appendChild(resizeBox);
		iframe = document.getElementById('iframeEditor');
		iframe.contentWindow.document.designMode = "on";

		// ***IMPORTANT*** This must be done AFTER the iframe is rendered or else it won't work.
		// The iframe was rendered when the is was apppened to the document above.
		// Turn on design mode and add a html document stub
		iframe.contentWindow.document.open('text/html; charset="UTF-8"');
		html = "<html><head></head><body class='editor'></body></html>";
		iframe.contentWindow.document.write(html);
		iframe.contentWindow.document.close();

		// Add style sheets to retain look
		for ( i = document.styleSheets.length - 1; i>=0; i-- )
		{   
			var	newLink = iframe.contentDocument.createElement("LINK");
			newLink.rel  = "stylesheet";
			newLink.type = "text/css";
			newLink.href = document.styleSheets[i].href;
			var iframeHead = iframe.contentDocument.getElementsByTagName('head')[0];
			if(iframeHead)
				iframeHead.appendChild(newLink);
		}   

		// The iframe with it's designMode set to on will support limited DOM functionality, here I use to add the conetent
		docSourceClone = iframe.contentWindow.document.body.appendChild(clone);

		// Adding selection caching for context sensitivity of highlighting and typing
		iframe.contentWindow.document.addEventListener('mouseup', cacheSelection, true);
		iframe.contentWindow.document.addEventListener('keypress', cacheSelection, true);
		iframe.contentWindow.document.addEventListener('click', checkClick, true);

		iframe.contentWindow.focus();

		// Here I turn on editing
		debug('designModeOnEditable(): Design Mode On', 'info');
		
		// Wait till the last step to change visibility, this makes for a smoother transition
		setStyle(docSourceOrig, 'visibility', 'hidden');
	}
	else
	{
		// Swap node, use getElementById() to fetch reference and get around appendChild() not correctly
		// returning reference to appended node
		docSourceClone = clone;
		docSourceOrig.parentNode.replaceChild(resizeBox, docSourceOrig);

		// Add highligthing
		addClass(docSourceClone, 'editBorder');
	
		// We have to make sure we unregister this or content editable will not function
		document.onmousemove = null;

		// Imagees can be manipulated the same way in all browsers
		if(docSourceOrig.tagName != 'IMG')
		{
			// This is the magic line which makes content editable for a element
//docSourceOrig.contentEditable = true;
//docSourceClone.unselectable = "off"
			docSourceClone.contentEditable = true;
			//document.getElementById("innerEditor").focus();

			// Layer in a event to trap any selected content for context sensitivity of highlighting and typing
			docSourceClone.onmouseup  = cacheSelection;
			docSourceClone.onkeypress = cacheSelection;
			docSourceClone.onclick	  =	checkClick;

			debug('designModeOnEditable(): Content Editable True', 'info');

		}
	}

	// Start off initial caching of selection for future comparison ofr context sensitivity
	if(docSourceOrig.tagName != 'IMG')
		cacheSelection();

	// Grab a reference to the resize table and make it visible
	editBoxRef = document.getElementById("nodeEditEditable");
	setStyle(editBoxRef, 'visibility', 'visible');

	// Inform editor wrapper in case for context sensitivity
	var callBacks = fetchInterface();
	if(docSourceOrig.tagName == 'IMG')
		callBacks.triggerCallBack('imageElementSelected');
	else
		callBacks.triggerCallBack('textElementSelected');

	return true;
}

// Turn off design mode on the node that is currently on
function designModeOffEditable(docSource)
{
	if(!docSource)
		debug("designModeOffEditable()", 'info');
	else
		debug("designModeOffEditable(): Selected = " + docSource.tagName, 'info');

	// You can't turn off something that is not on
	if(!editBoxRef)
		return false;

	// If the user clicks on something that is already on this means they are moving the cursor not turning the
	// design mode off for the element
	if(docSource == docSourceClone)
		return false;

	// Clean up spell check if it is on
	removeSpellCheckHighlighting();
 	document.getElementById('spellCheck').innerHTML = '';

	// If it's anything but mozilla and a text based element
	if(ie || docSourceOrig.tagName == 'IMG')
	{
		// Turn off content editing for the node
		if(ie && docSourceOrig.tagName != 'IMG')
		{
			// Unregister the event registered to capture any highlighting
			docSourceClone.onmouseup  = null;
			docSourceClone.onkeypress = null;
			docSourceClone.onclick	  = null;

			// Do a little housekeeping just in case
			cachedRange = null;
		
			// Turn off the content Editable feature
			docSourceClone.contentEditable = false;
			debug("designModeOffEditable() Content Editable false", 'info');
		}

		// Remove border highlighting
		removeClass(docSourceClone, 'editBorder');

		// Looks like javascript has  trouble fetching position in IE, gotta do it the old fashioned way instead.
		docSourceClone.style.position = docSourceOrig.style.position;

		// Swap out the newly edited node with the original
		editBoxRef.parentNode.replaceChild(docSourceClone, editBoxRef);

		// Fetch the offset
		var offSet = 0;
		if(ie)
			offSet = RESIZETABLEOFFSETIE; 
		else
			offSet = RESIZETABLEOFFSETMOZ;

		// The repositioning has to come after the replaceChild call above so it looks smooth in IE
		setStyle(docSourceClone, 'left', getStyle(editBoxRef, 'left', true) + offSet, true);
		setStyle(docSourceClone, 'top', getStyle(editBoxRef, 'top', true) + offSet, true);
	}
	else
	{
		// *** WARNING *** turning off designMode throw error if the editor is wrapped
		// in a iframe, we do not need to turn it off anyways becuase we destroy the iframe
	
		// Hide frame
		if(iframe)
		{
			// Shut off design mode, this doesn't actually affect anything becuase of the flaky support
			// but good practice for future compatibility
			//iframe.contentWindow.document.designMode = "off";
			setStyle(iframe, 'visibility', 'hidden');
			setStyle(iframe, 'width', 0, true);
			setStyle(iframe, 'height', 0, true);
			removeClass(iframe, 'editBorder');
		}

		// Replace edited node with edited content from frame, make frame visible
		if(docSourceOrig)
		{
			if(iframe && docSourceClone)
			{
				//Reset to original positioning
				setStyle(docSourceClone, 'left', getStyle(editBoxRef, 'left', true) + RESIZETABLEIFRAMEOFFSET, true);
				setStyle(docSourceClone, 'top',  getStyle(editBoxRef, 'top', true) + RESIZETABLEIFRAMEOFFSET, true); 
				// This line won't work in IE, will never get executed in IE, but will never work nevertheless
				setStyle(docSourceClone, 'position', getStyle(docSourceOrig, 'position'));

				// Replace the original with the edited clone
				docSourceOrig.parentNode.replaceChild(docSourceClone, docSourceOrig);
			}
			setStyle(docSourceOrig, 'visibility', 'visible');
		}
		
		editBoxRef.parentNode.removeChild(editBoxRef);
	}

	// Inform editor wrapper in case for context sensitivity
	var callBacks = fetchInterface();
    if(docSourceOrig.tagName == 'IMG')
        callBacks.triggerCallBack('imageElementUnSelected');
    else
        callBacks.triggerCallBack('textElementUnSelected');		
		
	// Clear global node references 
	docSourceOrig  = false;
	docSourceClone = false;
	editBoxRef     = false;
	iframe         = false;

	// Clear selection cache
    cachedSelection = null;
    cachedRange     = null;
    cachedInnerText = null;

	return true;
}

// Turn on design mode for draggable elements, similar to editable elements but toned down
function designModeOnDraggable(docSource)
{
	debug("designModeOnDraggable()", 'info');

	// Initalize global DOM references
    docSourceOrig  = docSource;
    docSourceClone = false;

	// Clone node
    var clone = docSource.cloneNode(true);
	docSourceClone = clone;
    setStyle(clone, 'left', 0, true);
    setStyle(clone, 'top', 0, true);
    setStyle(clone, 'position', 'relative');

	var highlightBox = buildHighlightBox(clone);
	highlightBox.id = 'nodeEditDraggable';

	setStyle(highlightBox, 'position', 'absolute');
	setStyle(highlightBox, 'left', getStyle(docSource, 'left', true), true);
	setStyle(highlightBox, 'top', getStyle(docSource, 'top', true), true);
	// Maintain the zIndex
    setStyle(highlightBox, 'zIndex', getStyle(docSource, 'zIndex'));

	docSource.parentNode.replaceChild(highlightBox, docSource);
	editBoxRef = document.getElementById("nodeEditDraggable");

	return true;
}

// Turn off design mode for draggable element
function designModeOffDraggable()
{
	debug("designModeOffDraggable()", 'info');

	editBoxRef.parentNode.replaceChild(docSourceOrig, editBoxRef);

	// Clear global node references
    docSourceOrig  = false;
    docSourceClone = false;
    editBoxRef     = false;

	return true;
}

// Turn on desgin mode for selected object depending on type
function designModeOn(docSource)
{
	debug("designModeOn(): Selected = " + docSource.tagName, 'info');

	stopDragCopy();

	if(docSource.className.search(/editable/gi) >= 0)
		return designModeOnEditable(docSource);
	else if(docSource.className.search(/draggable/gi) >= 0)
		return designModeOnDraggable(docSource);

	debug("designModeOn(): Cannot find matching element class", 'error');
	return false;
}

// Turn off design mode for a particular object depending on type
function designModeOff(docSource)
{
    debug("designModeOff()", 'info');

	// Check for type using orig doc ref, pass along clicked on node for comparison check in off functions
    if(docSourceOrig && docSourceOrig.className.search(/editable/gi) >= 0)
        var res = designModeOffEditable(docSource);
    else if(docSourceOrig && docSourceOrig.className.search(/draggable/gi) >= 0)
        var res = designModeOffDraggable(docSource);

	if(res)
	{
		startDragCopy();
		return res;
	}

    debug("designModeOff(): Cannot find matching element class", 'error');
    return false;
}

// Function to content to mouse position
function movemouse(e)
{
	// Flag to tell function to stop moving content
	if(!isDrag)
		return false;
	
	// Grab event based on browser
	var eventObject = getEventObject(e);

	// If not resize mode calulate new mouse position
	if(!resizeMode && docObj)
	{
		// Calculate new mouse position

		if(docObj.style.position == 'absolute')
		{


              var newX = tx + eventObject.clientX - x;
              var newY = ty + eventObject.clientY - y;

/*
			if (!ie)
			{
              var newX = tx + eventObject.clientX - x;
              var newY = ty + eventObject.clientY - y;
			}
			else if (eventObject.clientX || eventObject.clientY)
			{
				var newX = eventObject.clientX + document.body.scrollLeft;
				var newY = eventObject.clientY + document.body.scrollTop;
//				var newX = eventObject.clientX;
//				var newY = eventObject.clientY;
			}
			else if(eventObject.pageX && eventObject.pageY)
			{
				var newX = tx + eventObject.pageX - x;
				var newY = ty + eventObject.pageY - y;
			}
*/
		}
		else
		{
			var newX = tx + eventObject.clientX - x;
			var newY = ty + eventObject.clientY - y;
		}



	
		setStyle(docObj, 'left', newX);
		setStyle(docObj, 'top', newY);
	}
	// How to handle calculations for resize table
	else	
	{
		// Reset flags
		var vertical   = false;
		var horizontal = false;
		var lockRight  = false;
		var lockBottom = false;

		showLayoutGrid();

		// Define how each tab should react
		switch(docObjTab.className)
		{
			case "northWest":
				vertical   = true;
				horizontal = true;
				lockRight  = true;
				lockBottom = true;
			break;

			case "north":
				vertical   = true;
				lockBottom = true;
			break;

			case "northEast":
				vertical   = true;
				horizontal = true;
				lockBottom = true
			break;

			case "east":
				horizontal = true;
			break;

			case "southEast":
				vertical   = true;
				horizontal = true;
			break;

			case "south":
				vertical = true;
			break;

			case "southWest":
				vertical   = true;
				horizontal = true;
				lockRight  = true
			break;

			case "west":
				horizontal = true;
				lockRight  = true;
			break;
		}

		// Depending on which way user is pulling tab other side of table will stay static
		// so it is calculated opposite
		if(!lockRight)
			var width = resizeWidth + (eventObject.clientX - x);
		else
			var width = resizeWidth - (eventObject.clientX - x);

		debug('movemouse(): width =' + width, 'info');

		// Depending on which way user is pulling tab other side of table will stay static
		// so it is calclated opposite
		if(!lockBottom)
			var height = resizeHeight + (eventObject.clientY - y);
		else
			var height = resizeHeight - (eventObject.clientY - y);

		debug('movemouse(): height =' + height, 'info');

		// Make the resize
		if(width > 0 && horizontal)
		{
			// Calculate the diffrence in case there is a need to resposition
			var diffrence = getStyle(docSourceClone, 'width', true) - width;

			setStyle(docSourceClone, 'width', width, true);

			// Reposition if we are positioning the right side statically
			if(lockRight)
			{
				var resizeLeft = getStyle(editBoxRef, 'left', true) + diffrence;
				setStyle(editBoxRef, 'left',  resizeLeft, true);
			}
		}
		
		// Make the resize
		if(height > 0 && vertical)
		{
			// Calculate the diffrence in case there is a need to resposition
			var diffrence = getStyle(docSourceClone, 'height', true) - height;

			setStyle(docSourceClone, 'height', height, true);

			// Reposition if we are positioning the bottom statically
			if(lockBottom)
			{
				var resizeTop = getStyle(editBoxRef, 'top', true) + diffrence;
				setStyle(editBoxRef, 'top', resizeTop, true);
			}
		}
	}

	// ***WARNING EXTREMELY IMPORTANT*** - It seems you have to return false at the end of this function or else it
	// will not function correctly.  I beleive that if a function is registered to the onmousemove event if you return
	// true the function does continue to work correctly in IE.
	return false;
}

// Function to select and initiate appropriate action based on clicked on content clicked on
function selectElement(e, edit, resize) 
{
	// This flag tracks whether the docuemnt has changed, it is best to set it here becasue nearly all
	// editing actions are routed through this function
	documentChanged = true;

	var eventObject = getEventObject(e);
	var topelement  = getTopElement();
	docSource       = getDOMfromEventObject(eventObject);

	// A little hook to bring up debugger, have to piggy back it on to another event handler or else
	// there is issues with editor events being intercepted
	if(eventObject.altKey && eventObject.ctrlKey)
	{
		if(!debugOn)
		{
			debugOn = true;
			debug('START DEBUGGER', 'info');
		}
		else
		{
			debugOn = false;
			debug('STOP DEBUGGER', 'info');
		}
		return true;
	}

	// Print a recursive trace of all events, for debugging purposes, debugger must be on
	if(debugOn && eventObject.ctrlKey)
	{
		printAllEvents(document);	
		return true;
	}

	resizeMode = false;
	if(resize)
	{
		resizeMode = true;
		// Grab initial size for calculations on first select of resize tab
		if(!editBoxRef)
		{
			resizeWidth  = getStyle(docSourceOrig, 'width', true);
			resizeHeight = getStyle(docSourceOrig, 'height', true);
		}
		// Used clone for up to date size on reselect of resize tab
		else
		{
			resizeWidth  = getStyle(docSourceClone, 'width', true);
			resizeHeight = getStyle(docSourceClone, 'height', true);
		}

		if(!resizeWidth)
		{
			resizeWidth = docSourceClone.offsetWidth;
			setStyle(docSourceClone, 'width', resizeWidth, true);
		}
		if(!resizeHeight)
		{
            resizeHeight = docSourceClone.offsetHeight;
			setStyle(docSourceClone, 'height', resizeHeight, true);
		}

		debug('selectElement(): resizeWidth = ' + resizeWidth, 'info');
		debug('selectElement(): resizeHeight = ' + resizeHeight, 'info');
	}

	// Climb DOM tree till the node has a class of the specified type
	while (docSource.tagName != topelement && (
		   docSource.className.search(/draggable/gi) < 0 && 
		   docSource.className.search(/editable/gi) < 0 &&
		   docSource.className.search(/ESWuserForm/gi) < 0))
	{
		if(ie)
			docSource = docSource.parentElement;
		else
			docSource = docSource.parentNode;
	}

	if(edit == 'on' && docSource.className.search(/ESWuserForm/gi) >= 0)
	{
		var callBacks = fetchInterface();
		callBacks.triggerCallBack('editForm', docSource);
		return false;
	}

	// Turn on editing for selected node, triggered on double click
	if(edit == 'on' && (docSource.className.search(/editable/gi) >= 0 || docSource.className.search(/draggable/gi) >= 0))
		return designModeOn(docSource);
	else if(edit == 'off')
		return designModeOff(docSource);

	// If it's a node with a class of draggagle and not double clicked then take appropriate action
	if ((docSource.className.search(/draggable/gi) >= 0 || resizeMode) && edit != 'on') 
	{
		isDrag = true;
		
		// Save referece of node to move
		docObj = docSource;

		// Is this a plugin?
		if ( docSource.className.search(/editorPlugin/gi) >= 0 )
		{
			// hide the plugin IFRAME to prevent it from grabbing the events
			docSource.lastChild.style.visibility = "hidden";  
			docSource.style.borderStyle = "dashed";
		}

		// Calculate and initiate movement
		tx = getStyle(docObj, 'left', true) + 0;
		ty = getStyle(docObj, 'top', true) + 0;
		// z = getStyle(docObj, 'z-index', true) + 0;
		x  = eventObject.clientX;
		y  = eventObject.clientY;
		document.onmousemove = movemouse;
		document.onclick = stopDraggable;

		//if ( isNaN(z) )
		//	z = 0;
		debug('selectElement(): tx=' + tx + ' ty=' + ty + ' x=' + x + ' y=' + y, 'info');

		if(resizeMode)
			undoRedoSnapShot(getDocObjFromClone(), 'resize');
		else
		{
			undoRedoSnapShot(docObj, 'move');
			// Raise element to top after taking snapshot, gives it a nice feel, and will give undo/redo
			// functionality for bringing elements forward
			raiseToTop(docSource);

			// setup type of current element for the grid
			if ( docSource.className.indexOf('art') > -1 )
			  CURR_GRID_TYPE = "art";
			else
			  CURR_GRID_TYPE = "text";


			// leave a bit of a delay to ensure that we don't show the grid
			// unneccessarily
			setTimeout(showLayoutGrid,GRIDLAYOUT_DELAY);

		}


		

		return true;
	}
	
	// ***EXTREMELY IMPORTANT*** - You must always return something at the end on funcitons registered to events
	// or else you cannot properly unregister them, so if you layering events you will really mess up your results.
	// Currently this seems to be a IE issue mainly
	return false;
}


// displays a "layout grid" over the page to assist the user when aligning content blocks
function showLayoutGrid()
{
	if (!isDrag) {
		hideLayoutGrid();
		return false;
	}
	
	if ( CURR_GRID_TYPE == "art" )
	{
	  debug('showing grid: ' + (maxArtZIndex - 1), 'info');
	  document.getElementById(GRIDLAYOUT_ELEMENTID).style.zIndex = (maxArtZIndex - 1);
	}
	else
	{
	  debug('showing grid: ' + (maxZIndex - 1), 'info');
	  document.getElementById(GRIDLAYOUT_ELEMENTID).style.zIndex = (maxZIndex - 1);
	}
	document.getElementById(GRIDLAYOUT_ELEMENTID).style.width = getWindowWidth() + "px";
	if ( !ie )
  	  document.getElementById(GRIDLAYOUT_ELEMENTID).style.height = getWindowHeight() + this.scrollMaxY + "px";
	else
	{
		var grid_h = 0;
	  if ( getWindowHeight() > document.body.scrollHeight )
		grid_h = getWindowHeight() - 20;
	  else
		grid_h = document.body.scrollHeight;

  	  document.getElementById(GRIDLAYOUT_ELEMENTID).style.height = grid_h + "px";
	}
	document.getElementById(GRIDLAYOUT_ELEMENTID).style.display = "block";
}

function hideLayoutGrid()
{
	document.getElementById(GRIDLAYOUT_ELEMENTID).style.display = "none";
}

// Fetch event object from triggered event
function getEventObject(e)
{
	//debug("getEventObject()", 'info');

	if (ie)
        return event;
    else
        return e;
}

// Get DOM reference of object associated with event
function getDOMfromEventObject(eventObject)
{
	debug("getDOMfromEventObject()", 'info');
	
	if (ie)
        return eventObject.srcElement;
    else
        return eventObject.target;
}

// If we need to take a undo snapshot of a element that currently has some styles applied to it for editing purposes
// then make sure to remove some of those styles before the snapshot is taken
function getDocObjFromClone()
{
	debug('getDocObjFromClone()', 'info');

	if(!docSourceClone)
		return false;

    var snapShot = docSourceClone.cloneNode(true);

    snapShot.style.position = docSourceOrig.style.position;
    removeClass(snapShot, 'editBorder');

    // Fetch the offset
    var offSet = 0;
    if(ie)
    	offSet = RESIZETABLEOFFSETIE;
    else
        offSet = RESIZETABLEOFFSETMOZ;

    setStyle(snapShot, 'left', getStyle(editBoxRef, 'left', true) + offSet, true);
    setStyle(snapShot, 'top', getStyle(editBoxRef, 'top', true) + offSet, true);

	return snapShot;
}

// Fetch what is considered the top dom element depeding on the browser
function getTopElement()
{
	debug("getTopElement()", 'info');

    if (ie)
        return "BODY";
    else
        return "HTML";
}

// Move all text divs (non art) to foreground
function moveTextToForeground(docRef)
{

	debug("moveTextToForeground", 'info');


	if (docRef.nodeType == 1 /*Node.ELEMENT_NODE*/)  // Check if n is an Element
	{
		var tempZIndex = parseInt(getStyle(docRef, 'zIndex', true));
		if (!tempZIndex) 
		  tempZIndex = 0;
		if (docRef.className && docRef.className.indexOf('art') == -1 && docRef.className.indexOf(PAGECONTENT_CLASS) > -1 && tempZIndex < startZIndex)
		{
//			tempZIndex = maxZIndex - tempZIndex; 
			if (tempZIndex > 0 )
			  setStyle(docRef, 'zIndex', (maxZIndex + tempZIndex) );
			else if (tempZIndex == 0 )
			  setStyle(docRef, 'zIndex', startZIndex );
		}
	}
		
	// Now get all children of docref	
	var children = docRef.childNodes;

	// Loop through the children and recurse the children
	for(var i = 0; i < children.length; i++)
		moveTextToForeground(children[i]);

//	debug("moveTextToForeground():  = " + maxZIndex, 'info');

}

// This function will recurse all the elements of a DOM node and find the highest z-index
// it is used to find the highest index so we can propogate clicked on elements to the top of the document
function getMaxZIndex(docRef, type) 
{
	if (!type)
		type = "text"

	debug("getMaxZindex()"+type, 'info');

	var tempZIndex = 0;
	var tempArtZIndex = 0;

	if (docRef.nodeType == 1 /*Node.ELEMENT_NODE*/)  // Check if n is an Element
	{
		tempZIndex = parseInt(getStyle(docRef, 'zIndex', true));
		if (docRef.className && docRef.className.indexOf('art') > -1)
		{
			if(tempZIndex && tempZIndex > maxArtZIndex)
				maxArtZIndex = tempZIndex;
		}
		else
		{
			if(tempZIndex && tempZIndex > maxZIndex)
				maxZIndex = tempZIndex;
		}
	}
		
	// Now get all children of docref	
	var children = docRef.childNodes;

	// Loop through the children and recurse the children
	for(var i = 0; i < children.length - 1; i++)
		getMaxZIndex(children[i]);

	debug("getMaxZIndex(): maxZIndex = " + maxZIndex, 'info');
}

// This fucntion will propgate to be the closest element to the user in the viewport
function raiseToTop(docRef)
{
	debug("raiseToTop()", 'info');

	if ( docRef.className.indexOf('art') > -1 )
	{
		++maxArtZIndex;
		setStyle(docRef, 'zIndex', maxArtZIndex);
		debug("raiseToTop(): maxArtZIndex = " + maxArtZIndex, 'info');
	}
	else
	{
		++maxZIndex;
		setStyle(docRef, 'zIndex', maxZIndex);
		debug("raiseToTop(): maxZIndex = " + maxZIndex, 'info');
	}
}

// When a event is registered a reference to the element clicked on is passed to the registered function as a parameter.
// However, only one paramter can be passed, wrapping the function in another allows us to pass a flag to it.
function editElementOn(e)
{
	debug("editElementOn()", 'info');
	return selectElement(e, 'on');
}

// When a event is registered a reference to the element clicked on is passed to the registered function as a parameter.
// However, only one paramter can be passed, wrapping the function in another allows us to pass a flag to it.
function editElementOff(e)
{
	debug("editElementOff()", 'info');
	return selectElement(e, 'off');
}

// Turn off dragging of a element, set a flag to signal element moving function
function stopDraggable(e)
{
	debug("stopDraggable()", 'info');
	document.onmousemove = null;
	isDrag = false;

	/* attempt to restore the element's original z-index
	 * debug("restoring  z-index: " + z, "info");
	 * if ( !isNaN(z) )
	 *	setStyle(docObj, 'zIndex', z);
	 */

	// Is this a plugin?
	if ( docObj && docObj.className.search(/editorPlugin/gi) >= 0 )
	{
		// hide the plugin IFRAME to prevent it from grabbing the events
		docObj.lastChild.style.visibility = "visible";  
		docObj.style.borderStyle = "solid";
	}

	hideLayoutGrid();
	

	return true;
}

// ***EXTREMELY IMPORTANT*** - One of the most painful parts of the development of this application
// is the proper layering of events, browsers behave very diffrently then they should so it is very important
// that you be as careful as possible when changing anything to do with events.

// Register and layer functions for movable content
function startDragCopy()
{
	debug("startDragCopy()", 'info');

	document.onmousemove = null;
	editor.onmousemove   = null; // Original above
	editor.onmousedown   = selectElement;
	editor.onmouseup   	 = stopDraggable; // Original above
	editor.ondblclick    = editElementOn;
	document.onclick     = null;

}

// Unregister functions for movable content
function stopDragCopy()
{
	debug("stopDragCopy()", 'info');
	document.onclick   = editElementOff;
	editor.onmousedown = null;
	editor.onmouseup   = null;
	editor.onmousemove = null;
	editor.ondblclick  = null;
	
	stopDraggable();
}

// Start editor
function startEditor()
{
	debug("startEditor()", 'info');
	
	if(editorOn)
		return false;
	
	editor = document.getElementById(INNEREDITORID);
	moveTextToForeground(editor);
	getMaxZIndex(editor);
	registryStart();
	startDragCopy();
	editorOn = true;

	return true;
}

// Stop Editor
function stopEditor()
{
	debug("stopEditor()", 'info');

	if(!editorOn)
		return false;
	
	if(docSourceClone)
		designModeOff();
	stopDragCopy();
	document.onclick = null;
	
	editorOn = false;

	return true;
}

// Set the language code for the editor
function setLanguage(lang)
{
	debug("setLanguage("+lang+")", 'info');
	if ( lang && lang != "" )
	{
		EDITORUSERLANGUAGE = lang;
	}
}

// Event handler for when resize table tab is pressed
function resizeElementDown(e)
{
	debug("resizeElementDown()", 'info');

	// Get DOM reference of tab selected 
	docObjTab = getDOMfromEventObject(getEventObject(e));

	// Hide frame, show element to be resized, highlight
	if(iframe)
	{
		addClass(resizeTableContentRef, 'editBorder');
		setStyle(iframe, 'visibility', 'hidden');
		setStyle(iframe, 'width', 0, true);
		setStyle(iframe, 'height', 0, true);
		resizeTableContentRef.appendChild(docSourceClone);
	}

	// Layer in events to handle when user releases tab
	document.onclick    = null;
	document.onmouseup  = resizeElementUp;
	resizeElementUpSkip = true;
	selectElement(e, false, true);

	return true;
}

// Event handler for when a resize table tab is released
function resizeElementUp(e)
{
	debug("resizeElementUp()", 'info');

	// show frame again with new resized element placed in it
	if(iframe)
	{
		removeClass(resizeTableContentRef, 'editBorder');
		iframe.contentWindow.document.body.appendChild(docSourceClone);
		setStyle(iframe, 'width', getStyle(docSourceClone, 'width'));
		setStyle(iframe, 'height', getStyle(docSourceClone, 'height'));
		setStyle(iframe, 'visibility',  'visible');
	}
	
	if(resizeElementUpSkip)
		resizeElementUpSkip = false;
	else
		document.onclick = editElementOff;

	// Clean up events to smooth out content editable
	document.onmousemove = null;

	resizeMode = false;

	return true;
}

// Wrap execCommand
function execCommand(command, iface, variant)
{
	debug("execCommand(): commmand = " + command, 'info');

	if(!editorOn)
		return false;

	var undoRedo = fetchUndoRedo();
	var finalSnapShotTaken = false; // Flag to hold if a final undo snapshot was taken to capture last minute change

	// If user activated undo and we are at top of undo stack(i.e. no redos performed) and a object is being edited
	// grab a last minute snaphost of this object to capture any final changes and set flag to indicate this 
	if(command == 'undo' && undoRedo.atTopOfUndoStack() && docSourceClone) 
	{
		finalSnapShotTaken = true;
		undoRedoSnapShot(getDocObjFromClone(), 'edit');
	}
	// Otherwise alwasy take snapshot if the user has activated a command other then undo and redo, such as bold, etc...
	else if(command != 'undo' && command != 'redo' && docSourceClone)
		undoRedoSnapShot(getDocObjFromClone(), 'edit');
	
	// If the user wants to undo or redo something lets grab it from the undo/redo stack and swap it in
	if(command == 'undo' || command == 'redo')
	{
		// The undo/redo stack contains snapshots of changed dom nodes and a string describing the change.
		// Both are conainted in simple container object
		var container;
		
		if(command == 'undo')
		{
			// Grab next snapshot, return false if none left
			container = undoRedo.undo();
			// Drop down one more level to skip over the last minute snapshot if one was taken
			if(finalSnapShotTaken)
				container = undoRedo.undo();
		}
		else if(command == 'redo')
		{
			// Move forward one snapshot, return false if none left
			container = undoRedo.redo();
		}

		// If we have a valid container remove the cloned node from it and swap it with existing
		if(container)
		{
			var turnBackOn = false;	// Flag to hold if it is currently being edited
			// Check if it is editable right now
			if(docSourceClone && getId(docSourceClone) == getId(container.clone))
			{
				turnBackOn = true; // Set flag if it is
				designModeOff();   // Turn it off
			}
			
			// Find it and swap it
			if(container.type == 'edit' || container.type == 'resize' || container.type == 'move')
			{
				var undoDocRef = getElementByESWId(getId(container.clone));
				if(undoDocRef)
				{
					// If it's a edit of some sort we have to swap with exisiting in case we make 
					// various back and forth passes through the undo/redo stack, we will retian
					// modern changes.
					var currentDoc = undoDocRef.cloneNode(true);
					undoDocRef.parentNode.replaceChild(container.clone, undoDocRef);
					container.clone = currentDoc;
					undoRedo.replaceSnapShot(container);
				}
			}
			// Use existing functions to perform undo/redo of entire elements, swithc depending on direction
			else if((container.type == 'delete' && command == 'undo') ||
					(container.type == 'add' && command == 'redo'))
				addElement(container.clone, false, false, true);
			else if((container.type == 'add' && command == 'undo') ||
					(container.type == 'delete' && command == 'redo'))
				deleteElement(container.clone, true);
			
			// Turn it back on if it was on 
			if(turnBackOn && getElementByESWId(getId(container.clone)))
				designModeOn(getElementByESWId(getId(container.clone.id)));
		}
		return true;
	}

	if(ie || (!ie && command == 'receiveLink'))
	{
		// Highlight cached selection
		highlightSelection();
	}

    // Exec commands works smoother in Mozilla then IE, all we do is directoy exec
    if(iframe)
	{
		if(command == 'cut' || command == 'copy' || command == 'paste')
			debug('execCommand(): Command not supported = ' + command, 'error');
		else
		{
			iframe.contentWindow.focus();
			iframe.contentWindow.document.execCommand(command, iface, variant);
		}
	}
    // Otherwise we have to exec the command and reselect to maintain highlighting
    else
		document.execCommand(command, iface, variant);

	// Test if command state has changed by exec'd command and send appropriate signal if it has
	checkCommandState();

	return true;
}

// Handle user clicking on editable area
function checkClick()
{
	var clickedTag = getParentElement();
//clickedTag.contentEditable = true;
	// If the user clicked on a spell check span
	if(clickedTag && clickedTag.className && clickedTag.className.search(/spellCheck/gi) >= 0)
	{
		showSuggestions(clickedTag);
		return true;
	}
	
	// If the user clicked on 
	if(clickedTag && /^a$/i.test(clickedTag.tagName))
	{
		createLink();
		return true;
	}

	// If user clicked elsewhere hide spell check box
	document.getElementById('spellCheck').innerHTML = '';
}

// Return flag indicating whether or not document has been edited
function hasDocumentChanged()
{
	return documentChanged;
}

// Reset the "document changed" flag... (ie. after a Save operation)
function resetDocumentChangedFlag()
{
	documentChanged = false;
}


