// http://ajaxian.com/archives/enums-in-javascript (2007-02-12)
// http://jquery.com/dev/svn/trunk/plugins/fix_events/fix_events.js (2007-02-12)
function key(){}
key.BACKSPACE = 8;
key.ENTER = 13;
key.LEFT = 37;
key.UP = 38;
key.RIGHT = 39;
key.DOWN = 40;
key.CURSOR = new Array(37, 38, 39, 40);
key.DELETE = 46;


// http://de.selfhtml.org/javascript/objekte/node.htm#node_type (2007-02-13)
function nodeType(){}
nodeType.ELEMENT = 1;
nodeType.ATTRIBUTE = 2;
nodeType.TEXT = 3;




jQuery.fn.wymeditor = function(options) {

	options = jQuery.extend({
	
		iframeUrl:		"wymeditor/wymiframe.html",
		boxHtml:		"<div class='wym_box'></div>",
		menuHtml:		"<div class='wym_menu'></div>"

	}, options);

	return this.each(function(i) {

		new Wymeditor($(this),i,options);
	});
};

function Wymeditor(elem,index,options) {

	wym_instances[index] = this;

	this.element = elem;
	this.html = $(elem).val();
	this.doc = null;
	this.iframe = null;
	this.index = index;

	var box = $(elem).hide().after(options.boxHtml).next();
	
	this.box = box;

	var sHtml =   "<div class='" + options.iframeClass + "'>"
		+ "<iframe class='" + options.iframeClass + "' "
		+ "src='" + options.iframeUrl + "'"
		+ "onload='window.parent.wym_instances[" + index + "].init(this)' ";
	
	if(jQuery.browser.msie || jQuery.browser.opera) {
		sHtml +=  "onclick='window.parent.wym_instances[" + index + "].saveCaret()'"
			+ "onbeforedeactivate='window.parent.wym_instances[" + index + "].saveCaret()'";
	}

	sHtml += ' width="500" height="250"';
	sHtml += "></iframe></div>";
	sHtml += "<div class='wym_menu'><a class='wym_exec' href='#' name='Bold'>Bold</a></div>";
	sHtml += "<div class='wym_menu'><a class='wym_exec' href='#' name='Italic'>Italic</a></div>";
	sHtml += "<div class='wym_menu'><a class='wym_exec' href='#' name='H1'>Headline</a></div>";
	sHtml += "<div class='wym_menu'><a class='wym_exec' href='#' name='P'>Paragraph</a></div>";

	$(box).html(sHtml);
}

Wymeditor.prototype.init = function(iframe) {

	var doc = null;
	var wymeditor = this;

	if(jQuery.browser.mozilla) {
		doc = iframe.contentDocument;
		doc.title = wymeditor.index;
		doc.designMode="on";
		doc.execCommand("styleWithCSS",'',false);
		doc.addEventListener('contextmenu',this.contextmenuHandler,false);
	}
	else if(jQuery.browser.msie || jQuery.browser.opera) {

		doc = iframe.contentWindow.document;
		doc.body.contentEditable=true;
	}

	this.doc = doc;
	this.iframe=iframe;

	$(doc.body).html(this.html);

	//$(this.box).find(".wym_menu").hide();

	$(this.box).find("a.wym_exec").click(function(){
		wymeditor.exec($(this).attr("name"));
		return false;
	});
	
	$(this.box).find("a.wym_dialog").click(function(){
		alert(wymeditor.selectedContainer());
		return false;
	});


    // All nodes (dom, not jquery objecs) within this array will be deleted at
    // the beginning of the onKeyup event
    this.doc.deleteOnKeyup = new Array();


    // All nodes (dom, not jquery objecs) within this array will be deleted at
    // the beginning of the onMouseup event
    this.doc.deleteOnMouseup = new Array();


    $(iframe.contentDocument).bind('keyup', this.handleKeyup);
    $(iframe.contentDocument).bind('keydown', this.handleKeydown);
    $(iframe.contentDocument).bind('keypress', this.handleKeypress);
    $(iframe.contentDocument).bind('mousedown', this.handleMousedown);
    $(iframe.contentDocument).bind('mouseup', this.handleMouseup);

    // [DONE] TODO remove unneeded <br /> at the end of block elements

    // [DONE] IDEA When you e.g. join paragraphs (after a backspace at the start of
    // a paragraph), check if the new siblings fit together and join again
    // (recusively). Example: join <p><b>ab</b></p><p><b>cd</b></p> to
    // <p><b>bcd</b></p> instead of <p><b>ab</b><b>cd</b></p>

    // [DONE] TODO handle "entf-key"

    // [DONE] TODO remove empty nodes after a click somewhere else

    // BUG when you keep enter pressed in an empty paragraph and press up arrow
    // (move the cursor up), handleKeydown doesn't get called. It gets called
    // if you press up a second time (while enter is still pressed)
};


Wymeditor.prototype.handleKeyup = function(evt) {
// http://developer.mozilla.org/en/docs/DOM:Selection
    var win = this.defaultView;
    var doc = this;


    var sel = new selection(win.getSelection());
    var startNode = sel.original.getRangeAt(0).startContainer;
    //var endNode = sel.original.getRangeAt(0).endContainer;


    // Delete all nodes that should be deleted on an onkexup event
    while (doc.deleteOnKeyup.length!=0)
    {
        doc.deleteOnKeyup[0].parentNode.removeChild(doc.deleteOnKeyup[0]);
        //$(doc.deleteOnKeyup[0]).slideUp("normal", function(){
        //    this.parentNode.removeChild(this);
        //});
        doc.deleteOnKeyup.shift();
    }



    if(evt.keyCode==key.ENTER)
    {
        var parent = $(startNode).parentsOrSelf("P");

        if (parent.length > 0)
        {
            var prev = parent.prev();

            if (prev.length > 0 && prev[0].hasChildNodes())
            {

                // Test if last node is really a <br/> (perhaps nested like
                // <i><b><br/></b></i>)
                var nodeBr = prev[0].lastChild;
                while (nodeBr.hasChildNodes())
                {
                    var children = $(nodeBr).allChildren();
                    var last = children[children.length-1];

                    // last child is a <br>
                    if (last.nodeName == 'BR')
                    {
                        last.parentNode.removeChild(last);
                        break;
                    }
                    else if (last.nodeType == nodeType.ELEMENT)
                        nodeBr = last;
                    else
                        break;
                }
            }
        }
    }
}

Wymeditor.prototype.handleKeydown = function(evt) {
    var win = this.defaultView;
    var doc = this;

    var blockElements = "H1, H2, H3, H4, H5, H6, P";
    var sel = new selection(win.getSelection());

    var startNode = sel.original.getRangeAt(0).startContainer;
    if (isPhantomNode(startNode))
        startNode = startNode.nextSibling;
    var endNode = sel.original.getRangeAt(0).endContainer;
    if (isPhantomNode(endNode))
        endNode = endNode.previousSibling;
    var startOffset = sel.original.getRangeAt(0).startOffset;
    var endOffset = sel.original.getRangeAt(0).endOffset;


    if(evt.keyCode==key.BACKSPACE)
    {
        // Normally paragraphs (<p>) get merged with a <br> in between, but we
        // want them without a <br>
        if (sel.atStart("p"))
        {
            var parent = $(startNode).parentsOrSelf("p");
            var prev = parent.prev();

            if (prev.length!=0)
            {
                // stop event
                evt.stopPropagation();
                evt.preventDefault();

                // do what backspace normally does
                if (!sel.original.isCollapsed)
                {
                    sel.original.deleteFromDocument();
                    return true;
                }

                // if current paragraph is empty, just delete it and move
                // cursor to the end of the previous block
                if (parent.hasNoInformation())
                {
                    sel.cursorToEnd(prev);
                    parent[0].parentNode.removeChild(parent[0]);
                    return true;
                }

                // if prev has a <br/> as last child, delete it
                if (prev.children(":last")[0].nodeName == 'BR')
                    prev.children(":last").remove();


                var parentChild = parent.moveChildren(prev);
                    // this is the jquery way, but it doesn't preserve events
                    // use html(), as children() doesn't return text nodes
                    //prev.append(parent.html());
                    //parent.empty();


                // join nodes (recursive) if they have the same nodeName
                // e.g. join <b>x</b><b>y</b> to <b>xy</b>
                while ((parentChild.prev()[0].nodeName
                        == parentChild[0].nodeName)
                        && (parentChild[0].nodeType != nodeType.TEXT))
                    parentChild = parentChild.moveChildren(parentChild.prev());


                // move cursor to the start of the old paragraph node
                sel.cursorToStart(parentChild);
            }
        }
    }
    if(evt.keyCode==key.DELETE)
    {
        // Normally paragraphs (<p>) get merged with a <br> in between, but we
        // want them without a <br>
        if (sel.atEnd("p"))
        {
            var parent = $(startNode).parentsOrSelf("p");
            var next = parent.next();

            if (next.length!=0)
            {
                // stop event
                evt.stopPropagation();
                evt.preventDefault();

                // do what delete key normally does
                if (!sel.original.isCollapsed)
                {
                    sel.original.deleteFromDocument();
                    return true;
                }

                // if the current node has a <br/> as last child, delete it
                if (parent.children(":last")[0].nodeName == 'BR')
                    parent.children(":last").remove();

                // move choldren from the next node to the current node
                var parentChild = next.moveChildren(parent);

                // join nodes (recursive) if they have the same nodeName
                // e.g. join <b>x</b><b>y</b> to <b>xy</b>
                while ((parentChild.prev()[0].nodeName
                        == parentChild[0].nodeName)
                        && (parentChild[0].nodeType != nodeType.TEXT))
                    parentChild = parentChild.moveChildren(parentChild.prev());

                // move cursor to the start of the old paragraph node
                sel.cursorToStart(parentChild);
            }
        }
    }
    else if(evt.keyCode==key.ENTER)
    {
        var parent = $(startNode).parentsOrSelf(blockElements);

        // Don't allow adding more than one empty block Element
        if (parent.hasNoInformation())
        {
            // stop event
            evt.stopPropagation();
            evt.preventDefault();
            return true;
        }

        var isAtStart = sel.atStart(blockElements);
        var isAtEnd = sel.atEnd(blockElements)

        if (isAtStart)
        {
            // stop event
            evt.stopPropagation();
            evt.preventDefault();

            var parent = $(startNode).parentsOrSelf(blockElements);
            var prev = parent.prev();

            // do what enter normally does
            if (!sel.original.isCollapsed)
                sel.original.deleteFromDocument();


            // if previous paragraph is empty (<p><br/></p>) don't add a
            // new one
            // NOTE there won't be any empty paragraphs as they are deleted if
            // you leave them. But in the case you animate their deletion there
            // is a chance that the user presses "enter" twice, therefore it's
            // saver to check it
            if (prev.hasNoInformation())
                return true;


            // Enter was pressed in frontof a headline => new headline on top,
            // but we want a new paragraph on top instead
            var nodeP = $("<p><br /></p>");
            parent.before(nodeP);
            sel.cursorToStart(nodeP[0]);
        }

        if (isAtEnd)
        {
            // stop event
            evt.stopPropagation();
            evt.preventDefault();

            var parent = $(startNode).parentsOrSelf(blockElements);
            var next = parent.next();

            // do what enter normally does
            if (!sel.original.isCollapsed)
                sel.original.deleteFromDocument();


            // if next paragraph is empty (<p><br/></p>) don't add a new one
            // NOTE there won't be any empty paragraphs as they are deleted if
            // you leave them. But in the case you animate their deletion there
            // is a chance that the user presses "enter" twice, therefore it's
            // saver to check it
            if (next.hasNoInformation())
                return true;

            var nodeP = $("<p><br /></p>");
            parent.after(nodeP);
            sel.original.collapse(nodeP[0], 0);
        }

        // Pressing "enter" within a paragraph adds a <br/> (at least gecko)
        // => we do the break it manually
        // It would be nice, but it's to difficult => we delete the created
        ///<br/> on the keyup event
        if(!isAtStart && !isAtEnd)
        {

        }
    }
    // If cursor leave an empty node (e.g. <p><b><br/></b></p>), delete it
    else if(key.CURSOR.contains(evt.keyCode))
    {
        if (!sel.original.isCollapsed)
            return true;

        var parent = $(startNode).parentsOrSelf(blockElements);
        var next =  parent.next();
        var prev =  parent.prev();
        if (parent.hasNoInformation()
                && ((evt.keyCode==key.DOWN && parent.next().length!=0)
                    || (evt.keyCode==key.RIGHT && parent.next().length!=0)
                    || (evt.keyCode==key.UP && parent.prev().length!=0)
                    || (evt.keyCode==key.LEFT && parent.prev().length!=0)))
            doc.deleteOnKeyup.push(startNode);
    }
}




Wymeditor.prototype.handleKeypress = function(evt) {
    var win = this.defaultView;
    var doc = this;

    var sel = new selection(win.getSelection());
    var startNode = sel.original.getRangeAt(0).startContainer;
    if (isPhantomNode(startNode))
        startNode = startNode.nextSibling;

    var blockElements = "H1, H2, H3, H4, H5, H6, P";

    var parent = $(startNode).parentsOrSelf(blockElements);
    var allChildren = parent.allChildren();


    // Don't allow adding more than one empty block Element
    // NOTE it was already checked in handleKeyDown() but it's needed if you
    // hold Eenter down
    if(evt.keyCode==key.ENTER)
    {
        var parent = $(startNode).parentsOrSelf(blockElements);
        if (parent.hasNoInformation()
                || parent.prev().hasNoInformation()
                || parent.next().hasNoInformation())
        {
            // stop event
            evt.stopPropagation();
            evt.preventDefault();
            return true;
        }
    }
}







Wymeditor.prototype.handleMousedown = function(evt) {
    var win = this.defaultView;
    var doc = this;

    var blockElements = "H1, H2, H3, H4, H5, H6, P";
    var sel = new selection(win.getSelection());

    var startNode = sel.original.getRangeAt(0).startContainer;
    if (isPhantomNode(startNode))
        startNode = startNode.nextSibling;
    var parent = $(startNode).parentsOrSelf(blockElements);


    if (!sel.original.isCollapsed)
        return true;

    if (parent.hasNoInformation())
        doc.deleteOnMouseup.push(startNode);
}


Wymeditor.prototype.handleMouseup = function(evt) {
    var win = this.defaultView;
    var doc = this;

    // Delete all nodes that should be deleted on an onkeyup event
    while (doc.deleteOnMouseup.length!=0)
    {
        doc.deleteOnMouseup[0].parentNode.removeChild(doc.deleteOnMouseup[0]);
        //$(doc.deleteOnMouseup[0]).slideUp("normal", function(){
        //    this.parentNode.removeChild(this);
        //});
        doc.deleteOnMouseup.shift();
    }
}








function selection(sel)
{
    // the original selection object (from DOM specification)
    this.original = sel;

    var startNode = this.original.getRangeAt(0).startContainer;
    var endNode = this.original.getRangeAt(0).endContainer;
    if (isPhantomNode(startNode))
        startNode = startNode.nextSibling;
    if (isPhantomNode(endNode))
        endNode = endNode.previousSibling;
    var startOffset = this.original.getRangeAt(0).startOffset;
    var endOffset = this.original.getRangeAt(0).endOffset;


    var blockElements = "H1, H2, H3, H4, H5, H6, P";


    // returns true if selection starts at the beginning of a (nested) tag
    // (e.g. | = cursor, element = <p><b>|here</b></p>,
    // atStart("p") returns true)
    this.atStart = function(jqexpr)
    {
        var parent = $(startNode).parentsOrSelf(jqexpr);

        // jqexpr isn't a parent of the current cursor position
        if (parent.length==0)
            return false;
        else
            parent = parent[0];


        for (var n=startNode; n!=parent; n=n.parentNode)
        {
            if (n.nodeType == nodeType.TEXT)
            {
                if (startOffset != 0)
                    return false;
            }
            var firstChild = n.parentNode.firstChild;
            // node isn't first child => cursor can't be at the beginning
            // in gecko there the first child could be a phantom node

            // sometimes also whitespacenodes which aren't phatom nodes
            // get stripped, but this is ok, as this is a wysiwym editor
            if ((firstChild != n
                    || ($(firstChild).isPhantomNode()
                        && firstChild.nextSibling != n)))
            {
                return false;
            }
        }

        if (startOffset == 0)
            return true;
        else
            return false;
    }



    // returns true if selection ends at the end of a (nested) tag
    // (e.g. | = cursor, element = <p><b>here|</b></p>,
    // atEnd("p") returns true)
    this.atEnd = function(jqexpr)
    {
        var parent = $(endNode).parentsOrSelf(jqexpr);

        // jqexpr isn't a parent of the current cursor position
        if (parent.length==0)
            return false;
        else
            parent = parent[0];


        // This is the case if, e.g ("|" = cursor): <p>textnode|<br/></p>,
        // there the offset of endNode (endOffset) is 1 (behind the first node
        // of <p>)
        if (endNode == parent)
        {
            // NOTE Perhaps not needed
            //if (endNode.nodeName == "P")
            if (blockElements.split(", ").contains(startNode.nodeName))
            {
                // NOTE I don't know if it is a good idea to delete the <br>
                // here, as "atEnd()" probably shouldn't change the dom tree,
                // but only searching it
                if (endNode.lastChild.nodeName == "BR")
                    endNode.removeChild(endNode.lastChild);

                // if cursor is really at the end
                if (endOffset > 0 &&
                        endNode.childNodes[endOffset-1].nextSibling==null)
                    return true;
            }
        }
        else
        {
            for (var n=endNode; n!=parent; n=n.parentNode)
            {
                if (n.nodeType == nodeType.TEXT)
                {
                    if (endOffset != endNode.data.length)
                        return false;
                }
                else
                {
                    var lastChild = n.parentNode.lastChild;
                    // node isn't last child => cursor can't be at the end
                    // (is this true?) in gecko there the last child could be a
                    //     phantom node

                    // sometimes also whitespacenodes which aren't phatom nodes
                    // get stripped, but this is ok, as this is a wysiwym editor
                    if ((lastChild != n) ||
                            ($(lastChild).isPhantomNode()
                            && lastChild.previousSibling != n))
                    {
                        return false;
                    }
                }
            }
        }


        if (endOffset == endNode.length)
            return true;
        else
            return false;

    }


    // set the cursor to the first character (it can be nested),
    // e.g. {<p><b>test</b></p>}.cursorToStart() will set the cursor (|) in
    // front of test: <p><b>|test</b></p>
    this.cursorToStart = function(jqexpr)
    {
        var firstTextNode = $(jqexpr)[0];

        while (firstTextNode.nodeType!=nodeType.TEXT)
        {
            if (!firstTextNode.hasChildNodes())
                break;
            firstTextNode = firstTextNode.firstChild;
        }

        if (isPhantomNode(firstTextNode))
            firstTextNode = firstTextNode.nextSibling;

        if (firstTextNode.nodeType == nodeType.ELEMENT)
            this.original.collapse(firstTextNode.parentNode, 0);
        else
            this.original.collapse(firstTextNode, 0);
    }

    // set the cursor to the last character (it can be nested),
    // e.g. {<p><b>test</b></p>}.cursorToEnd() will set the cursor (|) behind
    // test: <p><b>test|</b></p>
    this.cursorToEnd = function(jqexpr)
    {
        var lastTextNode = $(jqexpr)[0];

        while (lastTextNode.nodeType!=nodeType.TEXT)
        {
            if (!lastTextNode.hasChildNodes())
                break;
            lastTextNode = lastTextNode.lastChild;
        }

        if (isPhantomNode(lastTextNode))
            lastTextNode = lastTextNode.previousSibling;

        if (lastTextNode.nodeType == nodeType.ELEMENT)
            this.original.collapse(lastTextNode.parentNode,
                lastTextNode.parentNode.childNodes.length);
        else
            this.original.collapse(lastTextNode, lastTextNode.length);
    }
}

function getLength(n)
{
    var length = 0;

    for (n=n.firstChild; n!=null; n=n.nextSibling)
    {
        if (n.nodeType == nodeType.TEXT)
        {
            length += n.length;
        }
        else if (n.hasChildNodes())
            length += getLength(n);
    }

    return length;
}


// Returns true if it is a text node with whitespaces only
$.fn.isPhantomNode = function()
{
    if (this[0].nodeType == 3)
        return !(/[^\t\n\r ]/.test(this[0].data));

    return false;
}
function isPhantomNode(n)
{
    if (n.nodeType == 3)
        return !(/[^\t\n\r ]/.test(n.data));

    return false;
}



// the children of the current object are copied to the other one
// TODO second argument: should the old object be removed fromtree? 
//                  true = yes, false = no, default = true
// returns the first child of the deleted element
$.fn.moveChildren = function(newParent)
{
    var newParent = newParent[0];
    var oldParent = this[0];
    var oldChild = oldParent.firstChild;

    for (var n=oldParent.firstChild; n!=null; n=next)
    {
        next = n.nextSibling;

        if(!isPhantomNode(n))
            newParent.appendChild(n);
        else
            oldparent.removeChild(n);
    }

    oldParent.parentNode.removeChild(oldParent);

    return $(oldChild);
}



// Returns the Parents or the node itself
// jqexpr = a jQuery expression
$.fn.parentsOrSelf = function(jqexpr)
{
    var n = this;

    if (n[0].nodeType == 3)
        n = n.parents().lt(1);

//    if (n.is(jqexpr)) // XXX should work, but doesn't (probably a jQuery bug)
    if (n.filter(jqexpr).size() == 1)
        return n;
    else
        return n.parents(jqexpr).lt(1);
}



// Returns all child nodes, including text nodes, but excluding phantom nodes
$.fn.allChildren = function()
{
    var result = new Array();

    if (this.length != 0)
    {
        for (var n=this.get(0).firstChild; n!=null; n=n.nextSibling)
        {
            if (!isPhantomNode(n))
                result.push(n);
        }
    }
    return $(result);
}



$.fn.isEmptyParagraph = function()
{
        if (this[0].nodeType == 3)
            return false;

        if (this.length == 1 && this[0].nodeName == "P")
        {
            var children = this.allChildren();
            if (children.length == 1 && children[0].nodeName == "BR")
                return true;
        }

        return false;
}



// Returns true if this node has a text node inside
// NOTE If you want to use this function to determine if a node can be deleted,
// please use the function "hasNoInformation()", as this one also checks for
// tags (like <img/>) which contain information
$.fn.hasTextNode = function()
{
    var allChildren = this.allChildren();

    var hasTextNode = false;
    for (var i = 0; allChildren[i]!=null && !hasTextNode; i++)
    {
        if (allChildren[i].nodeType == 3)
            hasTextNode = true;
        else if (allChildren[i].hasChildNodes())
            hasTextNode = $(allChildren[i]).hasTextNode();
    }
    return hasTextNode;
}


// Returns true if a textnode doesn't contain any information/contents and can
// therefore be deleted
$.fn.hasNoInformation = function()
{
    var allChildren = this.allChildren();

    if (allChildren.length == 0)
        return null;

    var hasNoInfo = true;
    for (var i = 0; allChildren[i]!=null && hasNoInfo; i++)
    {
        if (allChildren[i].nodeType == 3)
            hasNoInfo = false;
        else if (allChildren[i].nodeType == 1
            && allChildren[i].nodeName == "IMG")
            hasNoInfo = false;
        else if (allChildren[i].hasChildNodes())
            hasNoInfo = $(allChildren[i]).hasNoInformation();
    }

    return hasNoInfo;
}



$.fn.replace = function(o) {
    var n = this.get(0);
//    return this.get(0).parentNode.replaceChild($(o).get(0), this.get(0));
    return n.parentNode.replaceChild($(o).get(0), n);
};


Wymeditor.prototype.exec = function(cmd, val) {
        if (val === undefined) {
                /* there arent any arguments */
                val = null;
        }


/* formatblock commands */
//<P>	<H1>	<H2>	<H3>	<H4>	<H5>	<H6>	<PRE>	<ADDRESS>
        var formatblocks = new Array('P', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'PRE', 'ADRESS');
        if (formatblocks.contains(cmd))
        {
            val = cmd;
            cmd = 'formatblock';
        }

	if(jQuery.browser.mozilla) this.doc.execCommand(cmd,'',val);
	else if(jQuery.browser.msie || jQuery.browser.opera) this.doc.execCommand(cmd, false, val);
}

Wymeditor.prototype.selectedContainer = function() {
	if(jQuery.browser.mozilla || jQuery.browser.opera) {
		var sel=this.iframe.contentWindow.getSelection();
		var node=sel.focusNode;
		if(node.nodeName=="#text")return(node.parentNode);
		else return(node);
	}
	else if(jQuery.browser.msie) {
		var caretPos=this.iframe.caretPos;
    		if(caretPos!=null) {
			if(caretPos.parentElement!=undefined)return(caretPos.parentElement());
		}
	}
}

//EVENTS

Wymeditor.prototype.contextmenuHandler = function(evt) {
	evt.stopPropagation();
	var index = this.title;
	var box = wym_instances[index].box;
	
	$(box).find("div.wym_menu").show();
}


Wymeditor.prototype.clickHandler = function(evt) {
    alert("aha");
}


//MSIE RELATED

Wymeditor.prototype.saveCaret = function() {
	this.iframe.caretPos=document.selection.createRange();
}

//GLOBAL
var wym_instances = new Array();




// from http://forum.de.selfhtml.org/archiv/2004/3/t76079/#m438193 (2007-02-06)
Array.prototype.contains = function (elem) {
//  var i;
  for (var i = 0; i < this.length; i++) {
    if (this[i] === elem) {
      return true;
    }
  }
  return false;
};


