/**
*   WYSIWYG_Editor
*
*   Class constructor. Configures and displays the editor object according to values passed
*   This is an implementation of OO-Javascript based on my previous editor.
*
*   @param  instance_name   string  the name of the variable you assigned to this instance ( example: myEdt = new WYSIWYG_Editor('myEdt'); )
*                                   also used as the basis for the name and id attributes for this editor instance in the HTML (hidden input and iframe)
*   @param  content         string  a string of the content to display in the editor.
*   @param  path            string  the URI path to this directory to use for editor components (pallete, iamges, etc.)
*   @param  fwidth          int     the width in pixels of the editor interface on the screen
*   @param  fheight         int     the height in pixels of the editor interface on the screen
*   @param  styleHref       string  the URI of the stylesheet to use for the editor's content window
*   @param  spellCheckPath  string  the URI of the spellerpages install
**/
function WYSIWYG_Editor(instance_name, content, path, fwidth, fheight, styleHref, spellCheckPath){
    // for each of the passed parameters, we need to be sure they are defined, or we will use a default value

    // the name used to create the object - used to define the name of the field that contains the HTML on submission
    if(typeof(instance_name)=='undefined'){
        alert("ERROR: No instance name was passed for the editor.");
        return false;
    }else{
        this.instance_name=instance_name;
    }

    // the initial HTML source content for the editor
    if(typeof(content)=='undefined'){
        this.content='';
    }else{
        this.content=content;
    }

    // define the path to use for the editor components like images
    if(typeof(path)=='undefined'){
        this.wysiwyg_path='.'; // default value
    }else{
        path.replace(/[\/\\]$/,''); // remove trailing slashes
        this.wysiwyg_path=path;
    }

    // define the pixel dimensions of the editor
    if(typeof(fwidth)=='number' && Math.round(fwidth) > 50){
        this.frame_width=Math.round(fwidth); // default value
    }else{
        this.frame_width=548; // default width
    }
    if(typeof(fheight)=='number' && Math.round(fheight) > 50){
        this.frame_height=Math.round(fheight);
    }else{
        this.frame_height=250; // default height
    }

    // define the stylesheet to use for the editor components like images
    if(typeof(styleHref)=='undefined'){
        this.stylesheet=''; // default value
    }else{
        this.stylesheet=styleHref;
    }

    if(typeof(spellCheckPath)=='undefined'){
        this.spell_path=''; // default value
    }else{
        // show spell check button requires Speller Pages (http://spellerpages.sourceforge.net/)
        this.spell_path=spellCheckPath;
    }

    // properties that depended on the validated values above
    this.wysiwyg_content='rtf_WYSIWYG_Editor';  // the editor IFRMAE element id
    this.wysiwyg_hidden='rtf_content';          // the editor's hidden field to store the HTML in for the post
    this.ta_rows=Math.round(this.frame_height/15);              // number of rows for textarea for unsupported browsers
    this.ta_cols=Math.round(this.frame_width/8);                // number of cols for textarea for unsupported browsers

    // other property defaults
    this.viewMode=1;                                        // by default, set to design view
    this._X = this._Y = 0;                                  // these are used to determine mouse position when clicked

    // the folloing properties are safe to set through the object variable, for example:
    //  var editor = new WYSIWYG_Editor('editor');
    //  editor.allow_mode_toggle = false;
    // below are just the defaults that I use most of the time

    // these are used in my custom CMS
    this.web_toolbar4 = true;                               // show insert image and save buttons
    this.imagePopUp = null;                                 // used in the insertImage and addImage methods
    this.site_path = this.wysiwyg_path;                     // default to what was passed, should be set in HTML
    this.page_title = 'index';                              // default to nothing, should be set in HTML
}

/**
*   WYSIWYG_Editor::display
*
*   Display the editor interface for the user
**/
WYSIWYG_Editor.prototype.display = function (){
    document.getElementById('rtf_content').value=this.content;
		if(this.isSupported()){
			var thedoc = document.getElementById(this.wysiwyg_content).contentWindow.document;
			thedoc.designMode='On';
			// MSIE has caching problems...
			// http://technet2.microsoft.com/WindowsServer/en/Library/8e06b837-0027-4f47-95d6-0a60579904bc1033.mspx
			thedoc = document.getElementById(this.wysiwyg_content).contentWindow.document;
			thedoc.open();
			thedoc.write('<html><head>');
			if(this.stylesheet){
					// must be done after the document has been opened
					thedoc.write('<style type="text/css">@import url('+this.stylesheet+');</style>');
			}
			thedoc.write('</head><body>');
			thedoc.write(this.content);
			thedoc.write('</body></html>');
			thedoc.close();
		}
}


/**
*   WYSIWYG_Editor::doTextFormat
*
*   Apply a text formatting command to the selected text in the editor (or starting at the current cursor position)
*
*   @param  command string  Which of the editor/browser text formatting commands to apply
**/
WYSIWYG_Editor.prototype.doTextFormat = function (command, optn, evnt){
    if((command=='forecolor') || (command=='hilitecolor')){
        this.getPallete(command, optn, evnt);
    }else if(command=='createlink'){
        var szURL=prompt('Enter a URL:', '');
        if(document.getElementById(this.wysiwyg_content).contentWindow.document.queryCommandEnabled(command)){
            document.getElementById(this.wysiwyg_content).contentWindow.document.execCommand('CreateLink',false,szURL);
            return true;
        }else return false;
    }else{
        if(document.getElementById(this.wysiwyg_content).contentWindow.document.queryCommandEnabled(command)){
              document.getElementById(this.wysiwyg_content).contentWindow.document.execCommand(command, false, optn);
              return true;
          }else return false;
    }
    document.getElementById(this.wysiwyg_content).contentWindow.focus();
}


/**
*   WYSIWYG_Editor::toggleMode
*
*   Toggles between design view and source view in the IFRAME element
**/
WYSIWYG_Editor.prototype.toggleMode = function (){
    // change the display styles
    if(this.viewMode == 2){
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontFamily = '';
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontSize = '';
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.color = '';
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontWeight = '';
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.backgroundColor = '';
        document.getElementById(this.instance_name+'_toolbars').style.visibility='visible';
    }else{
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontFamily = 'monospace';
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontSize = '10pt';
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.color = '#000';
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.backgroundColor = '#fff';
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.style.fontWeight = 'normal';
        document.getElementById(this.instance_name+'_toolbars').style.visibility='hidden';
    }

    // do the content swapping
    if(this.isMSIE()){
        this._toggle_mode_ie();
    }else{
        this._toggle_mode_gecko();
    }
}

/**
*   WYSIWYG_Editor::_toggle_mode_ie
*
*   Toggles between design view and source view in the IFRAME element for MSIE
**/
WYSIWYG_Editor.prototype._toggle_mode_ie = function (){
    if(this.viewMode == 2){
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML = document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerText;
        document.getElementById(this.wysiwyg_content).contentWindow.focus();
        this.viewMode = 1; // WYSIWYG
    }else{
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerText = document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML;
        document.getElementById(this.wysiwyg_content).contentWindow.focus();
        this.viewMode = 2; // Code
    }
}

/**
*   WYSIWYG_Editor::_toggle_mode_gecko
*
*   Toggles between design view and source view in the IFRAME element for Gecko browsers
**/
WYSIWYG_Editor.prototype._toggle_mode_gecko = function (){
    if(this.viewMode == 2){
        var html = document.getElementById(this.wysiwyg_content).contentWindow.document.body.ownerDocument.createRange();
        html.selectNodeContents(document.getElementById(this.wysiwyg_content).contentWindow.document.body);
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML = html.toString();
        document.getElementById(this.wysiwyg_content).contentWindow.focus();
        this.viewMode = 1; // WYSIWYG
    }else{
        var html = document.createTextNode(document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML);
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML = '';
        document.getElementById(this.wysiwyg_content).contentWindow.document.body.appendChild(html);
        document.getElementById(this.wysiwyg_content).contentWindow.focus();
        this.viewMode = 2; // Code
    }
}

/**
*   WYSIWYG_Editor::_get_offset_left
*
*   Used to define position of pop-up pallete window in Gecko browsers
**/
WYSIWYG_Editor.prototype._get_offset_left = function (elm){
    var mOffsetLeft=elm.offsetLeft;
    var mOffsetParent=elm.offsetParent;
    while(mOffsetParent){
        mOffsetLeft += mOffsetParent.offsetLeft;
        mOffsetParent=mOffsetParent.offsetParent;
    }
    return mOffsetLeft;
}

/**
*   WYSIWYG_Editor::_get_offset_top
*
*   Used to define position of pop-up pallete window in Gecko browsers
**/
WYSIWYG_Editor.prototype._get_offset_top = function (elm){
    var mOffsetTop=elm.offsetTop;
    var mOffsetParent=elm.offsetParent;
    while(mOffsetParent){
        mOffsetTop += mOffsetParent.offsetTop;
        mOffsetParent=mOffsetParent.offsetParent;
    }
    return mOffsetTop;
}

/**
*   WYSIWYG_Editor::insertTable
*
*   Used for inserting a table into the IFRAME content window
**/
WYSIWYG_Editor.prototype.insertTable = function (){
    colstext = prompt('Enter the number of columns per row.');
    rowstext = prompt('Enter the number of rows to create.');
    rows = parseInt(rowstext,'0');
    cols = parseInt(colstext,'0');

    if(this.isMSIE()){
        return this._insert_table_ie(cols,rows);
    }else{
        return this._insert_table_gecko(cols,rows);
    }
}

/**
*   WYSIWYG_Editor::_insert_table_ie
*
*   This is the browser engine-specific code for inserting a table for MSIE browsers.
*
*   @param  cols    The number of columns to create
*   @param  rows    The number of rows to create
**/
WYSIWYG_Editor.prototype._insert_table_ie = function (cols, rows){
    document.getElementById(this.wysiwyg_content).contentWindow.focus();

    //get current selected range
    var cursor=document.getElementById(this.wysiwyg_content).contentWindow.document.selection.createRange();
    if((rows > 0) && (cols > 0)){
        var tableHTML = '<table border="'+this.table_border+'" cellpadding="'+this.table_cell_padding+'" cellspacing="'+this.table_cell_spacing+'">';
        while(rows>0){
            rows--;
            var rowCols = cols;
            tableHTML = tableHTML + '<tr>';
            while(parseInt(rowCols)>0){
                rowCols--;
                tableHTML=tableHTML+'<td>&nbsp;</td>';
            }
            tableHTML = tableHTML + '</tr>';
        }
        tableHTML = tableHTML + '</table>';
        cursor.pasteHTML(tableHTML);
        document.getElementById(this.wysiwyg_content).contentWindow.focus();
    }
    return true;
}

/**
*   WYSIWYG_Editor::_insert_table_gecko
*
*   This is the browser engine-specific code for inserting a table for Gecko browsers.
*
*   @param  cols    The number of columns to create
*   @param  rows    The number of rows to create
**/
WYSIWYG_Editor.prototype._insert_table_gecko = function (cols, rows){
    contentWin=document.getElementById(this.wysiwyg_content).contentWindow;
    if((rows > 0) && (cols > 0)){
        table=contentWin.document.createElement('table');
        table.setAttribute('border', this.table_border);
        table.setAttribute('cellpadding', this.table_cell_padding);
        table.setAttribute('cellspacing', this.table_cell_spacing);
        tbody=contentWin.document.createElement('tbody');
        for(i=0;i<rows;i++){
            tr=contentWin.document.createElement('tr');
            for(j=0;j<cols;j++){
                td=contentWin.document.createElement('td');
                br=contentWin.document.createElement('br');
                td.appendChild(br);
                tr.appendChild(td);
            }
            tbody.appendChild(tr);
        }
        table.appendChild(tbody);
        this._insert_element_gecko(contentWin, table);
    }
    return true;
}

/**
*   WYSIWYG_Editor::_insert_element_gecko
*
*   Used by Gecko browsers to insert elements into the document for the insertTable method
*
*   @param  win The window object to insert the element into
*   @param  elem    The element to insert into the content window
**/
WYSIWYG_Editor.prototype._insert_element_gecko = function (win, elem){
    var sel = win.getSelection();           // get current selection
    var range = sel.getRangeAt(0);          // get the first range of the selection (there's almost always only one range)
    sel.removeAllRanges();                  // deselect everything
    range.deleteContents();                 // remove content of current selection from document
    var container = range.startContainer;   // get location of current selection
    var pos = range.startOffset;
    range=document.createRange();           // make a new range for the new selection

    if (container.nodeType==3 && elem.nodeType==3) {
        // if we insert text in a textnode, do optimized insertion
        container.insertData(pos, elem.nodeValue);

        // put cursor after inserted text
        range.setEnd(container, pos+elem.length);
        range.setStart(container, pos+elem.length);
    }else{
        var afterNode;
        if (container.nodeType==3) {
          // when inserting into a textnode we create 2 new textnodes and put the elem in between
          var textNode = container;
          container = textNode.parentNode;
          var text = textNode.nodeValue;

          var textBefore = text.substr(0,pos);  // text before the split
          var textAfter = text.substr(pos);     // text after the split

          var beforeNode = document.createTextNode(textBefore);
          var afterNode = document.createTextNode(textAfter);

          // insert the 3 new nodes before the old one
          container.insertBefore(afterNode, textNode);
          container.insertBefore(elem, afterNode);
          container.insertBefore(beforeNode, elem);

          // remove the old node
          container.removeChild(textNode);
        }else{
          // else simply insert the node
          afterNode = container.childNodes[pos];
          container.insertBefore(elem, afterNode);
        }
    }
}

/**
*   WYSIWYG_Editor::setColor
*
*   Used to set the text or highlight color of the selected text in Gecko engine browsers
**/
WYSIWYG_Editor.prototype.setColor = function (color, command){
    // close the window we made to display the pallete in
    if(typeof(pwin)=='object'){ // make sure it exists
        if(!pwin.closed){ // if it is still open
            pwin.close();
        }
    }

    // only one difference for MSIE
    if(this.isMSIE() && command == 'hilitecolor') command = 'backcolor';

    //get current selected range
    var sel=document.getElementById(this.wysiwyg_content).contentWindow.document.selection;
    if(sel!=null){
        rng=sel.createRange();
    }

    document.getElementById(this.wysiwyg_content).contentWindow.focus();
    if(document.getElementById(this.wysiwyg_content).contentWindow.document.queryCommandEnabled(command)){
        document.getElementById(this.wysiwyg_content).contentWindow.document.execCommand(command, false, color);
    }else return false;
    document.getElementById(this.wysiwyg_content).contentWindow.focus();
    return true;
}

/**
*   WYSIWYG_Editor::getPallete
*
*   Apply a text color to selected text or starting at current position
*
*   @param  command string  Used to determine which pallete pop-up to display
**/
WYSIWYG_Editor.prototype.getPallete = function (command, optn, evnt) {
    // get the pallete HTML code
    html = this._get_palette_html(command);

    // close the window we made to display the pallete in
    // this is in case someone clicked the hilitecolor, then clicked the forcolor button
    // without making a choice
    if(typeof(pwin)=='object'){ // make sure it exists
        if(!pwin.closed){ // if it is still open
            pwin.close();
        }
    }

    // OK, now I need to open a new window to capture a click from
    pwin = window.open('','colorPallete','dependent=yes, directories=no, fullscreen=no,hotkeys=no,height=120,width=172,left='+evnt.screenX+',top='+evnt.screenY+',locatoin=no,menubar=no,resizable=no,scrollbars=no,status=no,titlebar=no,toolbar=no');
    pwin.document.write(html);
    pwin.document.close(); // prevents page from attempting to load more code
}

/**
*   WYSIWYG_Editor::isSupported
*
*   Checks that the browser supports this programming by writing an invisible IFRAME and testing its properties
*/
WYSIWYG_Editor.prototype.isSupported = function () {
    // This is to get rid of the browser UA check that was previously implemented for this class.
    // should be called from somewhere in the body of the document for best results
    document.write('<iframe id="WYSIWYG_Editor_Testing_Browser_Features" style="display: none; visibility: hidden;"></iframe>');
    test = typeof(document.getElementById('WYSIWYG_Editor_Testing_Browser_Features').contentWindow);
    if(test == 'object'){
        return true;
    }else{
        return false;
    }
    return this.supported;
}

/**
*   WYSIWYG_Editor::isMSIE
*
*   Checks if browser is MSIE by testing the document.all property that is only supported by MSIE and AOL
*/
WYSIWYG_Editor.prototype.isMSIE = function (){
    if(typeof(document.all)=='object'){
        return true;
    }else{
        return false;
    }
}

/**
*   WYSIWYG_Editor::prepareSubmit
*
*   Use this in the onSubmit event for the form that the editor is displayed inside.
*   Puts the HTML content into a hidden form field for the submission
**/
WYSIWYG_Editor.prototype.prepareSubmit = function (){
    if(this.viewMode == 2){
        // be sure this is in design view before submission
        this.toggleMode();
    }
    var htmlCode=document.getElementById(this.wysiwyg_content).contentWindow.document.body.innerHTML;
    document.getElementById(this.wysiwyg_hidden).value=htmlCode;
    return true;
}
