/**
 *$Id: minimal.js 5251 2006-11-21 21:41:56Z jcastro $
 * @fileoverview Basic Rich Text editor (see Midas specification)
 * This file provides functionality to create a new editor out of a given
 * container id and adjust various styles and actual behavior in design mode
 *
 * @link http://www.mozilla.org/editor/midas-spec.html
 *
 * <pre>
 * Copyright (c) 2006 by SouthProjects
 * http://www.southprojects.com
 * Santiago, Chile,
 * Version 1.2 Dec 2006.
 * All rights reserved.
 * </pre>
 */

/**
 * southprojects.MinimalEditor constructor. Creates a new editor object with given
 * parameters.
 *
 * @constructor
 * @extends southprojects.Widget
 * @param {object} objArgs Editor configuration
 *
 * Constructor recognizes the following properties of the config object
 * \code
 *	property name			| description
 *-------------------------------------------------------------------------------------------------
 *	field			| [string or object] Reference to DOM element to be used as
 *           | prototype for the editor. The editor pane will be created right
 *           | after this element and will take its size.
 * toolbarElements | [array] Array of strings representing toolbar buttons to
 *           | be added to the editor toolbar. The buttons will be added to
 *           | the toolbar in the order they are specified in this array.
 * maximizedBorder | [number] A margin between the editor and the browser window
 *           | border when in maximized mode (pixels)
 * \endcode
 *
 * Constructor recognizes the following strings as elements of toolbarElements
 * array
 * \code
 *	toolbar element name		| description
 *-------------------------------------------------------------------------------------------------
 *	maximize          | A toggle button for maximizing/restoring the editor to full page boundaries
 *	fontName					| A font name select field
 *	fontSize     			| A font size select field
 *	foreColor					| A font color picker button
 *	backColor				  | A background color picker button
 *	insertLink			  | A button for inserting hyperlinks
 *	insertImage			  | A button for inserting images
 *	insertTable			  | A button for inserting tables
 *	insertHorizontalRule | A button for inserting horizontal rules
 *	insertSpecialChar | A button for showing a character map window for selecting a special character to insert
 *	bold		      	  | A button for toggling bold style on selected text
 *	italic			      | A button for toggling italic style on selected text
 *	underline			    | A button for toggling underline style on selected text
 *	justifyLeft		    | A button for making paragraphs left justified
 *	justifyCenter	    | A button for making paragraphs centered
 *	justifyRight	    | A button for making paragraphs right justified
 *	insertOrderedList | A button for inserting ordered numbered lists
 *	insertUnorderedList | A button for inserting unordered bulleted lists
 *	selectAll         | A button for selecting all editor content
 *	copy              | A copy button
 *	cut               | A cut button
 *	paste             | A paste button
 *	outdent           | A button for decreasing indentation
 *	indent            | A button for increasing indentation
 *	undo              | An undo button
 *	redo              | A redo button
 *	about             | About this editor button
 *	browser           | A button for previewing editor html content in a new browser window
 *	switcher          | A toggle button to switch between HTML and WYSIWYG mode
 *	fetch             | A button for loading editor html content from a remote location
 *	save              | A button for sending editor html content to a remote location
 *	newPanel          | This element will make the next button appear in
 *                    | a new toolbar panel
 *	newRow            | This element will make the next button appear in
 *                    | a new row
 * \endcode
 *
 */
southprojects.MinimalEditor = function(objArgs){
	if(arguments.length == 0){
		objArgs = {};
	}

	// Call constructor of superclass
	southprojects.MinimalEditor.SUPERconstructor.call(this, objArgs);
}

/**
 * Unique static id of the widget class. Gives ability for southprojects#inherit to
 * determine and store path to this file correctly when it is included using
 * southprojects#include. When this file is included using southprojects#include or path
 * to this file is gotten using southprojects#getPath, this value must be specified
 * as script id.
 * @private
 */
southprojects.MinimalEditor.id = 'southprojects.MinimalEditor';

// Inherit southprojects.Widget
southprojects.inherit(southprojects.MinimalEditor, southprojects.Widget);

// Include required scripts
southprojects.Transport.include(southprojects.southprojectsPath + "button.js");
southprojects.Transport.include(southprojects.southprojectsPath + "pane.js");
southprojects.Transport.include(southprojects.southprojectsPath + "tooltips.js");

/**
 * Initializes object.
 *
 * @param {object} objArgs User configuration
 */
southprojects.MinimalEditor.prototype.init = function(objArgs){

	// processing Widget functionality
	southprojects.MinimalEditor.SUPERclass.init.call(this, objArgs);

  this.createEditor();

	this.initEditor();

  // set current mode to WYSIWYG(visual formatting)
  this.mode = "WYSIWYG";
}

/**
 * Creates editor DOM elements
 * @private
 */
southprojects.MinimalEditor.prototype.createEditor = function() {
  southprojects.Utils.createProperty(this, 'container', null);
  southprojects.Utils.createProperty(this, 'editorPanel', null);
  southprojects.Utils.createProperty(this, 'buttons', []);
  // Hides native controls that are under the editor when it is maximized
  southprojects.Utils.createProperty(this, 'wch', null);

  // Get original textarea field dimensions
  var fieldWidth = this.config.field.clientWidth;
  var fieldHeight = this.config.field.clientHeight;

  // Creare a container element
  this.container = southprojects.Utils.createElement("div");
  // Add container element after the original textarea
  southprojects.Utils.insertAfter(this.config.field, this.container);
  // Copy styles from textarea to our main container
  if (!southprojects.is_opera) {
    this.container.style.cssText = this.config.field.style.cssText;
  } else {
    for(var i in this.config.field.style) {
      try {
        this.container.style[i] = this.config.field.style[i];
      } catch (e) {}
    }
  }
  this.config.field.style.position = "absolute";
  this.config.field.style.left = "";
  this.config.field.style.top = "";

  // Creare an editor panel element
  this.editorPanel = document.createElement("div");
  this.editorPanel.position  = "relative";
  
  // Add editor panel to the container
  this.container.appendChild(this.editorPanel);
  // Set editor panel style class
  this.editorPanel.className = this.getClassName({prefix: "speditor",
          suffix: "EditorPanel"
  });

  // Remove textarea
  this.config.field.parentNode.removeChild(this.config.field);
  // Add textarea inside our container
  this.editorPanel.appendChild(this.config.field);

  // Set textfield style class
  this.config.field.className = this.getClassName({prefix: "speditor",
          suffix: "Textarea"
  });

  // Create WCH
  this.wch = southprojects.Utils.createWCH(this.editorPanel);
  // Put WCH under container
  if (this.wch) {
    this.wch.style.zIndex = -1;
  }

  // Add toolbar buttons
  this.addButtons();

  // Get iframe pane dimensions
  var paneSize = this.getPaneSize(fieldWidth, fieldHeight);

  // Create editor pane
  this.pane = new southprojects.Pane({
    containerType: 'iframe',
    width: paneSize.width,
    height: paneSize.height
  })
  // Remove iframe border
  this.pane.removeBorder();
  // Set editor pane style class
  this.pane.getContainer().className = this.getClassName({prefix: "speditor",
          suffix: "Pane"
  });

  // Set editor dimenisons
  this.setSize(fieldWidth, fieldHeight);

  // Move iframe to the editor panel just after our text area
  this.pane.getContainer().parentNode.removeChild(this.pane.getContainer());
  southprojects.Utils.insertAfter(this.config.field, this.pane.getContainer());

  // Hide html text area
  this.config.field.style.display = "none";

  // Show loading progress until pane is ready
  this.showLoader("loading editor");

  // Disable toolbar buttons
  this.toggleButtons(false);
}

/**
 * Configures the widget. Gets called from init and reconfigure methods of
 * superclass.
 *
 * @private
 * @param {object} objArgs User configuration
 */
southprojects.MinimalEditor.prototype.configure = function(objArgs) {
  this.defineConfigOption('field');
  this.defineConfigOption('asyncTheme', true);
  this.defineConfigOption('maximizedBorder', 6);
  this.defineConfigOption('toolbarElements', [
    'maximize',
    'newPanel',

    'fontName',
    'fontSize',
    'newPanel',

    'bold',
    'italic',
    'underline',
    'newPanel',

    'foreColor',
    'backColor',
    'insertLink',
    'insertImage',
    'insertTable',
    'insertHorizontalRule',

    'newRow',

    'justifyLeft',
    'justifyCenter',
    'justifyRight',
    'newPanel',

    'insertOrderedList',
    'insertUnorderedList',
    'newPanel',

    'selectall',
    'copy',
    'cut',
    'paste',
    'newPanel',

    'outdent',
    'indent',
    'newPanel',

    'undo',
    'redo',
    'newPanel',

    'switcher',
    'newPanel',

    'about'
  ]);

  this.defineConfigOption('persistKey', null);
  this.defineConfigOption('persistPath', null);

  this.defineConfigOption('externalBrowserWidth', 450);
  this.defineConfigOption('externalBrowserHeight', 350);

  // Call parent method
  southprojects.MinimalEditor.SUPERclass.configure.call(this, objArgs);

  // Check if required param "field" is defined
  if(typeof(this.config.field) == "undefined"){
    southprojects.Log({description: "No reference to target textarea!"})
    return false;
  }

  // if required param "field" is a string
  if(typeof(this.config.field) == "string"){
    // Get DOM element with specified id
    this.config.field = document.getElementById(this.config.field);
  }

  // Check if a DOM element was found for "field" and it's text area
  if(this.config.field == null ||
    this.config.field.nodeType != 1 ||
    this.config.field.nodeName.toLowerCase() != "textarea"){
    return null;
  }

  // Change all toobar element names to lower case
  for(var ii = 0; ii < this.config.toolbarElements.length; ii++){
    this.config.toolbarElements[ii] = this.config.toolbarElements[ii].toLowerCase();
  }

};

/**
 * Turn on design mode for the editor iframe pane
 * @private
 */
southprojects.MinimalEditor.prototype.initEditor = function(){
	var self = this;

	if(!this.pane.isReady()){
		setTimeout(function(){self.initEditor()}, 50);
		return null;
	}

  // Revalidate editor size
  self.setSize();

  // Handly Enter key manually in IE to write <br> instead of <p>
  if(southprojects.is_ie){
    this.pane.getContentElement().onkeydown = function(event) {

      if (null == event) {
        event = self.pane.getContainer().contentWindow.event;
      }

      var iKC = event.keyCode;

      // If Enter is pressed
      if (iKC == 13) {

        // Handle enter key
        var isHandled = self.onEnterPressed();
        if (isHandled) {

          // Prevent default handling
          event.cancelBubble = true;
          event.returnValue = false;

          return false;
        }
      }

      return true;
    }

    this.pane.getContentElement().onbeforedeactivate = function() {
      var sel = self.pane.getContainer().contentWindow.document.selection;
      if (sel) {

        // Focus editor
        var range = sel.createRange();

        self.oldSelectionRange = range;
      }
    }
  }

  // on each keyup event write value to original field
	southprojects.Utils.addEvent(this.pane.getIframeDocument(), "keyup",
          function(){self.saveContentToField()})

  // Enable design mode
  this.toggleDesignMode(true);

	// copy value from field to editor pane
	this.loadContentFromField();

  // Enable all toolbar buttons
  this.toggleButtons(true);
}

/**
 * Sets widget size in pixels
 *
 * @public
 * @param {number} width Editor container width
 * @param {number} height Editor container height
 */
southprojects.MinimalEditor.prototype.setSize = function(width, height) {
  if (!width && !height) {
    width = parseInt(this.container.style.width);
    height = parseInt(this.container.style.height);
  }
  // Set toolbar width
  this.toolbar.style.width = width + 'px';

  // Set container dimensions
  this.container.style.width = width + 'px';
  this.container.style.height = height + 'px';

  // Get iframe pane size
  var paneSize = this.getPaneSize(width,height);
  var paneWidth = paneSize.width;
  var paneHeight = paneSize.height;

  var iframeWidth = paneWidth;
  var iframeHeight = paneHeight;

  if (!southprojects.is_ie && !southprojects.is_khtml) {
    iframeHeight -= 4;

    if (!this.isMaximize) {
      iframeWidth -= 4;
    }
  }

  if (southprojects.is_ie7) {
    iframeWidth -= 1;
    iframeHeight -= 3;
  }
  
  // Set iframe dimensions
  this.pane.getContainer().style.width = iframeWidth + 'px';
  this.pane.getContainer().style.height = iframeHeight + 'px';
  this.pane.getContainer().style.position = "absolute";

  var editorPanelHeight = paneHeight;
  var editorPanelWidth = width-2;
  if (!southprojects.is_ie && !southprojects.is_khtml) {
    editorPanelHeight -= 4;
  }
  if (southprojects.is_ie7) {
    editorPanelHeight -= 1;
  }

  // Set editor panel dimensions
  this.editorPanel.style.width = editorPanelWidth + 'px';
  this.editorPanel.style.height = editorPanelHeight + 'px';

  // Setup WCH
  southprojects.Utils.setupWCH(this.wch, 0, 0, width, paneHeight);

  if(southprojects.is_ie) {
    paneWidth -= 2;
    paneHeight -= 2;
    if(southprojects.is_ie7) {
      paneWidth -= 5;
      paneHeight -= 6;
    }
  }
  else {
    if (!southprojects.is_khtml) {
      if (this.isMaximize) {
        paneHeight -= 5;
      }
      else {
        paneHeight -= 5;
        paneWidth -= 4;
      }
    }
  }

  // Set textarea field dimensions
  this.config.field.style.width = paneWidth + 'px';
  this.config.field.style.height = paneHeight + 'px';
  this.config.field.style.position = "absolute";
}

/**
 * Gets iframe size in pixels. Extracts toolbar dimensions from whole size
 *
 * @private
 * @param {number} width Editor container width
 * @param {number} height Editor container height
 */
southprojects.MinimalEditor.prototype.getPaneSize = function(width, height) {

  var paneWidth = width;
  var paneHeight = height;

  // If there are toolbar buttons
  if(this.config.toolbarElements.length != 0){
    paneHeight -= this.toolbar.offsetHeight;

    if(paneHeight < 50){
      paneHeight = 50;
    }
  }

  if(southprojects.is_ie) {
    paneWidth -= 2;
  }
  else {
    if(southprojects.is_khtml) {
      paneWidth -= 2;
    }
    else {
      if (this.isMaximize) {
        paneWidth -= 2;
      }
      else {
        paneWidth += 2;
      }
    }
  }

  return {width: paneWidth, height: paneHeight};
}

/**
 * Loads content from TEXTAREA to editor area
 */
southprojects.MinimalEditor.prototype.loadContentFromField = function(){
	if(this.mode == "WYSIWYG"){
		this.pane.setContent(this.config.field.value);
	} else {
    var html = this.config.field.value;
    this.setHTML(html);
	}
}

/**
 * Saves value from editor area to original field TEXTAREA
 */
southprojects.MinimalEditor.prototype.saveContentToField = function(){
	this.config.field.value = this.mode == "WYSIWYG" ? this.pane.getContentElement().innerHTML : this.pane.getContentElement().firstChild.nodeValue;
}

/**
 * Toggles design mode for editor
 * @private
 * @param {boolean} enable enable/disable design mode
 */
southprojects.MinimalEditor.prototype.toggleDesignMode = function(enable){
	if(southprojects.is_ie){
		this.pane.getContentElement().contentEditable = enable;
	} else {
    this.pane.getContainer().contentWindow.document.designMode = enable ? "on" : "off";
	}
}

/**
 * Invokes execCommand in a browser compatible way
 * @private
 * @param {string} command command to execute
 * @param {object} arg1 command argument 1
 * @param {object} arg2 command argument 2
 */
southprojects.MinimalEditor.prototype.execCommand = function(command, arg1, arg2){
  var object = null;

  if(southprojects.is_ie){
    object = this.pane.getIframeDocument();
  } else {
    object = this.pane.getContainer().contentWindow.document;
  }

  return object.execCommand(command, arg1, arg2);
}

/**
 * Make the WYSIWYG editor focused
 * @private
 */
southprojects.MinimalEditor.prototype.focus = function(){
  this.pane.getContainer().contentWindow.focus();
}

/**
 * Make the HTML text area focused
 * @private
 */
southprojects.MinimalEditor.prototype.focusHtml = function(){
  if (southprojects.is_ie7) {
    // Store page vertical scroll position
    var scrollTop = southprojects.Utils.getPageScrollY();
  }
  this.config.field.focus();
  if (southprojects.is_ie7) {
    // Restore page vertical scroll position
    window.scroll(0, scrollTop);
  }
}

/**
 * Creates toolbar with buttons under editor
 * @private
 */
southprojects.MinimalEditor.prototype.addButtons = function(){
	if(this.config.toolbarElements.length == 0){
		return false;
	}

	this.toolbar = document.createElement("table");
  // Add toolbar before original html textarea field
  this.container.insertBefore(this.toolbar, this.editorPanel);

	this.toolbar.border = 0;
	this.toolbar.className = this.getClassName({prefix: "speditor", suffix: "Toolbar"});
	this.toolbar.style.width = this.config.field.clientWidth + 'px';

	var tbody = document.createElement("tbody");
	this.toolbar.appendChild(tbody);

	var tr = document.createElement("tr");
	tbody.appendChild(tr)

	var td = document.createElement("td");
	td.setAttribute("nowrap", "true");
	tr.appendChild(td);

	var span = document.createElement("span");
	span.className = "toolbarPanel";
	td.appendChild(span)

	var self = this;

	// For each element in toolbar config
  for(var ii = 0; ii < this.config.toolbarElements.length; ii++){
		var element = this.config.toolbarElements[ii];
		switch(element){
			case "maximize":
        var button = this.createButton("Maximize", "maximize",
                "Maximize/Minimize Editor", function() {
          self.resizeEditor();
          // Focus editor
          self.focus();
        });
        span.appendChild(button);
        break;
			case "fontname":
				span.appendChild(this.createSelect(
					["Font", "Arial", "Courier", "Times New Roman"],
					["Font", "Arial", "Courier", "Times New Roman"],
					function(){
						self.execCommand("fontname", false, this.options[this.options.selectedIndex].value);
						self.saveContentToField();

            // Focus editor
            self.focus();
          }
				));
				break;
			case "fontsize":
				span.appendChild(this.createSelect(
					["Size", "1", "2", "3", "4", "5", "6", "7"],
					["Size", "1", "2", "3", "4", "5", "6", "7"],
					function(){
						self.execCommand("fontsize", false,
                    this.options[this.options.selectedIndex].value);
						self.saveContentToField();

            // Focus editor
            self.focus();
					}
				));
				break;
			case "forecolor":
        var button = this.createButton("ForeColor", "forecolor", "Font Color",
          function() {
            if (southprojects.is_khtml) {
              self.storeSelection();
            }

            var show = function() {
              showPicker(null, null, button, function(color) {
                if (southprojects.is_khtml) {
                  self.restoreSelection();
                }

                self.execCommand("forecolor", false, color);
                self.saveContentToField();

                // Focus editor
                self.focus();
              })
            }
            if (southprojects.is_khtml) {
              // Prevent button from staying pressed
              setTimeout(show, 10);
            }
            else {
              show();
            }
          });
        span.appendChild(button);
				break;
			case "backcolor":
        var button = this.createButton("BG Color", "backcolor",
                "Background Color", function() {

          if (southprojects.is_khtml) {
            self.storeSelection();
          }

          var show = function() {
            showPicker(null, null, button, function(color) {

              self.focus();

              if (southprojects.is_khtml) {
                self.restoreSelection();
              }

              // Determine correct background color change command
              var command;
              if (southprojects.is_ie || southprojects.is_khtml) {
                command = "backcolor";
              }
              else {
                command = "hilitecolor";
              }

              self.execCommand(command, false, color);

              self.saveContentToField();
            });
          }
          if (southprojects.is_khtml) {
            // Prevent button from staying pressed
            setTimeout(show, 10);
          }
          else {
            show();
          }
        });
        span.appendChild(button);

        break;
      case "insertlink":
        span.appendChild(this.createButton("Insert link", "link",
                "Insert Web Link", function(){
          if (southprojects.is_khtml) {
            self.storeSelection();
          }

					var url = prompt("Enter a URL:", "http://");

          if (southprojects.is_khtml) {
            self.restoreSelection();
          }

					if (url != null && url != ""){
						self.execCommand("createlink", false, url);
						self.saveContentToField();

            // Focus editor
            self.focus();
					}
				}));
				break;
			case "insertimage":
				span.appendChild(this.createButton("Insert image", "image",
                "Insert/Modify Image", function(){
					var imgUrl = prompt("Enter a URL:", "http://");

					if (imgUrl != null && imgUrl != ""){
						self.execCommand("insertimage", false, imgUrl);
						self.saveContentToField();

            // Focus editor
            self.focus();
					}
				}));
				break;
			case "inserttable":
				span.appendChild(this.createButton("Insert table", "table",
                "Insert Table", function(){

          if (southprojects.is_khtml) {
            self.storeSelection();
          }

          var rows = parseInt(prompt("Enter number of rows", 2));
					var cols = parseInt(prompt("Enter number of cols", 2));
					var borderWidth = parseInt(prompt("Enter border width", 2));

					if(isNaN(borderWidth)){
						borderWidth = 1;
					}

					if (!isNaN(rows) && rows > 0 && !isNaN(cols) && cols > 0) {
						var tbl = self.pane.getIframeDocument().createElement("table");
						tbl.setAttribute("border", borderWidth);
						tbl.setAttribute("cellpadding", "1");
						tbl.setAttribute("cellspacing", "1");

						var tbltbody = self.pane.getIframeDocument().createElement("tbody");

						for (var kk = 0; kk < rows; kk++) {
							var tbltr = self.pane.getIframeDocument().createElement("tr");

							for (var jj = 0; jj < cols; jj++) {
								var tbltd = self.pane.getIframeDocument().createElement("td");
								tbltd.setAttribute("width", 10);

								var tblbr = self.pane.getIframeDocument().createElement("br");
								tbltd.appendChild(tblbr);
								tbltr.appendChild(tbltd);
							}

							tbltbody.appendChild(tbltr);
						}

						tbl.appendChild(tbltbody);

						self.insertNodeAtSelection(tbl);
					}

					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
      case "inserthorizontalrule":
        span.appendChild(this.createButton("Insert Horizontal Rule", "hr",
                "Horizontal Rule", function(){

          var hr = self.pane.getIframeDocument().createElement("hr");
          self.insertNodeAtSelection(hr);

          self.saveContentToField();

          // Focus editor
          self.focus();
        }));
        break;
      case "insertspecialchar":
        span.appendChild(this.createButton("Insert Special Character",
                "insertspecial", "Insert special character", function(){

          // Show a character map window
          self.showCharMapWindow();

          // Focus editor
          self.focus();
        }));
        break;
      case "selectall":
        span.appendChild(this.createButton("Select All", "selectall",
                "Select All", function(){
          self.execCommand("selectall");

          self.focus();
        }));
        break;
			case "bold":
				span.appendChild(this.createButton("B", "bold", "Bold", function(){
					self.execCommand("bold", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "italic":
				span.appendChild(this.createButton("I", "italic", "Italic", function(){
					self.execCommand("italic", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "underline":
				span.appendChild(this.createButton("U", "underline", "Underline",
                function(){
					self.execCommand("underline", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "justifyleft":
				span.appendChild(this.createButton("Left", "justifyleft",
                "Justify Left", function(){
					self.execCommand("justifyleft", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "justifycenter":
				span.appendChild(this.createButton("Center", "justifycenter",
                "Justify Center", function(){
					self.execCommand("justifycenter", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "justifyright":
				span.appendChild(this.createButton("Right", "justifyright",
                "Justify Right", function(){
					self.execCommand("justifyright", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "insertorderedlist":
				span.appendChild(this.createButton("Number", "orderedlist",
                "Ordered List",function(){
					self.execCommand("insertorderedlist", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "insertunorderedlist":
				span.appendChild(this.createButton("Bullet", "unorderedlist",
                "Bulleted List", function(){
					self.execCommand("insertunorderedlist", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "copy":
				span.appendChild(this.createButton("Copy", "copy", "Copy", function(){
					self.execCommand("copy", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "cut":
				span.appendChild(this.createButton("Cut", "cut", "Cut", function(){
					self.execCommand("cut", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "paste":
				span.appendChild(this.createButton("Paste", "paste", "Paste",
                function(){
					self.execCommand("paste", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "outdent":
				span.appendChild(this.createButton("Outdent", "outdent",
                "Decrease Indent", function(){
					self.execCommand("outdent", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "indent":
				span.appendChild(this.createButton("Indent", "indent",
                "Increase Indent", function(){
					self.execCommand("indent", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "undo":
				if(southprojects.is_ie){
					continue;
				}

				span.appendChild(this.createButton("Undo", "undo", "Undo", function(){
					self.execCommand("undo", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
			case "redo":
				if(southprojects.is_ie){
					continue;
				}

				span.appendChild(this.createButton("Redo", "redo", "Redo", function(){
					self.execCommand("redo", false, null);
					self.saveContentToField();

          // Focus editor
          self.focus();
				}));
				break;
      case "about":
        var aboutButton = this.createButton("About", "about", "About",
                function(){

          var aboutText = "DHTML Minimal Editor \n" +
          "(c) southprojects.com 2002-2006\n" + // don't translate this this ;-)
          "For latest version visit: http://www.southprojects.com/";

          alert(aboutText);
        }, "?");

        span.appendChild(aboutButton);

        break;
      case "fetch":
        var fetchButton = this.createButton("Fetch", "fetch",
                "Load from server", function(){
          self.fetch();
        });

        span.appendChild(fetchButton);

        break;
      case "save":
        var saveButton = this.createButton("Save", "save",
                "Save to server", function(){
          self.save();
        });

        span.appendChild(saveButton);

        break;
      case "browser":
        var browserButton = this.createButton("Browser", "browser",
                "View in external browser window", function(){

          var attributes = "status=1, width="+self.config.externalBrowserWidth+
                           ", height="+self.config.externalBrowserHeight+
                           ", resizable=yes";
          var win = window.open ("", "previewWindow", attributes);
          win.document.body.innerHTML = self.getHTML();
        });

        span.appendChild(browserButton);

        break;
			case "switcher":
				var htmlButton = this.createButton("HTML", "html", "Switch to HTML",
                function(){
					if(self.mode == "WYSIWYG"){
            self.switchToHTML();
            var button = this;
            setTimeout(function() {
              htmlButton.style.display = 'none';
              htmlButton.speditorWYSIWYGButton.style.display = 'block';
            }, 10 );

            // Focus html editor
            self.focusHtml();
					}
				});

				var wysiwygButton = this.createButton("WYSIWYG", "wysiwyg",
                "Switch to WYSIWYG", function(){
					if(self.mode == "HTML"){
						self.switchToWYSIWYG();

            setTimeout(function() {
              wysiwygButton.style.display = 'none';
              wysiwygButton.speditorHTMLButton.style.display = 'block';
            }, 10 );

            // Focus editor
            self.focus();
					}
				});

				htmlButton.speditorWYSIWYGButton = wysiwygButton;
				wysiwygButton.speditorHTMLButton = htmlButton;
				wysiwygButton.style.display = 'none';

				htmlButton.className += " switch";
				wysiwygButton.className += " switch";

				span.appendChild(htmlButton);
				span.appendChild(wysiwygButton);

        break;
			case "newpanel":
				if(span.childNodes.length == 0){
					// if last created panel is empty - do not add new panel
					continue;
				}

				span = document.createElement("span");
				span.className = "toolbarPanel"
				td.appendChild(span);
				break;
			case "newrow":
				if(td.childNodes.length == 1 && span.childNodes.length == 0){
					// if last created row is empty - do not add new row
					continue;
				}

				tr = document.createElement("tr");
				tbody.appendChild(tr);
				td = document.createElement("td");
				tr.appendChild(td);
				span = document.createElement("span");
				span.className = "toolbarPanel";
				td.appendChild(span);
				break;
			default:
				southprojects.Log({description: "Unknown toolbar element name: " + element + ". Ignored"})
		}
	}
}

/**
 * Insert DOM element in place of the current selection
 * @private
 * @param {object} insertNode A html DOM reference to insert
 */
southprojects.MinimalEditor.prototype.insertNodeAtSelection = function(insertNode){
  if(southprojects.is_ie){
    var self = this;
    setTimeout(function() {
      var sel = self.pane.getContainer().contentWindow.document.selection;
      var range = sel.createRange();
      range.pasteHTML(insertNode.outerHTML);
    }, 10);
  } else {
    if(southprojects.is_khtml) {
      var sel;
      if (this.oldSelection) {
        sel = this.oldSelection;
      }
      else {
        sel = this.pane.getContainer().contentWindow.getSelection();
      }
      var range = this.pane.getContainer().contentWindow.document.createRange();

      var isRangeSet = false;
      if (sel.baseNode == sel.extentNode && sel.baseOffset == sel.extentOffset) {
        if (sel.type == "Range") {
          range.setStartBefore(sel.baseNode);
          range.setEndAfter(sel.extentNode);
          isRangeSet = true;
        }
      }
      if (!isRangeSet) {
        range.setStart(sel.baseNode, sel.baseOffset);
        range.setEnd(sel.extentNode, sel.extentOffset);
      }
    }
    else {
      var sel = this.pane.getContainer().contentWindow.getSelection();
      var range = sel.getRangeAt(0);
      sel.removeAllRanges();
    }

    range.deleteContents();

		var container = range.startContainer;
		var pos = range.startOffset;
    var doc = this.pane.getIframeDocument();
		range = doc.createRange();

		if(container.nodeType == 3 && insertNode.nodeType == 3) {
			container.insertData(pos, insertNode.nodeValue);
			range.setEnd(container, pos+insertNode.length);
			range.setStart(container, pos+insertNode.length);
		} else {
			var afterNode;
			if(container.nodeType == 3) {
				var textNode = container;
				container = textNode.parentNode;
				var text = textNode.nodeValue;

				var textBefore = text.substr(0,pos);
				var textAfter = text.substr(pos);

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

				container.insertBefore(afterNode, textNode);
				container.insertBefore(insertNode, afterNode);
				container.insertBefore(beforeNode, insertNode);

				container.removeChild(textNode);
			} else {
				afterNode = container.childNodes[pos];
        container.insertBefore(insertNode, afterNode);

        if (!afterNode) {
          // Move carret after currently inserted node
          range.setEnd(insertNode, 1);
          range.setStart(insertNode, 1);
        }
      }

			if (afterNode) {
        // Move carret at the node after currently inserted node
        range.setEnd(afterNode, 0);
        range.setStart(afterNode, 0);
      }
    }

		sel.addRange(range);
	}
}

/**
 * Set a given html inside both editors (HTML and WYSIWYG)
 * @private
 * @param {string} html A html content to set inside the editor
 */
southprojects.MinimalEditor.prototype.setHTML = function(html){
  this.pane.getIframeDocument().body.innerHTML = html;
  this.config.field.value = html;
}

/**
 * Gets html content inside the editor
 * @private
 */
southprojects.MinimalEditor.prototype.getHTML = function(){
  return this.mode == "WYSIWYG" ? this.pane.getContentElement().innerHTML :
         this.config.field.value;
}

/**
 * Switch editor to HTML mode
 * @private
 */
southprojects.MinimalEditor.prototype.switchToHTML = function(){
	if(this.mode != "WYSIWYG"){
		return false;
	}

  var html = this.getHTML();

	this.mode = "HTML";
	this.toggleButtons(false)

  this.setHTML(html);

  // show original field
  this.config.field.style.display = "block";

  // hide editable iframe
  if (southprojects.is_khtml) {
    this.pane.getContainer().style.visibility = "hidden";
  }
  else {
    this.pane.getContainer().style.display = "none";
  }
}

/**
 * Switch editor to WYSIWYG mode
 * @private
 */
southprojects.MinimalEditor.prototype.switchToWYSIWYG = function(){
	if(this.mode != "HTML"){
		return false;
	}

  var html = this.getHTML();

	this.mode = "WYSIWYG";

  // Enable all toolbar buttons
	this.toggleButtons(true)

  this.setHTML(html);

  // hide original field
  this.config.field.style.display = "none";

  // show editable iframe
  if (southprojects.is_khtml) {
    this.pane.getContainer().style.visibility = "show";
  }
  else {
    this.pane.getContainer().style.display = "block";
  }
}

/**
 * Creates a new toolbar button after the last one
 * @private
 * @param buttonName - [string] Button name
 * @param buttonClass - [string] A css class to apply to the button
 * @param tooltip - [string] Tooltip text
 * @param func - [function] An onClick handler function the be attached
 * @param buttonTitle - [string] Button text
 */
southprojects.MinimalEditor.prototype.createButton = function(buttonName, buttonClass,
                                                        tooltip, func, text){
  if (null == text) {
    text = "";
  }

  // Create a new button
  var button = new southprojects.Button({
		theme: this.config.theme,
		themePath: this.config.themePath,
		text: text,
//		image: this.config.themePath + "px.gif",
		className: "speditorButton " + buttonClass,
		downAction: function(){try{func()}catch(e){}}
	})

  // Create tooltip
  if (tooltip) {
    var div = document.createElement("div");
    div.innerHTML = tooltip;
    var objTooltip = new southprojects.Tooltip(button.container, div, this.container);
  }

  this.buttons.push(button);

	return button.getContainer();
}

/**
 * Help function for creating selects
 * @private
 * @param {array} options Array of string options to appear inside the select
 * @param {array} values Array of values to be associated with each option
 * @param {function} func A select onChange handler function to be attached
 */
southprojects.MinimalEditor.prototype.createSelect = function(options, values, func){
	if(options.length != values.length){
		southprojects.Log({description: "Sizes of options and values differs!"});
		return null;
	}

	var select = document.createElement("select");
	select.className = "speditorSelect";
	for(var ii = 0; ii < options.length; ii++){
		select.options[ii] = new Option(options[ii], values[ii])
	}

	select.onchange = func;

	return select;
}

/**
 * Enable/disable all buttons in the toolbar except the HTML/WYSIWYG switcher
 *
 * @private
 * @param {boolean} enable true to enable, false to disable
 */
southprojects.MinimalEditor.prototype.toggleButtons = function(enable){
	for(var ii = 0; ii < this.buttons.length; ii++){
		var button = this.buttons[ii];

		var className = button.getContainer().className;
    // Skip HTML/WYSIWYG toggle buttons
    if (/switch/.test(className)) {
      continue;
		}

		if(enable){
			button.enable();
		} else {
			button.disable();
		}
	}
}

/**
 * Handles Enter key pressed event. Deletes currently selected text and inserts
 * a &lt;br&gt; tag. This handler is attached only in IE where Enter produces
 * a &lt;p&gt; rather than &lt;br&gt; tag
 * @private
 */
southprojects.MinimalEditor.prototype.onEnterPressed = function() {
  var iframeDocument = this.pane.getIframeDocument();
  if (typeof iframeDocument.selection != 'undefined') {
    var selection = iframeDocument.selection;

    if (selection.type.toLowerCase() == 'control') {
      // Delete selection
      selection.clear();
    }

    var range = selection.createRange();

    // If we are inside an ordered/bulleted list
    if (iframeDocument.queryCommandState('InsertOrderedList') ||
        iframeDocument.queryCommandState('InsertUnorderedList')) {
      return false;
    }

    // Insert <br>
    range.pasteHTML('<br>');

    // Move cursor after <br>
    range.collapse(false);
    range.select();

    return true;
  }

  return false;
}

/**
* Maximizes and Restores the editor frame.  The icon is updated as
* needed.
*
* @param {boolean} isMaximize if true the editor iframe is to be maximized,
* otherwise it is to be restored to its original state. If omitted the function
* will act as a toggle inverting the maximize state in each call.
* @private
*/
southprojects.MinimalEditor.prototype.resizeEditor = function(isMaximize) {

  // If no param is provided act as toggle
  if (null == isMaximize) {
    isMaximize = !this.isMaximize;
  }

  // If maximized state is already set to the same value
  if (isMaximize == this.isMaximize) {
    return;
  }

  this.isMaximize = isMaximize;

  var body;
  if (southprojects.is_ie)  {
    body = document.getElementsByTagName('html');
  }
  else {
    body = document.getElementsByTagName('body');
  }

  // If editor is to be maximized
  if (true == isMaximize) {
    // Save editor position state before maximizing it
    this.restoreState = {};
    this.restoreState.containerLeft = this.container.style.left;
    this.restoreState.containerTop = this.container.style.top;
    this.restoreState.containerWidth = this.container.style.width;
    this.restoreState.containerHeight = this.container.style.height;

    this.restoreState.editorPanelWidth = this.editorPanel.style.width;
    this.restoreState.editorPanelHeight = this.editorPanel.style.height;

    this.restoreState.paneWidth = this.pane.getContainer().style.width;
    this.restoreState.paneHeight = this.pane.getContainer().style.height;
    this.restoreState.fieldWidth = this.config.field.style.width;
    this.restoreState.fieldHeight = this.config.field.style.height;
    this.restoreState.toolbarWidth = this.toolbar.style.width;

    this.restoreState.scrollTop = southprojects.Utils.getPageScrollY();
    this.restoreState.onresize = window.onresize;
    this.restoreState.containerPosition = this.container.style.position;

    this.container.style.position = 'absolute';

    // Position container
    this.container.style.left = this.config.maximizedBorder + 'px';
    this.container.style.top = this.config.maximizedBorder + 'px';

    // Scroll to top
    window.scroll(0,0);

    // Remove window scroller (if any)
    body[0].style.overflow = "hidden";

    var self = this;
    var sizeEditor = function() {
      // Get current browser window size
      var size = southprojects.Utils.getWindowSize();

      // Determine editor container dimensions
      var width = size.width - 2*self.config.maximizedBorder -4;
      var height = size.height - 2*self.config.maximizedBorder;

      // Set editor container size
      self.setSize(width, height);
    }

    // Size editor according to current browser window size
    sizeEditor();

    // Attach as a window resize event listener
    window.onresize = sizeEditor;

  }
  else {
    // Restore old window resize event listener
    window.onresize = this.restoreState.onresize;

    // Remove window scroller (if any)
    body[0].style.overflow = "auto";

    // Restore editor original position and size before the maximize
    this.container.style.position = this.restoreState.containerPosition;

    // Restore container size and position
    this.container.style.left = this.restoreState.containerLeft;
    this.container.style.top = this.restoreState.containerTop;
    this.container.style.width = this.restoreState.containerWidth;
    this.container.style.height = this.restoreState.containerHeight;

    // Restore editor panel size
    this.editorPanel.style.width = this.restoreState.editorPanelWidth;
    this.editorPanel.style.height = this.restoreState.editorPanelHeight;

    // Restore iframe size
    this.pane.getContainer().style.width = this.restoreState.paneWidth;
    this.pane.getContainer().style.height = this.restoreState.paneHeight;
    // Restore text area size
    this.config.field.style.width = this.restoreState.fieldWidth;
    this.config.field.style.height = this.restoreState.fieldHeight;

    // Restore toolbar width
    this.toolbar.style.width = this.restoreState.toolbarWidth;

    // Scroll to top
    window.scroll(0, this.restoreState.scrollTop);

    this.restoreState = null;
  }

  // Enumerate all toolbar buttons
  for(var ii = 0; ii < this.buttons.length; ii++){
    var button = this.buttons[ii];

    var buttonClass = button.config.className;

    if (-1 != buttonClass.indexOf('maximize')) {
      if (isMaximize) {
        southprojects.Utils.removeClass(button.internalContainer, 'maximize');
        southprojects.Utils.addClass(button.internalContainer, 'restore');
      }
      else {
        southprojects.Utils.removeClass(button.internalContainer, 'restore');
        southprojects.Utils.addClass(button.internalContainer, 'maximize');
      }
      break;
    }
  }
}

/**
 * Gets the key under which the html content is to be saved/fetched server-side
 *
 * @public
 */
southprojects.MinimalEditor.prototype.getPersistKey = function(){
  // If persist key is assigned by user
  if (null != this.config.persistKey){
    // Return it
    return this.config.persistKey;
  }

  var persistKeyInput = document.getElementById("persistKey");
  if (null != persistKeyInput) {
    return persistKeyInput.value;
  }

  return null;
}

/**
 * Sets the key under which the html content is to be saved/fetched server-side
 *
 * @public
 * @param {string} persistKey A key under which the html content is to be
 * saved/fetched server-side
 */
southprojects.MinimalEditor.prototype.setPersistKey = function(persistKey){
  this.config.persistKey = persistKey;
}

/**
 * Saves the editor content to a remote server.
 *
 * @public
 */
southprojects.MinimalEditor.prototype.save = function(){
  var key = this.getPersistKey();

	if(null == key || 0 == key.length){
		return false;
	}

	var url = this.config.persistPath + "?key=" + escape(key) + '&r=' + Math.random();

  var self = this;
	southprojects.Transport.fetch({
		url: url,
		method: "POST",
		content: "content=" + escape(self.getHTML()),
		onLoad: function(result) {},
		onError: function(error){
			switch(error.errorCode){
				case 404:
					alert(self._lc("No such file"));
					break;
				case 403:
					alert(self._lc("Read access forbidden for this file"));
					break;
				default:
					alert(self._lc("Error fetching file: ") + error.errorCode + " " + error.errorDescription);
			}
		}
	});

	return true;
};

/**
 * Loads editor content from a remove server
 *
 * @public
 */
southprojects.MinimalEditor.prototype.fetch = function(){
  var key = this.getPersistKey();

  if(null == key || 0 == key.length){
		return false;
	}

  var url = this.config.persistPath + "?key=" + escape(key) + '&r=' + Math.random();

	var self = this;
	southprojects.Transport.fetch({
		url: url,
		method: "GET",
		onLoad: function(result) {
      // Unescape unicode characters
      var html = self.unicodeToText(result.responseText);
      self.setHTML(html);
		},
		onError: function(error){
			switch(error.errorCode){
				case 404:
					alert(self._lc("No such file"));
					break;
				case 403:
					alert(self._lc("Write access forbidden for this file"));
					break;
				default:
					alert(self._lc("Error saving file: ") + error.errorCode + " " + error.errorDescription);
			}
		}
	});

	return true;
};

/**
 * Converts text in for of %u0075%u006e%u0069%u0063%u006f%u0064%u0065
 * to its text un-escaped equivalent
 *
 * @param {string} escapedUnicodeText text to convert
 * @private
 */
southprojects.MinimalEditor.prototype.unicodeToText = function(escapedUnicodeText) {
  if (-1 == escapedUnicodeText.indexOf("%u")) {
    // Nothing to un-escape
    return escapedUnicodeText;
  }
  escapedUnicodeText = escapedUnicodeText.replace(/%u/g, "\\u");

  // If there are quotes in string
  if (-1 != escapedUnicodeText.indexOf("\"")) {
    // Escape quotes before passing to eval to avoid JS error
    escapedUnicodeText = escapedUnicodeText.replace(/\"/g, "&quot;");
  }
  var text = eval("\"" + escapedUnicodeText + "\"");
  return text;
}

/**
 * Shows a special character input window
 *
 * @public
 */
southprojects.MinimalEditor.prototype.showCharMapWindow = function(){
  // If character map windows is not yet closed
  if (this.characterMapWindow) {
    return;
  }

  if (southprojects.is_khtml) {
    this.storeSelection();
  }

  var characters =
  [
    '&Yuml;', '&scaron;', '&#064;', '&quot;', '&iexcl;', '&cent;', '&pound;', '&curren;', '&yen;', '&brvbar;',
    '&sect;', '&uml;', '&copy;', '&ordf;', '&laquo;', '&not;', '&macr;', '&deg;', '&plusmn;', '&sup2;',
    '&sup3;', '&acute;', '&micro;', '&para;', '&middot;', '&cedil;', '&sup1;', '&ordm;', '&raquo;', '&frac14;',
    '&frac12;', '&frac34;', '&iquest;', '&times;', '&Oslash;', '&divide;', '&oslash;', '&fnof;', '&circ;',
    '&tilde;', '&ndash;', '&mdash;', '&lsquo;', '&rsquo;', '&sbquo;', '&ldquo;', '&rdquo;', '&bdquo;',
    '&dagger;', '&Dagger;', '&bull;', '&hellip;', '&permil;', '&lsaquo;', '&rsaquo;', '&euro;', '&trade;',
    '&Agrave;', '&Aacute;', '&Acirc;', '&Atilde;', '&Auml;', '&Aring;', '&AElig;', '&Ccedil;', '&Egrave;',
    '&Eacute;', '&Ecirc;', '&Euml;', '&Igrave;', '&Iacute;', '&Icirc;', '&Iuml;', '&ETH;', '&Ntilde;',
    '&Ograve;', '&Oacute;', '&Ocirc;', '&Otilde;', '&Ouml;', '&reg;', '&times;', '&Ugrave;', '&Uacute;',
    '&Ucirc;', '&Uuml;', '&Yacute;', '&THORN;', '&szlig;', '&agrave;', '&aacute;', '&acirc;', '&atilde;',
    '&auml;', '&aring;', '&aelig;', '&ccedil;', '&egrave;', '&eacute;', '&ecirc;', '&euml;', '&igrave;',
    '&iacute;', '&icirc;', '&iuml;', '&eth;', '&ntilde;', '&ograve;', '&oacute;', '&ocirc;', '&otilde;',
    '&ouml;', '&divide;', '&oslash;', '&ugrave;', '&uacute;', '&ucirc;', '&uuml;', '&yacute;', '&thorn;',
    '&yuml;', '&OElig;', '&oelig;', '&Scaron;'
  ];

  var html = '<table class="charmap" border="0" cellspacing="1" cellpadding="0" width="100%">';
  for (var charIndex = 0; charIndex < characters.length; ++charIndex) {
    var charStr = characters[charIndex];
    if (charIndex % 16 == 0) {
      if (0 != charIndex) {
        html += '</tr>';
      }
      html += '<tr>';
    }

    html += '<td class="character" onmouseover="';
    html += 'southprojects.MinimalEditor.OnMouseOverChar(' + this.id + ', this)" ';
    html += 'onclick="return southprojects.MinimalEditor.InsertChar(' + this.id + ', \''+
            charStr+'\')">'+charStr+'</td>';
  }
  html += '<td class="character" colspan="4">&nbsp;</td>';
  html += '</tr>';
  html += '</table>';

  var self = this;
  var onClose = function(win) {
    self.characterMapWindow = null;
  } 

  var oOffset = southprojects.Utils.getElementOffset(this.editorPanel);

  var top = oOffset.y - 100;
  // Limit window location to page top
  if (top < 0) {
    top = 0;
    if (this.isMaximize) {
      top += this.config.maximizedBorder;
    }
  }

  this.characterMapWindow = southprojects.Window.setup({
    left : oOffset.x + 100,
    top : top,
    width : 420,
    height : 220,
    raiseOnlyOnTitle : false,
    showStatus : false,
    canResize : false,
    showMaxButton : false,
    showMinButton : false,
    theme : 'winxp',
    content : html,
    title : "Character Map",
    onClose : onClose
  });
}

/**
 * Called internally when the mouse goes over a character cell in character map
 *
 * @private
 * @param {string} id editor id
 * @param {object} cell reference to character cell
 */
southprojects.MinimalEditor.OnMouseOverChar = function(id, cell) {
  // Get editor by id
  var objEditor = southprojects.Widget.getWidgetById(id);

  if (objEditor.oldHiliteCell) {
    objEditor.oldHiliteCell.className = "character";
  }

  var cellClass = cell.className;
  if (-1 == cellClass.indexOf("character-hilite")) {
    cell.className = "character character-hilite";
    objEditor.oldHiliteCell = cell;
  }
}

/**
 * Called internally when a character cell is clicked
 *
 * @private
 * @param {string} id editor id
 * @param {string} character selected character
 */
southprojects.MinimalEditor.InsertChar = function(id, character) {

  // Get editor by id
  var objEditor = southprojects.Widget.getWidgetById(id);

  // Close character map widow
  objEditor.characterMapWindow.close();
  
  if(southprojects.is_ie){
    var range = objEditor.oldSelectionRange;
    if (!range) {
      var sel = objEditor.pane.getContainer().contentWindow.document.selection;

      range = sel.createRange();
    }

    setTimeout(function() {
      range.pasteHTML(character);

      range.select();
    }, 50);

  } else {
    if (southprojects.is_khtml) {
      objEditor.restoreSelection();
    }

    var charElement = objEditor.pane.getIframeDocument().createTextNode(character);
    objEditor.insertNodeAtSelection(charElement);

    objEditor.focus();

  }
}

/**
 * Stores current selection state and its range. Safari specific.
 *
 * @private
 */
southprojects.MinimalEditor.prototype.storeSelection = function() {
  var sel = this.pane.getContainer().contentWindow.getSelection();

  var range = this.pane.getContainer().contentWindow.document.createRange();

  var isRangeSet = false;
  if (sel.baseNode == sel.extentNode && sel.baseOffset == sel.extentOffset) {
    if (sel.type == "Range") {
      range.setStartBefore(sel.baseNode);
      range.setEndAfter(sel.extentNode);
      isRangeSet = true;
    }
  }
  if (!isRangeSet) {
    range.setStart(sel.baseNode, sel.baseOffset);
    range.setEnd(sel.extentNode, sel.extentOffset);
  }


  this.oldSelection = sel;
  this.oldRange = range;
}

/**
 * Restores a previously stored selection state and its range. Safari specific.
 *
 * @private
 */
southprojects.MinimalEditor.prototype.restoreSelection = function() {
  var sel = this.oldSelection;

  var newSelection = this.pane.getContainer().contentWindow.getSelection();
  newSelection.setBaseAndExtent(this.oldRange.startContainer,
          this.oldRange.startOffset, this.oldRange.endContainer,
          this.oldRange.endOffset);
}

southprojects.Utils.addEvent(window, 'load', southprojects.Utils.checkActivation);
