/**
*
* Copyright (c) 2000 - <><> Working Software
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation 
* files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, 
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished 
* to do so, subject to the following conditions:
*
*    - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*    - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
*      MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
*      FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
*      WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/
var press_count = 0;
var emod = "";
var receiver = "";
var rr_widgets = new Array();
var did_a_scroll = false;
var did_a_wheel = false;
var did_a_mouse_down = false;

function setReceiver(e)
{
    did_a_scroll = false;

    if(receiver)
        realEscape(e);
    //FIND THE DIV
    cur = getMouseTarget(e);

    while(cur.nodeName.toLowerCase()!="div")
        cur = cur.parentNode;

    id = cur.getAttribute("id");

    if(id)
    {
        if(receiver.id!=id)
        {
            detachEvent(document.getElementById(id),"click",setReceiver,false);
            receiver = new Receiver(id);
            receiver.init();
            document.onmouseup = realEscape;
            document.onmousedown = function(e){did_mouse_down = true;}
            document.oncontextmenu = function(e){e.preventDefault(); e.stopPropagation(); return false;}
            document.onscroll = function(e){ if(!did_a_wheel&&receiver&&did_mouse_down){ did_a_scroll = true; did_mouse_down = false;}else did_a_wheel = false;}
            if (window.addEventListener)
                /** DOMMouseScroll is for mozilla. */
                window.addEventListener('DOMMouseScroll', wheel, false);
            save_id = id.replace("_receiver","");
            receiver.setSaveObject(document.getElementById(save_id));
            document.getElementById(save_id).value = document.getElementById(id).innerHTML;
        }
    }
    
    e.stopPropagation();
}

function wheel(e)
{
    did_a_scroll = false;
    did_a_wheel = true;
}

function realEscape(e)
{
    if(receiver&&!did_a_scroll)
    {
        el = document.getElementById(receiver.id);
        receiver.esc();
        receiver.detach();
        attachEvent(el,"click",setReceiver,false);
        receiver = false;
        document.onscroll = null;
        window.removeEventListener('DOMMouseScroll',wheel,false);
    }
    
    did_a_scroll = false;
    did_mouse_down = false;
    e.stopPropagation();
}

function transform(object,content)
{
    div = document.createElement("div");
    hidden = document.createElement("input");
    hidden.setAttribute("type","hidden");
    hidden.setAttribute("name",object.getAttribute("name"));
    hidden.setAttribute("id",object.getAttribute("id"));
    hidden.setAttribute("value",object.value);
    div.setAttribute("id",object.getAttribute("name")+"_receiver");

    if(!content)
        div.innerHTML = object.value.replace("?","&#59087;");
    
    if(!div.innerHTML.replace(/(<([^>]+)>)/ig,"").replace(/^\s+/g,"").replace( /\s+$/g,""))
        div.setAttribute("class","emptyReceiver");

    next = object.nextSibling;
    parent = object.parentNode;
    parent.removeChild(object);
    parent.insertBefore(div,next);
    parent.insertBefore(hidden,next);
    attachEvent(div,"click",setReceiver,false);
    hidden.value = div.innerHTML;
}

function integrateRichieRich()
{
    last_id = "";

    for(i = 0;i<rr_widgets.length;i++)
    {
        transform(document.getElementById(rr_widgets[i])); 
        last_id = rr_widgets[i]+"_receiver";
    }

    if(document.getElementById(last_id))
    {
        detachEvent(document.getElementById(last_id),"click",setReceiver,false);
        receiver = new Receiver(last_id);
        receiver.init();
        document.onmouseup = realEscape;
        document.oncontextmenu = function(e){e.preventDefault(); e.stopPropagation(); return false;}
        if (window.addEventListener)
            window.addEventListener('DOMMouseScroll', wheel, false);
        save_id = last_id.replace("_receiver","");
        receiver.setSaveObject(document.getElementById(save_id));
    }
}

var Receiver = function Receiver(id)
{
    this.id = id;
    this.state = "";
    this.alt = false;
    this.ctrl = false;
    this.shift = false;
    this.history = new History();
    this.active_block = null;
    this.almost_stripped = null;
    this.background_backup = null;
    this.changed_bg = "";
    this.cursor_position = 0;
    this.trimmed = false;
    this.paste_input = null;
    this.key_input = null;
    this.waiting_for_heading = false;
    this.prevent_mouse_click = false;
    this.key_map = new KeyMap();
    this.word_delimeters = new Array(" ","+",",","\n",";","-","\t",".");
    this.sentence_delimeters = new Array(",","\n",";","-","\t",".");
    this.formatting = new Array("strong","u","em","a");
    this.empty = new Array("br");
    this.current_selection = "";
    this.save_object = "";
    this.context_menu = "";
}

Receiver.prototype =
{
    setSaveObject: function(object)
    {
        this.save_object = object;
        this.save_object.value = document.getElementById(this.id).innerHTML;
    },

    init: function()
    {
        el = document.getElementById(this.id);
        attachEvent(el,"click",this.mouseClick.bind(this),false);
        attachEvent(el,"mouseup",this.handleSelection.bind(this),false);
        attachEvent(el,"contextmenu",this.contextMenu.bind(this),false);
        document.onkeypress = this.stopDefault.bind(this);
        document.onselection = this.stripCursor.bind(this);
        document.onkeyup = this.keyCheck.bind(this);
        document.onkeydown = this.stopDefault.bind(this);
        //NOW FIND FIRST NON EMPTY TAG
        walker = document.createTreeWalker(document.getElementById(this.id),NodeFilter.SHOW_ELEMENT,null,false);
        var target = false;
        while(walker.nextNode()&&!target)
        {
            if(!this.isEmpty(walker.currentNode))
                target = walker.currentNode;
        }
        
        if(!target)
        {
            p = document.createElement("p");
            newtext = document.createTextNode(this.cursor())
            p.appendChild(newtext);
            el.appendChild(p);
            target = p;
        }

        // target is some DOM node on which you wish to fire an event.
        oEvent = document.createEvent( "MouseEvents" );
        oEvent.initMouseEvent(
          "click",    // the type of mouse event
          true,       // do you want the event to
                      // bubble up through the tree?  (sure)
          true,       // can the default action for this
                      // event, on this element, be cancelled? (yep)
          window,     // the 'AbstractView' for this event,
                      // which I took to mean the thing sourcing
                      // the mouse input.  Either way, this is
                      // the only value I passed that would work
          1,          // details -- for 'click' type events, this
                      // contains the number of clicks. (single click here)
          1,          // screenXArg - I just stuck 1 in cos I
                      // really didn't care
          1,          // screenYArg - ditto
          1,          // clientXArg - ditto
          1,          // clientYArg - ditto
          false,      // is ctrl key depressed?
          false,      // is alt key depressed?
          false,      // is shift key depressed?
          false,      // is meta key depressed?
          0,          // which button is involved?
                      // I believe that 0 = left, 1 = right,
                      // 2 = middle
          target      // the originator of the event
                      // if you wanted to simulate a child
                      // element firing the event you'd put
                      // its handle here, and call this method
                      // on the parent catcher.  In this case,
                      // they are one and the same.
        );
        target.dispatchEvent( oEvent );
        this.ensureContentIntegrity();
    },

    destroyContextMenu: function()
    {
        if(this.context_menu)
        {
            this.context_menu.destroy();
            this.context_menu = "";
        }
    },

    contextMenu: function(e)
    {
        if(!e)
            heading_mode = true;
        else
            heading_mode = false;

        if(e)
        {
            el = document.getElementById(this.id);

            if(el.getAttribute("class")!="receiverInUse")
                this.mouseClick(e);
  
            e.preventDefault();
        }
        
        else if(this.context_menu)
            e = this.context_menu.event;
        else
            e = "none";

        new_menu = new ContextMenu(e,this,heading_mode);

        if(heading_mode)
        {
            old_top  = this.context_menu.top;
            old_left = this.context_menu.left;
        }

        this.destroyContextMenu();
        this.context_menu = new_menu;

        if(heading_mode)
            new_menu.setPosition(old_top,old_left);
        
        else if(e != "none")
        {
            var top           = scrollTop();
            var left          = scrollLeft();
            var scroll_height = document.body.scrollHeight;
            var height        = clientHeight();
            var width         = clientWidth();
            var x             = getMouseX(e);
            var y             = getMouseY(e);
            var item_height   = parseInt(getElementStyle(new_menu.menu_ref.firstChild,"height","height"));
            var item_width    = parseInt(getElementStyle(new_menu.menu_ref.firstChild,"width","width"));
            var menu_height   = parseInt(getElementStyle(new_menu.menu_ref,"height","height"));

            /*if((height<scroll_height)&&!top)
                height = scroll_height;*/
            if((y+menu_height)>(top+height))
                var menu_top  = y-menu_height;
            else
                var menu_top  = y;
            if((x+item_width)>(left+width))
                var menu_left = x-item_width;
            else
                var menu_left = x;
            
            menu_top = parseInt(menu_top)-parseInt(getOffsetY(new_menu.menu_ref.offsetParent));
            menu_left = parseInt(menu_left)-parseInt(getOffsetX(new_menu.menu_ref.offsetParent));
            new_menu.setPosition(menu_top,menu_left);
        }

        if(!this.getSelectionLength())
            this.key_input.focus();
    },

    setKeyHolder: function()
    {
        if(!this.key_input)
        {
            var key_holder = document.createElement("div");
            key_holder.setAttribute("id","keyHolder");
            key_holder.style["position"] = "absolute";
            key_holder.style["visibility"] = "hidden";
            key_holder.style["top"] = getOffsetY(document.getElementById(this.id))+"px";
            key_holder.style["left"] = getOffsetX(document.getElementById(this.id))+"px";
            this.key_input = document.createElement("textarea");
            this.key_input.style["visibility"] = "hidden";
            this.key_input.style["display"] = "inline";
            this.key_input.style["width"] = "0";
            this.key_input.style["height"] = "0";
            key_holder.appendChild(this.key_input);
            //document.getElementById(this.id).insertBefore(key_holder,document.getElementById(this.id).childNodes[0]);
            document.body.appendChild(key_holder,document.body.childNodes[0]);
            this.key_input.focus();
        }
    },

    detach: function()
    {
        el = document.getElementById(this.id);
        detachEvent(el,"click",this.mouseClick.bind(this),false);
        detachEvent(el,"mouseup",this.handleSelection.bind(this),false);
        detachEvent(el,"contextmenu",this.contextMenu.bind(this),false);
        document.onkeypress = "";
        document.onselection = "";
        document.onclick = "";
        document.onkeyup = "";
        document.onkeydown = "";
    },
    
    setState: function(state)
    { 
        this.state = state;
    },
    
    isList: function(node)
    {
        var ret = false;
        
        if(node)
        {
            if((node.nodeName.toLowerCase() == "ul")||(node.nodeName.toLowerCase() == "ol"))
                ret = true;
        }

        return ret;
    },

    isHeading: function(node)
    {
        var ret = false;

        if(node)
        {
            if((node.nodeName.toLowerCase() == "h1")||
               (node.nodeName.toLowerCase() == "h2")||
               (node.nodeName.toLowerCase() == "h3")||
               (node.nodeName.toLowerCase() == "h4")||
               (node.nodeName.toLowerCase() == "h5")||
               (node.nodeName.toLowerCase() == "h6")||
               (node.nodeName.toLowerCase() == "h7")||
               (node.nodeName.toLowerCase() == "h8")||
               (node.nodeName.toLowerCase() == "h9"))
                ret = true;
        }

        return ret;
    },

    stopHref: function(event)
    {
        this.destroyContextMenu();
        event.preventDefault();
        event.stopPropagation();
        this.setActive(this.landingBlock(this.getMouseTarget(event),"stationary"));
        this.setCursor(this.activeContent().length);
        this.setKeyHolder();
        this.keyAction(0);
        return false;
    },

    getHref: function(event)
    {
        if(event)
            target = this.getMouseTarget(event);
        else
            target = this.context_menu.target;

        if(target.nodeName.toLowerCase()=="a")
        {
            href = target.getAttribute("href");
            href = prompt("Please enter the URL",href);
            target.setAttribute("href",href);
            this.prevent_mouse_click = true;
        }

        if(event)
            event.preventDefault();
        return false;
    },

    setFormatBold: function()
    {
        this.setFormat("bold");
    },

    setFormatUnderline: function()
    {
        this.setFormat("underline");
    },

    setFormatItalic: function()
    {
        this.setFormat("italic");
    },

    setFormatLink: function()
    {
        this.setFormat("link");
    },

    setFormat: function(format)
    {
        // Format Definition
        if (format == "bold")
            var format_node_name = "strong";
        else if (format == "underline")
            var format_node_name = "u";
        else if (format == "italic")
            var format_node_name = "em";
        else if (format == "link")
            var format_node_name = "a";
        else if (format == "delete")
            var format_node_name = "delete";

        //FIRST STORE THE ELEMENT FOR THE UNDO FUNCTION
        this.history.store(document.getElementById(this.id),this.active_block);
        //IF THE USER HAS USED THE MOUSE TO SELECT SOME TEXT
        if(this.current_selection)
        {
            var anchor = this.current_selection.anchorNode;
            var a_offset = this.current_selection.anchorOffset
            var focus = this.current_selection.focusNode;
            var f_offset = this.current_selection.focusOffset; 
            var top_level = document.getElementById(this.id);
            var direction = this.getDirection(anchor,focus,top_level);
            var found_left = false;
            var found_right = false;

            if(direction == "left_to_right")
            {
                left = anchor;
                right = focus;
            }

            else if(direction == "right_to_left")
            {
                left = focus;
                right = anchor;
            }
            
            else
                alert("Could not determine selection direction");

            if((left.nextSibling == right.nextSibling)&&(left.parentNode == right.parentNode))
                var frustrating = true;
            else
                var frustrating = false;
            // Accept matched format nodes and skip unmatched format nodes
            var myFormatFilter =    
            function(node)
            {
                var res = NodeFilter.FILTER_SKIP;  
                if (node == left) 
                    found_left = true;
                else if (node == right)
                    found_right = true;    

                if(found_left&&!found_right)
                {
                    if((node.nodeName.toLowerCase() == format_node_name)||(node.parentNode.nodeName.toLowerCase() == format_node_name))
                        res = NodeFilter.FILTER_ACCEPT;
                    else
                        res = NodeFilter.FILTER_SKIP;  
                }
    
                if(frustrating&&found_left&&!found_right)
                    found_right = true;

                return res;
            }    

            var walker = document.createTreeWalker(top_level, NodeFilter.SHOW_ALL, myFormatFilter, false);
            
            if(walker.nextNode())
            {
                this.setActive(this.landingBlock(this.removeFormatting(anchor,a_offset,focus,f_offset,format,top_level),"stationary"));
                //this.stripCursor();
                this.current_selection = "";
                this.setCursor(0);
            }

            else
            {
                this.setActive(this.landingBlock(this.applyFormatToSelection(anchor,a_offset,focus,f_offset,format,top_level),"stationary"));
                //this.stripCursor();
                this.current_selection = "";
                this.setCursor(0);
            }
        }
        
        else
            alert("Please select text to format");
    },
 
            
    /**
    * Find the direction of the current mouse selection by traversing the entire
    * editable area and determining if we get to the anchor or focus node
    * first
    */
    getDirection: function(anchor,focus,top_level)
    {
        var direction ="";
        var walker = document.createTreeWalker(top_level, NodeFilter.SHOW_ALL, null, false);
        
        while(walker.nextNode()&&!direction)
        {
            if(walker.currentNode == anchor)
                direction = "left_to_right";
            else if(walker.currentNode == focus)
                direction = "right_to_left";
        }

        return direction;
    },

    /**
    * @param top_level     - this is the node that defines our total editable area
    * @param anchor        - the node that selection starts on
    * @param anchor_offset - the offset inside the anchor node
    * @param focus         - the node that selection ends on
    * @param focus_offset  - the offset inside the focus node
    * @param format        - the format we are applying
    */
    applySingleLineSpace: function(anchor,a_offset,focus,f_offset,top_level)
    {
        // Get the direction of the selection
        var direction = this.getDirection(anchor,focus,top_level);
        if (direction == "left_to_right")
        {
            var left = anchor;
            var right = focus;
            var left_offset = a_offset;
            var right_offset = f_offset;
            left_blockNode = this.getBlockLevelNode(left);
            right_blockNode = this.getBlockLevelNode(right);
        }
    
        else if (direction == "right_to_left")
        {
            var left = focus;
            var right = anchor;
            var left_offset = f_offset;
            var right_offset = a_offset;
            left_blockNode = this.getBlockLevelNode(left);
            right_blockNode = this.getBlockLevelNode(right);
        }
    
        else
            alert("Unable to determine selection direction");
    
        var right_nextSib = right.nextSibling;
        var new_p = document.createElement("p");
        var current_node = left_blockNode;    
        var last_node;
    
        // Check for valid block level node to apply formatting
        if ((current_node) && (left_blockNode == right_blockNode) && (left_blockNode.nodeName.toLowerCase() == "p"))   
        {
            /* Don't need to do anything if selection is within the same block node?*/
            /*var top_level = left_blockNode.parentNode;
            var new_br = document.createElement("br");
            var new_txtNode = document.createTextNode(current_node.innerHTML);
            new_p.appendChild(new_txtNode);
            new_p.appendChild(new_br);
    
            if (current_node.nextSibling)
                top_level.insertBefore(new_p, current_node.nextSibling);
            else
                top_level.appendChild(new_p);
            top_level.removeChild(current_node);*/
        }
    
        else
        {   // Traverse through looking for P tags to format
            if (current_node)
            {
                orig_parent = current_node.parentNode;

                var endloop = false;
                while (current_node && !endloop)
                {
                    if (current_node == right_blockNode)
                        endloop = true;
                    if (current_node.nodeName.toLowerCase() == "p")
                    {                    
                        var new_br = document.createElement("br");
    
                        if (current_node.hasChildNodes())
                        {             
                            var to_append = new Array();           
                            for (i = 0; i < current_node.childNodes.length; i++)                  
                                to_append.push(current_node.childNodes[i]);  
                            for (i = 0; i < to_append.length; i++)
                                new_p.appendChild(to_append[i]);                     
                        }
                        
                        if (!endloop)
                            new_p.appendChild(new_br);
    
                        last_node = current_node;
                        current_node = current_node.nextSibling;
                    }
                    else
                        current_node = current_node.nextSibling;
                }           
                // Add new_p to DOM tree
                if (current_node)
                    current_node.parentNode.insertBefore(new_p, last_node);
                else
                   orig_parent.appendChild(new_p);            
            }
        }
        
        return new_p;
    },/*END applySingleLineSpace*/

    /**
    * @param top_level     - this is the node that defines our total editable area
    * @param anchor        - the node that selection starts on
    * @param anchor_offset - the offset inside the anchor node
    * @param focus         - the node that selection ends on
    * @param focus_offset  - the offset inside the focus node
    * @param format        - the format we are applying
    */
    applyListToSelection: function(anchor,a_offset,focus,f_offset,top_level)
    {
        // Get the direction of the selection
        var direction = this.getDirection(anchor,focus,top_level);
    
        if (direction == "left_to_right")
        {
            var left = anchor;
            var right = focus;
            var left_offset = a_offset;
            var right_offset = f_offset;
            left_blockNode = this.getBlockLevelNode(left);
            right_blockNode = this.getBlockLevelNode(right);
        }
    
        else if (direction == "right_to_left")
        {
            var left = focus;
            var right = anchor;
            var left_offset = f_offset;
            var right_offset = a_offset;
            left_blockNode = this.getBlockLevelNode(left);
            right_blockNode = this.getBlockLevelNode(right);        
        }
    
        else
            alert("Unable to determine selection direction");
        
        var new_ulist = document.createElement("ul");
        var current_tag = left_blockNode;
        var last_tag;
        var garbage = new Array();
        /* Check if there's a valid block level node to apply formatting*/
        if ((current_tag) && (left_blockNode == right_blockNode) && (left_blockNode.nodeName.toLowerCase() == "p"))
        {           
            var top_level = left_blockNode.parentNode;
            var new_list = document.createElement("li");
            new_list.innerHTML = current_tag.innerHTML;
            new_ulist.appendChild(new_list);        
                                  
            if (current_tag.nextSibling)
                top_level.insertBefore(new_ulist,current_tag.nextSibling);
            else
                top_level.appendChild(new_ulist);
            top_level.removeChild(current_tag);
        }    
    
        else
        {   // Traverse through left to right block nodes looking for P tags to format
            if (current_tag)
            {        
                var orig_parent = current_tag.parentNode;
                var endloop = false;    

                while (current_tag && !endloop)
                {                
                    if (current_tag == right_blockNode)
                        endloop = true;
                    if (current_tag.nodeName.toLowerCase() == "p")
                    {                    
                        garbage.push(current_tag);
                        var new_list = document.createElement("li");
                        new_list.innerHTML = current_tag.innerHTML;               
                        new_ulist.appendChild(new_list);
                    
                        last_tag = current_tag;
                        current_tag = current_tag.nextSibling;
                    }
                    else
                        current_tag = current_tag.nextSibling;
                }
                // Add list to DOM tree
                if (current_tag)
                    orig_parent.insertBefore(new_ulist,last_tag);    
                else    
                    orig_parent.appendChild(new_ulist);  
                // Remove formatted P nodes
                for (var i = 0; i < garbage.length; i++)
                {        
                    var parent = garbage[i].parentNode;
                    parent.removeChild(garbage[i]);        
                }    
            }          
        }          
                  
        return new_list;
    },/*END applyListToSelection*/
    
    getBlockLevelNode: function(node)
    {
        var current = node.parentNode;
        if (!current)
        {            
            return false;
        }
        while (current)
        {
            if ((current.nodeName.toLowerCase() == "p") ||
                (current.nodeName.toLowerCase() == "li") ||
                (this.isHeading(current)))
                return current;
            else
                current = current.parentNode;
        }
        return false;
    },/*END getBlockLevelNode*/

    removeFormatting: function(anchor,a_offset,focus,f_offset,format,top_level)
    {
        // Format Definition
        if (format == "bold")
            var format_node_name = "strong";
        else if (format == "underline")
            var format_node_name = "u";
        else if (format == "italic")
            var format_node_name = "em";
        else if (format == "link")
            var format_node_name = "a";
        else if (format == "delete")
            var format_node_name = "delete";

        // Get the direction of the selection
        var direction = this.getDirection(anchor,focus,top_level);
        if (direction == "left_to_right")
        {
            var left = anchor;
            var right = focus;
            var left_offset = a_offset;
            var right_offset = f_offset;        
        }
        else if (direction == "right_to_left")
        {
            var left = focus;
            var right = anchor;
            var left_offset = f_offset;
            var right_offset = a_offset;        
        }
        else
            alert("Unable to determine selection direction");
            
        var found_left = false;    
        var found_right = false;    
        if((left.nextSibling == right.nextSibling)&&(left.parentNode == right.parentNode))
            var frustrating = true;
        else
            var frustrating = false;

        var myFormatFilter =    
        function(node)
        {
            var res = NodeFilter.FILTER_SKIP;  
            if (node == left) 
                found_left = true;
            else if (node == right)
                found_right = true;    

            if(found_left&&!found_right)
            {
                if((node.nodeName.toLowerCase() == format_node_name)||(node.parentNode.nodeName.toLowerCase() == format_node_name))
                    res = NodeFilter.FILTER_ACCEPT;
                else
                    res = NodeFilter.FILTER_SKIP;  
            }

            if(frustrating&&found_left&&!found_right)
                found_right = true;

            return res;
        }    
            
        var walker = document.createTreeWalker(top_level, NodeFilter.SHOW_ALL, myFormatFilter, false);        
        var stack = new Array();
        // Walk through all the format nodes that needs to be removed
        while (walker.nextNode())
        {
            var current_node = walker.currentNode;
            var parent = current_node.parentNode;

            if((current_node.nodeType == 3)&&(current_node.parentNode.nodeName.toLowerCase() == format_node_name))
                current_node = current_node.parentNode;

            var next_sibling = current_node.nextSibling;
            var parent = current_node.parentNode;

            if (next_sibling)
            {   
                var node_length = current_node.childNodes.length;

                for (var i = 0; i < node_length; i++)            
                    parent.insertBefore(current_node.childNodes[0], next_sibling);
                
                ret = next_sibling;
            }

            else
            {
                var node_length = current_node.childNodes.length;
                ret = current_node.childNodes[0];

                for (var i = 0; i < current_node.childNodes.length; i++)
                    parent.appendChild(current_node.childNodes[0]);
            }
            
            parent.removeChild(current_node);
        }  
         
        var walker = document.createTreeWalker(top_level, NodeFilter.SHOW_TEXT, null, false);        
        var garbage = new Array();

        while(walker.nextNode())
        {
            cur = walker.currentNode;
            cont = true;
            
            while(cur.nextSibling&&cont)
            {
                if(cur.nextSibling.nodeType == 3)
                {
                    cur.nodeValue = cur.nodeValue + cur.nextSibling.nodeValue;
                    cur.nextSibling.parentNode.removeChild(cur.nextSibling);
                }
                
                else
                    cont = false;
            }
        }

        return ret;
    },/*END removeFormatting*/

    /**
    * @param anchor         - the node that selection starts on
    * @param anchor_offset  - the offset inside the anchor node
    * @param focus          - the node that selection ends on
    * @param focus_offset   - the offset inside the focus node
    * @param format         - the format we are applying
    * @param top_level      - this is the node that defines our total editable area
    */
    applyFormatToSelection: function(anchor,a_offset,focus,f_offset,format,top_level)
    {
        var ret = false;
        //IF WE ARE CREATING A LINK, GET THE URL FROM THE USER
        if(format == "link")
            href = prompt();
        else
            href = true;

        /*A BIT HACKY - CONTINUE BY DEFAULT, BUT IF THE USER CANCELLED A URL, THEN DON'T CONTINUE*/
        if(href)
        {
            // Initialisation/Declaration
            var left_text = "";
            var middle_text = "";
            var right_text = "";
            var start = anchor;
            var start_offset = a_offset;
            var end = focus;
            var end_offset = f_offset;
            var left_selected;
            var left_deselected;
            var right_selected; 
            var right_deselected;       
            
            // Format Definition
            if (format == "bold")
                var format_node_name = "strong";
            else if (format == "underline")
                var format_node_name = "u";
            else if (format == "italic")
                var format_node_name = "em";
            else if (format == "link")
                var format_node_name = "a";
            else if (format == "delete")
                var format_node_name = "delete";
            
            //Get the direction of the selection
            var direction = this.getDirection(anchor,focus,top_level);
        
            if (direction == "left_to_right")
            {
                var left = anchor;
                var right = focus;
                var left_offset = a_offset;
                var right_offset = f_offset;
            }    
        
            else if (direction == "right_to_left")
            {
                var left = focus;
                var right = anchor;
                var left_offset = f_offset;
                var right_offset = a_offset;
            }
            
            else
                alert("Unable to determine selection direction");
            
            // Make sure we have a text node only (left & right == textNode)
            while ( (left.nodeType != 3) && (left.childNodes.length) )
                var left = left.firstChild;
            while ( (right.nodeType != 3) && (right.childNodes.length) )
                var right = right.firstChild;
        
            var left_parent = left.parentNode; 
            var right_parent = right.parentNode; 
            //GET THE SELECTED/DESELECTED TEXT AND ALSO ASSIGN TO TRIMMED VARIABLES TO TEST FOR
            //EMPTY TEXT NODES - PRESERVE ORIGINALS THOUGH TO PRESERVE SPACES
            if ( (left.nodeType == 3) && (right.nodeType==3) )
            {
                left_selected = left.nodeValue.substring(left_offset,left.nodeValue.length);
                trimmed_left_selected = left_selected.replace( /^\s+/g,""); // strip leading whitespace
                trimmed_left_selected = trimmed_left_selected.replace( /\s+$/g,""); // strip trailing whitespace*/
                
                left_deselected = left.nodeValue.substring(0,left_offset);
                trimmed_left_deselected = left_deselected.replace( /^\s+/g,""); // strip leading whitespace
                trimmed_left_deselected = trimmed_left_deselected.replace( /\s+$/g,""); // strip trailing whitespace
                
                right_selected = right.nodeValue.substring(0,right_offset);
                trimmed_right_selected = right_selected.replace( /^\s+/g,"");   // strip leading whitespace
                trimmed_right_selected = trimmed_right_selected.replace( /\s+$/g,"");   // strip trailing whitespace
                
                right_deselected = right.nodeValue.substring(right_offset,right.nodeValue.length);
                trimmed_right_deselected = right_deselected.replace( /^\s+/g,""); // strip leading whitespace
                trimmed_right_deselected = trimmed_right_deselected.replace( /\s+$/g,""); // strip trailing whitespace
            }
        
            else
                document.write("Couldn't find suitable left and right text nodes");
                
                
            var cur = left;
            var stacker = new Array();
            // Traverse from tree nodes to find left and right and apply formatting    
            //IN THIS CASE WE JUST WANT TO ADD ALL SELECTED CHILD NODES TO A FORMAT NODE
            if (left_parent == right_parent)
            {
                var appended = false;
    
                if(left == right)
                {
                    var all_text = left.nodeValue;
    
                    if(a_offset<f_offset)
                    {
                        var selected_text = all_text.substring(a_offset,f_offset);
                        var left_text = all_text.substring(0,a_offset);
                        var right_text = all_text.substring(f_offset,all_text.length);
                    }
    
                    else
                    {
                        var selected_text = all_text.substring(f_offset,a_offset);
                        var left_text = all_text.substring(0,f_offset);
                        var right_text = all_text.substring(a_offset,all_text.length);
                    }
    
                    var next = left.nextSibling;
                    var left_node = document.createTextNode(left_text);
                    var middle_node = document.createTextNode(selected_text);
                    var right_node = document.createTextNode(right_text);
                    var format_node = document.createElement(format_node_name);

                    if(format == "link")
                    {
                        format_node.setAttribute("href",href);
                        //attachEvent(format_node,"click",this.getHref.bind(this),false);
                        attachEvent(format_node,"click",this.stopHref.bind(this),false);
                    }

                    format_node.appendChild(middle_node);
                    left_parent.replaceChild(left_node,left);
    
                    if(next)
                    {
                        left_parent.insertBefore(format_node,next);
                        left_parent.insertBefore(right_node,next);
                    }
                    
                    else
                    {
                        left_parent.appendChild(format_node);
                        left_parent.appendChild(right_node);
                    }
                    
                    left_landing = left_node;
                    right_landing = right_node;
                    ret = format_node;
                }
                
                else
                {
                    //FIRST GET SOME PARAMETERS
                    var left_unselected_text = document.createTextNode(left_deselected);
                    var left_next = left.nextSibling;
                    var left_selected_text = document.createTextNode(left_selected);
                    var format_node = document.createElement(format_node_name);  
                    var right_selected_text = document.createTextNode(right_selected);
                    var right_unselected_text = document.createTextNode(right_deselected);
                    //NOW APPEND ALL NECESSARY CHILDREN TO THE FORMAT NODE
                    format_node.appendChild(left_selected_text);

                    if(format == "link")
                    {
                        format_node.setAttribute("href",href);
                        //attachEvent(format_node,"click",this.getHref.bind(this),false);
                        attachEvent(format_node,"click",this.stopHref.bind(this),false);
                    }
        
                    while(left_next&&(left_next!=right))
                    {
                        format_node.appendChild(left_next);
                        left_next = left_next.nextSibling;
                    }
            
                    format_node.appendChild(right_selected_text);
                    //NOW APPEND THEM ALL TO THE PARENT
                    left_parent.replaceChild(left_unselected_text,left);
                    left_parent.replaceChild(right_unselected_text,right);
                    left_parent.insertBefore(format_node,right_unselected_text);
                    left_landing = left_unselected_text;
                    right_landing = right_unselected_text;
                    ret = format_node;
                }
            }
            //IN THIS CASE, WE WANT TO LOOP THROUGH ALL NODES AND APPLY FORMATTING
            else if(format != "link")
            {
                while (cur)
                {    
                    var cur_parent = cur.parentNode;
                    var cur_next = cur.nextSibling;
       
                    if (cur.hasChildNodes())
                    {        
                        //PUSH THE CHILD NODES IN REVERSE ORDER TO PREVENT RIGHT FROM BEING FOUND FIRST
                        for (var j = (cur.childNodes.length-1); j >= 0; j--)
                            stacker.push(cur.childNodes[j]);
                    }
        
                    //if (cur.childNodes[j] == left)
                    if (cur == left)
                    {
                        //var temp_textNode = cur.childNodes[j];
                        var temp_textNode = cur;
                        var new_text = document.createTextNode(left_deselected);
                        var left_next = left.nextSibling;
                        left_parent.replaceChild(new_text,temp_textNode);                    
                        var el = document.createElement(format_node_name);
                        new_text = document.createTextNode(left_selected);
                        el.appendChild(new_text);
                        left_landing = new_text;

                        if(left_next)
                            left_parent.insertBefore(el,left_next);
                        else
                            left_parent.appendChild(el);
                    }
        
                    //else if (cur.childNodes[j].nodeType == 3)
                    else if (cur.nodeType == 3)
                    {
                        var temp_textNode = cur;
                        var el = document.createElement(format_node_name);
                        var new_text = document.createTextNode(temp_textNode.nodeValue);
                        el.appendChild(new_text);
                        cur_parent.replaceChild(el,temp_textNode);
                    }
        
                    cur = stacker.pop();
        
                    if(!cur)
                    {
                        if(cur_next)
                            cur = cur_next;
        
                        else
                        {
                            while(!cur&&(cur!=right))
                            {
                                if(cur_parent)
                                {
                                    cur = cur_parent.nextSibling;
                                    cur_parent = cur_parent.parentNode;
                                }
                                
                                else
                                    cur = right;
                            }
                        }
                    }
        
                    if(cur == right)
                    {
                        var temp_textNode = cur;
                        var right_next = right.nextSibling;
                        var el = document.createElement(format_node_name);
                        new_text = document.createTextNode(right_selected);
                        el.appendChild(new_text);
                        right_parent.replaceChild(el,temp_textNode);                    
                        var new_text = document.createTextNode(right_deselected);
                        right_landing = new_text;
                        
                        if(right_next)
                            right_parent.insertBefore(new_text,right_next);
                        else
                            right_parent.appendChild(new_text);
    
                        cur = false;
                    }
                }

            }    

                
            else if(format=="link")
            {
                alert("You can't apply a link across multiple lines");
                ret = this.active_block;
            }

            if(!ret)
            {
                if(direction == "left_to_right")
                    ret = right_landing;
                else
                    ret = left_landing;
            }
                
        }

        else
            ret = this.active_block;

        return ret;
    },/*END applyFormatToSelection*/

    stopDefault: function(e)
    {
        emod = (e) ? (e.eventPhase) ? "W3C" : "NN4" : (window.event) ? "IE4+" : "unknown";

        if (emod == "NN4")
        {
            //document.captureEvents(Event.CLICK);
            //document.captureEvents(Event.KEYDOWN);
        }

        switch (emod)
        {
            case "IE4+":
                e = window.event;
                alt = (e.altKey) ? true : false;
                ctrl = (e.ctrlKey) ? true : false;
                code = e.keyCode;
            break;
            
            case "NN4":
                alt = ((e.modifiers & Event.ALT_MASK) == Event.ALT_MASK);
                ctrl = ((e.modifiers & Event.CTRL_MASK) == Event.CTRL_MASK);
                code = e.which;
            break;
            
            case "W3C":
                alt = (e.altKey) ? true : false;
                shift = (e.shiftKey) ? true : false;
                ctrl = (e.metaKey) ? true : false;

                if(!ctrl)
                    ctrl = (e.ctrlKey) ? true : false;

                code = e.which;
            break;
        }
        
        if(this.key_map.stopDefault(code,shift))
           ret = false;
           //alert("false by the map");
        else if((String.fromCharCode(code).toLowerCase()=="t")||(String.fromCharCode(code).toLowerCase()=="c"))
           ret = true;
           //alert("true by t or c");

        else if(String.fromCharCode(code).toLowerCase()!="v")
        {
            if(alt||ctrl)//||(this.key_map.isSpecialFunction(code,shift)))
                ret = false;
            else
                ret = true;
        }
        
        else if(ctrl)
        {
            if(this.key_input)
            {
                /*var input = document.createElement("textarea");
                input.style["visibility"] = "hidden";
                input.style["display"] = "inline";
                input.style["width"] = "0px";
                input.style["height"] = "0px";
                document.body.insertBefore(input,document.body.childNodes[0]);
                input.focus();*/
                this.key_input.focus();
            }

            ret = true;
        }
        
        else
            ret = true;

        return ret;
    },/*END stopDefault*/

    keyCheck: function(e)
    {
        emod = (e) ? (e.eventPhase) ? "W3C" : "NN4" : (window.event) ? "IE4+" : "unknown";

        if (emod == "NN4")
        {
            //document.captureEvents(Event.CLICK);
            //document.captureEvents(Event.KEYDOWN);
        }

        if(!this.key_input)
        {
            var key_holder = document.createElement("div");
            key_holder.setAttribute("id","keyHolder");
            key_holder.style["position"] = "absolute";
            key_holder.style["visibility"] = "hidden";
            key_holder.style["top"] = "0px";
            key_holder.style["left"] = "0px";
            this.key_input = document.createElement("textarea");
            //this.key_input.type = "text";
            this.key_input.style["visibility"] = "hidden";
            this.key_input.style["display"] = "inline";
            this.key_input.style["width"] = "0";
            this.key_input.style["height"] = "0";
            key_holder.appendChild(this.key_input);
            //document.getElementById(this.id).insertBefore(key_holder,document.getElementById(this.id).childNodes[0]);
            document.body.insertBefore(key_holder,document.body.childNodes[0]);
        }

        if(!this.current_selection)
            this.key_input.focus();

        switch (emod)
        {
            case "IE4+":
                e = window.event;
                this.alt = (e.altKey) ? true : false;
                this.ctrl = (e.ctrlKey) ? true : false;
                this.shift = (e.shiftKey) ? true : false;
        
                output_string = " alt: "+alt+"<br /> ctrl: "+ctrl+"<br /> shift: "+shift;
        
                if ((e.keyCode<16)||(e.keyCode>18))
                    this.keyAction(e.keyCode);
            break;
            
            case "NN4":
                this.alt = ((e.modifiers & Event.ALT_MASK) == Event.ALT_MASK);
                this.ctrl = ((e.modifiers & Event.CTRL_MASK) == Event.CTRL_MASK);
                this.shift = ((e.modifiers & Event.SHIFT_MASK) == Event.SHIFT_MASK);
        
                if ((e.which)&&(e.which!=0))
                    this.keyAction(e.which);
            break;
            
            case "W3C":
                this.alt = (e.altKey) ? true : false;
                this.ctrl = (e.metaKey) ? true : false;

                if(!this.ctrl)
                    this.ctrl = (e.ctrlKey) ? true : false;

                this.shift = (e.shiftKey) ? true : false;

                if((e.which<16)||(e.which>18))
                    this.keyAction(e.which);
            break;
        }

    
        return false;
    },/*END keyCheck*/
    
    keyAction: function(code)
    {
        if(!this.key_input)
        {
            var key_holder = document.createElement("div");
            key_holder.setAttribute("id","keyHolder");
            key_holder.style["position"] = "absolute";
            key_holder.style["visibility"] = "hidden";
            key_holder.style["top"] = "0px";
            key_holder.style["left"] = "0px";
            this.key_input = document.createElement("textarea");
            //this.key_input.type = "text";
            this.key_input.style["visibility"] = "hidden";
            this.key_input.style["display"] = "inline";
            this.key_input.style["width"] = "0";
            this.key_input.style["height"] = "0";
            key_holder.appendChild(this.key_input);
            document.getElementById(this.id).insertBefore(key_holder,document.getElementById(this.id).childNodes[0]);
        }

        if(!this.current_selection)
            this.key_input.focus();

        var content = this.key_input.value;

        if(this.waiting_for_heading)
            this.heading(content);
        else if(this.ctrl)
            this.performKeyCommand(code);
        else if(!this.key_map.specialFunction(code,this))
            this.appendContent(content);
        
        this.alt = false;
        this.ctrl = false;
        this.shift = false;
        //this.reposition();

        if(!this.ctrl)
            this.history.commence(document.getElementById(this.id),this.active_block);

        this.key_input.value = "";
    },/*END keyAction*/

    getCallback: function(code)
    {
        return this.deleteNodes;
    },/*END getCallback*/

    deleteNodes: function(arg,start,start_offset,end,end_offset,struct)
    {
        ret = true;

        if(arg == start)
            alert("start");
        else if(arg == end)
            alert("end");
        return ret;
    },/*END deleteNodes*/

    setCursor: function(offset)
    {
        this.stripCursor();
        var length = this.activeContent().length;

        if(offset>length)
            offset = 0;

        this.cursor_position = offset;

        var left = this.activeContent().substring(0,(length-offset));
        var right = this.activeContent().substring((length-offset),length);
        this.setContent(left+this.cursor()+right);
    },/*END setCursor*/

    setContent: function(content,keep_empty)
    {
        if(this.active_block)
            this.active_block.nodeValue = content;
        /*if(!keep_empty)
            this.clearEmptyNodes();*/

        if(this.save_object)
        {
            if(document.getElementById(this.id))
                this.save_object.value = document.getElementById(this.id).innerHTML;
        }
    },/*END setContent*/

    activeContent: function()
    {
        ret = "none";

        if(this.active_block)
            ret = this.active_block.nodeValue;

        if(!ret)
            ret = " ";

        return ret;
    },/*END activeContent*/

    left: function()
    {
        var length = this.activeContent().length;
        return this.activeContent().substring(0,(length-this.cursor_position)).replace(this.cursor(),""); 
    },/*END left*/

    right: function()
    {
        var length = this.activeContent().length;
        return this.activeContent().substring((length-this.cursor_position),length).replace(this.cursor(),"");
    },/*END right*/
    
    cursor: function()
    {
        return "|";
    },/*END cursor*/

    appendContent: function(content)
    {
        left = this.left();
        right = this.right();
        this.setContent(left+content+this.cursor()+right);
    },/*END appendContent*/
    
    backspace: function()
    {
        this.history.store(document.getElementById(this.id),this.active_block);
        this.reposition();

        if(!this.current_selection)
        {
            left = this.left();
            left_test = left;
            left = left.substring(0,(left.length-1));
            right = this.right();
            this.stripCursor();

            if(!left_test.replace("|",""))
            {
                non = this.nextNonEmpty(this.active_block,"up");

                if(!non&&(this.active_block.parentNode==this.activeParent()))
                {
                    del_html = this.activeParent().innerHTML;
                    this.deleteNode();
                    new_html = this.activeParent().innerHTML+del_html;
                    parent = this.activeParent();
                    parent.innerHTML = new_html;
                    this.setActive(this.landingBlock(parent,"up"));
                    this.end();
                }
                
                else if(non.nodeName.toLowerCase() == "br")
                {
                    prev = non.previousSibling;
                    next = non.nextSibling;
                    non.parentNode.removeChild(non);

                    if(prev&&next)
                    {
                        prev.parentNode.removeChild(prev);
                        next.nodeValue = prev.nodeValue+this.cursor()+next.nodeValue;
                        this.setActive(next);
                    }
                }

                else
                    this.cursorLeft();
            }
            
            else
                this.setContent(left+this.cursor()+right);
        }

        else
        {
        //    this.stripCursor();
            this.current_selection.getRangeAt(0).deleteContents();
            this.current_selection.collapseToStart();
            this.clearEmptyNodes();
            this.current_selection = "";
        }
        
        this.ensureContentIntegrity();
    },/*END backspace*/
    
    reposition: function()
    {
    /*    if(this.active_block&&this.active_block.parentNode)
        {
            var current_active = this.active_block;
            var container = document.createElement("span");
            var left_container = document.createElement("span");
            left_container.innerHTML = this.left();
            var right_container = document.createElement("span");
            right_container.innerHTML = this.right();
            container.appendChild(left_container);
            container.appendChild(right_container);
            this.active_block.parentNode.replaceChild(container,this.active_block);
            var offset_y = getOffsetY(right_container);
            container.parentNode.replaceChild(current_active,container);
            this.setActive(current_active);
            //var offset_y = getOffsetY(this.active_block.parentNode);

            if(((scrollTop()+clientHeight())<offset_y)||
               (offset_y<scrollTop()))
            {
                if(this.key_input&&this.active_block&&this.active_block.parentNode)
                {
                    this.key_input.parentNode.style["position"] = "absolute";
                    //div_top = getOffsetY(document.getElementById(this.id));
                    div_top = 0;
                    this.key_input.parentNode.style["top"] = (offset_y-(clientHeight()/2)-div_top)+"px";
                }

                window.scrollTo(0,(offset_y-(clientHeight()/2)));
            }
        }*/
    },/*END reposition*/


    shiftEnter: function()
    {
        this.shift = true;
        this.enter();
    },

    floatImageLeft: function()
    {
        this.context_menu.target.style["cssFloat"] = "left";
    },

    floatImageRight: function()
    {
        this.context_menu.target.style["cssFloat"] = "right";
    },

    unFloatImage: function()
    {
        this.context_menu.target.style["cssFloat"] = "none";
    },

    toggleCentre: function()
    {
        this.stripCursor();
        image = this.context_menu.target;
        p = image.parentNode;
        
        var image_filter =
        function(test_node)
        {
            if(test_node.nodeType == 3)
            {
                test = test_node.nodeValue;
                test = test.replace( /^\s+/g,"");// strip leading
                test = test.replace( /\s+$/g,"");// strip trailing
    
                if(test)
                    var result = NodeFilter.FILTER_ACCEPT;
                else
                    var result = NodeFilter.FILTER_REJECT;
            }

            else
            {
                if(test_node.nodeName.toLowerCase()!="img")
                    var result = NodeFilter.FILTER_ACCEPT;
                else
                    var result = NodeFilter.FILTER_REJECT;
            }

            return result;
        }

        walker = document.createTreeWalker(p,NodeFilter.SHOW_ALL,image_filter,false);
        
        if(!walker.nextNode())
        {
            if(p.style["textAlign"] !="center")
                p.style["textAlign"] = "center";
            else
                p.style["textAlign"] = "left";

            this.setActive(this.landingBlock(p,"down"));
            this.end();
            this.keyAction(0);
        }

        else
        {
            alert("To centre an image, put it in a paragraph by itself");
        }
    },

    changeImageSource: function()
    {
        img = this.context_menu.target;
        src = img.src;
        src = prompt("Please enter path to this image",src);
        img.setAttribute("src",src);
    },

    deleteImage: function()
    {
        this.context_menu.target.parentNode.removeChild(this.context_menu.target);
        this.ensureContentIntegrity();
    },

    image: function()
    {
        this.history.store(document.getElementById(this.id),this.active_block);
        var img = document.createElement("img");
        src = 'path to image';
        src = prompt("Please enter path to this image",src);

        if(src)
        {
            img.setAttribute("src",src);
    
            if(!this.left()&&this.right())
            {
                this.activeParent().insertBefore(img,this.active_block);
                this.setActive(this.landingBlock(this.activeParent().firstChild.nextSibling,"stationary"));
            }
            
            else if(!this.right()&&this.left())
            {
                this.activeParent().appendChild(img);
                this.activeParent().appendChild(document.createTextNode(this.cursor()));
                this.setActive(this.landingBlock(this.activeParent().lastChild,"stationary"));
                this.setCursor(this.activeContent().length);
            }
            
            else if(!this.left()&&!this.right())
            {
                this.stripCursor();
                this.activeParent().appendChild(img);
                this.activeParent().appendChild(document.createTextNode(this.cursor()));
                this.setActive(this.landingBlock(this.activeParent().lastChild,"stationary"));
            }
            
            else if(this.left()&&this.right())
            {
                new_left = document.createTextNode(this.left());
                new_right = document.createTextNode(this.right());
                active_parent = this.activeParent();
                immed_parent = this.active_block.parentNode;
                immed_parent.replaceChild(new_left,this.active_block);
                next = new_left.nextSibling;
    
                if(next)
                {
                    immed_parent.insertBefore(img,next);
                    immed_parent.insertBefore(new_right,next);
                }
    
                else
                {
                    immed_parent.appendChild(img);
                    immed_parent.appendChild(new_right);
                }
    
                this.setActive(this.landingBlock(new_right,"stationary"));
                this.setCursor(this.activeContent().length);
            }
            
            this.ensureContentIntegrity();
            this.keyAction(0);
        }
    },

    exitCode: function()
    {
        this.enter("p",null,false,true);
    },

    enter: function(name,node,dont_store,break_out)
    {
        if((this.active_block.parentNode.nodeName.toLowerCase() == "pre")&&!break_out)
        {
            this.setContent(this.left()+"\n"+this.cursor()+this.right());
        }
        
        else
        {
            if(!dont_store)
                this.history.store(document.getElementById(this.id),this.active_block);
    
            //this.reposition();
    
            /*WHAT WE ARE GOING TO DO HERE IS CLONE THE ENTIRE CURRENT ACTIVE PARENT,
            THEN REMOVE ALL TEXT AND IMAGES FROM THE RIGHT HAD SIDE OF ONE, AND DO
            THE SAME FROM THE LEFT HAND SIDE OF THE OTHER, THEN INSERT THE ONE WITH
            RIGHT HAND SIDE CONTENT INTO THE DOCUMENT AFTER THE ACTIVE PARENT, AND
            THEN CALL ensureContentIntegrity() TO GEL TEXT NODES AND CLEAR EMPTY
            NODES*/
            if(!this.shift)
            {
                if(!name)
                    name = this.activeParent().nodeName.toLowerCase();
    
                if(name == "quote")
                    name = "p";

                /*FIRST GET THE DATA WE WILL NEED*/
                if(!node)
                {
                    var next = this.activeParent().nextSibling;
                    var current = this.activeParent();
                }
                
                else
                {
                    var next = node.nextSibling;
                    var current = node;
                }
    
                var left = document.createTextNode(this.left());
                var right = document.createTextNode(this.right());
                this.stripCursor();
                var active_content = this.active_block.nodeValue;
                /*NOW CLONE THE NODE*/
                if(name == current.nodeName.toLowerCase())
                    var new_node = current.cloneNode(true);
                else
                    var new_node = document.createElement(name);
                    
                if(new_node.style["textAlign"] == "center")
                    new_node.style["textAlign"] = "left";

                /*APPEND THIS NEW NODE TO THE DOCUMENT BECAUSE THE 
                JAVASCRIPT DOM IS PATHETIC AND THIS WON'T WORK OTHERWISE*/
                if(next)
                    current.parentNode.insertBefore(new_node,next);
                else
                    current.parentNode.appendChild(new_node);
    
                /*FILTER THROUGH AND DELETE TEXT AND IMAGES AND BRS FROM 
                LEFT AND RIGHT AS REQUIRED*/
                var text_and_image_filter =
                function(test_node)
                {
                    if(test_node.nodeType == 1)
                    {
                        if((test_node.nodeName.toLowerCase()=="img")||(test_node.nodeName.toLowerCase()=="br"))
                            var result = NodeFilter.FILTER_ACCEPT;
                        else
                            var result = NodeFilter.FILTER_SKIP;
                    }
    
                    else
                        var result = NodeFilter.FILTER_ACCEPT;
        
                    return result;
                }
                //APPARENTLY CAN'T REUSE THE SAME FILTER - PATHETIC
                var another_text_and_image_filter =
                function(test_node)
                {
                    if(test_node.nodeType == 1)
                    {
                        if((test_node.nodeName.toLowerCase()=="img")||(test_node.nodeName.toLowerCase()=="br"))
                            var result = NodeFilter.FILTER_ACCEPT;
                        else
                            var result = NodeFilter.FILTER_SKIP;
                    }
    
                    else
                        var result = NodeFilter.FILTER_ACCEPT;
        
                    return result;
                }
        
                var left_walker = document.createTreeWalker(current,NodeFilter.SHOW_ALL,text_and_image_filter,false);
                var matched = false;
                var to_remove = new Array();
    
                while(left_walker.nextNode())
                {
                    if(!matched)
                    {
                        if((left_walker.currentNode.nodeType == 3)&&(left_walker.currentNode.nodeValue == active_content))
                            matched = true;
                    }
    
                    else
                        to_remove.push(left_walker.currentNode);
                }
    
                for(i = 0;i < to_remove.length;i++)
                    to_remove[i].parentNode.removeChild(to_remove[i]);
    
                if(this.active_block.parentNode)
                    this.active_block.parentNode.replaceChild(left,this.active_block);

                var right_walker = document.createTreeWalker(new_node,NodeFilter.SHOW_ALL,another_text_and_image_filter,false);
                var matched = null;
                var to_remove = new Array();
    
                while(right_walker.nextNode())
                {
                    if(matched == null)
                    {
                        if((right_walker.currentNode.nodeType == 3)&&(right_walker.currentNode.nodeValue == active_content))
                            matched = right_walker.currentNode;
                        else
                            to_remove.push(right_walker.currentNode);
                    }
                }
                
                for(i = 0;i < to_remove.length;i++)
                    to_remove[i].parentNode.removeChild(to_remove[i]);
    
                if(matched != null)
                    matched.parentNode.replaceChild(right,matched);
    
                if(this.isEmpty(new_node))
                {
                    new_node.appendChild(document.createTextNode(this.cursor()));
                    act = new_node;
                }
    
                if(this.isEmpty(current))
                {
                    current.appendChild(document.createTextNode(this.cursor()));
                    act = current;
                }
                
                else
                    act = new_node;
    
                this.ensureContentIntegrity();
                this.setActive(this.landingBlock(act,"stationary"));
                this.setCursor(0);
                this.keyAction(0);
            }
            
            else
            {
                if(this.current_selection)
                {
                    var anchor = this.current_selection.anchorNode;
                    var a_offset = this.current_selection.anchorOffset
                    var focus = this.current_selection.focusNode;
                    var f_offset = this.current_selection.focusOffset; 
                    var top_level = document.getElementById(this.id);
                    this.stripCursor();
                    this.setActive(this.landingBlock(this.applySingleLineSpace(anchor,a_offset,focus,f_offset,top_level),"stationary"));
                    this.setCursor(0);
                    this.current_selection.collapseToStart();
                    this.current_selection = false;
                }
    
                else
                {
                    var br = document.createElement("br");
                    var next = this.active_block.nextSibling;
                    var parent = this.active_block.parentNode;
                    right = document.createTextNode(this.cursor()+this.right());
                    left = this.left();
                    this.stripCursor();
                    parent.replaceChild(document.createTextNode(left),this.active_block);
                    this.setActive(left);
        
                    if(next)
                    {
                        parent.insertBefore(br,next);
                        parent.insertBefore(right,next);
                    }
        
                    else
                    {
                        parent.appendChild(br);
                        parent.appendChild(right);
                    }
        
                    this.setActive(right);
                    this.setCursor(0);
                }
            }
        }
        this.ensureContentIntegrity();
        this.reposition();
    },/*END enter*/
    
    almostStripCursor: function()
    {
        this.almost_stripped = this.active_block;
    },/*END alsmotStripCursor*/

    stripCursor: function()
    {
        /*if(this.active_block)
        {
            var left = this.left().replace(this.cursor(),"");
            var right = this.right().replace(this.cursor(),"");
            this.setContent(left+right,true);
        }*/
        var walker = document.createTreeWalker(document.getElementById(this.id),NodeFilter.SHOW_TEXT,null,false);
        
        while(walker.nextNode())
        {
            walker.currentNode.nodeValue = walker.currentNode.nodeValue.replace(this.cursor(),"");
        }
    },/*END stripCursor*/
    
    setActive: function(node,keep_empty)
    {
        if(node)
        {
            this.active_block = node;
            if(!keep_empty)
                this.clearEmptyNodes();

            if(this.activeParent())
            {
                if(this.activeParent().firstChild.nodeName.toLowerCase()=="br")
                    this.activeParent().removeChild(this.activeParent().firstChild);
            }
        }
        
        else
            this.active_block = null;
    },/*END setActive*/
    
    isEmpty: function(node)
    {
        ret = true;
        var empty_text_filter =
        function(test_node)
        {
            test = test_node.nodeValue;
            test = test.replace( /^\s+/g,"");// strip leading
            test = test.replace( /\s+$/g,"");// strip trailing 
            
            if(test)
                var result = NodeFilter.FILTER_ACCEPT;
            else
                var result = NodeFilter.FILTER_REJECT;

            return result;
        }

        var image_filter =
        function(test_node)
        {
            if(test_node.nodeName.toLowerCase()=="img")
                var result = NodeFilter.FILTER_ACCEPT;
            else
                var result = NodeFilter.FILTER_REJECT;

            return result;
        }

        if(node)
        {
            walker = document.createTreeWalker(node,NodeFilter.SHOW_TEXT,empty_text_filter,false);
            ret = (!walker.nextNode());
        }

        if(node&&ret)
        {
            walker = document.createTreeWalker(node,NodeFilter.SHOW_ELEMENT,image_filter,false);
            ret = (!walker.nextNode());
        }

        if(node.nodeName.toLowerCase()=="img")
            ret = false;

        return ret;
    },/*END isEmpty*/
    
    ensureContentIntegrity: function()
    {
        var walker = document.createTreeWalker(document.getElementById(this.id),NodeFilter.SHOW_TEXT,null,false);

        while(walker.nextNode())
        {
            next = walker.currentNode.nextSibling;

            if(next)
            {
                if(next.nodeType == 3)
                {
                    walker.currentNode.nodeValue += next.nodeValue;
                    next.parentNode.removeChild(next);
                }
            }
        }

        var walker = document.createTreeWalker(document.getElementById(this.id),NodeFilter.SHOW_TEXT,null,false);
        
        while(walker.nextNode())
        {
            if(walker.currentNode.nodeType == 3)
            {
                if(walker.currentNode.parentNode == document.getElementById(this.id))
                {
                    var new_node = document.createElement("p");
                    var next = walker.currentNode.nextSibling;
                    walker.currentNode.parentNode.removeChild(walker.currentNode);
                    new_node.appendChild(walker.currentNode);
                    
                    if(next)
                        document.getElementById(this.id).insertBefore(new_node,next);
                    else
                        document.getElementById(this.id).appendChild(new_node);
                }
            }
        }

        var a_filter =
        function(node)
        {
            if(node.nodeName.toLowerCase()=="a")
                return NodeFilter.FILTER_ACCEPT;
            else
                return NodeFilter.FILTER_SKIP;
        }

        var walker = document.createTreeWalker(document.getElementById(this.id),NodeFilter.SHOW_ELEMENT,a_filter,false);

        while(walker.nextNode())
        {
            try
            {
                //detachEvent(walker.currentNode,"click",this.getHref.bind(this),false);
                detachEvent(walker.currentNode,"click",this.stopHref.bind(this),false);
            }
            
            catch(exc)
            {
            }

            //attachEvent(walker.currentNode,"click",this.getHref.bind(this),false);
            attachEvent(walker.currentNode,"click",this.stopHref.bind(this),false);
        }

        var brs = document.getElementsByTagName("br");
        var cont = true;
        var old_length = brs.length;

        while(brs.length&&cont)
        {
            for(i = 0; i < brs.length; i++)
            {
                if(brs[i].parentNode == document.getElementById(this.id))
                    brs[i].parentNode.removeChild(brs[i]);
            }
    
            var brs = document.getElementsByTagName("br");
            
            if(brs.length == old_length)
                cont = false;
            else
                old_length = brs.length;
        }
        
        this.clearEmptyNodes();
    },/*END ensureContetnIntegrity*/

    clearEmptyNodes: function(pause)
    {
        var clear_on_empty = 
        function(node)
        {
            if((node == document.getElementById("keyHolder"))||(node.nodeName.toLowerCase()=="br"))
                return NodeFilter.FILTER_REJECT;
            else
                return NodeFilter.FILTER_ACCEPT;
        }

        var walker = document.createTreeWalker(document.getElementById(this.id),NodeFilter.SHOW_ELEMENT,clear_on_empty,false);
        var to_check = new Array();

        while(walker.nextNode())
            to_check.push(walker.currentNode);
        
        for(i = 0; i < to_check.length;i++)
        {
            if(this.isEmpty(to_check[i]))
                to_check[i].parentNode.removeChild(to_check[i]);
        }
        
        var brs = document.getElementsByTagName("br");

        for(i = 0; i < brs.length; i++)
        {
            cur = brs[i];
            next = this.nextNonEmpty(cur,"down");
            prev = this.nextNonEmpty(cur,"up");

            if(!(next&&prev))
                cur.parentNode.removeChild(cur);
        }

        var walker = document.createTreeWalker(document.getElementById(this.id),NodeFilter.SHOW_TEXT,null,false);

        while(walker.nextNode())
        {
            if(!walker.currentNode.nodeValue)
                walker.currentNode.parentNode.removeChild(walker.currentNode);
        }

        var brs = document.getElementsByTagName("br");
        var to_remove = new Array();

        for(i = 0; i < brs.length; i++)
        {
            cur = brs[i];
            prev = cur.previousSibling;

            if(prev)
            {
                if(prev.nodeName.toLowerCase=="br")
                    to_remove.push(cur);
            }
        }
        
        for(i = 0;i < to_remove.length;i++)
        {
            try
            {
                if(to_remove[i].parentNode)
                {
                    to_remove[i].parentNode.removeChild(to_remove[i]);
                }
            }
            
            catch(e)
            {
            }
        }

        if(this.isEmpty(document.getElementById(this.id))&&!pause)
            document.getElementById(this.id).setAttribute("class","emptyReceiver");
    },/*END clearEmptyNodes*/
    
    cursorLeft: function()
    {
        if(!this.ctrl&&!this.alt)
        {
            this.cursor_position++;
    
            if(this.cursor_position < this.activeContent().length)
            {
                left = this.left();
                right = this.right();
                right = left.substring((left.length-1),left.length)+right;
                left = left.substring(0,(left.length-1));
                this.setContent(left+this.cursor()+right);
            }
            
            else
            {
                this.stripCursor();
                this.setActive(this.landingBlock(this.active_block,"up"));
                this.end();
            }
        }
        
        else if(this.ctrl)
        {
            this.ctrl = false;
            while(this.word_delimeters.search(this.activeContent().charAt(this.activeContent().length-(this.cursor_position+2)))==-1)
                this.cursorLeft();
            this.cursorLeft();
        }

        else if(this.alt)
        {
            this.alt = false;
            while(this.sentence_delimeters.search(this.activeContent().charAt(this.activeContent().length-(this.cursor_position+2)))==-1)
                this.cursorLeft();
            this.cursorLeft();
        }
    },/*END cursorLeft*/

    cursorUp: function()
    {
        this.stripCursor();
        var prev_length = this.activeContent().length;
        this.setActive(this.landingBlock(this.activeParent(),"up"));
        var prev_offset = prev_length-this.cursor_position;

        if(prev_offset>this.activeContent().length)
            prev_offset = this.activeContent().length;
        
        this.setCursor(this.activeContent().length-prev_offset);
    },/*END cursorUp*/

    cursorRight: function()
    {
        if(!this.ctrl&&!this.alt)
        {
            if(this.cursor_position > 0)
            {
                left = this.left();
                right = this.right();
                left = left+right.substring(0,1);
                right = right.substring(1,right.length);
                this.setContent(left+this.cursor()+right);
                this.cursor_position--;
            }
            
            else if(this.active_block.parentNode.nodeName.toLowerCase()!="a")
            {
                this.stripCursor();
                this.setActive(this.landingBlock(this.active_block,"down"));
                this.cursor_position = 0;
                this.home();
            }
            
            else
            {
                next = this.active_block.parentNode.nextSibling;
                
                if(next)
                {
                    if(this.isEmpty(next))
                    {
                        elem = document.createTextNode(this.cursor());
                        this.active_block.parentNode.parentNode.appendChild(elem);
                        this.setActive(elem);
                        this.setCursor(0);
                        this.keyAction(0);
                    }
                }
                
                else
                {
                    elem = document.createTextNode(this.cursor());
                    this.active_block.parentNode.parentNode.appendChild(elem);
                    this.setActive(elem);
                    this.setCursor(0);
                    this.keyAction(0);
                }
            }
        }
        
        else if(this.ctrl)
        {
            this.ctrl = false;

            while(this.word_delimeters.search(this.activeContent().charAt(this.activeContent().length-(this.cursor_position-1)))==-1)
                this.cursorRight();

            this.cursorRight();
        }
        
        else if(this.alt)
        {
            this.alt = false;

            while(this.sentence_delimeters.search(this.activeContent().charAt(this.activeContent().length-(this.cursor_position-1)))==-1)
                this.cursorRight();

            this.cursorRight();
        }
    },/*END cursorRight*/
    
    activeParent: function()
    {
        ret = false;

        if(this.active_block != null)
        {
            var current = this.active_block.parentNode;

            while(current)
            {
                if(
                      (current.nodeName.toLowerCase()!="p")&&
                      (current.nodeName.toLowerCase()!="pre")&&
                      (current.nodeName.toLowerCase()!="quote")&&
                      (current.nodeName.toLowerCase()!="li")&&
                      (!this.isHeading(current))&&
                      (current != document.getElementById(this.id))
                  )
                {
                    current = current.parentNode;
                }

                else
                {
                    ret = current;
                    current = "";
                }
            }
        }
        
        if(!ret||(ret == document.getElementById(this.id)))
        {
            var isHeading = this.isHeading;

            var ap_filter =
            function(node)
            {
                if((node.nodeName.toLowerCase()=="p")||(node.nodeName.toLowerCase()=="li")||(isHeading(node)))
                    return NodeFilter.FILTER_ACCEPT;
                else
                    return NodeFilter.FILTER_SKIP;
            }

            var walker = document.createTreeWalker(document.getElementById(this.id),NodeFilter.SHOW_ELEMENT,ap_filter,false);
            ret = walker.firstChild();
        }
        
        return ret;
    },/*END activeParent*/

    activeChild: function()
    {
        ret = false;

        if(this.active_block != null)
        {
            if(this.active_block.parentNode)
            {
                if(this.formatting.search(this.active_block.parentNode.nodeName.toLowerCase())!=-1)
                    ret = this.active_block.parentNode;
                else
                    ret = this.active_block;
            }
        }
        
        return ret;
    },/*END activeChild*/

    cursorDown: function()
    {
        this.stripCursor();
        var prev_length = this.activeContent().length;

        this.setActive(this.landingBlock(this.activeParent(),"down"));
        
        var prev_offset = prev_length-this.cursor_position;

        if(prev_offset>this.activeContent().length)
            prev_offset = this.activeContent().length;
        
        this.setCursor(this.activeContent().length-prev_offset);
    },/*END cursorDown*/

    /*
    * Get the next sibling that is not an empty text node
    */
    nextNonEmpty: function(node,direction)
    {
        var land = false;

        if(node)
        {
            if(direction == "down")
                var land = node.nextSibling;
            else
                var land = node.previousSibling;
    
            if(land)
            {
                if(land.nodeType == 3)
                {
                    test = land.nodeValue;
                    test = test.replace( /^\s+/g,"");// strip leading
                    test = test.replace( /\s+$/g,"");// strip trailing 
                    
                    while(land&&!test)
                    {
                        if(direction == "down")
                            land = land.nextSibling;
                        else
                            land = land.previousSibling;
                        
                        if(land)
                        {
                            if(land.nodeType == 3)
                            {
                                test = land.nodeValue;
                                test = test.replace(/^\s+/g,"");// strip leading
                                test = test.replace(/\s+$/g,"");// strip trailing 
                            }
                            
                            else
                                test = true;
                        }
                    }
                }
            }
        }
        
        return land;
    },/*END nextNonEmpty*/

    /**
    * Find the next editable text node given a node and a direction
    */
    landingBlock: function(node,direction)
    {
        this.clearEmptyNodes();
        var ret = false;
        node_previous = this.nextNonEmpty(node,"up");
        node_next = this.nextNonEmpty(node,"down");
        
        if(node_previous)
            node_previous_name = node_previous.nodeName.toLowerCase();
        else
            node_previous_name = "";
        if(node_next)
            node_next_name = node_next.nodeName.toLowerCase();
        else
            node_next_name = "";
        
        if(direction == "stationary")
        {
            if(node)
            {
                if(node.nodeType != 3)
                {
                    var valid_block_filter = 
                    function(test_node)
                    {
                        test = test_node.nodeValue;
                        test = test.replace( /^\s+/g,"");// strip leading
                        test = test.replace( /\s+$/g,"");// strip trailing 
                        
                        if(test)
                            return NodeFilter.FILTER_ACCEPT;
                        else
                            return NodeFilter.FILTER_REJECT;
                    }
                    
                    walker = document.createTreeWalker(node,NodeFilter.SHOW_TEXT,valid_block_filter,false);
                    ret = walker.firstChild();

                    if(ret == null)
                    {
                        var image_filter =
                        function(test_node)
                        {
                            if(test_node.nodeName.toLowerCase()=="img")
                                var result = NodeFilter.FILTER_ACCEPT;
                            else
                                var result = NodeFilter.FILTER_REJECT;

                            return result;
                        }

                        walker = document.createTreeWalker(node,NodeFilter.SHOW_ALL,image_filter,false);

                        if(walker.nextNode())
                        {
                            text = document.createTextNode(this.cursor());
                            this.stripCursor();

                            if((direction == "stationary")||(direction == "down"))
                                node.appendChild(text);
                            else
                                node.insertBefore(text,walker.currentNode);

                            ret = text;
                        }
                    }
                    
                    if(node.nodeName.toLowerCase()=="img")
                    {
                        next = this.nextNonEmpty(node,"down");
                        
                        if(next != null)
                            ret = next;
                        else
                        {
                            this.stripCursor();
                            text = document.createTextNode(this.cursor());
                            node.parentNode.appendChild(text);
                            ret = text;
                        }
                    }
                }
                
                else
                    ret = node;
            }
        }

        else if((direction == "up")&&(node_previous_name=="img"))
        {
            before = this.nextNonEmpty(node_previous,"up");
            
            if(before!=null)
                ret = before;
            else
            {
                this.stripCursor();
                text = document.createTextNode(this.cursor());
                node.parentNode.insertBefore(text,node_previous);
                ret = text;
            }
        }

        else if((direction == "down")&&(node_next_name=="img"))
        {
            after = this.nextNonEmpty(node_next,"down");
            
            if(after!=null)
                ret = before;
            else
            {
                this.stripCursor();
                text = document.createTextNode(this.cursor());
                node.parentNode.appendChild(text);
                ret = text;
            }
        }

        else
        {
            if(node.parentNode)
            {
                land_par = node.parentNode;
                land = this.nextNonEmpty(node,direction);
    
                if(!land)
                {
                    while(land_par&&(land_par != document.getElementById(this.id)))
                    {
                        land = this.nextNonEmpty(land_par,direction);
    
                        if(land)
                            land_par = "";
                        else
                            land_par = land_par.parentNode;
                    }
                }

                if(land)
                {
                    if(land.nodeType == 1)
                    {
                        if((!this.isEmpty(land))&&(land.nodeName.toLowerCase()!="img"))
                        {
                            var valid_block_filter = 
                            function(test_node)
                            {
                                test = test_node.nodeValue;
                                test = test.replace( /^\s+/g,"");// strip leading
                                test = test.replace( /\s+$/g,"");// strip trailing 
                                
                                if(test)
                                    return NodeFilter.FILTER_ACCEPT;
                                else
                                    return NodeFilter.FILTER_REJECT;
                            }
                            
                            walker = document.createTreeWalker(land,NodeFilter.SHOW_TEXT,valid_block_filter,false);
                            
                            if(direction == "up")
                                ret = walker.lastChild();
                            else
                                ret = walker.firstChild();
                        }
                        
                        if(ret == null)
                        {
                            var image_filter =
                            function(test_node)
                            {
                                if(test_node.nodeName.toLowerCase()=="img")
                                    var result = NodeFilter.FILTER_ACCEPT;
                                else
                                    var result = NodeFilter.FILTER_REJECT;
                
                                return result;
                            }
                
                            walker = document.createTreeWalker(land,NodeFilter.SHOW_ALL,image_filter,false);
                
                            if(walker.nextNode())
                            {
                                text = document.createTextNode(this.cursor());
                                this.stripCursor();
                
                                if((direction == "stationary")||(direction == "down"))
                                    land.appendChild(text);
                                else
                                    land.insertBefore(text,walker.currentNode);
                
                                ret = text;
                            }
                        }
                    }
                    
                    else
                        ret = land;
                }
            }
        }

        if(!ret)
        {
            this.clearEmptyNodes();

            if(document.getElementById("holdingElement"))
                ret = document.getElementById("holdingElement").firstChild;
            else
            {
                var valid_block_filter = 
                function(test_node)
                {
                    test = test_node.nodeValue;
                    test = test.replace( /^\s+/g,"");// strip leading
                    test = test.replace( /\s+$/g,"");// strip trailing 
                    
                    if(test)
                        return NodeFilter.FILTER_ACCEPT;
                    else
                        return NodeFilter.FILTER_REJECT;
                }
                
                walker = document.createTreeWalker(document.getElementById(this.id),NodeFilter.SHOW_TEXT,valid_block_filter,false);
                ret = walker.firstChild();
            }
        }

        if(!ret)
        {
            this.clearEmptyNodes();
            el.setAttribute("class","receiverInUse");
            p = document.createElement("p");
            p.appendChild(document.createTextNode(this.cursor()));
            el.appendChild(p);
            ret = p.firstChild;
        }

        return ret;
    },/*END landingBlock*/

    esc: function()
    {
        this.destroyContextMenu();
        this.stripCursor();
        this.clearEmptyNodes(true);
        /*this.trimmed = false;
        this.setActive();*/

        if(document.getElementById("keyHolder"))
        {
            //document.getElementById(this.id).removeChild(document.getElementById("keyHolder"));
            document.getElementById("keyHolder").parentNode.removeChild(document.getElementById("keyHolder"));
            this.key_input = false;
        }

        this.save_object.value = document.getElementById(this.id).innerHTML;

        if(this.isEmpty(document.getElementById(this.id)))
            document.getElementById(this.id).setAttribute("class","emptyReceiver");
        else
            document.getElementById(this.id).setAttribute("class","");
    },/*END esc*/

    end: function()
    {
        if(this.cursor_position > 0)
        {
            this.cursor_position = 0;
            this.setContent(this.left()+this.right()+this.cursor());
        }
        
        else
        {
            this.stripCursor();
            this.setActive(this.landingBlock(this.active_block,"down"));
            this.cursor_position = this.activeContent().length;
            this.end();
        }
    },/*END end*/

    home: function()
    {

        if(this.cursor_position < this.activeContent().length)
        {
            this.setContent(this.cursor()+this.left()+this.right());
            this.cursor_position = this.activeContent().length;
        }
        
        else
        {
            this.cursor_position = 0;
            this.stripCursor();
            this.setActive(this.landingBlock(this.active_block,"up"));
            this.home();
        }
    },/*END home*/
    
    del: function()
    {
        if(!this.current_selection)
        {
            left = this.left();
            right = this.right();
            right = right.substring(1,right.length);
            this.setContent(left+this.cursor()+right);
            this.cursor_position--;
        }
        
        else
        {
            this.current_selection.getRangeAt(0).deleteContents();
            this.current_selection.collapseToStart();
            this.clearEmptyNodes();
            this.current_selection = "";
        }
    },/*END del*/

    performKeyCommand: function(code)
    {
        var pressed = String.fromCharCode(code).toLowerCase();
        
        switch(pressed)
        {
            case "a":
                this.setFormat("link");
            break;

            case "b":
               this.setFormat("bold");
            break;
            
            case "i":
                this.setFormat("italic");
            break;
           
            case "u":
                this.setFormat("underline");
            break;
           
            case "l":
                this.list();
            break;
           
            case "h":
                this.heading();
            break;
            
            case "v":
                this.paste();
            break;
            
            case "d":
                this.deleteNode();
            break;
            
            case "z":
                this.undo();
            break;
            
            case "r":
                this.redo();
            break;
            
            default:
                this.key_map.specialFunction(code,this);
            break;
        }
        
        if((pressed != "z")&&(pressed != "r"))
            this.history.commence(document.getElementById(this.id));
    },/*END performKeyCommand*/
    
    redo: function()
    {
        if(this.history.potentiates())
        {
            this.stripCursor();
            obj = document.getElementById(this.id);
            hist = this.history.redo();
            obj.parentNode.replaceChild(hist,obj);
            attachEvent(hist,"click",this.mouseClick.bind(this),false);
            attachEvent(hist,"mouseup",this.handleSelection.bind(this),false);
            attachEvent(hist,"contextmenu",this.contextMenu.bind(this),false);
            this.key_input = document.getElementById("keyHolder").firstChild;

            if(this.history.present_active)
            {
                this.setActive(this.history.present_active);
                this.setCursor(0);
            }

            else
                this.setActive(this.landingBlock(document.getElementById(this.id).firstChild,"down"));
        }

        this.ensureContentIntegrity();
    },/*END redo*/
    
    deleteNode: function()
    {
        this.history.store(document.getElementById(this.id),this.active_block);
        if(this.active_block)
        {
            active = this.activeParent();
            this.cursorUp();

            if(active.parentNode)
                active.parentNode.removeChild(active);
            this.clearEmptyNodes();
        }
    },/*END deleteNode*/

    paste: function()
    {
        this.history.store(document.getElementById(this.id),this.active_block);
        var pasted = this.key_input.value.replace(/(<([^>]+)>)/ig,"").split("\n");
        this.stripCursor();

        if(this.active_block.parentNode.nodeName.toLowerCase() != "pre")
        {
            if(!this.isHeading(this.activeParent())&&(this.activeParent()!=document.getElementById(this.id)))
                var name = this.activeParent().nodeName;
            else
                var name = "p";
    
            if(this.activeParent().nextSibling)
                next = this.activeParent().nextSibling;
            else
                next = false;
    
            for(i = 0;i<pasted.length;i++)
            {
                new_text = document.createTextNode(pasted[i]);
    
                if(i == 0)
                    this.active_block.nodeValue = this.activeContent()+new_text.nodeValue;
                else
                {
                    new_node = document.createElement(name);
                    new_node.appendChild(new_text);
    
                    if(next)
                        this.activeParent().parentNode.insertBefore(new_node,next);
                    else
                        this.activeParent().parentNode.appendChild(new_node);
                }
            }
            
            this.key_input.value = "";
            this.setActive(new_text);
            this.setCursor(0);
        }
        
        else
            this.setContent(this.left()+this.right()+this.key_input.value+this.cursor());
    },/*END paste*/

    h1: function()
    {
        this.heading(1);
    },

    h2: function()
    {
        this.heading(2);
    },

    h3: function()
    {
        this.heading(3);
    },

    h4: function()
    {
        this.heading(4);
    },

    h5: function()
    {
        this.heading(5);
    },

    hh: function()
    {
        this.heading('h');
    },
    
    menuHeading: function()
    {
        this.heading();
        this.contextMenu();
    },

    codeSnippet: function()
    {
        this.history.store(document.getElementById(this.id),this.active_block);

        if(this.activeParent().nodeName.toLowerCase() == "p")
        {
            //var code = document.createElement("code");
            var pre  = document.createElement("pre");
            pre.innerHTML = this.activeParent().innerHTML;
            //code.appendChild(pre);
            this.activeParent().parentNode.replaceChild(pre,this.activeParent());
            this.setActive(this.landingBlock(pre.firstChild,"stationary"));
            this.keyAction(0);
        }

        else if(this.active_block.parentNode.nodeName.toLowerCase() == "pre")
        {
            parent = this.active_block.parentNode;
            var p  = document.createElement("p");
            p.innerHTML = parent.innerHTML;
            //code.appendChild(pre);
            parent.parentNode.replaceChild(p,parent);
            this.setActive(this.landingBlock(p.firstChild,"stationary"));
            this.keyAction(0);
        }
    },

    quote: function()
    {
        this.history.store(document.getElementById(this.id),this.active_block);

        if(this.activeParent().nodeName.toLowerCase() == "p")
        {
            var quote  = document.createElement("quote");
            quote.innerHTML = this.activeParent().innerHTML;
            //code.appendChild(quote);
            this.activeParent().parentNode.replaceChild(quote,this.activeParent());
            this.setActive(this.landingBlock(quote.firstChild,"stationary"));
        }

        else if(this.active_block.parentNode.nodeName.toLowerCase() == "quote")
        {
            parent = this.activeParent();
            var p  = document.createElement("p");
            p.innerHTML = parent.innerHTML;
            parent.parentNode.replaceChild(p,parent);
            this.setActive(this.landingBlock(p.firstChild,"stationary"));
        }

        this.setKeyHolder();
        this.setCursor(0);
        this.keyAction(0);
        this.reposition();
    },

    heading: function(num) 
    {
        this.history.store(document.getElementById(this.id),this.active_block);

        if(!this.waiting_for_heading)
        {
            this.waiting_for_heading = true;
            this.key_input.focus();
        }

        else
        {
            if(!this.current_selection&&((this.activeParent().nodeName.toLowerCase()=="p")||(this.isHeading(this.activeParent()))))
            {
                if(parseInt(num))
                    var el = document.createElement("h"+num);
                else
                    var el = document.createElement("p");

                this.stripCursor();
                html = this.activeParent().innerHTML;
                el.innerHTML = html;
                this.activeParent().parentNode.replaceChild(el,this.activeParent());
                this.setActive(this.landingBlock(el,"stationary"));
                this.setCursor(0);
            }

            else if(this.activeParent().nodeName.toLowerCase()=="p")
                alert("To create a heading, click on a line without dragging the mouse");
            else
                alert("You can't apply a heading inside a list");

            this.waiting_for_heading = false;
        }
    },/*END heading*/

    shiftList: function()
    {
        this.shift = true;
        this.list();
    },

    list: function()
    {
        this.history.store(document.getElementById(this.id),this.active_block);

        if(this.current_selection)
        {
            var anchor = this.current_selection.anchorNode;
            var a_offset = this.current_selection.anchorOffset
            var focus = this.current_selection.focusNode;
            var f_offset = this.current_selection.focusOffset; 
            var top_level = document.getElementById(this.id);
            this.stripCursor();
            this.setActive(this.landingBlock(this.applyListToSelection(anchor,a_offset,focus,f_offset,top_level),"stationary"));
            this.setCursor(0);
            this.current_selection.collapseToStart();
            this.current_selection = false;
        }

        if((this.activeParent().nodeName.toLowerCase() != "li")||this.shift)
        {
            if(!this.activeParent().getElementsByTagName("ul")[0])
            {
                var list = document.createElement("ul");
                var item = document.createElement("li");
                var text = document.createTextNode(this.cursor());
                this.stripCursor();
                item.appendChild(text);
                list.appendChild(item);
                
                if(this.activeParent().nodeName.toLowerCase() == "p"||(this.activeParent().nodeName.toLowerCase() == "li"))
                {
                    if(this.activeParent().nodeName.toLowerCase() == "li")
                        this.activeParent().appendChild(list);
                    else if(this.activeParent().nextSibling)
                        document.getElementById(this.id).insertBefore(list,this.activeParent().nextSibling);
                    else
                        document.getElementById(this.id).appendChild(list);
                    
                    this.setActive(text);
                }
    
                else
                {
                    alert("You can only add a list to a normal paragraph, or from within another list. You are currently trying to add a list to a <"+this.active_block.parentNode.nodeName.toLowerCase()+"> tag");
                    this.end();
                }
            }
            
            else
            {
                this.stripCursor();
                this.setActive(this.landingBlock(this.activeParent().getElementsByTagName("ul")[0],"down"));
            }
        }
        
        else
        {
            this.stripCursor();
            this.setCursor(0);

            if(this.activeParent().parentNode.parentNode.nodeName.toLowerCase() == "li")
                this.setActive(this.activeParent().parentNode.parentNode.firstChild);
            else
            {
                //this.enter("p",this.activeParent().parentNode);
                next = this.activeParent().parentNode.nextSibling;
                new_node = document.createElement("p");
                text = document.createTextNode(this.cursor());
                new_node.appendChild(text);

                if(next)
                {
                    if(next.getAttribute("id")!="richieRichContextMenu")
                        this.activeParent().parentNode.parentNode.insertBefore(new_node,next);
                    else
                        this.activeParent().parentNode.parentNode.appendChild(new_node);
                }

                else
                    this.activeParent().parentNode.parentNode.appendChild(new_node);

                this.setActive(text);
                this.setCursor(0);
                this.keyAction(0);
            }
        }
        
        this.keyAction(0);
    },/*END list*/
    
    getSelectionObject: function() 
    {
        if(window.getSelection)
            var obj = window.getSelection();

        else if (document.getSelection)
        {
            obj = document.getSelection();
            //alert('document.getSelection: implement me!');
        }

        else if (document.selection)
        {
            obj = document.selection.createRange().text;
            //alert('document.selection: implement me!');
        }

        ret = new SelectionObject(obj);

        return ret;
    },

    getSelectionLength: function()
    {
        obj = this.getSelectionObject();

        //if(window.getSelection)
            var length = obj.toString().length;

        /*else if (document.getSelection)
            alert('document.getSelection: implement me!');
        else if (document.selection)
            alert('document.selection: implement me!');*/

        return length;
    },/*END getSelectionLength*/

    getSelectionOffset: function(type)
    {
        obj = this.getSelectionObject();

        if(window.getSelection)
        {
            if(type == "anchor")
                var offset = obj.anchorOffset;
            else if(type == "focus")
                var offset = obj.focusOffset;
            else
                alert("Unsupported offset type: "+type);
        }
/*
        else if (document.getSelection)
            alert('document.getSelection: implement me!');
        else if (document.selection)
            alert('document.selection: implement me!');
*/

        return offset;
    },/*END getSelectionOffset*/
    
    getSelectionNode: function(type)
    {
        obj = this.getSelectionObject();

        if(window.getSelection)
        {
            if(type == "anchor")
                var focus = obj.anchorNode;
            else if(type == "focus")
                var focus = obj.focusNode;
            else
                alert("Unsupported node type: "+type);
        }

/*
        else if (document.getSelection)
            alert('document.getSelection: implement me!');
        else if (document.selection)
            alert('document.selection: implement me!');
*/
        return focus;
    },/*END getSelectionNode*/
    
    getMouseTarget: getMouseTarget,

    handleSelection: function(e)
    {
        did_a_scroll = false;
        did_mouse_down = false;

        if(this)
        {
            length = this.getSelectionLength();
    
            if(length)
            {
                obj = this.getSelectionObject();
                this.current_selection = obj;
            }
        }

        e.stopPropagation();
    },/*END handleSelection*/

    undo: function()
    {
        if(this.history.exists())
        {
            this.stripCursor();
            obj = document.getElementById(this.id);
            hist = this.history.undo();
            obj.parentNode.replaceChild(hist,obj);
            attachEvent(hist,"click",this.mouseClick.bind(this),false);
            attachEvent(hist,"mouseup",this.handleSelection.bind(this),false);
            attachEvent(hist,"contextmenu",this.contextMenu.bind(this),false);
            this.key_input = document.getElementById("keyHolder").firstChild;

            if(this.history.present_active)
            {
                walker = document.createTreeWalker(document.getElementById(this.id),NodeFilter.SHOW_TEXT,null,false);
                var act = "";

                while(walker.nextNode())
                {
                    if(this.history.present_active.nodeValue.replace("|","") == walker.currentNode.nodeValue.replace("|",""))
                        act = walker.currentNode;
                }

                if(act)
                {
                    this.setActive(act);
                    this.setCursor(0);
                }
            }

            else
                this.setActive(this.landingBlock(document.getElementById(this.id).firstChild,"down"));

            this.reposition();
        }
        
        this.ensureContentIntegrity();
    },

    mouseClick: function(e)
    {
        did_a_scroll = false;
        did_mouse_down = false;
        this.destroyContextMenu();
        length = this.getSelectionLength();
        //this.clearEmptyNodes();

        if(!length)
        {
            el = document.getElementById(this.id);

            if(el.getAttribute("class")=="emptyReceiver")
            {
                p = document.createElement("p");
                newtext = document.createTextNode(this.cursor())
                p.appendChild(newtext);
                el.appendChild(p);
                this.setActive(this.landingBlock(p,"up"));
                this.end();
                this.setKeyHolder();
                el.setAttribute("class","receiverInUse");
                this.reposition();
                this.keyAction(0);
            }
            
            else
            {
                el.setAttribute("class","receiverInUse");
                this.current_selection = "";
                offset = this.getSelectionOffset("anchor");
    
                if(this.active_block)
                    this.stripCursor();

                if(target = this.getMouseTarget(e))
                {
                    if(this.getMouseTarget(e).nodeName.toLowerCase() == "img")
                        land = this.getMouseTarget(e);
                    else if(target.nodeType != 3)
                        land = this.getSelectionNode("anchor");
                    else
                        land = target;
                }

                else
                    land = this.getSelectionNode("anchor");

                this.setActive(this.landingBlock(land,"stationary"),true);
                this.setCursor(this.activeContent().length-offset);
                this.setKeyHolder();
                this.keyAction(0);
            }

            this.reposition();
        }
        
        e.stopPropagation();
        return false;
    }
}

var History = function()
{
    this.past = new Array();
    this.past_active = new Array();
    this.future = new Array();
    this.future_active = new Array();
    this.present = "";
    this.present_active = "";
    this.undoing = false;
    
}

History.prototype = 
{
    potentiates: function()
    {
        this.undoing = true;
        return this.future.length;
    },

    exists: function()
    {
        this.undoing = true;
        return this.past.length;
    },

    store: function(obj,active_block,bg)
    {
        this.past_active.push(active_block);
        to_push = obj.cloneNode(true);
        lists = to_push.getElementsByTagName("ul");

        for(i = 0;i < lists.length;i++)
        {
            if(lists[i].getAttribute("id")=="richieRichContextMenu")
            {
                length = lists.length;
                to_push.removeChild(lists[i]);
                i = length;
            }
        }
        
        this.past.push(to_push);
        this.undoing = true;

        if(this.past.length > 50)
            this.past.shift();
    },/*END store*/
    
    undo: function()
    {
        ret = this.past.pop();
        
        if(this.present)
        {
            to_push = this.present;
            lists = to_push.getElementsByTagName("ul");
    
            for(i = 0;i < lists.length;i++)
            {
                if(lists[i].getAttribute("id")=="richieRichContextMenu")
                {
                    length = lists.length;
                    to_push.removeChild(lists[i]);
                    i = length;
                }
            }

            this.future.push(to_push);
            this.future_active.push(this.present_active);
        }

        this.present = ret;
        this.present_active = this.past_active.pop();
        this.undoing = true;
        return ret;
    },/*END undo*/
    
    redo: function()
    {
        ret = this.future.pop();
        this.undoing = true;
        
        if(this.present)
        {
            to_push = this.present;
            lists = to_push.getElementsByTagName("ul");

            for(i = 0;i < lists.length;i++)
            {
                if(lists[i].getAttribute("id")=="richieRichContextMenu")
                {
                    length = lists.length;
                    to_push.removeChild(lists[i]);
                    i = length;
                }
            }

            this.past.push(to_push);
            this.past_active.push(this.present_active);
        }

        this.present = ret;
        this.present_active = this.future_active.pop();

        return ret;
    },/*END redo*/
    
    commence: function(obj,active)
    {
        if(!this.undoing&&this.future.length)
        {
            this.store(obj,active);
            this.future = new Array();
            this.future_active = new Array();
            this.present = "";
            this.present_active = "";
        }
        
        this.undoing = false;
    }/*END commence*/
}

var KeyMap = function()
{
    this.function_map = new Array();
    this.function_map[8] = this.backspace;
    this.function_map[13] = this.enter;
    this.function_map[27] = this.esc;
    this.function_map[35] = this.end;
    this.function_map[36] = this.home;
    this.function_map[37] = this.cursorLeft;
    this.function_map[38] = this.cursorUp;
    this.function_map[39] = this.cursorRight;
    this.function_map[40] = this.cursorDown;
    this.function_map[46] = this.del;
}

KeyMap.prototype =
{
    stopDefault: function(code,shift)
    {
        ret = false;

        if((((code == 36)||(code == 35))&&!shift)||(code==8))
            ret = true;

        return ret;
    },

    translate: function(code,shift)
    {
        var ret;
        
        if(this.output_map[code]&&!shift)
            ret = this.output_map[code];
        else if(this.shift_map[code]&&shift)
            ret = this.shift_map[code];
        else
            ret = String.fromCharCode(code);

        if(!shift)
            ret = ret.toLowerCase();

        return ret;
    },

    isSpecialFunction: function(code,shift)
    {
        var ret = false;
        
        if(this.function_map[code]&&!shift)
            ret = true;

        return ret;
    },

    specialFunction: function(code,receiver)
    {
        var ret = false;

        if(this.function_map[code])
        {
            this.function_map[code](receiver);
            ret = true
        }

        return ret;

    },
    
    backspace: function(receiver)
    {
        receiver.backspace();
    },

    enter: function(receiver)
    {
        receiver.enter();
    },

    cursorLeft: function(receiver)
    {
        receiver.cursorLeft();
    },

    cursorUp: function(receiver)
    {
        receiver.cursorUp();
    },

    cursorRight: function(receiver)
    {
        receiver.cursorRight();
    },

    cursorDown: function(receiver)
    {
        receiver.cursorDown();
    },

    esc: function(receiver)
    {
        receiver.esc();
    },

    end: function(receiver)
    {
        receiver.end();
    },

    home: function(receiver)
    {
        receiver.home();
    },

    del: function(receiver)
    {
        receiver.del();
    }
}

Function.prototype.bind = function(obj)
{
    if(this.cache_obj == null)
        this.cache_obj = new Array();
    if(this.cache_func == null)
        this.cache_func = new Array();

    var existing = this.cache_obj.search(obj);

    if(existing == -1)
    {
        var method = this,
        ret = function() {return method.apply(obj, arguments);};
        this.cache_obj.push(obj);
        this.cache_func.push(ret);
    }
    
    else
    {
        ret = this.cache_func[existing];
    }

    return ret;
} 

Function.prototype.cache_obj = null;
Function.prototype.cache_func = null;

Array.prototype.search = 
function(search_term)
{
    var i = this.length;
    if (i > 0)
    {
        do{if (this[i] === search_term){ return i;} }
        while (i--);
    }

    return -1;
}

function getElementStyle(elem, IEStyleProp, CSSStyleProp)
{
    if (elem.currentStyle)
    {
        return elem.currentStyle[IEStyleProp];
    }
    
    else if (window.getComputedStyle)
    {
        var compStyle = window.getComputedStyle(elem, "");
        return compStyle.getPropertyValue(CSSStyleProp);
    }

    return "";
}

function getOffsetX(node) {
    var result = node.offsetLeft;
    for (var parent = node; parent = parent.offsetParent; parent != null) {
        result += parent.offsetLeft;
    }
    return result;
}

function getOffsetY(node) {
    var result = node.offsetTop;
    for (var parent = node; parent = parent.offsetParent; parent != null) {
        result += parent.offsetTop;
    }
    return result;
}

function clientWidth() {
    return  filterResults (
        window.innerWidth ? window.innerWidth : 0,
        document.documentElement ? document.documentElement.clientWidth : 0,
        document.body ? document.body.clientWidth : 0
    );
}

function clientHeight() {
    return  filterResults (
        window.innerHeight ? window.innerHeight : 0,
        document.documentElement ? document.documentElement.clientHeight : 0,
        document.body ? document.body.clientHeight : 0
    );
}

function scrollLeft() {
    return  filterResults (
        window.pageXOffset ? window.pageXOffset : 0,
        document.documentElement ? document.documentElement.scrollLeft : 0,
        document.body ? document.body.scrollLeft : 0
    );
}

function scrollTop() {
    return  filterResults (
        window.pageYOffset ? window.pageYOffset : 0,
        document.documentElement ? document.documentElement.scrollTop : 0,
        document.body ? document.body.scrollTop : 0
    );
}

function filterResults(n_win, n_docel, n_body) {
    var n_result = n_win ? n_win : 0;
    if (n_docel && (!n_result || (n_result > n_docel)))
        n_result = n_docel;
    return n_body && (!n_result || (n_result > n_body)) ? n_body : n_result;
}

function attachEvent(obj,evt,fnc,useCapture){
	if (!useCapture) useCapture=false;
	if (obj.addEventListener){
		obj.addEventListener(evt,fnc,useCapture);
		return true;
	} else if (obj.attachEvent) {
ret =  obj.attachEvent("on"+evt,fnc);
return ret;
}
	else{
		myAttachEvent(obj,evt,fnc);
		obj['on'+evt]=function(){ myFireEvent(obj,evt) };
	}
} 

function detachEvent(obj,evType,fn,useCapture)
{
  if (obj.removeEventListener){
    obj.removeEventListener(evType, fn, useCapture);
    return true;
  } else if (obj.detachEvent){
    var r = obj.detachEvent("on"+evType, fn);
    return r;
  } else {
    alert("Handler could not be removed");
  }
}

//The following are for browsers like NS4 or IE5Mac which dont support either
//attachEvent or addEventListener
function myAttachEvent(obj,evt,fnc){
	if (!obj.myEvents) obj.myEvents={};
	if (!obj.myEvents[evt]) obj.myEvents[evt]=[];
	var evts = obj.myEvents[evt];
	evts[evts.length]=fnc;
}

function myFireEvent(obj,evt){
	if (!obj || !obj.myEvents || !obj.myEvents[evt]) return;
	var evts = obj.myEvents[evt];
	for (var i=0,len=evts.length;i<len;i++) evts[i]();
}

function getMouseTarget(e)
{
    emod = (e) ? (e.eventPhase) ? "W3C" : "NN4" : (window.event) ? "IE4+" : "unknown";

    if (emod == "NN4")
    {
        //document.captureEvents(Event.CLICK);
        //document.captureEvents(Event.KEYDOWN);
    }

    switch(emod)
    {
        case "NN4":
            var target = e.target;
            var text = e.target.text;
        break;
        case "W3C":
            var target = e.target;
            var text = e.target.text;
        break;
        case "IE4+":
            e = window.event;
            var target = e.srcElement;
            var text = e.srcElement.innerText;
        break;
    }

    return target;
}

function getMouseX(e) {
  tempX = e.pageX;
  if (tempX < 0){tempX = 0}
  return tempX;
}

function getMouseY(e) {
  tempY = e.pageY;
  if (tempY < 0){tempY = 0}
  return tempY;
}

var ContextMenu = function(event,widget,heading_mode)
{
    this.widget = widget;
    this.event = event;
    this.target = getMouseTarget(event);
    this.heading_mode = heading_mode;
    this.top = 0;
    this.left = 0;
    this.item_count = 0;
    var ul = document.createElement("ul");
    ul.id = "richieRichContextMenu";
/*    ul.style["cssFloat"] = "left";
    ul.style["top"] = getMouseY(event);
    ul.style["left"] = getMouseX(event);*/
    this.createMenuItems(receiver.getMouseTarget(event),ul);
    //document.body.appendChild(ul);
    document.getElementById(this.widget.id).appendChild(ul);
    this.menu_ref = ul;
}

ContextMenu.prototype =
{
    setPosition: function(top,left)
    {
        this.menu_ref.style["top"]  = top+"px";
        this.menu_ref.style["left"] = left+"px";
        this.top                    = top;
        this.left                   = left;
    },

    createMenuItem: function()
    {
        ret = document.createElement("li");
        ret.setAttribute("class","contextMenuItem");
        return ret;
    },

    appendItemToList: function(list,item)
    {
        list.appendChild(item);
        this.item_count++;
    },

    createMenuItems: function(target,list)
    {
        if(!this.heading_mode&&(target.nodeName.toLowerCase()!="img"))
        {
            name = target.nodeName.toLowerCase();
            //UNDO AND REDO ARE THERE ALL THE TIME
            undo = this.createMenuItem();
            undo.setAttribute("id","undoItem");
            undo.appendChild(document.createTextNode("Undo"));
            attachEvent(undo,"click",this.menuAction.bind(this),false);
            undo.action = this.widget.undo.bind(this.widget);
            this.appendItemToList(list,undo);
            redo = this.createMenuItem();
            redo.setAttribute("id","redoItem");
            redo.appendChild(document.createTextNode("Redo"));
            attachEvent(redo,"click",this.menuAction.bind(this),false);
            redo.action = this.widget.redo.bind(this.widget);
            this.appendItemToList(list,redo);
            
            if(this.widget.current_selection)
                this.appendSelectionActions(list);
            else
                this.appendStandardActions(target,list);
        }
        
        else if(target.nodeName.toLowerCase()=="img")
            this.appendImageActions(list,target)
        else
            this.appendHeadingActions(list);
    },
    
    appendImageActions: function(ul,target)
    {
        //FIRST CREATE THE LIST ITEMS WE WILL USE
        float_left = this.createMenuItem();
        float_right = this.createMenuItem();
        un_float = this.createMenuItem();
        centre = this.createMenuItem();
        change = this.createMenuItem();
        delete_image = this.createMenuItem();
        //NOW ADD TEXT
        float_left.appendChild(document.createTextNode("Float Left"));
        float_right.appendChild(document.createTextNode("Float Right"));
        un_float.appendChild(document.createTextNode("Un-float"));

        if(target.parentNode.style["textAlign"]!="center")
            centre.appendChild(document.createTextNode("Centre"));
        else
            centre.appendChild(document.createTextNode("Un-centre"));

        change.appendChild(document.createTextNode("Source"));
        delete_image.appendChild(document.createTextNode("Delete Image"));
        //NOW ATTACH EVENTS
        attachEvent(float_left,"click",this.menuAction.bind(this),false);
        attachEvent(float_right,"click",this.menuAction.bind(this),false);
        attachEvent(un_float,"click",this.menuAction.bind(this),false);
        attachEvent(centre,"click",this.menuAction.bind(this),false);
        attachEvent(change,"click",this.menuAction.bind(this),false);
        attachEvent(delete_image,"click",this.menuAction.bind(this),false);
        //NOW ATTACH ACTIONS
        float_left.action = this.widget.floatImageLeft.bind(this.widget);
        float_right.action = this.widget.floatImageRight.bind(this.widget);
        un_float.action = this.widget.unFloatImage.bind(this.widget);
        centre.action = this.widget.toggleCentre.bind(this.widget);
        change.action = this.widget.changeImageSource.bind(this.widget);
        delete_image.action = this.widget.deleteImage.bind(this.widget);
        //NOW APPEND TO THE LIST
        if(target.style["cssFloat"] != "left")
            this.appendItemToList(ul,float_left);
        if(target.style["cssFloat"] != "right")
            this.appendItemToList(ul,float_right);
        if((target.style["cssFloat"] == "left")||(target.style["cssFloat"] == "right"))
            this.appendItemToList(ul,un_float);

        this.appendItemToList(ul,centre);
        this.appendItemToList(ul,change);
        this.appendItemToList(ul,delete_image);
    },

    appendHeadingActions: function(ul)
    {
        //FIRST CREATE THE LIST ITEMS WE WILL USE
        h1 = this.createMenuItem();
        h2 = this.createMenuItem();
        h3 = this.createMenuItem();
        h4 = this.createMenuItem();
        h5 = this.createMenuItem();
        normal = this.createMenuItem();
        //NOW ADD TEXT
        item = document.createElement("h1");
        item.appendChild(document.createTextNode("H1"));
        h1.appendChild(item);
        item = document.createElement("h2");
        item.appendChild(document.createTextNode("H2"));
        h2.appendChild(item);
        item = document.createElement("h3");
        item.appendChild(document.createTextNode("H3"));
        h3.appendChild(item);
        item = document.createElement("h4");
        item.appendChild(document.createTextNode("H4"));
        h4.appendChild(item);
        item = document.createElement("h5");
        item.appendChild(document.createTextNode("H5"));
        h5.appendChild(item);
        normal.appendChild(document.createTextNode("Normal"));
        //NOW ATTACH EVENTS
        attachEvent(h1,"click",this.menuAction.bind(this),false);
        attachEvent(h2,"click",this.menuAction.bind(this),false);
        attachEvent(h3,"click",this.menuAction.bind(this),false);
        attachEvent(h4,"click",this.menuAction.bind(this),false);
        attachEvent(h5,"click",this.menuAction.bind(this),false);
        attachEvent(normal,"click",this.menuAction.bind(this),false);
        //NOW ATTACH ACTIONS
        h1.action = this.widget.h1.bind(this.widget);
        h2.action = this.widget.h2.bind(this.widget);
        h3.action = this.widget.h3.bind(this.widget);
        h4.action = this.widget.h4.bind(this.widget);
        h5.action = this.widget.h5.bind(this.widget);
        normal.action = this.widget.hh.bind(this.widget);
        //NOW APPEND TO THE LIST
        this.appendItemToList(ul,h1);
        this.appendItemToList(ul,h2);
        this.appendItemToList(ul,h3);
        this.appendItemToList(ul,h4);
        this.appendItemToList(ul,h5);
        this.appendItemToList(ul,normal);
    },

    appendSelectionActions: function(ul)
    {
        //FIRST CREATE THE LIST ITEMS WE WILL USE
        list = this.createMenuItem();
        single = this.createMenuItem();
        bold = this.createMenuItem();
        italic = this.createMenuItem();
        underline = this.createMenuItem();
        link = this.createMenuItem();
        delete_item = this.createMenuItem();
        //NOW ADD TEXT
        list.appendChild(document.createTextNode("Create List"));
        single.appendChild(document.createTextNode("Single Line Space"));
        bold.appendChild(document.createTextNode("Bold"));
        italic.appendChild(document.createTextNode("Italic"));
        underline.appendChild(document.createTextNode("Underline"));
        link.appendChild(document.createTextNode("Link"));
        delete_item.appendChild(document.createTextNode("Delete"));
        //NOW ATTACH EVENTS
        attachEvent(list,"click",this.menuAction.bind(this),false);
        attachEvent(single,"click",this.menuAction.bind(this),false);
        attachEvent(bold,"click",this.menuAction.bind(this),false);
        attachEvent(italic,"click",this.menuAction.bind(this),false);
        attachEvent(underline,"click",this.menuAction.bind(this),false);
        attachEvent(link,"click",this.menuAction.bind(this),false);
        attachEvent(delete_item,"click",this.menuAction.bind(this),false);
        //NOW ATTACH ACTIONS
        list.action = this.widget.list.bind(this.widget);
        single.action = this.widget.shiftEnter.bind(this.widget);
        bold.action = this.widget.setFormatBold.bind(this.widget);
        italic.action = this.widget.setFormatItalic.bind(this.widget);
        underline.action = this.widget.setFormatUnderline.bind(this.widget);
        link.action = this.widget.setFormatLink.bind(this.widget);
        delete_item.action = this.widget.backspace.bind(this.widget);
        //NOW APPEND TO THE LIST
        this.appendItemToList(ul,list);
        this.appendItemToList(ul,single);
        this.appendItemToList(ul,bold);
        this.appendItemToList(ul,italic);
        this.appendItemToList(ul,underline);
        this.appendItemToList(ul,link);
        this.appendItemToList(ul,delete_item);
    },

    appendStandardActions: function(target,ul)
    {
        if((target.nodeName.toLowerCase()=="ul")||
           (target.nodeName.toLowerCase()=="li"))
            in_list = true;
        else
            in_list = false;

        if((target.nodeName.toLowerCase()=="pre")||
           (target.parentNode.nodeName.toLowerCase()=="pre"))
            in_pre = true;
        else
            in_pre = false;

        if(target.nodeName.toLowerCase()=="a")
            in_a = true;
        else
            in_a = false;

        if(this.widget.activeParent().nodeName.toLowerCase() == "quote")
            in_quote = true;
        else
            in_quote = false;

        //FIRST CREATE THE LIST ITEMS WE WILL USE
        list = this.createMenuItem();
        sub_list = this.createMenuItem();
        heading = this.createMenuItem();
        line_break = this.createMenuItem();
        image = this.createMenuItem();
        code = this.createMenuItem();
        exit = this.createMenuItem();
        quote = this.createMenuItem();
        src = this.createMenuItem();
        //NOW ADD TEXT
        if(in_list)
            list.appendChild(document.createTextNode("Exit List"));
        else
            list.appendChild(document.createTextNode("Create List"));
            
        sub_list.appendChild(document.createTextNode("Create Sub List"));
        heading.appendChild(document.createTextNode("Heading"));
        line_break.appendChild(document.createTextNode("Line Break"));
        image.appendChild(document.createTextNode("Image"));
        
        if(!in_pre)
            code.appendChild(document.createTextNode("Code Snippet"));
        else if(!in_quote)
            code.appendChild(document.createTextNode("Normal Paragraph"));

        if(!in_quote)
            quote.appendChild(document.createTextNode("Quote"));
        else
            quote.appendChild(document.createTextNode("Un-Quote"));

        src.appendChild(document.createTextNode("Link Target"));
        exit.appendChild(document.createTextNode("Exit Code"));
        //NOW ATTACH EVENTS
        attachEvent(list,"click",this.menuAction.bind(this),false);
        attachEvent(sub_list,"click",this.menuAction.bind(this),false);
        attachEvent(heading,"click",this.menuAction.bind(this),false);
        attachEvent(line_break,"click",this.menuAction.bind(this),false);
        attachEvent(image,"click",this.menuAction.bind(this),false);
        attachEvent(code,"click",this.menuAction.bind(this),false);
        attachEvent(quote,"click",this.menuAction.bind(this),false);
        attachEvent(src,"click",this.menuAction.bind(this),false);
        attachEvent(exit,"click",this.menuAction.bind(this),false);
        //NOW ATTACH ACTIONS
        list.action = this.widget.list.bind(this.widget);
        sub_list.action = this.widget.shiftList.bind(this.widget);
        heading.action = this.widget.menuHeading.bind(this.widget);
        line_break.action = this.widget.shiftEnter.bind(this.widget);
        image.action = this.widget.image.bind(this.widget);
        code.action = this.widget.codeSnippet.bind(this.widget);
        quote.action = this.widget.quote.bind(this.widget);
        src.action = this.widget.getHref.bind(this.widget);
        exit.action = this.widget.exitCode.bind(this.widget);
        //NOW APPEND TO THE LIST
        if(!in_pre&&!in_quote)
        {
            this.appendItemToList(ul,list);
            
            if(in_list)
                this.appendItemToList(ul,sub_list);
    
            this.appendItemToList(ul,heading);
            this.appendItemToList(ul,line_break);
            this.appendItemToList(ul,image);
            
            if(in_a)
                this.appendItemToList(ul,src);
        }
        
        if(!in_quote)
        {
            this.appendItemToList(ul,code);
            
        }

        if(!in_pre)
            this.appendItemToList(ul,quote);

        if(in_pre)
            this.appendItemToList(ul,exit);
    },

    destroy: function()
    {
        if(this.menu_ref)
        {
            try
            {
                this.menu_ref.parentNode.removeChild(this.menu_ref);
            }
            
            catch(e)
            {
            }
        }

        this.menu_ref = null;
        this.widget.context_menu = false;
    },
    
    menuAction: function(e)
    {
        target = this.widget.getMouseTarget(e);
        
        if(!target.action)
            target = target.parentNode;

        target.action();
        e.stopPropagation();
        e.preventDefault();
        this.destroy();
        return false;
    },
}

function SelectionObject(obj)
{
    this.anchorNode = obj.anchorNode;
    this.anchorOffset = obj.anchorOffset
    this.focusNode = obj.focusNode;
    this.focusOffset = obj.focusOffset;
    
    try
    {
        this.range = obj.getRangeAt(0);
    }

    catch(e)
    {
    }

    this.selection = obj;
    this.string = obj.toString();

    this.collapseToStart = 
    function()
    {
        if(this.selection)
            this.selection.collapseToStart();
    }
    
    this.getRangeAt =
    function(index)
    {
        return this.range;
    }
    
    this.toString = 
    function()
    {
        return this.string;
    }
}
