/*
 * Copyright 2005 Tridium, Inc. All Rights Reserved.
 */

/**
 * RichTextEditor javascript support.
 *
 * @author    Andy Frank
 * @creation  20 Sep 05
 * @version   $Revision$ $Date$
 * @since     Baja 1.0
 */

var rte = new RichTextEditor();
function RichTextEditor()
{
////////////////////////////////////////////////////////////////
// Attributes
////////////////////////////////////////////////////////////////
  
  var contentId = null;
  var editorId = null;    
  var win = null;
  var doc = null;
  var initialContent = "";
  
////////////////////////////////////////////////////////////////
// Lifecycle
////////////////////////////////////////////////////////////////
  
  /**
   * Set the content the RTE will initally contain.  This 
   * must be called before init() is called.
   */
  this.setInitialContent = function(_initialContent)
  {
    initialContent = _initialContent;
  }
  
  /**
   * Initialize the RTE.
   */
  this.init = function(_contentId, _editorId)
  {
    // Cache values
    contentId = _contentId;
    editorId = _editorId;

    var iframe = document.getElementById(editorId);
    win = iframe.contentWindow;
    doc = iframe.contentWindow.document;      

    // Register our keyboard event handler
    if (hx.ie)
      doc.attachEvent("onkeypress", this.handleKeyPress);
    else
      doc.addEventListener("keypress", this.handleKeyPress, true);
   
    // Turn on editing mode
    doc.designMode = "on";
    
    // Set our initial content for editor
    if (hx.ie)
    {
      var html = "<html>";
      html += "<head></head>";
      html += "<body style='font:10pt Verdana;'>";
      html += initialContent
      html += "</body>";
      html += "</html>";
      doc.write(html);
    }
    else
    {
      doc.rte = rte;
      doc.body.style.font = "10pt Verdana";
      doc.body.innerHTML = initialContent;
      
      try
      {
        // IE can only use HTML to style text, so make 
        // Mozilla do the same for consistency.        
        doc.execCommand("useCSS", false, true); // old moz call  
        doc.execCommand("styleWithCSS", false, false); // new moz call     
      }
      catch (e) {} // ignore
    }    
  }
  
  /**
   * Save contents of editor into a hidden form field.
   */
  this.save = function()
  {
    var content = new HtmlToXml().toXml(doc.body, false);
    hx.setFormValue(contentId, content);
  }
     
////////////////////////////////////////////////////////////////
// Methods
////////////////////////////////////////////////////////////////

  /**
   * Invoke the given command.
   */
  this.invoke = function(name, params)
  {
    win.focus();
    doc.execCommand(name, false, params);
    win.focus();
  }
  
  /**
   * Wrap the currently selected text with a hyperlink.   
   */
  this.createHyperlink = function()
  {
    // Check that we have something selected first
    var sel = "";
    if (hx.ie)
    {
      var range = doc.selection.createRange();
      sel = range.text;
    }
    else
    {
      sel = win.getSelection().toString();
    }
    
    if (StringUtil.trim(sel).length == 0)
    {
      alert("First select the text you want to make into a link.");
      return;
    }
    
    // Prompt user for URL
    var url = prompt("Enter a URL:", "http://");    
    if (url == null) return;
    
    // Create link
    this.invoke("createlink", url);
  }  
  
////////////////////////////////////////////////////////////////
// Events
////////////////////////////////////////////////////////////////

  /**
   * Handle keyboard events.
   */
  this.handleKeyPress = function(event)
  {
    // Command to invoke, or null if none
    var cmd = null;
    
    // Look for a command with this key combo
    if (event.ctrlKey)
    {
      var key = String.fromCharCode(event.charCode).toLowerCase();      
      switch (key) 
      {
        case 'b': cmd = "bold"; break;
        case 'i': cmd = "italic"; break;
        case 'u': cmd = "underline"; break;        
      }
    }
    
    // Check if we have a command to invoke
    if (cmd != null) 
    {
      rte.invoke(cmd, null);
     
      // Stop the event bubble
      event.preventDefault();
      event.stopPropagation();
    }
  }
  
////////////////////////////////////////////////////////////////
// Spell Checking
////////////////////////////////////////////////////////////////
  
  /**
   * Check the spelling for the current editor contents..
   */
  this.checkSpelling = function()
  {
    // Save the plain content for spell checking
    var plain = rte.getPlainText(doc.body);
    hx.setFormValue(contentId + ".plain", plain);
  }
  
  /**
   * Get the text content of the RTE, removing all HTML markup.
   */
  this.getPlainText = function(node)
  {
    var text = "";
    
    if (node.nodeType == 3) // TEXT_NODE
    {
      text = node.nodeValue;
      text = text.replace(/\u00A0/g, " ");
      // TODO - do we need to do anything else here?  Is this
      // going to mess up our offsets?
    }
    
    if (node.childNodes)
      for (var i=0; i<node.childNodes.length; i++)
        text += rte.getPlainText(node.childNodes[i]);
        
    return text;
  }
  
  /**
   * Handle spell check response.
   */
  this.doCheckSpelling = function(errs)
  {
    // If no errors, just notify user, then done
    if (errs.length == 0)
    {
      alert("No spelling errors found.");
    }
    else
    {
      // reset indices
      index = 0;
      error = 0;
      errors = errs;
      paintWords(doc.body);
    }
  }  
  
  var index = 0; // Index of text
  var error = 0; // Current error index
  var errors = null;  
  function paintWords(node)
  { 
    // No more errors, bail out
    if (error == errors.length) return; 
    
    if (node.nodeType == 3) // TEXT_NODE
    {
      var text = node.nodeValue;
      var origLen = text.length;
      var currOff = 0;
      var newText = "";
            
      var e = errors[error];
      while (error < errors.length && e.pos >= index && e.pos < index + origLen)
      {
        var start = e.pos - index;
        var replace = text.substring(currOff, start);
        currOff += replace.length + e.word.length;
        
        replace += "<span name='ignore' style='cursor: pointer;";
        replace += " border-bottom:1px dotted red;'";
        replace += " onclick='document.rte.suggest(event, " + error + ");'>";
        replace += e.word;
        replace += "</span>";
        
        newText += replace;
        
        error++;
        e = errors[error];
      }
      
      // If we modified node, replace it with new markup
      if (newText.length > 0)
      {
        // Append any remaing text
        if (currOff < origLen) 
          newText += text.substring(currOff);
      
        var newChild = doc.createElement("span");
        newChild.innerHTML = newText;        
        node.parentNode.replaceChild(newChild, node);
      } 
      
      index += origLen;
    }
    
    // Walk children
    if (node.childNodes)
      for (var i=0; i<node.childNodes.length; i++)
        text += paintWords(node.childNodes[i]);
  }
  
  /**
   * Show the suggestions for this mispelt word.
   */
  this.suggest = function(event, index)
  {
    var err = errors[index];
    var mx = 0;
    var my = 0;
    
    if (!event.target)
    {
      mx = event.clientX + document.body.scrollLeft;
      my = event.clientY + document.body.scrollTop;  
    }
    else
    {
      mx = event.pageX;
      my = event.pageY;
    }
    
    var iframe = document.getElementById(editorId);
    //mx += iframe.offsetLeft;
    my += iframe.offsetTop;

    var div = document.createElement("div");
    div.style.position = "absolute";
    div.style.left = mx + "px";
    div.style.top  = my + "px";
    div.style.background = "#ccc";
    div.style.border = "1px solid black";
    
    if (err.sug.length == 0)
    {
      var row = document.createElement("div");
      row.style.padding = "5px;";
      row.innerHTML = "No suggestions";
      div.appendChild(row);
    }
    
    for (var i=0; i<err.sug.length; i++)
    {
      var row = document.createElement("div");
      row.style.padding = "5px;";
      row.innerHTML = err.sug[i];
      div.appendChild(row);
    }
  
    var row = document.createElement("div");
    row.style.padding = "5px;";
    row.innerHTML = "Edit...";
    div.appendChild(row);
    
    document.body.appendChild(div);    
  }
}

      
