function CEPMOZ()
{
    this.contentWindow = document.getElementById("codeArea").contentWindow;
    this.parentWindow = this.contentWindow.parent;
    this.codeAreaDoc = this.contentWindow.document;
    this.codeAreaBody = this.contentWindow.document.body;


    var _this = this;
    this.setKeyHandlers = function() {
        // keypress, to cancel keys
        _this.codeAreaDoc.addEventListener('keypress',  // keypress is the only one can cancel
            function(e) 
            {
                return _this.onkeypressCodeAreaHandler(e);
            },
            true
        );
        
        // keydown, to fire shortcut keys.
        _this.codeAreaDoc.addEventListener('keydown',  
            function(e) 
            {
                return _this.onkeydownCodeAreaHandler(e);
            },
            false
        );
        
        _this.codeAreaDoc.addEventListener('click',
			function (e)
			{
				return _this.onmouseleftclickCodeAreaHandler(e);
			},
			false
		);
		
		};
		
    //setTimeout(setKeyHandlers, 900);

     
    this.hasSyntaxHighlighting = false;
    this.line = -1;
    this.column = -1;
    this.lineColumnDirty = true;
}

/**
*
* Events
*
*/


// -- onmouseleftclickCodeAreaHandler 
//
// handles the left click in the code area window of the Code Editor.  Which we
// need to hook into so that we can move the cursor down because in FF clicking
// below the last line of text does not move the cursor down.
CEPMOZ.prototype.onmouseleftclickCodeAreaHandler = function (evt)
{
	// if the evt occurs below the last line of text, then we need to move
	// the cursor down.	
	if (evt.button == 0)
	{
		var codeAsString = this.getFromCodeArea();
		
		var numLines = 1;
		var pos = -1;
		
		while (1)
		{
			if ( (pos = codeAsString.indexOf("\r\n", pos+1)) == -1 )
				break;
			numLines++;
		}
		
		//alert("clientY: " + evt.clientY + ", lines: " + numLines + ", target: " + evt.target + ", tagName: " + evt.target.tagName);
		
		if (numLines > 1 && evt.target.tagName == "HTML")
			this.moveToLine(numLines);
		
		/*
		if (evt.clientY > (8 + (numLines*16)))
		{
			// move cursor
			this.moveToLine(numLines);
			//alert(evt.clientY + " " + numLines);
		}*/
	}
}


// -- onkeypressCodeAreaHandler
//
// handles onkeypress event in the CodeEditor.  This is where you have to cancel
// the keystrokes in Mozilla.  you can't cancel from keydown.
CEPMOZ.prototype.onkeypressCodeAreaHandler = function (evt)
{
	// first check for control-c, and let it through.
    if (pageMode != "edit")
    {
		if (evt.ctrlKey && ((evt.charCode == 67) || evt.charCode == 99))
			return true;
			
		if (evt.keyCode != 18) { // let control go through.
			evt.preventDefault();
			//e.stopPropagation();
			return false;
		}
    }
        
	if ( ! ((evt.keyCode > 33 && evt.keyCode < 38) || // Arrow keys
	         evt.keyCode == 17 || // Alt
	         evt.keyCode == 18 || // Ctrl
	        (evt.ctrlKey && (evt.charCode == 67)) || // Ctrl-C (CAPS)
	        (evt.ctrlKey && (evt.charCode == 99)) // Ctrl-c	        
	         ))
		CodeEditorCommon.applicationState.isCodeDirty = true;
	    
    if (CodeEditorCommon.applicationState.keyPressCancelNextEvent)
    {
        evt.preventDefault();
        evt.stopPropagation();
        CodeEditorCommon.applicationState.keyPressCancelNextEvent = false;
        return false;
    }
}


// -- onkeydownCodeAreaHandler
//
// handles the keydown event, this is where we have to send the keyboard shortcuts 
// off from because in keypress (above) when a normal character key is pressed the
// keyCode is not returned.
CEPMOZ.prototype.onkeydownCodeAreaHandler = function (evt)
{
    //document.getElementById("feedbackArea").innerHTML += "onkeydownCodeAreaHandler: entered";
    //
    // Processes keybindings.
    //
    if (pageMode == "edit" && evt.altKey &&
        CodeEditorCommon.shortcutKeyBindings && 
        CodeEditorCommon.shortcutKeyBindings["keyCode" + evt.keyCode])
    {
		var _this = this;
        var funcName = CodeEditorCommon.shortcutKeyBindings["keyCode" + evt.keyCode];
        setTimeout( function () { _this.parentWindow[funcName](); }, 1);
        CodeEditorCommon.applicationState.keyPressCancelNextEvent = true;
        return false;
    }
}



CEPMOZ.prototype.appendMethod = function(methodName)
{
    var methodHTML = "Widget." + methodName + " = function()<br>\n" +
                     "{<br>\n" +
                     "// type your code here<br>\n" +
                     "}<br>\n";

    this.codeAreaBody.innerHTML += methodHTML;
    window.frames["codeArea"].focus();
    
    // refresh methods so we can zoom to it
    this.refreshMethodList();
    
    // zoom to the new method.
    for (var i = 0; i < CodeEditorCommon.widgetMethods.length; i++)
    {
        if (CodeEditorCommon.widgetMethods[i][1] == "Widget." + methodName)
        {
            this.moveToMethod(i);
            break;
        }   
    }
}


CEPMOZ.prototype.refreshMethodList = function ()
{
    var methodNavSel = document.getElementById("methodNavSelect");
    
    if (CodeEditorCommon.currentView != "javascript")
    {
		methodNavSel.innerHTML = "";    
        return;
    }
    
    var htmlLines = this.codeAreaDoc.body.innerHTML.replace(/&nbsp;/g, " ").split("<br>");
    CodeEditorCommon.widgetMethods = [];
    
    methodNavSel.innerHTML = "";

    for (var i = 0; i < htmlLines.length; i++)
    {
        var htmlLine = CodeEditorUtils.trim(htmlLines[i]);
        var resultAPI = htmlLine.match(/^(widget\.[a-zA-Z0-9]+)\s+=\s+function\s*/i); // non-general method detection
        var resultGEN = htmlLine.match(/function\s+([^(]+)\s*\(/i); // general method detection
        var resultLIT = htmlLine.match(/([^:]+)\s*:\s*function/i);
                
		var result = null;
		

		if (resultAPI != null && resultAPI.length > 0)
		{
			result = resultAPI;
		}
		
		if (resultGEN != null && resultGEN.length > 0)
		{
			result = resultGEN;
		}
		
		if (resultLIT != null && resultLIT.length > 0)
		{
			result = resultLIT;
		} 		

        if (result != null && result.length >= 1)
        {
            // widgetMethods: lineNum, "widget.name", complete line.
            CodeEditorCommon.widgetMethods.push( [ (i+1), result[1], result[0] ] );

            var opt = this.codeAreaDoc.createElement("option");
            opt.innerHTML = result[1];
            opt.value = (i+1);

            methodNavSel.appendChild(opt);
        }
    }
    
    CodeEditorCommon.applicationState.methodListGenerated = true;
}


CEPMOZ.prototype.moveToMethod = function(selectedIndex)
{
    var ln = CodeEditorCommon.widgetMethods[selectedIndex][0];
    
    this.moveToLine(ln);
    
    /*var currentLine = 1;
    var sel = this.contentWindow.getSelection();
    var range = this.codeAreaDoc.createRange();

    if (ln == 1)
    {
        range.setEnd(this.codeAreaBody.childNodes[0], 0);
        range.setStart(this.codeAreaBody.childNodes[0], 0);
        sel.removeAllRanges();
        sel.addRange(range);
        return;
    }
   
    for (var i = 0; i < this.codeAreaBody.childNodes.length; i++)
    {
        if (currentLine == ln)
        {
            sel.removeAllRanges();
            
            if (this.codeAreaBody.childNodes[i].nodeName == "BR")
            {
                // two BR's in a row.  then the container element is the HTML, not BODY,
                // and offset is number of nodes, not characters.
                range.setEnd(this.codeAreaBody, i);
                range.setStart(this.codeAreaBody, i);
            }
            else
            {
                // previous node is a text node
                range.setEnd(this.codeAreaBody.childNodes[i], 0);
                range.setStart(this.codeAreaBody.childNodes[i], 0);
            }
            
            sel.addRange(range);

            this.codeAreaDoc.execCommand("inserthtml", false, "-");
            this.codeAreaDoc.execCommand("undo", false, null);
                    
            break;
        }
        
        if (this.codeAreaBody.childNodes[i].nodeType == 1 &&
            this.codeAreaBody.childNodes[i].nodeName == "BR")
        {
            currentLine++;
        }   
    }*/
    return;
}


CEPMOZ.prototype.moveToLine = function (lineNumber)
{
	var currentLine = 1;
    var sel = this.contentWindow.getSelection();
    var range = this.codeAreaDoc.createRange();
    	
    if (lineNumber == 1)
    {
        range.setEnd(this.codeAreaBody.childNodes[0], 0);
        range.setStart(this.codeAreaBody.childNodes[0], 0);
        sel.removeAllRanges();
        sel.addRange(range);
        return;
    }
   
    for (var i = 0; i < this.codeAreaBody.childNodes.length; i++)
    {
        if (currentLine == lineNumber)
        {
            sel.removeAllRanges();
            
            if (this.codeAreaBody.childNodes[i].nodeName == "BR")
            {
                // two BR's in a row.  then the container element is the HTML, not BODY,
                // and offset is number of nodes, not characters.
                range.setEnd(this.codeAreaBody, i);
                range.setStart(this.codeAreaBody, i);
            }
            else
            {
                // previous node is a text node
                range.setEnd(this.codeAreaBody.childNodes[i], 0);
                range.setStart(this.codeAreaBody.childNodes[i], 0);
            }
            
            sel.addRange(range);

            this.codeAreaDoc.execCommand("inserthtml", false, "-");
            this.codeAreaDoc.execCommand("undo", false, null);
                    
            break;
        }
        
        if (this.codeAreaBody.childNodes[i].nodeType == 1 &&
            this.codeAreaBody.childNodes[i].nodeName == "BR")
        {
            currentLine++;
        }   
    }
    return;


}


CEPMOZ.prototype.calculateLineColumn = function ()
{
    try
    {
        var sel = this.contentWindow.getSelection();
        var range = sel.getRangeAt(0);
        var line = 1;
        var column = 1;
        
        selContainer = range.startContainer;
        
        // actually calculate the column number here to be more efficient.
        if (selContainer.nodeName != "BODY")
            column = sel.focusOffset+1;
        
        // If we're on an empty line, it's a <BR> node, which means FF classes our 
        // startContainer as the HTML tag, where as TEXT nodes count the BODY 
        // element as their parent (as you'd expect).  This means we have to use a 
        // slightly different way of counting line numbers.
        if (selContainer.nodeName == "BODY")
        {
            var untilNode = range.startOffset;        
        
            for (var i = 0; i < untilNode; i++)
            {
                if (selContainer.childNodes[i].nodeName == "BR")
                    line++;
            }
        }
        else
        {
            for(var i = 0; i < this.codeAreaBody.childNodes.length; i++)
            {
                if (this.codeAreaBody.childNodes[i].nodeType == 1 && this.codeAreaBody.childNodes[i].nodeName == "BR")
                    line++;
                    
                if (this.codeAreaBody.childNodes[i] == selContainer)
                    break;
            }
        }

        if (column < 1)
            column = "-";
        if (line < 1)
            line = "-";

        if (column == this.column && line == this.line)
        {
            this.lineColumnDirty = false;
        }
        else
        {            
            this.line = line;
            this.column = column;
            this.lineColumnDirty = true;
        }
    }
    catch (E)
    {
        this.column = "-";
        this.line = "-";
    }    
}


CEPMOZ.prototype.getFromCodeArea = function () 
{
    var codeStr = codeArea.document.body.innerHTML.replace(/\r\n/g, "");

    // first replace any end-div followed by br. (with just one newline!)
    codeStr = codeStr.replace(/<\/div><br>/ig, "\r\n");

    codeStr = codeStr.replace(/<br><\/div>/ig, "\r\n");
    codeStr = codeStr.replace(/<\/div>/ig, "\r\n");
    codeStr = codeStr.replace(/<br>/ig, "\r\n");
    
    var codeLines = codeStr.split("\r\n");

    for (i = 0; i < codeLines.length; i++)
    {
        codeLines[i] = codeLines[i].replace(/(<([^>]+)>)/ig,"");
    }
    codeLines.pop();
    
    var codeAsText = codeLines.join("\r\n");
    
    codeAsText = CodeEditorUtils.decodeHTMLEnts(codeAsText);    

    return codeAsText;
}



// -- loadIntoCodeArea
//
// This is what loads code into the text box, making it editable.  Factored out of
// the loadingcode event handler because it's also used when inserting code 
// into the text box direct from memory.
CEPMOZ.prototype.loadIntoCodeArea = function (text)
{
    var codeDoc = document.getElementById("codeArea").contentWindow.document;
    var innerHTML = "";
    
    codeDoc.body.innerHTML = "";
    codeDoc.body.style.fontFamily = "monospace";
    //codeDoc.body.style.overflowX = "hidden";

    
    if (pageMode != "edit")
    {
        codeDoc.body.style.color = "#666666";
    }
    
    text = CodeEditorUtils.encodeHTMLEnts(text);
    lines = text.split("\r\n");

    for (i = 0; i < lines.length; i++)
    {
        // not sure why this was in here.  seems to work fine without.
        //if (lines[i].search(/^[ ]*$/) > -1)
        //{
        //    lines[i] = "<br>";
        //} 
        //codeDoc.writeln(lines[i] + "<br>\n");
        
		innerHTML += lines[i] + "<br>\n";
    }
    // this gets around the "half newline" issue which is caused by a trailing 
    // \n character on the end of the string.
    if (innerHTML[innerHTML.length-1] == '\n') 
		innerHTML = innerHTML.slice( 0, innerHTML.length-1 );
      
    //alert(innerHTML);
    //codeDoc.execCommand("inserthtml", false, innerHTML);
    codeDoc.body.innerHTML = codeDoc.createTextNode(innerHTML).textContent;
    //codeDoc.body.appendChild(codeDoc.createTextNode(innerHTML));
    
    // this is for Mozilla.  Moz/FF don't like designMode being set to on when 
    // it's already one.  And if you do it after the doc.open() blah blah then
    // it gives exception regarding security problem with the URI.
    //if (codeDoc.designMode == "off" || codeDoc.designMode == "Inherit")
    //    codeDoc.designMode = "on";
    /*
    codeDoc.clear();
    codeDoc.open();
    codeDoc.writeln("<body style='font-family: courier;'>");
    */

    //codeDoc.close();   */
    
   
    //window.frames["codeArea"].focus();
    
    if (pageMode == "edit")
    {
		try
		{
			//codeDoc.execCommand("inserthtml", false, "-");
			//codeDoc.execCommand("undo", false, null);
		}
		catch (ex)
		{
			//alert(ex);
		}
	}

}
